Exemplo n.º 1
0
def check_ghosts():           
    yield from bot.wait_until_ready()
    while not bot.is_closed:
        for server in bot.servers:
            for member in server.members:
                c = userDatabase.cursor()
                now = datetime.utcnow()
                try:
                    c.execute("SELECT * FROM user_servers WHERE id = ? AND server = ?", (member.id, member.server.id,))
                    result = c.fetchone()
                    if result is not None:
                        last_message = datetime.strptime(result["last_message"], "%Y-%m-%d %H:%M:%S.%f")                       
                        name = result["name"]                                                
                        diff = (now - last_message).total_seconds() / 60.0
                        if (diff/(24*60) > 7.0):
                            #yield from bot.send_message(member.server, "Simulando remoção de usuários: Usuário {0} será removido pois ficou {1} dia(s) sem escrever nada.".format(name, int(diff/(24*60))))                            
                            #yield from bot.send_message(member, "Você foi kickado do servidor {0} por ficar {1} dia(s) sem escrever nada. Saí daqui seu ghost!".format(member.server.name, int(diff/(24*60))))
                            for owners in owner_ids:
                                yield from bot.send_message(member.server.get_member(owners), "O usuário {0} foi kickado (simulação apenas) do servidor {1} por ficar {2} dia(s) sem escrever nada.".format(member.name, member.server.name, int(diff/(24*60))))
                            #yield from bot.kick(member)
                    else: # Não há registro deste usuário escrever algo mas ele está no servidor                        
                        c.execute("""INSERT INTO user_servers (id, server, last_message, name) values (?, ?, ?, ?);""", (member.id, member.server.id, now, member.name))
                finally:        
                    c.close()    
                    userDatabase.commit()   
        
        yield from asyncio.sleep(60*60)  # Verifica a cada uma hora (60 minutos)
Exemplo n.º 2
0
Arquivo: mod.py Projeto: Tschis/NabBot
    async def unregistered(self, ctx: NabCtx):
        """Shows a list of users with no registered characters."""
        entries = []
        if ctx.world is None:
            await ctx.send("This server is not tracking any worlds.")
            return

        with closing(userDatabase.cursor()) as c:
            c.execute(
                "SELECT user_id FROM chars WHERE world LIKE ? GROUP BY user_id",
                (ctx.world, ))
            result = c.fetchall()
            if len(result) <= 0:
                await ctx.send("There are no unregistered users.")
                return
            users = [i["user_id"] for i in result]
        for member in ctx.guild.members:  # type: discord.Member
            # Skip bots
            if member.bot:
                continue
            if member.id not in users:
                entries.append(
                    f"@**{member.display_name}** \u2014 Joined: **{member.joined_at.date()}**"
                )
        if len(entries) == 0:
            await ctx.send("There are no unregistered users.")
            return

        pages = Pages(ctx, entries=entries, per_page=10)
        pages.embed.title = "Unregistered members"
        try:
            await pages.paginate()
        except CannotPaginate as e:
            await ctx.send(e)
Exemplo n.º 3
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)
Exemplo n.º 4
0
    def check(self, ctx):
        """Check which users are currently not registered."""
        if not ctx.message.channel.is_private:
            return True

        author = ctx.message.author
        if author.id in mod_ids + owner_ids:
            author_servers = get_user_servers(self.bot, author.id)
        else:
            author_servers = get_user_admin_servers(self.bot, author.id)

        embed = discord.Embed(description="Members with unregistered users.")

        yield from self.bot.send_typing(ctx.message.channel)
        c = userDatabase.cursor()
        try:
            for server in author_servers:
                world = tracked_worlds.get(server.id, None)
                if world is None:
                    continue
                c.execute(
                    "SELECT user_id FROM chars WHERE world LIKE ? GROUP BY user_id",
                    (world, ))
                result = c.fetchall()
                if len(result) <= 0:
                    embed.add_field(
                        name=server.name,
                        value="There are no registered characters.",
                        inline=False)
                    continue
                users = [str(i["user_id"]) for i in result]
                empty_members = list()
                for member in server.members:
                    if member.id == self.bot.user.id:
                        continue
                    if member.id not in users:
                        empty_members.append("**@" + member.display_name +
                                             "**")
                if len(empty_members) == 0:
                    embed.add_field(name=server.name,
                                    value="There are no unregistered users.",
                                    inline=False)
                    continue
                field_value = "\n{0}".format("\n".join(empty_members))
                split_value = split_message(field_value, FIELD_VALUE_LIMIT)
                for empty_member in split_value:
                    if empty_member == split_value[0]:
                        name = server.name
                    else:
                        name = "\u200F"
                    embed.add_field(name=name,
                                    value=empty_member,
                                    inline=False)
            yield from self.bot.say(embed=embed)
        finally:
            c.close()
Exemplo n.º 5
0
    async def bot_info(self, ctx: NabCtx):
        """Shows advanced information about the bot."""
        char_count = 0
        deaths_count = 0
        levels_count = 0
        with closing(userDatabase.cursor()) as c:
            c.execute("SELECT COUNT(*) as count FROM chars")
            result = c.fetchone()
            if result is not None:
                char_count = result["count"]
            c.execute("SELECT COUNT(*) as count FROM char_deaths")
            result = c.fetchone()
            if result is not None:
                deaths_count = result["count"]
            c.execute("SELECT COUNT(*) as count FROM char_levelups")
            result = c.fetchone()
            if result is not None:
                levels_count = result["count"]

        used_ram = psutil.Process().memory_full_info().uss / 1024 ** 2
        total_ram = psutil.virtual_memory().total / 1024 ** 2
        percentage_ram = psutil.Process().memory_percent()

        def ram(value):
            if value >= 1024:
                return f"{value/1024:.2f}GB"
            else:
                return f"{value:.2f}MB"

        # Calculate ping
        t1 = time.perf_counter()
        await ctx.trigger_typing()
        t2 = time.perf_counter()
        ping = round((t2 - t1) * 1000)

        embed = discord.Embed()
        embed.set_author(name="NabBot", url="https://github.com/Galarzaa90/NabBot",
                         icon_url="https://github.com/fluidicon.png")
        embed.description = f"🔰 Version: **{self.bot.__version__}**\n" \
                            f"⏱ ️Uptime **{parse_uptime(self.bot.start_time)}**\n" \
                            f"🖥️ OS: **{platform.system()} {platform.release()}**\n" \
                            f"📉 RAM: **{ram(used_ram)}/{ram(total_ram)} ({percentage_ram:.2f}%)**\n"
        try:
            embed.description += f"⚙️ CPU: **{psutil.cpu_count()} @ {psutil.cpu_freq().max} MHz**\n"
        except AttributeError:
            pass
        embed.description += f"🏓 Ping: **{ping} ms**\n" \
                             f"👾 Servers: **{len(self.bot.guilds):,}**\n" \
                             f"💬 Channels: **{len(list(self.bot.get_all_channels())):,}**\n" \
                             f"👨 Users: **{len(self.bot.users):,}** \n" \
                             f"👤 Characters: **{char_count:,}**\n" \
                             f"🌐 Tracked worlds: **{len(self.bot.tracked_worlds_list)}/{len(tibia_worlds)}**\n" \
                             f"{config.levelup_emoji} Level ups: **{levels_count:,}**\n" \
                             f"{config.death_emoji} Deaths: **{deaths_count:,}**"
        await ctx.send(embed=embed)
