Add commentaries, error handling, permission lock, help command and improve response message

This commit is contained in:
Ethanell 2019-08-10 17:42:14 +02:00
parent c3b78e81c5
commit ffe63c87b9

224
bot.py
View file

@ -1,21 +1,25 @@
import shelve import shelve
from discord import NotFound, InvalidArgument from discord import NotFound, InvalidArgument, Embed
from discord.ext import commands from discord.ext import commands
bot = commands.Bot(command_prefix="%") bot = commands.Bot(command_prefix="%") # Set bot object and command prefix
bot.remove_command("help") # Override default help command
def set_config(guild, conf): def set_config(guild, conf):
"""guild : guild object, conf: shelve object
Set configuration for server on configuration file"""
print(f"Set config for {guild.name}") print(f"Set config for {guild.name}")
conf[str(guild.id)] = dict() conf[str(guild.id)] = dict() # Create default dictionary
for opt in [["default_role_id", ""], ["reaction_messages", dict()]]: for opt in [["default_role_id", ""], ["reaction_messages", dict()]]: # Set up each option in configuration file
conf[str(guild.id)][opt[0]] = opt[1] conf[str(guild.id)][opt[0]] = opt[1]
@bot.event @bot.event
async def on_ready(): async def on_ready():
"""When bot start check if guilds doesn't need configuration"""
with shelve.open("config.conf", writeback=True) as conf: with shelve.open("config.conf", writeback=True) as conf:
for g in bot.guilds: for g in bot.guilds: # Check each guild
if str(g.id) not in conf: if str(g.id) not in conf:
set_config(g, conf) set_config(g, conf)
@ -24,136 +28,234 @@ async def on_ready():
@bot.event @bot.event
async def on_guild_join(guild): async def on_guild_join(guild):
"""guild: guild object
If the bot get a new guild, setup the configuration"""
with shelve.open("config.conf", writeback=True) as conf: with shelve.open("config.conf", writeback=True) as conf:
set_config(guild, conf) set_config(guild, conf)
@bot.event @bot.event
async def on_member_join(member): async def on_member_join(member):
"""member: member object
If available, add a default role to new members of guild"""
with shelve.open("config.conf") as conf: with shelve.open("config.conf") as conf:
if "default_role_id" in conf[str(member.guild.id)] and conf[str(member.guild.id)]["default_role_id"]: if "default_role_id" in conf[str(member.guild.id)] and conf[str(member.guild.id)]["default_role_id"]:
role = member.guild.get_role(conf[str(member.guild.id)]["default_role_id"]) role = member.guild.get_role(conf[str(member.guild.id)]["default_role_id"]) # Get the role from guild
await member.add_roles(role) await member.add_roles(role)
@bot.event @bot.event
async def on_raw_reaction_add(payload): async def on_raw_reaction_add(payload):
guild = bot.get_guild(payload.guild_id) """payload: RawReaction payload object
member = guild.get_member(payload.user_id) When a reaction is add check if is a role reaction and apply the request"""
guild = bot.get_guild(payload.guild_id) # Get the current guild
member = guild.get_member(payload.user_id) # Get the current member
await reaction_role(payload.message_id, guild, str(payload.emoji), member, True) await reaction_role(payload.message_id, guild, str(payload.emoji), member, True)
@bot.event @bot.event
async def on_raw_reaction_remove(payload): async def on_raw_reaction_remove(payload):
guild = bot.get_guild(payload.guild_id) """payload: RawReaction payload object
member = guild.get_member(payload.user_id) When a reaction is remove check if is a role reaction and apply the request"""
guild = bot.get_guild(payload.guild_id) # Get the current guild
member = guild.get_member(payload.user_id) # Get the current member
await reaction_role(payload.message_id, guild, str(payload.emoji), member, False) await reaction_role(payload.message_id, guild, str(payload.emoji), member, False)
async def reaction_role(message_id, guild, emoji, member, state): async def reaction_role(message_id, guild, emoji, member, state):
"""message_id: (int) the id of the message, guild: guild object, emoji: the reaction emoji, member: member object,
state: (bool) add or remove target role
Check the configuration and if available, set the link role to the player"""
with shelve.open("config.conf") as conf: with shelve.open("config.conf") as conf:
# Avoid bot user and check if message and reaction are in configuration
if not member.bot and \ if not member.bot and \
"reaction_messages" in conf[str(guild.id)] and \ "reaction_messages" in conf[str(guild.id)] and \
message_id in conf[str(guild.id)]["reaction_messages"] and \ message_id in conf[str(guild.id)]["reaction_messages"] and \
emoji in conf[str(guild.id)]["reaction_messages"][message_id]: emoji in conf[str(guild.id)]["reaction_messages"][message_id]:
role = guild.get_role(conf[str(guild.id)]["reaction_messages"][message_id][emoji]) role = guild.get_role(conf[str(guild.id)]["reaction_messages"][message_id][emoji]) # Get the target role
# State-dependent action
if state: if state:
await member.add_roles(role) await member.add_roles(role)
else: else:
await member.remove_roles(role) await member.remove_roles(role)
@bot.command() @bot.command(name="help")
async def shutdown(ctx): @commands.has_permissions(administrator=True)
if await bot.is_owner(ctx.author): async def help_cmd(ctx):
await ctx.send("Shutdown !") embed = Embed(title="Help", description="", color=0xffff00)
await bot.logout() embed.add_field(name="Set default role", value="``set_default_role @role`` to set new member default role, if any "
await bot.close() "role mentioned this disable the option")
else: embed.add_field(name="Default role", value="``default_role`` show the new member default role")
await ctx.send("Not allowed !") embed.add_field(name="Reaction message", value="""``reaction_message <action> <message id> <...>``
**__actions :__**
``set <message id>`` to set a reaction message
``unset <message id>`` to unset a reaction message
``add <message id> <reaction emoji> @role`` to add a reaction on set message
``remove <message id> <reaction emoji>`` to remove a reaction on set message""")
await ctx.send(embed=embed)
@help_cmd.error
async def help_cmd_error(ctx, error):
"""ctx: context object, error: raised error
Manage help_cmd command errors"""
if isinstance(error, commands.errors.MissingPermissions):
await ctx.send("You are missing Administrator permission to run this command ! :no_entry:")
@bot.command() @bot.command()
@commands.guild_only() @commands.guild_only()
@commands.has_permissions(administrator=True)
async def set_default_role(ctx): async def set_default_role(ctx):
"""ctx: context object
Set the default role of a guild"""
with shelve.open("config.conf", writeback=True) as conf: with shelve.open("config.conf", writeback=True) as conf:
if len(ctx.message.role_mentions) == 1: if len(ctx.message.role_mentions) == 1: # Accept only one mentioned role
conf[str(ctx.guild.id)]["default_role_id"] = ctx.message.role_mentions[0].id conf[str(ctx.guild.id)]["default_role_id"] = ctx.message.role_mentions[0].id # Set in configuration
await ctx.send(f"Role ``{ctx.message.role_mentions[0].name}`` set to default role") await ctx.send(f"Role ``{ctx.message.role_mentions[0].name}`` set to default role :white_check_mark:")
elif len(ctx.message.role_mentions) == 0: elif len(ctx.message.role_mentions) == 0: # If any role, disable default role option
conf[str(ctx.guild.id)]["default_role_id"] = "" conf[str(ctx.guild.id)]["default_role_id"] = "" # Set in configuration
await ctx.send("Default role disabled") await ctx.send("Default role disabled :warning:")
else: else: # If invalid arguments
await ctx.send("Too many roles !") raise commands.BadArgument
@set_default_role.error
async def set_default_role_error(ctx, error):
"""ctx: context object, error: raised error
Manage set_default_role command errors"""
if isinstance(error, commands.BadArgument):
await ctx.send("Invalid mentioned role ! :x:")
if isinstance(error, commands.errors.MissingPermissions):
await ctx.send("You are missing Administrator permission to run this command ! :no_entry:")
@bot.command() @bot.command()
@commands.guild_only() @commands.guild_only()
@commands.has_permissions(administrator=True)
async def default_role(ctx): async def default_role(ctx):
"""ctx: context object
Show guild default role"""
with shelve.open("config.conf") as conf: with shelve.open("config.conf") as conf:
await ctx.send(conf[str(ctx.guild.id)]["default_role_id"]) role = ctx.guild.get_role(conf[str(ctx.guild.id)]["default_role_id"]).name # Get role
await ctx.send(f"Guild default role: ``{role}``")
@default_role.error
async def default_role_error(ctx, error):
"""ctx: context object, error: raised error
Manage default_role command errors"""
if isinstance(error, commands.errors.MissingPermissions):
await ctx.send("You are missing Administrator permission to run this command ! :no_entry:")
@bot.command() @bot.command()
@commands.guild_only() @commands.guild_only()
async def reaction_message(ctx, state=None, message_id=None, reaction=None): @commands.has_permissions(administrator=True)
if state not in ["set", "unset", "add", "remove"]: async def reaction_message(ctx, action=None, message_id=None, reaction=None):
await ctx.send("Wrong argument !") """ctx: context object, action: (str) command action, message_id: (str) the message id,
reaction: (str) reaction emoji
Set or unset a reaction message and add reaction emoji link with roles"""
if action not in ["set", "unset", "add", "remove"]: # Check if action is correct
raise commands.BadArgument("argument")
else: else:
try: try: # Check message id
message = await ctx.fetch_message(int(message_id)) message = await ctx.fetch_message(int(message_id))
except (TypeError, ValueError, NotFound): except (TypeError, ValueError, NotFound):
await ctx.send("Invalid message ID !") raise commands.BadArgument("message id")
else: else:
with shelve.open("config.conf", writeback=True) as conf: with shelve.open("config.conf", writeback=True) as conf:
if state == "set": if action == "set": # Set a reaction message
if message.id in conf[str(ctx.guild.id)]["reaction_messages"]: if message.id in conf[str(ctx.guild.id)]["reaction_messages"]: # Check if not already set
await ctx.send("Message already set !") raise commands.BadArgument("already set")
else: else:
conf[str(ctx.guild.id)]["reaction_messages"][message.id] = dict() conf[str(ctx.guild.id)]["reaction_messages"][message.id] = dict() # Add to configuration
await ctx.send("Message set") await ctx.send("Message set :white_check_mark:")
elif state == "unset": elif action == "unset": # Unset a reaction message
try: try: # Check if the message is set when remove
del conf[str(ctx.guild.id)]["reaction_messages"][message.id] del conf[str(ctx.guild.id)]["reaction_messages"][message.id]
except KeyError: except KeyError:
await ctx.send("Message not set !") raise commands.BadArgument("not set")
else: else:
await ctx.send("Message remove") await ctx.send("Message remove :wastebasket:")
else: # Add and Remove reaction actions
if message.id not in conf[str(ctx.guild.id)]["reaction_messages"]: # Check if message is set
raise commands.BadArgument("not set")
else: else:
if message.id not in conf[str(ctx.guild.id)]["reaction_messages"]: if action == "add": # Add reaction
await ctx.send("Message not set !") if len(ctx.message.role_mentions) != 1: # Check if correct mentioned role
raise commands.BadArgument("role")
else: else:
if state == "add": try: # Add reaction to message and check if given emoji is correct
if len(ctx.message.role_mentions) != 1:
await ctx.send("Invalid role !")
else:
try:
await message.add_reaction(reaction) await message.add_reaction(reaction)
except InvalidArgument: except InvalidArgument:
await ctx.send("Invalid reaction !") raise commands.BadArgument("reaction")
else: else: # Add reaction and role to configuration
conf[str(ctx.guild.id)]["reaction_messages"][message.id][reaction] =\ conf[str(ctx.guild.id)]["reaction_messages"][message.id][reaction] =\
ctx.message.role_mentions[0].id ctx.message.role_mentions[0].id
await ctx.send("Reaction add") await ctx.send("Reaction add :white_check_mark:")
if state == "remove": if action == "remove": # Remove a reaction
try: try: # Remove reaction from message and check if given emoji is correct
await message.remove_reaction(reaction, bot.user) await message.remove_reaction(reaction, bot.user)
except (InvalidArgument, NotFound): except (InvalidArgument, NotFound):
await ctx.send("Invalid reaction !") raise commands.BadArgument("reaction")
else: else: # Delete reaction from configuration
del conf[str(ctx.guild.id)]["reaction_messages"][message.id][reaction] del conf[str(ctx.guild.id)]["reaction_messages"][message.id][reaction]
await ctx.send("Reaction remove") await ctx.send("Reaction remove :wastebasket:")
@reaction_message.error
async def reaction_message_error(ctx, error):
"""ctx: context object, error: raised error
Manage reaction_message command errors"""
err = {"argument": "Invalid arguments !", "message id": "Invalid message id", "already set": "Message already set",
"not set": "Message not set", "role": "Invalid mentioned role", "reaction": "Invalid reaction"} # Database
if str(error) in err:
await ctx.send(f"{err[str(error)]} :x:")
if isinstance(error, commands.errors.MissingPermissions):
await ctx.send("You are missing Administrator permission to run this command ! :no_entry:")
@bot.command() @bot.command()
@commands.is_owner()
async def shutdown(ctx):
"""ctx: context object
Owner shutdown command"""
if await bot.is_owner(ctx.author):
await ctx.send("Shutdown ! :wave:")
await bot.logout()
await bot.close()
@shutdown.error
async def shutdown_error(ctx, error):
"""ctx: context object, error: raised error
Manage shutdown command errors"""
if isinstance(error, commands.errors.NotOwner):
await ctx.send("You do not own this bot ! :no_entry:")
@bot.command()
@commands.is_owner()
async def debug(ctx, config=None): async def debug(ctx, config=None):
"""ctx: context object, config: (str) configuration entry to check"""
if await bot.is_owner(ctx.author):
with shelve.open("config.conf") as conf: with shelve.open("config.conf") as conf:
data = conf[str(ctx.guild.id)] data = conf[str(ctx.guild.id)] # Add all guild configuration
if config: if config: # If specified send only target configuration
data = data[config] data = data[config]
print(data)
await ctx.send(f"``{data}``") await ctx.send(f"``{data}``")
bot.run(open("token.ini").read())
@debug.error
async def debug_error(ctx, error):
"""ctx: context object, error: raised error
Manage debug command errors"""
if isinstance(error, commands.errors.NotOwner):
await ctx.send("You do not own this bot ! :no_entry:")
bot.run(open("token.ini").read()) # Get token from token.ini and start the bot