Skip to content

Commit 5c118f0

Browse files
authored
Refactor configuration (#213)
* Remove unused forward reference * Registration: Use channel names instead of IDs * Registration: Use role names instead of IDs * Refactor config and cog setup General * User __name__ as logger name everywhere '$participants' command: * Move from extension module to simple cog * Use dynamic role list instead of hard-coded role names * Add channel visibility check bot.py * Simplify and refactor program notifications * Use channel names in config, not IDs * Simplify notification logic * Make Config non-Singleton * Streamline configuration * Config: Require file path explicitly * run-bot: Expect config file path as --config-file or CONFIG_FILE * README: Update * uv: Remove python-dotenv and certifi dependencies * Move config files to repo root * Docker: Expect CONFIG_FILE * Compose: Specify config file and .secrets * Streamline configuration Model all configuration as cog-specific pydantic.BaseModels. * Remove unused dependencies Remove certifi, yarl and arrow * Remove ansible * Docker: Use bind instead of COPY for prod-config.toml * Require --config-file, drop CONFIG_FILE * Dockerfile: Drop $PATH overwrite * Update livestreams and schedule-cache file paths * Registration: Improve accessibility Use plain messages instead of embeds, use ASCII codes instead of unicode emojis * fix formatting * Fix emoji insertion on button label * Update README and screenshots
1 parent 005d7b2 commit 5c118f0

31 files changed

+511
-1014
lines changed

.gitignore

+2-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
.secrets
33
.idea
44
.vscode
5-
README.md
6-
src/europython_discord/config.local.toml
75
__pycache__
86
.DS_Store
97
registered_log.txt
10-
schedule.json
8+
schedule_cache.json
119
pretix_cache.json
1210
*.egg-info/
11+
livestreams.toml

Dockerfile

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ RUN groupadd --gid 1000 bot && \
88
USER bot
99
WORKDIR /home/bot
1010

11-
ENV PATH="/home/bot/.local/bin:$PATH"
12-
1311
COPY --chown=bot:bot pyproject.toml uv.lock ./
1412
COPY --chown=bot:bot src ./src
1513

1614
RUN uv sync
1715

18-
ENTRYPOINT ["uv", "run", "run-bot"]
16+
ENTRYPOINT ["uv", "run", "run-bot", "--config-file", "prod-config.toml"]

README.md

+71-50
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,95 @@
1-
# Europython Discord Bot
1+
# Europython Discord
22

3-
An easy to deploy conference bot that manages roles for attendees via registration, notifies about upcoming sessions.
4-
Exposes Discord server statistics to organizers.
5-
We hosted the bot on Hetzner. And deployed with a single click Action from GitHub 😎.
3+
A suite of tools for managing the EuroPython Conference Discord server:
64

7-
![registration_view.png](./img/registration_view.png)
5+
* [src/europython_discord](./src/europython_discord): Discord bot
6+
* [scripts/configure-guild.py](./scripts/configure-guild.py): Configure channels and roles of a Discord server
7+
* [scripts/export-members.py](./scripts/export-members.py): Export a list of all server members and their roles
88

9-
## Overview
9+
The scripts work standalone and only require an Auth token. Please find more documentation in the respective files.
1010

11-
The `main` method in `src/europython_discord/bot.py` is the entry point for the bot.
12-
I't a good starting point to start browsing the codebase.
13-
It requires a `.secrets` file in the root of the repository with `DISCORD_BOT_TOKEN` and `PRETIX_TOKEN` environment variables.
11+
The bot has the following extensions ("Cogs"):
1412

15-
### Registration
13+
* Ping: To check if the bot is running, write `$ping` in any channel. The bot will respond with `Pong!`.
14+
* Guild Statistics: As an organizer, write `$participants` in an organizer-only channel. The bot will respond with a list of roles, and the number of members per role.
15+
* Registration: On startup, the bot posts a registration form. New users must register using their Pretix ticket data. On success, the bot assigns the appropriate roles.
16+
* Programme Notifications: Before each session, the bot posts a session summary and updates the livestream URLs.
1617

17-
At EuroPython, we use [pretix](https://pretix.eu/about/en/) as our ticketing system.
18+
## Screenshots
19+
### Registration Channel:
20+
![Registration Channel](./img/registration-channel.png)
1821

19-
The bot utilizes the Pretix API to fetch ticket information and creates an in-memory key-value store to retrieve the ticket type for a given Discord user. The mapping between ticket types and Discord roles is defined in a JSON file, such as ticket_to_roles_prod.json, and is used by the bot to assign roles to users.
22+
### Registration Form:
23+
![Registration Form](./img/registration-form.png)
2024

21-
There are safeguard methods in place to prevent users from registering multiple times and to make a direct Pretix API call in case the user information is not available in the in-memory store.
25+
### Programme Notification:
26+
![Programme Notification](./img/programme-notification.png)
2227

28+
## Configuration
2329

24-
### Program notifications
30+
All configuration is server-agnostic. You can set up your own Discord server and use the included configuration.
2531

26-
Is a service to push the programme notification to Discord. Pretalx API is used to fetch the programme information, and `config.toml` holds information about livestream URLs.
32+
Arguments and environment variables:
2733

28-
### Organizers extension
34+
* Argument `--config-file`: Path to .toml configuration file
35+
* Environment variable `DISCORD_BOT_TOKEN`: Discord bot auth token (with Admin and `GUILD_MEMBERS` privileges)
36+
* Environment variable `PRETIX_TOKEN`: Pretix access token (preferably read-only)
2937

30-
A set of commands that are available only for organizers that are allowing to get statistics about the Discord server.
38+
Included example configuration files:
39+
40+
* [`prod-config.toml`](./prod-config.toml) or [`test-config.toml`](./test-config.toml): Prod/Test configuration
41+
* [`test-livestreams.toml`](./test-livestreams.toml): Test livestream URL configuration
42+
43+
Used cache and log files (will be created if necessary):
44+
45+
* `pretix_cache.json`: Local cache of Pretix ticket data
46+
* `registered_log.txt`: Log of registered users
47+
* `schedule_cache.json`: Local cache of [programapi](https://github.com/europython/programapi) schedule
3148

3249
## Setup
3350
### Quickstart using `pip`
3451

3552
This project uses [uv](https://github.com/astral-sh/uv) for managing dependencies.
36-
If you just want to try the bot and skip all the development setup,
53+
If you just want to try the bot and skip the development setup,
3754
you can use `pip` instead of `uv` (requires Python >= 3.11):
3855

3956
```shell
4057
# create and activate virtual environment (optional, but recommended)
4158
python -m venv .venv
42-
. .venv/bin/activate # Windows: '.venv/Scripts/activate'
59+
. .venv/bin/activate # Windows: .venv/Scripts/activate
4360

44-
# install this package
61+
# install this package (use '-e' for 'editable mode' if you plan to modify the code)
4562
pip install .
4663

47-
# run the bot
48-
run-bot
64+
# set environment variables
65+
export DISCORD_BOT_TOKEN=... # Windows: $env:DISCORD_BOT_TOKEN = '...'
66+
export PRETIX_TOKEN=... # Windows: $env:PRETIX_TOKEN = '...'
67+
68+
# run the bot with a given config file
69+
run-bot --config your-config-file.toml
4970
```
5071

5172
### Development setup using `uv`
5273

53-
Install `uv` as documented [here](https://docs.astral.sh/uv/getting-started/installation/), then
54-
create/update virtual environment with all dependencies according to [`uv.lock`](./uv.lock)
55-
with `uv sync --dev`.
74+
Install `uv` as documented [here](https://docs.astral.sh/uv/getting-started/installation/), then run `uv sync --dev` to create/update a
75+
virtual environment with all dependencies according to [`uv.lock`](./uv.lock).
5676

5777
If required, `uv` will download the required Python version, as specified in
5878
[`.python-version`](./.python-version).
5979

60-
### Using `uv`
80+
To run the bot, use the following:
81+
82+
```shell
83+
# set environment variables
84+
export DISCORD_BOT_TOKEN=... # Windows: $env:DISCORD_BOT_TOKEN = '...'
85+
export PRETIX_TOKEN=... # Windows: $env:PRETIX_TOKEN = '...'
86+
87+
# run the bot with a given config file
88+
uv run run-bot --config your-config-file.toml
89+
```
90+
91+
#### Useful `uv` commands
6192

62-
This is a summary of useful `uv` commands.
6393
Please refer to the [uv documentation](https://docs.astral.sh/uv) or `uv help` for details.
6494

6595
```shell
@@ -70,19 +100,22 @@ uv sync --dev # include dev dependencies
70100
# activate uv-generated venv
71101
. .venv/bin/activate # Windows: '.venv/Scripts/activate'
72102

103+
# execute command inside uv-generated venv
104+
uv run [command]
105+
73106
# reset all packages to versions pinned in uv.lock
74107
uv sync
75108
uv sync --dev # include dev dependencies
76109

77110
# add package
78-
uv add package
79-
uv add --dev package # install as dev dependency
111+
uv add [package]
112+
uv add --dev [package] # install as dev dependency
80113

81114
# upgrade packages
82115
uv lock --upgrade
83116

84117
# remove package
85-
uv remove package
118+
uv remove [package]
86119
```
87120

88121
### Development tools
@@ -93,26 +126,14 @@ uv remove package
93126
* Check code style: `uv run --dev ruff check .`
94127
* Run tests: `uv run --dev pytest .`
95128

96-
### Configuration
97-
98-
Create `config.local.toml` file in the `src/europython_discord` directory, it would be used instead of `config.toml` if exists.
99-
100-
Add `.secrets` file to the root of the repository with the following content:
101-
102-
```shell
103-
DISCORD_BOT_TOKEN=<EuroPythonTestBotToken_from_1Password>
104-
PRETIX_TOKEN=<PretixStagingToken_from_1Password>
105-
````
106-
107-
After you have added the `.secrets` file, you can run the bot with the following command:
129+
### Deployment
108130

109-
```shell
110-
run-bot
111-
```
131+
The bot is deployed on a VPS using a GitHub Action.
132+
It uses Ansible to configure the VPS, and Docker Compose to run the bot.
112133

113-
or with docker:
134+
Related files:
114135

115-
```shell
116-
docker build --tag discord_bot .
117-
docker run --interactive --tty --env DISCORD_BOT_TOKEN=$DISCORD_BOT_TOKEN --env PRETIX_TOKEN=$PRETIX_TOKEN discord_bot
118-
```
136+
* [.github/workflows/deploy.yml](./.github/workflows/deploy.yml): The GitHub Action
137+
* [ansible/deploy-playbook.yml](./ansible/deploy-playbook.yml): The Ansible Playbook
138+
* [Dockerfile](./Dockerfile): The Docker container recipe
139+
* [compose.yaml](./compose.yaml): The Docker Compose recipe

ansible/deploy-playbook.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
repository_url: https://github.com/EuroPython/discord.git
66

77
tasks:
8-
98
- name: Enable persistent logging for journald
109
ini_file:
1110
path: /etc/systemd/journald.conf
@@ -15,7 +14,6 @@
1514
no_extra_spaces: true
1615
backup: true
1716

18-
1917
- name: reload systemd-journald
2018
systemd:
2119
name: systemd-journald
@@ -94,9 +92,9 @@
9492
owner: bot
9593
group: bot
9694

97-
- name: Create schedule.json in bot's home directory
95+
- name: Create schedule_cache.json in bot's home directory
9896
file:
99-
path: /home/bot/schedule.json
97+
path: /home/bot/schedule_cache.json
10098
state: touch
10199
owner: bot
102100
group: bot

compose.yaml

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
services:
22
EuroPythonBot:
33
image: europythonbot
4-
build: .
4+
build:
5+
context: .
6+
env_file:
7+
- /root/.secrets
58
volumes:
69
- type: bind
7-
source: /etc/EuroPython/discord/.secrets
8-
target: /home/bot/.secrets
10+
source: prod-config.toml
11+
target: /home/bot/prod-config.toml
912
read_only: true
1013

1114
- type: bind
12-
source: /etc/EuroPython/livestreams/livestreams.toml
15+
source: /root/livestreams.toml
1316
target: /home/bot/livestreams.toml
1417
read_only: true
1518

@@ -19,8 +22,8 @@ services:
1922
read_only: false
2023

2124
- type: bind
22-
source: /home/bot/schedule.json
23-
target: /home/bot/schedule.json
25+
source: /home/bot/schedule_cache.json
26+
target: /home/bot/schedule_cache.json
2427
read_only: false
2528

2629
- type: bind

img/programme-notification.png

24.4 KB
Loading

img/registration-channel.png

90.4 KB
Loading

img/registration-form.png

28 KB
Loading

img/registration_view.png

-389 KB
Binary file not shown.

prod-config.toml

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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]
14+
# onsite participants
15+
"Business" = ["Participants", "Onsite Participants"]
16+
"Personal" = ["Participants", "Onsite Participants"]
17+
"Education" = ["Participants", "Onsite Participants"]
18+
"Community Contributors" = ["Participants", "Onsite Participants"]
19+
"Grant ticket" = ["PARTICIPANTS", "Onsite Participants"]
20+
# remote participants
21+
"Remote Participation Ticket" = ["Participants", "Remote Participants"]
22+
"Remote Grant ticket" = ["Participants", "Remote Participants"]
23+
"Remote Community Organiser" = ["Participants", "Remote Participants"]
24+
# sponsors
25+
"Sponsor Conference Pass" = ["Participants", "Onsite Participants", "Sponsors"]
26+
# speakers
27+
"Presenter" = ["Participants", "Onsite Participants", "Speakers"]
28+
29+
[registration.variation_to_roles]
30+
"Volunteer" = ["Volunteers"]
31+
32+
[program_notifications]
33+
# UTC offset in hours (e.g. 2 for CEST)
34+
timezone_offset = 2
35+
api_url = "https://static.europython.eu/programme/ep2025/releases/current/schedule.json"
36+
schedule_cache_file = "schedule_cache.json"
37+
livestream_url_file = "livestreams.toml"
38+
main_notification_channel_name = "programme-notifications"
39+
40+
# optional simulated start time for testing program notifications
41+
# simulated_start_time = "2024-07-10T07:30:00+02:00"
42+
43+
# optional fast mode for faster testing of program notifications
44+
# will only take effect if simulated_start_time is set
45+
# fast_mode = true
46+
47+
[program_notifications.rooms_to_channel_names]
48+
"Forum Hall" = "forum-hall"
49+
"South Hall 2A" = "south-hall-2a"
50+
"South Hall 2B" = "south-hall-2b"
51+
"North Hall" = "north-hall"
52+
"Terrace 2A" = "terrace-2a"
53+
"Terrace 2B" = "terrace-2b"
54+
"Exhibit Hall" = "exhibit-hall"
55+
56+
[guild_statistics]
57+
required_role = "Organizers"

pyproject.toml

-5
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,12 @@ dependencies = [
1414
"discord-py>=2.3.1",
1515
"aiofiles>=24.1.0",
1616
"aiohttp>=3.11.16",
17-
"arrow>=1.3.0",
18-
"certifi>=2024.7.4",
19-
"python-dotenv>=1.0.1",
20-
"yarl>=1.19.0",
2117
"pydantic>=2.8.2",
2218
"unidecode>=1.3.8",
2319
]
2420

2521
[dependency-groups]
2622
dev = [
27-
"ansible>=10.2.0",
2823
"pytest>=8.3.5",
2924
"pytest-aiohttp>=1.1.0",
3025
"pytest-asyncio>=0.26.0",

0 commit comments

Comments
 (0)