Exemplo n.º 6
0
def update_ghost(member):
    c = userDatabase.cursor()
    now = datetime.utcnow()
    try:
        c.execute("SELECT * FROM user_servers WHERE id = ? AND server = ?", (member.id, member.server.id,))
        result = c.fetchone()
        if result is not None:
            c.execute("""UPDATE user_servers SET last_message = ?, name = ? WHERE id = ? AND server = ?;""", (now, member.id, member.server.id, member.name))
        else:
            c.execute("""INSERT INTO user_servers (id, server, last_message, name) values (?, ?, ?, ?);""", (member.id, member.server.id, now, member.name))
    finally:        
        c.close()    
        userDatabase.commit()   
Exemplo n.º 7
0
    def remove_char(self, ctx, *, name):
        """Removes a registered character.

        The syntax is:
        /stalk removechar name"""
        if not ctx.message.channel.is_private:
            return True
        # This could be used to remove deleted chars so we don't need to check anything
        # Except if the char exists in the database...
        yield from self.bot.send_typing(ctx.message.channel)
        c = userDatabase.cursor()
        try:
            c.execute(
                "SELECT name, user_id, world, ABS(last_level) as level, vocation "
                "FROM chars WHERE name LIKE ?", (name, ))
            result = c.fetchone()
            if result is None:
                yield from self.bot.say(
                    "There's no character with that name registered.")
                return
            user = get_member(self.bot, result["user_id"])
            username = "******" if user is None else user.display_name
            c.execute("DELETE FROM chars WHERE name LIKE ?", (name, ))
            yield from self.bot.say(
                "**{0}** was removed successfully from **@{1}**.".format(
                    result["name"], username))
            if user is not None:
                for server in get_user_servers(self.bot, user.id):
                    world = tracked_worlds.get(server.id, None)
                    if world != result["world"]:
                        continue
                    log_msg = "{0.mention} removed **{1}** ({2} {3}) from {4.mention}.".\
                        format(ctx.message.author, result["name"], result["level"], result["vocation"], user)
                    yield from send_log_message(self.bot, server, log_msg)

            return
        finally:
            c.close()
            userDatabase.commit()
Exemplo n.º 8
0
 def refresh_names(self, ctx):
     """Checks and updates user names on the database."""
     if not ctx.message.channel.is_private:
         return True
     c = userDatabase.cursor()
     try:
         c.execute("SELECT id FROM users")
         result = c.fetchall()
         if len(result) <= 0:
             yield from self.bot.say("There are no registered users.")
             return
         update_users = list()
         for user in result:
             update_users.append(
                 ("unknown" if get_member(self.bot, user[0]) is None else
                  get_member(self.bot, user[0]).display_name, user["id"]))
         c.executemany("UPDATE users SET name = ? WHERE id LIKE ?",
                       update_users)
         yield from self.bot.say("Usernames updated successfully.")
     finally:
         c.close()
         userDatabase.commit()
Exemplo n.º 9
0
Arquivo: mod.py Projeto: Sinsae/NabBot
    def reload_ignored(self):
        """Refresh the world list from the database

        This is used to avoid reading the database everytime the world list is needed.
        A global variable holding the world list is loaded on startup and refreshed only when worlds are modified"""
        c = userDatabase.cursor()
        ignored_dict_temp = {}  # type: Dict[int, List[int]]
        try:
            c.execute("SELECT server_id, channel_id FROM ignored_channels")
            result = c.fetchall()  # type: Dict
            if len(result) > 0:
                for row in result:
                    if not ignored_dict_temp.get(row["server_id"]):
                        ignored_dict_temp[row["server_id"]] = []

                    ignored_dict_temp[row["server_id"]].append(
                        row["channel_id"])

            self.ignored.clear()
            self.ignored.update(ignored_dict_temp)
        finally:
            c.close()
Exemplo n.º 10
0
Arquivo: mod.py Projeto: Sinsae/NabBot
    async def unregistered(self, ctx):
        """Check which users are currently not registered."""

        world = tracked_worlds.get(ctx.guild.id, None)
        entries = []
        if world is None:
            await ctx.send("This server is not tracking any worlds.")
            return

        with closing(userDatabase.cursor()) as c:
            c.execute(
                "SELECT user_id FROM chars WHERE world LIKE ? GROUP BY user_id",
                (world, ))
            result = c.fetchall()
            if len(result) <= 0:
                await ctx.send("There are no registered characters.")
                return
            users = [i["user_id"] for i in result]
        for member in ctx.guild.members:  # type: discord.Member
            if member.id == ctx.me.id:
                continue
            if member.id not in users:
                entries.append(
                    f"@**{member.display_name}** \u2014 Joined on: **{member.joined_at.date()}**"
                )
        if len(entries) == 0:
            await ctx.send("There are no unregistered users.")
            return

        pages = Paginator(self.bot,
                          message=ctx.message,
                          entries=entries,
                          title="Unregistered members",
                          per_page=10)
        try:
            await pages.paginate()
        except CannotPaginate as e:
            await ctx.send(e)
Exemplo n.º 11
0
    def reload_worlds(self):
        """Refresh the world list from the database

        This is used to avoid reading the database every time the world list is needed.
        A global variable holding the world list is loaded on startup and refreshed only when worlds are modified"""
        c = userDatabase.cursor()
        tibia_servers_dict_temp = {}
        try:
            c.execute(
                "SELECT server_id, value FROM server_properties WHERE name = 'world' ORDER BY value ASC"
            )
            result: Dict = c.fetchall()
            del self.tracked_worlds_list[:]
            if len(result) > 0:
                for row in result:
                    if row["value"] not in self.tracked_worlds_list:
                        self.tracked_worlds_list.append(row["value"])
                    tibia_servers_dict_temp[int(
                        row["server_id"])] = row["value"]

            self.tracked_worlds.clear()
            self.tracked_worlds.update(tibia_servers_dict_temp)
        finally:
            c.close()
