From 3f0ef5cfeb84d8fc56bf26bc7cbb337d24d9acc4 Mon Sep 17 00:00:00 2001 From: ddc <34492089+ddc@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:33:29 -0300 Subject: [PATCH 1/2] v3.0.10 --- .github/FUNDING.yml | 2 +- .github/PULL_REQUEST_TEMPLATE | 3 - .github/workflows/workflow.yml | 4 +- .gitignore | 11 +- README.md | 4 +- pyproject.toml | 6 +- src/bot/constants/messages.py | 214 +++++++++++++++--------------- src/bot/constants/settings.py | 32 ++--- src/gw2/constants/gw2_messages.py | 108 +++++++-------- src/gw2/constants/gw2_teams.py | 4 +- src/gw2/tools/gw2_client.py | 3 +- uv.lock | 22 +-- 12 files changed, 208 insertions(+), 205 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 6505644f..b909b3c4 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ github: ddc -ko_fi: ddcsta +ko_fi: ddc custom: "https://www.paypal.com/ncp/payment/6G9Z78QHUD4RJ" diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index eba1f84f..63a08014 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,6 +1,3 @@ -## Summary - - ## Changes Made diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 96d9b968..a38f7723 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -34,7 +34,7 @@ jobs: run: uv python install ${{ env.LATEST_PYTHON_VERSION }} - name: Install dependencies - run: uv sync --locked --all-extras --group dev + run: uv sync --locked --all-extras --all-groups - name: Run tests with coverage uses: nick-fields/retry@v3 @@ -68,7 +68,7 @@ jobs: run: uv python install ${{ env.LATEST_PYTHON_VERSION }} - name: Install dependencies - run: uv sync --locked --all-extras --group dev + run: uv sync --locked --all-extras --all-groups - name: Run integration tests uses: nick-fields/retry@v3 diff --git a/.gitignore b/.gitignore index 0c39f7c3..726e6c0f 100644 --- a/.gitignore +++ b/.gitignore @@ -122,12 +122,11 @@ celerybeat.pid # Environments .env .env.prod -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ +.env.dev +.venv* +env*/ +venv*/ +ENV*/ # Spyder project settings .spyderproject diff --git a/README.md b/README.md index 2c8ea6b5..c5e8195b 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,10 @@ Donate
Python - Release uv Ruff License: MIT + Release
issues codecov @@ -266,7 +266,7 @@ Requires [UV](https://docs.astral.sh/uv/getting-started/installation/) to be ins ## Setup ```shell -uv sync --all-extras --all-groups +uv lock --upgrade && uv sync --all-extras --all-groups ``` ## Running Tests diff --git a/pyproject.toml b/pyproject.toml index b355ec43..3327da2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "DiscordBot" -version = "3.0.9" +version = "3.0.10" description = "A simple Discord bot with OpenAI support and server administration tools" urls.Repository = "https://github.com/ddc/DiscordBot" urls.Homepage = "https://ddc.github.io/DiscordBot" @@ -36,7 +36,7 @@ dependencies = [ "ddcdatabases[postgres]>=4.0.1", "discord-py>=2.7.1", "gTTS>=2.5.4", - "openai>=2.29.0", + "openai>=2.30.0", "PyNaCl>=1.6.2", "pythonLogs>=7.0.0", "uuid-utils>=0.14.1", @@ -63,7 +63,7 @@ test-integration = "uv run pytest tests/integration" hadolint.shell = "docker run --rm -i -v $(pwd)/.hadolint.yml:/.config/hadolint.yml:ro hadolint/hadolint < Dockerfile" test-docker = "uv run pytest tests/docker" tests.sequence = ["linter", "hadolint", "test-docker", "test", "test-integration"] -updatedev.sequence = ["linter", {shell = "uv lock --upgrade && uv sync --all-extras --group dev"}] +updatedev.sequence = ["linter", {shell = "uv lock --upgrade && uv sync --all-extras --all-groups"}] migration = "uv run --frozen alembic upgrade head" [tool.pytest.ini_options] diff --git a/src/bot/constants/messages.py b/src/bot/constants/messages.py index fddcfa7f..68689749 100644 --- a/src/bot/constants/messages.py +++ b/src/bot/constants/messages.py @@ -1,3 +1,5 @@ +from typing import Final + """Bot message constants organized by domain.""" @@ -225,17 +227,17 @@ class Owner: # ============================================================================ # Bot -BOT_TOKEN_NOT_FOUND = Bot.MISSING_ENV_VAR -BOT_TERMINATED = Bot.TERMINATED -BOT_STOPPED_CTRTC = Bot.STOPPED_CTRTC -BOT_FATAL_ERROR_MAIN = Bot.FATAL_ERROR_MAIN -BOT_CRASHED = Bot.CRASHED -BOT_CLOSING = Bot.CLOSING -BOT_LOGIN_FAILED = Bot.LOGIN_FAILED -BOT_INIT_PREFIX_FAILED = Bot.INIT_PREFIX_FAILED -BOT_LOAD_SETTINGS_FAILED = Bot.LOAD_SETTINGS_FAILED -BOT_LOAD_COGS_FAILED = Bot.LOAD_COGS_FAILED -BOT_LOADED_ALL_COGS_SUCCESS = Bot.LOADED_ALL_COGS_SUCCESS +BOT_TOKEN_NOT_FOUND: Final = Bot.MISSING_ENV_VAR +BOT_TERMINATED: Final = Bot.TERMINATED +BOT_STOPPED_CTRTC: Final = Bot.STOPPED_CTRTC +BOT_FATAL_ERROR_MAIN: Final = Bot.FATAL_ERROR_MAIN +BOT_CRASHED: Final = Bot.CRASHED +BOT_CLOSING: Final = Bot.CLOSING +BOT_LOGIN_FAILED: Final = Bot.LOGIN_FAILED +BOT_INIT_PREFIX_FAILED: Final = Bot.INIT_PREFIX_FAILED +BOT_LOAD_SETTINGS_FAILED: Final = Bot.LOAD_SETTINGS_FAILED +BOT_LOAD_COGS_FAILED: Final = Bot.LOAD_COGS_FAILED +BOT_LOADED_ALL_COGS_SUCCESS: Final = Bot.LOADED_ALL_COGS_SUCCESS bot_online = Bot.online bot_starting = Bot.starting bot_disconnected = Bot.disconnected @@ -245,129 +247,129 @@ class Owner: bg_task_warning = Admin.bg_task_warning # Config -CONFIG_JOIN = Config.JOIN -CONFIG_LEAVE = Config.LEAVE -CONFIG_SERVER = Config.SERVER -CONFIG_MEMBER = Config.MEMBER -CONFIG_BLOCK_INVIS_MEMBERS = Config.BLOCK_INVIS_MEMBERS -CONFIG_BOT_WORD_REACTIONS = Config.BOT_WORD_REACTIONS -CONFIG_PFILTER_CHANNELS = Config.PFILTER_CHANNELS +CONFIG_JOIN: Final = Config.JOIN +CONFIG_LEAVE: Final = Config.LEAVE +CONFIG_SERVER: Final = Config.SERVER +CONFIG_MEMBER: Final = Config.MEMBER +CONFIG_BLOCK_INVIS_MEMBERS: Final = Config.BLOCK_INVIS_MEMBERS +CONFIG_BOT_WORD_REACTIONS: Final = Config.BOT_WORD_REACTIONS +CONFIG_PFILTER_CHANNELS: Final = Config.PFILTER_CHANNELS config_pfilter = Config.pfilter -CONFIG_CHANNEL_ID_INSTEAD_NAME = Config.CHANNEL_ID_INSTEAD_NAME -CONFIG_NOT_ACTIVATED_ERROR = Config.NOT_ACTIVATED_ERROR -MISING_REUIRED_ARGUMENT = Config.MISSING_REQUIRED_ARGUMENT -CHANNEL_ID_NOT_FOUND = Config.CHANNEL_ID_NOT_FOUND -BOT_MISSING_MANAGE_MESSAGES_PERMISSION = Config.BOT_MISSING_MANAGE_MESSAGES -NO_CHANNELS_LISTED = Config.NO_CHANNELS_LISTED +CONFIG_CHANNEL_ID_INSTEAD_NAME: Final = Config.CHANNEL_ID_INSTEAD_NAME +CONFIG_NOT_ACTIVATED_ERROR: Final = Config.NOT_ACTIVATED_ERROR +MISING_REUIRED_ARGUMENT: Final = Config.MISSING_REQUIRED_ARGUMENT +CHANNEL_ID_NOT_FOUND: Final = Config.CHANNEL_ID_NOT_FOUND +BOT_MISSING_MANAGE_MESSAGES_PERMISSION: Final = Config.BOT_MISSING_MANAGE_MESSAGES +NO_CHANNELS_LISTED: Final = Config.NO_CHANNELS_LISTED # Custom Command -ALREADY_A_STANDARD_COMMAND = CustomCommand.ALREADY_A_STANDARD_COMMAND -COMMAND_LENGHT_ERROR = CustomCommand.LENGTH_ERROR -CUSTOM_COMMAND_ADDED = CustomCommand.ADDED -CUSTOM_COMMAND_EDITED = CustomCommand.EDITED -CUSTOM_COMMAND_REMOVED = CustomCommand.REMOVED -CUSTOM_COMMAND_ALL_REMOVED = CustomCommand.ALL_REMOVED -COMMAND_ALREADY_EXISTS = CustomCommand.ALREADY_EXISTS -NO_CUSTOM_COMMANDS_FOUND = CustomCommand.NO_COMMANDS_FOUND -CUSTOM_COMMAND_UNABLE_REMOVE = CustomCommand.UNABLE_REMOVE -CUSTOM_COMMANDS_SERVER = CustomCommand.COMMANDS_SERVER -GET_CONFIGS_ERROR = CustomCommand.GET_CONFIGS_ERROR +ALREADY_A_STANDARD_COMMAND: Final = CustomCommand.ALREADY_A_STANDARD_COMMAND +COMMAND_LENGHT_ERROR: Final = CustomCommand.LENGTH_ERROR +CUSTOM_COMMAND_ADDED: Final = CustomCommand.ADDED +CUSTOM_COMMAND_EDITED: Final = CustomCommand.EDITED +CUSTOM_COMMAND_REMOVED: Final = CustomCommand.REMOVED +CUSTOM_COMMAND_ALL_REMOVED: Final = CustomCommand.ALL_REMOVED +COMMAND_ALREADY_EXISTS: Final = CustomCommand.ALREADY_EXISTS +NO_CUSTOM_COMMANDS_FOUND: Final = CustomCommand.NO_COMMANDS_FOUND +CUSTOM_COMMAND_UNABLE_REMOVE: Final = CustomCommand.UNABLE_REMOVE +CUSTOM_COMMANDS_SERVER: Final = CustomCommand.COMMANDS_SERVER +GET_CONFIGS_ERROR: Final = CustomCommand.GET_CONFIGS_ERROR # Command Error -MISSING_REQUIRED_ARGUMENT_HELP_MESSAGE = CommandError.MISSING_REQUIRED_ARGUMENT_HELP -COMMAND_NOT_FOUND = CommandError.NOT_FOUND -COMMAND_ERROR = CommandError.ERROR -COMMAND_RAISED_EXCEPTION = CommandError.RAISED_EXCEPTION -NOT_ADMIN_USE_COMMAND = CommandError.NOT_ADMIN -BOT_OWNERS_ONLY_COMMAND = CommandError.OWNERS_ONLY -PREFIXES_CHOICE = CommandError.PREFIXES_CHOICE -MORE_INFO = CommandError.MORE_INFO -UNKNOWN_OPTION = CommandError.UNKNOWN_OPTION -HELP_COMMAND_MORE_INFO = CommandError.HELP_MORE_INFO -NO_OPTION_FOUND = CommandError.NO_OPTION_FOUND -NO_PERMISSION_EXECUTE_COMMAND = CommandError.NO_PERMISSION -INVALID_MESSAGE = CommandError.INVALID_MESSAGE -COMMAND_INTERNAL_ERROR = CommandError.INTERNAL_ERROR -CONTACT_ADMIN = CommandError.CONTACT_ADMIN -DM_CANNOT_EXECUTE_COMMAND = CommandError.DM_CANNOT_EXECUTE -PRIVILEGE_LOW = CommandError.PRIVILEGE_LOW -DIRECT_MESSAGES_DISABLED = CommandError.DIRECT_MESSAGES_DISABLED +MISSING_REQUIRED_ARGUMENT_HELP_MESSAGE: Final = CommandError.MISSING_REQUIRED_ARGUMENT_HELP +COMMAND_NOT_FOUND: Final = CommandError.NOT_FOUND +COMMAND_ERROR: Final = CommandError.ERROR +COMMAND_RAISED_EXCEPTION: Final = CommandError.RAISED_EXCEPTION +NOT_ADMIN_USE_COMMAND: Final = CommandError.NOT_ADMIN +BOT_OWNERS_ONLY_COMMAND: Final = CommandError.OWNERS_ONLY +PREFIXES_CHOICE: Final = CommandError.PREFIXES_CHOICE +MORE_INFO: Final = CommandError.MORE_INFO +UNKNOWN_OPTION: Final = CommandError.UNKNOWN_OPTION +HELP_COMMAND_MORE_INFO: Final = CommandError.HELP_MORE_INFO +NO_OPTION_FOUND: Final = CommandError.NO_OPTION_FOUND +NO_PERMISSION_EXECUTE_COMMAND: Final = CommandError.NO_PERMISSION +INVALID_MESSAGE: Final = CommandError.INVALID_MESSAGE +COMMAND_INTERNAL_ERROR: Final = CommandError.INTERNAL_ERROR +CONTACT_ADMIN: Final = CommandError.CONTACT_ADMIN +DM_CANNOT_EXECUTE_COMMAND: Final = CommandError.DM_CANNOT_EXECUTE +PRIVILEGE_LOW: Final = CommandError.PRIVILEGE_LOW +DIRECT_MESSAGES_DISABLED: Final = CommandError.DIRECT_MESSAGES_DISABLED # Guild Join guild_join_bot_message = GuildJoin.bot_message # Guild Update -NEW_SERVER_SETTINGS = GuildUpdate.NEW_SERVER_SETTINGS -NEW_SERVER_ICON = GuildUpdate.NEW_SERVER_ICON -NEW_SERVER_NAME = GuildUpdate.NEW_SERVER_NAME -PREVIOUS_NAME = GuildUpdate.PREVIOUS_NAME -PREVIOUS_SERVER_OWNER = GuildUpdate.PREVIOUS_SERVER_OWNER -NEW_SERVER_OWNER = GuildUpdate.NEW_SERVER_OWNER +NEW_SERVER_SETTINGS: Final = GuildUpdate.NEW_SERVER_SETTINGS +NEW_SERVER_ICON: Final = GuildUpdate.NEW_SERVER_ICON +NEW_SERVER_NAME: Final = GuildUpdate.NEW_SERVER_NAME +PREVIOUS_NAME: Final = GuildUpdate.PREVIOUS_NAME +PREVIOUS_SERVER_OWNER: Final = GuildUpdate.PREVIOUS_SERVER_OWNER +NEW_SERVER_OWNER: Final = GuildUpdate.NEW_SERVER_OWNER # Member Join -JOINED_THE_SERVER = MemberJoin.JOINED_THE_SERVER +JOINED_THE_SERVER: Final = MemberJoin.JOINED_THE_SERVER # Member Remove -LEFT_THE_SERVER = MemberRemove.LEFT_THE_SERVER +LEFT_THE_SERVER: Final = MemberRemove.LEFT_THE_SERVER # Member Update -PROFILE_CHANGES = MemberUpdate.PROFILE_CHANGES -PREVIOUS_NICKNAME = MemberUpdate.PREVIOUS_NICKNAME -NEW_NICKNAME = MemberUpdate.NEW_NICKNAME -PREVIOUS_ROLES = MemberUpdate.PREVIOUS_ROLES -NEW_ROLES = MemberUpdate.NEW_ROLES +PROFILE_CHANGES: Final = MemberUpdate.PROFILE_CHANGES +PREVIOUS_NICKNAME: Final = MemberUpdate.PREVIOUS_NICKNAME +NEW_NICKNAME: Final = MemberUpdate.NEW_NICKNAME +PREVIOUS_ROLES: Final = MemberUpdate.PREVIOUS_ROLES +NEW_ROLES: Final = MemberUpdate.NEW_ROLES # Messages -BOT_REACT_EMOJIS = Messages.BOT_REACT_EMOJIS -OWNER_DM_BOT_MESSAGE = Messages.OWNER_DM_BOT_MESSAGE -NO_DM_MESSAGES = Messages.NO_DM_MESSAGES -DM_COMMAND_NOT_ALLOWED = Messages.DM_COMMAND_NOT_ALLOWED -DM_COMMANDS_ALLOW_LIST = Messages.DM_COMMANDS_ALLOW_LIST -BOT_REACT_STUPID = Messages.BOT_REACT_STUPID -BOT_REACT_RETARD = Messages.BOT_REACT_RETARD -MESSAGE_CENSURED = Messages.MESSAGE_CENSURED -PRIVATE_BOT_MESSAGE = Messages.PRIVATE_BOT_MESSAGE +BOT_REACT_EMOJIS: Final = Messages.BOT_REACT_EMOJIS +OWNER_DM_BOT_MESSAGE: Final = Messages.OWNER_DM_BOT_MESSAGE +NO_DM_MESSAGES: Final = Messages.NO_DM_MESSAGES +DM_COMMAND_NOT_ALLOWED: Final = Messages.DM_COMMAND_NOT_ALLOWED +DM_COMMANDS_ALLOW_LIST: Final = Messages.DM_COMMANDS_ALLOW_LIST +BOT_REACT_STUPID: Final = Messages.BOT_REACT_STUPID +BOT_REACT_RETARD: Final = Messages.BOT_REACT_RETARD +MESSAGE_CENSURED: Final = Messages.MESSAGE_CENSURED +PRIVATE_BOT_MESSAGE: Final = Messages.PRIVATE_BOT_MESSAGE blocked_invis_message = Messages.blocked_invis # User Update -NEW_AVATAR = UserUpdate.NEW_AVATAR -NEW_NAME = UserUpdate.NEW_NAME -PREVIOUS_DISCRIMINATOR = UserUpdate.PREVIOUS_DISCRIMINATOR -NEW_DISCRIMINATOR = UserUpdate.NEW_DISCRIMINATOR +NEW_AVATAR: Final = UserUpdate.NEW_AVATAR +NEW_NAME: Final = UserUpdate.NEW_NAME +PREVIOUS_DISCRIMINATOR: Final = UserUpdate.PREVIOUS_DISCRIMINATOR +NEW_DISCRIMINATOR: Final = UserUpdate.NEW_DISCRIMINATOR # Bot Utils -LOADING_EXTENSIONS = BotUtils.LOADING_EXTENSIONS -LOADING_EXTENSION_FAILED = BotUtils.LOADING_EXTENSION_FAILED -DISABLED_DM = BotUtils.DISABLED_DM -MESSAGE_REMOVED_FOR_PRIVACY = BotUtils.MESSAGE_REMOVED_FOR_PRIVACY -DELETE_MESSAGE_NO_PERMISSION = BotUtils.DELETE_MESSAGE_NO_PERMISSION -SEND_MESSAGE_FAILED = BotUtils.SEND_MESSAGE_FAILED +LOADING_EXTENSIONS: Final = BotUtils.LOADING_EXTENSIONS +LOADING_EXTENSION_FAILED: Final = BotUtils.LOADING_EXTENSION_FAILED +DISABLED_DM: Final = BotUtils.DISABLED_DM +MESSAGE_REMOVED_FOR_PRIVACY: Final = BotUtils.MESSAGE_REMOVED_FOR_PRIVACY +DELETE_MESSAGE_NO_PERMISSION: Final = BotUtils.DELETE_MESSAGE_NO_PERMISSION +SEND_MESSAGE_FAILED: Final = BotUtils.SEND_MESSAGE_FAILED # Dice Rolls -DICE_SIZE_NOT_VALID = DiceRolls.SIZE_NOT_VALID -MEMBER_HIGHEST_ROLL_ANOUNCE = DiceRolls.MEMBER_HIGHEST_ROLL_ANNOUNCE -SERVER_HIGHEST_ROLL_ANOUNCE = DiceRolls.SERVER_HIGHEST_ROLL_ANNOUNCE -MEMBER_SERVER_WINNER_ANOUNCE = DiceRolls.MEMBER_SERVER_WINNER_ANNOUNCE -MEMBER_HIGHEST_ROLL = DiceRolls.MEMBER_HIGHEST_ROLL -MEMBER_HAS_HIGHEST_ROLL = DiceRolls.MEMBER_HAS_HIGHEST_ROLL -DICE_SIZE_HIGHER_ONE = DiceRolls.SIZE_HIGHER_ONE -RESET_ALL_ROLLS = DiceRolls.RESET_ALL -DELETED_ALL_ROLLS = DiceRolls.DELETED_ALL +DICE_SIZE_NOT_VALID: Final = DiceRolls.SIZE_NOT_VALID +MEMBER_HIGHEST_ROLL_ANOUNCE: Final = DiceRolls.MEMBER_HIGHEST_ROLL_ANNOUNCE +SERVER_HIGHEST_ROLL_ANOUNCE: Final = DiceRolls.SERVER_HIGHEST_ROLL_ANNOUNCE +MEMBER_SERVER_WINNER_ANOUNCE: Final = DiceRolls.MEMBER_SERVER_WINNER_ANNOUNCE +MEMBER_HIGHEST_ROLL: Final = DiceRolls.MEMBER_HIGHEST_ROLL +MEMBER_HAS_HIGHEST_ROLL: Final = DiceRolls.MEMBER_HAS_HIGHEST_ROLL +DICE_SIZE_HIGHER_ONE: Final = DiceRolls.SIZE_HIGHER_ONE +RESET_ALL_ROLLS: Final = DiceRolls.RESET_ALL +DELETED_ALL_ROLLS: Final = DiceRolls.DELETED_ALL no_dice_size_rolls = DiceRolls.no_size_rolls # Misc -PEPE_DOWNLOAD_ERROR = Misc.PEPE_DOWNLOAD_ERROR -INVITE_TITLE = Misc.INVITE_TITLE -UNLIMITED_INVITES = Misc.UNLIMITED_INVITES -TEMPORARY_INVITES = Misc.TEMPORARY_INVITES -REVOKED_INVITES = Misc.REVOKED_INVITES -NO_INVITES = Misc.NO_INVITES -DO_NOT_DISTURB = Misc.DO_NOT_DISTURB -JOINED_DISCORD_ON = Misc.JOINED_DISCORD_ON -JOINED_THIS_SERVER_ON = Misc.JOINED_THIS_SERVER_ON -LIST_COMMAND_CATEGORIES = Misc.LIST_COMMAND_CATEGORIES +PEPE_DOWNLOAD_ERROR: Final = Misc.PEPE_DOWNLOAD_ERROR +INVITE_TITLE: Final = Misc.INVITE_TITLE +UNLIMITED_INVITES: Final = Misc.UNLIMITED_INVITES +TEMPORARY_INVITES: Final = Misc.TEMPORARY_INVITES +REVOKED_INVITES: Final = Misc.REVOKED_INVITES +NO_INVITES: Final = Misc.NO_INVITES +DO_NOT_DISTURB: Final = Misc.DO_NOT_DISTURB +JOINED_DISCORD_ON: Final = Misc.JOINED_DISCORD_ON +JOINED_THIS_SERVER_ON: Final = Misc.JOINED_THIS_SERVER_ON +LIST_COMMAND_CATEGORIES: Final = Misc.LIST_COMMAND_CATEGORIES dev_info_msg = Misc.dev_info # Owner -BOT_PREFIX_CHANGED = Owner.PREFIX_CHANGED -BOT_DESCRIPTION_CHANGED = Owner.DESCRIPTION_CHANGED +BOT_PREFIX_CHANGED: Final = Owner.PREFIX_CHANGED +BOT_DESCRIPTION_CHANGED: Final = Owner.DESCRIPTION_CHANGED diff --git a/src/bot/constants/settings.py b/src/bot/constants/settings.py index eefede7f..1756f16f 100644 --- a/src/bot/constants/settings.py +++ b/src/bot/constants/settings.py @@ -13,30 +13,30 @@ class BotSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="BOT_", env_file=".env", extra="allow") # Bot - prefix: str | None = Field(default="!") + prefix: str = Field(default="!") token: str | None = Field(default=None) - bg_activity_timer: int | None = Field(default=0) - allowed_dm_commands: str | None = Field(default="owner, about, gw2") - bot_reaction_words: str | None = Field(default="stupid, retard, retarded, noob") - embed_color: str | None = Field(default="green") - embed_owner_color: str | None = Field(default="dark_purple") - exclusive_users: str | None = Field(default="") + bg_activity_timer: int = Field(default=0) + allowed_dm_commands: str = Field(default="owner, about, gw2") + bot_reaction_words: str = Field(default="stupid, retard, retarded, noob") + embed_color: str = Field(default="green") + embed_owner_color: str = Field(default="dark_purple") + exclusive_users: str = Field(default="") # OpenAi - openai_model: str | None = Field(default="gpt-5.2") + openai_model: str = Field(default="gpt-5.4", description="https://developers.openai.com/api/docs/models") openai_api_key: str | None = Field(default=None) # Cooldowns - admin_cooldown: int | None = Field(default=20) - config_cooldown: int | None = Field(default=20) - custom_cmd_cooldown: int | None = Field(default=20) - dice_rolls_cooldown: int | None = Field(default=10) - misc_cooldown: int | None = Field(default=20) - openai_cooldown: int | None = Field(default=10) - owner_cooldown: int | None = Field(default=5) + admin_cooldown: int = Field(default=20) + config_cooldown: int = Field(default=20) + custom_cmd_cooldown: int = Field(default=20) + dice_rolls_cooldown: int = Field(default=10) + misc_cooldown: int = Field(default=20) + openai_cooldown: int = Field(default=10) + owner_cooldown: int = Field(default=5) # Alembic migration settings - alembic_version_table_name: str | None = Field(default="alembic_version") + alembic_version_table_name: str = Field(default="alembic_version") @lru_cache(maxsize=1) diff --git a/src/gw2/constants/gw2_messages.py b/src/gw2/constants/gw2_messages.py index 02ad5e46..68eb9eda 100644 --- a/src/gw2/constants/gw2_messages.py +++ b/src/gw2/constants/gw2_messages.py @@ -1,3 +1,5 @@ +from typing import Final + GW2_FULL_NAME = "Guild Wars 2" ################################# # EVENT ON COMMAND ERROR @@ -7,24 +9,24 @@ ################################# # GW2 API ################################# -INVALID_APIKEY_MSG = "This API Key is INVALID or no longer exists in gw2 api database" -API_ERROR = "GW2 API ERROR" -API_DOWN = "GW2 API is currently down. Try again later." -API_NOT_FOUND = "GW2 API Not found." -API_REQUEST_REACHED = "GW2 API Requests limit has been saturated. Try again later." -API_ACCESS_DENIED = "Access denied with your GW2 API key." +INVALID_APIKEY_MSG: Final = "This API Key is INVALID or no longer exists in gw2 api database" +API_ERROR: Final = "GW2 API ERROR" +API_DOWN: Final = "GW2 API is currently down. Try again later." +API_NOT_FOUND: Final = "GW2 API Not found." +API_REQUEST_REACHED: Final = "GW2 API Requests limit has been saturated. Try again later." +API_ACCESS_DENIED: Final = "Access denied with your GW2 API key." ################################# # GW2 UTILS ################################# -API_KEY_MESSAGE_REMOVED = "Your message with your API Key was removed for privacy." -API_KEY_MESSAGE_REMOVED_DENIED = ( +API_KEY_MESSAGE_REMOVED: Final = "Your message with your API Key was removed for privacy." +API_KEY_MESSAGE_REMOVED_DENIED: Final = ( "Bot does not have permission to delete the message with your API key.\nMissing bot permission: `Manage Messages`" ) ################################# # GW2 ACCOUNT/CHARACTERS ################################# -NO_API_KEY = "You dont have an API key registered.\n" -INVALID_API_KEY_HELP_MESSAGE = "This API Key is INVALID or no longer exists in gw2 api database.\n" +NO_API_KEY: Final = "You dont have an API key registered.\n" +INVALID_API_KEY_HELP_MESSAGE: Final = "This API Key is INVALID or no longer exists in gw2 api database.\n" def key_add_info_help(prefix: str) -> str: @@ -35,27 +37,27 @@ def key_more_info_help(prefix: str) -> str: return f"To get info about your api key: `{prefix}gw2 key info`" -API_KEY_NO_PERMISSION = ( +API_KEY_NO_PERMISSION: Final = ( "Your API key doesnt have permission to access your gw2 account.\nPlease add one key with account permission." ) ################################# # GW2 CONFIG ################################# -CONFIG_TITLE = "Guild Wars 2 configurations for" +CONFIG_TITLE: Final = "Guild Wars 2 configurations for" def config_more_info(prefix: str) -> str: return f"For more info: {prefix}help gw2 config" -USER_SESSION_TITLE = "GW2 Users Session" -SESSION_ACTIVATED = "Last session `ACTIVATED`\nBot will now record Gw2 users last sessions." -SESSION_DEACTIVATED = "Last session `DEACTIVATED`\nBot will `NOT` record Gw2 users last sessions." +USER_SESSION_TITLE: Final = "GW2 Users Session" +SESSION_ACTIVATED: Final = "Last session `ACTIVATED`\nBot will now record Gw2 users last sessions." +SESSION_DEACTIVATED: Final = "Last session `DEACTIVATED`\nBot will `NOT` record Gw2 users last sessions." ################################# # GW2 KEY ################################# -KEY_ALREADY_IN_USE = "That API key is already in use by someone else." -KEY_REMOVED_SUCCESSFULLY = "Your GW2 API Key has been deleted successfully." +KEY_ALREADY_IN_USE: Final = "That API key is already in use by someone else." +KEY_REMOVED_SUCCESSFULLY: Final = "Your GW2 API Key has been deleted successfully." def key_replaced_successfully(old: str, new: str, server: str) -> str: @@ -69,63 +71,63 @@ def key_added_successfully(key_name: str, server_name: str) -> str: ################################# # GW2 MISC ################################# -LONG_SEARCH = "Search too long" -WIKI_SEARCH_RESULTS = "Wiki Search Results" -NO_RESULTS = "No results!" -CLICK_HERE = "Click here" +LONG_SEARCH: Final = "Search too long" +WIKI_SEARCH_RESULTS: Final = "Wiki Search Results" +NO_RESULTS: Final = "No results!" +CLICK_HERE: Final = "Click here" def displaying_wiki_search_title(count: int, keyword: str) -> str: return f"Displaying **{count}** closest titles that matches **{keyword}**" -CLICK_ON_LINK = "Click on link above for more info !!!" +CLICK_ON_LINK: Final = "Click on link above for more info !!!" ################################# # GW2 SESSIONS ################################# -SESSION_TITLE = "GW2 Last Session" +SESSION_TITLE: Final = "GW2 Last Session" def session_not_active(prefix: str) -> str: return f"Last session is not active on this server.\nTo activate use: `{prefix}gw2 config session on`" -SESSION_MISSING_PERMISSIONS_TITLE = "To use this command your API key needs to have the following permissions" -ADD_RIGHT_API_KEY_PERMISSIONS = ( +SESSION_MISSING_PERMISSIONS_TITLE: Final = "To use this command your API key needs to have the following permissions" +ADD_RIGHT_API_KEY_PERMISSIONS: Final = ( "Please add another API key with permissions that are MISSING if you want to use this command." ) -SESSION_BOT_STILL_UPDATING = "Bot still updating your stats!" -SESSION_END_PROCESSING = ( +SESSION_BOT_STILL_UPDATING: Final = "Bot still updating your stats!" +SESSION_END_PROCESSING: Final = ( "Your session is still being processed.\n" "The bot waits a few minutes after you stop playing to ensure accurate data from the GW2 API.\n" "Please try again shortly." ) -SESSION_USER_STILL_PLAYING = "You are playing Guild Wars 2 at the moment.\nYour stats may NOT be accurate." -WAITING_TIME = "Waiting time" -ACCOUNT_NAME = "Account Name" -SERVER = "Server" -PLAY_TIME = "Play time" -TIMES_YOU_DIED = "Times you died" -WVW_RANKS = "WvW ranks" -YAKS_KILLED = "Yaks killed" -YAKS_SCORTED = "Yaks escorted" -PLAYERS_KILLED = "Players killed" -KEEPS_CAPTURED = "Keeps captured" -TOWERS_CAPTURED = "Towers captured" -CAMPS_CAPTURED = "Camps captured" -SMC_CAPTURED = "SMC captured" -SESSION_IN_PROGRESS = ( +SESSION_USER_STILL_PLAYING: Final = "You are playing Guild Wars 2 at the moment.\nYour stats may NOT be accurate." +WAITING_TIME: Final = "Waiting time" +ACCOUNT_NAME: Final = "Account Name" +SERVER: Final = "Server" +PLAY_TIME: Final = "Play time" +TIMES_YOU_DIED: Final = "Times you died" +WVW_RANKS: Final = "WvW ranks" +YAKS_KILLED: Final = "Yaks killed" +YAKS_SCORTED: Final = "Yaks escorted" +PLAYERS_KILLED: Final = "Players killed" +KEEPS_CAPTURED: Final = "Keeps captured" +TOWERS_CAPTURED: Final = "Towers captured" +CAMPS_CAPTURED: Final = "Camps captured" +SMC_CAPTURED: Final = "SMC captured" +SESSION_IN_PROGRESS: Final = ( "Your Guild Wars 2 session is still in progress.\nSession stats will be available after you stop playing." ) -SESSION_SAVE_ERROR = ( +SESSION_SAVE_ERROR: Final = ( "There was a problem trying to record your last finished session.\n" "Please, do not close discord when the game is running." ) -SESSION_API_DOWN_DM = ( +SESSION_API_DOWN_DM: Final = ( "The GW2 API was unreachable while recording your session.\n" "Your session data may be incomplete. Please try again later." ) -USER_NO_SESSION_FOUND = ( +USER_NO_SESSION_FOUND: Final = ( "No records were found in your name.\n" "You are probably trying to execute this command without playing the game.\n" "Make sure your status is NOT set to invisible in discord.\n" @@ -135,20 +137,20 @@ def session_not_active(prefix: str) -> str: ################################# # GW2 WORLDS ################################# -NA_SERVERS_TITLE = "~~~~~ NA Servers ~~~~~" -EU_SERVERS_TITLE = "~~~~~ EU Servers ~~~~~" +NA_SERVERS_TITLE: Final = "~~~~~ NA Servers ~~~~~" +EU_SERVERS_TITLE: Final = "~~~~~ EU Servers ~~~~~" ################################# # GW2 WVW ################################# -INVALID_WORLD_NAME = "Invalid world name" -MISSING_WORLD_NAME = "Missing World Name" -WORLD_COLOR_ERROR = "Could not resolve world's color" +INVALID_WORLD_NAME: Final = "Invalid world name" +MISSING_WORLD_NAME: Final = "Missing World Name" +WORLD_COLOR_ERROR: Final = "Could not resolve world's color" def match_world_name_help(prefix: str) -> str: return f"Use `{prefix}gw2 match `\nOr register an API key on your account.\n" -WVW_KDR_TITLE = "WvW Kills/Death Ratings" -NA_TIER_TITLE = "North America Tier" -EU_TIER_TITLE = "Europe Tier" +WVW_KDR_TITLE: Final = "WvW Kills/Death Ratings" +NA_TIER_TITLE: Final = "North America Tier" +EU_TIER_TITLE: Final = "Europe Tier" diff --git a/src/gw2/constants/gw2_teams.py b/src/gw2/constants/gw2_teams.py index a92fa6de..f25a3476 100644 --- a/src/gw2/constants/gw2_teams.py +++ b/src/gw2/constants/gw2_teams.py @@ -5,8 +5,10 @@ API, so names must be hardcoded. """ +from typing import Final + # NA teams: 11001-11012, EU teams: 12001-12015 -WR_TEAM_NAMES: dict[int, str] = { +WR_TEAM_NAMES: Final[dict[int, str]] = { # North America 11001: "Team 1 (NA)", 11002: "Team 2 (NA)", diff --git a/src/gw2/tools/gw2_client.py b/src/gw2/tools/gw2_client.py index 5ee8236c..c52bfe01 100644 --- a/src/gw2/tools/gw2_client.py +++ b/src/gw2/tools/gw2_client.py @@ -10,9 +10,10 @@ APIInvalidKey, APINotFound, ) +from typing import Final _gw2_settings = get_gw2_settings() -_RETRYABLE_STATUSES = (502, 503, 504) +_RETRYABLE_STATUSES: Final = (502, 503, 504) class Gw2Client: diff --git a/uv.lock b/uv.lock index a1aea700..d97c993b 100644 --- a/uv.lock +++ b/uv.lock @@ -99,14 +99,14 @@ wheels = [ [[package]] name = "anyio" -version = "4.12.1" +version = "4.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, ] [[package]] @@ -381,7 +381,7 @@ wheels = [ [[package]] name = "discordbot" -version = "3.0.9" +version = "3.0.10" source = { virtual = "." } dependencies = [ { name = "alembic" }, @@ -413,7 +413,7 @@ requires-dist = [ { name = "ddcdatabases", extras = ["postgres"], specifier = ">=4.0.1" }, { name = "discord-py", specifier = ">=2.7.1" }, { name = "gtts", specifier = ">=2.5.4" }, - { name = "openai", specifier = ">=2.29.0" }, + { name = "openai", specifier = ">=2.30.0" }, { name = "pynacl", specifier = ">=1.6.2" }, { name = "pythonlogs", specifier = ">=7.0.0" }, { name = "uuid-utils", specifier = ">=0.14.1" }, @@ -707,7 +707,7 @@ wheels = [ [[package]] name = "openai" -version = "2.29.0" +version = "2.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -719,9 +719,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b4/15/203d537e58986b5673e7f232453a2a2f110f22757b15921cbdeea392e520/openai-2.29.0.tar.gz", hash = "sha256:32d09eb2f661b38d3edd7d7e1a2943d1633f572596febe64c0cd370c86d52bec", size = 671128, upload-time = "2026-03-17T17:53:49.599Z" } +sdist = { url = "https://files.pythonhosted.org/packages/88/15/52580c8fbc16d0675d516e8749806eda679b16de1e4434ea06fb6feaa610/openai-2.30.0.tar.gz", hash = "sha256:92f7661c990bda4b22a941806c83eabe4896c3094465030dd882a71abe80c885", size = 676084, upload-time = "2026-03-25T22:08:59.96Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/b1/35b6f9c8cf9318e3dbb7146cc82dab4cf61182a8d5406fc9b50864362895/openai-2.29.0-py3-none-any.whl", hash = "sha256:b7c5de513c3286d17c5e29b92c4c98ceaf0d775244ac8159aeb1bddf840eb42a", size = 1141533, upload-time = "2026-03-17T17:53:47.348Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9e/5bfa2270f902d5b92ab7d41ce0475b8630572e71e349b2a4996d14bdda93/openai-2.30.0-py3-none-any.whl", hash = "sha256:9a5ae616888eb2748ec5e0c5b955a51592e0b201a11f4262db920f2a78c5231d", size = 1146656, upload-time = "2026-03-25T22:08:58.2Z" }, ] [[package]] @@ -1046,7 +1046,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.5" +version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1054,9 +1054,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, ] [[package]] From d9f95a80fc70d8576ab384717be01edf78f54597 Mon Sep 17 00:00:00 2001 From: ddc <34492089+ddc@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:41:35 -0300 Subject: [PATCH 2/2] v3.0.10 --- tests/unit/bot/constants/test_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/bot/constants/test_settings.py b/tests/unit/bot/constants/test_settings.py index bcdbf8ff..78b73b66 100644 --- a/tests/unit/bot/constants/test_settings.py +++ b/tests/unit/bot/constants/test_settings.py @@ -96,7 +96,7 @@ def test_partial_env_var_overrides(self): assert settings.admin_cooldown == 35 # Default values for non-overridden fields - assert settings.openai_model == "gpt-5.2" + assert settings.openai_model == "gpt-5.4" # Note: openai_api_key might have a value from actual env, so we'll check it's set assert settings.embed_color == "green" assert settings.config_cooldown == 20