Newer
Older
from disnake import InteractionMessage
import utils
from cogs.help import help, handle_error, help_category
"""
Environment Variablen:
DISCORD_LEARNINGGROUPS_OPEN - ID der Kategorie für offene Lerngruppen
DISCORD_LEARNINGGROUPS_CLOSE - ID der Kategorie für private Lerngruppen
DISCORD_LEARNINGGROUPS_ARCHIVE - ID der Kategorie für archivierte Lerngruppen
DISCORD_LEARNINGGROUPS_REQUEST - ID des Channels in welchem Requests vom Bot eingestellt werden
DISCORD_LEARNINGGROUPS_INFO - ID des Channels in welchem 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 (minimalter Inhalt: {})
DISCORD_MOD_ROLE - ID der Moderator Rolle von der erweiterte Lerngruppen-Actionen ausgeführt werden dürfen
"""
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 Lerngruppen-Kanäle beantragen und verwalten.",
"Hier kannst du Lerngruppen-Kanä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 = {} # owner and learninggroup-member ids
self.channels = {} # complete channel configs
self.header = {} # headlines for statusmessage
@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
return None
def is_request_owner(self, request, member):
return request["owner_id"] == member.id
def is_group_owner(self, channel, member):
channel_config = self.groups["groups"].get(str(channel.id))
if channel_config:
return channel_config["owner_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 limitiert die Aufrufe für manche Funktionen, 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['owner_id'])
if user:
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"Es fehlen noch Überschriften für folgende Kurse in der Lerngruppenübersicht: **{', '.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.remove_group(channel)
await self.update_permissions(channel)
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 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 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["owner_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."
"```"
"Besitzerinfunktionen:\n"
"!lg addmember <@newmember>: Fügt ein Mitglied zur Lerngruppe hinzu.\n"
"!lg owner <@newowner>: Ändert die Besitzerin der Lerngruppe auf @newowner.\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 owner: Zeigt die Besitzerin der Lerngruppe.\n"
"!lg leave: Du verlässt die Lerngruppe.\n"
"!lg join: Anfrage stellen in die Lerngruppe aufgenommen zu werden.\n"
"\nMit dem nachfolgenden Kommando kann eine Kommilitonin darum "
"bitten in die Lerngruppe aufgenommen zu werden wenn diese bereits privat ist.\n"
f"!lg join {channel.id}"
"\n(manche Kommandos sind von Discord limitiert und können nur einmal alle 5 Minuten ausgeführt werden)"
"```"
)
self.groups["groups"][str(channel.id)] = {
"owner_id": requested_channel_config["owner_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:
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 Lerngruppen-Channel. "
"Diese Nachricht kannst du bei Bedarf in unserer Unterhaltung "
"über Rechtsklick anpinnen.")
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)
}
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
if not group_config:
return overwrites
owner = self.bot.get_user(group_config["owner_id"])
if not owner:
return overwrites
overwrites[owner] = 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="Updated 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. "
"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. Bei bereits existierender "
"Kursnummer wird die Überschrift abgeä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 Lerngruppen-Kanal 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 Besitzerin für die Lerngruppe gesetzt."
async def cmd_add_group(self, ctx, arg_course, arg_name, arg_semester, arg_state, arg_owner: disnake.Member):
state = self.arg_state_to_group_state(arg_state)
channel_config = {"owner_id": arg_owner.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 Lerngruppen-Kanal.",
example="!lg request 1142 mathegenies sose22 closed",
description=("Moderatorinnen können diese Anfrage bestätigen, dann wird die Gruppe eingerichtet. "
"Die Besitzerin der Gruppe ist die Benutzerin die die Anfrage eingestellt 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)."
}
)
@cmd_lg.command(name="request", aliases=["r", "req"])
async def cmd_request_group(self, ctx, arg_course, arg_name, arg_semester, arg_state):
state = self.arg_state_to_group_state(arg_state)
arg_name = re.sub(
r"[^A-Za-zäöüß0-9-]",
"",
arg_name.lower().replace(" ", "-")
)
arg_semester = arg_semester.lower()
if len(arg_semester) == 8:
arg_semester = f"{arg_semester[0:4]}{arg_semester[-2:]}"
channel_config = {"owner_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 Lerngruppen-Kanal ausgeführt werden. "
"Die Lerngruppe wird in der Übersicht der Lerngruppen gelistet, so können Kommilitoninnen noch "
"Anfragen stellen, um in die Lerngruppe aufgenommen zu werden."
"Diese Aktion kann nur von der Besitzerin der Lerngruppe ausgeführt werden. ")
)
@cmd_lg.command(name="show")
async def cmd_show(self, ctx):
if self.is_group_owner(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 Lerngruppen-Kanal ausgeführt werden. "
"Die Lerngruppe wird nicht mehr in der Liste der Lerngruppen aufgeführt. "
"Diese Aktion kann nur von der Besitzerin der Lerngruppe ausgeführt werden. ")
)
@cmd_lg.command(name="hide")
async def cmd_hide(self, ctx):
if self.is_group_owner(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 Lerngruppen-Kanal wenn du die Besitzerin bist. ",
description=("Muss im betreffenden Lerngruppen-Kanal ausgeführt werden. "
"Verschiebt den Lerngruppen-Kanal in die Kategorie für offene Kanäle und ändert das Icon. "
"Diese Aktion kann nur von der Besitzerin der Lerngruppe ausgeführt werden. ")
@cmd_lg.command(name="open", aliases=["opened", "offen"])
async def cmd_open(self, ctx):
if self.is_group_owner(ctx.channel, ctx.author) or utils.is_mod(ctx):
await self.set_channel_state(ctx.channel, state=GroupState.OPEN)
brief="Schließt den Lerngruppen-Kanal wenn du die Besitzerin bist. ",
description=("Muss im betreffenden Lerngruppen-Kanal 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 Besitzerin der Lerngruppe ausgeführt werden. ")
)
@cmd_lg.command(name="close", aliases=["closed", "geschlossen"])
async def cmd_close(self, ctx):
if self.is_group_owner(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 Besitzerin bist. ",
description=("Muss im betreffenden Lerngruppen-Kanal 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 Besitzerin der Lerngruppe ausgeführt werden. ")
@cmd_lg.command(name="private", aliases=["privat"])
async def cmd_private(self, ctx):
if self.is_group_owner(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 Lerngruppen-Kanals, in dem das Komando 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",
category="learninggroups",
brief="Archiviert den Lerngruppen-Kanal",
description="Verschiebt den Lerngruppen-Kanal, in welchem dieses Kommando ausgeführt wird, ins Archiv.",
mod=True
)
@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 owner <@usermention>",
example="!owner @someuser",
brief="Setzt die Besitzerin eines Lerngruppen-Kanals",
description="Muss im betreffenden Lerngruppen-Kanal ausgeführt werden. ",
parameters={
"@usermention": "Die neue Besitzerin der Lerngruppe."
}
)
@cmd_lg.command(name="owner")
async def cmd_owner(self, ctx, new_owner: 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)]
owner_id = group_config.get("owner_id")
if not owner_id:
return
if not new_owner:
user = await self.bot.fetch_user(owner_id)
await ctx.channel.send(f"Besitzerin: @{user.name}#{user.discriminator}")
elif isinstance(group_config, dict):
owner = await self.bot.fetch_user(owner_id)
if self.is_group_owner(ctx.channel, ctx.author) or utils.is_mod(ctx):
group_config["owner_id"] = new_owner.id
await self.remove_member_from_group(ctx.channel, new_owner, False)
if new_owner != owner:
await self.add_member_to_group(ctx.channel, owner, False)
await self.save_groups()
await self.update_permissions(ctx.channel)
await ctx.channel.send(
f"Glückwunsch {new_owner.mention}! Du bist jetzt die Besitzerin 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 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 angehängt werden. `!lg addmember <@usermention> <#channel>`")
return
arg_channel = ctx.channel
if self.is_group_owner(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)
syntax="!lg members",
brief="Listet 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
owner_id = group_config.get("owner_id")
if not owner_id:
return
owner = await self.bot.fetch_user(owner_id)
users = group_config.get("users", {})
if not users and not owner:
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"Besitzerin: **@{owner.name}#{owner.discriminator}**\nMitglieder: " +
(f"{', '.join(names)}" if len(names) > 0 else "Keine"))
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
@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_owner(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 Besitzerin einer Lerngruppe um Aufnahme.",
parameters={
"id": "Die ID zur Lerngruppe."
}
)
@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"Anfrage von <@!{ctx.author.id}>",
await utils.send_dm(ctx.author, f"Deine Anfrage wurde an **#{channel.name}** gesendet. "
"Sobald die Besitzerin der Lerngruppe darüber "
"entschieden hat bekommst du Bescheid.")
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
@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_owner(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["owner_id"] == ctx.author.id:
await ctx.channel.send("Du kannst nicht aus deiner eigenen Lerngruppe flüchten. Übertrage erst den Besitz.")
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_owner(request, member) or self.is_mod(member)):
if self.is_mod(member):
user = await self.bot.fetch_user(request["owner_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):
channel = interaction.channel
member = interaction.author
message = interaction.message
group_config = self.groups["groups"].get(str(channel.id))
if not group_config:
return
if self.is_group_owner(channel, member) or self.is_mod(member):
if confirmed:
if message.mentions and len(message.mentions) == 1:
await self.add_member_to_group(channel, message.mentions[0])
await self.update_permissions(channel)
else:
await channel.send(f"Leider ist ein Fehler aufgetreten.")
else:
if message.mentions and len(message.mentions) == 1:
await utils.send_dm(message.mentions[0], f"Deine Anfrage für die Lerngruppe **#{channel.name}**"
"wurde abgelehnt.")
await message.delete()
async def cog_command_error(self, ctx, error):