commit
6541aad040
8 changed files with 1459 additions and 0 deletions
@ -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 |
@ -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/ |
@ -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"] |
@ -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() |
File diff suppressed because it is too large
Load Diff
@ -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" |
Loading…
Reference in new issue