discord-bot/main.py

164 lines
5 KiB
Python
Raw Normal View History

2024-09-24 16:44:14 +02:00
from configparser import ConfigParser
import logging
2024-08-19 20:27:46 +02:00
import discord
from discord.ext import commands
TOKEN = 'MTE1NDc0ODQ3MzE4MzMwNTc4OQ.Gz_Q7u.02sb2YNV_QQy7Bs19roXlB62mjoMKA6y8aubHU' # Bot token (replace with your own)
DEFAULT_CONFIG_FILE = 'config.ini' # Configuration file with server IDs and study group roles
2024-09-24 16:44:14 +02:00
DEFAULT_LOG_FILE = 'kruhobot.log' # Log file for the bot
2024-09-28 00:24:59 +02:00
HELP_MESSAGE = """Usage: `!grant_role kruh-## UKCO`
- Replace `##` with your study group number
- Replace `UKCO` with your UKCO (8-digit personal number)."""
# Create an instance of the bot
intents = discord.Intents.default()
intents.members = True
2024-09-28 00:02:04 +02:00
intents.message_content = True
bot = commands.Bot(command_prefix='!', intents=intents)
2024-09-24 15:13:52 +02:00
2024-09-24 16:44:14 +02:00
logging.basicConfig(filename=DEFAULT_LOG_FILE, level=logging.INFO)
logger = logging.getLogger("Kruhobot")
2024-09-28 00:24:59 +02:00
config = ConfigParser()
config.read(DEFAULT_CONFIG_FILE)
2024-08-19 20:27:46 +02:00
2024-09-28 00:24:59 +02:00
async def reply_error(ctx, message: str):
"""
2024-09-28 00:24:59 +02:00
Reply to the user with an error message.
:param ctx: The context of the command.
:param message: The error message.
"""
2024-09-28 00:24:59 +02:00
await reply(ctx, f'**Error:** {message}')
2024-08-19 20:27:46 +02:00
2024-09-28 00:24:59 +02:00
async def reply(ctx, message: str):
"""
Reply to the user.
:param ctx: The context of the command or the channel from the event handler.
:param message: The message to reply with.
"""
2024-09-28 00:32:55 +02:00
await ctx.send(message)
2024-09-28 00:24:59 +02:00
else:
logger.error(f'Failed to send the following message: "{message}"')
2024-08-19 20:27:46 +02:00
@bot.event
async def on_ready():
2024-09-24 16:44:14 +02:00
logger.info(f'Logged in as {bot.user.name}')
2024-08-19 20:27:46 +02:00
@bot.command()
async def grant_role(ctx, study_group_name: str):
author_id = ctx.author.id
for guild_id in server_ids:
2024-08-19 20:27:46 +02:00
guild = bot.get_guild(guild_id)
2024-08-19 20:27:46 +02:00
if guild is None:
continue
2024-08-19 20:27:46 +02:00
author = guild.get_member(author_id)
2024-08-19 20:27:46 +02:00
if author is None:
continue
if study_group_name in study_groups[guild_id]:
role_name = study_groups[guild_id][study_group_name]
2024-08-19 20:27:46 +02:00
role = discord.utils.get(guild.roles, id=role_name)
if role is not None:
await author.add_roles(role)
2024-09-24 15:13:52 +02:00
await reply(ctx, f'{author.mention} has been granted the {study_group_name} role on the [{guild.name}] server.')
2024-09-24 16:44:14 +02:00
logger.info(f'Granted role [{role_name}] to [{author_id}] in [{guild_id}] corresponding to the study group [{study_group_name}].')
2024-08-19 20:27:46 +02:00
return
2024-09-24 15:13:52 +02:00
await reply_error(ctx, 'Something went wrong. Please check your command and try again.')
2024-09-24 16:44:14 +02:00
logger.error(f'Failed to grant a role to [{author_id}] in [{guild_id}] corresponding to the study group [{study_group_name}].')
2024-08-19 20:27:46 +02:00
return
2024-09-28 00:24:59 +02:00
def check_command(message):
# Split the message content into words
words = message.content.split()
if len(words) == 0:
return False
# Check if the message is a command
if not words[0].startswith('!'):
reply(message.channel, HELP_MESSAGE)
return False
# Check if the command is recognized
if not words[0] == '!grant_role':
reply_error(message.channel, 'Unrecognized command.')
reply(message.channel, HELP_MESSAGE)
return False
# Check if the number of arguments is correct
if not len(words) == 3:
reply_error(message.channel, 'Invalid number of arguments.')
reply(message.channel, HELP_MESSAGE)
return False
# Check study group format
if not words[1].startswith('kruh-') or not words[1][5:].isdigit():
reply_error(message.channel, 'Invalid study group format.')
reply(message.channel, HELP_MESSAGE)
return False
if not words[2].isdigit() or not len(words[2]) == 8:
reply_error(message.channel, 'Invalid UKCO format.')
reply(message.channel, HELP_MESSAGE)
return False
return True
2024-08-19 20:27:46 +02:00
@bot.event
async def on_message(message):
# Check if the message is a private message and not from the bot itself
2024-09-28 00:24:59 +02:00
if not isinstance(message.channel, discord.DMChannel) or message.author == bot.user:
return
2024-08-19 20:27:46 +02:00
2024-09-28 00:24:59 +02:00
is_valid_command = check_command(message)
if not is_valid_command:
return
2024-08-19 20:27:46 +02:00
2024-09-28 00:24:59 +02:00
await bot.process_commands(message)
def init(config):
"""
Initialize the server IDs and study groups from the configuration file.
:param config: The configuration file.
:return: A tuple with server IDs and study groups.
"""
def get_section_server_id(section):
return int(config[section]["server_ID"])
server_ids = {get_section_server_id(section) for section in config.sections() if "server_ID" in config[section]}
study_groups = {server_id: dict() for server_id in server_ids}
for section in config.sections():
server_id = get_section_server_id(section)
if server_id is None:
continue
# Study group names are in style "kruh-XX", where XX is a two-digit number
# Iterate over all from top to bottom
for (key, value) in config.items(section):
if key.startswith('kruh-'):
study_groups[server_id][key] = int(value)
return server_ids, study_groups
server_ids, study_groups = init(config)
2024-08-19 20:27:46 +02:00
# Run the bot
bot.run(TOKEN)