first commit

This commit is contained in:
Michel Roux 2022-05-08 16:02:19 +02:00
commit 6541aad040
8 changed files with 1459 additions and 0 deletions

22
.drone.yml Normal file
View File

@ -0,0 +1,22 @@
kind: pipeline
name: default
type: docker
steps:
- name: flake8
image: python:slim
commands:
- pip install poetry
- poetry install
- flake8
- mypy .
- name: docker
image: plugins/docker
settings:
repo: xefir/divent
auto_tag: true
username:
from_secret: docker_username
password:
from_secret: docker_password

2
.env.dist Normal file
View File

@ -0,0 +1,2 @@
DISCORD_TOKEN=
SENTRY_DSN=

2
.flake8 Normal file
View File

@ -0,0 +1,2 @@
[flake8]
max-line-length = 100

161
.gitignore vendored Normal file
View File

@ -0,0 +1,161 @@
# https://github.com/github/gitignore/blob/main/Python.gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#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/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# 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/

7
Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM python:3.10.4-slim
WORKDIR /app
COPY . .
RUN pip install poetry && poetry install --no-dev
CMD ["poetry", "run", "python", "bot.py"]

97
bot.py Normal file
View File

@ -0,0 +1,97 @@
from asyncio import new_event_loop
from datetime import timedelta
from os import environ
from disnake import Client, Guild, Member
from dotenv import load_dotenv
from ics import Calendar, Event # type: ignore
from ics.alarm.display import DisplayAlarm # type: ignore
from quart import Quart, abort
from sentry_sdk import init
from sentry_sdk.integrations.quart import QuartIntegration
load_dotenv()
DISCORD_TOKEN = environ.get("DISCORD_TOKEN")
if not DISCORD_TOKEN:
raise Exception("Missing DISCORD_TOKEN env")
SENTRY_DSN = environ.get("SENTRY_DSN")
if SENTRY_DSN:
init(SENTRY_DSN, integrations=[QuartIntegration()])
class Discord(Client):
async def on_ready(self):
print(f"Logged on as {self.user}!")
client = Discord()
app = Quart(__name__)
def get_guild_by_id(guild_id: int | str) -> Guild | None:
for guild in client.guilds:
if guild.id == int(guild_id) or guild.vanity_url_code == str(guild_id):
return guild
return None
def get_bot_member(guild: Guild) -> Member | None:
for member in guild.members:
if member.id == client.user.id:
return member
return None
async def get_guild_tag(member: Member) -> str | None:
if member.guild_permissions.manage_guild:
if member.guild.vanity_url_code:
return member.guild.vanity_url_code
else:
invites = await member.guild.invites()
return invites[0].code
return None
@app.route("/<guild_id>.ics")
async def index(guild_id):
guild = get_guild_by_id(guild_id)
if guild is None:
abort(404)
bot = get_bot_member(guild)
guild_tag = await get_guild_tag(bot)
calendar = Calendar()
for scheduled_event in guild.scheduled_events:
event = Event()
event.name = scheduled_event.name
event.begin = scheduled_event.scheduled_start_time
event.end = scheduled_event.scheduled_end_time
event.uid = str(scheduled_event.id)
event.description = scheduled_event.description
event.location = (
scheduled_event.entity_metadata.location
if scheduled_event.entity_metadata
else None
)
if guild_tag:
event.url = f"https://discord.gg/{guild_tag}?event={scheduled_event.id}"
alarm = DisplayAlarm()
alarm.trigger = timedelta(hours=1)
event.alarms.append(alarm)
calendar.events.add(event)
return str(calendar)
loop = new_event_loop()
discord_task = loop.create_task(client.start(DISCORD_TOKEN))
discord_task.add_done_callback(lambda f: loop.stop())
quart_task = loop.create_task(app.run_task())
quart_task.add_done_callback(lambda f: loop.stop())
loop.run_forever()

1138
poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

30
pyproject.toml Normal file
View File

@ -0,0 +1,30 @@
[tool.poetry]
name = "divent"
version = "0.1.0"
description = ""
authors = ["Xéfir Destiny"]
license = "WTFPL"
[tool.poetry.dependencies]
python = "^3.8"
disnake = "2.5.0"
ics = "0.7"
python-dotenv = "0.20.0"
quart = "0.17.0"
sentry-sdk = {extras = ["quart"], version = "1.5.11"}
[tool.poetry.dev-dependencies]
flake8 = "4.0.1"
black = "22.3.0"
isort = "5.10.1"
mypy = "0.950"
flake8-black = "0.3.2"
flake8-isort = "4.1.1"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.isort]
profile = "black"