示例#1
0
    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')
示例#2
0
 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)
示例#3
0
 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)
示例#4
0
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))
示例#5
0
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)
示例#6
0
    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)
示例#7
0
 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))
示例#8
0
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))
示例#9
0
    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))
示例#10
0
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))
示例#11
0
 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]
示例#12
0
    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))
示例#13
0
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))
示例#14
0
    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)
示例#15
0
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))
示例#16
0
    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
示例#17
0
 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)
示例#18
0
    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)
示例#19
0
    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)