Exemplo n.º 12
0
    def stalk_namelock(self, ctx, *, params):
        """Register the name of a new character that was namelocked.

        Characters that get namelocked can't be searched by their old name, so they must be reassigned manually.

        If the character got a name change (from the store), searching the old name redirects to the new name, so
        this are usually reassigned automatically.

        The syntax is:
        /stalk namelock oldname,newname"""
        if not ctx.message.channel.is_private:
            return True
        params = params.split(",")
        if len(params) != 2:
            yield from self.bot.say(
                "The correct syntax is: `/stalk namelock oldname,newname")
            return

        old_name = params[0]
        new_name = params[1]
        yield from self.bot.send_typing(ctx.message.channel)
        c = userDatabase.cursor()
        try:
            c.execute("SELECT * FROM chars WHERE name LIKE ? LIMIT 1",
                      (old_name, ))
            old_char_db = c.fetchone()
            # If character wasn't registered, there's nothing to do.
            if old_char_db is None:
                yield from self.bot.say(
                    "I don't have a character registered with the name: **{0}**"
                    .format(old_name))
                return
            # Search old name to see if there's a result
            old_char = yield from get_character(old_name)
            if old_char == ERROR_NETWORK:
                yield from self.bot.say(
                    "I'm having problem with 'the internet' as you humans say, try again."
                )
                return
            # Check if returns a result
            if type(old_char) is dict:
                if old_name.lower() == old_char["name"].lower():
                    yield from self.bot.say(
                        "The character **{0}** wasn't namelocked.".format(
                            old_char["name"]))
                else:
                    yield from self.bot.say(
                        "The character **{0}** was renamed to **{1}**.".format(
                            old_name, old_char["name"]))
                    # Renaming is actually done in get_character(), no need to do anything.
                return

            # Check if new name exists
            new_char = yield from get_character(new_name)
            if new_char == ERROR_NETWORK:
                yield from self.bot.say(
                    "I'm having problem with 'the internet' as you humans say, try again."
                )
                return
            if new_char == ERROR_DOESNTEXIST:
                yield from self.bot.say(
                    "The character **{0}** doesn't exists.".format(new_name))
                return
            # Check if vocations are similar
            if not (old_char_db["vocation"].lower()
                    in new_char["vocation"].lower()
                    or new_char["vocation"].lower()
                    in old_char_db["vocation"].lower()):
                yield from self.bot.say(
                    "**{0}** was a *{1}* and **{2}** is a *{3}*. I think you're making a mistake."
                    .format(old_char_db["name"], old_char_db["vocation"],
                            new_char["name"], new_char["vocation"]))
                return
            confirm_message = "Are you sure **{0}** ({1} {2}) is **{3}** ({4} {5}) now? `yes/no`"
            yield from self.bot.say(
                confirm_message.format(old_char_db["name"],
                                       abs(old_char_db["last_level"]),
                                       old_char_db["vocation"],
                                       new_char["name"], new_char["level"],
                                       new_char["vocation"]))
            reply = yield from self.bot.wait_for_message(
                author=ctx.message.author,
                channel=ctx.message.channel,
                timeout=50.0)
            if reply is None:
                yield from self.bot.say(
                    "No answer? I guess you changed your mind.")
                return
            elif reply.content.lower() not in ["yes", "y"]:
                yield from self.bot.say("No then? Alright.")
                return

            # Check if new name was already registered
            c.execute("SELECT * FROM chars WHERE name LIKE ?",
                      (new_char["name"], ))
            new_char_db = c.fetchone()

            if new_char_db is None:
                c.execute(
                    "UPDATE chars SET name = ?, vocation = ?, last_level = ? WHERE id = ?",
                    (
                        new_char["name"],
                        new_char["vocation"],
                        new_char["level"],
                        old_char_db["id"],
                    ))
            else:
                # Replace new char with old char id and delete old char, reassign deaths and levelups
                c.execute(
                    "DELETE FROM chars WHERE id = ?",
                    (old_char_db["id"]),
                )
                c.execute("UPDATE chars SET id = ? WHERE id = ?", (
                    old_char_db["id"],
                    new_char_db["id"],
                ))
                c.execute("UPDATE char_deaths SET id = ? WHERE id = ?", (
                    old_char_db["id"],
                    new_char_db["id"],
                ))
                c.execute("UPDATE char_levelups SET id = ? WHERE id = ?", (
                    old_char_db["id"],
                    new_char_db["id"],
                ))

            yield from self.bot.say("Character renamed successfully.")
        finally:
            c.close()
            userDatabase.commit()
Exemplo n.º 13
0
    async def on_character_change(self, user_id: int):
        try:
            with closing(userDatabase.cursor()) as c:
                guilds_raw = c.execute(
                    "SELECT guild FROM chars WHERE user_id = ?",
                    (user_id, )).fetchall()
                rules_raw = c.execute(
                    "SELECT * FROM auto_roles ORDER BY server_id").fetchall()
            # Flatten list of guilds
            guilds = set(g['guild'] for g in guilds_raw)

            # Flatten rules
            rules = {}
            for rule in rules_raw:
                server_id = rule["server_id"]
                if server_id not in rules:
                    rules[rule["server_id"]] = []
                rules[server_id].append((rule["role_id"], rule["guild"]))
            for server, rules in rules.items():
                guild: discord.Guild = self.bot.get_guild(server)
                if guild is None:
                    continue
                member: discord.Member = guild.get_member(user_id)
                if member is None:
                    continue

                all_roles = set()
                to_add = set()
                for role_id, tibia_guild in rules:
                    role: discord.Role = discord.utils.get(guild.roles,
                                                           id=role_id)
                    if role is None:
                        continue
                    all_roles.add(role)
                    if (tibia_guild == "*"
                            and guilds) or tibia_guild in guilds:
                        to_add.add(role)
                to_remove = all_roles - to_add
                try:
                    before_roles = set(member.roles)
                    await member.remove_roles(*to_remove,
                                              reason="Automatic roles")
                    await member.add_roles(*to_add, reason="Automatic roles")
                    # A small delay is needed so member.roles is updated with the added possible added roles.
                    await asyncio.sleep(0.15)
                    after_roles = set(member.roles)

                    new_roles = after_roles - before_roles
                    removed_roles = before_roles - after_roles
                    if new_roles or removed_roles:
                        embed = discord.Embed(
                            colour=discord.Colour.dark_blue(),
                            title="Autorole changes")
                        embed.set_author(
                            name="{0.name}#{0.discriminator} (ID: {0.id})".
                            format(member),
                            icon_url=get_user_avatar(member))
                        if new_roles:
                            embed.add_field(name="Added roles",
                                            value=", ".join(
                                                r.mention for r in new_roles))
                        if removed_roles:
                            embed.add_field(name="Removed roles",
                                            value=", ".join(
                                                r.mention
                                                for r in removed_roles))
                        await self.bot.send_log_message(guild, embed=embed)
                except discord.HTTPException:
                    pass
        except Exception:
            log.exception("Event: character_change")
