Skip to content

Commit 5b01113

Browse files
committed
Streamline configuration
Model all configuration as cog-specific pydantic.BaseModels.
1 parent 5cf865a commit 5b01113

File tree

9 files changed

+138
-132
lines changed

9 files changed

+138
-132
lines changed

prod-config.toml

+16-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
[item_to_role]
1+
log_level = "INFO"
2+
3+
[registration]
4+
registration_form_channel_name = "registration-form"
5+
registration_help_channel_name = "registration-help"
6+
registration_log_channel_name = "registration-log"
7+
8+
pretix_base_url = "https://pretix.eu/api/v1/organizers/europython/events/ep2025"
9+
10+
registered_cache_file = "registered_log.txt"
11+
pretix_cache_file = "pretix_cache.json"
12+
13+
[registration.item_to_roles]
214
# onsite participants
315
"Business" = ["Participants", "Onsite Participants"]
416
"Personal" = ["Participants", "Onsite Participants"]
@@ -14,22 +26,9 @@
1426
# speakers
1527
"Presenter" = ["Participants", "Onsite Participants", "Speakers"]
1628

17-
[variation_to_role]
29+
[registration.variation_to_roles]
1830
"Volunteer" = ["Volunteers"]
1931

20-
[registration]
21-
REG_CHANNEL_NAME = "registration-form"
22-
REG_HELP_CHANNEL_NAME = "registration-help"
23-
REG_LOG_CHANNEL_NAME = "registration-log"
24-
REGISTERED_LOG_FILE = "registered_log.txt"
25-
26-
[pretix]
27-
PRETIX_BASE_URL = "https://pretix.eu/api/v1/organizers/europython/events/ep2025"
28-
PRETIX_CACHE_FILE = "pretix_cache.json"
29-
30-
[logging]
31-
LOG_LEVEL = "INFO"
32-
3332
[program_notifications]
3433
# UTC offset in hours (e.g. 2 for CEST)
3534
timezone_offset = 2
@@ -39,7 +38,7 @@ livestream_url_file = "livestreams.toml"
3938
main_notification_channel_name = "programme-notifications"
4039

4140
# optional simulated start time for testing program notifications
42-
# simulated_start_time = "2024-07-10T07:30:00"
41+
# simulated_start_time = "2024-07-10T07:30:00+02:00"
4342

4443
# optional fast mode for faster testing of program notifications
4544
# will only take effect if simulated_start_time is set
@@ -54,5 +53,5 @@ main_notification_channel_name = "programme-notifications"
5453
"Terrace 2B" = "terrace-2b"
5554
"Exhibit Hall" = "exhibit-hall"
5655

57-
[server_statistics]
56+
[guild_statistics]
5857
required_role = "Organizers"

src/europython_discord/bot.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
1+
from __future__ import annotations
2+
13
import argparse
24
import asyncio
35
import logging
46
import os
57
import sys
8+
import tomllib
69
from pathlib import Path
10+
from typing import Literal
711

812
import discord
913
from discord.ext import commands
14+
from pydantic import BaseModel
1015

11-
from europython_discord.cogs.guild_statistics import GuildStatisticsCog
16+
from europython_discord.cogs.guild_statistics import GuildStatisticsCog, GuildStatisticsConfig
1217
from europython_discord.cogs.ping import PingCog
13-
from europython_discord.configuration import Config
1418
from europython_discord.program_notifications.cog import ProgramNotificationsCog
19+
from europython_discord.program_notifications.config import ProgramNotificationsConfig
1520
from europython_discord.registration.cog import RegistrationCog
21+
from europython_discord.registration.config import RegistrationConfig
1622

1723
_logger = logging.getLogger(__name__)
1824

1925

26+
class Config(BaseModel):
27+
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
28+
registration: RegistrationConfig
29+
program_notifications: ProgramNotificationsConfig
30+
guild_statistics: GuildStatisticsConfig
31+
32+
2033
async def run_bot(config: Config, auth_token: str) -> None:
2134
intents = discord.Intents.all()
2235
intents.presences = False
@@ -27,9 +40,9 @@ async def run_bot(config: Config, auth_token: str) -> None:
2740

2841
async with commands.Bot(intents=intents, command_prefix="$") as bot:
2942
await bot.add_cog(PingCog(bot))
30-
await bot.add_cog(RegistrationCog(bot, config))
31-
await bot.add_cog(ProgramNotificationsCog(bot, config))
32-
await bot.add_cog(GuildStatisticsCog(bot, config.ROLE_REQUIRED_FOR_STATISTICS))
43+
await bot.add_cog(RegistrationCog(bot, config.registration))
44+
await bot.add_cog(ProgramNotificationsCog(bot, config.program_notifications))
45+
await bot.add_cog(GuildStatisticsCog(bot, config.guild_statistics))
3346

3447
await bot.start(auth_token)
3548

@@ -47,10 +60,11 @@ def main() -> None:
4760
raise RuntimeError("Missing environment variable 'DISCORD_BOT_TOKEN'")
4861
bot_auth_token = os.environ["DISCORD_BOT_TOKEN"]
4962

50-
config = Config(Path(config_file))
63+
config_file_content = Path(config_file).read_text()
64+
config = Config(**tomllib.loads(config_file_content))
5165

5266
logging.basicConfig(
53-
level=config.LOG_LEVEL,
67+
level=config.log_level,
5468
stream=sys.stdout,
5569
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
5670
)

src/europython_discord/cogs/guild_statistics.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
"""Commands for organisers."""
22

3+
from __future__ import annotations
4+
35
import logging
46

57
from discord import Role
68
from discord.ext import commands
79
from discord.utils import get as discord_get
10+
from pydantic import BaseModel
811

