diff options
Diffstat (limited to 'bot')
-rw-r--r-- | bot/__main__.py | 10 | ||||
-rw-r--r-- | bot/constants.py | 164 | ||||
-rw-r--r-- | bot/exts/events/hacktoberfest/hacktober-issue-finder.py | 2 | ||||
-rw-r--r-- | bot/exts/events/hacktoberfest/hacktoberstats.py | 4 | ||||
-rw-r--r-- | bot/exts/fun/game.py | 4 | ||||
-rw-r--r-- | bot/exts/fun/movie.py | 4 | ||||
-rw-r--r-- | bot/exts/fun/snakes/_snakes_cog.py | 6 | ||||
-rw-r--r-- | bot/exts/fun/space.py | 2 | ||||
-rw-r--r-- | bot/exts/holidays/easter/earth_photos.py | 6 | ||||
-rw-r--r-- | bot/exts/holidays/halloween/scarymovie.py | 4 | ||||
-rw-r--r-- | bot/exts/holidays/halloween/spookygif.py | 2 | ||||
-rw-r--r-- | bot/exts/holidays/valentines/be_my_valentine.py | 6 | ||||
-rw-r--r-- | bot/exts/holidays/valentines/lovecalculator.py | 6 | ||||
-rw-r--r-- | bot/exts/utilities/cheatsheet.py | 2 | ||||
-rw-r--r-- | bot/exts/utilities/githubinfo.py | 2 | ||||
-rw-r--r-- | bot/exts/utilities/reddit.py | 2 | ||||
-rw-r--r-- | bot/exts/utilities/wolfram.py | 2 |
17 files changed, 141 insertions, 87 deletions
diff --git a/bot/__main__.py b/bot/__main__.py index d39e93bf..1b6b9f1e 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -19,11 +19,11 @@ log = logging.getLogger(__name__) async def _create_redis_session() -> RedisSession: """Create and connect to a redis session.""" redis_session = RedisSession( - host=constants.RedisConfig.host, - port=constants.RedisConfig.port, - password=constants.RedisConfig.password, + host=constants.Redis.host, + port=constants.Redis.port, + password=constants.Redis.password.get_secret_value(), max_connections=20, - use_fakeredis=constants.RedisConfig.use_fakeredis, + use_fakeredis=constants.Redis.use_fakeredis, global_namespace="bot", decode_responses=True, ) @@ -81,7 +81,7 @@ async def main() -> None: if constants.Client.in_ci: await test_bot_in_ci(_bot) else: - await _bot.start(constants.Client.token) + await _bot.start(constants.Client.token.get_secret_value()) asyncio.run(main()) diff --git a/bot/constants.py b/bot/constants.py index 7e5fa1d9..a5f12fe2 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -1,7 +1,8 @@ import enum import logging from os import environ -from typing import NamedTuple + +from pydantic import BaseSettings, SecretStr __all__ = ( "Branding", @@ -13,13 +14,12 @@ __all__ = ( "Colours", "Emojis", "Icons", - "Lovefest", "Month", "Roles", "Tokens", "Wolfram", "Reddit", - "RedisConfig", + "Redis", "RedirectOutput", "PYTHON_PREFIX", "MODERATION_ROLES", @@ -36,21 +36,38 @@ log = logging.getLogger(__name__) PYTHON_PREFIX = "!" -class Branding: - cycle_frequency = int(environ.get("CYCLE_FREQUENCY", 3)) # 0: never, 1: every day, 2: every other day, ... +class EnvConfig(BaseSettings): + """Our default configuration for models that should load from .env files.""" + + class Config: + """Specify what .env files to load, and how to load them.""" + + env_file = ".env", + env_file_encoding = "utf-8" + + +class _Branding(EnvConfig): + EnvConfig.Config.env_prefix = "branding_" + + cycle_frequency = 3 # 0: never, 1: every day, 2: every other day, ... + + +Branding = _Branding() class Cats: cats = ["ᓚᘏᗢ", "ᘡᘏᗢ", "🐈", "ᓕᘏᗢ", "ᓇᘏᗢ", "ᓂᘏᗢ", "ᘣᘏᗢ", "ᕦᘏᗢ", "ᕂᘏᗢ"] -class Channels(NamedTuple): +class _Channels(EnvConfig): + EnvConfig.Config.env_prefix = "channels_" + algos_and_data_structs = 650401909852864553 bot_commands = 267659945086812160 community_meta = 267659945086812160 organisation = 551789653284356126 data_science_and_ai = 366673247892275221 - devlog = int(environ.get("CHANNEL_DEVLOG", 622895325144940554)) + devlog = 622895325144940554 dev_contrib = 635950537262759947 mod_meta = 775412552795947058 mod_tools = 775413915391098921 @@ -58,40 +75,57 @@ class Channels(NamedTuple): off_topic_1 = 463035241142026251 off_topic_2 = 463035268514185226 python_help = 1035199133436354600 - sir_lancebot_playground = int(environ.get("CHANNEL_COMMUNITY_BOT_COMMANDS", 607247579608121354)) + sir_lancebot_playground = 607247579608121354 voice_chat_0 = 412357430186344448 voice_chat_1 = 799647045886541885 staff_voice = 541638762007101470 - reddit = int(environ.get("CHANNEL_REDDIT", 458224812528238616)) + reddit = 458224812528238616 -class Categories(NamedTuple): - help_in_use = 696958401460043776 +Channels = _Channels() + + +class _Categories(EnvConfig): + EnvConfig.Config.env_prefix = "categories_" + + python_help_system = 691405807388196926 development = 411199786025484308 devprojects = 787641585624940544 media = 799054581991997460 staff = 364918151625965579 -codejam_categories_name = "Code Jam" # Name of the codejam team categories +Categories = _Categories() + +CODEJAM_CATEGORY_NAME = "Code Jam" # Name of the codejam team categories + +class _Client(EnvConfig): + EnvConfig.Config.env_prefix = "client_" -class Client(NamedTuple): name = "Sir Lancebot" - guild = int(environ.get("BOT_GUILD", 267624335836053506)) - prefix = environ.get("PREFIX", ".") - token = environ.get("BOT_TOKEN") - debug = environ.get("BOT_DEBUG", "true").lower() == "true" - in_ci = environ.get("IN_CI", "false").lower() == "true" - github_bot_repo = "https://github.com/python-discord/sir-lancebot" + guild = 267624335836053506 + prefix = "." + token: SecretStr + debug = True + in_ci = False + github_repo = "https://github.com/python-discord/sir-lancebot" # Override seasonal locks: 1 (January) to 12 (December) - month_override = int(environ["MONTH_OVERRIDE"]) if "MONTH_OVERRIDE" in environ else None + month_override: int | None = None -class Logging(NamedTuple): +Client = _Client() + + +class _Logging(EnvConfig): + EnvConfig.Config.env_prefix = "logging_" + debug = Client.debug - file_logs = environ.get("FILE_LOGS", "false").lower() == "true" - trace_loggers = environ.get("BOT_TRACE_LOGGERS") + file_logs = False + trace_loggers = "" + + +Logging = _Logging() class Colours: @@ -208,10 +242,6 @@ class Icons: ) -class Lovefest: - role_id = int(environ.get("LOVEFEST_ROLE_ID", 542431903886606399)) - - class Month(enum.IntEnum): JANUARY = 1 FEBRUARY = 2 @@ -236,39 +266,59 @@ if Client.month_override is not None: Month(Client.month_override) -class Roles(NamedTuple): +class _Roles(EnvConfig): + + EnvConfig.Config.env_prefix = "roles_" + owners = 267627879762755584 - admins = int(environ.get("BOT_ADMIN_ROLE_ID", 267628507062992896)) + admins = 267628507062992896 moderation_team = 267629731250176001 - helpers = int(environ.get("ROLE_HELPERS", 267630620367257601)) + helpers = 267630620367257601 core_developers = 587606783669829632 - everyone = int(environ.get("BOT_GUILD", 267624335836053506)) + everyone = Client.guild + + lovefest = 542431903886606399 + + +Roles = _Roles() + +class _Tokens(EnvConfig): + EnvConfig.Config.env_prefix = "tokens_" -class Tokens(NamedTuple): - giphy = environ.get("GIPHY_TOKEN") - aoc_session_cookie = environ.get("AOC_SESSION_COOKIE") - omdb = environ.get("OMDB_API_KEY") - youtube = environ.get("YOUTUBE_API_KEY") - tmdb = environ.get("TMDB_API_KEY") - nasa = environ.get("NASA_API_KEY") - igdb_client_id = environ.get("IGDB_CLIENT_ID") - igdb_client_secret = environ.get("IGDB_CLIENT_SECRET") - github = environ.get("GITHUB_TOKEN") - unsplash_access_key = environ.get("UNSPLASH_KEY") + giphy: SecretStr = "" + youtube: SecretStr = "" + tmdb: SecretStr = "" + nasa: SecretStr = "" + igdb_client_id: SecretStr = "" + igdb_client_secret: SecretStr = "" + github: SecretStr = "" + unsplash: SecretStr = "" -class Wolfram(NamedTuple): - user_limit_day = int(environ.get("WOLFRAM_USER_LIMIT_DAY", 10)) - guild_limit_day = int(environ.get("WOLFRAM_GUILD_LIMIT_DAY", 67)) - key = environ.get("WOLFRAM_API_KEY") +Tokens = _Tokens() -class RedisConfig(NamedTuple): - host = environ.get("REDIS_HOST", "redis.default.svc.cluster.local") - port = environ.get("REDIS_PORT", 6379) - password = environ.get("REDIS_PASSWORD") - use_fakeredis = environ.get("USE_FAKEREDIS", "false").lower() == "true" +class _Wolfram(EnvConfig): + EnvConfig.Config.env_prefix = "wolfram_" + user_limit_day = 10 + guild_limit_day = 67 + key: SecretStr = "" + + +Wolfram = _Wolfram() + + +class _Redis(EnvConfig): + EnvConfig.Config.env_prefix = "redis_" + + host = "redis.default.svc.cluster.local" + port = 6379 + password: SecretStr = "" + use_fakeredis = False + + +Redis = _Redis() class Source: @@ -280,13 +330,17 @@ class RedirectOutput: delete_delay: int = 10 -class Reddit: +class _Reddit(EnvConfig): + EnvConfig.Config.env_prefix = "reddit_" + subreddits = ["r/Python"] - client_id = environ.get("REDDIT_CLIENT_ID") - secret = environ.get("REDDIT_SECRET") - webhook = int(environ.get("REDDIT_WEBHOOK", 635408384794951680)) + client_id: SecretStr = "" + secret: SecretStr = "" + webhook = 635408384794951680 + +Reddit = _Reddit() # Default role combinations MODERATION_ROLES = {Roles.moderation_team, Roles.admins, Roles.owners} diff --git a/bot/exts/events/hacktoberfest/hacktober-issue-finder.py b/bot/exts/events/hacktoberfest/hacktober-issue-finder.py index aeffc8d7..4f7bef5d 100644 --- a/bot/exts/events/hacktoberfest/hacktober-issue-finder.py +++ b/bot/exts/events/hacktoberfest/hacktober-issue-finder.py @@ -18,7 +18,7 @@ REQUEST_HEADERS = { "User-Agent": "Python Discord Hacktoberbot", "Accept": "application / vnd.github.v3 + json" } -if GITHUB_TOKEN := Tokens.github: +if GITHUB_TOKEN := Tokens.github.get_secret_value(): REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" diff --git a/bot/exts/events/hacktoberfest/hacktoberstats.py b/bot/exts/events/hacktoberfest/hacktoberstats.py index c29e24b0..0b4266d8 100644 --- a/bot/exts/events/hacktoberfest/hacktoberstats.py +++ b/bot/exts/events/hacktoberfest/hacktoberstats.py @@ -24,8 +24,8 @@ REQUEST_HEADERS = {"User-Agent": "Python Discord Hacktoberbot"} # using repo topics API during preview period requires an accept header GITHUB_TOPICS_ACCEPT_HEADER = {"Accept": "application/vnd.github.mercy-preview+json"} if GITHUB_TOKEN := Tokens.github: - REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" - GITHUB_TOPICS_ACCEPT_HEADER["Authorization"] = f"token {GITHUB_TOKEN}" + REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN.get_secret_value()}" + GITHUB_TOPICS_ACCEPT_HEADER["Authorization"] = f"token {GITHUB_TOKEN.get_secret_value()}" GITHUB_NONEXISTENT_USER_MESSAGE = ( "The listed users cannot be searched either because the users do not exist " diff --git a/bot/exts/fun/game.py b/bot/exts/fun/game.py index a8b0b3a0..c38dc063 100644 --- a/bot/exts/fun/game.py +++ b/bot/exts/fun/game.py @@ -20,8 +20,8 @@ from bot.utils.pagination import ImagePaginator, LinePaginator # Base URL of IGDB API BASE_URL = "https://api.igdb.com/v4" -CLIENT_ID = Tokens.igdb_client_id -CLIENT_SECRET = Tokens.igdb_client_secret +CLIENT_ID = Tokens.igdb_client_id.get_secret_value() +CLIENT_SECRET = Tokens.igdb_client_secret.get_secret_value() # The number of seconds before expiry that we attempt to re-fetch a new access token ACCESS_TOKEN_RENEWAL_WINDOW = 60*60*24*2 diff --git a/bot/exts/fun/movie.py b/bot/exts/fun/movie.py index 422a83ac..73ef0a3c 100644 --- a/bot/exts/fun/movie.py +++ b/bot/exts/fun/movie.py @@ -22,7 +22,7 @@ THUMBNAIL_URL = "https://i.imgur.com/LtFtC8H.png" # Define movie params, that will be used for every movie request MOVIE_PARAMS = { - "api_key": Tokens.tmdb, + "api_key": Tokens.tmdb.get_secret_value(), "language": "en-US" } @@ -106,7 +106,7 @@ class Movie(Cog): """Return JSON of TMDB discover request.""" # Define params of request params = { - "api_key": Tokens.tmdb, + "api_key": Tokens.tmdb.get_secret_value(), "language": "en-US", "sort_by": "popularity.desc", "include_adult": "false", diff --git a/bot/exts/fun/snakes/_snakes_cog.py b/bot/exts/fun/snakes/_snakes_cog.py index d0542c23..892a3dd2 100644 --- a/bot/exts/fun/snakes/_snakes_cog.py +++ b/bot/exts/fun/snakes/_snakes_cog.py @@ -773,7 +773,7 @@ class Snakes(Cog): "query": "snake", "page": page, "language": "en-US", - "api_key": Tokens.tmdb, + "api_key": Tokens.tmdb.get_secret_value(), } ) data = await response.json() @@ -785,7 +785,7 @@ class Snakes(Cog): f"https://api.themoviedb.org/3/movie/{movie}", params={ "language": "en-US", - "api_key": Tokens.tmdb, + "api_key": Tokens.tmdb.get_secret_value(), } ) data = await response.json() @@ -1095,7 +1095,7 @@ class Snakes(Cog): "part": "snippet", "q": urllib.parse.quote_plus(query), "type": "video", - "key": Tokens.youtube + "key": Tokens.youtube.get_secret_value() } ) response = await response.json() diff --git a/bot/exts/fun/space.py b/bot/exts/fun/space.py index 22a89050..c688b281 100644 --- a/bot/exts/fun/space.py +++ b/bot/exts/fun/space.py @@ -210,7 +210,7 @@ class Space(Cog): """Fetch information from NASA API, return result.""" params = {} if use_api_key: - params["api_key"] = Tokens.nasa + params["api_key"] = Tokens.nasa.get_secret_value() # Add additional parameters to request parameters only when they provided by user if additional_params is not None: diff --git a/bot/exts/holidays/easter/earth_photos.py b/bot/exts/holidays/easter/earth_photos.py index e60e2626..013122c8 100644 --- a/bot/exts/holidays/easter/earth_photos.py +++ b/bot/exts/holidays/easter/earth_photos.py @@ -23,7 +23,7 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with self.bot.http_session.get( API_URL, - params={"query": "planet_earth", "client_id": Tokens.unsplash_access_key} + params={"query": "planet_earth", "client_id": Tokens.unsplash.get_secret_value()} ) as r: jsondata = await r.json() linksdata = jsondata.get("urls") @@ -37,7 +37,7 @@ class EarthPhotos(commands.Cog): rf = "?utm_source=Sir%20Lancebot&utm_medium=referral" async with self.bot.http_session.get( downloadlinksdata.get("download_location"), - params={"client_id": Tokens.unsplash_access_key} + params={"client_id": Tokens.unsplash.get_secret_value()} ) as _: pass @@ -59,7 +59,7 @@ class EarthPhotos(commands.Cog): async def setup(bot: Bot) -> None: """Load the Earth Photos cog.""" - if not Tokens.unsplash_access_key: + if not Tokens.unsplash: log.warning("No Unsplash access key found. Cog not loading.") return await bot.add_cog(EarthPhotos(bot)) diff --git a/bot/exts/holidays/halloween/scarymovie.py b/bot/exts/holidays/halloween/scarymovie.py index 9f1a95fd..00c96153 100644 --- a/bot/exts/holidays/halloween/scarymovie.py +++ b/bot/exts/holidays/halloween/scarymovie.py @@ -36,7 +36,7 @@ class ScaryMovie(commands.Cog): """Selects a random movie and returns a JSON of movie details from TMDb.""" url = "https://api.themoviedb.org/3/discover/movie" params = { - "api_key": Tokens.tmdb, + "api_key": Tokens.tmdb.get_secret_value(), "with_genres": "27", "vote_count.gte": "5", "include_adult": "false" @@ -65,7 +65,7 @@ class ScaryMovie(commands.Cog): # Get full details and credits async with self.bot.http_session.get( url=f"https://api.themoviedb.org/3/movie/{selection_id}", - params={"api_key": Tokens.tmdb, "append_to_response": "credits"} + params={"api_key": Tokens.tmdb.get_secret_value(), "append_to_response": "credits"} ) as selection: return await selection.json() diff --git a/bot/exts/holidays/halloween/spookygif.py b/bot/exts/holidays/halloween/spookygif.py index 750e86ca..7a90a8a9 100644 --- a/bot/exts/holidays/halloween/spookygif.py +++ b/bot/exts/holidays/halloween/spookygif.py @@ -21,7 +21,7 @@ class SpookyGif(commands.Cog): async def spookygif(self, ctx: commands.Context) -> None: """Fetches a random gif from the GIPHY API and responds with it.""" async with ctx.typing(): - params = {"api_key": Tokens.giphy, "tag": "halloween", "rating": "g"} + params = {"api_key": Tokens.giphy.get_secret_value(), "tag": "halloween", "rating": "g"} # Make a GET request to the Giphy API to get a random halloween gif. async with self.bot.http_session.get(API_URL, params=params) as resp: data = await resp.json() diff --git a/bot/exts/holidays/valentines/be_my_valentine.py b/bot/exts/holidays/valentines/be_my_valentine.py index 5ffd14e6..3a2beef3 100644 --- a/bot/exts/holidays/valentines/be_my_valentine.py +++ b/bot/exts/holidays/valentines/be_my_valentine.py @@ -7,7 +7,7 @@ import discord from discord.ext import commands from bot.bot import Bot -from bot.constants import Channels, Colours, Lovefest, Month, PYTHON_PREFIX +from bot.constants import Channels, Colours, Month, PYTHON_PREFIX, Roles from bot.utils.decorators import in_month from bot.utils.exceptions import MovedCommandError @@ -60,7 +60,7 @@ class BeMyValentine(commands.Cog): # This command should only be used in the server raise commands.UserInputError("You are supposed to use this command in the server.") - if Lovefest.role_id not in [role.id for role in user.roles]: + if Roles.lovefest not in [role.id for role in user.roles]: raise commands.UserInputError( f"You cannot send a valentine to {user} as they do not have the lovefest role!" ) @@ -95,7 +95,7 @@ class BeMyValentine(commands.Cog): example : .bemyvalentine secret Iceman#6508 Hey I love you, wanna hang around ? (sends the custom message to Iceman in DM making you anonymous) """ - if Lovefest.role_id not in [role.id for role in user.roles]: + if Roles.lovefest not in [role.id for role in user.roles]: await ctx.message.delete() raise commands.UserInputError( f"You cannot send a valentine to {user} as they do not have the lovefest role!" diff --git a/bot/exts/holidays/valentines/lovecalculator.py b/bot/exts/holidays/valentines/lovecalculator.py index c212e833..eab3d083 100644 --- a/bot/exts/holidays/valentines/lovecalculator.py +++ b/bot/exts/holidays/valentines/lovecalculator.py @@ -12,7 +12,7 @@ from discord.ext import commands from discord.ext.commands import BadArgument, Cog, clean_content from bot.bot import Bot -from bot.constants import Channels, Lovefest, Month, PYTHON_PREFIX +from bot.constants import Channels, Month, PYTHON_PREFIX, Roles from bot.utils.decorators import in_month log = logging.getLogger(__name__) @@ -45,8 +45,8 @@ class LoveCalculator(Cog): Running .love @chrisjl#2655 @joe#6000 will yield the same result as before. """ if ( - Lovefest.role_id not in [role.id for role in who.roles] - or (whom is not None and Lovefest.role_id not in [role.id for role in whom.roles]) + Roles.lovefest not in [role.id for role in who.roles] + or (whom is not None and Roles.lovefest not in [role.id for role in whom.roles]) ): raise BadArgument( "This command can only be ran against members with the lovefest role! " diff --git a/bot/exts/utilities/cheatsheet.py b/bot/exts/utilities/cheatsheet.py index 3141a050..d7eb4917 100644 --- a/bot/exts/utilities/cheatsheet.py +++ b/bot/exts/utilities/cheatsheet.py @@ -80,7 +80,7 @@ class CheatSheet(commands.Cog): aliases=("cht.sh", "cheatsheet", "cheat-sheet", "cht"), ) @commands.cooldown(1, 10, BucketType.user) - @whitelist_override(categories=[Categories.help_in_use]) + @whitelist_override(categories=[Categories.python_help_system]) async def cheat_sheet(self, ctx: Context, *search_terms: str) -> None: """ Search cheat.sh. diff --git a/bot/exts/utilities/githubinfo.py b/bot/exts/utilities/githubinfo.py index a7979718..4e008e9f 100644 --- a/bot/exts/utilities/githubinfo.py +++ b/bot/exts/utilities/githubinfo.py @@ -26,7 +26,7 @@ ISSUE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/issues/{numbe PR_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/pulls/{number}" if Tokens.github: - REQUEST_HEADERS["Authorization"] = f"token {Tokens.github}" + REQUEST_HEADERS["Authorization"] = f"token {Tokens.github.get_secret_value()}" CODE_BLOCK_RE = re.compile( r"^`([^`\n]+)`" # Inline codeblock diff --git a/bot/exts/utilities/reddit.py b/bot/exts/utilities/reddit.py index 028c16bc..43a82aef 100644 --- a/bot/exts/utilities/reddit.py +++ b/bot/exts/utilities/reddit.py @@ -36,7 +36,7 @@ class Reddit(Cog): self.webhook = None self.access_token = None - self.client_auth = BasicAuth(RedditConfig.client_id, RedditConfig.secret) + self.client_auth = BasicAuth(RedditConfig.client_id.get_secret_value(), RedditConfig.secret.get_secret_value()) self.auto_poster_loop.start() diff --git a/bot/exts/utilities/wolfram.py b/bot/exts/utilities/wolfram.py index a2f1228a..a036b50f 100644 --- a/bot/exts/utilities/wolfram.py +++ b/bot/exts/utilities/wolfram.py @@ -15,7 +15,7 @@ from bot.utils.pagination import ImagePaginator log = logging.getLogger(__name__) -APPID = Wolfram.key +APPID = Wolfram.key.get_secret_value() DEFAULT_OUTPUT_FORMAT = "JSON" QUERY = "http://api.wolframalpha.com/v2/{request}" WOLF_IMAGE = "https://www.symbols.com/gi.php?type=1&id=2886&i=1" |