Exemplo n.º 14
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
Exemplo n.º 15
0
def get_character(name, tries=5):
    """Returns a dictionary with a player's info

    The dictionary contains the following keys: name, deleted, level, vocation, world, residence,
    married, gender, guild, last,login, chars*.
        *chars is list that contains other characters in the same account (if not hidden).
        Each list element is dictionary with the keys: name, world.
    May return ERROR_DOESNTEXIST or ERROR_NETWORK accordingly."""
    try:
        url = url_character + urllib.parse.quote(name.encode('iso-8859-1'))
    except UnicodeEncodeError:
        return ERROR_DOESNTEXIST
    char = dict()

    # Fetch website
    try:
        page = yield from aiohttp.get(url)
        content = yield from page.text(encoding='ISO-8859-1')
    except Exception:
        if tries == 0:
            log.error(
                "getPlayer: Couldn't fetch {0}, network error.".format(name))
            return ERROR_NETWORK
        else:
            tries -= 1
            yield from asyncio.sleep(network_retry_delay)
            ret = yield from get_character(name, tries)
            return ret

    # Trimming content to reduce load
    try:
        startIndex = content.index('<div class="BoxContent"')
        endIndex = content.index("<B>Search Character</B>")
        content = content[startIndex:endIndex]
    except ValueError:
        # Website fetch was incomplete, due to a network error
        if tries == 0:
            log.error(
                "getPlayer: Couldn't fetch {0}, network error.".format(name))
            return ERROR_NETWORK
        else:
            tries -= 1
            yield from asyncio.sleep(network_retry_delay)
            ret = yield from get_character(name, tries)
            return ret
    # Check if player exists
    if "Name:</td><td>" not in content:
        return ERROR_DOESNTEXIST

    # TODO: Is there a way to reduce this part?
    # Name
    m = re.search(r'Name:</td><td>([^<,]+)', content)
    if m:
        char['name'] = m.group(1).strip()

    # Deleted
    m = re.search(r', will be deleted at ([^<]+)', content)
    if m:
        char['deleted'] = True

    # Vocation
    m = re.search(r'Vocation:</td><td>([^<]+)', content)
    if m:
        char['vocation'] = m.group(1)

    # Level
    m = re.search(r'Level:</td><td>(\d+)', content)
    if m:
        char['level'] = int(m.group(1))
    # Use database levels for online characters
    for onchar in global_online_list:
        if onchar.split("_", 1)[1] == char['name']:
            c = userDatabase.cursor()
            c.execute("SELECT last_level FROM chars WHERE name LIKE ?",
                      (char['name'], ))
            result = c.fetchone()
            if result:
                char['level'] = abs(result["last_level"])
            c.close()
            break

    # World
    m = re.search(r'World:</td><td>([^<]+)', content)
    if m:
        char['world'] = m.group(1)

    # Residence (City)
    m = re.search(r'Residence:</td><td>([^<]+)', content)
    if m:
        char['residence'] = m.group(1)

    # Marriage
    m = re.search(r'Married To:</td><td>?.+name=([^"]+)', content)
    if m:
        char['married'] = urllib.parse.unquote_plus(m.group(1),
                                                    encoding='ISO-8859-1')

    # Sex
    m = re.search(r'Sex:</td><td>([^<]+)', content)
    if m:
        if m.group(1) == 'male':
            char['gender'] = 'male'
        else:
            char['gender'] = 'female'

    # Guild rank
    m = re.search(r'Membership:</td><td>([^<]+)\sof the', content)
    if m:
        char['rank'] = m.group(1)
        # Guild membership
        m = re.search(r'GuildName=.*?([^&]+).+', content)
        if m:
            char['guild'] = urllib.parse.unquote_plus(m.group(1))

    # House
    m = re.search(
        r'House:</td><td> <a href=\"https://secure\.tibia\.com/community/\?subtopic=houses.+houseid=(\d+)'
        r'&amp;character=(?:[^&]+)&amp;action=characters\" >([^<]+)</a> \(([^(]+)\) is paid until '
        r'([A-z]+).*?;(\d+).*?;(\d+)', content)
    if m:
        char["house_id"] = m.group(1)
        char["house"] = m.group(2)
        char["house_town"] = m.group(3)

    # Last login
    m = re.search(r'Last Login:</td><td>([^<]+)', content)
    if m:
        lastLogin = m.group(1).replace("&#160;", " ").replace(",", "")
        if "never" in lastLogin:
            char['last_login'] = None
        else:
            char['last_login'] = lastLogin

    # Discord owner
    c = userDatabase.cursor()
    c.execute("SELECT user_id FROM chars WHERE name LIKE ?", (char["name"], ))
    result = c.fetchone()
    char["owner_id"] = None if result is None else result["user_id"]

    # Update name, vocation and world for chars in database if necessary
    c = userDatabase.cursor()
    c.execute("SELECT vocation, name, id, world FROM chars WHERE name LIKE ?",
              (name, ))
    result = c.fetchone()
    if result:
        if result["vocation"] != char['vocation']:
            c.execute("UPDATE chars SET vocation = ? WHERE id = ?", (
                char['vocation'],
                result["id"],
            ))
            log.info(
                "{0}'s vocation was set to {1} from {2} during get_character()"
                .format(char['name'], char['vocation'], result["vocation"]))
        if result["name"] != char["name"]:
            c.execute("UPDATE chars SET name = ? WHERE id = ?", (
                char['name'],
                result["id"],
            ))
            log.info("{0} was renamed to {1} during get_character()".format(
                result["name"], char['name']))

        if result["world"] != char["world"]:
            c.execute("UPDATE chars SET world = ? WHERE id = ?", (
                char['world'],
                result["id"],
            ))
            log.info(
                "{0}'s world was set to {1} from {2} during get_character()".
                format(char['name'], char['world'], result["world"]))

    #Skills from highscores
    c = userDatabase.cursor()
    for category in highscores_categories:
        c.execute(
            "SELECT " + category + "," + category +
            "_rank FROM chars WHERE name LIKE ?", (name, ))
        result = c.fetchone()
        if result:
            if result[category] is not None and result[category +
                                                       '_rank'] is not None:
                char[category] = result[category]
                char[category + '_rank'] = result[category + '_rank']

    char["deaths"] = []
    regex_deaths = r'valign="top" >([^<]+)</td><td>(.+?)</td></tr>'
    pattern = re.compile(regex_deaths, re.MULTILINE + re.S)
    matches = re.findall(pattern, content)

    for m in matches:
        death_time = m[0].replace('&#160;', ' ').replace(",", "")
        death_level = ""
        death_killer = ""
        death_by_player = False

        if m[1].find("Died") != -1:
            regex_deathinfo_monster = r'Level (\d+) by ([^.]+)'
            pattern = re.compile(regex_deathinfo_monster, re.MULTILINE + re.S)
            m_deathinfo_monster = re.search(pattern, m[1])
            if m_deathinfo_monster:
                death_level = m_deathinfo_monster.group(1)
                death_killer = m_deathinfo_monster.group(2)
        else:
            regex_deathinfo_player = r'Level (\d+) by .+?name=([^"]+)'
            pattern = re.compile(regex_deathinfo_player, re.MULTILINE + re.S)
            m_deathinfo_player = re.search(pattern, m[1])
            if m_deathinfo_player:
                death_level = m_deathinfo_player.group(1)
                death_killer = urllib.parse.unquote_plus(
                    m_deathinfo_player.group(2))
                death_by_player = True
        try:
            char["deaths"].append({
                'time': death_time,
                'level': int(death_level),
                'killer': death_killer,
                'byPlayer': death_by_player
            })
        except ValueError:
            # Some pvp deaths have no level, so they are raising a ValueError, they will be ignored for now.
            continue

    # Other chars
    # note that an empty char list means the character is hidden
    # otherwise you'd have at least the same char in the list
    char['chars'] = []
    try:
        # See if there is a character list
        startIndex = content.index("<B>Characters</B>")
        content = content[startIndex:]

        # Find characters
        regex_chars = r'<TD WIDTH=10%><NOBR>([^<]+)[^?]+.+?VALUE=\"([^\"]+)'
        pattern = re.compile(regex_chars, re.MULTILINE + re.S)
        m = re.findall(pattern, content)

        if m:
            for (world, name) in m:
                name = urllib.parse.unquote_plus(name)
                char['chars'].append({'name': name, 'world': world})
    except Exception:
        pass
    return char
