async def on_ready(self): """Called when the bot is ready.""" print('Logged in as') print(self.user) print(self.user.id) print(f"Version {self.__version__}") print('------') # Notify reset author if len(sys.argv) > 1: user = self.get_member(int(sys.argv[1])) sys.argv[1] = 0 if user is not None: await user.send("Restart complete") # Populating members's guild list self.members = {} for guild in self.guilds: for member in guild.members: if member.id in self.members: self.members[member.id].append(guild.id) else: self.members[member.id] = [guild.id] log.info('Bot is online and ready')
async def on_guild_join(self, guild: discord.Guild): """Called when the bot joins a guild (server).""" log.info( "Nab Bot added to server: {0.name} (ID: {0.id})".format(guild)) message = f"**I've been added to this server.**\n" \ f"Some things you should know:\n" \ f"‣ My command prefix is: `{config.command_prefix[0]}` (it is customizable)\n" \ f"‣ You can see all my commands with: `{config.command_prefix[0]}help` or " \ f"`{config.command_prefix[0]}commands`\n" \ f"‣ You can configure me using: `{config.command_prefix[0]}settings`\n" \ f"‣ You can set a world for me to track by using `{config.command_prefix[0]}settings world`\n" \ f"‣ If you want a logging channel, create a channel named `{config.log_channel_name}`\n" \ f"‣ If you need help, join my support server: **<https://discord.me/NabBot>**\n" \ f"‣ For more information and links in: `{config.command_prefix[0]}about`" for member in guild.members: if member.id in self.members: self.members[member.id].append(guild.id) else: self.members[member.id] = [guild.id] try: channel = self.get_top_channel(guild) if channel is None: log.warning( f"Could not send join message on server: {guild.name}. No allowed channel found." ) return await channel.send(message) except discord.HTTPException as e: log.error(f"Could not send join message on server: {guild.name}.", exc_info=e)
async def on_guild_join(self, guild: discord.Guild): """Called when the bot joins a guild (server).""" log.info( "Nab Bot added to server: {0.name} (ID: {0.id})".format(guild)) message = "Hello! I'm now in **{0.name}**. To see my available commands, type `{3}help`\n" \ "I will reply to commands from any channel I can see, but if you create a channel called *{1}*, " \ "I will give longer replies and more information there.\n" \ "If you want a server log channel, create a channel called *{2}*, I will post logs in there." \ "You might want to make it private though.\n" \ "To tweak NabBot settings, use `{3}settings` in your server." formatted_message = message.format(guild, config.ask_channel_name, config.log_channel_name, config.command_prefix[0]) for member in guild.members: if member.id in self.members: self.members[member.id].append(guild.id) else: self.members[member.id] = [guild.id] try: await guild.owner.send(formatted_message) except discord.Forbidden: # Owner doesn't allow PMs top_channel = self.get_top_channel(guild, True) if top_channel is not None: formatted_message += "\n*I meant to send this privately, but you do not allow private messages.*" await top_channel.send(formatted_message)
def on_command(command, ctx): """Chamado toda vez que um comando é utilizado. Usado para armazenar logs dos comandos em um arquivo.""" if ctx.message.channel.is_private: destination = 'PM' else: destination = '#{0.channel.name} ({0.server.name})'.format(ctx.message) message_decoded = decode_emoji(ctx.message.content) log.info('Command by {0} in {1}: {2}'.format(ctx.message.author.display_name, destination, message_decoded))
def on_server_join(server: discord.Server): log.info("Monkey Slave added to server: {0.name} (ID: {0.id})".format(server)) message = "Opa! Estou agora em **{0.name}**. Para ver meus comandos disponíveis, digite \help\n" \ "Eu responderei à comandos de qualquer canal que eu puder ver, mas se você criar um canal chamado *{1}*, eu poderei " \ "dar respostas mais longas e mais informações lá.\n" \ "Se você quiser um canal com os logs, crie um canal chamado *{2}* e eu colocarei os logs lá. Acredito " \ "que você irá querer tornar este canal privado, é claro." formatted_message = message.format(server, ask_channel_name, log_channel_name) yield from bot.send_message(server.owner, formatted_message)
async def on_member_join(self, member: discord.Member): """ Called when a member joins a guild (server) the bot is in.""" log.info("{0.display_name} (ID: {0.id}) joined {0.guild.name}".format( member)) # Updating member list if member.id in self.members: self.members[member.id].append(member.guild.id) else: self.members[member.id] = [member.guild.id] # No welcome message for lite servers and servers not tracking worlds if member.guild.id in config.lite_servers or tracked_worlds.get( member.guild.id) is None: return server_welcome = get_server_property("welcome", member.guild.id, "") pm = (config.welcome_pm + "\n" + server_welcome).format( user=member, server=member.guild, bot=self.user, owner=member.guild.owner) embed = discord.Embed(description="{0.mention} joined.".format(member)) icon_url = get_user_avatar(member) embed.colour = discord.Colour.green() embed.set_author(name="{0.name}#{0.discriminator}".format(member), icon_url=icon_url) embed.timestamp = dt.datetime.utcnow() # Check if user already has characters registered and announce them on log_channel # This could be because he rejoined the server or is in another server tracking the same worlds world = tracked_worlds.get(member.guild.id) if world is not None: c = userDatabase.cursor() try: c.execute( "SELECT name, vocation, ABS(level) as level, guild " "FROM chars WHERE user_id = ? and world = ?", ( member.id, world, )) results = c.fetchall() if len(results) > 0: pm += "\nYou already have these characters in {0} registered to you: {1}"\ .format(world, join_list([r["name"] for r in results], ", ", " and ")) characters = [ "\u2023 {name} - Level {level} {voc} - **{guild}**". format(**c, voc=get_voc_abb_and_emoji(c["vocation"])) for c in results ] embed.add_field(name="Registered characters", value="\n".join(characters)) finally: c.close() await self.send_log_message(member.guild, embed=embed) await member.send(pm)
async def on_command(self, ctx: Context): """Called when a command is used. Used to log commands on a file.""" if isinstance(ctx.message.channel, abc.PrivateChannel): destination = 'PM' else: destination = '#{0.channel.name} ({0.guild.name})'.format( ctx.message) message_decoded = decode_emoji(ctx.message.content) log.info('Command by {0} in {1}: {2}'.format( ctx.message.author.display_name, destination, message_decoded))
def on_message_delete(message: discord.Message): """Chamado sempre que uma mensagem for deletada.""" if message.channel.name == ask_channel_name: return message_decoded = decode_emoji(message.clean_content) attachment = "" if message.attachments: attachment = "\n\tAttached file: "+message.attachments[0]['filename'] log.info("A message by @{0} was deleted in #{2} ({3}):\n\t'{1}'{4}".format(message.author.display_name, message_decoded, message.channel.name, message.server.name, attachment))
async def on_message_delete(self, message: discord.Message): """Called every time a message is deleted.""" if message.channel.name == config.ask_channel_name: return message_decoded = decode_emoji(message.clean_content) attachment = "" if message.attachments: attachment = "\n\tAttached file: " + message.attachments[0][ 'filename'] log.info( "A message by @{0} was deleted in #{2} ({3}):\n\t'{1}'{4}".format( message.author.display_name, message_decoded, message.channel.name, message.guild.name, attachment))
def on_message_edit(before: discord.Message, after: discord.Message): """Chamado sempre que uma mensagem é editada.""" if before.channel.is_private: return if before.author.id == bot.user.id: return if before.content == after.content: return before_decoded = decode_emoji(before.clean_content) after_decoded = decode_emoji(after.clean_content) log.info("@{0} edited a message in #{3} ({4}):\n\t'{1}'\n\t'{2}'".format(before.author.name, before_decoded, after_decoded, before.channel, before.server))
async def on_guild_join(self, guild: discord.Guild): """Called when the bot joins a guild (server).""" log.info( "Nab Bot added to server: {0.name} (ID: {0.id})".format(guild)) message = "Hello! I'm now in **{0.name}**. To see my available commands, type \help\n" \ "I will reply to commands from any channel I can see, but if you create a channel called *{1}*," \ "I will give longer replies and more information there.\n" \ "If you want a server log channel, create a channel called *{2}*, I will post logs in there." \ "You might want to make it private though.\n" \ "To have all of Nab Bot's features, use `/setworld <tibia_world>`" formatted_message = message.format(guild, config.ask_channel_name, config.log_channel_name) await guild.owner.send(formatted_message) for member in guild.members: if member.id in self.members: self.members[member.id].append(guild.id) else: self.members[member.id] = [guild.id]
async def on_message_edit(self, before: discord.Message, after: discord.Message): """Called every time a message is edited.""" # Ignore bot messages if before.author.id == self.user.id: return # Ignore private messages if isinstance(before.channel, abc.PrivateChannel): return # Ignore if content didn't change (usually fired when attaching files) if before.content == after.content: return before_decoded = decode_emoji(before.clean_content) after_decoded = decode_emoji(after.clean_content) log.info( "@{0} edited a message in #{3} ({4}):\n\t'{1}'\n\t'{2}'".format( before.author.name, before_decoded, after_decoded, before.channel, before.guild))
def on_member_join(member: discord.Member): """Chamado toda vez que um membro entra em um servidor visível ao bot.""" log.info("{0.display_name} (ID: {0.id}) joined {0.server.name}".format(member)) if lite_mode: return server_id = member.server.id server_welcome = welcome_messages.get(server_id, "") pm = (welcome_pm+"\n"+server_welcome).format(member, bot) log_message = "{0.mention} joined.".format(member) # Atualiza o status de ghost do membro update_ghost(member) # Coloca o membro na role "visitantes" roleName = 'visitante' for role in get_role_list(member.server): if role.name.lower() == roleName: yield from bot.add_roles(member, role) log.info("{0.display_name} (ID: {0.id}) added to role {1.name}".format(member, role)) yield from send_log_message(bot, member.server, log_message) yield from bot.send_message(member, pm) yield from bot.send_message(member.server, "Ei, {0.mention}, bem vindo. E nada de cobranças aqui!".format(member))
async def on_member_remove(self, member: discord.Member): """Called when a member leaves or is kicked from a guild.""" now = dt.datetime.utcnow() self.members[member.id].remove(member.guild.id) bot_member = member.guild.me # type: discord.Member embed = discord.Embed(description="Left the server or was kicked") icon_url = get_user_avatar(member) embed.set_author(name="{0.name}#{0.discriminator}".format(member), icon_url=icon_url) embed.timestamp = now embed.colour = discord.Colour(0xffff00) # If bot can see audit log, he can see if it was a kick or member left on it's own if bot_member.guild_permissions.view_audit_log: async for entry in member.guild.audit_logs( limit=20, reverse=False, action=discord.AuditLogAction.kick, after=now - dt.timedelta(0, 5)): # type: discord.AuditLogEntry if abs((entry.created_at - now).total_seconds()) >= 5: # After is broken in the API, so we must check if entry is too old. break if entry.target.id == member.id: embed.description = "Kicked" icon_url = get_user_avatar(entry.user) embed.set_footer(text="{0.name}#{0.discriminator}".format( entry.user), icon_url=icon_url) embed.timestamp = entry.created_at embed.colour = discord.Colour(0xff0000) if entry.reason: embed.description += f"\n**Reason:** {entry.reason}" log.info( "{0.display_name} (ID:{0.id}) was kicked from {0.guild.name} by {1.display_name}" .format(member, entry.user)) await self.send_log_message(member.guild, embed=embed) return embed.description = "Left the server" log.info("{0.display_name} (ID:{0.id}) left {0.guild.name}".format( member)) await self.send_log_message(member.guild, embed=embed) return # Otherwise, we are not certain log.info( "{0.display_name} (ID:{0.id}) left or was kicked from {0.guild.name}" .format(member)) await self.send_log_message(member.guild, embed=embed)
def on_member_remove(member: discord.Member): """Chamado sempre que um membro deixa o servidor ou é kickado do mesmo.""" log.info("{0.display_name} (ID:{0.id}) left or was kicked from {0.server.name}".format(member)) yield from send_log_message(bot, member.server, "**{0.name}#{0.discriminator}** left or was kicked.".format(member))
async def on_member_join(self, member: discord.Member): """ Called when a member joins a guild (server) the bot is in.""" log.info("{0.display_name} (ID: {0.id}) joined {0.guild.name}".format( member)) # Updating member list if member.id in self.members: self.members[member.id].append(member.guild.id) else: self.members[member.id] = [member.guild.id] embed = discord.Embed(description="{0.mention} joined.".format(member), color=discord.Color.green()) embed.set_author( name="{0.name}#{0.discriminator} (ID: {0.id})".format(member), icon_url=get_user_avatar(member)) previously_registered = "" # If server is not tracking worlds, we don't check the database if member.guild.id in config.lite_servers or self.tracked_worlds.get( member.guild.id) is None: await self.send_log_message(member.guild, embed=embed) else: # Check if user already has characters registered and announce them on log_channel # This could be because he rejoined the server or is in another server tracking the same worlds world = self.tracked_worlds.get(member.guild.id) previously_registered = "" if world is not None: c = userDatabase.cursor() try: c.execute( "SELECT name, vocation, ABS(level) as level, guild " "FROM chars WHERE user_id = ? and world = ?", ( member.id, world, )) results = c.fetchall() if len(results) > 0: previously_registered = "\n\nYou already have these characters in {0} registered to you: *{1}*"\ .format(world, join_list([r["name"] for r in results], ", ", " and ")) characters = [ "\u2023 {name} - Level {level} {voc} - **{guild}**" .format(**c, voc=get_voc_abb_and_emoji(c["vocation"])) for c in results ] embed.add_field(name="Registered characters", value="\n".join(characters)) finally: c.close() self.dispatch("character_change", member.id) await self.send_log_message(member.guild, embed=embed) welcome_message = get_server_property(member.guild.id, "welcome") welcome_channel_id = get_server_property(member.guild.id, "welcome_channel", is_int=True) if welcome_message is None: return message = welcome_message.format(user=member, server=member.guild, bot=self, owner=member.guild.owner) message += previously_registered channel = member.guild.get_channel(welcome_channel_id) # If channel is not found, send via pm as fallback if channel is None: channel = member try: await channel.send(message) except discord.Forbidden: try: # If bot has no permissions to send the message on that channel, send on private message # If the channel was already a private message, don't try it again if welcome_channel_id is None: return await member.send(message) except discord.Forbidden: pass
async def on_guild_remove(self, guild: discord.Guild): """Called when the bot leaves a guild (server).""" log.info("Nab Bot left server: {0.name} (ID: {0.id})".format(guild)) for member in guild.members: if member.id in self.members: self.members[member.id].remove(guild.id)
async def add_account(self, ctx, *, params): """Register a character and all other visible characters to a discord user. If a character is hidden, only that character will be added. Characters in other worlds are skipped. The syntax is the following: /addacc user,char""" params = params.split(",") if len(params) != 2: await ctx.send( "The correct syntax is: ``/addacc username,character``") return target_name, char_name = params # This is equivalent to someone using /stalk addacc on themselves. user = ctx.author world = tracked_worlds.get(ctx.guild.id) if world is None: await ctx.send("This server is not tracking any tibia worlds.") return target = self.bot.get_member(target_name, ctx.guild) if target is None: await ctx.send(f"I couldn't find any users named @{target_name}") return target_guilds = self.bot.get_user_guilds(target.id) target_guilds = list(filter(lambda x: x == world, target_guilds)) await ctx.trigger_typing() try: char = await get_character(char_name) if char is None: await ctx.send("That character doesn't exists.") return except NetworkError: await ctx.send("I couldn't fetch the character, please try again.") return chars = char.other_characters # If the char is hidden,we still add the searched character, if we have just one, we replace it with the # searched char, so we don't have to look him up again if len(chars) == 0 or len(chars) == 1: chars = [char] skipped = [] updated = [] added = [] # type: List[Character] existent = [] for char in chars: # Skip chars in non-tracked worlds if char.world != world: skipped.append(char) continue with closing(userDatabase.cursor()) as c: c.execute( "SELECT name, guild, user_id as owner FROM chars WHERE name LIKE ?", (char.name, )) db_char = c.fetchone() if db_char is not None: owner = self.bot.get_member(db_char["owner"]) # Previous owner doesn't exist anymore if owner is None: updated.append({ 'name': char.name, 'world': char.world, 'prevowner': db_char["owner"] }) continue # Char already registered to this user elif owner.id == user.id: existent.append("{0.name} ({0.world})".format(char)) continue # Character is registered to another user, we stop the whole process else: reply = "A character in that account ({0}) is already registered to **{1.display_name}**" await ctx.send(reply.format(db_char["name"], owner)) return # If we only have one char, it already contains full data if len(chars) > 1: try: await ctx.message.channel.trigger_typing() char = await get_character(char.name) except NetworkError: await ctx.send( "I'm having network troubles, please try again.") return if char.deleted is not None: skipped.append(char) continue added.append(char) if len(skipped) == len(chars): await ctx.send( f"Sorry, I couldn't find any characters in **{world}**.") return reply = "" log_reply = dict().fromkeys([server.id for server in target_guilds], "") if len(existent) > 0: reply += "\nThe following characters were already registered to @{1}: {0}" \ .format(join_list(existent, ", ", " and "), target.display_name) if len(added) > 0: reply += "\nThe following characters were added to @{1.display_name}: {0}" \ .format(join_list(["{0.name} ({0.world})".format(c) for c in added], ", ", " and "), target) for char in added: log.info( "{2.display_name} registered character {0} was assigned to {1.display_name} (ID: {1.id})" .format(char.name, target, user)) # Announce on server log of each server for guild in target_guilds: _guild = "No guild" if char.guild is None else char.guild_name log_reply[ guild. id] += "\n\t{1.name} - {1.level} {1.vocation} - **{0}**".format( _guild, char) if len(updated) > 0: reply += "\nThe following characters were reassigned to @{1.display_name}: {0}" \ .format(join_list(["{name} ({world})".format(**c) for c in updated], ", ", " and "), target) for char in updated: log.info( "{2.display_name} reassigned character {0} to {1.display_name} (ID: {1.id})" .format(char['name'], target, user)) # Announce on server log of each server for guild in target_guilds: log_reply[guild.id] += "\n\t{name} (Reassigned)".format( **char) for char in updated: with userDatabase as conn: conn.execute("UPDATE chars SET user_id = ? WHERE name LIKE ?", (user.id, char['name'])) for char in added: with userDatabase as conn: conn.execute( "INSERT INTO chars (name,level,vocation,user_id, world, guild) VALUES (?,?,?,?,?,?)", (char.name, char.level * -1, char.vocation, user.id, char.world, char.guild_name)) with userDatabase as conn: conn.execute( "INSERT OR IGNORE INTO users (id, name) VALUES (?, ?)", ( user.id, user.display_name, )) conn.execute("UPDATE users SET name = ? WHERE id = ?", ( user.display_name, user.id, )) await ctx.send(reply) for server_id, message in log_reply.items(): if message: message = f"{user.mention} registered the following characters to {target.mention}" + message await self.bot.send_log_message(self.bot.get_guild(server_id), message)
async def add_account(self, ctx: NabCtx, *, params): """Register a character and all other visible characters to a discord user. If a character is hidden, only that character will be added. Characters in other worlds are skipped.""" params = params.split(",") if len(params) != 2: raise commands.BadArgument() target_name, char_name = params user = ctx.author world = ctx.world target = self.bot.get_member(target_name, ctx.guild) if target is None: return await ctx.send( f"{ctx.tick(False)} I couldn't find any users named @{target_name}" ) if target.bot: return await ctx.send( f"{ctx.tick(False)} You can't register characters to discord bots!" ) # Get list of the user's shared servers with the bot target_guilds = self.bot.get_user_guilds(target.id) # Filter only the servers that follow the same as the current context target_guilds = list( filter(lambda x: self.bot.tracked_worlds.get(x.id) == world, target_guilds)) msg = await ctx.send(f"{config.loading_emoji} Fetching characters...") try: char = await get_character(char_name) if char is None: return await msg.edit(content="That character doesn't exist.") except NetworkError: return await msg.edit( content="I couldn't fetch the character, please try again.") chars = char.other_characters # If the char is hidden,we still add the searched character, if we have just one, we replace it with the # searched char, so we don't have to look him up again if len(chars) == 0 or len(chars) == 1: chars = [char] skipped = [] updated = [] added: List[Character] = [] existent = [] for char in chars: # Skip chars in non-tracked worlds if char.world != world: skipped.append(char) continue with closing(userDatabase.cursor()) as c: c.execute( "SELECT name, guild, user_id as owner, abs(level) as level " "FROM chars " "WHERE name LIKE ?", (char.name, )) db_char = c.fetchone() if db_char is not None: owner = self.bot.get_member(db_char["owner"]) # Previous owner doesn't exist anymore if owner is None: updated.append({ 'name': char.name, 'world': char.world, 'prevowner': db_char["owner"], 'vocation': db_char["vocation"], 'level': db_char['level'], 'guild': db_char['guild'] }) continue # Char already registered to this user elif owner.id == target.id: existent.append("{0.name} ({0.world})".format(char)) continue # Character is registered to another user, we stop the whole process else: reply = "A character in that account ({0}) is already registered to **{1.display_name}**" await ctx.send(reply.format(db_char["name"], owner)) return # If we only have one char, it already contains full data if len(chars) > 1: try: await ctx.channel.trigger_typing() char = await get_character(char.name) except NetworkError: await ctx.send( "I'm having network troubles, please try again.") return if char.deleted is not None: skipped.append(char) continue added.append(char) if len(skipped) == len(chars): await ctx.send( f"Sorry, I couldn't find any characters in **{world}**.") return reply = "" log_reply = dict().fromkeys([server.id for server in target_guilds], "") if len(existent) > 0: reply += "\nThe following characters were already registered to @{1}: {0}" \ .format(join_list(existent, ", ", " and "), target.display_name) if len(added) > 0: reply += "\nThe following characters were added to @{1.display_name}: {0}" \ .format(join_list(["{0.name} ({0.world})".format(c) for c in added], ", ", " and "), target) for char in added: log.info( "{2.display_name} registered character {0} was assigned to {1.display_name} (ID: {1.id})" .format(char.name, target, user)) # Announce on server log of each server for guild in target_guilds: _guild = "No guild" if char.guild is None else char.guild_name voc = get_voc_abb_and_emoji(char.vocation) log_reply[guild.id] += "\n\u2023 {1.name} - Level {1.level} {2} - **{0}**" \ .format(_guild, char, voc) if len(updated) > 0: reply += "\nThe following characters were reassigned to @{1.display_name}: {0}" \ .format(join_list(["{name} ({world})".format(**c) for c in updated], ", ", " and "), target) for char in updated: log.info( "{2.display_name} reassigned character {0} to {1.display_name} (ID: {1.id})" .format(char['name'], target, user)) # Announce on server log of each server for guild in target_guilds: char["voc"] = get_voc_abb_and_emoji(char["vocation"]) if char["guild"] is None: char["guild"] = "No guild" log_reply[guild.id] += "\n\u2023 {name} - Level {level} {voc} - **{guild}** (Reassigned)". \ format(**char) for char in updated: with userDatabase as conn: conn.execute("UPDATE chars SET user_id = ? WHERE name LIKE ?", (target.id, char['name'])) for char in added: with userDatabase as conn: conn.execute( "INSERT INTO chars (name,level,vocation,user_id, world, guild) VALUES (?,?,?,?,?,?)", (char.name, char.level * -1, char.vocation, target.id, char.world, char.guild_name)) with userDatabase as conn: conn.execute( "INSERT OR IGNORE INTO users (id, name) VALUES (?, ?)", ( target.id, target.display_name, )) conn.execute("UPDATE users SET name = ? WHERE id = ?", ( target.display_name, target.id, )) await ctx.send(reply) for server_id, message in log_reply.items(): if message: message = f"{target.mention} registered:" + message embed = discord.Embed(description=message) embed.set_author(name=f"{target.name}#{target.discriminator}", icon_url=get_user_avatar(target)) embed.colour = discord.Colour.dark_teal() icon_url = get_user_avatar(user) embed.set_footer( text="{0.name}#{0.discriminator}".format(user), icon_url=icon_url) await self.bot.send_log_message(self.bot.get_guild(server_id), embed=embed)