Skip to content

Commit f0d3f03

Browse files
Dependency updates for current Python (#235)
* Get building on current Poetry and Python * Update actions to latest versions for Node 24 * Update pyproject for Poetry 2 * I love the formatter * Fixing up type issues * Update icalendar and Pillow
1 parent cee8896 commit f0d3f03

17 files changed

Lines changed: 1690 additions & 1142 deletions

.github/workflows/build-docker.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ jobs:
1818

1919
steps:
2020
- name: Checkout repository
21-
uses: actions/checkout@v4
21+
uses: actions/checkout@v6
2222

2323
# Login against the Docker registry
2424
# https://github.com/docker/login-action
2525
- name: Log into registry ${{ env.REGISTRY }}
26-
uses: docker/login-action@v3
26+
uses: docker/login-action@v4
2727
with:
2828
registry: ${{ env.REGISTRY }}
2929
username: ${{ github.actor }}
@@ -32,7 +32,7 @@ jobs:
3232
# Build and push Docker image with Buildx
3333
# https://github.com/docker/build-push-action
3434
- name: Build and push Docker image
35-
uses: docker/build-push-action@v5
35+
uses: docker/build-push-action@v7
3636
with:
3737
context: .
3838
push: true

.github/workflows/run-black.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ jobs:
1616
with:
1717
options: "--check --verbose"
1818
src: "./uqcsbot"
19-
version: "23.12.1"
19+
version: "26.5.1"
2020

2121
- name: Convert logs to artifacts
22-
uses: actions/upload-artifact@v4
22+
uses: actions/upload-artifact@v7
2323
if: always() && (steps.runBlack.outcome == 'failure')
2424
with:
2525
name: "black-logs"
2626
path: "/tmp/blk_*"
27-
27+

.github/workflows/run-tests.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ on:
66
pull_request:
77

88
env:
9-
PYTHON_VERSION: '3.10'
10-
POETRY_VERSION: '1.7.1'
9+
PYTHON_VERSION: '3.13'
10+
POETRY_VERSION: '2.4.1'
1111

1212
jobs:
1313
tests:
@@ -16,7 +16,7 @@ jobs:
1616

1717
steps:
1818
- name: Checkout repository
19-
uses: actions/checkout@v4
19+
uses: actions/checkout@v6
2020

2121
# Local action that tries to cache as much of python & poetry as possible
2222
- name: Setup environment
@@ -34,7 +34,7 @@ jobs:
3434

3535
steps:
3636
- name: Checkout repo
37-
uses: actions/checkout@v4
37+
uses: actions/checkout@v6
3838

3939
# Local action that tries to cache as much of python & poetry as possible
4040
- name: Setup environment

.github/workflows/setup-python/action.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ runs:
1616
# Get python
1717
# ------
1818
- name: Setup Python
19-
uses: actions/setup-python@v5
19+
uses: actions/setup-python@v6
2020
with:
2121
python-version: ${{ inputs.python-version }}
2222

@@ -25,7 +25,7 @@ runs:
2525
# ------
2626
- name: Check for cached poetry binary
2727
id: cached-poetry-binary
28-
uses: actions/cache@v4
28+
uses: actions/cache@v5
2929
with:
3030
path: ~/.local
3131
# poetry depends on OS, python version, and poetry version
@@ -50,7 +50,7 @@ runs:
5050
# ------
5151
- name: Check for cached dependencies
5252
id: cached-poetry-dependencies
53-
uses: actions/cache@v4
53+
uses: actions/cache@v5
5454
with:
5555
path: '**/.venv'
5656
# poetry dependencies depend on OS, python version, poetry version, and repository lockfile

Dockerfile

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
FROM python:3.10-slim as python-base
1+
FROM python:3.13-slim AS python-base
22

33
# Environment variables that should exist in all images.
44
ENV PYTHONUNBUFFERED=1 \
55
PIP_DISABLE_PIP_VERSION_CHECK=1 \
66
PIP_DEFAULT_TIMEOUT=100 \
7-
POETRY_NO_INTERACTION=1 \
8-
POETRY_VERSION=1.7.1 \
7+
POETRY_VERSION=2.4.1 \
98
POETRY_VIRTUALENVS_IN_PROJECT=1 \
109
POETRY_CACHE_DIR='/var/cache/pypoetry'
1110

1211

1312
# poetry-base stage installs Poetry and installs prod deps
14-
FROM python-base as poetry-base
13+
FROM python-base AS poetry-base
1514

1615
WORKDIR /app
1716
RUN pip install "poetry==$POETRY_VERSION" && poetry --version
@@ -22,7 +21,7 @@ RUN poetry install --without=dev
2221

2322
# dev stage continues off poetry-base to install dev deps
2423
# and have poetry available within the container.
25-
FROM poetry-base as dev
24+
FROM poetry-base AS dev
2625

2726
ENV VIRTUAL_ENV=/app/.venv \
2827
PATH="/app/.venv/bin:$PATH"
@@ -35,7 +34,7 @@ ENTRYPOINT ["python", "-m", "uqcsbot"]
3534

3635
# prod stage creates the final image for production and excludes
3736
# poetry as it is unneeded on prod.
38-
FROM python-base as prod
37+
FROM python-base AS prod
3938

4039
ENV VIRTUAL_ENV=/app/.venv \
4140
PATH="/app/.venv/bin:$PATH"

docker-compose.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
version: '3'
2-
31
services:
4-
52
# Build the local Dockerfile to run UQCSbot itself.
63
uqcsbot:
74
build:
@@ -20,7 +17,7 @@ services:
2017

2118
# Build a database container for UQCSbot to use.
2219
database:
23-
image: 'postgres:13'
20+
image: 'postgres:17'
2421
restart: "unless-stopped"
2522
ports:
2623
- 5432:5432
@@ -35,3 +32,4 @@ services:
3532
# We want database data to persist, so we put it into a volume.
3633
volumes:
3734
database-data:
35+

poetry.lock

Lines changed: 1597 additions & 1033 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,49 @@
1-
[tool.poetry]
1+
[project]
22
name = "uqcsbot"
33
version = "0.1.0"
44
description = "UQCSbot is a chat bot built in Python for the UQCS Discord server."
5-
authors = ["UQ Computing Society <contact@uqcs.org.au>"]
5+
authors = [{name = "UQ Computing Society", email = "contact@uqcs.org"}]
66
license = "MIT"
77

8+
[project.scripts]
9+
botdev = "dev.cli:main"
10+
11+
[tool.poetry]
12+
package-mode = false
13+
requires-poetry = ">=2.0"
14+
815
[tool.poetry.dependencies]
9-
python = "^3.10"
10-
"discord.py" = "^2.3.2"
11-
icalendar = "^5.0"
16+
python = "^3.13"
17+
"discord.py" = "^2.7.1"
18+
icalendar = "^7.1.1"
1219
"python-dateutil" = "^2.9"
13-
requests = "^2.31"
14-
beautifulsoup4 = "^4.12"
15-
APScheduler = "^3.10"
16-
SQLAlchemy = {version = "^2.0.28", extras = ["postgresql_psycopg2binary"]}
17-
humanize = "^4.9"
18-
aiohttp = "^3.9"
19-
aio-mc-rcon = "^3.2.0"
20+
requests = "^2.34.2"
21+
beautifulsoup4 = "^4.14.3"
22+
APScheduler = "^3.11.2"
23+
SQLAlchemy = {version = "^2.0.49", extras = ["postgresql_psycopg2binary"]}
24+
humanize = "^4.15"
25+
aiohttp = "^3.13.5"
26+
aio-mc-rcon = "^3.4.2"
2027
PyYAML = "^6.0"
2128
mcstatus = "^11.1.0"
22-
pillow = "^11.0.0"
29+
pillow = "^12.0.0"
2330
tzdata = "*"
24-
25-
[tool.poetry.scripts]
26-
botdev = "dev.cli:main"
31+
pytest = "^9.0.3"
32+
pytz = "^2026.2"
2733

2834
[tool.poetry.group.dev.dependencies]
29-
pytest = "^7.4.4"
35+
pytest = "^9.0.3"
3036
pytest-datafiles = "^3.0.0"
3137
python-dotenv = "^1.0.0"
32-
black = "^23.12.0"
33-
pyright = "^1.1.316"
34-
types-requests = "^2.30.0.0"
38+
black = "^26.5.1"
39+
pyright = "^1.1.409"
40+
types-requests = "^2.33.0.0"
3541
types-beautifulsoup4 = "^4.12.0.4"
3642
types-python-dateutil = "^2.8.19.12"
37-
types-pytz = "^2023.3.0.0"
43+
types-pytz = "^2026.2.0.0"
3844

3945
[build-system]
40-
requires = ["poetry-core>=1.3.0"]
46+
requires = ["poetry-core>=2.0.0"]
4147
build-backend = "poetry.core.masonry.api"
4248

4349
[tool.pyright]

uqcsbot/advent.py

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@
6464
member.times[day].get(2, MAXIMUM_TIME_FOR_STAR),
6565
),
6666
"Star 2 Time": lambda member, day: (
67-
member.times[day][2] - member.times[day][1]
68-
if 2 in member.times[day]
69-
else MAXIMUM_TIME_FOR_STAR,
67+
(
68+
member.times[day][2] - member.times[day][1]
69+
if 2 in member.times[day]
70+
else MAXIMUM_TIME_FOR_STAR
71+
),
7072
member.times[day].get(1, MAXIMUM_TIME_FOR_STAR),
7173
),
7274
"Star 1 & 2 Time": lambda member, day: (
@@ -478,24 +480,19 @@ async def help_command(
478480
"""
479481
match command:
480482
case None:
481-
await interaction.response.send_message(
482-
"""
483+
await interaction.response.send_message("""
483484
[Advent of Code](https://adventofcode.com/) is a yearly coding competition that occurs during the first 12 days (used to be 25 days before year 2025) of december. Coding puzzles are released at 3pm AEST each day, with two stars available for each puzzle. You can spend as long as you like on each puzzle, but UQCS also has a private leaderboard with prizes on offer.
484485
485486
To join, go to <https://adventofcode.com/> and sign in. The UQCS private leaderboard join code is `989288-0ff5a98d`. To be eligible for prizes, you will also have to link your discord account. This can be done by using the `/advent register` command. Reach out to committee if you are having any issues.
486487
487488
For more help, you can use `/advent help <command name>` to get information about a specific command.
488-
"""
489-
)
489+
""")
490490
case "help":
491-
await interaction.response.send_message(
492-
"""
491+
await interaction.response.send_message("""
493492
`/advent help` is a help menu for all the Advent of Code commands. If you use `/advent help <command name>` you can see details of a particular command. Not much else to say here, try another command.
494-
"""
495-
)
493+
""")
496494
case "leaderboard":
497-
await interaction.response.send_message(
498-
"""
495+
await interaction.response.send_message("""
499496
`/advent leaderboard` displays a leaderboard for the Advent of Code challenges. There are two types of leaderboard: for a single day, and for the entire month. These are selected by either providing the `day` option or not. You can also display the leaderboard for a past year or another leaderboard (say another private leaderboard that you have).
500497
501498
There are 6 different sorting options, which do slightly different things depending on whether the leaderboard is for a single day or an entire month. The default sorting method changes on which type of leaderboard you want.
@@ -507,11 +504,9 @@ async def help_command(
507504
`Global ` - This sorts by users global score. Note that this will only show users with global score. Note that this only works for year before 2025, as Advent of code remvoed global leaderboard for years after and including 2025.
508505
509506
You can also style the leaderboard (i.e. change the columns). The default style will change depending on whether the leaderboard is for a single-day or the entire month, and depending on the sorting method. Styles consist of a string, with each character representing a column. Use `/advent help leaderboard-style` to see the possoble characters.
510-
"""
511-
)
507+
""")
512508
case "leaderboard_style":
513-
await interaction.response.send_message(
514-
"""
509+
await interaction.response.send_message("""
515510
Not a command, but an option given to the command `/advent leaderboard` controlling the columns in the leaderboard. Each character in the given string represents a certain column. The possible characters are:
516511
The characters in the string can be:
517512
`# ` - Provides a column of the form "XXX)" telling the order for the given leaderboard
@@ -527,41 +522,29 @@ async def help_command(
527522
`B ` - A progress bar of the stars each person has
528523
`space` - A padding column of a single character
529524
All other characters will be ignored.
530-
"""
531-
)
525+
""")
532526
case "register":
533-
await interaction.response.send_message(
534-
"""
527+
await interaction.response.send_message("""
535528
`/advent register` links an Advent of Code account and a discord user so that you are eligble for prizes. Each Advent of Code account and discord account can only be linked to one other account. Registrations persist across years. If you are having any issues with this, message committee to help.
536-
"""
537-
)
529+
""")
538530
case "register-force":
539-
await interaction.response.send_message(
540-
"""
531+
await interaction.response.send_message("""
541532
`/advent register-force` is an admin-only command to force a registration (i.e. create a registration between any Advent of Code account and Discord user). This can be used for moderation, if someone is having difficulties registering or if you want to register someone for a previous year. This command can break things (such as creating duplicate registrations), so be careful. Exactly one of `aoc_name` or `aoc_id` should be given. Also note that you need to use the Discord ID, not the discord username. If you have developer options enables on your account, this can be found by right clicking on the user and selecting `Copy User ID`.
542-
"""
543-
)
533+
""")
544534
case "unregister":
545-
await interaction.response.send_message(
546-
"""
535+
await interaction.response.send_message("""
547536
`/advent unregister` unlinks your discord account from the currently linked Advent of Code account. Message committee if you need any help.
548-
"""
549-
)
537+
""")
550538
case "unregister-force":
551-
await interaction.response.send_message(
552-
"""
539+
await interaction.response.send_message("""
553540
`/advent unregister-force` is an admin-only command that removes a registration from the database. This can be used as a moderation tool, to remove someone who has registered to an Advent of Code account that isn't there. Note that you need to use the Discord ID, not the discord username. If you have developer options enables on your account, this can be found by right clicking on the user and selecting `Copy User ID`.
554-
"""
555-
)
541+
""")
556542
case "previous-winners":
557-
await interaction.response.send_message(
558-
"""
543+
await interaction.response.send_message("""
559544
`/advent previous-winners` displays the previous winners for a particular year. Note that the records for year prior to 2022 may not be accurate, as the current system was not in use then.
560-
"""
561-
)
545+
""")
562546
case "new-winner":
563-
await interaction.response.send_message(
564-
"""
547+
await interaction.response.send_message("""
565548
`/advent add-winner` is an admin-only command that allows you to either manually or randomly select winners. Participants will only be eligible to win if they have completed at least one star within the given times. For manual selection, provide an Advent of Code user ID (note that this is not the same as their Advent of Code name), otherwise a random winner will be drawn.
566549
567550
The arguments for the command have a bit of nuance. They are as follow:
@@ -572,14 +555,11 @@ async def help_command(
572555
`allow_repeat_winners ` - This allows participants to win multiple times from the same selection if `number_of_winners` is greater than 1. Note that regardless of this option, someone can win multiple times in a year, just not in a single selection.
573556
`allow_unregistered_users` - This allows Advent of Code accounts that do not have a linked discord account to win. Note that it can be difficult to give out prizes to users that do not have a linked discord.
574557
`year ` - The year the prize is for.
575-
"""
576-
)
558+
""")
577559
case "remove-winner":
578-
await interaction.response.send_message(
579-
"""
560+
await interaction.response.send_message("""
580561
`/advent remove-winner` is an admin-only command that removes a winner from the database. It uses the database ID (which is distinct from the Advent of code user ID and the Discord user ID). You can find these ids by running `/advent previous-winners show_ids:True`.
581-
"""
582-
)
562+
""")
583563

584564
@advent_command_group.command(name="leaderboard")
585565
@app_commands.describe(

uqcsbot/dominos_coupons.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def _get_coupons_from_page(source: SingleSource) -> List[Coupon]:
267267
if url == COUPONESE_DOMINOS_URL:
268268
soup_coupons = soup.find_all(class_="ov-coupon")
269269
elif url == FRUGAL_FEEDS_DOMINOS_URL:
270-
tables = soup.select('[class^="tablepress"]')
270+
tables = soup.find_all(class_="tablepress")
271271
for table in tables:
272272
# Headers have stuff we don't want
273273
rows = table.find_all("tr")[1:]

0 commit comments

Comments
 (0)