Exemplo n.º 16
0
async def get_character(name, tries=5) -> Optional[Character]:
    """Fetches a character from TibiaData, parses and returns a Character object

    The character object contains all the information available on Tibia.com
    Information from the user's database is also added, like owner and highscores.
    If the character can't be fetch due to a network error, an NetworkError exception is raised
    If the character doesn 't exist, None is returned.
    """
    if tries == 0:
        log.error(
            "get_character: Couldn't fetch {0}, network error.".format(name))
        raise NetworkError()
    try:
        url = f"https://api.tibiadata.com/v2/characters/{urllib.parse.quote(name, safe='')}.json"
    except UnicodeEncodeError:
        return None
    # Fetch website
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as resp:
                content = await resp.text(encoding='ISO-8859-1')
    except Exception:
        await asyncio.sleep(config.network_retry_delay)
        return await get_character(name, tries - 1)

    content_json = json.loads(content)
    character = Character.parse_from_tibiadata(content_json)
    if character is None:
        return None

    if character.house is not None:
        with closing(tibiaDatabase.cursor()) as c:
            c.execute("SELECT id FROM houses WHERE name LIKE ?",
                      (character.house["name"].strip(), ))
            result = c.fetchone()
            if result:
                character.house["houseid"] = result["id"]

    # Database operations
    c = userDatabase.cursor()
    # Skills from highscores
    c.execute("SELECT category, rank, value FROM highscores WHERE name LIKE ?",
              (character.name, ))
    results = c.fetchall()
    if len(results) > 0:
        character.highscores = results

    # Discord owner
    c.execute(
        "SELECT user_id, vocation, name, id, world, guild FROM chars WHERE name LIKE ?",
        (name, ))
    result = c.fetchone()
    if result is None:
        # Untracked character
        return character

    character.owner = result["user_id"]
    if result["vocation"] != character.vocation:
        with userDatabase as conn:
            conn.execute("UPDATE chars SET vocation = ? WHERE id = ?", (
                character.vocation,
                result["id"],
            ))
            log.info(
                "{0}'s vocation was set to {1} from {2} during get_character()"
                .format(character.name, character.vocation,
                        result["vocation"]))
    if result["name"] != character.name:
        with userDatabase as conn:
            conn.execute("UPDATE chars SET name = ? WHERE id = ?", (
                character.name,
                result["id"],
            ))
            log.info("{0} was renamed to {1} during get_character()".format(
                result["name"], character.name))

    if result["world"] != character.world:
        with userDatabase as conn:
            conn.execute("UPDATE chars SET world = ? WHERE id = ?", (
                character.world,
                result["id"],
            ))
            log.info(
                "{0}'s world was set to {1} from {2} during get_character()".
                format(character.name, character.world, result["world"]))
    if character.guild is not None and result["guild"] != character.guild[
            "name"]:
        with userDatabase as conn:
            conn.execute("UPDATE chars SET guild = ? WHERE id = ?", (
                character.guild["name"],
                result["id"],
            ))
            log.info(
                "{0}'s guild was set to {1} from {2} during get_character()".
                format(character.name, character.guild["name"],
                       result["guild"]))
    return character
