Newer
Older
from disnake import InteractionMessage
import utils
from cogs.help import help, handle_error, help_category
"""
Umgebungsvariablen:
DISCORD_LEARNINGGROUPS_OPEN - Kategorie-ID der offenen Lerngruppen
DISCORD_LEARNINGGROUPS_CLOSE - Kategorie-ID der geschlossenen Lerngruppen
DISCORD_LEARNINGGROUPS_PRIVATE - Kategorie-ID der privaten Lerngruppen
DISCORD_LEARNINGGROUPS_ARCHIVE - Kategorie-ID der archivierten Lerngruppen
DISCORD_LEARNINGGROUPS_REQUEST - ID des Kanals, in dem Anfragen, die über den Bot gestellt wurden, eingetragen werden
DISCORD_LEARNINGGROUPS_INFO - ID des Kanals, in dem die Lerngruppen-Informationen gepostet/aktualisert werden
DISCORD_LEARNINGGROUPS_FILE - Name der Datei mit Verwaltungsdaten der Lerngruppen (minimaler Inhalt: {"requested": {},"groups": {}})
DISCORD_LEARNINGGROUPS_COURSE_FILE - Name der Datei welche die Kursnamen für die Lerngruppen-Informationen enthält (minimaler Inhalt: {})
DISCORD_MOD_ROLE - ID der Moderations-Rolle, die erweiterte Lerngruppen-Aktionen ausführen darf
LG_OPEN_SYMBOL = f'🌲'
LG_CLOSE_SYMBOL = f'🛑'
LG_PRIVATE_SYMBOL = f'🚪'
class GroupState(Enum):
OPEN = "OPEN"
CLOSED = "CLOSED"
PRIVATE = "PRIVATE"
ARCHIVED = "ARCHIVED"
REMOVED = "REMOVED"
"Mit dem Lerngruppen-Feature kannst du Lerngruppenkanäle beantragen und verwalten.",
"Hier kannst du Lerngruppenkanäle anlegen, beantragen und verwalten.")
class LearningGroups(commands.Cog):
def __init__(self, bot):
self.bot = bot
# ratelimit 2 in 10 minutes (305 * 2 = 610 = 10 minutes and 10 seconds)
self.rename_ratelimit = 305
self.msg_max_len = 2000
self.categories = {
GroupState.OPEN: os.getenv('DISCORD_LEARNINGGROUPS_OPEN'),
GroupState.CLOSED: os.getenv('DISCORD_LEARNINGGROUPS_CLOSE'),
GroupState.PRIVATE: os.getenv('DISCORD_LEARNINGGROUPS_PRIVATE'),
GroupState.ARCHIVED: os.getenv('DISCORD_LEARNINGGROUPS_ARCHIVE')
}
self.symbols = {
GroupState.OPEN: LG_OPEN_SYMBOL,
GroupState.CLOSED: LG_CLOSE_SYMBOL,
GroupState.PRIVATE: LG_PRIVATE_SYMBOL
}
self.channel_request = os.getenv('DISCORD_LEARNINGGROUPS_REQUEST')
self.channel_info = os.getenv('DISCORD_LEARNINGGROUPS_INFO')
self.group_file = os.getenv('DISCORD_LEARNINGGROUPS_FILE')
self.header_file = os.getenv('DISCORD_LEARNINGGROUPS_COURSE_FILE')
self.support_channel = os.getenv('DISCORD_SUPPORT_CHANNEL')
self.guild_id = os.getenv("DISCORD_GUILD")
self.groups = {} # organizer and learninggroup-member ids
self.channels = {} # complete channel configs
self.header = {} # headlines for status message
@commands.Cog.listener()
async def on_button_click(self, interaction: InteractionMessage):
button: Button = interaction.component
if button.custom_id == "learninggroups:group_yes":
await self.on_group_request(True, button, interaction)
elif button.custom_id == "learninggroups:group_no":
await self.on_group_request(False, button, interaction)
elif button.custom_id == "learninggroups:join_yes":
await self.on_join_request(True, button, interaction)
elif button.custom_id == "learninggroups:join_no":
await self.on_join_request(False, button, interaction)
@commands.Cog.listener(name="on_ready")
async def on_ready(self):
await self.update_channels()
def load_header(self):
file = open(self.header_file, mode='r')
self.header = json.load(file)
def save_header(self):
file = open(self.header_file, mode='w')
json.dump(self.header, file)
def load_groups(self):
group_file = open(self.group_file, mode='r')
self.groups = json.load(group_file)
if not self.groups.get("groups"):
self.groups['groups'] = {}
if not self.groups.get("requested"):
self.groups['requested'] = {}
if not self.groups.get("messageids"):
self.groups['messageids'] = []
for _, group in self.groups['requested'].items():
group["state"] = GroupState[group["state"]]
async def save_groups(self):
await self.update_channels()
groups = copy.deepcopy(self.groups)
for _, group in groups['requested'].items():
group["state"] = group["state"].name
json.dump(groups, group_file)
def arg_state_to_group_state(self, state: str):
if state in ["offen", "open", "o"]:
return GroupState.OPEN
if state in ["geschlossen", "closed", "close"]:
return GroupState.CLOSED
if state in ["private", "privat"]:
return GroupState.PRIVATE
def is_request_organizer(self, request, member):
return request["organizer_id"] == member.id
def is_group_organizer(self, channel, member):
channel_config = self.groups["groups"].get(str(channel.id))
if channel_config:
return channel_config["organizer_id"] == member.id
return False
def is_mod(self, member):
roles = member.roles
for role in roles:
if role.id == int(self.mod_role):
return True
return False
def is_group_request_message(self, message):
return len(message.embeds) > 0 and message.embeds[0].title == "Lerngruppenanfrage!"
async def is_channel_config_valid(self, ctx, channel_config, command=None):
if channel_config['state'] is None:
f"Fehler! Bitte gib an ob die Gruppe **offen** (**open**) **geschlossen** (**closed**) oder **privat** (**private**) ist. Gib `!help {command}` für Details ein.")
return False
if not re.match(r"^[0-9]+$", channel_config['course']):
if command:
await ctx.channel.send(
f"Fehler! Die Kursnummer muss numerisch sein. Gib `!help {command}` für Details ein.")
return False
if not re.match(r"^(sose|wise)[0-9]{2}$", channel_config['semester']):
if command:
await ctx.channel.send(
f"Fehler! Das Semester muss mit **sose** oder **wise** angegeben werden gefolgt von der **zweistelligen Jahreszahl**. Gib `!help {command}` für Details ein.")
return False
return True
async def check_rename_rate_limit(self, channel_config):
if channel_config.get("last_rename") is None:
return False
now = int(time.time())
seconds = channel_config["last_rename"] + self.rename_ratelimit - now
if seconds > 0:
channel = await self.bot.fetch_channel(int(channel_config["channel_id"]))
await channel.send(f"Discord schränkt die Anzahl der Aufrufe für manche Funktionen ein, daher kannst du diese Aktion erst wieder in {seconds} Sekunden ausführen.")
async def category_of_channel(self, state: GroupState):
category_to_fetch = self.categories[state]
category = await self.bot.fetch_channel(category_to_fetch)
return category
def full_channel_name(self, channel_config):
return (f"{self.symbols[channel_config['state']]}"
f"{channel_config['course']}-{channel_config['name']}-{channel_config['semester']}"
f"{LG_LISTED_SYMBOL if channel_config['is_listed'] else ''}")
async def update_statusmessage(self):
info_message_ids = self.groups.get("messageids")
channel = await self.bot.fetch_channel(int(self.channel_info))
for info_message_id in info_message_ids:
message = await channel.fetch_message(info_message_id)
await message.delete()
info_message_ids = []
msg = f"**Lerngruppen**\n\n"
course_msg = ""
sorted_channels = sorted(self.channels.values(
), key=lambda channel: f"{channel['course']}-{channel['name']}")
open_channels = [channel for channel in sorted_channels if channel['state'] in [GroupState.OPEN]
or channel['is_listed']]
no_headers = []
for lg_channel in open_channels:
if lg_channel['course'] != courseheader:
if len(msg) + len(course_msg) > self.msg_max_len:
message = await channel.send(msg)
info_message_ids.append(message.id)
msg = course_msg
course_msg = ""
else:
msg += course_msg
course_msg = ""
header = self.header.get(lg_channel['course'])
if header:
course_msg += f"**{header}**\n"
else:
course_msg += f"**{lg_channel['course']} - -------------------------------------**\n"
no_headers.append(lg_channel['course'])
courseheader = lg_channel['course']
groupchannel = await self.bot.fetch_channel(int(lg_channel['channel_id']))
course_msg += f" {groupchannel.mention}"
if lg_channel['is_listed'] and lg_channel['state'] == GroupState.PRIVATE:
group_config = self.groups["groups"].get(lg_channel['channel_id'])
if group_config:
user = await self.bot.fetch_user(group_config['organizer_id'])
course_msg += f" **@{user.name}#{user.discriminator}**"
course_msg += f"\n **↳** `!lg join {groupchannel.id}`"
course_msg += "\n"
msg += course_msg
message = await channel.send(msg)
if len(no_headers) > 0:
support_channel = await self.bot.fetch_channel(int(self.support_channel))
if support_channel:
await support_channel.send(f"In der Lerngruppenübersicht fehlen noch Überschriften für die folgenden Kurse: **{', '.join(no_headers)}**")
info_message_ids.append(message.id)
self.groups["messageids"] = info_message_ids
group_config = self.groups["groups"].get(str(channel.id))
if not group_config:
await channel.send("Das ist kein Lerngruppenkanal.")
return
category = await self.bot.fetch_channel(self.categories[GroupState.ARCHIVED])
await self.move_channel(channel, category)
await channel.edit(name=f"archiv-${channel.name[1:]}")
await self.update_permissions(channel)
await self.remove_group(channel)
await self.update_statusmessage()
async def set_channel_state(self, channel, state: GroupState = None):
channel_config = self.channels[str(channel.id)]
if await self.check_rename_rate_limit(channel_config):
return False # prevent api requests when ratelimited
if state is not None:
old_state = channel_config["state"]
if old_state == state:
return False # prevent api requests when nothing has changed
channel_config["state"] = state
await self.alter_channel(channel, channel_config)
return True
async def set_channel_listing(self, channel, is_listed):
channel_config = self.channels[str(channel.id)]
if await self.check_rename_rate_limit(channel_config):
return False # prevent api requests when ratelimited
if channel_config["state"] in [GroupState.CLOSED, GroupState.PRIVATE]:
was_listed = channel_config["is_listed"]
if was_listed == is_listed:
return False # prevent api requests when nothing has changed
channel_config["is_listed"] = is_listed
await self.alter_channel(channel, channel_config)
return True
async def alter_channel(self, channel, channel_config):
self.groups["groups"][str(channel.id)]["last_rename"] = int(time.time())
await channel.edit(name=self.full_channel_name(channel_config))
category = await self.category_of_channel(channel_config["state"])
await self.move_channel(channel, category,
sync=True if channel_config["state"] in [GroupState.OPEN, GroupState.CLOSED] else False)
await self.save_groups()
await self.update_statusmessage()
return True
channel_config = self.channels[str(channel.id)]
if await self.check_rename_rate_limit(channel_config):
return # prevent api requests when ratelimited
self.groups["groups"][str(channel.id)]["last_rename"] = int(time.time())
channel_config["name"] = name
await channel.edit(name=self.full_channel_name(channel_config))
await self.save_groups()
await self.update_statusmessage()
async def move_channel(self, channel, category, sync=True):
for sortchannel in category.text_channels:
if sortchannel.name[1:] > channel.name[1:]:
await channel.move(category=category, before=sortchannel, sync_permissions=sync)
await channel.move(category=category, sync_permissions=sync, end=True)
async def add_requested_group_channel(self, message, direct=False):
requested_channel_config = self.groups["requested"].get(str(message.id))
category = await self.category_of_channel(requested_channel_config["state"])
full_channel_name = self.full_channel_name(requested_channel_config)
channel = await category.create_text_channel(full_channel_name)
await self.move_channel(channel, category, False)
user = await self.bot.fetch_user(requested_channel_config["organizer_id"])
await channel.send(f":wave: <@!{user.id}>, hier ist deine neue Lerngruppe.\n"
"Es gibt offene und private Lerngruppen. Eine offene Lerngruppe ist für jeden sichtbar "
"und jeder kann darin schreiben. Eine private Lerngruppe ist unsichtbar und auf eine "
"Gruppe an Kommilitoninnen beschränkt."
"```"
"Funktionen für Lerngruppenorganisatorinnen:\n"
"!lg addmember <@newmember>: Fügt ein Mitglied zur Lerngruppe hinzu.\n"
"!lg organizer <@neworganizer>: Ändert die Organisatorin der Lerngruppe auf @neworganizer.\n"
"!lg open: Öffnet eine Lerngruppe.\n"
"!lg close: Schließt eine Lerngruppe.\n"
"!lg private: Stellt die Lerngruppe auf privat.\n"
"!lg show: Zeigt eine private oder geschlossene Lerngruppe in der Lerngruppenliste an.\n"
"!lg hide: Entfernt eine private oder geschlossene Lerngruppe aus der Lerngruppenliste.\n"
"!lg kick <@user>: Schließt eine Benutzerin von der Lerngruppe aus.\n"
"\nKommandos für alle:\n"
"!lg id: Zeigt die ID der Lerngruppe an mit der andere Kommilitoninnen beitreten können.\n"
"!lg members: Zeigt die Mitglieder der Lerngruppe an.\n"
"!lg organizer: Zeigt die Organisatorin der Lerngruppe an.\n"
"!lg leave: Du verlässt die Lerngruppe.\n"
"!lg join: Anfrage, um der Lerngruppe beizutreten.\n"
"\nMit dem nachfolgenden Kommando kann eine Kommilitonin darum "
"bitten in die Lerngruppe aufgenommen zu werden wenn die Gruppe privat ist.\n"
"\n(Manche Kommandos werden von Discord eingeschränkt und können nur einmal alle 5 Minuten ausgeführt werden.)"
self.groups["groups"][str(channel.id)] = {
"organizer_id": requested_channel_config["organizer_id"],
"last_rename": int(time.time())
}
await self.remove_group_request(message)
await self.save_groups()
await self.update_statusmessage()
if requested_channel_config["state"] is GroupState.PRIVATE:
await self.update_permissions(channel)
async def remove_group_request(self, message):
async def remove_group(self, channel):
await self.save_groups()
def channel_to_channel_config(self, channel):
cid = str(channel.id)
is_listed = channel.name[-1] == LG_LISTED_SYMBOL
result = re.match(r"([0-9]+)-(.*)-([a-z0-9]+)$", channel.name[1:] if not is_listed else channel.name[1:-1])
state = None
if channel.name[0] == LG_OPEN_SYMBOL:
state = GroupState.OPEN
elif channel.name[0] == LG_CLOSE_SYMBOL:
state = GroupState.CLOSED
elif channel.name[0] == LG_PRIVATE_SYMBOL:
state = GroupState.PRIVATE
course, name, semester = result.group(1, 2, 3)
channel_config = {"course": course, "name": name, "category": channel.category_id, "semester": semester,
"state": state, "is_listed": is_listed, "channel_id": cid}
if self.groups["groups"].get(cid):
channel_config.update(self.groups["groups"].get(cid))
return channel_config
async def update_channels(self):
self.channels = {}
for state in [GroupState.OPEN, GroupState.CLOSED, GroupState.PRIVATE]:
category = await self.category_of_channel(state)
channel_config = self.channel_to_channel_config(channel)
self.channels[str(channel.id)] = channel_config
async def add_member_to_group(self, channel: disnake.TextChannel, arg_member: disnake.Member, send_message=True):
group_config = self.groups["groups"].get(str(channel.id))
if not group_config:
await channel.send("Das ist kein Lerngruppenkanal.")
return
users = group_config.get("users")
if not users:
users = {}
mid = str(arg_member.id)
if not users.get(mid):
users[mid] = True
user = await self.bot.fetch_user(mid)
if user and send_message:
try:
await utils.send_dm(user, f"Du wurdest in die Lerngruppe <#{channel.id}> aufgenommen. "
"Viel Spass beim gemeinsamen Lernen!\n"
"Dieser Link führt dich direkt zum Lerngruppenkanal. "
"Diese Nachricht kannst du in unserer Unterhaltung mit Rechtsklick anpinnen, "
"wenn du möchtest.")
group_config["users"] = users
await self.save_groups()
async def remove_member_from_group(self, channel: disnake.TextChannel, arg_member: disnake.Member, send_message=True):
group_config = self.groups["groups"].get(str(channel.id))
if not group_config:
await channel.send("Das ist kein Lerngruppenkanal.")
return
users = group_config.get("users")
if not users:
return
mid = str(arg_member.id)
if users.pop(mid, None):
user = await self.bot.fetch_user(mid)
if user and send_message:
await utils.send_dm(user, f"Du wurdest aus der Lerngruppe {channel.name} entfernt.")
async def update_permissions(self, channel):
channel_config = self.channels[str(channel.id)]
if channel_config.get("state") == GroupState.PRIVATE:
overwrites = await self.overwrites(channel)
await channel.edit(overwrites=overwrites)
else:
await channel.edit(sync_permissions=True)
async def overwrites(self, channel):
channel = await self.bot.fetch_channel(str(channel.id))
group_config = self.groups["groups"].get(str(channel.id))
guild = await self.bot.fetch_guild(int(self.guild_id))
mods = guild.get_role(int(self.mod_role))
overwrites = {
mods: disnake.PermissionOverwrite(read_messages=True),
guild.default_role: disnake.PermissionOverwrite(read_messages=False)
}
if not group_config:
return overwrites
organizer = self.bot.get_user(group_config["organizer_id"])
if not organizer:
overwrites[organizer] = disnake.PermissionOverwrite(read_messages=True)
users = group_config.get("users")
if not users:
return overwrites
for userid in users.keys():
user = await self.bot.fetch_user(userid)
overwrites[user] = disnake.PermissionOverwrite(read_messages=True)
return overwrites
@help(
category="learninggroups",
syntax="!lg <command>",
brief="Lerngruppenverwaltung"
)
@commands.group(name="lg", aliases=["learninggroup", "lerngruppe"], pass_context=True)
async def cmd_lg(self, ctx):
await ctx.channel.send("Gib `!help lg` ein, um eine Übersicht über die Lerngruppen-Kommandos zu erhalten.")
@help(
command_group="lg",
category="learninggroups",
brief="Aktualisiert die Lerngruppenliste",
mod=True
)
@cmd_lg.command(name="update")
@commands.check(utils.is_mod)
async def cmd_update(self, ctx):
await self.update_channels()
await self.update_statusmessage()
syntax="!lg header <coursenumber> <name...>",
brief="Fügt einen Kurs als neue Überschrift in Botys Lerngruppen-Liste (Kanal #lerngruppen) hinzu. "
"Der Name darf Leerzeichen enthalten, Anführungszeichen sind nicht erforderlich.",
example="!lg header 1141 Mathematische Grundlagen",
parameters={
"coursenumber": "Nummer des Kurses wie von der Fernuni angegeben (ohne führende Nullen z. B. 1142).",
"name...": "Ein frei wählbarer Text (darf Leerzeichen enthalten).",
},
description="Kann auch zum Bearbeiten einer Überschrift genutzt werden. Existiert die Kursnummer bereits, "
"wird die Überschrift geändert.",
async def cmd_add_header(self, ctx, arg_course, *arg_name):
if not re.match(r"[0-9]+", arg_course):
await ctx.channel.send(
f"Fehler! Die Kursnummer muss numerisch sein. Gib `!help add-course` für Details ein.")
return
self.header[arg_course] = f"{arg_course} - {' '.join(arg_name)}"
self.save_header()
await self.update_statusmessage()
syntax="!lg add <coursenumber> <name> <semester> <status> <@usermention>",
example="!lg add 1142 mathegenies sose22 closed @someuser",
brief="Fügt einen Lerngruppenkanal hinzu. Der Name darf keine Leerzeichen enthalten.",
parameters={
"coursenumber": "Nummer des Kurses wie von der Fernuni angegeben (ohne führende Nullen z. B. 1142).",
"name": "Ein frei wählbarer Text ohne Leerzeichen. Bindestriche sind zulässig.",
"semester": ("Das Semester, für welches diese Lerngruppe erstellt werden soll."
"sose oder wise gefolgt von der zweistelligen Jahreszahl (z. B. sose22)."),
"status": "Gibt an ob die Lerngruppe für weitere Lernwillige geöffnet ist (open) oder nicht (closed).",
"@usermention": "Die so erwähnte Benutzerin wird als Organisatorin der Lerngruppe eingesetzt."
async def cmd_add_group(self, ctx, arg_course, arg_name, arg_semester, arg_state, arg_organizer: disnake.Member):
state = self.arg_state_to_group_state(arg_state)
channel_config = {"organizer_id": arg_organizer.id, "course": arg_course, "name": arg_name, "semester": arg_semester,
"state": state, "is_listed": False}
if not await self.is_channel_config_valid(ctx, channel_config, ctx.command.name):
return
self.groups["requested"][str(ctx.message.id)] = channel_config
await self.add_requested_group_channel(ctx.message, direct=True)
@help(
syntax="!lg request <coursenumber> <name> <semester> <status>",
brief="Stellt eine Anfrage für einen neuen Lerngruppenkanal.",
example="!lg request 1142 mathegenies sose22 closed",
description=("Moderatorinnen können diese Anfrage bestätigen, dann wird die Gruppe eingerichtet. "
"Die Organisatorin der Gruppe ist die Benutzerin, die die Anfrage gestellt hat."),
parameters={
"coursenumber": "Nummer des Kurses, wie von der FernUni angegeben (ohne führende Nullen z. B. 1142).",
"name": "Ein frei wählbarer Text ohne Leerzeichen.",
"semester": "Das Semester, für welches diese Lerngruppe erstellt werden soll. sose oder wise gefolgt "
"von der zweistelligen Jahreszahl (z. B. sose22).",
"status": "Gibt an ob die Lerngruppe für weitere Lernwillige geöffnet ist (open) oder nicht (closed) oder ob es sich um eine private Lerngruppe handelt (private)."
@cmd_lg.command(name="request", aliases=["r", "req"])
async def cmd_request_group(self, ctx, arg_course, arg_name, arg_semester, arg_state):
arg_state = re.sub(r"[^a-z0-9]", "", arg_state.lower())
arg_semester = re.sub(r"[^a-z0-9]", "", arg_semester.lower())
if re.match(r"(wise)|(sose)[0-9]+", arg_state) and re.match(r"(open)|(closed*)|(private)", arg_semester):
tmp = arg_state
arg_state = arg_semester
arg_semester = tmp
arg_semester = re.sub(r"[^wiseo0-9]", "", arg_semester)
arg_state = re.sub(r"[^a-z]", "", arg_state)
state = self.arg_state_to_group_state(arg_state)
arg_course = re.sub(r"[^0-9]", "", arg_course)
arg_course = re.sub(r"^0+", "", arg_course)
arg_name = re.sub(
r"[^A-Za-zäöüß0-9-]",
"",
arg_name.lower().replace(" ", "-")
)
if len(arg_semester) == 8:
arg_semester = f"{arg_semester[0:4]}{arg_semester[-2:]}"
channel_config = {"organizer_id": ctx.author.id, "course": arg_course, "name": arg_name, "semester": arg_semester,
"state": state, "is_listed": False}
if not await self.is_channel_config_valid(ctx, channel_config, ctx.command.name):
return
channel = await self.bot.fetch_channel(int(self.channel_request))
channel_name = self.full_channel_name(channel_config)
message = await utils.confirm(
channel=channel,
title="Lerngruppenanfrage",
description=f"<@!{ctx.author.id}> möchte gerne die Lerngruppe **#{channel_name}** eröffnen.",
self.groups["requested"][str(message.id)] = channel_config
await self.save_groups()
@help(
command_group="lg",
category="learninggroups",
syntax="!lg show",
brief="Zeigt einen privaten Lerngruppenkanal trotzdem in der Liste an.",
description=("Muss im betreffenden Lerngruppenkanal ausgeführt werden. "
"Die Lerngruppe wird in der Übersicht der Lerngruppen aufgeführt, so dass Kommilitoninnen noch "
"anfragen können, in die Lerngruppe aufgenommen zu werden."
"Diese Aktion kann nur von der Organisatorin der Lerngruppe ausgeführt werden. ")
)
@cmd_lg.command(name="show")
async def cmd_show(self, ctx):
if self.is_group_organizer(ctx.channel, ctx.author) or utils.is_mod(ctx):
channel_config = self.channels[str(ctx.channel.id)]
if channel_config:
if channel_config.get("state") == GroupState.PRIVATE:
if await self.set_channel_listing(ctx.channel, True):
await ctx.channel.send("Die Lerngruppe wird nun in der Lerngruppenliste angezeigt.")
elif channel_config.get("state") == GroupState.OPEN:
await ctx.channel.send("Nichts zu tun. Offene Lerngruppen werden sowieso in der Liste angezeigt.")
elif channel_config.get("state") == GroupState.CLOSED:
await ctx.channel.send("Möchtest du die Gruppen öffnen? Versuch's mit `!lg open`")
syntax="!lg hide",
brief="Versteckt einen privaten Lerngruppenkanal. ",
description=("Muss im betreffenden Lerngruppenkanal ausgeführt werden. "
"Die Lerngruppe wird nicht mehr in der Liste der Lerngruppen angezeigt. "
"Diese Aktion kann nur von der Organisatorin der Lerngruppe ausgeführt werden. ")
)
@cmd_lg.command(name="hide")
async def cmd_hide(self, ctx):
if self.is_group_organizer(ctx.channel, ctx.author) or utils.is_mod(ctx):
channel_config = self.channels[str(ctx.channel.id)]
if channel_config:
if channel_config.get("state") == GroupState.PRIVATE:
if await self.set_channel_listing(ctx.channel, False):
await ctx.channel.send("Die Lerngruppe wird nun nicht mehr in der Lerngruppenliste angezeigt.")
return
elif channel_config.get("state") == GroupState.OPEN:
await ctx.channel.send("Offene Lerngruppen können nicht aus der Lerngruppenliste entfernt werden. "
"Führe `!lg close` aus, um die Lerngruppe zu schließen, "
"oder `!lg private`, um diese auf "
"privat zu schalten.")
elif channel_config.get("state") == GroupState.CLOSED:
await ctx.channel.send("Wenn diese Gruppe privat werden soll, ist das Kommando das du brauchst: `!lg private`")
@cmd_lg.command(name="debug")
@commands.check(utils.is_mod)
async def cmd_debug(self, ctx):
channel_config = self.channels[str(ctx.channel.id)]
if not channel_config:
await ctx.channel.send("None")
return
await ctx.channel.send(str(channel_config))
@help(
command_group="lg",
category="learninggroups",
syntax="!lg open",
brief="Öffnet den Lerngruppenkanal, wenn du die Organisatorin bist. ",
description=("Muss im betreffenden Lerngruppenkanal ausgeführt werden. "
"Verschiebt den Lerngruppenkanal in die Kategorie für offene Kanäle und ändert das Icon. "
"Diese Aktion kann nur von der Organisatorin der Lerngruppe ausgeführt werden. ")
@cmd_lg.command(name="open", aliases=["opened", "offen"])
if self.is_group_organizer(ctx.channel, ctx.author) or utils.is_mod(ctx):
await self.set_channel_state(ctx.channel, state=GroupState.OPEN)
brief="Schließt den Lerngruppenkanal, wenn du die Organisatorin bist. ",
description=("Muss im betreffenden Lerngruppenkanal ausgeführt werden. "
"Stellt die Lerngruppe auf geschlossen. Dies ist rein symbolisch und zeigt an, "
"dass keine neuen Mitglieder mehr aufgenommen werden. "
"Diese Aktion kann nur von der Organisatorin der Lerngruppe ausgeführt werden. ")
)
@cmd_lg.command(name="close", aliases=["closed", "geschlossen"])
async def cmd_close(self, ctx):
if self.is_group_organizer(ctx.channel, ctx.author) or utils.is_mod(ctx):
await self.set_channel_state(ctx.channel, state=GroupState.CLOSED)
@help(
command_group="lg",
category="learninggroups",
syntax="!lg private",
brief="Macht aus deiner Lerngruppe eine private Lerngruppe, wenn du die Organisatorin bist. ",
description=("Muss im betreffenden Lerngruppenkanal ausgeführt werden. "
"Stellt die Lerngruppe auf privat. Es haben nur noch Mitglieder "
"der Lerngruppe Zugriff auf den Kanal. (siehe `!lg members`)"
"Diese Aktion kann nur von der Organisatorin der Lerngruppe ausgeführt werden. ")
@cmd_lg.command(name="private", aliases=["privat"])
async def cmd_private(self, ctx):
if self.is_group_organizer(ctx.channel, ctx.author) or utils.is_mod(ctx):
if await self.set_channel_state(ctx.channel, state=GroupState.PRIVATE):
await self.update_permissions(ctx.channel)
brief="Ändert den Namen des Lerngruppenkanals, in dem das Kommando ausgeführt wird.",
example="!lg rename matheluschen",
description="Aus #1142-matheprofis-sose22 wird nach dem Aufruf des Beispiels #1142-matheluschen-sose22.",
parameters={
"name": "Der neue Name der Lerngruppe ohne Leerzeichen."
},
mod=True
)
@commands.check(utils.is_mod)
async def cmd_rename(self, ctx, arg_name):
await self.set_channel_name(ctx.channel, arg_name)
@help(
command_group="lg",
syntax="!lg archive",
brief="Archiviert den Lerngruppenkanal.",
description="Verschiebt den Lerngruppenkanal, in welchem dieses Kommando ausgeführt wird, ins Archiv.",
@cmd_lg.command(name="archive", aliases=["archiv"])
@commands.check(utils.is_mod)
async def cmd_archive(self, ctx):
await self.archive(ctx.channel)
@help(
syntax="!lg organizer <@usermention>",
example="!lg organizer @someuser",
brief="Bestimmt die Organisatorin eines Lerngruppenkanals.",
description="Muss im betreffenden Lerngruppenkanal ausgeführt werden. ",
"@usermention": "Die neue Organisatorin der Lerngruppe."
@cmd_lg.command(name="organizer")
async def cmd_organizer(self, ctx, new_organizer: disnake.Member = None):
group_config = self.groups["groups"].get(str(ctx.channel.id))
if not group_config:
self.groups["groups"][str(ctx.channel.id)] = {}
group_config = self.groups["groups"][str(ctx.channel.id)]
organizer_id = group_config.get("organizer_id")
if not new_organizer:
user = await self.bot.fetch_user(organizer_id)
await ctx.channel.send(f"Organisatorin: @{user.name}#{user.discriminator}")
elif isinstance(group_config, dict):
organizer = await self.bot.fetch_user(organizer_id)
if self.is_group_organizer(ctx.channel, ctx.author) or utils.is_mod(ctx):
group_config["organizer_id"] = new_organizer.id
await self.remove_member_from_group(ctx.channel, new_organizer, False)
if new_organizer != organizer:
await self.add_member_to_group(ctx.channel, organizer, False)
await self.save_groups()
await self.update_permissions(ctx.channel)
await ctx.channel.send(
f"Glückwunsch {new_organizer.mention}! Du bist jetzt die Organisatorin dieser Lerngruppe.")
@help(
command_group="lg",
category="learninggroups",
syntax="!lg addmember <@usermention> <#channel>",
example="!lg addmember @someuser #1141-mathegl-lerngruppe-sose21",
brief="Fügt eine Benutzerin zu einer Lerngruppe hinzu.",
"@usermention": "Die so erwähnte Benutzerin wird zur Lerngruppe hinzugefügt.",
"#channel": "(optional) Der Kanal, zu dem die Benutzerin hinzugefügt werden soll."
}
)
@cmd_lg.command(name="addmember", aliases=["addm", "am"])
async def cmd_add_member(self, ctx, arg_member: disnake.Member, arg_channel: disnake.TextChannel = None):
if not arg_channel:
if not self.channels.get(str(ctx.channel.id)):
await ctx.channel.send("Wenn das Kommando außerhalb eines Lerngruppenkanals aufgerufen wird, muss der"
"Lerngruppenkanal angefügt werden. `!lg addmember <@usermention> <#channel>`")
return
arg_channel = ctx.channel
if self.is_group_organizer(arg_channel, ctx.author) or utils.is_mod(ctx):
await self.add_member_to_group(arg_channel, arg_member)
await self.update_permissions(arg_channel)
@help(
command_group="lg",
category="learninggroups",
syntax="!lg removemember <@usermention> <#channel>",
example="!lg removemember @someuser #1141-mathegl-lerngruppe-sose21",
brief="Entfernt eine Benutzerin aus einer Lerngruppe.",
"#channel": "Der Kanal, aus dem die Benutzerin gelöscht werden soll.",
"@usermention": "Die so erwähnte Benutzerin wird aus der Lerngruppe entfernt."
@cmd_lg.command(name="removemember", aliases=["remm", "rm"])
async def cmd_remove_member(self, ctx, arg_member: disnake.Member, arg_channel: disnake.TextChannel):
await self.remove_member_from_group(arg_channel, arg_member)
await self.update_permissions(arg_channel)
brief="Zählt die Mitglieder der Lerngruppe auf.",
@cmd_lg.command(name="members")
async def cmd_members(self, ctx):
group_config = self.groups["groups"].get(str(ctx.channel.id))
if not group_config:
await ctx.channel.send("Das ist kein Lerngruppenkanal.")
return
organizer_id = group_config.get("organizer_id")
organizer = await self.bot.fetch_user(organizer_id)
users = group_config.get("users", {})
await ctx.channel.send("Keine Lerngruppenmitglieder vorhanden.")
for user_id in users:
user = await self.bot.fetch_user(user_id)
names.append("@" + user.name + "#" + user.discriminator)
await ctx.channel.send(f"Organisatorin: **@{organizer.name}#{organizer.discriminator}**\nMitglieder: " +
(f"{', '.join(names)}" if len(names) > 0 else "Keine"))
@help(
command_group="lg",
category="learninggroups",
syntax="!lg id",
brief="Zeigt die ID für deine Lerngruppe an.",
)
@cmd_lg.command(name="id")
async def cmd_id(self, ctx):
if self.is_group_organizer(ctx.channel, ctx.author) or utils.is_mod(ctx):
group_config = self.groups["groups"].get(str(ctx.channel.id))
if not group_config:
await ctx.channel.send("Das ist kein Lerngruppenkanal.")
return
await ctx.channel.send(f"Die ID dieser Lerngruppe lautet: `{str(ctx.channel.id)}`.\n"
f"Beitrittsanfrage mit: `!lg join {str(ctx.channel.id)}`")
@help(
command_group="lg",
category="learninggroups",
syntax="!lg join <lg-id>",
brief="Fragt bei der Organisatorin einer Lerngruppe um Aufnahme an.",
}
)
@cmd_lg.command(name="join")
async def cmd_join(self, ctx, arg_id_or_channel: Union[int, disnake.TextChannel] = None):
if arg_id_or_channel is None:
arg_id_or_channel = ctx.channel
cid = arg_id_or_channel.id if type(arg_id_or_channel) is disnake.TextChannel else arg_id_or_channel
group_config = self.groups["groups"].get(str(cid))
if not group_config:
await ctx.channel.send("Das ist keine gültiger Lerngruppenkanal.")
channel = await self.bot.fetch_channel(int(cid))
await utils.confirm(
channel=channel,
title="Jemand möchte deiner Lerngruppe beitreten!",
description=f"<@!{ctx.author.id}> möchte gerne der Lerngruppe **#{channel.name}** beitreten.",
message=f"<@!{group_config['organizer_id']}>, du wirst gebraucht. Anfrage von <@!{ctx.author.id}>:",
await utils.send_dm(ctx.author, f"Deine Anfrage wurde an **#{channel.name}** gesendet. "
"Sobald die Organisatorin der Lerngruppe darüber "
"entschieden hat, bekommst du Bescheid.")
@help(
command_group="lg",
category="learninggroups",
syntax="!lg kick <@usermention>",
brief="Wirft @usermention aus der Gruppe."
)
@cmd_lg.command(name="kick")
async def cmd_kick(self, ctx, arg_member: disnake.Member):
if self.is_group_organizer(ctx.channel, ctx.author) or utils.is_mod(ctx):
group_config = self.groups["groups"].get(str(ctx.channel.id))
if not group_config:
await ctx.channel.send("Das ist keine gültiger Lerngruppenkanal.")
return
await self.remove_member_from_group(ctx.channel, arg_member)
await self.update_permissions(ctx.channel)
@help(
command_group="lg",
category="learninggroups",
syntax="!lg leave",
brief="Du verlässt die Lerngruppe."
)
@cmd_lg.command(name="leave")
async def cmd_leave(self, ctx):
group_config = self.groups["groups"].get(str(ctx.channel.id))
if not group_config:
await ctx.channel.send("Das ist keine gültiger Lerngruppenkanal.")
return
if group_config["organizer_id"] == ctx.author.id:
await ctx.channel.send("Du kannst nicht aus deiner eigenen Lerngruppe flüchten. Gib erst die Verantwortung ab.")
return
await self.remove_member_from_group(ctx.channel, ctx.author)
await self.update_permissions(ctx.channel)
async def on_group_request(self, confirmed, button, interaction: InteractionMessage):
channel = interaction.channel
member = interaction.author
message = interaction.message
if str(channel.id) == str(self.channel_request):
request = self.groups["requested"].get(str(message.id))
if confirmed and self.is_mod(member):
await self.add_requested_group_channel(message, direct=False)
elif not confirmed and (self.is_request_organizer(request, member) or self.is_mod(member)):
if self.is_mod(member):
user = await self.bot.fetch_user(request["organizer_id"] )
if user:
await utils.send_dm(user, f"Deine Lerngruppenanfrage für #{self.full_channel_name(request)} wurde abgelehnt.")
await self.remove_group_request(message)
async def on_join_request(self, confirmed, button, interaction: InteractionMessage):