@@ -134,8 +134,47 @@ def __getitem__(self, item: str) -> "Any": # type: ignore[explicit-any] # noqa
134134
135135 raise KeyError (key_error_message ) from None
136136
137- @staticmethod
138- def _setup_logging () -> None :
137+ @classmethod
138+ def _setup_discord_log_channel_webhook (cls ) -> "Logger" :
139+ raw_discord_log_channel_webhook_url : str = os .getenv (
140+ "DISCORD_LOG_CHANNEL_WEBHOOK_URL" ,
141+ "" ,
142+ )
143+
144+ if raw_discord_log_channel_webhook_url and (
145+ not validators .url (raw_discord_log_channel_webhook_url )
146+ or not raw_discord_log_channel_webhook_url .startswith (
147+ "https://discord.com/api/webhooks/"
148+ )
149+ ):
150+ INVALID_DISCORD_LOG_CHANNEL_WEBHOOK_URL_MESSAGE : Final [str ] = (
151+ "DISCORD_LOG_CHANNEL_WEBHOOK_URL must be a valid webhook URL "
152+ "that points to a discord channel where logs should be displayed."
153+ )
154+ raise ImproperlyConfiguredError (INVALID_DISCORD_LOG_CHANNEL_WEBHOOK_URL_MESSAGE )
155+
156+ webhook_config_logger : Logger = logging .getLogger ("_temp_webhook_config" )
157+
158+ if raw_discord_log_channel_webhook_url :
159+ discord_logging_handler : logging .Handler = DiscordHandler (
160+ service_name = "TeX-Bot" ,
161+ webhook_url = raw_discord_log_channel_webhook_url ,
162+ )
163+
164+ discord_logging_handler .setLevel (logging .WARNING )
165+
166+ discord_logging_handler .setFormatter (
167+ logging .Formatter ("{levelname} | {message}" , style = "{" ),
168+ )
169+
170+ webhook_config_logger .addHandler (discord_logging_handler )
171+
172+ cls ._settings ["DISCORD_LOG_CHANNEL_WEBHOOK_URL" ] = raw_discord_log_channel_webhook_url
173+
174+ return webhook_config_logger
175+
176+ @classmethod
177+ def _setup_logging (cls ) -> None :
139178 raw_console_log_level : str = str (os .getenv ("CONSOLE_LOG_LEVEL" , "INFO" )).upper ()
140179
141180 if raw_console_log_level not in LOG_LEVEL_CHOICES :
@@ -177,47 +216,6 @@ def _setup_discord_bot_token(cls) -> None:
177216
178217 cls ._settings ["DISCORD_BOT_TOKEN" ] = raw_discord_bot_token
179218
180- @classmethod
181- def _setup_discord_log_channel_webhook_url (cls ) -> None :
182- raw_discord_log_channel_webhook_url : str = os .getenv (
183- "DISCORD_LOG_CHANNEL_WEBHOOK_URL" ,
184- "" ,
185- )
186-
187- DISCORD_LOG_CHANNEL_WEBHOOK_URL_IS_VALID : Final [bool ] = bool (
188- not raw_discord_log_channel_webhook_url
189- or (
190- validators .url (raw_discord_log_channel_webhook_url )
191- and raw_discord_log_channel_webhook_url .startswith (
192- "https://discord.com/api/webhooks/" ,
193- )
194- ),
195- )
196- if not DISCORD_LOG_CHANNEL_WEBHOOK_URL_IS_VALID :
197- INVALID_DISCORD_LOG_CHANNEL_WEBHOOK_URL_MESSAGE : Final [str ] = (
198- "DISCORD_LOG_CHANNEL_WEBHOOK_URL must be a valid webhook URL "
199- "that points to a discord channel where logs should be displayed."
200- )
201- raise ImproperlyConfiguredError (INVALID_DISCORD_LOG_CHANNEL_WEBHOOK_URL_MESSAGE )
202-
203- cls ._settings ["DISCORD_LOG_CHANNEL_WEBHOOK_URL" ] = raw_discord_log_channel_webhook_url
204-
205- @classmethod
206- def _setup_discord_logging_channel (cls ) -> None :
207- if cls ._settings ["DISCORD_LOG_CHANNEL_WEBHOOK_URL" ]:
208- discord_logging_handler : logging .Handler = DiscordHandler (
209- service_name = "TeX-Bot" ,
210- webhook_url = cls ._settings ["DISCORD_LOG_CHANNEL_WEBHOOK_URL" ],
211- )
212-
213- discord_logging_handler .setLevel (logging .WARNING )
214-
215- discord_logging_handler .setFormatter (
216- logging .Formatter ("{levelname} | {message}" , style = "{" ),
217- )
218-
219- logger .addHandler (discord_logging_handler )
220-
221219 @classmethod
222220 def _setup_discord_guild_id (cls ) -> None :
223221 raw_discord_guild_id : str | None = os .getenv ("DISCORD_GUILD_ID" )
@@ -230,7 +228,6 @@ def _setup_discord_guild_id(cls) -> None:
230228 "DISCORD_GUILD_ID must be a valid Discord guild ID "
231229 "(see https://docs.pycord.dev/en/stable/api/abcs.html#discord.abc.Snowflake.id)."
232230 )
233- logger .error (INVALID_DISCORD_GUILD_ID_MESSAGE )
234231 raise ImproperlyConfiguredError (INVALID_DISCORD_GUILD_ID_MESSAGE )
235232
236233 cls ._settings ["_DISCORD_MAIN_GUILD_ID" ] = int (raw_discord_guild_id ) # type: ignore[arg-type]
@@ -247,8 +244,8 @@ def _setup_group_full_name(cls) -> None:
247244 INVALID_GROUP_FULL_NAME : Final [str ] = (
248245 "GROUP_NAME must not contain any invalid characters."
249246 )
250- logger .error (INVALID_GROUP_FULL_NAME )
251247 raise ImproperlyConfiguredError (INVALID_GROUP_FULL_NAME )
248+
252249 cls ._settings ["_GROUP_FULL_NAME" ] = raw_group_full_name
253250
254251 @classmethod
@@ -263,8 +260,8 @@ def _setup_group_short_name(cls) -> None:
263260 INVALID_GROUP_SHORT_NAME : Final [str ] = (
264261 "GROUP_SHORT_NAME must not contain any invalid characters."
265262 )
266- logger .error (INVALID_GROUP_SHORT_NAME )
267263 raise ImproperlyConfiguredError (INVALID_GROUP_SHORT_NAME )
264+
268265 cls ._settings ["_GROUP_SHORT_NAME" ] = raw_group_short_name
269266
270267 @classmethod
@@ -278,7 +275,6 @@ def _setup_purchase_membership_url(cls) -> None:
278275 INVALID_PURCHASE_MEMBERSHIP_URL_MESSAGE : Final [str ] = (
279276 "PURCHASE_MEMBERSHIP_URL must be a valid URL."
280277 )
281- logger .error (INVALID_PURCHASE_MEMBERSHIP_URL_MESSAGE )
282278 raise ImproperlyConfiguredError (INVALID_PURCHASE_MEMBERSHIP_URL_MESSAGE )
283279
284280 cls ._settings ["PURCHASE_MEMBERSHIP_URL" ] = raw_purchase_membership_url
@@ -294,7 +290,6 @@ def _setup_membership_perks_url(cls) -> None:
294290 INVALID_MEMBERSHIP_PERKS_URL_MESSAGE : Final [str ] = (
295291 "MEMBERSHIP_PERKS_URL must be a valid URL."
296292 )
297- logger .error (INVALID_MEMBERSHIP_PERKS_URL_MESSAGE )
298293 raise ImproperlyConfiguredError (INVALID_MEMBERSHIP_PERKS_URL_MESSAGE )
299294
300295 cls ._settings ["MEMBERSHIP_PERKS_URL" ] = raw_membership_perks_url
@@ -311,7 +306,6 @@ def _setup_ping_command_easter_egg_probability(cls) -> None:
311306 os .getenv ("PING_COMMAND_EASTER_EGG_PROBABILITY" , "0.01" ),
312307 )
313308 except ValueError as e :
314- logger .error (INVALID_PING_COMMAND_EASTER_EGG_PROBABILITY_MESSAGE ) # noqa: TRY400
315309 raise (
316310 ImproperlyConfiguredError (INVALID_PING_COMMAND_EASTER_EGG_PROBABILITY_MESSAGE )
317311 ) from e
@@ -343,7 +337,6 @@ def _get_messages_dict(cls, raw_messages_file_path: str | None) -> Mapping[str,
343337 MESSAGES_FILE_PATH_DOES_NOT_EXIST_MESSAGE : Final [str ] = (
344338 "MESSAGES_FILE_PATH must be a path to a file that exists."
345339 )
346- logger .error (MESSAGES_FILE_PATH_DOES_NOT_EXIST_MESSAGE )
347340 raise ImproperlyConfiguredError (MESSAGES_FILE_PATH_DOES_NOT_EXIST_MESSAGE )
348341
349342 messages_file : IO [str ]
@@ -352,11 +345,9 @@ def _get_messages_dict(cls, raw_messages_file_path: str | None) -> Mapping[str,
352345 try :
353346 messages_dict : object = json .load (messages_file )
354347 except json .JSONDecodeError as e :
355- logger .error (JSON_DECODING_ERROR_MESSAGE ) # noqa: TRY400
356348 raise ImproperlyConfiguredError (JSON_DECODING_ERROR_MESSAGE ) from e
357349
358350 if not isinstance (messages_dict , Mapping ):
359- logger .error (JSON_DECODING_ERROR_MESSAGE )
360351 raise ImproperlyConfiguredError (JSON_DECODING_ERROR_MESSAGE )
361352
362353 return messages_dict
@@ -375,7 +366,6 @@ def _setup_welcome_messages(cls) -> None:
375366 and messages_dict ["welcome_messages" ],
376367 )
377368 if not WELCOME_MESSAGES_KEY_IS_VALID :
378- logger .error ("Unable to locate welcome messages." )
379369 raise MessagesJSONFileValueError (
380370 dict_key = "welcome_messages" ,
381371 invalid_value = messages_dict ["welcome_messages" ],
@@ -396,11 +386,11 @@ def _setup_roles_messages(cls) -> None:
396386 messages_dict ["roles_messages" ], Iterable
397387 ) and bool (messages_dict ["roles_messages" ])
398388 if not ROLES_MESSAGES_KEY_IS_VALID :
399- logger .error ("Unable to locate role messages." )
400389 raise MessagesJSONFileValueError (
401390 dict_key = "roles_messages" ,
402391 invalid_value = messages_dict ["roles_messages" ],
403392 )
393+
404394 cls ._settings ["ROLES_MESSAGES" ] = set (messages_dict ["roles_messages" ]) # type: ignore[call-overload]
405395
406396 @classmethod
@@ -415,7 +405,6 @@ def _setup_organisation_id(cls) -> None:
415405 INVALID_ORGANISATION_ID_MESSAGE : Final [str ] = (
416406 "ORGANISATION_ID must be an integer 4 to 5 digits long."
417407 )
418- logger .error (INVALID_ORGANISATION_ID_MESSAGE )
419408 raise ImproperlyConfiguredError (message = INVALID_ORGANISATION_ID_MESSAGE )
420409
421410 cls ._settings ["ORGANISATION_ID" ] = raw_organisation_id
@@ -434,7 +423,6 @@ def _setup_members_list_auth_session_cookie(cls) -> None:
434423 INVALID_MEMBERS_LIST_AUTH_SESSION_COOKIE_MESSAGE : Final [str ] = (
435424 "MEMBERS_LIST_URL_SESSION_COOKIE must be a valid .ASPXAUTH cookie."
436425 )
437- logger .error (INVALID_MEMBERS_LIST_AUTH_SESSION_COOKIE_MESSAGE )
438426 raise ImproperlyConfiguredError (INVALID_MEMBERS_LIST_AUTH_SESSION_COOKIE_MESSAGE )
439427
440428 cls ._settings ["MEMBERS_LIST_AUTH_SESSION_COOKIE" ] = (
@@ -451,7 +439,6 @@ def _setup_send_introduction_reminders(cls) -> None:
451439 INVALID_SEND_INTRODUCTION_REMINDERS_MESSAGE : Final [str ] = (
452440 'SEND_INTRODUCTION_REMINDERS must be one of: "Once", "Interval" or "False".'
453441 )
454- logger .error (INVALID_SEND_INTRODUCTION_REMINDERS_MESSAGE )
455442 raise ImproperlyConfiguredError (INVALID_SEND_INTRODUCTION_REMINDERS_MESSAGE )
456443
457444 if raw_send_introduction_reminders in TRUE_VALUES :
@@ -484,7 +471,6 @@ def _setup_send_introduction_reminders_delay(cls) -> None:
484471 "SEND_INTRODUCTION_REMINDERS_DELAY must contain the delay "
485472 "in any combination of seconds, minutes, hours, days or weeks."
486473 )
487- logger .error (INVALID_SEND_INTRODUCTION_REMINDERS_DELAY_MESSAGE )
488474 raise ImproperlyConfiguredError (
489475 INVALID_SEND_INTRODUCTION_REMINDERS_DELAY_MESSAGE ,
490476 )
@@ -502,7 +488,6 @@ def _setup_send_introduction_reminders_delay(cls) -> None:
502488 "SEND_INTRODUCTION_REMINDERS_DELAY must be longer than or equal to 1 day "
503489 "(in any allowed format)."
504490 )
505- logger .error (TOO_SMALL_SEND_INTRODUCTION_REMINDERS_DELAY_MESSAGE )
506491 raise ImproperlyConfiguredError (
507492 TOO_SMALL_SEND_INTRODUCTION_REMINDERS_DELAY_MESSAGE ,
508493 )
@@ -518,7 +503,6 @@ def _setup_send_introduction_reminders_interval(cls) -> None:
518503 "Invalid setup order: SEND_INTRODUCTION_REMINDERS must be set up "
519504 "before SEND_INTRODUCTION_REMINDERS_INTERVAL can be set up."
520505 )
521- logger .error (INVALID_SETUP_ORDER_MESSAGE )
522506 raise RuntimeError (INVALID_SETUP_ORDER_MESSAGE )
523507
524508 raw_send_introduction_reminders_interval : re .Match [str ] | None = re .fullmatch (
@@ -536,7 +520,6 @@ def _setup_send_introduction_reminders_interval(cls) -> None:
536520 "SEND_INTRODUCTION_REMINDERS_INTERVAL must contain the interval "
537521 "in any combination of seconds, minutes or hours."
538522 )
539- logger .error (INVALID_SEND_INTRODUCTION_REMINDERS_INTERVAL_MESSAGE )
540523 raise ImproperlyConfiguredError (
541524 INVALID_SEND_INTRODUCTION_REMINDERS_INTERVAL_MESSAGE ,
542525 )
@@ -561,7 +544,6 @@ def _setup_send_get_roles_reminders(cls) -> None:
561544 INVALID_SEND_GET_ROLES_REMINDERS_MESSAGE : Final [str ] = (
562545 "SEND_GET_ROLES_REMINDERS must be a boolean value."
563546 )
564- logger .error (INVALID_SEND_GET_ROLES_REMINDERS_MESSAGE )
565547 raise ImproperlyConfiguredError (INVALID_SEND_GET_ROLES_REMINDERS_MESSAGE )
566548
567549 cls ._settings ["SEND_GET_ROLES_REMINDERS" ] = raw_send_get_roles_reminders in TRUE_VALUES
@@ -588,7 +570,6 @@ def _setup_send_get_roles_reminders_delay(cls) -> None:
588570 "SEND_GET_ROLES_REMINDERS_DELAY must contain the delay "
589571 "in any combination of seconds, minutes, hours, days or weeks."
590572 )
591- logger .error (INVALID_SEND_GET_ROLES_REMINDERS_DELAY_MESSAGE )
592573 raise ImproperlyConfiguredError (
593574 INVALID_SEND_GET_ROLES_REMINDERS_DELAY_MESSAGE ,
594575 )
@@ -606,7 +587,6 @@ def _setup_send_get_roles_reminders_delay(cls) -> None:
606587 "SEND_SEND_GET_ROLES_REMINDERS_DELAY "
607588 "must be longer than or equal to 1 day (in any allowed format)."
608589 )
609- logger .error (TOO_SMALL_SEND_GET_ROLES_REMINDERS_DELAY_MESSAGE )
610590 raise ImproperlyConfiguredError (
611591 TOO_SMALL_SEND_GET_ROLES_REMINDERS_DELAY_MESSAGE ,
612592 )
@@ -622,7 +602,6 @@ def _setup_advanced_send_get_roles_reminders_interval(cls) -> None:
622602 "Invalid setup order: SEND_GET_ROLES_REMINDERS must be set up "
623603 "before ADVANCED_SEND_GET_ROLES_REMINDERS_INTERVAL can be set up."
624604 )
625- logger .error (INVALID_SETUP_ORDER_MESSAGE )
626605 raise RuntimeError (INVALID_SETUP_ORDER_MESSAGE )
627606
628607 raw_advanced_send_get_roles_reminders_interval : re .Match [str ] | None = re .fullmatch (
@@ -642,7 +621,6 @@ def _setup_advanced_send_get_roles_reminders_interval(cls) -> None:
642621 "ADVANCED_SEND_GET_ROLES_REMINDERS_INTERVAL must contain the interval "
643622 "in any combination of seconds, minutes or hours."
644623 )
645- logger .error (INVALID_ADVANCED_SEND_GET_ROLES_REMINDERS_INTERVAL_MESSAGE )
646624 raise ImproperlyConfiguredError (
647625 INVALID_ADVANCED_SEND_GET_ROLES_REMINDERS_INTERVAL_MESSAGE ,
648626 )
@@ -668,7 +646,6 @@ def _setup_statistics_days(cls) -> None:
668646 INVALID_STATISTICS_DAYS_MESSAGE : Final [str ] = (
669647 "STATISTICS_DAYS must contain the statistics period in days."
670648 )
671- logger .error (INVALID_STATISTICS_DAYS_MESSAGE ) # noqa: TRY400
672649 raise ImproperlyConfiguredError (INVALID_STATISTICS_DAYS_MESSAGE ) from e
673650
674651 cls ._settings ["STATISTICS_DAYS" ] = timedelta (days = raw_statistics_days )
@@ -698,7 +675,6 @@ def _setup_moderation_document_url(cls) -> None:
698675 MODERATION_DOCUMENT_URL_MESSAGE : Final [str ] = (
699676 "MODERATION_DOCUMENT_URL must be a valid URL."
700677 )
701- logger .error (MODERATION_DOCUMENT_URL_MESSAGE )
702678 raise ImproperlyConfiguredError (MODERATION_DOCUMENT_URL_MESSAGE )
703679
704680 cls ._settings ["MODERATION_DOCUMENT_URL" ] = raw_moderation_document_url
@@ -714,7 +690,6 @@ def _setup_strike_performed_manually_warning_location(cls) -> None:
714690 "MANUAL_MODERATION_WARNING_MESSAGE_LOCATION must be a valid name "
715691 "of a channel in your group's Discord guild."
716692 )
717- logger .error (STRIKE_PERFORMED_MANUALLY_WARNING_LOCATION_MESSAGE )
718693 raise ImproperlyConfiguredError (STRIKE_PERFORMED_MANUALLY_WARNING_LOCATION_MESSAGE )
719694
720695 cls ._settings ["STRIKE_PERFORMED_MANUALLY_WARNING_LOCATION" ] = (
@@ -751,31 +726,35 @@ def _setup_env_variables(cls) -> None:
751726
752727 dotenv .load_dotenv ()
753728
754- cls ._setup_logging ()
755- cls ._setup_discord_bot_token ()
756- cls ._setup_discord_log_channel_webhook_url ()
757- cls ._setup_discord_logging_channel ()
758- cls ._setup_discord_guild_id ()
759- cls ._setup_group_full_name ()
760- cls ._setup_group_short_name ()
761- cls ._setup_ping_command_easter_egg_probability ()
762- cls ._setup_welcome_messages ()
763- cls ._setup_roles_messages ()
764- cls ._setup_organisation_id ()
765- cls ._setup_members_list_auth_session_cookie ()
766- cls ._setup_membership_perks_url ()
767- cls ._setup_purchase_membership_url ()
768- cls ._setup_send_introduction_reminders ()
769- cls ._setup_send_introduction_reminders_delay ()
770- cls ._setup_send_introduction_reminders_interval ()
771- cls ._setup_send_get_roles_reminders ()
772- cls ._setup_send_get_roles_reminders_delay ()
773- cls ._setup_advanced_send_get_roles_reminders_interval ()
774- cls ._setup_statistics_days ()
775- cls ._setup_statistics_roles ()
776- cls ._setup_moderation_document_url ()
777- cls ._setup_strike_performed_manually_warning_location ()
778- cls ._setup_auto_add_committee_to_threads ()
729+ webhook_config_logger : Logger = cls ._setup_discord_log_channel_webhook ()
730+
731+ try :
732+ cls ._setup_logging ()
733+ cls ._setup_discord_bot_token ()
734+ cls ._setup_discord_guild_id ()
735+ cls ._setup_group_full_name ()
736+ cls ._setup_group_short_name ()
737+ cls ._setup_ping_command_easter_egg_probability ()
738+ cls ._setup_welcome_messages ()
739+ cls ._setup_roles_messages ()
740+ cls ._setup_organisation_id ()
741+ cls ._setup_members_list_auth_session_cookie ()
742+ cls ._setup_membership_perks_url ()
743+ cls ._setup_purchase_membership_url ()
744+ cls ._setup_send_introduction_reminders ()
745+ cls ._setup_send_introduction_reminders_delay ()
746+ cls ._setup_send_introduction_reminders_interval ()
747+ cls ._setup_send_get_roles_reminders ()
748+ cls ._setup_send_get_roles_reminders_delay ()
749+ cls ._setup_advanced_send_get_roles_reminders_interval ()
750+ cls ._setup_statistics_days ()
751+ cls ._setup_statistics_roles ()
752+ cls ._setup_moderation_document_url ()
753+ cls ._setup_strike_performed_manually_warning_location ()
754+ cls ._setup_auto_add_committee_to_threads ()
755+ except ImproperlyConfiguredError as e :
756+ webhook_config_logger .error (e .message ) # noqa: TRY400
757+ raise e from e
779758
780759 cls ._is_env_variables_setup = True
781760
@@ -799,8 +778,7 @@ class RuntimeSettings(Settings):
799778
800779
801780def run_setup () -> None :
802- """Execute the setup functions required, before other modules can be run."""
803- # noinspection PyProtectedMember
781+ """Execute the required setup functions."""
804782 settings ._setup_env_variables () # noqa: SLF001
805783
806784 logger .debug ("Begin database setup" )
0 commit comments