Exemplo n.º 17
0
async def get_character(name,
                        tries=5,
                        *,
                        bot: commands.Bot = None) -> Optional[Character]:
    """Fetches a character from TibiaData, parses and returns a Character object

    The character object contains all the information available on Tibia.com
    Information from the user's database is also added, like owner and highscores.
    If the character can't be fetch due to a network error, an NetworkError exception is raised
    If the character doesn 't exist, None is returned.
    """
    if tries == 0:
        log.error(
            "get_character: Couldn't fetch {0}, network error.".format(name))
        raise NetworkError()
    try:
        url = f"https://api.tibiadata.com/v2/characters/{urllib.parse.quote(name.strip(), safe='')}.json"
    except UnicodeEncodeError:
        return None
    # Fetch website
    try:
        character = CACHE_CHARACTERS[name.lower()]
    except KeyError:
        req_log.info(f"get_character({name})")
        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(url) as resp:
                    content = await resp.text(encoding='ISO-8859-1')
        except Exception:
            await asyncio.sleep(config.network_retry_delay)
            return await get_character(name, tries - 1)

        content_json = json.loads(content)
        character = Character.parse_from_tibiadata(content_json)
        CACHE_CHARACTERS[name.lower()] = character
    if character is None:
        return None

    if character.house is not None:
        with closing(tibiaDatabase.cursor()) as c:
            c.execute("SELECT id FROM houses WHERE name LIKE ?",
                      (character.house["name"].strip(), ))
            result = c.fetchone()
            if result:
                character.house["houseid"] = result["id"]

    # If the character exists in the online list use data from there where possible
    for c in global_online_list:
        if c == character:
            character.level = c.level
            character.vocation = c.vocation
            break

    # Database operations
    c = userDatabase.cursor()
    # Skills from highscores
    c.execute("SELECT category, rank, value FROM highscores WHERE name LIKE ?",
              (character.name, ))
    results = c.fetchall()
    if len(results) > 0:
        character.highscores = results

    # Check if this user was recently renamed, and update old reference to this
    for old_name in character.former_names:
        c.execute("SELECT id FROM chars WHERE name LIKE ? LIMIT 1",
                  (old_name, ))
        result = c.fetchone()
        if result:
            with userDatabase as conn:
                conn.execute("UPDATE chars SET name = ? WHERE id = ?",
                             (character.name, result["id"]))
                log.info(
                    "{0} was renamed to {1} during get_character()".format(
                        old_name, character.name))

    # Discord owner
    c.execute(
        "SELECT user_id, vocation, name, id, world, guild FROM chars WHERE name LIKE ? OR name LIKE ?",
        (name, character.name))
    result = c.fetchone()
    if result is None:
        # Untracked character
        return character

    character.owner = result["user_id"]
    if result["vocation"] != character.vocation:
        with userDatabase as conn:
            conn.execute("UPDATE chars SET vocation = ? WHERE id = ?", (
                character.vocation,
                result["id"],
            ))
            log.info(
                "{0}'s vocation was set to {1} from {2} during get_character()"
                .format(character.name, character.vocation,
                        result["vocation"]))
    # This condition PROBABLY can't be met again
    if result["name"] != character.name:
        with userDatabase as conn:
            conn.execute("UPDATE chars SET name = ? WHERE id = ?", (
                character.name,
                result["id"],
            ))
            log.info("{0} was renamed to {1} during get_character()".format(
                result["name"], character.name))

    if result["world"] != character.world:
        with userDatabase as conn:
            conn.execute("UPDATE chars SET world = ? WHERE id = ?", (
                character.world,
                result["id"],
            ))
            log.info(
                "{0}'s world was set to {1} from {2} during get_character()".
                format(character.name, character.world, result["world"]))
    if character.guild is not None and result["guild"] != character.guild[
            "name"]:
        with userDatabase as conn:
            conn.execute("UPDATE chars SET guild = ? WHERE id = ?", (
                character.guild["name"],
                result["id"],
            ))
            log.info(
                "{0}'s guild was set to {1} from {2} during get_character()".
                format(character.name, character.guild["name"],
                       result["guild"]))
            if bot is not None:
                bot.dispatch("character_change", character.owner)
    if character.guild is None and result["guild"] is not None:
        with userDatabase as conn:
            conn.execute("UPDATE chars SET guild = ? WHERE id = ?", (
                None,
                result["id"],
            ))
            log.info(
                "{0}'s guild was set to {1} from {2} during get_character()".
                format(character.name, None, result["guild"]))
            if bot is not None:
                bot.dispatch("character_change", character.owner)

    return character
Exemplo n.º 18
0
    def add_char(self, ctx, *, params):
        """Registers a tibia character to a discord user.

        The syntax is:
        /stalk addchar user,character"""
        if not ctx.message.channel.is_private:
            return True
        params = params.split(",")
        if len(params) != 2:
            yield from self.bot.say(
                "The correct syntax is: ``/stalk addchar username,character``")
            return

        author = ctx.message.author
        if author.id in mod_ids + owner_ids:
            author_servers = get_user_servers(self.bot, author.id)
        else:
            author_servers = get_user_admin_servers(self.bot, author.id)
        author_worlds = get_user_worlds(self.bot, author.id)

        # Only search in the servers the command author is
        user = get_member_by_name(self.bot,
                                  params[0],
                                  server_list=author_servers)
        user_servers = get_user_servers(self.bot, user.id)
        user_worlds = get_user_worlds(self.bot, author.id)

        common_worlds = list(set(author_worlds) & set(user_worlds))

        yield from self.bot.send_typing(ctx.message.channel)
        char = yield from get_character(params[1])

        if user is None:
            yield from self.bot.say(
                "I don't see any user named **{0}** in the servers you manage."
                .format(params[0]))
            return
        if type(char) is not dict:
            if char == ERROR_NETWORK:
                yield from self.bot.say(
                    "I couldn't fetch the character, please try again.")
            elif char == ERROR_DOESNTEXIST:
                yield from self.bot.say("That character doesn't exists.")
            return
        if char["world"] not in common_worlds:
            yield from self.bot.say(
                "**{name}** ({world}) is not in a world you can manage.".
                format(**char))
            return
        if char.get("deleted", False):
            yield from self.bot.say(
                "**{name}** ({world}) is scheduled for deletion and can't be added."
                .format(**char))
            return
        c = userDatabase.cursor()
        try:
            c.execute("SELECT id, name, user_id FROM chars WHERE name LIKE ?",
                      (char['name'], ))
            result = c.fetchone()
            # Char is already in database
            if result is not None:
                # Update name if it was changed
                if char['name'] != params[1]:
                    c.execute("UPDATE chars SET name = ? WHERE id = ?", (
                        char['name'],
                        result["id"],
                    ))
                    yield from self.bot.say(
                        "This character's name was changed from **{0}** to **{1}**"
                        .format(params[1], char['name']))
                # Registered to a different user
                if result["user_id"] != user.id:
                    current_user = get_member(self.bot, result["user_id"])
                    # User no longer in server
                    if current_user is None:
                        c.execute("UPDATE chars SET user_id = ? WHERE id = ?",
                                  (
                                      user.id,
                                      result["id"],
                                  ))
                        yield from self.bot.say(
                            "This character was registered to a user no longer in server. "
                            "It was assigned to this user successfully.")
                        # Log on relevant servers
                        for server in user_servers:
                            world = tracked_worlds.get(server.id, None)
                            if world == char["world"]:
                                log_msg = "{0.mention} registered **{1}** ({2} {3}) to {4.mention}."
                                yield from send_log_message(
                                    self.bot, server,
                                    log_msg.format(author, char["name"],
                                                   char["level"],
                                                   char["vocation"], user))
                    else:
                        yield from self.bot.say(
                            "This character is already registered to **@{0}**".
                            format(current_user.display_name))
                    return
                # Registered to current user
                yield from self.bot.say(
                    "This character is already registered to this user.")
                return
            c.execute(
                "INSERT INTO chars (name,last_level,vocation,user_id, world) VALUES (?,?,?,?,?)",
                (char["name"], char["level"] * -1, char["vocation"], user.id,
                 char["world"]))
            # Check if user is already registered
            c.execute("SELECT id from users WHERE id = ?", (user.id, ))
            result = c.fetchone()
            if result is None:
                c.execute("INSERT INTO users(id,name) VALUES (?,?)", (
                    user.id,
                    user.display_name,
                ))
            yield from self.bot.say(
                "**{0}** was registered successfully to this user.".format(
                    char['name']))
            # Log on relevant servers
            for server in user_servers:
                world = tracked_worlds.get(server.id, None)
                if world == char["world"]:
                    char["guild"] = char.get("guild", "No guild")
                    log_msg = "{0.mention} registered **{1}** ({2} {3}, {4}) to {5.mention}."
                    yield from send_log_message(
                        self.bot, server,
                        log_msg.format(author, char["name"], char["level"],
                                       char["vocation"], char["guild"], user))
            return
        finally:
            c.close()
            userDatabase.commit()
