diff options
Diffstat (limited to '')
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | bot/constants.py | 1106 | ||||
| -rw-r--r-- | bot/exts/backend/branding/_cog.py | 2 | ||||
| -rw-r--r-- | bot/exts/backend/config_verifier.py | 4 | ||||
| -rw-r--r-- | bot/exts/filters/antispam.py | 14 | ||||
| -rw-r--r-- | bot/exts/fun/duck_pond.py | 2 | ||||
| -rw-r--r-- | bot/exts/help_channels/_channel.py | 4 | ||||
| -rw-r--r-- | bot/exts/help_channels/_cog.py | 4 | ||||
| -rw-r--r-- | bot/exts/help_channels/_stats.py | 2 | ||||
| -rw-r--r-- | bot/exts/info/codeblock/_cog.py | 2 | ||||
| -rw-r--r-- | bot/exts/moderation/incidents.py | 6 | ||||
| -rw-r--r-- | bot/exts/moderation/watchchannels/_watchchannel.py | 2 | ||||
| -rw-r--r-- | bot/exts/moderation/watchchannels/bigbrother.py | 4 | ||||
| -rw-r--r-- | bot/exts/recruitment/talentpool/_review.py | 2 | ||||
| -rw-r--r-- | botstrap.py | 164 | ||||
| -rw-r--r-- | config-default.yml | 560 | ||||
| -rw-r--r-- | docker-compose.yml | 7 | ||||
| -rw-r--r-- | poetry.lock | 269 | ||||
| -rw-r--r-- | pyproject.toml | 4 | 
19 files changed, 863 insertions, 1296 deletions
| diff --git a/.gitignore b/.gitignore index 6691dbea1..65a9af431 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,4 @@ TEST-**.xml  # Mac OS .DS_Store, which is a file that stores custom attributes of its containing folder  .DS_Store +*.env* diff --git a/bot/constants.py b/bot/constants.py index f1fb5471f..5e5173a63 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -1,703 +1,726 @@  """ -Loads bot configuration from YAML files. -By default, this simply loads the default -configuration located at `config-default.yml`. -If a file called `config.yml` is found in the -project directory, the default configuration -is recursively updated with any settings from -the custom configuration. Any settings left -out in the custom user configuration will stay -their default values from `config-default.yml`. +Loads bot configuration from environment variables +and `.env` files. By default, this simply loads the +default configuration defined thanks to the `default` +keyword argument in each instance of the `Field` class +If two files called `.env` and `.env.server` are found +in the project directory, the values will be loaded +from both of them, thus overlooking the predefined defaults. +Any settings left out in the custom user configuration +will default to the values passed to the `default` kwarg.  """  import os -from collections.abc import Mapping  from enum import Enum -from pathlib import Path -from typing import Dict, List, Optional +from typing import Optional -import yaml +from pydantic import BaseModel, BaseSettings, root_validator -try: -    import dotenv -    dotenv.load_dotenv() -except ModuleNotFoundError: -    pass +class EnvConfig(BaseSettings): +    class Config: +        env_file = ".env", ".env.server", +        env_file_encoding = 'utf-8' -def _env_var_constructor(loader, node): -    """ -    Implements a custom YAML tag for loading optional environment -    variables. If the environment variable is set, returns the -    value of it. Otherwise, returns `None`. -    Example usage in the YAML configuration: +class _Miscellaneous(EnvConfig): +    debug = True +    file_logs = False -        # Optional app configuration. Set `MY_APP_KEY` in the environment to use it. -        application: -            key: !ENV 'MY_APP_KEY' -    """ -    default = None +Miscellaneous = _Miscellaneous() -    # Check if the node is a plain string value -    if node.id == 'scalar': -        value = loader.construct_scalar(node) -        key = str(value) -    else: -        # The node value is a list -        value = loader.construct_sequence(node) -        if len(value) >= 2: -            # If we have at least two values, then we have both a key and a default value -            default = value[1] -            key = value[0] -        else: -            # Otherwise, we just have a key -            key = value[0] +FILE_LOGS = Miscellaneous.file_logs +DEBUG_MODE = Miscellaneous.debug -    return os.getenv(key, default) +class _Bot(EnvConfig): +    EnvConfig.Config.env_prefix = "bot_" -def _join_var_constructor(loader, node): -    """ -    Implements a custom YAML tag for concatenating other tags in -    the document to strings. This allows for a much more DRY configuration -    file. -    """ +    prefix = "!" +    sentry_dsn = "" +    token = "" +    trace_loggers = "*" -    fields = loader.construct_sequence(node) -    return "".join(str(x) for x in fields) +Bot = _Bot() -yaml.SafeLoader.add_constructor("!ENV", _env_var_constructor) -yaml.SafeLoader.add_constructor("!JOIN", _join_var_constructor) -# Pointing old tag to !ENV constructor to avoid breaking existing configs -yaml.SafeLoader.add_constructor("!REQUIRED_ENV", _env_var_constructor) +class _Channels(EnvConfig): +    EnvConfig.Config.env_prefix = "channels_" +    announcements = 354619224620138496 +    changelog = 748238795236704388 +    mailing_lists = 704372456592506880 +    python_events = 729674110270963822 +    python_news = 704372456592506880 +    reddit = 458224812528238616 -with open("config-default.yml", encoding="UTF-8") as f: -    _CONFIG_YAML = yaml.safe_load(f) +    dev_contrib = 635950537262759947 +    dev_core = 411200599653351425 +    dev_log = 622895325144940554 +    meta = 429409067623251969 +    python_general = 267624335836053506 -def _recursive_update(original, new): -    """ -    Helper method which implements a recursive `dict.update` -    method, used for updating the original configuration with -    configuration specified by the user. -    """ +    python_help = 1035199133436354600 -    for key, value in original.items(): -        if key not in new: -            continue +    attachment_log = 649243850006855680 +    filter_log = 1014943924185473094 +    message_log = 467752170159079424 +    mod_log = 282638479504965634 +    nomination_voting_archive = 833371042046148738 +    user_log = 528976905546760203 +    voice_log = 640292421988646961 -        if isinstance(value, Mapping): -            if not any(isinstance(subvalue, Mapping) for subvalue in value.values()): -                original[key].update(new[key]) -            _recursive_update(original[key], new[key]) -        else: -            original[key] = new[key] +    off_topic_0 = 291284109232308226 +    off_topic_1 = 463035241142026251 +    off_topic_2 = 463035268514185226 +    bot_commands = 267659945086812160 +    discord_bots = 343944376055103488 +    esoteric = 470884583684964352 +    voice_gate = 764802555427029012 +    code_jam_planning = 490217981872177157 -if Path("config.yml").exists(): -    print("Found `config.yml` file, loading constants from it.") -    with open("config.yml", encoding="UTF-8") as f: -        user_config = yaml.safe_load(f) -    _recursive_update(_CONFIG_YAML, user_config) +    # Staff +    admins = 365960823622991872 +    admin_spam = 563594791770914816 +    defcon = 464469101889454091 +    helpers = 385474242440986624 +    incidents = 714214212200562749 +    incidents_archive = 720668923636351037 +    mod_alerts = 473092532147060736 +    mod_meta = 775412552795947058 +    mods = 305126844661760000 +    nominations = 822920136150745168 +    nomination_voting = 822853512709931008 +    organisation = 551789653284356126 +    # Staff announcement channels +    admin_announcements = 749736155569848370 +    mod_announcements = 372115205867700225 +    staff_announcements = 464033278631084042 +    staff_info = 396684402404622347 +    staff_lounge = 464905259261755392 -def check_required_keys(keys): -    """ -    Verifies that keys that are set to be required are present in the -    loaded configuration. -    """ -    for key_path in keys: -        lookup = _CONFIG_YAML -        try: -            for key in key_path.split('.'): -                lookup = lookup[key] -                if lookup is None: -                    raise KeyError(key) -        except KeyError: -            raise KeyError( -                f"A configuration for `{key_path}` is required, but was not found. " -                "Please set it in `config.yml` or setup an environment variable and try again." -            ) - - -try: -    required_keys = _CONFIG_YAML['config']['required_keys'] -except KeyError: -    pass -else: -    check_required_keys(required_keys) - - -class YAMLGetter(type): +    # Voice Channels +    admins_voice = 500734494840717332 +    code_help_voice_0 = 751592231726481530 +    code_help_voice_1 = 764232549840846858 +    general_voice_0 = 751591688538947646 +    general_voice_1 = 799641437645701151 +    staff_voice = 412375055910043655 + +    black_formatter = 846434317021741086 + +    # Voice Chat +    code_help_chat_0 = 755154969761677312 +    code_help_chat_1 = 766330079135268884 +    staff_voice_chat = 541638762007101470 +    voice_chat_0 = 412357430186344448 +    voice_chat_1 = 799647045886541885 + +    big_brother = 468507907357409333 +    duck_pond = 637820308341915648 +    roles = 851270062434156586 + + +Channels = _Channels() + + +class _Roles(EnvConfig): + +    EnvConfig.Config.env_prefix = "roles_" + +    # Self-assignable roles, see the Subscribe cog +    advent_of_code = 518565788744024082 +    announcements = 463658397560995840 +    lovefest = 542431903886606399 +    pyweek_announcements = 897568414044938310 +    revival_of_code = 988801794668908655 +    legacy_help_channels_access = 1074780483776417964 + +    contributors = 295488872404484098 +    help_cooldown = 699189276025421825 +    muted = 277914926603829249 +    partners = 323426753857191936 +    python_community = 458226413825294336 +    voice_verified = 764802720779337729 + +    # Streaming +    video = 764245844798079016 + +    # Staff +    admins = 267628507062992896 +    core_developers = 587606783669829632 +    code_jam_event_team = 787816728474288181 +    devops = 409416496733880320 +    domain_leads = 807415650778742785 +    events_lead = 778361735739998228 +    helpers = 267630620367257601 +    moderators = 831776746206265384 +    mod_team = 267629731250176001 +    owners = 267627879762755584 +    project_leads = 815701647526330398 + +    # Code Jam +    jammers = 737249140966162473 + +    # Patreon +    patreon_tier_1 = 505040943800516611 +    patreon_tier_2 = 743399725914390631 +    patreon_tier_3 = 743400204367036520 + + +Roles = _Roles() + + +class _Categories(EnvConfig): +    EnvConfig.Config.env_prefix = "categories_" + +    logs = 468520609152892958 +    moderators = 749736277464842262 +    modmail = 714494672835444826 +    appeals = 890331800025563216 +    appeals_2 = 895417395261341766 +    voice = 356013253765234688 + +    # 2021 Summer Code Jam +    summer_code_jam = 861692638540857384 + + +Categories = _Categories() + + +class _Guild(EnvConfig): +    EnvConfig.Config.env_prefix = "guild_" + +    id = 267624335836053506 +    invite = "https://discord.gg/python" + +    moderation_categories = [ +        Categories.moderators, +        Categories.modmail, +        Categories.logs, +        Categories.appeals, +        Categories.appeals_2 +    ] +    moderation_channels = [Channels.admins, Channels.admin_spam, Channels.mods] +    modlog_blacklist = [ +        Channels.attachment_log, +        Channels.message_log, +        Channels.mod_log, +        Channels.staff_voice, +        Channels.filter_log +    ] +    reminder_whitelist = [Channels.bot_commands, Channels.dev_contrib, Channels.black_formatter] +    moderation_roles = [Roles.admins, Roles.mod_team, Roles.moderators, Roles.owners] +    staff_roles = [Roles.admins, Roles.helpers, Roles.mod_team, Roles.owners] + + +Guild = _Guild() + + +class Event(Enum):      """ -    Implements a custom metaclass used for accessing -    configuration data by simply accessing class attributes. -    Supports getting configuration from up to two levels -    of nested configuration through `section` and `subsection`. - -    `section` specifies the YAML configuration section (or "key") -    in which the configuration lives, and must be set. - -    `subsection` is an optional attribute specifying the section -    within the section from which configuration should be loaded. - -    Example Usage: - -        # config.yml -        bot: -            prefixes: -                direct_message: '' -                guild: '!' - -        # config.py -        class Prefixes(metaclass=YAMLGetter): -            section = "bot" -            subsection = "prefixes" - -        # Usage in Python code -        from config import Prefixes -        def get_prefix(bot, message): -            if isinstance(message.channel, PrivateChannel): -                return Prefixes.direct_message -            return Prefixes.guild +    Event names. This does not include every event (for example, raw +    events aren't here), but only events used in ModLog for now.      """ -    subsection = None +    guild_channel_create = "guild_channel_create" +    guild_channel_delete = "guild_channel_delete" +    guild_channel_update = "guild_channel_update" +    guild_role_create = "guild_role_create" +    guild_role_delete = "guild_role_delete" +    guild_role_update = "guild_role_update" +    guild_update = "guild_update" + +    member_join = "member_join" +    member_remove = "member_remove" +    member_ban = "member_ban" +    member_unban = "member_unban" +    member_update = "member_update" -    def __getattr__(cls, name): -        name = name.lower() +    message_delete = "message_delete" +    message_edit = "message_edit" -        try: -            if cls.subsection is not None: -                return _CONFIG_YAML[cls.section][cls.subsection][name] -            return _CONFIG_YAML[cls.section][name] -        except KeyError as e: -            dotted_path = '.'.join( -                (cls.section, cls.subsection, name) -                if cls.subsection is not None else (cls.section, name) -            ) -            print(f"Tried accessing configuration variable at `{dotted_path}`, but it could not be found.") -            raise AttributeError(repr(name)) from e +    voice_state_update = "voice_state_update" -    def __getitem__(cls, name): -        return cls.__getattr__(name) -    def __iter__(cls): -        """Return generator of key: value pairs of current constants class' config values.""" -        for name in cls.__annotations__: -            yield name, getattr(cls, name) +class ThreadArchiveTimes(Enum): +    HOUR = 60 +    DAY = 1440 +    THREE_DAY = 4320 +    WEEK = 10080 -# Dataclasses -class Bot(metaclass=YAMLGetter): -    section = "bot" +class Webhook(BaseModel): +    id: int +    channel: Optional[int] -    prefix: str -    sentry_dsn: Optional[str] -    token: str -    trace_loggers: Optional[str] +class _Webhooks(EnvConfig): +    EnvConfig.Config.env_prefix = "webhooks_" +    EnvConfig.Config.env_nested_delimiter = '_' -class Redis(metaclass=YAMLGetter): -    section = "bot" -    subsection = "redis" +    big_brother: Webhook = Webhook(id=569133704568373283, channel=Channels.big_brother) +    dev_log: Webhook = Webhook(id=680501655111729222, channel=Channels.dev_log) +    duck_pond: Webhook = Webhook(id=637821475327311927, channel=Channels.duck_pond) +    incidents: Webhook = Webhook(id=816650601844572212, channel=Channels.incidents) +    incidents_archive: Webhook = Webhook(id=720671599790915702, channel=Channels.incidents_archive) +    python_news: Webhook = Webhook(id=704381182279942324, channel=Channels.python_news) -    host: str -    password: Optional[str] -    port: int -    use_fakeredis: bool  # If this is True, Bot will use fakeredis.aioredis +Webhooks = _Webhooks() -class Filter(metaclass=YAMLGetter): -    section = "filter" -    filter_domains: bool -    filter_everyone_ping: bool -    filter_invites: bool -    filter_zalgo: bool -    watch_regex: bool -    watch_rich_embeds: bool +class _BigBrother(EnvConfig): +    EnvConfig.Config.env_prefix = "big_brother_" -    # Notifications are not expected for "watchlist" type filters +    header_message_limit = 15 +    log_delay = 15 -    notify_user_domains: bool -    notify_user_everyone_ping: bool -    notify_user_invites: bool -    notify_user_zalgo: bool -    offensive_msg_delete_days: int -    ping_everyone: bool +BigBrother = _BigBrother() -    channel_whitelist: List[int] -    role_whitelist: List[int] +class _CodeBlock(EnvConfig): +    EnvConfig.Config.env_prefix = "code_block_" -class Cooldowns(metaclass=YAMLGetter): -    section = "bot" -    subsection = "cooldowns" +    # The channels in which code blocks will be detected. They are not subject to a cooldown. +    channel_whitelist: list[int] = [Channels.bot_commands] +    # The channels which will be affected by a cooldown. These channels are also whitelisted. +    cooldown_channels: list[int] = [Channels.python_general] -    tags: int +    cooldown_seconds = 300 +    minimum_lines = 4 -class Colours(metaclass=YAMLGetter): -    section = "style" -    subsection = "colours" +CodeBlock = _CodeBlock() -    blue: int -    bright_green: int -    orange: int -    pink: int -    purple: int -    soft_green: int -    soft_orange: int -    soft_red: int -    white: int -    yellow: int +class _Colours(EnvConfig): +    EnvConfig.Config.env_prefix = "colours_" -class DuckPond(metaclass=YAMLGetter): -    section = "duck_pond" +    blue = 0x3775a8 +    bright_green = 0x01d277 +    orange = 0xe67e22 +    pink = 0xcf84e0 +    purple = 0xb734eb +    soft_green = 0x68c290 +    soft_orange = 0xf9cb54 +    soft_red = 0xcd6d6d +    white = 0xfffffe +    yellow = 0xffd241 -    threshold: int -    channel_blacklist: List[int] +    @root_validator(pre=True) +    def parse_hex_values(cls, values): +        for key, value in values.items(): +            values[key] = int(value, 16) +        return values -class Emojis(metaclass=YAMLGetter): -    section = "style" -    subsection = "emojis" +Colours = _Colours() -    badge_bug_hunter: str -    badge_bug_hunter_level_2: str -    badge_early_supporter: str -    badge_hypesquad: str -    badge_hypesquad_balance: str -    badge_hypesquad_bravery: str -    badge_hypesquad_brilliance: str -    badge_partner: str -    badge_staff: str -    badge_verified_bot_developer: str -    verified_bot: str -    bot: str -    defcon_shutdown: str  # noqa: E704 -    defcon_unshutdown: str  # noqa: E704 -    defcon_update: str  # noqa: E704 +class _Free(EnvConfig): +    EnvConfig.Config.env_prefix = "free_" -    failmail: str +    activity_timeout = 600 +    cooldown_per = 60.0 +    cooldown_rate = 1 -    incident_actioned: str -    incident_investigating: str -    incident_unactioned: str -    status_dnd: str -    status_idle: str -    status_offline: str -    status_online: str +Free = _Free() -    ducky_dave: str -    trashcan: str +class Punishment(BaseModel): +    remove_after = 600 +    role_id: int = Roles.muted -    bullet: str -    check_mark: str -    cross_mark: str -    new: str -    pencil: str -    ok_hand: str +class Rule(BaseModel): +    interval: int +    max: int -class Icons(metaclass=YAMLGetter): -    section = "style" -    subsection = "icons" +# Some help in choosing an appropriate name for this is appreciated +class ExtendedRule(Rule): +    max_consecutive: int -    crown_blurple: str -    crown_green: str -    crown_red: str -    defcon_denied: str    # noqa: E704 -    defcon_shutdown: str  # noqa: E704 -    defcon_unshutdown: str   # noqa: E704 -    defcon_update: str   # noqa: E704 +class Rules(BaseModel): +    attachments: Rule = Rule(interval=10, max=10) +    burst: Rule = Rule(interval=10, max=7) +    chars: Rule = Rule(interval=5, max=200) +    discord_emojis: Rule = Rule(interval=10, max=20) +    duplicates: Rule = Rule(interval=10, max=3) +    links: Rule = Rule(interval=10, max=10) +    mentions: Rule = Rule(interval=10, max=5) +    newlines: ExtendedRule = ExtendedRule(interval=10, max=100, max_consecutive=10) +    role_mentions: Rule = Rule(interval=10, max=3) -    filtering: str -    green_checkmark: str -    green_questionmark: str -    guild_update: str +class _AntiSpam(EnvConfig): +    EnvConfig.Config.env_prefix = 'anti_spam_' +    EnvConfig.Config.env_nested_delimiter = '_' -    hash_blurple: str -    hash_green: str -    hash_red: str +    cache_size = 100 -    message_bulk_delete: str -    message_delete: str -    message_edit: str +    clean_offending = True +    ping_everyone = True -    pencil: str +    punishment = Punishment() +    rules = Rules() -    questionmark: str -    remind_blurple: str -    remind_green: str -    remind_red: str +AntiSpam = _AntiSpam() -    sign_in: str -    sign_out: str -    superstarify: str -    unsuperstarify: str +class _HelpChannels(EnvConfig): +    EnvConfig.Config.env_prefix = "help_channels_" -    token_removed: str +    enable = True +    idle_minutes = 30 +    deleted_idle_minutes = 5 +    # Roles which are allowed to use the command which makes channels dormant +    cmd_whitelist: list[int] = [Roles.helpers] -    user_ban: str -    user_mute: str -    user_unban: str -    user_unmute: str -    user_update: str -    user_verified: str -    user_warn: str -    voice_state_blue: str -    voice_state_green: str -    voice_state_red: str +HelpChannels = _HelpChannels() -class CleanMessages(metaclass=YAMLGetter): -    section = "bot" -    subsection = "clean" +class _RedirectOutput(EnvConfig): +    EnvConfig.Config.env_prefix = "redirect_output_" -    message_limit: int +    delete_delay = 15 +    delete_invocation = True -class Stats(metaclass=YAMLGetter): -    section = "bot" -    subsection = "stats" +RedirectOutput = _RedirectOutput() -    presence_update_timeout: int -    statsd_host: str +class _DuckPond(EnvConfig): +    EnvConfig.Config.env_prefix = "duck_pond_" -class Categories(metaclass=YAMLGetter): -    section = "guild" -    subsection = "categories" +    threshold = 7 -    moderators: int -    modmail: int -    voice: int +    channel_blacklist: list[str] = [ +        Channels.announcements, +        Channels.python_news, +        Channels.python_events, +        Channels.mailing_lists, +        Channels.reddit, +        Channels.duck_pond, +        Channels.changelog, +        Channels.staff_announcements, +        Channels.mod_announcements, +        Channels.admin_announcements, +        Channels.staff_info +    ] -    # 2021 Summer Code Jam -    summer_code_jam: int - - -class Channels(metaclass=YAMLGetter): -    section = "guild" -    subsection = "channels" - -    announcements: int -    change_log: int -    mailing_lists: int -    python_events: int -    python_news: int -    reddit: int - -    dev_contrib: int -    dev_core: int -    dev_log: int - -    meta: int -    python_general: int - -    help_system_forum: int - -    attachment_log: int -    filter_log: int -    message_log: int -    mod_log: int -    nomination_archive: int -    user_log: int -    voice_log: int - -    off_topic_0: int -    off_topic_1: int -    off_topic_2: int - -    bot_commands: int -    discord_bots: int -    esoteric: int -    voice_gate: int -    code_jam_planning: int - -    admins: int -    admin_spam: int -    defcon: int -    helpers: int -    incidents: int -    incidents_archive: int -    mod_alerts: int -    mod_meta: int -    mods: int -    nominations: int -    nomination_voting: int -    organisation: int - -    admin_announcements: int -    mod_announcements: int -    staff_announcements: int - -    admins_voice: int -    code_help_voice_0: int -    code_help_voice_1: int -    general_voice_0: int -    general_voice_1: int -    staff_voice: int - -    code_help_chat_0: int -    code_help_chat_1: int -    staff_voice_chat: int -    voice_chat_0: int -    voice_chat_1: int - -    big_brother_logs: int - -    roles: int - - -class Webhooks(metaclass=YAMLGetter): -    section = "guild" -    subsection = "webhooks" - -    big_brother: int -    dev_log: int -    duck_pond: int -    incidents: int -    incidents_archive: int - - -class Roles(metaclass=YAMLGetter): -    section = "guild" -    subsection = "roles" -    # Self-assignable roles, see the Subscribe cog -    advent_of_code: int -    announcements: int -    lovefest: int -    pyweek_announcements: int -    revival_of_code: int -    legacy_help_channels_access: int - -    contributors: int -    help_cooldown: int -    muted: int -    partners: int -    python_community: int -    sprinters: int -    voice_verified: int -    video: int - -    admins: int -    core_developers: int -    code_jam_event_team: int -    devops: int -    domain_leads: int -    events_lead: int -    helpers: int -    moderators: int -    mod_team: int -    owners: int -    project_leads: int - -    jammers: int - -    patreon_tier_1: int -    patreon_tier_2: int -    patreon_tier_3: int - - -class Guild(metaclass=YAMLGetter): -    section = "guild" +DuckPond = _DuckPond() -    id: int -    invite: str  # Discord invite, gets embedded in chat -    moderation_categories: List[int] -    moderation_channels: List[int] -    modlog_blacklist: List[int] -    reminder_whitelist: List[int] -    moderation_roles: List[int] -    staff_roles: List[int] +class _PythonNews(EnvConfig): +    EnvConfig.Config.env_prefix = "python_news_" + +    channel: int = Webhooks.python_news.channel +    webhook: int = Webhooks.python_news.id +    mail_lists = ['python-ideas', 'python-announce-list', 'pypi-announce', 'python-dev'] + + +PythonNews = _PythonNews() + + +class _VoiceGate(EnvConfig): +    EnvConfig.Config.env_prefix = "voice_gate_" + +    bot_message_delete_delay = 10 +    minimum_activity_blocks = 3 +    minimum_days_member = 3 +    minimum_messages = 50 +    voice_ping_delete_delay = 60 + + +VoiceGate = _VoiceGate() + + +class _Branding(EnvConfig): +    EnvConfig.Config.env_prefix = "branding_" + +    cycle_frequency = 3 -class Keys(metaclass=YAMLGetter): -    section = "keys" +Branding = _Branding() -    github: Optional[str] -    site_api: Optional[str] +class _VideoPermission(EnvConfig): +    EnvConfig.Config.env_prefix = "video_permission_" -class URLs(metaclass=YAMLGetter): -    section = "urls" +    default_permission_duration = 5 + + +VideoPermission = _VideoPermission() + + +class _Redis(EnvConfig): +    EnvConfig.Config.env_prefix = "redis_" + +    host = "redis.default.svc.cluster.local" +    password = "" +    port = 6379 +    use_fakeredis = False  # If this is True, Bot will use fakeredis.aioredis + + +Redis = _Redis() + + +class _CleanMessages(EnvConfig): +    EnvConfig.Config.env_prefix = "clean_" + +    message_limit = 10_000 + + +CleanMessages = _CleanMessages() + + +class _Stats(EnvConfig): +    EnvConfig.Config.env_prefix = "stats_" + +    presence_update_timeout = 30 +    statsd_host = "graphite.default.svc.cluster.local" + + +Stats = _Stats() + + +class _Cooldowns(EnvConfig): +    EnvConfig.Config.env_prefix = "cooldowns_" + +    tags = 60 + + +Cooldowns = _Cooldowns() + + +class _Metabase(EnvConfig): +    EnvConfig.Config.env_prefix = "metabase_" + +    username = "" +    password = "" +    base_url = "http://metabase.default.svc.cluster.local" +    public_url = "https://metabase.pythondiscord.com" +    max_session_age = 20_160 + + +Metabase = _Metabase() + + +class _BaseURLs(EnvConfig): +    EnvConfig.Config.env_prefix = "urls_"      # Snekbox endpoints -    snekbox_eval_api: str -    snekbox_311_eval_api: str +    snekbox_eval_api = "http://snekbox-310.default.svc.cluster.local/eval" +    snekbox_311_eval_api = "http://snekbox.default.svc.cluster.local/eval" -    # Discord API endpoints -    discord_api: str -    discord_invite_api: str +    # Discord API +    discord_api = "https://discordapp.com/api/v7/"      # Misc endpoints -    bot_avatar: str -    github_bot_repo: str +    bot_avatar = "https://raw.githubusercontent.com/python-discord/branding/main/logos/logo_circle/logo_circle.png" + +    github_bot_repo = "https://github.com/python-discord/bot" + +    # Site +    site = "pythondiscord.com" +    site_schema = "https://" +    site_api = "site.default.svc.cluster.local/api" +    site_api_schema = "http://" + + +BaseURLs = _BaseURLs() + + +class _URLs(_BaseURLs): + +    # Discord API endpoints +    discord_invite_api: str = "".join([BaseURLs.discord_api, "invites"])      # Base site vars -    connect_max_retries: int -    connect_cooldown: int -    site: str -    site_api: str -    site_schema: str -    site_api_schema: str +    connect_max_retries = 3 +    connect_cooldown = 5 + +    site_staff: str = "".join([BaseURLs.site_schema, BaseURLs.site, "/staff"]) +    site_paste = "".join(["paste.", BaseURLs.site])      # Site endpoints -    site_logs_view: str -    paste_service: str +    site_logs_view: str = "".join([BaseURLs.site_schema, BaseURLs.site, "/staff/bot/logs"]) +    paste_service: str = "".join([BaseURLs.site_schema, "paste.", BaseURLs.site, "/{key}"]) -class Metabase(metaclass=YAMLGetter): -    section = "metabase" +URLs = _URLs() -    username: Optional[str] -    password: Optional[str] -    base_url: str -    public_url: str -    max_session_age: int +class _Emojis(EnvConfig): +    EnvConfig.Config.env_prefix = "emojis_" -class AntiSpam(metaclass=YAMLGetter): -    section = 'anti_spam' +    badge_bug_hunter = "<:bug_hunter_lvl1:743882896372269137>" +    badge_bug_hunter_level_2 = "<:bug_hunter_lvl2:743882896611344505>" +    badge_early_supporter = "<:early_supporter:743882896909140058>" +    badge_hypesquad = "<:hypesquad_events:743882896892362873>" +    badge_hypesquad_balance = "<:hypesquad_balance:743882896460480625>" +    badge_hypesquad_bravery = "<:hypesquad_bravery:743882896745693335>" +    badge_hypesquad_brilliance = "<:hypesquad_brilliance:743882896938631248>" +    badge_partner = "<:partner:748666453242413136>" +    badge_staff = "<:discord_staff:743882896498098226>" +    badge_verified_bot_developer = "<:verified_bot_dev:743882897299210310>" +    verified_bot = "<:verified_bot:811645219220750347>" +    bot = "<:bot:812712599464443914>" -    cache_size: int +    defcon_shutdown = "<:defcondisabled:470326273952972810>"  # noqa: E704 +    defcon_unshutdown = "<:defconenabled:470326274213150730>"  # noqa: E704 +    defcon_update = "<:defconsettingsupdated:470326274082996224>"  # noqa: E704 -    clean_offending: bool -    ping_everyone: bool +    failmail = "<:failmail:633660039931887616>" -    punishment: Dict[str, Dict[str, int]] -    rules: Dict[str, Dict[str, int]] +    incident_actioned = "<:incident_actioned:714221559279255583>" +    incident_investigating = "<:incident_investigating:714224190928191551>" +    incident_unactioned = "<:incident_unactioned:714223099645526026>" +    status_dnd = "<:status_dnd:470326272082313216>" +    status_idle = "<:status_idle:470326266625785866>" +    status_offline = "<:status_offline:470326266537705472>" +    status_online = "<:status_online:470326272351010816>" -class BigBrother(metaclass=YAMLGetter): -    section = 'big_brother' +    ducky_dave = "<:ducky_dave:742058418692423772>" -    header_message_limit: int -    log_delay: int +    trashcan = "<:trashcan:637136429717389331>" +    bullet = "\u2022" +    check_mark = "\u2705" +    cross_mark = "\u274C" +    new = "\U0001F195" +    pencil = "\u270F" -class CodeBlock(metaclass=YAMLGetter): -    section = 'code_block' +    ok_hand = ":ok_hand:" -    channel_whitelist: List[int] -    cooldown_channels: List[int] -    cooldown_seconds: int -    minimum_lines: int +Emojis = _Emojis() -class Free(metaclass=YAMLGetter): -    section = 'free' -    activity_timeout: int -    cooldown_per: float -    cooldown_rate: int +class _Icons(EnvConfig): +    EnvConfig.Config.env_prefix = "icons_" +    crown_blurple = "https://cdn.discordapp.com/emojis/469964153289965568.png" +    crown_green = "https://cdn.discordapp.com/emojis/469964154719961088.png" +    crown_red = "https://cdn.discordapp.com/emojis/469964154879344640.png" -class HelpChannels(metaclass=YAMLGetter): -    section = 'help_channels' +    defcon_denied = "https://cdn.discordapp.com/emojis/472475292078964738.png"    # noqa: E704 +    defcon_shutdown = "https://cdn.discordapp.com/emojis/470326273952972810.png"  # noqa: E704 +    defcon_unshutdown = "https://cdn.discordapp.com/emojis/470326274213150730.png"   # noqa: E704 +    defcon_update = "https://cdn.discordapp.com/emojis/472472638342561793.png"   # noqa: E704 -    enable: bool -    idle_minutes: int -    deleted_idle_minutes: int -    cmd_whitelist: List[int] +    filtering = "https://cdn.discordapp.com/emojis/472472638594482195.png" +    green_checkmark = "https://raw.githubusercontent.com/python-discord/branding/main/icons/checkmark/green-checkmark-dist.png" +    green_questionmark = "https://raw.githubusercontent.com/python-discord/branding/main/icons/checkmark/green-question-mark-dist.png" +    guild_update = "https://cdn.discordapp.com/emojis/469954765141442561.png" -class RedirectOutput(metaclass=YAMLGetter): -    section = 'redirect_output' +    hash_blurple = "https://cdn.discordapp.com/emojis/469950142942806017.png" +    hash_green = "https://cdn.discordapp.com/emojis/469950144918585344.png" +    hash_red = "https://cdn.discordapp.com/emojis/469950145413251072.png" -    delete_delay: int -    delete_invocation: bool +    message_bulk_delete = "https://cdn.discordapp.com/emojis/469952898994929668.png" +    message_delete = "https://cdn.discordapp.com/emojis/472472641320648704.png" +    message_edit = "https://cdn.discordapp.com/emojis/472472638976163870.png" +    pencil = "https://cdn.discordapp.com/emojis/470326272401211415.png" -class PythonNews(metaclass=YAMLGetter): -    section = 'python_news' +    questionmark = "https://cdn.discordapp.com/emojis/512367613339369475.png" -    channel: int -    webhook: int -    mail_lists: List[str] +    remind_blurple = "https://cdn.discordapp.com/emojis/477907609215827968.png" +    remind_green = "https://cdn.discordapp.com/emojis/477907607785570310.png" +    remind_red = "https://cdn.discordapp.com/emojis/477907608057937930.png" +    sign_in = "https://cdn.discordapp.com/emojis/469952898181234698.png" +    sign_out = "https://cdn.discordapp.com/emojis/469952898089091082.png" -class VoiceGate(metaclass=YAMLGetter): -    section = "voice_gate" +    superstarify = "https://cdn.discordapp.com/emojis/636288153044516874.png" +    unsuperstarify = "https://cdn.discordapp.com/emojis/636288201258172446.png" -    bot_message_delete_delay: int -    minimum_activity_blocks: int -    minimum_days_member: int -    minimum_messages: int -    voice_ping_delete_delay: int +    token_removed = "https://cdn.discordapp.com/emojis/470326273298792469.png" +    user_ban = "https://cdn.discordapp.com/emojis/469952898026045441.png" +    user_mute = "https://cdn.discordapp.com/emojis/472472640100106250.png" +    user_unban = "https://cdn.discordapp.com/emojis/469952898692808704.png" +    user_unmute = "https://cdn.discordapp.com/emojis/472472639206719508.png" +    user_update = "https://cdn.discordapp.com/emojis/469952898684551168.png" +    user_verified = "https://cdn.discordapp.com/emojis/470326274519334936.png" +    user_warn = "https://cdn.discordapp.com/emojis/470326274238447633.png" -class Branding(metaclass=YAMLGetter): -    section = "branding" +    voice_state_blue = "https://cdn.discordapp.com/emojis/656899769662439456.png" +    voice_state_green = "https://cdn.discordapp.com/emojis/656899770094452754.png" +    voice_state_red = "https://cdn.discordapp.com/emojis/656899769905709076.png" -    cycle_frequency: int +Icons = _Icons() -class Event(Enum): -    """ -    Event names. This does not include every event (for example, raw -    events aren't here), but only events used in ModLog for now. -    """ -    guild_channel_create = "guild_channel_create" -    guild_channel_delete = "guild_channel_delete" -    guild_channel_update = "guild_channel_update" -    guild_role_create = "guild_role_create" -    guild_role_delete = "guild_role_delete" -    guild_role_update = "guild_role_update" -    guild_update = "guild_update" +class _Filter(EnvConfig): +    EnvConfig.Config.env_prefix = "filters_" -    member_join = "member_join" -    member_remove = "member_remove" -    member_ban = "member_ban" -    member_unban = "member_unban" -    member_update = "member_update" +    filter_domains = True +    filter_everyone_ping = True +    filter_invites = True +    filter_zalgo = False +    watch_regex = True +    watch_rich_embeds = True -    message_delete = "message_delete" -    message_edit = "message_edit" +    # Notifications are not expected for "watchlist" type filters -    voice_state_update = "voice_state_update" +    notify_user_domains = False +    notify_user_everyone_ping = True +    notify_user_invites = True +    notify_user_zalgo = False +    offensive_msg_delete_days = 7 +    ping_everyone = True -class VideoPermission(metaclass=YAMLGetter): -    section = "video_permission" +    channel_whitelist = [ +        Channels.admins, +        Channels.big_brother, +        Channels.dev_log, +        Channels.message_log, +        Channels.mod_log, +        Channels.staff_lounge +    ] +    role_whitelist = [ +        Roles.admins, +        Roles.helpers, +        Roles.moderators, +        Roles.owners, +        Roles.python_community, +        Roles.partners +    ] -    default_permission_duration: int +Filter = _Filter() -class ThreadArchiveTimes(Enum): -    HOUR = 60 -    DAY = 1440 -    THREE_DAY = 4320 -    WEEK = 10080 +class _Keys(EnvConfig): + +    EnvConfig.Config.env_prefix = "api_keys_" + +    github = "" +    site_api = "" + + +Keys = _Keys() -# Debug mode -DEBUG_MODE: bool = _CONFIG_YAML["debug"] == "true" -FILE_LOGS: bool = _CONFIG_YAML["file_logs"].lower() == "true" -# Paths  BOT_DIR = os.path.dirname(__file__)  PROJECT_ROOT = os.path.abspath(os.path.join(BOT_DIR, os.pardir)) @@ -715,6 +738,7 @@ MODERATION_CATEGORIES = Guild.moderation_categories  # Git SHA for Sentry  GIT_SHA = os.environ.get("GIT_SHA", "development") +  # Bot replies  NEGATIVE_REPLIES = [      "Noooooo!!", diff --git a/bot/exts/backend/branding/_cog.py b/bot/exts/backend/branding/_cog.py index ff2704835..94429c172 100644 --- a/bot/exts/backend/branding/_cog.py +++ b/bot/exts/backend/branding/_cog.py @@ -325,7 +325,7 @@ class Branding(commands.Cog):          # Notify guild of new event ~ this reads the information that we cached above.          if event_changed and not event.meta.is_fallback: -            await self.send_info_embed(Channels.change_log, is_notification=True) +            await self.send_info_embed(Channels.changelog, is_notification=True)          else:              log.trace("Omitting #changelog notification. Event has not changed, or new event is fallback.") diff --git a/bot/exts/backend/config_verifier.py b/bot/exts/backend/config_verifier.py index 97c8869a1..84ae5ca92 100644 --- a/bot/exts/backend/config_verifier.py +++ b/bot/exts/backend/config_verifier.py @@ -24,12 +24,12 @@ class ConfigVerifier(Cog):          server_channel_ids = {channel.id for channel in server.channels}          invalid_channels = [ -            channel_name for channel_name, channel_id in constants.Channels +            (channel_name, channel_id) for channel_name, channel_id in constants.Channels              if channel_id not in server_channel_ids          ]          if invalid_channels: -            log.warning(f"Configured channels do not exist in server: {', '.join(invalid_channels)}.") +            log.warning(f"Configured channels do not exist in server: {invalid_channels}.")  async def setup(bot: Bot) -> None: diff --git a/bot/exts/filters/antispam.py b/bot/exts/filters/antispam.py index d7783292d..4d2e67a31 100644 --- a/bot/exts/filters/antispam.py +++ b/bot/exts/filters/antispam.py @@ -41,6 +41,8 @@ RULE_FUNCTION_MAPPING = {      'role_mentions': rules.apply_role_mentions,  } +ANTI_SPAM_RULES = AntiSpamConfig.rules.dict() +  @dataclass  class DeletionContext: @@ -121,7 +123,7 @@ class AntiSpam(Cog):      def __init__(self, bot: Bot, validation_errors: Dict[str, str]) -> None:          self.bot = bot          self.validation_errors = validation_errors -        role_id = AntiSpamConfig.punishment['role_id'] +        role_id = AntiSpamConfig.punishment.role_id          self.muted_role = Object(role_id)          self.expiration_date_converter = Duration() @@ -129,7 +131,7 @@ class AntiSpam(Cog):          # Fetch the rule configuration with the highest rule interval.          max_interval_config = max( -            AntiSpamConfig.rules.values(), +            ANTI_SPAM_RULES.values(),              key=itemgetter('interval')          )          self.max_interval = max_interval_config['interval'] @@ -178,8 +180,7 @@ class AntiSpam(Cog):          earliest_relevant_at = arrow.utcnow() - timedelta(seconds=self.max_interval)          relevant_messages = list(takewhile(lambda msg: msg.created_at > earliest_relevant_at, self.cache)) -        for rule_name in AntiSpamConfig.rules: -            rule_config = AntiSpamConfig.rules[rule_name] +        for rule_name, rule_config in ANTI_SPAM_RULES.items():              rule_function = RULE_FUNCTION_MAPPING[rule_name]              # Create a list of messages that were sent in the interval that the rule cares about. @@ -229,7 +230,7 @@ class AntiSpam(Cog):      async def punish(self, msg: Message, member: Member, reason: str) -> None:          """Punishes the given member for triggering an antispam rule."""          if not any(role.id == self.muted_role.id for role in member.roles): -            remove_role_after = AntiSpamConfig.punishment['remove_after'] +            remove_role_after = AntiSpamConfig.punishment.remove_after              # Get context and make sure the bot becomes the actor of infraction by patching the `author` attributes              context = await self.bot.get_context(msg) @@ -297,10 +298,11 @@ class AntiSpam(Cog):          self.cache.update(after) -def validate_config(rules_: Mapping = AntiSpamConfig.rules) -> Dict[str, str]: +def validate_config(rules_: Mapping = ANTI_SPAM_RULES) -> Dict[str, str]:      """Validates the antispam configs."""      validation_errors = {}      for name, config in rules_.items(): +        config = config          if name not in RULE_FUNCTION_MAPPING:              log.error(                  f"Unrecognized antispam rule `{name}`. " diff --git a/bot/exts/fun/duck_pond.py b/bot/exts/fun/duck_pond.py index 1815e54f2..fee933b47 100644 --- a/bot/exts/fun/duck_pond.py +++ b/bot/exts/fun/duck_pond.py @@ -21,7 +21,7 @@ class DuckPond(Cog):      def __init__(self, bot: Bot):          self.bot = bot -        self.webhook_id = constants.Webhooks.duck_pond +        self.webhook_id = constants.Webhooks.duck_pond.id          self.webhook = None          self.ducked_messages = []          self.relay_lock = None diff --git a/bot/exts/help_channels/_channel.py b/bot/exts/help_channels/_channel.py index f64162006..670a10446 100644 --- a/bot/exts/help_channels/_channel.py +++ b/bot/exts/help_channels/_channel.py @@ -30,7 +30,7 @@ NEW_POST_ICON_URL = f"{BRANDING_REPO_RAW_URL}/main/icons/checkmark/green-checkma  CLOSED_POST_MSG = f"""  This help channel has been closed and it's no longer possible to send messages here. \ -If your question wasn't answered, feel free to create a new post in <#{constants.Channels.help_system_forum}>. \ +If your question wasn't answered, feel free to create a new post in <#{constants.Channels.python_help}>. \  To maximize your chances of getting a response, check out this guide on [asking good questions]({ASKING_GUIDE_URL}).  """  CLOSED_POST_ICON_URL = f"{BRANDING_REPO_RAW_URL}/main/icons/zzz/zzz-dist.png" @@ -39,7 +39,7 @@ CLOSED_POST_ICON_URL = f"{BRANDING_REPO_RAW_URL}/main/icons/zzz/zzz-dist.png"  def is_help_forum_post(channel: discord.abc.GuildChannel) -> bool:      """Return True if `channel` is a post in the help forum."""      log.trace(f"Checking if #{channel} is a help channel.") -    return getattr(channel, "parent_id", None) == constants.Channels.help_system_forum +    return getattr(channel, "parent_id", None) == constants.Channels.python_help  async def _close_help_post(closed_post: discord.Thread, closing_reason: _stats.ClosingReason) -> None: diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index bc6bd0303..29d238a5c 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -39,9 +39,9 @@ class HelpForum(commands.Cog):      async def cog_load(self) -> None:          """Archive all idle open posts, schedule check for later for active open posts."""          log.trace("Initialising help forum cog.") -        self.help_forum_channel = self.bot.get_channel(constants.Channels.help_system_forum) +        self.help_forum_channel = self.bot.get_channel(constants.Channels.python_help)          if not isinstance(self.help_forum_channel, discord.ForumChannel): -            raise TypeError("Channels.help_system_forum is not a forum channel!") +            raise TypeError("Channels.python_help is not a forum channel!")          for post in self.help_forum_channel.threads:              await _channel.maybe_archive_idle_post(post, self.scheduler, has_task=False) diff --git a/bot/exts/help_channels/_stats.py b/bot/exts/help_channels/_stats.py index 1075b439e..6ca40139b 100644 --- a/bot/exts/help_channels/_stats.py +++ b/bot/exts/help_channels/_stats.py @@ -22,7 +22,7 @@ class ClosingReason(Enum):  def report_post_count() -> None:      """Report post count stats of the help forum.""" -    help_forum = bot.instance.get_channel(constants.Channels.help_system_forum) +    help_forum = bot.instance.get_channel(constants.Channels.python_help)      bot.instance.stats.gauge("help.total.in_use", len(help_forum.threads)) diff --git a/bot/exts/info/codeblock/_cog.py b/bot/exts/info/codeblock/_cog.py index a431175fd..073a91a53 100644 --- a/bot/exts/info/codeblock/_cog.py +++ b/bot/exts/info/codeblock/_cog.py @@ -50,7 +50,7 @@ class CodeBlockCog(Cog, name="Code Block"):      The cog only detects messages in whitelisted channels. Channels may also have a cooldown on the      instructions being sent. Note all help channels are also whitelisted with cooldowns enabled. -    For configurable parameters, see the `code_block` section in config-default.py. +    For configurable parameters, see the `_CodeBlock` class in constants.py.      """      def __init__(self, bot: Bot): diff --git a/bot/exts/moderation/incidents.py b/bot/exts/moderation/incidents.py index ce83ca3fe..3e06cc215 100644 --- a/bot/exts/moderation/incidents.py +++ b/bot/exts/moderation/incidents.py @@ -329,9 +329,9 @@ class Incidents(Cog):          await self.bot.wait_until_guild_available()          try: -            self.incidents_webhook = await self.bot.fetch_webhook(Webhooks.incidents) +            self.incidents_webhook = await self.bot.fetch_webhook(Webhooks.incidents.id)          except discord.HTTPException: -            log.error(f"Failed to fetch incidents webhook with id `{Webhooks.incidents}`.") +            log.error(f"Failed to fetch incidents webhook with id `{Webhooks.incidents.id}`.")      async def crawl_incidents(self) -> None:          """ @@ -389,7 +389,7 @@ class Incidents(Cog):          embed, attachment_file = await make_embed(incident, outcome, actioned_by)          try: -            webhook = await self.bot.fetch_webhook(Webhooks.incidents_archive) +            webhook = await self.bot.fetch_webhook(Webhooks.incidents_archive.id)              await webhook.send(                  embed=embed,                  username=sub_clyde(incident.author.display_name), diff --git a/bot/exts/moderation/watchchannels/_watchchannel.py b/bot/exts/moderation/watchchannels/_watchchannel.py index 2871eb5de..bc70a8c1d 100644 --- a/bot/exts/moderation/watchchannels/_watchchannel.py +++ b/bot/exts/moderation/watchchannels/_watchchannel.py @@ -53,7 +53,7 @@ class WatchChannel(metaclass=CogABCMeta):      ) -> None:          self.bot = bot -        self.destination = destination  # E.g., Channels.big_brother_logs +        self.destination = destination  # E.g., Channels.big_brother          self.webhook_id = webhook_id  # E.g.,  Webhooks.big_brother          self.api_endpoint = api_endpoint  # E.g., 'bot/infractions'          self.api_default_params = api_default_params  # E.g., {'active': 'true', 'type': 'watch'} diff --git a/bot/exts/moderation/watchchannels/bigbrother.py b/bot/exts/moderation/watchchannels/bigbrother.py index 4a746edff..fe652cc5b 100644 --- a/bot/exts/moderation/watchchannels/bigbrother.py +++ b/bot/exts/moderation/watchchannels/bigbrother.py @@ -19,8 +19,8 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"):      def __init__(self, bot: Bot) -> None:          super().__init__(              bot, -            destination=Channels.big_brother_logs, -            webhook_id=Webhooks.big_brother, +            destination=Channels.big_brother, +            webhook_id=Webhooks.big_brother.id,              api_endpoint='bot/infractions',              api_default_params={'active': 'true', 'type': 'watch', 'ordering': '-inserted_at', 'limit': 10_000},              logger=log diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py index b9e76b60f..efc9c7a4d 100644 --- a/bot/exts/recruitment/talentpool/_review.py +++ b/bot/exts/recruitment/talentpool/_review.py @@ -292,7 +292,7 @@ class Reviewer:          else:              embed_title = f"Vote for `{user_id}`" -        channel = self.bot.get_channel(Channels.nomination_archive) +        channel = self.bot.get_channel(Channels.nomination_voting_archive)          for number, part in enumerate(                  textwrap.wrap(embed_content, width=MAX_EMBED_SIZE, replace_whitespace=False, placeholder="")          ): diff --git a/botstrap.py b/botstrap.py new file mode 100644 index 000000000..4b00be9aa --- /dev/null +++ b/botstrap.py @@ -0,0 +1,164 @@ +import os +import re +from pathlib import Path + +from dotenv import load_dotenv +from httpx import Client, HTTPStatusError, Response + +from bot.constants import Webhooks, _Categories, _Channels, _Roles +from bot.log import get_logger + +load_dotenv() +log = get_logger("Config Bootstrapper") + +env_file_path = Path(".env.server") +BOT_TOKEN = os.getenv("BOT_TOKEN", None) +GUILD_ID = os.getenv("GUILD_ID", None) + + +if not BOT_TOKEN: +    message = ( +        "Couldn't find BOT_TOKEN in the environment variables." +        "Make sure to add it to the `.env` file likewise: `BOT_TOKEN=value_of_your_bot_token`" +    ) +    log.warning(message) +    raise ValueError(message) + +if not GUILD_ID: +    message = ( +        "Couldn't find GUILD_ID in the environment variables." +        "Make sure to add it to the `.env` file likewise: `GUILD_ID=value_of_your_discord_server_id`" +    ) +    log.warning(message) +    raise ValueError(message) + + +class DiscordClient(Client): +    """An HTTP client to communicate with Discord's APIs.""" + +    def __init__(self): +        super().__init__( +            base_url="https://discord.com/api/v10", +            headers={"Authorization": f"Bot {BOT_TOKEN}"}, +            event_hooks={"response": [self._raise_for_status]} +        ) + +    @staticmethod +    def _raise_for_status(response: Response) -> None: +        response.raise_for_status() + + +def get_all_roles(guild_id: int | str, client: DiscordClient) -> dict: +    """Fetches all the roles in a guild.""" +    result = {} + +    response = client.get(f"guilds/{guild_id}/roles") +    roles = response.json() + +    for role in roles: +        name = "_".join(part.lower() for part in role["name"].split(" ")).replace("-", "_") +        result[name] = role["id"] + +    return result + + +def get_all_channels_and_categories( +        guild_id: int | str, +        client: DiscordClient +) -> tuple[dict[str, str], dict[str, str]]: +    """Fetches all the text channels & categories in a guild.""" +    off_topic_channel_name_regex = r"ot\d{1}(_.*)+" +    off_topic_count = 0 +    channels = {}  # could be text channels only as well +    categories = {} + +    response = client.get(f"guilds/{guild_id}/channels") +    server_channels = response.json() + +    for channel in server_channels: +        channel_type = channel["type"] +        name = "_".join(part.lower() for part in channel["name"].split(" ")).replace("-", "_") +        if re.match(off_topic_channel_name_regex, name): +            name = f"off_topic_{off_topic_count}" +            off_topic_count += 1 + +        if channel_type == 4: +            categories[name] = channel["id"] +        else: +            channels[name] = channel["id"] + +    return channels, categories + + +def webhook_exists(webhook_id_: int, client: DiscordClient) -> bool: +    """A predicate that indicates whether a webhook exists already or not.""" +    try: +        client.get(f"webhooks/{webhook_id_}") +        return True +    except HTTPStatusError: +        return False + + +def create_webhook(name: str, channel_id_: int, client: DiscordClient) -> str: +    """Creates a new webhook for a particular channel.""" +    payload = {"name": name} + +    response = client.post(f"channels/{channel_id_}/webhooks", json=payload) +    new_webhook = response.json() +    return new_webhook["id"] + + +with DiscordClient() as discord_client: +    config_str = "#Roles\n" + +    all_roles = get_all_roles(guild_id=GUILD_ID, client=discord_client) + +    for role_name in _Roles.__fields__: + +        role_id = all_roles.get(role_name, None) +        if not role_id: +            log.warning(f"Couldn't find the role {role_name} in the guild, PyDis' default values will be used.") +            continue + +        config_str += f"roles_{role_name}={role_id}\n" + +    all_channels, all_categories = get_all_channels_and_categories(guild_id=GUILD_ID, client=discord_client) + +    config_str += "\n#Channels\n" + +    for channel_name in _Channels.__fields__: +        channel_id = all_channels.get(channel_name, None) +        if not channel_id: +            log.warning( +                f"Couldn't find the channel {channel_name} in the guild, PyDis' default values will be used." +            ) +            continue + +        config_str += f"channels_{channel_name}={channel_id}\n" + +    config_str += "\n#Categories\n" + +    for category_name in _Categories.__fields__: +        category_id = all_categories.get(category_name, None) +        if not category_id: +            log.warning( +                f"Couldn't find the category {category_name} in the guild, PyDis' default values will be used." +            ) +            continue + +        config_str += f"categories_{category_name}={category_id}\n" + +    env_file_path.write_text(config_str) + +    config_str += "\n#Webhooks\n" + +    for webhook_name, webhook_model in Webhooks: +        webhook = webhook_exists(webhook_model.id, client=discord_client) +        if not webhook: +            webhook_channel_id = int(all_channels[webhook_name]) +            webhook_id = create_webhook(webhook_name, webhook_channel_id, client=discord_client) +        else: +            webhook_id = webhook_model.id +        config_str += f"webhooks_{webhook_name}.id={webhook_id}\n" + +    env_file_path.write_text(config_str) diff --git a/config-default.yml b/config-default.yml deleted file mode 100644 index de0f7e4e8..000000000 --- a/config-default.yml +++ /dev/null @@ -1,560 +0,0 @@ -debug:     !ENV ["BOT_DEBUG", "true"] -file_logs: !ENV ["FILE_LOGS", "false"] - - -bot: -    prefix:         "!" -    sentry_dsn:     !ENV "BOT_SENTRY_DSN" -    token:          !ENV "BOT_TOKEN" -    trace_loggers:  !ENV "BOT_TRACE_LOGGERS" - -    clean: -        # Maximum number of messages to traverse for clean commands -        message_limit: 10000 - -    cooldowns: -        # Per channel, per tag. -        tags: 60 - -    redis: -        host:  "redis.default.svc.cluster.local" -        password: !ENV "REDIS_PASSWORD" -        port:  6379 -        use_fakeredis: false - -    stats: -        presence_update_timeout: 300 -        statsd_host: "graphite.default.svc.cluster.local" - - -style: -    colours: -        blue: 0x3775a8 -        bright_green: 0x01d277 -        orange: 0xe67e22 -        pink: 0xcf84e0 -        purple: 0xb734eb -        soft_green: 0x68c290 -        soft_orange: 0xf9cb54 -        soft_red: 0xcd6d6d -        white: 0xfffffe -        yellow: 0xffd241 - -    emojis: -        badge_bug_hunter: "<:bug_hunter_lvl1:743882896372269137>" -        badge_bug_hunter_level_2: "<:bug_hunter_lvl2:743882896611344505>" -        badge_early_supporter: "<:early_supporter:743882896909140058>" -        badge_hypesquad: "<:hypesquad_events:743882896892362873>" -        badge_hypesquad_balance: "<:hypesquad_balance:743882896460480625>" -        badge_hypesquad_bravery: "<:hypesquad_bravery:743882896745693335>" -        badge_hypesquad_brilliance: "<:hypesquad_brilliance:743882896938631248>" -        badge_partner: "<:partner:748666453242413136>" -        badge_staff: "<:discord_staff:743882896498098226>" -        badge_verified_bot_developer: "<:verified_bot_dev:743882897299210310>" -        bot: "<:bot:812712599464443914>" -        verified_bot: "<:verified_bot:811645219220750347>" - -        defcon_shutdown:    "<:defcondisabled:470326273952972810>" -        defcon_unshutdown:  "<:defconenabled:470326274213150730>" -        defcon_update:      "<:defconsettingsupdated:470326274082996224>" - -        failmail: "<:failmail:633660039931887616>" - -        incident_actioned: "<:incident_actioned:714221559279255583>" -        incident_investigating: "<:incident_investigating:714224190928191551>" -        incident_unactioned: "<:incident_unactioned:714223099645526026>" - -        status_dnd:     "<:status_dnd:470326272082313216>" -        status_idle:    "<:status_idle:470326266625785866>" -        status_offline: "<:status_offline:470326266537705472>" -        status_online:  "<:status_online:470326272351010816>" - -        ducky_dave: "<:ducky_dave:742058418692423772>" - -        trashcan: "<:trashcan:637136429717389331>" - -        bullet:     "\u2022" -        check_mark: "\u2705" -        cross_mark: "\u274C" -        new:        "\U0001F195" -        pencil:     "\u270F" - -        ok_hand: ":ok_hand:" - -    icons: -        crown_blurple: "https://cdn.discordapp.com/emojis/469964153289965568.png" -        crown_green:   "https://cdn.discordapp.com/emojis/469964154719961088.png" -        crown_red:     "https://cdn.discordapp.com/emojis/469964154879344640.png" - -        defcon_denied:   "https://cdn.discordapp.com/emojis/472475292078964738.png" -        defcon_shutdown: "https://cdn.discordapp.com/emojis/470326273952972810.png" -        defcon_unshutdown:  "https://cdn.discordapp.com/emojis/470326274213150730.png" -        defcon_update:  "https://cdn.discordapp.com/emojis/472472638342561793.png" - -        filtering: "https://cdn.discordapp.com/emojis/472472638594482195.png" - -        green_checkmark: "https://raw.githubusercontent.com/python-discord/branding/main/icons/checkmark/green-checkmark-dist.png" -        green_questionmark: "https://raw.githubusercontent.com/python-discord/branding/main/icons/checkmark/green-question-mark-dist.png" -        guild_update: "https://cdn.discordapp.com/emojis/469954765141442561.png" - -        hash_blurple: "https://cdn.discordapp.com/emojis/469950142942806017.png" -        hash_green:   "https://cdn.discordapp.com/emojis/469950144918585344.png" -        hash_red:     "https://cdn.discordapp.com/emojis/469950145413251072.png" - -        message_bulk_delete: "https://cdn.discordapp.com/emojis/469952898994929668.png" -        message_delete:      "https://cdn.discordapp.com/emojis/472472641320648704.png" -        message_edit:        "https://cdn.discordapp.com/emojis/472472638976163870.png" - -        pencil: "https://cdn.discordapp.com/emojis/470326272401211415.png" - -        questionmark: "https://cdn.discordapp.com/emojis/512367613339369475.png" - -        remind_blurple: "https://cdn.discordapp.com/emojis/477907609215827968.png" -        remind_green: "https://cdn.discordapp.com/emojis/477907607785570310.png" -        remind_red: "https://cdn.discordapp.com/emojis/477907608057937930.png" - -        sign_in:  "https://cdn.discordapp.com/emojis/469952898181234698.png" -        sign_out: "https://cdn.discordapp.com/emojis/469952898089091082.png" - -        superstarify: "https://cdn.discordapp.com/emojis/636288153044516874.png" -        unsuperstarify: "https://cdn.discordapp.com/emojis/636288201258172446.png" - -        token_removed: "https://cdn.discordapp.com/emojis/470326273298792469.png" - -        user_ban:    "https://cdn.discordapp.com/emojis/469952898026045441.png" -        user_mute:     "https://cdn.discordapp.com/emojis/472472640100106250.png" -        user_unban:  "https://cdn.discordapp.com/emojis/469952898692808704.png" -        user_unmute:   "https://cdn.discordapp.com/emojis/472472639206719508.png" -        user_update: "https://cdn.discordapp.com/emojis/469952898684551168.png" -        user_verified: "https://cdn.discordapp.com/emojis/470326274519334936.png" -        user_warn: "https://cdn.discordapp.com/emojis/470326274238447633.png" - -        voice_state_blue: "https://cdn.discordapp.com/emojis/656899769662439456.png" -        voice_state_green: "https://cdn.discordapp.com/emojis/656899770094452754.png" -        voice_state_red: "https://cdn.discordapp.com/emojis/656899769905709076.png" - - -guild: -    id: 267624335836053506 -    invite: "https://discord.gg/python" - -    categories: -        logs:               &LOGS           468520609152892958 -        moderators:         &MODS_CATEGORY  749736277464842262 -        modmail:            &MODMAIL        714494672835444826 -        appeals:            &APPEALS        890331800025563216 -        appeals2:           &APPEALS2       895417395261341766 -        voice:                              356013253765234688 -        summer_code_jam:                    861692638540857384 - -    channels: -        # Public announcement and news channels -        announcements:              &ANNOUNCEMENTS      354619224620138496 -        change_log:                 &CHANGE_LOG         748238795236704388 -        mailing_lists:              &MAILING_LISTS      704372456592506880 -        python_events:              &PYEVENTS_CHANNEL   729674110270963822 -        python_news:                &PYNEWS_CHANNEL     704372456592506880 -        reddit:                     &REDDIT_CHANNEL     458224812528238616 - -        # Development -        dev_contrib:        &DEV_CONTRIB     635950537262759947 -        dev_core:           &DEV_CORE        411200599653351425 -        dev_voting:         &DEV_CORE_VOTING 839162966519447552 -        dev_log:            &DEV_LOG         622895325144940554 - -        # Discussion -        meta:                               429409067623251969 -        python_general:     &PY_GENERAL     267624335836053506 - -        # Python Help -        help_system_forum:  1035199133436354600 - -        # Topical -        discord_bots:         343944376055103488 - -        # Logs -        attachment_log:     &ATTACH_LOG     649243850006855680 -        filter_log:         &FILTER_LOG     1014943924185473094 -        message_log:        &MESSAGE_LOG    467752170159079424 -        mod_log:            &MOD_LOG        282638479504965634 -        nomination_archive:                 833371042046148738 -        user_log:                           528976905546760203 -        voice_log:                          640292421988646961 - -        # Open Source Projects -        black_formatter:    &BLACK_FORMATTER 846434317021741086 - -        # Off-topic -        off_topic_0:    291284109232308226 -        off_topic_1:    463035241142026251 -        off_topic_2:    463035268514185226 - -        # Special -        bot_commands:       &BOT_CMD        267659945086812160 -        esoteric:                           470884583684964352 -        voice_gate:                         764802555427029012 -        code_jam_planning:                  490217981872177157 - -        # Staff -        admins:             &ADMINS         365960823622991872 -        admin_spam:         &ADMIN_SPAM     563594791770914816 -        defcon:             &DEFCON         464469101889454091 -        duck_pond:          &DUCK_POND      637820308341915648 -        helpers:            &HELPERS        385474242440986624 -        incidents:                          714214212200562749 -        incidents_archive:                  720668923636351037 -        mod_alerts:                         473092532147060736 -        mods:               &MODS           305126844661760000 -        mod_meta:                           775412552795947058 -        nominations:                        822920136150745168 -        nomination_voting:                  822853512709931008 -        organisation:       &ORGANISATION   551789653284356126 -        staff_lounge:       &STAFF_LOUNGE   464905259261755392 -        staff_info:         &STAFF_INFO     396684402404622347 - -        # Staff announcement channels -        admin_announcements:    &ADMIN_ANNOUNCEMENTS    749736155569848370 -        mod_announcements:      &MOD_ANNOUNCEMENTS      372115205867700225 -        staff_announcements:    &STAFF_ANNOUNCEMENTS    464033278631084042 - -        # Voice Channels -        admins_voice:       &ADMINS_VOICE   500734494840717332 -        code_help_voice_0:                  751592231726481530 -        code_help_voice_1:                  764232549840846858 -        general_voice_0:                    751591688538947646 -        general_voice_1:                    799641437645701151 -        staff_voice:        &STAFF_VOICE    412375055910043655 - -        # Voice Chat -        code_help_chat_0:                   755154969761677312 -        code_help_chat_1:                   766330079135268884 -        staff_voice_chat:                   541638762007101470 -        voice_chat_0:                       412357430186344448 -        voice_chat_1:                       799647045886541885 - -        # Watch -        big_brother_logs:   &BB_LOGS        468507907357409333 - -        # Information -        roles:                              851270062434156586 - -    moderation_categories: -        - *MODS_CATEGORY -        - *MODMAIL -        - *LOGS -        - *APPEALS -        - *APPEALS2 - -    moderation_channels: -        - *ADMINS -        - *ADMIN_SPAM -        - *MODS - -    # Modlog cog explicitly ignores events which occur in these channels. -    # This is on top of implicitly ignoring events in channels that the mod team cannot view. -    modlog_blacklist: -        - *ATTACH_LOG -        - *MESSAGE_LOG -        - *MOD_LOG -        - *STAFF_VOICE -        - *FILTER_LOG - -    reminder_whitelist: -        - *BOT_CMD -        - *DEV_CONTRIB -        - *BLACK_FORMATTER - -    roles: -        # Self-assignable roles, see the Subscribe cog -        advent_of_code:                         518565788744024082 -        announcements:                          463658397560995840 -        lovefest:                               542431903886606399 -        pyweek_announcements:                   897568414044938310 -        revival_of_code:                        988801794668908655 -        legacy_help_channels_access:            1074780483776417964 - -        contributors:                           295488872404484098 -        help_cooldown:                          699189276025421825 -        muted:              &MUTED_ROLE         277914926603829249 -        partners:           &PY_PARTNER_ROLE    323426753857191936 -        python_community:   &PY_COMMUNITY_ROLE  458226413825294336 -        sprinters:          &SPRINTERS          758422482289426471 -        voice_verified:                         764802720779337729 - -        # Staff -        admins:             &ADMINS_ROLE    267628507062992896 -        core_developers:                    587606783669829632 -        code_jam_event_team:                787816728474288181 -        devops:                             409416496733880320 -        domain_leads:                       807415650778742785 -        events_lead:                        778361735739998228 -        helpers:            &HELPERS_ROLE   267630620367257601 -        moderators:         &MODS_ROLE      831776746206265384 -        mod_team:           &MOD_TEAM_ROLE  267629731250176001 -        owners:             &OWNERS_ROLE    267627879762755584 -        project_leads:                      815701647526330398 - -        # Code Jam -        jammers:        737249140966162473 - -        # Streaming -        video:          764245844798079016 - -        # Patreon -        patreon_tier_1: 505040943800516611 -        patreon_tier_2: 743399725914390631 -        patreon_tier_3: 743400204367036520 - -    moderation_roles: -        - *ADMINS_ROLE -        - *MOD_TEAM_ROLE -        - *MODS_ROLE -        - *OWNERS_ROLE - -    staff_roles: -        - *ADMINS_ROLE -        - *HELPERS_ROLE -        - *MOD_TEAM_ROLE -        - *OWNERS_ROLE - -    webhooks: -        big_brother:                        569133704568373283 -        dev_log:                            680501655111729222 -        duck_pond:                          637821475327311927 -        incidents:                          816650601844572212 -        incidents_archive:                  720671599790915702 -        python_news:        &PYNEWS_WEBHOOK 704381182279942324 - - -filter: -    # What do we filter? -    filter_domains:        true -    filter_everyone_ping:  true -    filter_invites:        true -    filter_zalgo:          false -    watch_regex:           true -    watch_rich_embeds:     true - -    # Notify user on filter? -    # Notifications are not expected for "watchlist" type filters -    notify_user_domains:        false -    notify_user_everyone_ping:  true -    notify_user_invites:        true -    notify_user_zalgo:          false - -    # Filter configuration -    offensive_msg_delete_days: 7     # How many days before deleting an offensive message? -    ping_everyone:             true - -    # Censor doesn't apply to these -    channel_whitelist: -        - *ADMINS -        - *BB_LOGS -        - *DEV_LOG -        - *MESSAGE_LOG -        - *MOD_LOG -        - *STAFF_LOUNGE - -    role_whitelist: -        - *ADMINS_ROLE -        - *HELPERS_ROLE -        - *MODS_ROLE -        - *OWNERS_ROLE -        - *PY_COMMUNITY_ROLE -        - *SPRINTERS -        - *PY_PARTNER_ROLE - - -keys: -    github:      !ENV "GITHUB_API_KEY" -    site_api:    !ENV "BOT_API_KEY" - - -urls: -    # PyDis site vars -    connect_max_retries:       3 -    connect_cooldown:          5 -    site:        &DOMAIN       "pythondiscord.com" -    site_api:    &API          "site.default.svc.cluster.local/api" -    site_api_schema:           "http://" -    site_paste:  &PASTE  !JOIN ["paste.", *DOMAIN] -    site_schema: &SCHEMA       "https://" -    site_staff:  &STAFF  !JOIN [*SCHEMA, *DOMAIN, "/staff"] - -    paste_service:                      !JOIN [*SCHEMA, *PASTE, "/{key}"] -    site_logs_view:                     !JOIN [*STAFF, "/bot/logs"] - -    # Snekbox -    snekbox_eval_api: !ENV ["SNEKBOX_EVAL_API", "http://snekbox.default.svc.cluster.local/eval"] -    snekbox_311_eval_api: !ENV ["SNEKBOX_311_EVAL_API", "http://snekbox-311.default.svc.cluster.local/eval"] - -    # Discord API URLs -    discord_api:        &DISCORD_API "https://discordapp.com/api/v7/" -    discord_invite_api: !JOIN [*DISCORD_API, "invites"] - -    # Misc URLsw -    bot_avatar:      "https://raw.githubusercontent.com/python-discord/branding/main/logos/logo_circle/logo_circle.png" -    github_bot_repo: "https://github.com/python-discord/bot" - - -anti_spam: -    cache_size: 100 - -    # Clean messages that violate a rule. -    clean_offending: true -    ping_everyone: true - -    punishment: -        remove_after: 600 -        role_id: *MUTED_ROLE - -    rules: -        attachments: -            interval: 10 -            max: 6 - -        burst: -            interval: 10 -            max: 7 - -        # Burst shared it (temporarily) disabled to prevent -        # the bug that triggers multiple infractions/DMs per -        # user. It also tends to catch a lot of innocent users -        # now that we're so big. -        # burst_shared: -        #    interval: 10 -        #    max: 20 - -        chars: -            interval: 5 -            max: 4_200 - -        discord_emojis: -            interval: 10 -            max: 20 - -        duplicates: -            interval: 10 -            max: 3 - -        links: -            interval: 10 -            max: 10 - -        mentions: -            interval: 10 -            max: 5 - -        newlines: -            interval: 10 -            max: 100 -            max_consecutive: 10 - -        role_mentions: -            interval: 10 -            max: 3 - - -metabase: -    username: !ENV      "METABASE_USERNAME" -    password: !ENV      "METABASE_PASSWORD" -    base_url:           "http://metabase.default.svc.cluster.local" -    public_url:         "https://metabase.pythondiscord.com" -    # 14 days, see https://www.metabase.com/docs/latest/operations-guide/environment-variables.html#max_session_age -    max_session_age:    20160 - - -big_brother: -    header_message_limit: 15 -    log_delay: 15 - - -code_block: -    # The channels in which code blocks will be detected. They are not subject to a cooldown. -    channel_whitelist: -        - *BOT_CMD - -    # The channels which will be affected by a cooldown. These channels are also whitelisted. -    cooldown_channels: -        - *PY_GENERAL - -    # Sending instructions triggers a cooldown on a per-channel basis. -    # More instruction messages will not be sent in the same channel until the cooldown has elapsed. -    cooldown_seconds: 300 - -    # The minimum amount of lines a message or code block must have for instructions to be sent. -    minimum_lines: 4 - - -free: -    # Seconds to elapse for a channel -    # to be considered inactive. -    activity_timeout: 600 -    cooldown_per: 60.0 -    cooldown_rate: 1 - - -help_channels: -    enable: true - -    # Allowed duration of inactivity before archiving a help post -    idle_minutes: 30 - -    # Allowed duration of inactivity when post is empty (due to deleted messages) -    # before archiving a help post -    deleted_idle_minutes: 5 - -    # Roles which are allowed to use the command which makes channels dormant -    cmd_whitelist: -        - *HELPERS_ROLE - -redirect_output: -    delete_delay: 15 -    delete_invocation: true - - -duck_pond: -    threshold: 7 -    channel_blacklist: -        - *ANNOUNCEMENTS -        - *PYNEWS_CHANNEL -        - *PYEVENTS_CHANNEL -        - *MAILING_LISTS -        - *REDDIT_CHANNEL -        - *DUCK_POND -        - *CHANGE_LOG -        - *STAFF_ANNOUNCEMENTS -        - *MOD_ANNOUNCEMENTS -        - *ADMIN_ANNOUNCEMENTS -        - *STAFF_INFO - - -python_news: -    channel: *PYNEWS_CHANNEL -    webhook: *PYNEWS_WEBHOOK - -    mail_lists: -        - 'python-ideas' -        - 'python-announce-list' -        - 'pypi-announce' -        - 'python-dev' - - -voice_gate: -    bot_message_delete_delay: 10  # Seconds before deleting bot's response in Voice Gate -    minimum_activity_blocks: 3  # Number of 10 minute blocks during which a user must have been active -    minimum_days_member: 3  # How many days the user must have been a member for -    minimum_messages: 50  # How many messages a user must have to be eligible for voice -    voice_ping_delete_delay: 60  # Seconds before deleting the bot's ping to user in Voice Gate - - -branding: -    cycle_frequency: 3  # How many days bot wait before refreshing server icon - - -config: -    required_keys: ['bot.token'] - - -video_permission: -    default_permission_duration: 5  # Default duration for stream command in minutes diff --git a/docker-compose.yml b/docker-compose.yml index bc53c482b..694f44507 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -112,4 +112,9 @@ services:      env_file:        - .env      environment: -      BOT_API_KEY: badbot13m0n8f570f942013fc818f234916ca531 +      API_KEYS_SITE_API: "badbot13m0n8f570f942013fc818f234916ca531" +      URLS_SITE_API: "web:8000/api" +      URLS_SNEKBOX_EVAL_API: "http://snekbox/eval" +      URLS_SNEKBOX_311_EVAL_API: "http://snekbox-311/eval" +      REDIS_HOST: "redis" +      STATS_STATSD_HOST: "http://localhost" diff --git a/poetry.lock b/poetry.lock index 3bc0ea024..3ed2ade4b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -17,106 +17,106 @@ pycares = ">=4.0.0"  [[package]]  name = "aiohttp" -version = "3.8.4" +version = "3.8.3"  description = "Async http client/server framework (asyncio)"  category = "main"  optional = false  python-versions = ">=3.6"  files = [ -    {file = "aiohttp-3.8.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5ce45967538fb747370308d3145aa68a074bdecb4f3a300869590f725ced69c1"}, -    {file = "aiohttp-3.8.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b744c33b6f14ca26b7544e8d8aadff6b765a80ad6164fb1a430bbadd593dfb1a"}, -    {file = "aiohttp-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a45865451439eb320784918617ba54b7a377e3501fb70402ab84d38c2cd891b"}, -    {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a86d42d7cba1cec432d47ab13b6637bee393a10f664c425ea7b305d1301ca1a3"}, -    {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee3c36df21b5714d49fc4580247947aa64bcbe2939d1b77b4c8dcb8f6c9faecc"}, -    {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:176a64b24c0935869d5bbc4c96e82f89f643bcdf08ec947701b9dbb3c956b7dd"}, -    {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c844fd628851c0bc309f3c801b3a3d58ce430b2ce5b359cd918a5a76d0b20cb5"}, -    {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5393fb786a9e23e4799fec788e7e735de18052f83682ce2dfcabaf1c00c2c08e"}, -    {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e4b09863aae0dc965c3ef36500d891a3ff495a2ea9ae9171e4519963c12ceefd"}, -    {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:adfbc22e87365a6e564c804c58fc44ff7727deea782d175c33602737b7feadb6"}, -    {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:147ae376f14b55f4f3c2b118b95be50a369b89b38a971e80a17c3fd623f280c9"}, -    {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:eafb3e874816ebe2a92f5e155f17260034c8c341dad1df25672fb710627c6949"}, -    {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6cc15d58053c76eacac5fa9152d7d84b8d67b3fde92709195cb984cfb3475ea"}, -    {file = "aiohttp-3.8.4-cp310-cp310-win32.whl", hash = "sha256:59f029a5f6e2d679296db7bee982bb3d20c088e52a2977e3175faf31d6fb75d1"}, -    {file = "aiohttp-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:fe7ba4a51f33ab275515f66b0a236bcde4fb5561498fe8f898d4e549b2e4509f"}, -    {file = "aiohttp-3.8.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d8ef1a630519a26d6760bc695842579cb09e373c5f227a21b67dc3eb16cfea4"}, -    {file = "aiohttp-3.8.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b3f2e06a512e94722886c0827bee9807c86a9f698fac6b3aee841fab49bbfb4"}, -    {file = "aiohttp-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a80464982d41b1fbfe3154e440ba4904b71c1a53e9cd584098cd41efdb188ef"}, -    {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b631e26df63e52f7cce0cce6507b7a7f1bc9b0c501fcde69742130b32e8782f"}, -    {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f43255086fe25e36fd5ed8f2ee47477408a73ef00e804cb2b5cba4bf2ac7f5e"}, -    {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d347a172f866cd1d93126d9b239fcbe682acb39b48ee0873c73c933dd23bd0f"}, -    {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3fec6a4cb5551721cdd70473eb009d90935b4063acc5f40905d40ecfea23e05"}, -    {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80a37fe8f7c1e6ce8f2d9c411676e4bc633a8462844e38f46156d07a7d401654"}, -    {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d1e6a862b76f34395a985b3cd39a0d949ca80a70b6ebdea37d3ab39ceea6698a"}, -    {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cd468460eefef601ece4428d3cf4562459157c0f6523db89365202c31b6daebb"}, -    {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:618c901dd3aad4ace71dfa0f5e82e88b46ef57e3239fc7027773cb6d4ed53531"}, -    {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:652b1bff4f15f6287550b4670546a2947f2a4575b6c6dff7760eafb22eacbf0b"}, -    {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80575ba9377c5171407a06d0196b2310b679dc752d02a1fcaa2bc20b235dbf24"}, -    {file = "aiohttp-3.8.4-cp311-cp311-win32.whl", hash = "sha256:bbcf1a76cf6f6dacf2c7f4d2ebd411438c275faa1dc0c68e46eb84eebd05dd7d"}, -    {file = "aiohttp-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:6e74dd54f7239fcffe07913ff8b964e28b712f09846e20de78676ce2a3dc0bfc"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:880e15bb6dad90549b43f796b391cfffd7af373f4646784795e20d92606b7a51"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb96fa6b56bb536c42d6a4a87dfca570ff8e52de2d63cabebfd6fb67049c34b6"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a6cadebe132e90cefa77e45f2d2f1a4b2ce5c6b1bfc1656c1ddafcfe4ba8131"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f352b62b45dff37b55ddd7b9c0c8672c4dd2eb9c0f9c11d395075a84e2c40f75"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ab43061a0c81198d88f39aaf90dae9a7744620978f7ef3e3708339b8ed2ef01"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9cb1565a7ad52e096a6988e2ee0397f72fe056dadf75d17fa6b5aebaea05622"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:1b3ea7edd2d24538959c1c1abf97c744d879d4e541d38305f9bd7d9b10c9ec41"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7c7837fe8037e96b6dd5cfcf47263c1620a9d332a87ec06a6ca4564e56bd0f36"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3b90467ebc3d9fa5b0f9b6489dfb2c304a1db7b9946fa92aa76a831b9d587e99"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:cab9401de3ea52b4b4c6971db5fb5c999bd4260898af972bf23de1c6b5dd9d71"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d1f9282c5f2b5e241034a009779e7b2a1aa045f667ff521e7948ea9b56e0c5ff"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-win32.whl", hash = "sha256:5e14f25765a578a0a634d5f0cd1e2c3f53964553a00347998dfdf96b8137f777"}, -    {file = "aiohttp-3.8.4-cp36-cp36m-win_amd64.whl", hash = "sha256:4c745b109057e7e5f1848c689ee4fb3a016c8d4d92da52b312f8a509f83aa05e"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:aede4df4eeb926c8fa70de46c340a1bc2c6079e1c40ccf7b0eae1313ffd33519"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ddaae3f3d32fc2cb4c53fab020b69a05c8ab1f02e0e59665c6f7a0d3a5be54f"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4eb3b82ca349cf6fadcdc7abcc8b3a50ab74a62e9113ab7a8ebc268aad35bb9"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bcb89336efa095ea21b30f9e686763f2be4478f1b0a616969551982c4ee4c3b"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c08e8ed6fa3d477e501ec9db169bfac8140e830aa372d77e4a43084d8dd91ab"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6cd05ea06daca6ad6a4ca3ba7fe7dc5b5de063ff4daec6170ec0f9979f6c332"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7a00a9ed8d6e725b55ef98b1b35c88013245f35f68b1b12c5cd4100dddac333"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:de04b491d0e5007ee1b63a309956eaed959a49f5bb4e84b26c8f5d49de140fa9"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:40653609b3bf50611356e6b6554e3a331f6879fa7116f3959b20e3528783e699"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dbf3a08a06b3f433013c143ebd72c15cac33d2914b8ea4bea7ac2c23578815d6"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854f422ac44af92bfe172d8e73229c270dc09b96535e8a548f99c84f82dde241"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-win32.whl", hash = "sha256:aeb29c84bb53a84b1a81c6c09d24cf33bb8432cc5c39979021cc0f98c1292a1a"}, -    {file = "aiohttp-3.8.4-cp37-cp37m-win_amd64.whl", hash = "sha256:db3fc6120bce9f446d13b1b834ea5b15341ca9ff3f335e4a951a6ead31105480"}, -    {file = "aiohttp-3.8.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fabb87dd8850ef0f7fe2b366d44b77d7e6fa2ea87861ab3844da99291e81e60f"}, -    {file = "aiohttp-3.8.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91f6d540163f90bbaef9387e65f18f73ffd7c79f5225ac3d3f61df7b0d01ad15"}, -    {file = "aiohttp-3.8.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d265f09a75a79a788237d7f9054f929ced2e69eb0bb79de3798c468d8a90f945"}, -    {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d89efa095ca7d442a6d0cbc755f9e08190ba40069b235c9886a8763b03785da"}, -    {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dac314662f4e2aa5009977b652d9b8db7121b46c38f2073bfeed9f4049732cd"}, -    {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe11310ae1e4cd560035598c3f29d86cef39a83d244c7466f95c27ae04850f10"}, -    {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ddb2a2026c3f6a68c3998a6c47ab6795e4127315d2e35a09997da21865757f8"}, -    {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e75b89ac3bd27d2d043b234aa7b734c38ba1b0e43f07787130a0ecac1e12228a"}, -    {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6e601588f2b502c93c30cd5a45bfc665faaf37bbe835b7cfd461753068232074"}, -    {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a5d794d1ae64e7753e405ba58e08fcfa73e3fad93ef9b7e31112ef3c9a0efb52"}, -    {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a1f4689c9a1462f3df0a1f7e797791cd6b124ddbee2b570d34e7f38ade0e2c71"}, -    {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3032dcb1c35bc330134a5b8a5d4f68c1a87252dfc6e1262c65a7e30e62298275"}, -    {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8189c56eb0ddbb95bfadb8f60ea1b22fcfa659396ea36f6adcc521213cd7b44d"}, -    {file = "aiohttp-3.8.4-cp38-cp38-win32.whl", hash = "sha256:33587f26dcee66efb2fff3c177547bd0449ab7edf1b73a7f5dea1e38609a0c54"}, -    {file = "aiohttp-3.8.4-cp38-cp38-win_amd64.whl", hash = "sha256:e595432ac259af2d4630008bf638873d69346372d38255774c0e286951e8b79f"}, -    {file = "aiohttp-3.8.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5a7bdf9e57126dc345b683c3632e8ba317c31d2a41acd5800c10640387d193ed"}, -    {file = "aiohttp-3.8.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:22f6eab15b6db242499a16de87939a342f5a950ad0abaf1532038e2ce7d31567"}, -    {file = "aiohttp-3.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7235604476a76ef249bd64cb8274ed24ccf6995c4a8b51a237005ee7a57e8643"}, -    {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea9eb976ffdd79d0e893869cfe179a8f60f152d42cb64622fca418cd9b18dc2a"}, -    {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92c0cea74a2a81c4c76b62ea1cac163ecb20fb3ba3a75c909b9fa71b4ad493cf"}, -    {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:493f5bc2f8307286b7799c6d899d388bbaa7dfa6c4caf4f97ef7521b9cb13719"}, -    {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a63f03189a6fa7c900226e3ef5ba4d3bd047e18f445e69adbd65af433add5a2"}, -    {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10c8cefcff98fd9168cdd86c4da8b84baaa90bf2da2269c6161984e6737bf23e"}, -    {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bca5f24726e2919de94f047739d0a4fc01372801a3672708260546aa2601bf57"}, -    {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:03baa76b730e4e15a45f81dfe29a8d910314143414e528737f8589ec60cf7391"}, -    {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8c29c77cc57e40f84acef9bfb904373a4e89a4e8b74e71aa8075c021ec9078c2"}, -    {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:03543dcf98a6619254b409be2d22b51f21ec66272be4ebda7b04e6412e4b2e14"}, -    {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17b79c2963db82086229012cff93ea55196ed31f6493bb1ccd2c62f1724324e4"}, -    {file = "aiohttp-3.8.4-cp39-cp39-win32.whl", hash = "sha256:34ce9f93a4a68d1272d26030655dd1b58ff727b3ed2a33d80ec433561b03d67a"}, -    {file = "aiohttp-3.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:41a86a69bb63bb2fc3dc9ad5ea9f10f1c9c8e282b471931be0268ddd09430b04"}, -    {file = "aiohttp-3.8.4.tar.gz", hash = "sha256:bf2e1a9162c1e441bf805a1fd166e249d574ca04e03b34f97e2928769e91ab5c"}, +    {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ba71c9b4dcbb16212f334126cc3d8beb6af377f6703d9dc2d9fb3874fd667ee9"}, +    {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d24b8bb40d5c61ef2d9b6a8f4528c2f17f1c5d2d31fed62ec860f6006142e83e"}, +    {file = "aiohttp-3.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f88df3a83cf9df566f171adba39d5bd52814ac0b94778d2448652fc77f9eb491"}, +    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97decbb3372d4b69e4d4c8117f44632551c692bb1361b356a02b97b69e18a62"}, +    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309aa21c1d54b8ef0723181d430347d7452daaff93e8e2363db8e75c72c2fb2d"}, +    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad5383a67514e8e76906a06741febd9126fc7c7ff0f599d6fcce3e82b80d026f"}, +    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20acae4f268317bb975671e375493dbdbc67cddb5f6c71eebdb85b34444ac46b"}, +    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05a3c31c6d7cd08c149e50dc7aa2568317f5844acd745621983380597f027a18"}, +    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d6f76310355e9fae637c3162936e9504b4767d5c52ca268331e2756e54fd4ca5"}, +    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:256deb4b29fe5e47893fa32e1de2d73c3afe7407738bd3c63829874661d4822d"}, +    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5c59fcd80b9049b49acd29bd3598cada4afc8d8d69bd4160cd613246912535d7"}, +    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:059a91e88f2c00fe40aed9031b3606c3f311414f86a90d696dd982e7aec48142"}, +    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2feebbb6074cdbd1ac276dbd737b40e890a1361b3cc30b74ac2f5e24aab41f7b"}, +    {file = "aiohttp-3.8.3-cp310-cp310-win32.whl", hash = "sha256:5bf651afd22d5f0c4be16cf39d0482ea494f5c88f03e75e5fef3a85177fecdeb"}, +    {file = "aiohttp-3.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:653acc3880459f82a65e27bd6526e47ddf19e643457d36a2250b85b41a564715"}, +    {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:86fc24e58ecb32aee09f864cb11bb91bc4c1086615001647dbfc4dc8c32f4008"}, +    {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75e14eac916f024305db517e00a9252714fce0abcb10ad327fb6dcdc0d060f1d"}, +    {file = "aiohttp-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d1fde0f44029e02d02d3993ad55ce93ead9bb9b15c6b7ccd580f90bd7e3de476"}, +    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab94426ddb1ecc6a0b601d832d5d9d421820989b8caa929114811369673235c"}, +    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89d2e02167fa95172c017732ed7725bc8523c598757f08d13c5acca308e1a061"}, +    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02f9a2c72fc95d59b881cf38a4b2be9381b9527f9d328771e90f72ac76f31ad8"}, +    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7149272fb5834fc186328e2c1fa01dda3e1fa940ce18fded6d412e8f2cf76d"}, +    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:512bd5ab136b8dc0ffe3fdf2dfb0c4b4f49c8577f6cae55dca862cd37a4564e2"}, +    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7018ecc5fe97027214556afbc7c502fbd718d0740e87eb1217b17efd05b3d276"}, +    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88c70ed9da9963d5496d38320160e8eb7e5f1886f9290475a881db12f351ab5d"}, +    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:da22885266bbfb3f78218dc40205fed2671909fbd0720aedba39b4515c038091"}, +    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:e65bc19919c910127c06759a63747ebe14f386cda573d95bcc62b427ca1afc73"}, +    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:08c78317e950e0762c2983f4dd58dc5e6c9ff75c8a0efeae299d363d439c8e34"}, +    {file = "aiohttp-3.8.3-cp311-cp311-win32.whl", hash = "sha256:45d88b016c849d74ebc6f2b6e8bc17cabf26e7e40c0661ddd8fae4c00f015697"}, +    {file = "aiohttp-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:96372fc29471646b9b106ee918c8eeb4cca423fcbf9a34daa1b93767a88a2290"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c971bf3786b5fad82ce5ad570dc6ee420f5b12527157929e830f51c55dc8af77"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff25f48fc8e623d95eca0670b8cc1469a83783c924a602e0fbd47363bb54aaca"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e381581b37db1db7597b62a2e6b8b57c3deec95d93b6d6407c5b61ddc98aca6d"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db19d60d846283ee275d0416e2a23493f4e6b6028825b51290ac05afc87a6f97"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25892c92bee6d9449ffac82c2fe257f3a6f297792cdb18ad784737d61e7a9a85"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398701865e7a9565d49189f6c90868efaca21be65c725fc87fc305906be915da"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4a4fbc769ea9b6bd97f4ad0b430a6807f92f0e5eb020f1e42ece59f3ecfc4585"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:b29bfd650ed8e148f9c515474a6ef0ba1090b7a8faeee26b74a8ff3b33617502"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:1e56b9cafcd6531bab5d9b2e890bb4937f4165109fe98e2b98ef0dcfcb06ee9d"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ec40170327d4a404b0d91855d41bfe1fe4b699222b2b93e3d833a27330a87a6d"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2df5f139233060578d8c2c975128fb231a89ca0a462b35d4b5fcf7c501ebdbe1"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-win32.whl", hash = "sha256:f973157ffeab5459eefe7b97a804987876dd0a55570b8fa56b4e1954bf11329b"}, +    {file = "aiohttp-3.8.3-cp36-cp36m-win_amd64.whl", hash = "sha256:437399385f2abcd634865705bdc180c8314124b98299d54fe1d4c8990f2f9494"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:09e28f572b21642128ef31f4e8372adb6888846f32fecb288c8b0457597ba61a"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f3553510abdbec67c043ca85727396ceed1272eef029b050677046d3387be8d"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e168a7560b7c61342ae0412997b069753f27ac4862ec7867eff74f0fe4ea2ad9"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db4c979b0b3e0fa7e9e69ecd11b2b3174c6963cebadeecfb7ad24532ffcdd11a"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e164e0a98e92d06da343d17d4e9c4da4654f4a4588a20d6c73548a29f176abe2"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8a78079d9a39ca9ca99a8b0ac2fdc0c4d25fc80c8a8a82e5c8211509c523363"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:21b30885a63c3f4ff5b77a5d6caf008b037cb521a5f33eab445dc566f6d092cc"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4b0f30372cef3fdc262f33d06e7b411cd59058ce9174ef159ad938c4a34a89da"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:8135fa153a20d82ffb64f70a1b5c2738684afa197839b34cc3e3c72fa88d302c"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:ad61a9639792fd790523ba072c0555cd6be5a0baf03a49a5dd8cfcf20d56df48"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978b046ca728073070e9abc074b6299ebf3501e8dee5e26efacb13cec2b2dea0"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-win32.whl", hash = "sha256:0d2c6d8c6872df4a6ec37d2ede71eff62395b9e337b4e18efd2177de883a5033"}, +    {file = "aiohttp-3.8.3-cp37-cp37m-win_amd64.whl", hash = "sha256:21d69797eb951f155026651f7e9362877334508d39c2fc37bd04ff55b2007091"}, +    {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ca9af5f8f5812d475c5259393f52d712f6d5f0d7fdad9acdb1107dd9e3cb7eb"}, +    {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d90043c1882067f1bd26196d5d2db9aa6d268def3293ed5fb317e13c9413ea4"}, +    {file = "aiohttp-3.8.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d737fc67b9a970f3234754974531dc9afeea11c70791dcb7db53b0cf81b79784"}, +    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebf909ea0a3fc9596e40d55d8000702a85e27fd578ff41a5500f68f20fd32e6c"}, +    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5835f258ca9f7c455493a57ee707b76d2d9634d84d5d7f62e77be984ea80b849"}, +    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da37dcfbf4b7f45d80ee386a5f81122501ec75672f475da34784196690762f4b"}, +    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f44875f2804bc0511a69ce44a9595d5944837a62caecc8490bbdb0e18b1342"}, +    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:527b3b87b24844ea7865284aabfab08eb0faf599b385b03c2aa91fc6edd6e4b6"}, +    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5ba88df9aa5e2f806650fcbeedbe4f6e8736e92fc0e73b0400538fd25a4dd96"}, +    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e7b8813be97cab8cb52b1375f41f8e6804f6507fe4660152e8ca5c48f0436017"}, +    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:2dea10edfa1a54098703cb7acaa665c07b4e7568472a47f4e64e6319d3821ccf"}, +    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:713d22cd9643ba9025d33c4af43943c7a1eb8547729228de18d3e02e278472b6"}, +    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2d252771fc85e0cf8da0b823157962d70639e63cb9b578b1dec9868dd1f4f937"}, +    {file = "aiohttp-3.8.3-cp38-cp38-win32.whl", hash = "sha256:66bd5f950344fb2b3dbdd421aaa4e84f4411a1a13fca3aeb2bcbe667f80c9f76"}, +    {file = "aiohttp-3.8.3-cp38-cp38-win_amd64.whl", hash = "sha256:84b14f36e85295fe69c6b9789b51a0903b774046d5f7df538176516c3e422446"}, +    {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16c121ba0b1ec2b44b73e3a8a171c4f999b33929cd2397124a8c7fcfc8cd9e06"}, +    {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8d6aaa4e7155afaf994d7924eb290abbe81a6905b303d8cb61310a2aba1c68ba"}, +    {file = "aiohttp-3.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43046a319664a04b146f81b40e1545d4c8ac7b7dd04c47e40bf09f65f2437346"}, +    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599418aaaf88a6d02a8c515e656f6faf3d10618d3dd95866eb4436520096c84b"}, +    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92a2964319d359f494f16011e23434f6f8ef0434acd3cf154a6b7bec511e2fb7"}, +    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73a4131962e6d91109bca6536416aa067cf6c4efb871975df734f8d2fd821b37"}, +    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598adde339d2cf7d67beaccda3f2ce7c57b3b412702f29c946708f69cf8222aa"}, +    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75880ed07be39beff1881d81e4a907cafb802f306efd6d2d15f2b3c69935f6fb"}, +    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0239da9fbafd9ff82fd67c16704a7d1bccf0d107a300e790587ad05547681c8"}, +    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4e3a23ec214e95c9fe85a58470b660efe6534b83e6cbe38b3ed52b053d7cb6ad"}, +    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:47841407cc89a4b80b0c52276f3cc8138bbbfba4b179ee3acbd7d77ae33f7ac4"}, +    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:54d107c89a3ebcd13228278d68f1436d3f33f2dd2af5415e3feaeb1156e1a62c"}, +    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c37c5cce780349d4d51739ae682dec63573847a2a8dcb44381b174c3d9c8d403"}, +    {file = "aiohttp-3.8.3-cp39-cp39-win32.whl", hash = "sha256:f178d2aadf0166be4df834c4953da2d7eef24719e8aec9a65289483eeea9d618"}, +    {file = "aiohttp-3.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:88e5be56c231981428f4f506c68b6a46fa25c4123a2e86d156c58a8369d31ab7"}, +    {file = "aiohttp-3.8.3.tar.gz", hash = "sha256:3828fb41b7203176b82fe5d699e0d845435f2374750a44b480ea6b930f6be269"},  ]  [package.dependencies]  aiosignal = ">=1.1.2"  async-timeout = ">=4.0.0a3,<5.0"  attrs = ">=17.3.0" -charset-normalizer = ">=2.0,<4.0" +charset-normalizer = ">=2.0,<3.0"  frozenlist = ">=1.1.1"  multidict = ">=4.5,<7.0"  yarl = ">=1.0,<2.0" @@ -326,89 +326,19 @@ files = [  [[package]]  name = "charset-normalizer" -version = "3.1.0" +version = "2.1.1"  description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."  category = "main"  optional = false -python-versions = ">=3.7.0" -files = [ -    {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, -    {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, -    {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, -    {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, -    {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, -    {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, -    {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +python-versions = ">=3.6.0" +files = [ +    {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, +    {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},  ] +[package.extras] +unicode-backport = ["unicodedata2"] +  [[package]]  name = "colorama"  version = "0.4.6" @@ -1619,6 +1549,7 @@ files = [  ]  [package.dependencies] +python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""}  typing-extensions = ">=4.2.0"  [package.extras] @@ -1787,7 +1718,7 @@ six = ">=1.5"  name = "python-dotenv"  version = "1.0.0"  description = "Read key-value pairs from a .env file and set them as environment variables" -category = "dev" +category = "main"  optional = false  python-versions = ">=3.8"  files = [ @@ -2454,4 +2385,4 @@ multidict = ">=4.0"  [metadata]  lock-version = "2.0"  python-versions = "3.10.*" -content-hash = "55864b0999f050d425372702a3ee959010419e76f8269b5d2cfa239b84bf8c53" +content-hash = "a6f4f1d677e9273746ab05c3bfe87ef0f7969fe42983d3f5cb7ab4ef10324d78" diff --git a/pyproject.toml b/pyproject.toml index 67f72e776..227ea4303 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ python = "3.10.*"  # See https://bot-core.pythondiscord.com/ for docs.  pydis_core = { version = "9.5.1", extras = ["async-rediscache"] } +aiohttp = "3.8.3"  arrow = "1.2.3"  beautifulsoup4 = "4.11.2"  colorama = { version = "0.4.6", markers = "sys_platform == 'win32'" } @@ -28,7 +29,7 @@ rapidfuzz = "2.13.7"  regex = "2022.10.31"  sentry-sdk = "1.16.0"  tldextract = "3.4.0" -pydantic = "1.10.5" +pydantic = { version = "1.10.5", extras = ["dotenv"]}  [tool.poetry.dev-dependencies]  coverage = "7.2.1" @@ -45,7 +46,6 @@ pre-commit = "3.1.1"  pip-licenses = "4.1.0"  pytest = "7.2.2"  pytest-cov = "4.0.0" -python-dotenv = "1.0.0"  pytest-subtests = "0.10.0"  pytest-xdist = "3.2.0"  taskipy = "1.10.3" | 
