2023-11-19 15:41:19 +00:00
|
|
|
import os
|
|
|
|
import tomllib
|
2023-11-20 13:29:39 +00:00
|
|
|
from io import BytesIO
|
2023-11-20 12:37:36 +00:00
|
|
|
from typing import Annotated, Self
|
2023-11-19 15:41:19 +00:00
|
|
|
|
2023-11-20 13:29:39 +00:00
|
|
|
import aiohttp
|
2023-11-19 16:32:12 +00:00
|
|
|
import discord
|
2023-11-20 12:37:36 +00:00
|
|
|
from pydantic import BaseModel, StringConstraints
|
|
|
|
|
|
|
|
StrippedStr = Annotated[str, StringConstraints(strip_whitespace=True)]
|
2023-11-19 15:41:19 +00:00
|
|
|
|
2023-11-19 18:01:06 +00:00
|
|
|
|
2023-11-19 18:36:29 +00:00
|
|
|
class Post(BaseModel):
|
2023-11-19 23:26:23 +00:00
|
|
|
# users authorized to posts
|
2023-11-19 18:36:29 +00:00
|
|
|
users: list[int]
|
2023-11-19 23:26:23 +00:00
|
|
|
# where to put posts
|
|
|
|
channel: int
|
2023-11-19 15:41:19 +00:00
|
|
|
|
2023-11-19 18:36:29 +00:00
|
|
|
def get_channel(
|
|
|
|
self,
|
|
|
|
client: discord.Client,
|
|
|
|
) -> discord.Thread | discord.TextChannel:
|
2023-11-19 16:32:12 +00:00
|
|
|
"""
|
|
|
|
Zielkanal für Posts finden
|
|
|
|
"""
|
|
|
|
|
2023-11-19 18:36:29 +00:00
|
|
|
channel = client.get_channel(self.channel)
|
|
|
|
assert isinstance(channel, discord.Thread | discord.TextChannel)
|
2023-11-19 16:32:12 +00:00
|
|
|
|
2023-11-19 18:36:29 +00:00
|
|
|
return channel
|
2023-11-19 16:32:12 +00:00
|
|
|
|
|
|
|
|
2023-11-20 12:37:36 +00:00
|
|
|
class InfoCommand(BaseModel):
|
2023-11-20 13:32:00 +00:00
|
|
|
name: StrippedStr
|
2023-11-20 12:37:36 +00:00
|
|
|
description: StrippedStr = "..."
|
|
|
|
content: StrippedStr = ""
|
|
|
|
|
|
|
|
|
|
|
|
class FileCommand(InfoCommand):
|
2023-11-20 13:29:39 +00:00
|
|
|
file_url: str = ""
|
|
|
|
file_name: str = ""
|
|
|
|
|
|
|
|
@property
|
|
|
|
async def as_bytes(self) -> bytes | None:
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
|
async with session.get(self.file_url) as response:
|
|
|
|
if response.status != 200:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return await response.read()
|
|
|
|
|
|
|
|
@property
|
|
|
|
async def as_discord_file(self) -> discord.File | None:
|
|
|
|
if (as_bytes := await self.as_bytes) is not None:
|
|
|
|
return discord.File(
|
|
|
|
fp=BytesIO(as_bytes),
|
|
|
|
filename=self.file_name,
|
|
|
|
)
|
2023-11-20 12:37:36 +00:00
|
|
|
|
|
|
|
|
2023-11-19 23:26:23 +00:00
|
|
|
class ClubInfo(BaseModel):
|
2023-11-20 12:37:36 +00:00
|
|
|
channels: list[int]
|
|
|
|
|
2023-11-20 12:55:20 +00:00
|
|
|
info: InfoCommand
|
2023-11-21 15:02:43 +00:00
|
|
|
vorstand: InfoCommand
|
2023-11-20 12:37:36 +00:00
|
|
|
linktree: InfoCommand
|
2023-11-22 21:37:30 +00:00
|
|
|
beitreten: InfoCommand
|
2023-11-20 12:55:20 +00:00
|
|
|
fest: InfoCommand
|
|
|
|
aktion: InfoCommand
|
2023-11-19 23:26:23 +00:00
|
|
|
|
2023-12-02 23:38:14 +00:00
|
|
|
def in_allowed_channel(self, interaction: discord.Interaction) -> bool:
|
|
|
|
if interaction.channel is None:
|
|
|
|
return False
|
|
|
|
|
|
|
|
# öffentliche Antwort erlaubt in:
|
|
|
|
# - DM channels
|
|
|
|
# - "allowed" channels
|
|
|
|
is_dm_channel = isinstance(interaction.channel, discord.DMChannel)
|
|
|
|
is_listed = interaction.channel.id in self.channels
|
|
|
|
|
|
|
|
return is_dm_channel or is_listed
|
|
|
|
|
2023-11-19 23:26:23 +00:00
|
|
|
|
2023-11-19 15:41:19 +00:00
|
|
|
class Config(BaseModel):
|
|
|
|
discord_token: str
|
2023-11-20 12:37:36 +00:00
|
|
|
command_prefix: str
|
2023-11-20 13:29:39 +00:00
|
|
|
command_failed: str
|
2023-11-26 19:34:50 +00:00
|
|
|
status: str
|
2023-11-20 12:37:36 +00:00
|
|
|
|
2023-11-19 16:32:12 +00:00
|
|
|
post: Post
|
2023-11-19 23:26:23 +00:00
|
|
|
ev_info: ClubInfo
|
2023-11-19 15:41:19 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get(cls) -> Self:
|
|
|
|
cfg_path = os.getenv(
|
|
|
|
key="CONFIG_PATH",
|
|
|
|
default="/usr/local/etc/lenaverse-bot/lenaverse-bot.toml",
|
|
|
|
)
|
|
|
|
|
|
|
|
with open(cfg_path, "rb") as cfg_file:
|
|
|
|
return cls.model_validate(tomllib.load(cfg_file))
|
|
|
|
|
|
|
|
|
|
|
|
CONFIG = Config.get()
|