912
_logger = logging.getLogger(__name__)
1013

1114

15+
class GuildStatisticsConfig(BaseModel):
16+
required_role: str
17+
18+
1219
class GuildStatisticsCog(commands.Cog):
1320
"""A cog with commands for organisers."""
1421

15-
def __init__(self, bot: commands.Bot, required_role_name: str) -> None:
22+
def __init__(self, bot: commands.Bot, config: GuildStatisticsConfig) -> None:
1623
self._bot = bot
17-
self._required_role_name = required_role_name
24+
self._required_role_name = config.required_role
1825

1926
@commands.command(name="participants")
2027
async def list_participants(self, ctx: commands.Context) -> None:

src/europython_discord/configuration.py

-58
This file was deleted.

src/europython_discord/program_notifications/cog.py

+15-15
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@
44
from discord.ext import commands, tasks
55
from discord.utils import get as discord_get
66

7-
from europython_discord.configuration import Config
87
from europython_discord.program_notifications import session_to_embed
8+
from europython_discord.program_notifications.config import ProgramNotificationsConfig
99
from europython_discord.program_notifications.livestream_connector import LivestreamConnector
1010
from europython_discord.program_notifications.program_connector import ProgramConnector
1111

1212
_logger = logging.getLogger(__name__)
1313

1414

1515
class ProgramNotificationsCog(commands.Cog):
16-
def __init__(self, bot: Client, config: Config) -> None:
16+
def __init__(self, bot: Client, config: ProgramNotificationsConfig) -> None:
1717
self.bot = bot
1818
self.config = config
1919
self.program_connector = ProgramConnector(
20-
api_url=self.config.PROGRAM_API_URL,
21-
timezone_offset=self.config.TIMEZONE_OFFSET,
22-
cache_file=self.config.SCHEDULE_CACHE_FILE,
23-
simulated_start_time=self.config.SIMULATED_START_TIME,
24-
fast_mode=self.config.FAST_MODE,
20+
api_url=self.config.api_url,
21+
timezone_offset=self.config.timezone_offset,
22+
cache_file=self.config.schedule_cache_file,
23+
simulated_start_time=self.config.simulated_start_time,
24+
fast_mode=self.config.fast_mode,
2525
)
2626

27-
self.livestream_connector = LivestreamConnector(self.config.LIVESTREAM_URL_FILE)
27+
self.livestream_connector = LivestreamConnector(self.config.livestream_url_file)
2828

2929
self.notified_sessions = set()
3030
_logger.info("Cog 'Program Notifications' has been initialized")
3131

3232
@commands.Cog.listener()
3333
async def on_ready(self) -> None:
34-
if self.config.SIMULATED_START_TIME:
34+
if self.config.simulated_start_time:
3535
_logger.info("Running in simulated time mode.")
3636
_logger.info("Will purge all room channels to avoid pile-up of test notifications.")
3737
await self.purge_all_room_channels()
38-
_logger.debug(f"Simulated start time: {self.config.SIMULATED_START_TIME}")
39-
_logger.debug(f"Fast mode: {self.config.FAST_MODE}")
38+
_logger.debug(f"Simulated start time: {self.config.simulated_start_time}")
39+
_logger.debug(f"Fast mode: {self.config.fast_mode}")
4040
_logger.info("Starting the session notifier...")
4141
self.notify_sessions.start()
4242
_logger.info("Cog 'Program Notifications' is ready")
@@ -49,7 +49,7 @@ async def cog_load(self) -> None:
4949
self.fetch_schedule.start()
5050
self.fetch_livestreams.start()
5151
self.notify_sessions.change_interval(
52-
seconds=2 if self.config.FAST_MODE and self.config.SIMULATED_START_TIME else 60
52+
seconds=2 if self.config.fast_mode and self.config.simulated_start_time else 60
5353
)
5454
_logger.info("Schedule updater started and interval set for the session notifier")
5555

@@ -86,7 +86,7 @@ async def notify_sessions(self) -> None:
8686
return
8787

8888
main_notification_channel = discord_get(
89-
self.bot.get_all_channels(), name=self.config.MAIN_NOTIFICATION_CANNEL_NAME
89+
self.bot.get_all_channels(), name=self.config.main_notification_channel_name
9090
)
9191
await main_notification_channel.send(content="# Sessions starting in 5 minutes:")
9292

@@ -116,13 +116,13 @@ async def notify_sessions(self) -> None:
116116

117117
async def purge_all_room_channels(self) -> None:
118118
_logger.info("Purging all room channels...")
119-
for channel_name in self.config.ROOMS_TO_CHANNEL_NAMES.values():
119+
for channel_name in self.config.rooms_to_channel_names.values():
120120
channel = discord_get(self.bot.get_all_channels(), name=channel_name)
121121
await channel.purge()
122122
_logger.info("Purged all room channels.")
123123

124124
def _get_room_channel(self, room_name: str) -> TextChannel | None:
125-
channel_name = self.config.ROOMS_TO_CHANNEL_NAMES.get(room_name)
125+
channel_name = self.config.rooms_to_channel_names.get(room_name)
126126
if channel_name is None:
127127
_logger.warning(f"No notification channel configured for room {room_name!r}")
128128
return None
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Mapping
4+
from datetime import datetime
5+
from pathlib import Path
6+
7+
from pydantic import BaseModel
8+
9+
10+
class ProgramNotificationsConfig(BaseModel):
11+
timezone_offset: int
12+
api_url: str
13+
schedule_cache_file: Path
14+
livestream_url_file: Path
15+
main_notification_channel_name: str
16+
rooms_to_channel_names: Mapping[str, str]
17+
18+
simulated_start_time: datetime | None = None
19+
fast_mode: bool = False

0 commit comments

Comments
 (0)