From 81ae7f0a68f8a3c55c1f113d7a0474eaaf919a65 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Sun, 12 Mar 2023 20:11:02 +0100 Subject: add constants needed --- botstrap.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index 90a954d9b..98ecceabb 100644 --- a/botstrap.py +++ b/botstrap.py @@ -15,6 +15,10 @@ env_file_path = Path(".env.server") BOT_TOKEN = os.getenv("BOT_TOKEN", None) GUILD_ID = os.getenv("GUILD_ID", None) +COMMUNITY_FEATURE = "COMMUNITY" +PYTHON_HELP_NAME = _Channels.__fields__["python_help"].name +GUILD_FORUM_TYPE = 15 + if not BOT_TOKEN: message = ( -- cgit v1.2.3 From 0862e39627a99635fd46e49c63cb2edf32aa56a7 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Sun, 12 Mar 2023 20:11:37 +0100 Subject: add new utilities to check/upgrade guild/channel states --- botstrap.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index 98ecceabb..d5dbacac3 100644 --- a/botstrap.py +++ b/botstrap.py @@ -52,6 +52,44 @@ class DiscordClient(Client): response.raise_for_status() +def is_community_server(guild_id: int | str, client: DiscordClient) -> bool: + """A predicate that indicates whether a server has the COMMUNITY feature or not""" + response = client.get(f"/guilds/{guild_id}") + return COMMUNITY_FEATURE in response.json()["features"] + + +def upgrade_server_to_community(guild_id: int | str, client: DiscordClient) -> bool: + """Transforms a server into a community one. + + Return true if the server has been correctly upgraded, False otherwise. + """ + + payload = {"features": [COMMUNITY_FEATURE]} + response = client.patch(f"/guilds/{guild_id}", json=payload) + + return 200 <= response.status_code < 300 + + +def create_forum_channel(channel_name_: str, guild_id: str, client: DiscordClient, category_id_: int | None = None): + """Creates a new forum channel""" + + payload = {"name": channel_name_, "type": GUILD_FORUM_TYPE} + if category_id_: + payload["parent_id"] = category_id_ + + response = client.post(f"/guilds/{guild_id}/channels", json=payload) + + return response.json()["id"] + + +def delete_channel(channel_id_: id, client: DiscordClient): + """Upgrades a channel to a channel of type GUILD FORUM""" + + response = client.delete(f"/channels/{channel_id_}") + + return response.json() + + def get_all_roles(guild_id: int | str, client: DiscordClient) -> dict: """Fetches all the roles in a guild.""" result = {} -- cgit v1.2.3 From 71bbbbcf5dd339fe5c65699036852efceb63716c Mon Sep 17 00:00:00 2001 From: shtlrs Date: Sun, 12 Mar 2023 20:26:44 +0100 Subject: add is_forum_channel predicate --- botstrap.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index d5dbacac3..caf4459d7 100644 --- a/botstrap.py +++ b/botstrap.py @@ -82,6 +82,13 @@ def create_forum_channel(channel_name_: str, guild_id: str, client: DiscordClien return response.json()["id"] +def is_forum_channel(channel_id_: str, client: DiscordClient) -> bool: + """A boolean that indicates if a channel is of type GUILD_FORUM""" + + response = client.get(f"/channels/{channel_id_}") + return response.json()["type"] == GUILD_FORUM_TYPE + + def delete_channel(channel_id_: id, client: DiscordClient): """Upgrades a channel to a channel of type GUILD FORUM""" -- cgit v1.2.3 From 9367f76221302571175cf0b9209e11f493dcc83d Mon Sep 17 00:00:00 2001 From: shtlrs Date: Sun, 12 Mar 2023 20:27:28 +0100 Subject: add necessary workflow to check guild features & act accordingly --- botstrap.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index caf4459d7..ae8652f4a 100644 --- a/botstrap.py +++ b/botstrap.py @@ -175,6 +175,20 @@ with DiscordClient() as discord_client: config_str += "\n#Channels\n" + if not is_community_server(GUILD_ID, discord_client): + upgrade_server_to_community(GUILD_ID, discord_client) + + create_help_channel = True + if PYTHON_HELP_NAME in all_channels: + python_help_channel_id = all_channels[PYTHON_HELP_NAME] + if not is_forum_channel(python_help_channel_id, discord_client): + delete_channel(python_help_channel_id, discord_client) + else: + create_help_channel = False + + if create_help_channel: + python_help_channel_id = create_forum_channel(PYTHON_HELP_NAME.replace('_', '-'), GUILD_ID, discord_client) + for channel_name in _Channels.__fields__: channel_id = all_channels.get(channel_name, None) if not channel_id: @@ -184,6 +198,7 @@ with DiscordClient() as discord_client: continue config_str += f"channels_{channel_name}={channel_id}\n" + config_str += f"channels_{PYTHON_HELP_NAME}={python_help_channel_id}\n" config_str += "\n#Categories\n" -- cgit v1.2.3 From 8df08a6b516868370bbdb52f7b0101f58097ee15 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Sun, 12 Mar 2023 22:45:21 +0100 Subject: replace upgrade_server_to_community with upgrade_server_to_community_if_necessary --- bot/constants.py | 2 ++ botstrap.py | 51 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 18 deletions(-) (limited to 'botstrap.py') diff --git a/bot/constants.py b/bot/constants.py index 006d0e4ce..d9327deec 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -125,6 +125,8 @@ class _Channels(EnvConfig): duck_pond = 637820308341915648 roles = 851270062434156586 + rules = 693837295685730335 + Channels = _Channels() diff --git a/botstrap.py b/botstrap.py index ae8652f4a..aa6cf87a9 100644 --- a/botstrap.py +++ b/botstrap.py @@ -16,7 +16,9 @@ BOT_TOKEN = os.getenv("BOT_TOKEN", None) GUILD_ID = os.getenv("GUILD_ID", None) COMMUNITY_FEATURE = "COMMUNITY" -PYTHON_HELP_NAME = _Channels.__fields__["python_help"].name +PYTHON_HELP_CHANNEL_NAME = _Channels.__fields__["python_help"].name +ANNOUNCEMENTS_CHANNEL_NAME = _Channels.__fields__["announcements"].name +RULES_CHANNEL_NAME = _Channels.__fields__["rules"].name GUILD_FORUM_TYPE = 15 @@ -44,7 +46,6 @@ class DiscordClient(Client): super().__init__( base_url="https://discord.com/api/v10", headers={"Authorization": f"Bot {BOT_TOKEN}"}, - event_hooks={"response": [self._raise_for_status]} ) @staticmethod @@ -52,22 +53,32 @@ class DiscordClient(Client): response.raise_for_status() -def is_community_server(guild_id: int | str, client: DiscordClient) -> bool: - """A predicate that indicates whether a server has the COMMUNITY feature or not""" +def upgrade_server_to_community_if_necessary( + guild_id: int | str, + rules_channel_id_: int | str, + announcements_channel_id_: int | str, + client: DiscordClient) -> None: + """Fetches server info & upgrades to COMMUNITY if necessary""" response = client.get(f"/guilds/{guild_id}") - return COMMUNITY_FEATURE in response.json()["features"] + payload = response.json() + + if COMMUNITY_FEATURE not in payload["features"]: + log.warning("This server is currently not a community, upgrading.") + payload["features"].append(COMMUNITY_FEATURE) + payload["rules_channel_id"] = rules_channel_id_ + payload["public_updates_channel_id"] = announcements_channel_id_ + client.patch(f"/guilds/{guild_id}", json=payload) -def upgrade_server_to_community(guild_id: int | str, client: DiscordClient) -> bool: +def upgrade_server_to_community(guild_id: int | str, client: DiscordClient) -> None: """Transforms a server into a community one. Return true if the server has been correctly upgraded, False otherwise. """ payload = {"features": [COMMUNITY_FEATURE]} - response = client.patch(f"/guilds/{guild_id}", json=payload) - - return 200 <= response.status_code < 300 + client.patch(f"/guilds/{guild_id}", json=payload) + log.info(f"Server {guild_id} has been successfully updated to a community.") def create_forum_channel(channel_name_: str, guild_id: str, client: DiscordClient, category_id_: int | None = None): @@ -78,8 +89,9 @@ def create_forum_channel(channel_name_: str, guild_id: str, client: DiscordClien payload["parent_id"] = category_id_ response = client.post(f"/guilds/{guild_id}/channels", json=payload) - - return response.json()["id"] + forum_channel_id = response.json()["id"] + log.info(f"New forum channel: {channel_name_} has been successfully created.") + return forum_channel_id def is_forum_channel(channel_id_: str, client: DiscordClient) -> bool: @@ -91,7 +103,7 @@ def is_forum_channel(channel_id_: str, client: DiscordClient) -> bool: def delete_channel(channel_id_: id, client: DiscordClient): """Upgrades a channel to a channel of type GUILD FORUM""" - + log.info(f"Channel python-help: {channel_id_} is not a forum channel and will be replaced with one.") response = client.delete(f"/channels/{channel_id_}") return response.json() @@ -175,19 +187,22 @@ with DiscordClient() as discord_client: config_str += "\n#Channels\n" - if not is_community_server(GUILD_ID, discord_client): - upgrade_server_to_community(GUILD_ID, discord_client) + rules_channel_id = all_channels[RULES_CHANNEL_NAME] + announcements_channel_id = all_channels[ANNOUNCEMENTS_CHANNEL_NAME] + + upgrade_server_to_community_if_necessary(GUILD_ID, rules_channel_id, announcements_channel_id, discord_client) create_help_channel = True - if PYTHON_HELP_NAME in all_channels: - python_help_channel_id = all_channels[PYTHON_HELP_NAME] + + if PYTHON_HELP_CHANNEL_NAME in all_channels: + python_help_channel_id = all_channels[PYTHON_HELP_CHANNEL_NAME] if not is_forum_channel(python_help_channel_id, discord_client): delete_channel(python_help_channel_id, discord_client) else: create_help_channel = False if create_help_channel: - python_help_channel_id = create_forum_channel(PYTHON_HELP_NAME.replace('_', '-'), GUILD_ID, discord_client) + python_help_channel_id = create_forum_channel(PYTHON_HELP_CHANNEL_NAME.replace('_', '-'), GUILD_ID, discord_client) for channel_name in _Channels.__fields__: channel_id = all_channels.get(channel_name, None) @@ -198,7 +213,7 @@ with DiscordClient() as discord_client: continue config_str += f"channels_{channel_name}={channel_id}\n" - config_str += f"channels_{PYTHON_HELP_NAME}={python_help_channel_id}\n" + config_str += f"channels_{PYTHON_HELP_CHANNEL_NAME}={python_help_channel_id}\n" config_str += "\n#Categories\n" -- cgit v1.2.3 From db3f040f8f8aed37bd8d9dcdacc179fb5aef2135 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Sun, 12 Mar 2023 22:53:23 +0100 Subject: bring back the response event hook --- botstrap.py | 1 + 1 file changed, 1 insertion(+) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index aa6cf87a9..f6d35485d 100644 --- a/botstrap.py +++ b/botstrap.py @@ -46,6 +46,7 @@ class DiscordClient(Client): super().__init__( base_url="https://discord.com/api/v10", headers={"Authorization": f"Bot {BOT_TOKEN}"}, + event_hooks={"response": [self._raise_for_status]}, ) @staticmethod -- cgit v1.2.3 From a915d4700c0cf0bc48166baae648e6b0e9a1cdc1 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Sun, 12 Mar 2023 22:55:54 +0100 Subject: appease linter --- botstrap.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index f6d35485d..472b18478 100644 --- a/botstrap.py +++ b/botstrap.py @@ -59,7 +59,7 @@ def upgrade_server_to_community_if_necessary( rules_channel_id_: int | str, announcements_channel_id_: int | str, client: DiscordClient) -> None: - """Fetches server info & upgrades to COMMUNITY if necessary""" + """Fetches server info & upgrades to COMMUNITY if necessary.""" response = client.get(f"/guilds/{guild_id}") payload = response.json() @@ -72,19 +72,23 @@ def upgrade_server_to_community_if_necessary( def upgrade_server_to_community(guild_id: int | str, client: DiscordClient) -> None: - """Transforms a server into a community one. + """ + Transforms a server into a community one. Return true if the server has been correctly upgraded, False otherwise. """ - payload = {"features": [COMMUNITY_FEATURE]} client.patch(f"/guilds/{guild_id}", json=payload) log.info(f"Server {guild_id} has been successfully updated to a community.") -def create_forum_channel(channel_name_: str, guild_id: str, client: DiscordClient, category_id_: int | None = None): - """Creates a new forum channel""" - +def create_forum_channel( + channel_name_: str, + guild_id: str, + client: DiscordClient, + category_id_: int | None = None +) -> int: + """Creates a new forum channel.""" payload = {"name": channel_name_, "type": GUILD_FORUM_TYPE} if category_id_: payload["parent_id"] = category_id_ @@ -96,18 +100,15 @@ def create_forum_channel(channel_name_: str, guild_id: str, client: DiscordClien def is_forum_channel(channel_id_: str, client: DiscordClient) -> bool: - """A boolean that indicates if a channel is of type GUILD_FORUM""" - + """A boolean that indicates if a channel is of type GUILD_FORUM.""" response = client.get(f"/channels/{channel_id_}") return response.json()["type"] == GUILD_FORUM_TYPE -def delete_channel(channel_id_: id, client: DiscordClient): - """Upgrades a channel to a channel of type GUILD FORUM""" +def delete_channel(channel_id_: id, client: DiscordClient) -> None: + """Upgrades a channel to a channel of type GUILD FORUM.""" log.info(f"Channel python-help: {channel_id_} is not a forum channel and will be replaced with one.") - response = client.delete(f"/channels/{channel_id_}") - - return response.json() + client.delete(f"/channels/{channel_id_}") def get_all_roles(guild_id: int | str, client: DiscordClient) -> dict: @@ -203,7 +204,8 @@ with DiscordClient() as discord_client: create_help_channel = False if create_help_channel: - python_help_channel_id = create_forum_channel(PYTHON_HELP_CHANNEL_NAME.replace('_', '-'), GUILD_ID, discord_client) + python_help_channel_name = PYTHON_HELP_CHANNEL_NAME.replace('_', '-') + python_help_channel_id = create_forum_channel(python_help_channel_name, GUILD_ID, discord_client) for channel_name in _Channels.__fields__: channel_id = all_channels.get(channel_name, None) -- cgit v1.2.3 From 00330956f6140476d9e9f633b045921b214b3d37 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Mon, 13 Mar 2023 08:03:27 +0100 Subject: remove olf version o fupgrade to community func --- botstrap.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index 472b18478..e2a9534d2 100644 --- a/botstrap.py +++ b/botstrap.py @@ -69,17 +69,7 @@ def upgrade_server_to_community_if_necessary( payload["rules_channel_id"] = rules_channel_id_ payload["public_updates_channel_id"] = announcements_channel_id_ client.patch(f"/guilds/{guild_id}", json=payload) - - -def upgrade_server_to_community(guild_id: int | str, client: DiscordClient) -> None: - """ - Transforms a server into a community one. - - Return true if the server has been correctly upgraded, False otherwise. - """ - payload = {"features": [COMMUNITY_FEATURE]} - client.patch(f"/guilds/{guild_id}", json=payload) - log.info(f"Server {guild_id} has been successfully updated to a community.") + log.info(f"Server {guild_id} has been successfully updated to a community.") def create_forum_channel( -- cgit v1.2.3 From 6a782da04193b825220de2a9cefab1983351e134 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Sun, 19 Mar 2023 17:47:31 +0100 Subject: remove "circular" referencing of channel names --- botstrap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index e2a9534d2..f882f48ab 100644 --- a/botstrap.py +++ b/botstrap.py @@ -16,9 +16,9 @@ BOT_TOKEN = os.getenv("BOT_TOKEN", None) GUILD_ID = os.getenv("GUILD_ID", None) COMMUNITY_FEATURE = "COMMUNITY" -PYTHON_HELP_CHANNEL_NAME = _Channels.__fields__["python_help"].name -ANNOUNCEMENTS_CHANNEL_NAME = _Channels.__fields__["announcements"].name -RULES_CHANNEL_NAME = _Channels.__fields__["rules"].name +PYTHON_HELP_CHANNEL_NAME = "python_help" +ANNOUNCEMENTS_CHANNEL_NAME = "announcements" +RULES_CHANNEL_NAME = "rules" GUILD_FORUM_TYPE = 15 -- cgit v1.2.3 From cc36306547b2b40abfa95a247504aba06a9ea9b6 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Fri, 24 Mar 2023 19:58:42 +0000 Subject: Improve wording in botstrap error --- botstrap.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index 90a954d9b..90c2c2fbc 100644 --- a/botstrap.py +++ b/botstrap.py @@ -18,16 +18,16 @@ 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`" + "Couldn't find the `BOT_TOKEN` environment variable. " + "Make sure to add it to your `.env` file like this: `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`" + "Couldn't find the `GUILD_ID` environment variable. " + "Make sure to add it to your `.env` file like this: `GUILD_ID=value_of_your_discord_server_id`" ) log.warning(message) raise ValueError(message) -- cgit v1.2.3 From 01c2fc14631dff8f84a3695b788317ccb0d02030 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Thu, 30 Mar 2023 12:56:19 +0100 Subject: update channels dict after forum creation --- botstrap.py | 1 + 1 file changed, 1 insertion(+) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index b77b98f1b..2ed3c17af 100644 --- a/botstrap.py +++ b/botstrap.py @@ -196,6 +196,7 @@ with DiscordClient() as discord_client: if create_help_channel: python_help_channel_name = PYTHON_HELP_CHANNEL_NAME.replace('_', '-') python_help_channel_id = create_forum_channel(python_help_channel_name, GUILD_ID, discord_client) + all_channels[PYTHON_HELP_CHANNEL_NAME] = python_help_channel_id for channel_name in _Channels.__fields__: channel_id = all_channels.get(channel_name, None) -- cgit v1.2.3 From 54bc9b2c2ffcc2633a20a0174a9ab9a0f6a848fc Mon Sep 17 00:00:00 2001 From: shtlrs Date: Thu, 30 Mar 2023 12:57:29 +0100 Subject: dedent signature --- botstrap.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index 2ed3c17af..05f96282b 100644 --- a/botstrap.py +++ b/botstrap.py @@ -55,10 +55,11 @@ class DiscordClient(Client): def upgrade_server_to_community_if_necessary( - guild_id: int | str, - rules_channel_id_: int | str, - announcements_channel_id_: int | str, - client: DiscordClient) -> None: + guild_id: int | str, + rules_channel_id_: int | str, + announcements_channel_id_: int | str, + client: DiscordClient +) -> None: """Fetches server info & upgrades to COMMUNITY if necessary.""" response = client.get(f"/guilds/{guild_id}") payload = response.json() @@ -73,10 +74,10 @@ def upgrade_server_to_community_if_necessary( def create_forum_channel( - channel_name_: str, - guild_id: str, - client: DiscordClient, - category_id_: int | None = None + channel_name_: str, + guild_id: str, + client: DiscordClient, + category_id_: int | None = None ) -> int: """Creates a new forum channel.""" payload = {"name": channel_name_, "type": GUILD_FORUM_TYPE} -- cgit v1.2.3 From e4903f80dc409d6ebee7170be8ee5e38c242d81d Mon Sep 17 00:00:00 2001 From: shtlrs Date: Thu, 30 Mar 2023 13:13:29 +0100 Subject: append forum channel to category --- bot/constants.py | 1 + botstrap.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'botstrap.py') diff --git a/bot/constants.py b/bot/constants.py index ec9130e3e..a0a20373f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -189,6 +189,7 @@ class _Categories(EnvConfig): # 2021 Summer Code Jam summer_code_jam = 861692638540857384 + python_help_system = 691405807388196926 Categories = _Categories() diff --git a/botstrap.py b/botstrap.py index 05f96282b..8d443b3f5 100644 --- a/botstrap.py +++ b/botstrap.py @@ -17,6 +17,7 @@ GUILD_ID = os.getenv("GUILD_ID", None) COMMUNITY_FEATURE = "COMMUNITY" PYTHON_HELP_CHANNEL_NAME = "python_help" +PYTHON_HELP_CATEGORY_NAME = "python_help_system" ANNOUNCEMENTS_CHANNEL_NAME = "announcements" RULES_CHANNEL_NAME = "rules" GUILD_FORUM_TYPE = 15 @@ -196,7 +197,8 @@ with DiscordClient() as discord_client: if create_help_channel: python_help_channel_name = PYTHON_HELP_CHANNEL_NAME.replace('_', '-') - python_help_channel_id = create_forum_channel(python_help_channel_name, GUILD_ID, discord_client) + python_help_category_id = all_categories[PYTHON_HELP_CATEGORY_NAME] + python_help_channel_id = create_forum_channel(python_help_channel_name, GUILD_ID, discord_client, python_help_category_id) all_channels[PYTHON_HELP_CHANNEL_NAME] = python_help_channel_id for channel_name in _Channels.__fields__: -- cgit v1.2.3 From ae3db03f95eb157db2c1c11787e7c1725d7883e6 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Thu, 30 Mar 2023 13:23:17 +0100 Subject: move all methods under the DiscordClient --- botstrap.py | 227 ++++++++++++++++++++++++++++-------------------------------- 1 file changed, 107 insertions(+), 120 deletions(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index 8d443b3f5..df783d1e4 100644 --- a/botstrap.py +++ b/botstrap.py @@ -22,7 +22,6 @@ ANNOUNCEMENTS_CHANNEL_NAME = "announcements" RULES_CHANNEL_NAME = "rules" GUILD_FORUM_TYPE = 15 - if not BOT_TOKEN: message = ( "Couldn't find the `BOT_TOKEN` environment variable. " @@ -43,130 +42,118 @@ if not GUILD_ID: class DiscordClient(Client): """An HTTP client to communicate with Discord's APIs.""" - def __init__(self): + def __init__(self, guild_id): super().__init__( base_url="https://discord.com/api/v10", headers={"Authorization": f"Bot {BOT_TOKEN}"}, event_hooks={"response": [self._raise_for_status]}, ) + self.guild_id = guild_id @staticmethod def _raise_for_status(response: Response) -> None: response.raise_for_status() - -def upgrade_server_to_community_if_necessary( - guild_id: int | str, - rules_channel_id_: int | str, - announcements_channel_id_: int | str, - client: DiscordClient -) -> None: - """Fetches server info & upgrades to COMMUNITY if necessary.""" - response = client.get(f"/guilds/{guild_id}") - payload = response.json() - - if COMMUNITY_FEATURE not in payload["features"]: - log.warning("This server is currently not a community, upgrading.") - payload["features"].append(COMMUNITY_FEATURE) - payload["rules_channel_id"] = rules_channel_id_ - payload["public_updates_channel_id"] = announcements_channel_id_ - client.patch(f"/guilds/{guild_id}", json=payload) - log.info(f"Server {guild_id} has been successfully updated to a community.") - - -def create_forum_channel( - channel_name_: str, - guild_id: str, - client: DiscordClient, - category_id_: int | None = None -) -> int: - """Creates a new forum channel.""" - payload = {"name": channel_name_, "type": GUILD_FORUM_TYPE} - if category_id_: - payload["parent_id"] = category_id_ - - response = client.post(f"/guilds/{guild_id}/channels", json=payload) - forum_channel_id = response.json()["id"] - log.info(f"New forum channel: {channel_name_} has been successfully created.") - return forum_channel_id - - -def is_forum_channel(channel_id_: str, client: DiscordClient) -> bool: - """A boolean that indicates if a channel is of type GUILD_FORUM.""" - response = client.get(f"/channels/{channel_id_}") - return response.json()["type"] == GUILD_FORUM_TYPE - - -def delete_channel(channel_id_: id, client: DiscordClient) -> None: - """Upgrades a channel to a channel of type GUILD FORUM.""" - log.info(f"Channel python-help: {channel_id_} is not a forum channel and will be replaced with one.") - client.delete(f"/channels/{channel_id_}") - - -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: + def upgrade_server_to_community_if_necessary( + self, + rules_channel_id_: int | str, + announcements_channel_id_: int | str, + ) -> None: + """Fetches server info & upgrades to COMMUNITY if necessary.""" + response = self.get(f"/guilds/{self.guild_id}") + payload = response.json() + + if COMMUNITY_FEATURE not in payload["features"]: + log.warning("This server is currently not a community, upgrading.") + payload["features"].append(COMMUNITY_FEATURE) + payload["rules_channel_id"] = rules_channel_id_ + payload["public_updates_channel_id"] = announcements_channel_id_ + self.patch(f"/guilds/{self.guild_id}", json=payload) + log.info(f"Server {self.guild_id} has been successfully updated to a community.") + + def create_forum_channel( + self, + channel_name_: str, + category_id_: int | str | None = None + ) -> int: + """Creates a new forum channel.""" + payload = {"name": channel_name_, "type": GUILD_FORUM_TYPE} + if category_id_: + payload["parent_id"] = category_id_ + + response = self.post(f"/guilds/{self.guild_id}/channels", json=payload) + forum_channel_id = response.json()["id"] + log.info(f"New forum channel: {channel_name_} has been successfully created.") + return forum_channel_id + + def is_forum_channel(self, channel_id_: str) -> bool: + """A boolean that indicates if a channel is of type GUILD_FORUM.""" + response = self.get(f"/channels/{channel_id_}") + return response.json()["type"] == GUILD_FORUM_TYPE + + def delete_channel(self, channel_id_: id) -> None: + """Upgrades a channel to a channel of type GUILD FORUM.""" + log.info(f"Channel python-help: {channel_id_} is not a forum channel and will be replaced with one.") + self.delete(f"/channels/{channel_id_}") + + def get_all_roles(self) -> dict: + """Fetches all the roles in a guild.""" + result = {} + + response = self.get(f"guilds/{self.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(self) -> 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 = self.get(f"guilds/{self.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(self, webhook_id_: int) -> bool: + """A predicate that indicates whether a webhook exists already or not.""" + try: + self.get(f"webhooks/{webhook_id_}") + return True + except HTTPStatusError: + return False + + def create_webhook(self, name: str, channel_id_: int) -> str: + """Creates a new webhook for a particular channel.""" + payload = {"name": name} + + response = self.post(f"channels/{channel_id_}/webhooks", json=payload) + new_webhook = response.json() + return new_webhook["id"] + + +with DiscordClient(guild_id=GUILD_ID) as discord_client: config_str = "#Roles\n" - all_roles = get_all_roles(guild_id=GUILD_ID, client=discord_client) + all_roles = discord_client.get_all_roles() for role_name in _Roles.__fields__: @@ -177,28 +164,28 @@ with DiscordClient() as discord_client: 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) + all_channels, all_categories = discord_client.get_all_channels_and_categories() config_str += "\n#Channels\n" rules_channel_id = all_channels[RULES_CHANNEL_NAME] announcements_channel_id = all_channels[ANNOUNCEMENTS_CHANNEL_NAME] - upgrade_server_to_community_if_necessary(GUILD_ID, rules_channel_id, announcements_channel_id, discord_client) + discord_client.upgrade_server_to_community_if_necessary(rules_channel_id, announcements_channel_id) create_help_channel = True if PYTHON_HELP_CHANNEL_NAME in all_channels: python_help_channel_id = all_channels[PYTHON_HELP_CHANNEL_NAME] - if not is_forum_channel(python_help_channel_id, discord_client): - delete_channel(python_help_channel_id, discord_client) + if not discord_client.is_forum_channel(python_help_channel_id): + discord_client.delete_channel(python_help_channel_id) else: create_help_channel = False if create_help_channel: python_help_channel_name = PYTHON_HELP_CHANNEL_NAME.replace('_', '-') python_help_category_id = all_categories[PYTHON_HELP_CATEGORY_NAME] - python_help_channel_id = create_forum_channel(python_help_channel_name, GUILD_ID, discord_client, python_help_category_id) + python_help_channel_id = discord_client.create_forum_channel(python_help_channel_name, python_help_category_id) all_channels[PYTHON_HELP_CHANNEL_NAME] = python_help_channel_id for channel_name in _Channels.__fields__: @@ -229,10 +216,10 @@ with DiscordClient() as discord_client: config_str += "\n#Webhooks\n" for webhook_name, webhook_model in Webhooks: - webhook = webhook_exists(webhook_model.id, client=discord_client) + webhook = discord_client.webhook_exists(webhook_model.id) if not webhook: webhook_channel_id = int(all_channels[webhook_name]) - webhook_id = create_webhook(webhook_name, webhook_channel_id, client=discord_client) + webhook_id = discord_client.create_webhook(webhook_name, webhook_channel_id) else: webhook_id = webhook_model.id config_str += f"webhooks_{webhook_name}__id={webhook_id}\n" -- cgit v1.2.3 From 0d049bcd9ed752f15ed4c53948f5b52f84574b3e Mon Sep 17 00:00:00 2001 From: shtlrs Date: Thu, 30 Mar 2023 13:29:24 +0100 Subject: add type annotation for guild_id --- botstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index df783d1e4..79b296288 100644 --- a/botstrap.py +++ b/botstrap.py @@ -42,7 +42,7 @@ if not GUILD_ID: class DiscordClient(Client): """An HTTP client to communicate with Discord's APIs.""" - def __init__(self, guild_id): + def __init__(self, guild_id: int | str): super().__init__( base_url="https://discord.com/api/v10", headers={"Authorization": f"Bot {BOT_TOKEN}"}, -- cgit v1.2.3 From b30475b1fbabbf970dfe1af4801152f70d917080 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Thu, 30 Mar 2023 22:18:21 +0200 Subject: introduce SilencedDict --- botstrap.py | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index 79b296288..d36222b83 100644 --- a/botstrap.py +++ b/botstrap.py @@ -1,5 +1,6 @@ import os import re +import sys from pathlib import Path from dotenv import load_dotenv @@ -39,6 +40,23 @@ if not GUILD_ID: raise ValueError(message) +class SilencedDict(dict): + """A dictionary that silences KeyError exceptions upon subscription to non existent items.""" + + def __init__(self, name: str): + self.name = name + super().__init__() + + def __getitem__(self, item: str): + try: + super().__getitem__(item) + except KeyError: + log.warning(f"Couldn't find key: {item} in dict: {self.name} ") + log.warning("Please make sure to use our template: https://discord.new/zmHtscpYN9E3" + "to make your own copy of the server to guarantee a successful run of botstrap ") + sys.exit(-1) + + class DiscordClient(Client): """An HTTP client to communicate with Discord's APIs.""" @@ -92,13 +110,13 @@ class DiscordClient(Client): return response.json()["type"] == GUILD_FORUM_TYPE def delete_channel(self, channel_id_: id) -> None: - """Upgrades a channel to a channel of type GUILD FORUM.""" + """Delete a channel.""" log.info(f"Channel python-help: {channel_id_} is not a forum channel and will be replaced with one.") self.delete(f"/channels/{channel_id_}") def get_all_roles(self) -> dict: """Fetches all the roles in a guild.""" - result = {} + result = SilencedDict(name="Roles dictionary") response = self.get(f"guilds/{self.guild_id}/roles") roles = response.json() @@ -113,8 +131,8 @@ class DiscordClient(Client): """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 = {} + channels = SilencedDict(name="Channels dictionary") + categories = SilencedDict(name="Categories dictionary") response = self.get(f"guilds/{self.guild_id}/channels") server_channels = response.json() @@ -175,12 +193,11 @@ with DiscordClient(guild_id=GUILD_ID) as discord_client: create_help_channel = True - if PYTHON_HELP_CHANNEL_NAME in all_channels: - python_help_channel_id = all_channels[PYTHON_HELP_CHANNEL_NAME] - if not discord_client.is_forum_channel(python_help_channel_id): - discord_client.delete_channel(python_help_channel_id) - else: - create_help_channel = False + python_help_channel_id = all_channels[PYTHON_HELP_CHANNEL_NAME] + if not discord_client.is_forum_channel(python_help_channel_id): + discord_client.delete_channel(python_help_channel_id) + else: + create_help_channel = False if create_help_channel: python_help_channel_name = PYTHON_HELP_CHANNEL_NAME.replace('_', '-') -- cgit v1.2.3 From dace921097e56dc39d6643f384d1ca08befc9560 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Fri, 31 Mar 2023 01:52:04 +0200 Subject: bring back the check of python help channel this also updates the logs in SilencedDict --- botstrap.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index d36222b83..d3462c122 100644 --- a/botstrap.py +++ b/botstrap.py @@ -52,8 +52,11 @@ class SilencedDict(dict): super().__getitem__(item) except KeyError: log.warning(f"Couldn't find key: {item} in dict: {self.name} ") - log.warning("Please make sure to use our template: https://discord.new/zmHtscpYN9E3" - "to make your own copy of the server to guarantee a successful run of botstrap ") + log.warning( + "Please make sure to follow our contribution guideline " + "https://www.pythondiscord.com/pages/guides/pydis-guides/contributing/bot/ " + "to guarantee a successful run of botstrap " + ) sys.exit(-1) @@ -193,11 +196,12 @@ with DiscordClient(guild_id=GUILD_ID) as discord_client: create_help_channel = True - python_help_channel_id = all_channels[PYTHON_HELP_CHANNEL_NAME] - if not discord_client.is_forum_channel(python_help_channel_id): - discord_client.delete_channel(python_help_channel_id) - else: - create_help_channel = False + if PYTHON_HELP_CHANNEL_NAME in all_channels: + python_help_channel_id = all_channels[PYTHON_HELP_CHANNEL_NAME] + if not discord_client.is_forum_channel(python_help_channel_id): + discord_client.delete_channel(python_help_channel_id) + else: + create_help_channel = False if create_help_channel: python_help_channel_name = PYTHON_HELP_CHANNEL_NAME.replace('_', '-') -- cgit v1.2.3 From caaa13f78d8e9a577a08d5b20847bdd597ef6a38 Mon Sep 17 00:00:00 2001 From: shtlrs Date: Fri, 31 Mar 2023 14:41:49 +0100 Subject: return value from SilencedDict --- botstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'botstrap.py') diff --git a/botstrap.py b/botstrap.py index d3462c122..ccf6993f5 100644 --- a/botstrap.py +++ b/botstrap.py @@ -49,7 +49,7 @@ class SilencedDict(dict): def __getitem__(self, item: str): try: - super().__getitem__(item) + return super().__getitem__(item) except KeyError: log.warning(f"Couldn't find key: {item} in dict: {self.name} ") log.warning( -- cgit v1.2.3