Compare commits

..

No commits in common. "master" and "4.0.0" have entirely different histories.

8 changed files with 931 additions and 886 deletions

View File

@ -4,14 +4,14 @@ on: [push]
jobs:
lint:
runs-on: ubuntu-latest
container: python:3.12.6
container: python:3.11
steps:
- run: apt-get update
- run: apt-get install -y git nodejs
- uses: actions/checkout@v4
- uses: Gr1N/setup-poetry@v9
- uses: Gr1N/setup-poetry@v8
- run: poetry install
- run: poetry run ruff check .
- run: poetry run flake8 .
- run: poetry run mypy .
- run: poetry run djlint .
@ -35,22 +35,22 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/build-push-action@v6
- uses: docker/build-push-action@v5
with:
push: ${{ gitea.ref_name == gitea.event.repository.default_branch || gitea.ref_type == 'tag' }}
push: ${{ gitea.ref == 'refs/heads/master' || startsWith(gitea.ref, 'refs/tags') }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
pypi:
runs-on: ubuntu-latest
container: python:3.12.6
container: python:3.11
needs: [lint]
if: gitea.ref_type == 'tag'
if: startsWith(gitea.ref, 'refs/tags')
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.POETRY_PYPI_TOKEN_PYPI }}
steps:
- run: apt-get update
- run: apt-get install -y git nodejs
- uses: actions/checkout@v4
- uses: Gr1N/setup-poetry@v9
- uses: Gr1N/setup-poetry@v8
- run: poetry publish --build

6
.gitignore vendored
View File

@ -107,10 +107,8 @@ ipython_config.py
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
@ -160,4 +158,4 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.idea/

View File

