299 lines
10 KiB
Python
299 lines
10 KiB
Python
|
|
import asyncio
|
|
import re
|
|
from typing import Callable
|
|
|
|
import discord
|
|
from discord.ext import commands
|
|
|
|
|
|
TOKEN = ""
|
|
AUTHORIZED_USERS = {
|
|
}
|
|
AUTHORIZED_GUILDS = [
|
|
]
|
|
KRUH_ROLE_PATTERN = r"^Kruh [0-9]{2}$"
|
|
MIGRATED_KRUH_ROLE_PATTERN = r"^Loňský Kruh [0-9]{2}$"
|
|
|
|
|
|
intents = discord.Intents.default()
|
|
intents.members = True
|
|
intents.message_content = True
|
|
bot = commands.Bot(command_prefix='!', intents=intents)
|
|
|
|
|
|
def _build_category_mapping(guild: discord.Guild) -> dict[str, discord.Guild]:
|
|
return {category.name: category for category in guild.categories}
|
|
|
|
|
|
@bot.event
|
|
async def on_ready():
|
|
guild = bot.get_guild(0)
|
|
if guild is None:
|
|
print("Guild not found.")
|
|
return
|
|
|
|
|
|
@bot.slash_command(guild_ids=AUTHORIZED_GUILDS)
|
|
async def migrate(ctx: discord.ApplicationContext,
|
|
role: discord.Role,
|
|
):
|
|
user: discord.Member = ctx.user
|
|
if not user.id in AUTHORIZED_USERS:
|
|
await ctx.respond("Unauthorized!")
|
|
return
|
|
|
|
if not re.match(KRUH_ROLE_PATTERN, role.name):
|
|
await ctx.respond("Unsupported role!")
|
|
return
|
|
|
|
category_mapping = _build_category_mapping(ctx.guild)
|
|
if not role.name in category_mapping:
|
|
await ctx.respond("Matching category not found!")
|
|
return
|
|
|
|
original_name = role.name
|
|
new_role = await _migrate_role(role, category_mapping[original_name], lambda name: f"Loňský {name}")
|
|
await ctx.respond(f"Migrated [{original_name}] to [{new_role.name}]")
|
|
|
|
|
|
@bot.slash_command(guild_ids=AUTHORIZED_GUILDS)
|
|
async def migrate_all(ctx: discord.ApplicationContext):
|
|
await ctx.defer()
|
|
|
|
user: discord.Member = ctx.user
|
|
if not user.id in AUTHORIZED_USERS:
|
|
await ctx.respond("Unauthorized!")
|
|
return
|
|
|
|
guild: discord.Guild = ctx.guild
|
|
kruh_roles = [role for role in guild.roles if re.match(KRUH_ROLE_PATTERN, role.name)]
|
|
|
|
guild_categories = {category.name: category for category in guild.categories}
|
|
kruh_categories = {}
|
|
for role in kruh_roles:
|
|
if not role.name in guild_categories:
|
|
await ctx.respond(f"Category for [{role.name}] not found!")
|
|
return
|
|
kruh_categories[role.name] = guild_categories[role.name]
|
|
|
|
for role in kruh_roles:
|
|
original_name = role.name
|
|
await _migrate_role(role, kruh_categories[original_name], lambda name: f"Loňský {name}")
|
|
|
|
await ctx.respond(f"Migrated {len(kruh_roles)} roles")
|
|
|
|
|
|
@bot.slash_command(guild_ids=AUTHORIZED_GUILDS)
|
|
async def undo_migrate(ctx: discord.ApplicationContext,
|
|
role: discord.Role,
|
|
):
|
|
user: discord.Member = ctx.user
|
|
if not user.id in AUTHORIZED_USERS:
|
|
await ctx.respond("Unauthorized!")
|
|
return
|
|
|
|
if not re.match(MIGRATED_KRUH_ROLE_PATTERN, role.name):
|
|
await ctx.respond("Unsupported role!")
|
|
return
|
|
|
|
category_mapping = _build_category_mapping(ctx.guild)
|
|
if not role.name in category_mapping:
|
|
await ctx.respond("Matching category not found!")
|
|
return
|
|
|
|
original_name = role.name
|
|
new_role = await _migrate_role(role, category_mapping[original_name], lambda name: name[7:])
|
|
await ctx.respond(f"Migrated [{original_name}] to [{new_role.name}]")
|
|
|
|
|
|
@bot.slash_command(guild_ids=AUTHORIZED_GUILDS)
|
|
async def undo_migrate_all(ctx: discord.ApplicationContext):
|
|
user: discord.Member = ctx.user
|
|
if not user.id in AUTHORIZED_USERS:
|
|
await ctx.respond("Unauthorized!")
|
|
return
|
|
|
|
guild: discord.Guild = ctx.guild
|
|
kruh_roles = [role for role in guild.roles if re.match(MIGRATED_KRUH_ROLE_PATTERN, role.name)]
|
|
|
|
guild_categories = {category.name: category for category in guild.categories}
|
|
kruh_categories = {}
|
|
for role in kruh_roles:
|
|
if not role.name in guild_categories:
|
|
await ctx.respond(f"Category for [{role.name}] not found!")
|
|
return
|
|
kruh_categories[role.name] = guild_categories[role.name]
|
|
|
|
for role in kruh_roles:
|
|
original_name = role.name
|
|
await _migrate_role(role, kruh_categories[original_name], lambda name: name[7:])
|
|
|
|
await ctx.respond(f"Migrated {len(kruh_roles)} roles")
|
|
|
|
|
|
async def _migrate_role(role: discord.Role, category: discord.CategoryChannel,
|
|
f_migrate: Callable[[str], str]):
|
|
original_name = role.name
|
|
new_name = f_migrate(original_name)
|
|
new_role = await role.edit(name=new_name)
|
|
await category.edit(name=new_name)
|
|
return new_role
|
|
|
|
|
|
@bot.slash_command(guild_ids=AUTHORIZED_GUILDS)
|
|
async def migrate_check(ctx: discord.ApplicationContext):
|
|
user: discord.Member = ctx.user
|
|
if not user.id in AUTHORIZED_USERS:
|
|
await ctx.respond("Unauthorized!")
|
|
return
|
|
|
|
guild: discord.Guild = ctx.guild
|
|
kruh_roles = [role for role in guild.roles if re.match(KRUH_ROLE_PATTERN, role.name)]
|
|
|
|
embed = discord.Embed(
|
|
title="Roles to migrate",
|
|
)
|
|
|
|
kruh_role_names = "".join(sorted(f"- {role.name}\n" for role in kruh_roles))
|
|
embed.add_field(name="Roles", value=kruh_role_names, inline=True)
|
|
|
|
await ctx.respond("The following roles are ready for migration", embed=embed)
|
|
|
|
|
|
@bot.slash_command(guild_ids=AUTHORIZED_GUILDS)
|
|
async def create_kruhy(ctx: discord.ApplicationContext):
|
|
await ctx.defer()
|
|
|
|
user: discord.Member = ctx.user
|
|
if not user.id in AUTHORIZED_USERS:
|
|
await ctx.respond("Unauthorized!")
|
|
return
|
|
|
|
KRUHY_TO_CREATE = {
|
|
"fyzika": [
|
|
11, 12, 13, 14, 15, 16, 17, 18, # FP
|
|
],
|
|
"matematika": [
|
|
19, 20, # MMOP
|
|
51, 52, 53, 54, 55, 56, 57, 58, # MOMP+MITP
|
|
61, 62, 63, 64, # MFMP
|
|
],
|
|
"informatika": [
|
|
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, # IPP
|
|
],
|
|
"ucitelstvi": [
|
|
70, 71, 72, 73, 74, 75, # UC
|
|
]
|
|
}
|
|
OBORY_COLORS = {
|
|
"matematika": discord.Color.from_rgb(240, 139, 170),
|
|
"fyzika": discord.Color.from_rgb(55, 196, 229),
|
|
"informatika": discord.Color.from_rgb(138, 199, 90),
|
|
"ucitelstvi": discord.Color.from_rgb(245, 191, 105),
|
|
}
|
|
|
|
guild: discord.Guild = ctx.guild
|
|
if any(re.match(KRUH_ROLE_PATTERN, role.name) for role in guild.roles):
|
|
await ctx.respond("Kruhy not migrated!")
|
|
return
|
|
|
|
role_everyone = next(iter(role for role in guild.roles if role.name == "@everyone"))
|
|
|
|
for obor in KRUHY_TO_CREATE:
|
|
for kruh_id in KRUHY_TO_CREATE[obor]:
|
|
name = f"Kruh {kruh_id}"
|
|
role = await guild.create_role(name=name, color=OBORY_COLORS[obor], hoist=False, mentionable=True)
|
|
category = await guild.create_category(
|
|
name=name,
|
|
overwrites={
|
|
role_everyone: discord.PermissionOverwrite.from_pair(
|
|
[],
|
|
[("view_channel", True)]),
|
|
role: discord.PermissionOverwrite.from_pair(
|
|
[("view_channel", True), ("send_messages", True), ("connect", True)],
|
|
[]),
|
|
},
|
|
)
|
|
channel = await category.create_text_channel(f"obecné-{kruh_id}")
|
|
|
|
response = "\n".join(["Created categories and roles for Kruhy:"] + [f"- {obor.capitalize()}: {len(kruhy)}" for obor, kruhy in KRUHY_TO_CREATE.items()])
|
|
await ctx.respond(response)
|
|
|
|
|
|
@bot.slash_command(guild_ids=AUTHORIZED_GUILDS)
|
|
async def delete_kruhy(ctx: discord.ApplicationContext):
|
|
await ctx.defer()
|
|
|
|
user: discord.Member = ctx.user
|
|
if not user.id in AUTHORIZED_USERS:
|
|
await ctx.respond("Unauthorized!")
|
|
return
|
|
|
|
guild: discord.Guild = ctx.guild
|
|
kruhy_roles = [role for role in guild.roles if re.match(KRUH_ROLE_PATTERN, role.name)]
|
|
kruhy_categories = [category for category in guild.categories if re.match(KRUH_ROLE_PATTERN, category.name)]
|
|
|
|
nums = (len(kruhy_roles), len(kruhy_categories))
|
|
|
|
delete_tasks = []
|
|
for category in kruhy_categories:
|
|
for channel in category.channels:
|
|
delete_tasks.append(channel.delete())
|
|
delete_tasks.append(category.delete())
|
|
for role in kruhy_roles:
|
|
delete_tasks.append(role.delete())
|
|
|
|
await asyncio.gather(*delete_tasks)
|
|
|
|
await ctx.respond(f"Deleted {nums[0]} categories and {nums[1]} roles")
|
|
|
|
|
|
@bot.slash_command(guild_ids=AUTHORIZED_GUILDS)
|
|
async def rename_migrated(ctx: discord.ApplicationContext):
|
|
await ctx.defer()
|
|
|
|
user: discord.Member = ctx.user
|
|
if not user.id in AUTHORIZED_USERS:
|
|
await ctx.respond("Unauthorized!")
|
|
return
|
|
|
|
guild: discord.Guild = ctx.guild
|
|
migrated_roles = [role for role in guild.roles if re.match(MIGRATED_KRUH_ROLE_PATTERN, role.name)]
|
|
migrated_categories = [category for category in guild.categories if re.match(MIGRATED_KRUH_ROLE_PATTERN, category.name)]
|
|
|
|
nums = (len(migrated_roles), len(migrated_categories))
|
|
|
|
rename_tasks = []
|
|
for category in migrated_categories:
|
|
old_name = category.name
|
|
new_name = old_name[7:] + " (2024/25)"
|
|
rename_tasks.append(category.edit(name=new_name))
|
|
for role in migrated_roles:
|
|
old_name = category.name
|
|
new_name = old_name[7:] + " (2024/25)"
|
|
rename_tasks.append(role.edit(name=new_name))
|
|
|
|
await asyncio.gather(*rename_tasks)
|
|
|
|
await ctx.respond(f"Renamed {nums[0]} categories and {nums[1]} roles")
|
|
|
|
|
|
@bot.slash_command(guild_ids=AUTHORIZED_GUILDS)
|
|
async def fetch_kruhy_ids(ctx: discord.ApplicationContext):
|
|
user: discord.Member = ctx.user
|
|
if not user.id in AUTHORIZED_USERS:
|
|
await ctx.respond("Unauthorized!")
|
|
return
|
|
|
|
guild: discord.Guild = ctx.guild
|
|
|
|
kruh_roles = [role for role in guild.roles if re.match(KRUH_ROLE_PATTERN, role.name)]
|
|
for role in sorted(kruh_roles, key=lambda x: x.name):
|
|
print(role.name, role.id)
|
|
|
|
await ctx.respond("Done")
|
|
|
|
|
|
# Run the bot
|
|
bot.run(TOKEN)
|