mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-12-05 17:02:32 -05:00
Permissions redesign (#2149)
API changes: - Cogs must now inherit from `commands.Cog` (see #2151 for discussion and more details) - All functions which are not decorators in the `redbot.core.checks` module are now deprecated in favour of their counterparts in `redbot.core.utils.mod`. This is to make this module more consistent and end the confusing naming convention. - `redbot.core.checks.check_overrides` function is now gone, overrideable checks can now be created with the `@commands.permissions_check` decorator - Command, Group, Cog and Context have some new attributes and methods, but they are for internal use so shouldn't concern cog creators (unless they're making a permissions cog!). - `__permissions_check_before` and `__permissions_check_after` have been replaced: A cog method named `__permissions_hook` will be evaluated as permissions hooks in the same way `__permissions_check_before` previously was. Permissions hooks can also be added/removed/verified through the new `*_permissions_hook()` methods on the bot object, and they will be verified even when permissions is unloaded. - New utility method `redbot.core.utils.chat_formatting.humanize_list` - New dependency [`schema`](https://github.com/keleshev/schema) User-facing changes: - When a `@bot_has_permissions` check fails, the bot will respond saying what permissions were actually missing. - All YAML-related `[p]permissions` subcommands now reside under the `[p]permissions acl` sub-group (tbh I still think the whole cog has too many top-level commands) - The YAML schema for these commands has been changed - A rule cannot be set as allow and deny at the same time (previously this would just default to allow) Documentation: - New documentation for `redbot.core.commands.requires` and `redbot.core.checks` modules - Renewed documentation for the permissions cog - `sphinx.ext.doctest` is now enabled Note: standard discord.py checks will still behave exactly the same way, in fact they are checked before `Requires` is looked at, so they are not overrideable. Signed-off-by: Toby Harradine <tobyharradine@gmail.com>
This commit is contained in:
@@ -38,8 +38,9 @@ RUNNING_ANNOUNCEMENT = (
|
||||
)
|
||||
|
||||
|
||||
class Admin:
|
||||
class Admin(commands.Cog):
|
||||
def __init__(self, config=Config):
|
||||
super().__init__()
|
||||
self.conf = config.get_conf(self, 8237492837454039, force_registration=True)
|
||||
|
||||
self.conf.register_global(serverlocked=False)
|
||||
|
||||
@@ -14,7 +14,7 @@ _ = Translator("Alias", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Alias:
|
||||
class Alias(commands.Cog):
|
||||
"""
|
||||
Alias
|
||||
|
||||
@@ -31,6 +31,7 @@ class Alias:
|
||||
default_guild_settings = {"enabled": False, "entries": []} # Going to be a list of dicts
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self._aliases = Config.get_conf(self, 8927348724)
|
||||
|
||||
|
||||
@@ -27,8 +27,9 @@ __author__ = ["aikaterna", "billy/bollo/ati"]
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Audio:
|
||||
class Audio(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, 2711759130, force_registration=True)
|
||||
|
||||
|
||||
@@ -54,10 +54,11 @@ def check_global_setting_admin():
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Bank:
|
||||
class Bank(commands.Cog):
|
||||
"""Bank"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
|
||||
# SECTION commands
|
||||
|
||||
@@ -14,10 +14,11 @@ _ = Translator("Cleanup", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Cleanup:
|
||||
class Cleanup(commands.Cog):
|
||||
"""Commands for cleaning messages"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -172,12 +172,13 @@ class CommandObj:
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class CustomCommands:
|
||||
class CustomCommands(commands.Cog):
|
||||
"""Custom commands
|
||||
|
||||
Creates commands used to display text"""
|
||||
|
||||
def __init__(self, bot):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.key = 414589031223512
|
||||
self.config = Config.get_conf(self, self.key)
|
||||
|
||||
@@ -11,12 +11,13 @@ _ = Translator("DataConverter", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class DataConverter:
|
||||
class DataConverter(commands.Cog):
|
||||
"""
|
||||
Cog for importing Red v2 Data
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
|
||||
@checks.is_owner()
|
||||
|
||||
@@ -23,8 +23,9 @@ _ = Translator("Downloader", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Downloader:
|
||||
class Downloader(commands.Cog):
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
|
||||
self.conf = Config.get_conf(self, identifier=998240343, force_registration=True)
|
||||
|
||||
@@ -105,7 +105,7 @@ class SetParser:
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Economy:
|
||||
class Economy(commands.Cog):
|
||||
"""Economy
|
||||
|
||||
Get rich and have fun with imaginary currency!"""
|
||||
@@ -128,6 +128,7 @@ class Economy:
|
||||
default_user_settings = default_member_settings
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.file_path = "data/economy/settings.json"
|
||||
self.config = Config.get_conf(self, 1256844281)
|
||||
|
||||
@@ -10,10 +10,11 @@ _ = Translator("Filter", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Filter:
|
||||
class Filter(commands.Cog):
|
||||
"""Filter-related commands"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.settings = Config.get_conf(self, 4766951341)
|
||||
default_guild_settings = {
|
||||
|
||||
@@ -33,10 +33,11 @@ class RPSParser:
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class General:
|
||||
class General(commands.Cog):
|
||||
"""General commands."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.stopwatches = {}
|
||||
self.ball = [
|
||||
_("As I see it, yes"),
|
||||
|
||||
@@ -11,12 +11,13 @@ GIPHY_API_KEY = "dc6zaTOxFJmzC"
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Image:
|
||||
class Image(commands.Cog):
|
||||
"""Image related commands."""
|
||||
|
||||
default_global = {"imgur_client_id": None}
|
||||
|
||||
def __init__(self, bot):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.settings = Config.get_conf(self, identifier=2652104208, force_registration=True)
|
||||
self.settings.register_global(**self.default_global)
|
||||
|
||||
@@ -26,7 +26,7 @@ def mod_or_voice_permissions(**perms):
|
||||
else:
|
||||
return True
|
||||
|
||||
return commands.check(pred)
|
||||
return commands.permissions_check(pred)
|
||||
|
||||
|
||||
def admin_or_voice_permissions(**perms):
|
||||
@@ -48,7 +48,7 @@ def admin_or_voice_permissions(**perms):
|
||||
else:
|
||||
return True
|
||||
|
||||
return commands.check(pred)
|
||||
return commands.permissions_check(pred)
|
||||
|
||||
|
||||
def bot_has_voice_permissions(**perms):
|
||||
|
||||
@@ -18,7 +18,7 @@ _ = Translator("Mod", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Mod:
|
||||
class Mod(commands.Cog):
|
||||
"""Moderation tools."""
|
||||
|
||||
default_guild_settings = {
|
||||
@@ -38,6 +38,7 @@ class Mod:
|
||||
default_user_settings = {"past_names": []}
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.settings = Config.get_conf(self, 4961522000, force_registration=True)
|
||||
|
||||
|
||||
@@ -9,10 +9,11 @@ _ = Translator("ModLog", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class ModLog:
|
||||
class ModLog(commands.Cog):
|
||||
"""Log for mod actions"""
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
|
||||
@commands.group()
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
from .permissions import Permissions
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Permissions(bot))
|
||||
async def setup(bot):
|
||||
cog = Permissions(bot)
|
||||
await cog.initialize()
|
||||
# It's important that these listeners are added prior to load, so
|
||||
# the permissions commands themselves have rules added.
|
||||
# Automatic listeners being added in add_cog happen in arbitrary
|
||||
# order, so we want to circumvent that.
|
||||
bot.add_listener(cog.cog_added, "on_cog_add")
|
||||
bot.add_listener(cog.command_added, "on_command_add")
|
||||
bot.add_cog(cog)
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
from typing import NamedTuple, Union, Optional
|
||||
from redbot.core import commands
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
class CogOrCommand(commands.Converter):
|
||||
async def convert(self, ctx: commands.Context, arg: str) -> Tuple[str]:
|
||||
ret = ctx.bot.get_cog(arg)
|
||||
if ret:
|
||||
return "cogs", ret.__class__.__name__
|
||||
ret = ctx.bot.get_command(arg)
|
||||
if ret:
|
||||
return "commands", ret.qualified_name
|
||||
class CogOrCommand(NamedTuple):
|
||||
type: str
|
||||
name: str
|
||||
obj: Union[commands.Command, commands.Cog]
|
||||
|
||||
# noinspection PyArgumentList
|
||||
@classmethod
|
||||
async def convert(cls, ctx: commands.Context, arg: str) -> "CogOrCommand":
|
||||
cog = ctx.bot.get_cog(arg)
|
||||
if cog:
|
||||
return cls(type="COG", name=cog.__class__.__name__, obj=cog)
|
||||
cmd = ctx.bot.get_command(arg)
|
||||
if cmd:
|
||||
return cls(type="COMMAND", name=cmd.qualified_name, obj=cmd)
|
||||
|
||||
raise commands.BadArgument(
|
||||
'Cog or command "{arg}" not found. Please note that this is case sensitive.'
|
||||
@@ -17,28 +23,34 @@ class CogOrCommand(commands.Converter):
|
||||
)
|
||||
|
||||
|
||||
class RuleType(commands.Converter):
|
||||
async def convert(self, ctx: commands.Context, arg: str) -> str:
|
||||
class RuleType:
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@classmethod
|
||||
async def convert(cls, ctx: commands.Context, arg: str) -> bool:
|
||||
if arg.lower() in ("allow", "whitelist", "allowed"):
|
||||
return "allow"
|
||||
return True
|
||||
if arg.lower() in ("deny", "blacklist", "denied"):
|
||||
return "deny"
|
||||
return False
|
||||
|
||||
raise commands.BadArgument(
|
||||
'"{arg}" is not a valid rule. Valid rules are "allow" or "deny"'.format(arg=arg)
|
||||
)
|
||||
|
||||
|
||||
class ClearableRuleType(commands.Converter):
|
||||
async def convert(self, ctx: commands.Context, arg: str) -> str:
|
||||
class ClearableRuleType:
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@classmethod
|
||||
async def convert(cls, ctx: commands.Context, arg: str) -> Optional[bool]:
|
||||
if arg.lower() in ("allow", "whitelist", "allowed"):
|
||||
return "allow"
|
||||
return True
|
||||
if arg.lower() in ("deny", "blacklist", "denied"):
|
||||
return "deny"
|
||||
return False
|
||||
if arg.lower() in ("clear", "reset"):
|
||||
return "clear"
|
||||
return None
|
||||
|
||||
raise commands.BadArgument(
|
||||
'"{arg}" is not a valid rule. Valid rules are "allow" or "deny", or "clear" to remove the rule'
|
||||
"".format(arg=arg)
|
||||
'"{arg}" is not a valid rule. Valid rules are "allow" or "deny", or "clear" to '
|
||||
"remove the rule".format(arg=arg)
|
||||
)
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
from redbot.core import commands
|
||||
from redbot.core.config import Config
|
||||
from .resolvers import entries_from_ctx, resolve_lists
|
||||
|
||||
# This has optimizations in it that may not hold True if other parts of the permission
|
||||
# model are changed from the state they are in currently.
|
||||
# (commit hash ~ 3bcf375204c22271ad3ed1fc059b598b751aa03f)
|
||||
#
|
||||
# This is primarily to help with the performance of the help formatter
|
||||
|
||||
# This is less efficient if only checking one command,
|
||||
# but is much faster for checking all of them.
|
||||
|
||||
|
||||
async def mass_resolve(*, ctx: commands.Context, config: Config):
|
||||
"""
|
||||
Get's all the permission cog interactions for all loaded commands
|
||||
in the given context.
|
||||
"""
|
||||
|
||||
owner_settings = await config.owner_models()
|
||||
guild_owner_settings = await config.guild(ctx.guild).owner_models() if ctx.guild else None
|
||||
|
||||
ret = {"allowed": [], "denied": [], "default": []}
|
||||
|
||||
for cogname, cog in ctx.bot.cogs.items():
|
||||
|
||||
cog_setting = resolve_cog_or_command(
|
||||
objname=cogname, models=owner_settings, ctx=ctx, typ="cogs"
|
||||
)
|
||||
if cog_setting is None and guild_owner_settings:
|
||||
cog_setting = resolve_cog_or_command(
|
||||
objname=cogname, models=guild_owner_settings, ctx=ctx, typ="cogs"
|
||||
)
|
||||
|
||||
for command in [c for c in ctx.bot.all_commands.values() if c.instance is cog]:
|
||||
resolution = recursively_resolve(
|
||||
com_or_group=command,
|
||||
o_models=owner_settings,
|
||||
g_models=guild_owner_settings,
|
||||
ctx=ctx,
|
||||
)
|
||||
|
||||
for com, resolved in resolution:
|
||||
if resolved is None:
|
||||
resolved = cog_setting
|
||||
if resolved is True:
|
||||
ret["allowed"].append(com)
|
||||
elif resolved is False:
|
||||
ret["denied"].append(com)
|
||||
else:
|
||||
ret["default"].append(com)
|
||||
|
||||
ret = {k: set(v) for k, v in ret.items()}
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def recursively_resolve(*, com_or_group, o_models, g_models, ctx, override=False):
|
||||
ret = []
|
||||
if override:
|
||||
current = False
|
||||
else:
|
||||
current = resolve_cog_or_command(
|
||||
typ="commands", objname=com_or_group.qualified_name, ctx=ctx, models=o_models
|
||||
)
|
||||
if current is None and g_models:
|
||||
current = resolve_cog_or_command(
|
||||
typ="commands", objname=com_or_group.qualified_name, ctx=ctx, models=o_models
|
||||
)
|
||||
ret.append((com_or_group, current))
|
||||
if isinstance(com_or_group, commands.Group):
|
||||
for com in com_or_group.commands:
|
||||
ret.extend(
|
||||
recursively_resolve(
|
||||
com_or_group=com,
|
||||
o_models=o_models,
|
||||
g_models=g_models,
|
||||
ctx=ctx,
|
||||
override=(current is False),
|
||||
)
|
||||
)
|
||||
return ret
|
||||
|
||||
|
||||
def resolve_cog_or_command(*, typ, ctx, objname, models: dict) -> bool:
|
||||
"""
|
||||
Resolves models in order.
|
||||
"""
|
||||
|
||||
resolved = None
|
||||
|
||||
if objname in models.get(typ, {}):
|
||||
blacklist = models[typ][objname].get("deny", [])
|
||||
whitelist = models[typ][objname].get("allow", [])
|
||||
resolved = resolve_lists(ctx=ctx, whitelist=whitelist, blacklist=blacklist)
|
||||
if resolved is not None:
|
||||
return resolved
|
||||
resolved = models[typ][objname].get("default", None)
|
||||
if resolved is not None:
|
||||
return resolved
|
||||
return None
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,81 +0,0 @@
|
||||
import types
|
||||
import contextlib
|
||||
import asyncio
|
||||
import logging
|
||||
from redbot.core import commands
|
||||
|
||||
log = logging.getLogger("redbot.cogs.permissions.resolvers")
|
||||
|
||||
|
||||
def entries_from_ctx(ctx: commands.Context) -> tuple:
|
||||
voice_channel = None
|
||||
with contextlib.suppress(Exception):
|
||||
voice_channel = ctx.author.voice.voice_channel
|
||||
entries = [x.id for x in (ctx.author, voice_channel, ctx.channel) if x]
|
||||
roles = sorted(ctx.author.roles, reverse=True) if ctx.guild else []
|
||||
entries.extend([x.id for x in roles])
|
||||
# entries now contains the following (in order) (if applicable)
|
||||
# author.id
|
||||
# author.voice.voice_channel.id
|
||||
# channel.id
|
||||
# role.id for each role (highest to lowest)
|
||||
# (implicitly) guild.id because
|
||||
# the @everyone role shares an id with the guild
|
||||
return tuple(entries)
|
||||
|
||||
|
||||
async def val_if_check_is_valid(*, ctx: commands.Context, check: object, level: str) -> bool:
|
||||
"""
|
||||
Returns the value from a check if it is valid
|
||||
"""
|
||||
|
||||
val = None
|
||||
# let's not spam the console with improperly made 3rd party checks
|
||||
try:
|
||||
if asyncio.iscoroutinefunction(check):
|
||||
val = await check(ctx, level=level)
|
||||
else:
|
||||
val = check(ctx, level=level)
|
||||
except Exception as e:
|
||||
# but still provide a way to view it (run with debug flag)
|
||||
log.debug(str(e))
|
||||
|
||||
return val
|
||||
|
||||
|
||||
def resolve_models(*, ctx: commands.Context, models: dict) -> bool:
|
||||
"""
|
||||
Resolves models in order.
|
||||
"""
|
||||
|
||||
cmd_name = ctx.command.qualified_name
|
||||
cog_name = ctx.cog.__class__.__name__
|
||||
|
||||
resolved = None
|
||||
|
||||
to_iter = (("commands", cmd_name), ("cogs", cog_name))
|
||||
|
||||
for model_name, ctx_attr in to_iter:
|
||||
if ctx_attr in models.get(model_name, {}):
|
||||
blacklist = models[model_name][ctx_attr].get("deny", [])
|
||||
whitelist = models[model_name][ctx_attr].get("allow", [])
|
||||
resolved = resolve_lists(ctx=ctx, whitelist=whitelist, blacklist=blacklist)
|
||||
if resolved is not None:
|
||||
return resolved
|
||||
resolved = models[model_name][ctx_attr].get("default", None)
|
||||
if resolved is not None:
|
||||
return resolved
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def resolve_lists(*, ctx: commands.Context, whitelist: list, blacklist: list) -> bool:
|
||||
"""
|
||||
resolves specific lists
|
||||
"""
|
||||
for entry in entries_from_ctx(ctx):
|
||||
if entry in whitelist:
|
||||
return True
|
||||
if entry in blacklist:
|
||||
return False
|
||||
return None
|
||||
@@ -1,19 +0,0 @@
|
||||
cogs:
|
||||
Admin:
|
||||
allow:
|
||||
- 78631113035100160
|
||||
deny:
|
||||
- 96733288462286848
|
||||
Audio:
|
||||
allow:
|
||||
- 133049272517001216
|
||||
default: deny
|
||||
commands:
|
||||
cleanup bot:
|
||||
allow:
|
||||
- 78631113035100160
|
||||
default: deny
|
||||
ping:
|
||||
deny:
|
||||
- 96733288462286848
|
||||
default: allow
|
||||
@@ -1,67 +0,0 @@
|
||||
import io
|
||||
import yaml
|
||||
import pathlib
|
||||
import discord
|
||||
|
||||
|
||||
def yaml_template() -> dict:
|
||||
template_fp = pathlib.Path(__file__).parent / "template.yaml"
|
||||
|
||||
with template_fp.open() as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
async def yamlset_acl(ctx, *, config, update):
|
||||
_fp = io.BytesIO()
|
||||
await ctx.message.attachments[0].save(_fp)
|
||||
|
||||
try:
|
||||
data = yaml.safe_load(_fp)
|
||||
except yaml.YAMLError:
|
||||
_fp.close()
|
||||
del _fp
|
||||
raise
|
||||
|
||||
old_data = await config()
|
||||
|
||||
for outer, inner in data.items():
|
||||
for ok, iv in inner.items():
|
||||
for k, v in iv.items():
|
||||
if k == "default":
|
||||
data[outer][ok][k] = {"allow": True, "deny": False}.get(v.lower(), None)
|
||||
|
||||
if not update:
|
||||
continue
|
||||
try:
|
||||
if isinstance(old_data[outer][ok][k], list):
|
||||
data[outer][ok][k].extend(old_data[outer][ok][k])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
await config.set(data)
|
||||
|
||||
|
||||
async def yamlget_acl(ctx, *, config):
|
||||
data = await config()
|
||||
removals = []
|
||||
|
||||
for outer, inner in data.items():
|
||||
for ok, iv in inner.items():
|
||||
for k, v in iv.items():
|
||||
if k != "default":
|
||||
continue
|
||||
if v is True:
|
||||
data[outer][ok][k] = "allow"
|
||||
elif v is False:
|
||||
data[outer][ok][k] = "deny"
|
||||
else:
|
||||
removals.append((outer, ok, k))
|
||||
|
||||
for tup in removals:
|
||||
o, i, k = tup
|
||||
data[o][i].pop(k, None)
|
||||
|
||||
_fp = io.BytesIO(yaml.dump(data, default_flow_style=False).encode())
|
||||
_fp.seek(0)
|
||||
await ctx.author.send(file=discord.File(_fp, filename="acl.yaml"))
|
||||
_fp.close()
|
||||
@@ -20,7 +20,7 @@ log = logging.getLogger("red.reports")
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Reports:
|
||||
class Reports(commands.Cog):
|
||||
|
||||
default_guild_settings = {"output_channel": None, "active": False, "next_ticket": 1}
|
||||
|
||||
@@ -40,6 +40,7 @@ class Reports:
|
||||
]
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, 78631113035100160, force_registration=True)
|
||||
self.config.register_guild(**self.default_guild_settings)
|
||||
|
||||
@@ -35,7 +35,7 @@ _ = Translator("Streams", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Streams:
|
||||
class Streams(commands.Cog):
|
||||
|
||||
global_defaults = {"tokens": {}, "streams": [], "communities": []}
|
||||
|
||||
@@ -44,6 +44,7 @@ class Streams:
|
||||
role_defaults = {"mention": False}
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.db = Config.get_conf(self, 26262626)
|
||||
|
||||
self.db.register_global(**self.global_defaults)
|
||||
|
||||
@@ -23,10 +23,11 @@ class InvalidListError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Trivia:
|
||||
class Trivia(commands.Cog):
|
||||
"""Play trivia with friends!"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.trivia_sessions = []
|
||||
self.conf = Config.get_conf(self, identifier=UNIQUE_ID, force_registration=True)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ _ = Translator("Warnings", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Warnings:
|
||||
class Warnings(commands.Cog):
|
||||
"""A warning system for Red"""
|
||||
|
||||
default_guild = {"actions": [], "reasons": {}, "allow_custom_reasons": False}
|
||||
@@ -28,6 +28,7 @@ class Warnings:
|
||||
default_member = {"total_points": 0, "status": "", "warnings": {}}
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
self.config = Config.get_conf(self, identifier=5757575755)
|
||||
self.config.register_guild(**self.default_guild)
|
||||
self.config.register_member(**self.default_member)
|
||||
|
||||
Reference in New Issue
Block a user