@ -1,10 +1,10 @@
FROM python:3.12.6 as build
FROM python:3.11.6 as build
WORKDIR /app
COPY . .
RUN pip install poetry && poetry build
FROM python:3.12.6
FROM python:3.11.5
COPY --from=build /app/dist /tmp/dist
RUN pip install /tmp/dist/*.whl && rm -rf /tmp/dist

View File

View File

@ -1,3 +0,0 @@
from divent.bot import run
run()

View File

@ -3,17 +3,18 @@ import logging
from datetime import datetime, timedelta
from functools import wraps
from os import getenv, path
from typing import Dict, Optional, Union
from typing import Dict, Optional
from disnake import Asset, Client, Guild, Intents, Member, User
from disnake import Asset, Client, Guild
from disnake.guild_scheduled_event import GuildScheduledEvent
from dotenv import load_dotenv
from hypercorn.middleware import ProxyFixMiddleware
from ics import Calendar, ContentLine, Event
from ics.alarm import DisplayAlarm
from oauthlib.oauth2 import OAuth2Error
from quart import Quart, redirect, render_template, request, session, url_for
from requests_oauthlib import OAuth2Session # type: ignore
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
load_dotenv()
@ -35,51 +36,17 @@ API_BASE_URL = getenv("API_BASE_URL", "https://discordapp.com/api")
AUTHORIZATION_BASE_URL = f"{API_BASE_URL}/oauth2/authorize"
TOKEN_URL = f"{API_BASE_URL}/oauth2/token"
CATALOG_CACHE = {}
EVENTS_CACHE = {}
class Discord(Client):
async def on_ready(self):
print(f"Logged on as {self.user}!", flush=True)
for guild in self.guilds:
for scheduled_event in guild.scheduled_events:
EVENTS_CACHE[scheduled_event.id] = [
member.id
for member in await scheduled_event.fetch_users().flatten()
]
print("Events synchronised!", flush=True)
async def on_guild_scheduled_event_subscribe(
self, event: GuildScheduledEvent, user: Union[Member, User]
):
EVENTS_CACHE[event.id].append(user.id)
async def on_guild_scheduled_event_unsubscribe(
self, event: GuildScheduledEvent, user: Union[Member, User]
):
EVENTS_CACHE[event.id].remove(user.id)
async def on_guild_scheduled_event_create(self, event: GuildScheduledEvent):
EVENTS_CACHE[event.id] = [
member.id for member in await event.fetch_users().flatten()
]
async def on_guild_scheduled_event_delete(self, event: GuildScheduledEvent):
EVENTS_CACHE.pop(event.id)
intents = Intents.default()
intents.guild_scheduled_events = True
intents.members = True
client = Discord(intents=intents)
client = Discord()
app = Quart(__name__)
app.config["SECRET_KEY"] = OAUTH2_CLIENT_SECRET
app.config["EXPLAIN_TEMPLATE_LOADING"] = QUART_DEBUG
app.asgi_app = ProxyFixMiddleware(app.asgi_app) # type: ignore
app.asgi_app = ProxyHeadersMiddleware(app.asgi_app, "*") # type: ignore
def get_guild_by_id(guild_id: str) -> Optional[Guild]:
@ -90,6 +57,9 @@ def get_guild_by_id(guild_id: str) -> Optional[Guild]:
return None
CATALOG_CACHE = {}
@app.errorhandler(500)
async def errorhandler(error: Exception):
print(f"\33[31m{error}\33[m", flush=True)
@ -251,10 +221,7 @@ async def subscribe(entity_id: str):
entity_id=guild.vanity_url_code or guild.id,
)
try:
user = await client.get_or_fetch_user(int(entity_id))
except ValueError:
return redirect(url_for(".login"))
user = await client.get_or_fetch_user(int(entity_id))
if user and str(user.id) == entity_id:
return await render_template(
@ -314,10 +281,7 @@ async def ical(entity_id: str):
return calendar.serialize()
try:
user = await client.get_or_fetch_user(int(entity_id))
except ValueError:
return redirect(url_for(".login"))
user = await client.get_or_fetch_user(int(entity_id))
if user:
calendar = Calendar()
@ -331,16 +295,21 @@ async def ical(entity_id: str):
)
for guild in client.guilds:
for scheduled_event in guild.scheduled_events:
if user.id in EVENTS_CACHE[scheduled_event.id]:
event = make_event(scheduled_event)
calendar.events.append(event)
if await guild.get_or_fetch_member(int(entity_id)):
for scheduled_event in guild.scheduled_events:
if user.id in [
member.id
for member in await scheduled_event.fetch_users().flatten()
]:
event = make_event(scheduled_event)
calendar.events.append(event)
return calendar.serialize()
return redirect(url_for(".login"))
def run():
client.loop.create_task(client.start(DISCORD_TOKEN))
app.run("0.0.0.0", loop=client.loop)
def __main__():
quart_task = client.loop.create_task(app.run_task("0.0.0.0"))
quart_task.add_done_callback(lambda f: client.loop.stop())
client.run(DISCORD_TOKEN)

1678
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "divent"
version = "4.1.4"
version = "4.0.0"
description = "The discord scheduled event calendar generator"
authors = ["Xéfir Destiny <xefir@crystalyx.net>"]
license = "WTFPL"
@ -9,29 +9,34 @@ homepage = "https://divent.crystalyx.net/"
repository = "https://git.crystalyx.net/Xefir/Divent"
[tool.poetry.scripts]
divent = 'divent.bot:run'
divent = 'divent.bot:__main__'
[tool.poetry.dependencies]
python = ">=3.8,<4.0"
disnake = "^2.9.2"
python = ">=3.8.1,<3.12"
disnake = "^2.9.1"
ics = "0.8.0.dev0"
python-dotenv = "^1.0.1"
quart = "^0.19.6"
requests-oauthlib = "^2.0.0"
python-dotenv = "^1.0.0"
quart = "^0.19.3"
requests-oauthlib = "^1.3.1"
uvicorn = "^0.24.0"
[tool.poetry.dev-dependencies]
djlint = "^1.35.2"
mypy = "^1.11.2"
ruff = "^0.6.5"
types-oauthlib = "^3.2.0"
black = "^23.10.1"
djlint = "^1.34.0"
flake8 = "^6.1.0"
flake8-alphabetize = "^0.0.21"
flake8-black = "^0.3.6"
flake8-pyproject = "^1.2.3"
mypy = "^1.6.1"
types-oauthlib = "^3.2.0.10"
[tool.flake8]
max-line-length = 88
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.ruff.lint]
select = ["E", "F", "I"]
[tool.djlint]
extension = "j2"
profile = "jinja"