Exemplo n.º 19
0
    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:
        /stalk addacc user,char"""
        if not ctx.message.channel.is_private:
            return True
        params = params.split(",")
        if len(params) != 2:
            yield from self.bot.say(
                "The correct syntax is: ``/stalk addacc username,character``")
            return

        author = ctx.message.author
        if author.id in mod_ids + owner_ids:
            author_servers = get_user_servers(self.bot, author.id)
        else:
            author_servers = get_user_admin_servers(self.bot, author.id)
        author_worlds = get_user_worlds(self.bot, author.id)

        user = get_member_by_name(self.bot,
                                  params[0],
                                  server_list=author_servers)
        user_servers = get_user_servers(self.bot, user.id)
        user_worlds = get_user_worlds(self.bot, user.id)

        common_worlds = list(set(author_worlds) & set(user_worlds))
        yield from self.bot.send_typing(ctx.message.channel)
        character = yield from get_character(params[1])
        if user is None:
            yield from self.bot.say(
                "I don't see any user named **{0}** in the servers you manage."
                .format(params[0]))
            return
        if type(character) is not dict:
            if character == ERROR_NETWORK:
                yield from self.bot.say(
                    "I couldn't fetch the character, please try again.")
            elif character == ERROR_DOESNTEXIST:
                yield from self.bot.say("That character doesn't exists.")
            return
        c = userDatabase.cursor()
        try:
            chars = character['chars']
            # If the char is hidden,we still add the searched character
            if len(chars) == 0:
                yield from self.bot.say("Character is hidden.")
                chars = [character]
            skipped = list()
            added = list()
            added_tuples = list()
            reassigned_tuples = list()
            existent = list()
            error = list()
            for char in chars:
                # Character not in followed server(s), skip.
                if char['world'] not in common_worlds:
                    skipped.append([char["name"], char["world"]])
                    continue
                name = char["name"]
                # If char is the same we already looked up, no need to look him up again
                if character["name"] == char["name"]:
                    char = character
                else:
                    char = yield from get_character(char["name"])
                if type(char) is not dict:
                    error.append(name)
                    continue
                # Skip characters scheduled for deletion
                if char.get("deleted", False):
                    skipped.append([char["name"], char["world"]])
                    continue
                c.execute(
                    "SELECT id, name,user_id FROM chars WHERE name LIKE ?",
                    (char['name'], ))
                result = c.fetchone()
                # Char is already in database
                if result is not None:
                    # Registered to different user
                    if str(result["user_id"]) != user.id:
                        current_user = get_member(self.bot, result["user_id"])
                        # Char is registered to user no longer in server
                        if current_user is None:
                            added.append(char)
                            reassigned_tuples.append((
                                user.id,
                                result["id"],
                            ))
                            continue
                        else:
                            yield from self.bot.say(
                                "{0} is already assigned to {1}. We can't add any other of these "
                                "characters.".format(
                                    char["name"], current_user.display_name))
                            return
                    # Registered to current user
                    existent.append(char)
                    continue
                added.append(char)
                added_tuples.append((
                    char["name"],
                    char["level"] * -1,
                    char["vocation"],
                    user.id,
                    char["world"],
                ))
            c.execute("SELECT id from users WHERE id = ?", (user.id, ))
            result = c.fetchone()
            if result is None:
                c.execute("INSERT INTO users(id,name) VALUES (?,?)", (
                    user.id,
                    user.display_name,
                ))

            c.executemany(
                "INSERT INTO chars(name,last_level,vocation,user_id, world) VALUES (?,?,?,?,?)",
                added_tuples)
            c.executemany("UPDATE chars SET user_id = ? WHERE id = ?",
                          reassigned_tuples)
            reply = ""
            log_reply = dict().fromkeys([server.id for server in user_servers],
                                        "")
            if added:
                reply += "\nThe following characters were registered or reassigned successfully:"
                for char in added:
                    char["guild"] = char.get("guild", "No guild")
                    reply += "\n\t**{name}** ({level} {vocation}) - **{guild}**".format(
                        **char)
                    # Announce on server log of each server
                    for server in user_servers:
                        # Only announce on worlds where the character's world is tracked
                        if tracked_worlds.get(server.id,
                                              None) == char["world"]:
                            log_reply[
                                server.
                                id] += "\n\t{name} - {level} {vocation} - **{guild}**".format(
                                    **char)
            if existent:
                reply += "\nThe following characters were already registered to this user:"******"guild"] = char.get("guild", "No guild")
                    reply += "\n\t**{name}** ({level} {vocation}) - **{guild}**".format(
                        **char)
            if skipped:
                reply += "\nThe following characters were skipped (not in tracked worlds or scheduled deletion):"
                for char, world in skipped:
                    reply += "\n\t{0} ({1})".format(char, world)
            if error:
                reply += "\nThe following characters couldn't be fetched: "
                reply += ", ".join(error)
            yield from self.bot.say(reply)
            for server_id, message in log_reply.items():
                if message:
                    message = "{0.mention} registered the following characters to {1.mention}: {2}".format(
                        author, user, message)
                    yield from send_log_message(self.bot,
                                                self.bot.get_server(server_id),
                                                message)
            return
        finally:
            c.close()
            userDatabase.commit()
Exemplo n.º 20
0
    def remove_user(self, ctx, *, name):
        """Removes a discord user from the database

        The syntax is:
        /stalk remove name"""
        if not ctx.message.channel.is_private:
            return True
        c = userDatabase.cursor()
        yield from self.bot.send_typing(ctx.message.channel)
        # Searching users in server
        user = get_member_by_name(self.bot, name)
        # Searching users in database
        try:
            c.execute("SELECT id, name from users WHERE name LIKE ?", (name, ))
            result = c.fetchone()
            # Users in database and not in servers
            if result is not None and get_member(self.bot,
                                                 result['id']) is None:
                yield from self.bot.say(
                    "**@{0}** was no longer in server and was removed successfully."
                    .format(result["name"]))
                delete_id = result["id"]
            # User in servers and in database
            elif user is not None and result is not None:
                yield from self.bot.say(
                    "**{0}** was removed successfully.".format(
                        user.display_name))
                delete_id = user.id
            # User in server but not in database
            elif user is not None and result is None:
                yield from self.bot.say("**{0}** is not registered.".format(
                    user.display_name))
                return
            # User not in server or database
            else:
                yield from self.bot.say(
                    "I don't see any user named **{0}**.".format(name))
                return

            c.execute("DELETE FROM users WHERE id = ?", (delete_id, ))
            c.execute("SELECT name FROM chars WHERE user_id = ?",
                      (delete_id, ))
            result = c.fetchall()
            if len(result) >= 1:
                chars = ["**" + i["name"] + "**" for i in result]
                reply = "The following characters were registered to the user:\n\t"
                reply += "\n\t".join(chars)
                reply += "\nDo you want to delete them? ``(yes/no)``"
                yield from self.bot.say(reply)

                answer = yield from self.bot.wait_for_message(
                    author=ctx.message.author,
                    channel=ctx.message.channel,
                    timeout=30.0)
                if answer is None:
                    yield from self.bot.say(
                        "I will take your silence as a no...")
                elif answer.content.lower() in ["yes", "y"]:
                    c.execute("DELETE FROM chars WHERE user_id = ?",
                              (delete_id, ))
                    yield from self.bot.say("Characters deleted successfully.")
                else:
                    yield from self.bot.say("Ok, we are done then.")
            return
        finally:
            c.close()
            userDatabase.commit()
Exemplo n.º 21
0
    def purge(self, ctx):
        """Performs a database cleanup

        Removes characters that have been deleted and users with no characters or no longer in server."""
        if not ctx.message.channel.is_private:
            return True
        c = userDatabase.cursor()
        try:
            c.execute("SELECT id FROM users")
            result = c.fetchall()
            if result is None:
                yield from self.bot.say("There are no users registered.")
                return
            delete_users = list()
            yield from self.bot.say("Initiating purge...")
            # Deleting users no longer in server
            for row in result:
                user = get_member(self.bot, row["id"])
                if user is None:
                    delete_users.append((row["id"], ))
            if len(delete_users) > 0:
                c.executemany("DELETE FROM users WHERE id = ?", delete_users)
                yield from self.bot.say(
                    "{0} user(s) no longer in the server were removed.".format(
                        c.rowcount))

            # Deleting chars with non-existent user
            c.execute(
                "SELECT name FROM chars WHERE user_id NOT IN (SELECT id FROM users)"
            )
            result = c.fetchall()
            if len(result) >= 1:
                chars = ["**" + i["name"] + "**" for i in result]
                reply = "{0} char(s) were assigned to a non-existent user and were deleted:\n\t".format(
                    len(result))
                reply += "\n\t".join(chars)
                yield from self.bot.say(reply)
                c.execute(
                    "DELETE FROM chars WHERE user_id NOT IN (SELECT id FROM users)"
                )

            # Removing deleted chars
            c.execute("SELECT name,last_level,vocation FROM chars")
            result = c.fetchall()
            if result is None:
                return
            delete_chars = list()
            rename_chars = list()
            # revoc_chars = list()
            for row in result:
                char = yield from get_character(row["name"])
                if char == ERROR_NETWORK:
                    yield from self.bot.say(
                        "Couldn't fetch **{0}**, skipping...".format(
                            row["name"]))
                    continue
                # Char was deleted
                if char == ERROR_DOESNTEXIST:
                    delete_chars.append((row["name"], ))
                    yield from self.bot.say(
                        "**{0}** doesn't exists, deleting...".format(
                            row["name"]))
                    continue
                # Char was renamed
                if char['name'] != row["name"]:
                    rename_chars.append((
                        char['name'],
                        row["name"],
                    ))
                    yield from self.bot.say(
                        "**{0}** was renamed to **{1}**, updating...".format(
                            row["name"], char['name']))

            # No need to check if user exists cause those were removed already
            if len(delete_chars) > 0:
                c.executemany("DELETE FROM chars WHERE name LIKE ?",
                              delete_chars)
                yield from self.bot.say("{0} char(s) were removed.".format(
                    c.rowcount))
            if len(rename_chars) > 0:
                c.executemany("UPDATE chars SET name = ? WHERE name LIKE ?",
                              rename_chars)
                yield from self.bot.say("{0} char(s) were renamed.".format(
                    c.rowcount))

            # Remove users with no chars
            c.execute(
                "SELECT id FROM users WHERE id NOT IN (SELECT user_id FROM chars)"
            )
            result = c.fetchall()
            if len(result) >= 1:
                c.execute(
                    "DELETE FROM users WHERE id NOT IN (SELECT user_id FROM chars)"
                )
                yield from self.bot.say(
                    "{0} user(s) with no characters were removed.".format(
                        c.rowcount))

            # Remove level ups of removed characters
            c.execute(
                "DELETE FROM char_levelups WHERE char_id NOT IN (SELECT id FROM chars)"
            )
            if c.rowcount > 0:
                yield from self.bot.say(
                    "{0} level up registries from removed characters were deleted."
                    .format(c.rowcount))
            c.execute(
                "DELETE FROM char_deaths WHERE char_id NOT IN (SELECT id FROM chars)"
            )
            # Remove deaths of removed characters
            if c.rowcount > 0:
                yield from self.bot.say(
                    "{0} death registries from removed characters were deleted."
                    .format(c.rowcount))
            yield from self.bot.say("Purge done.")
            return
        finally:
            userDatabase.commit()
            c.close()