Esempio n. 1
0
    async def emoji_info(self, ctx: NabCtx, *, emoji: discord.Emoji=None):
        """Shows information about an emoji, or shows all emojis.

        If the command is used with no arguments, all the server emojis are shown.

        If a emoji, its id or name is provided, it will show more information about it.

        Only emojis in the current servers can be checked."""
        if emoji is not None:
            embed = discord.Embed(title=emoji.name, timestamp=emoji.created_at, color=discord.Color.blurple())
            embed.set_thumbnail(url=emoji.url)
            embed.set_footer(text="Created at")
            embed.add_field(name="ID", value=emoji.id)
            embed.add_field(name="Usage", value=f"`{emoji}`")
            embed.add_field(name="Attributes", inline=False,
                            value=f"{ctx.tick(emoji.managed)} Twitch managed\n"
                                  f"{ctx.tick(emoji.require_colons)} Requires colons\n"
                                  f"{ctx.tick(len(emoji.roles) > 0)} Role limited")
        else:
            emojis: List[discord.Emoji] = ctx.guild.emojis
            if not emojis:
                return await ctx.send("This server has no custom emojis.")
            normal = [str(e) for e in emojis if not e.animated]
            animated = [str(e) for e in emojis if e.animated]
            embed = discord.Embed(title="Custom Emojis", color=discord.Color.blurple())
            if normal:
                emojis_str = "\n".join(normal)
                fields = split_message(emojis_str, FIELD_VALUE_LIMIT)
                for i, value in enumerate(fields):
                    if i == 0:
                        name = f"Regular ({len(normal)})"
                    else:
                        name = "\u200F"
                    embed.add_field(name=name, value=value.replace("\n", ""))
            if animated:
                emojis_str = "\n".join(animated)
                fields = split_message(emojis_str, FIELD_VALUE_LIMIT)
                for i, value in enumerate(fields):
                    if i == 0:
                        name = f"Animated (Nitro required) ({len(animated)})"
                    else:
                        name = "\u200F"
                    embed.add_field(name=name, value=value.replace("\n", ""))
        await ctx.send(embed=embed)
Esempio n. 2
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()
Esempio n. 3
0
    def get_npc_embed(ctx, npc, long):
        """Gets the embed to show in /npc command"""
        short_limit = 5
        long_limit = 100
        too_long = False

        if type(npc) is not dict:
            return

        embed = discord.Embed(title=npc["name"],
                              url=get_article_url(npc["title"]))
        embed.set_author(name="TibiaWiki",
                         icon_url=WIKI_ICON,
                         url=get_article_url(npc["title"]))
        embed.add_field(name="Job", value=npc["job"])
        if npc["name"] == "Rashid":
            rashid = get_rashid_info()
            npc["city"] = rashid["city"]
            npc["x"] = rashid["x"]
            npc["y"] = rashid["y"]
            npc["z"] = rashid["z"]
        if npc["name"] == "Yasir":
            npc["x"] = None
            npc["y"] = None
            npc["z"] = None
        embed.add_field(name="City", value=npc["city"])
        if npc["selling"]:
            count = 0
            value = ""
            for item in npc["selling"]:
                count += 1
                item["currency"] = item["currency"].replace(
                    "gold coin", "gold")
                value += "\n{name} \u2192 {value:,} {currency}".format(**item)
                if count > short_limit and not long:
                    value += "\n*...And {0} others*".format(
                        len(npc['selling']) - short_limit)
                    too_long = True
                    break
                if long and count > long_limit:
                    value += "\n*...And {0} others*".format(
                        len(npc['selling']) - long_limit)
                    break
            split_selling = split_message(value, FIELD_VALUE_LIMIT)
            for value in split_selling:
                if value == split_selling[0]:
                    name = "Selling"
                else:
                    name = "\u200F"
                embed.add_field(name=name, value=value)
        if npc["buying"]:
            count = 0
            value = ""
            for item in npc["buying"]:
                count += 1
                item["currency"] = item["currency"].replace(
                    "gold coin", "gold")
                value += "\n{name} \u2192 {value:,} {currency}".format(**item)
                if count > short_limit and not long:
                    value += "\n*...And {0} others*".format(
                        len(npc['buying']) - short_limit)
                    too_long = True
                    break
                if long and count > long_limit:
                    value += "\n*...And {0} others*".format(
                        len(npc['buying']) - long_limit)
                    break
            split_buying = split_message(value, FIELD_VALUE_LIMIT)
            for value in split_buying:
                if value == split_buying[0]:
                    name = "Buying"
                else:
                    name = "\u200F"
                embed.add_field(name=name, value=value)
        if npc["destinations"]:
            count = 0
            value = ""
            for destination in npc["destinations"]:
                count += 1
                value += "\n{name} \u2192 {price} gold".format(**destination)
            embed.add_field(name="Destinations", value=value)
        vocs = ["knight", "sorcerer", "paladin", "druid"]
        if npc["spells"]:
            values = {}
            count = {}
            skip = {}
            for spell in npc["spells"]:
                value = "\n{name} \u2014 {price:,} gold".format(**spell)
                for voc in vocs:
                    if skip.get(voc, False):
                        continue
                    if spell[voc] == 0:
                        continue
                    values[voc] = values.get(voc, "") + value
                    count[voc] = count.get(voc, 0) + 1
                    if count.get(voc, 0) >= short_limit and not long:
                        values[voc] += "\n*...And more*"
                        too_long = True
                        skip[voc] = True
            for voc, content in values.items():
                fields = split_message(content, FIELD_VALUE_LIMIT)
                for i, split_field in enumerate(fields):
                    name = f"Teaches ({voc.title()}s)" if i == 0 else "\u200F"
                    embed.add_field(name=name,
                                    value=split_field,
                                    inline=not len(fields) > 1)
        if too_long:
            ask_channel = ctx.bot.get_channel_by_name(config.ask_channel_name,
                                                      ctx.message.guild)
            if ask_channel:
                askchannel_string = " or use #" + ask_channel.name
            else:
                askchannel_string = ""
            embed.set_footer(
                text="To see more, PM me{0}.".format(askchannel_string))
        return embed
Esempio n. 4
0
    def get_monster_embed(ctx, monster, long):
        """Gets the monster embeds to show in /mob command
        The message is split in two embeds, the second contains loot only and is only shown if long is True"""
        embed = discord.Embed(title=monster["title"],
                              url=get_article_url(monster["title"]))
        embed.set_author(name="TibiaWiki",
                         icon_url=WIKI_ICON,
                         url=get_article_url(monster["title"]))
        hp = "?" if monster["hitpoints"] is None else "{0:,}".format(
            monster["hitpoints"])
        experience = "?" if monster["experience"] is None else "{0:,}".format(
            monster["experience"])
        if not (monster["experience"] is None or monster["hitpoints"] is None
                or monster["hitpoints"] < 0):
            ratio = "{0:.2f}".format(monster['experience'] /
                                     monster['hitpoints'])
        else:
            ratio = "?"
        embed.add_field(name="HP", value=hp)
        embed.add_field(name="Experience", value=experience)
        embed.add_field(name="HP/Exp Ratio", value=ratio)

        weak = []
        resist = []
        immune = []
        elements = [
            "physical", "holy", "death", "fire", "ice", "energy", "earth",
            "drown", "lifedrain"
        ]
        # Iterate through elemental types
        for index, value in monster.items():
            if index in elements:
                if monster[index] is None:
                    continue
                if monster[index] == 0:
                    immune.append(index.title())
                elif monster[index] > 100:
                    weak.append([index.title(), monster[index] - 100])
                elif monster[index] < 100:
                    resist.append([index.title(), monster[index] - 100])
        # Add paralysis to immunities
        if monster["paralysable"] == 0:
            immune.append("Paralysis")
        if monster["see_invisible"] == 1:
            immune.append("Invisibility")

        if immune:
            embed.add_field(name="Immune to", value="\n".join(immune))
        else:
            embed.add_field(name="Immune to", value="Nothing")

        if resist:
            embed.add_field(name="Resistant to",
                            value="\n".join(
                                ["{1}% {0}".format(*i) for i in resist]))
        else:
            embed.add_field(name="Resistant to", value="Nothing")
        if weak:
            embed.add_field(name="Weak to",
                            value="\n".join(
                                ["+{1}% {0}".format(*i) for i in weak]))
        else:
            embed.add_field(name="Weak to", value="Nothing")

        # If monster drops no loot, we might as well show everything
        if long or not monster["loot"]:
            embed.add_field(
                name="Max damage",
                value="{max_damage:,}".format(
                    **monster) if monster["max_damage"] is not None else "???")
            embed.add_field(name="Abilities",
                            value=monster["abilities"],
                            inline=False)
        if monster["loot"] and long:
            loot_string = ""

            for item in monster["loot"]:
                if item["chance"] is None:
                    item["chance"] = "??.??%"
                elif item["chance"] >= 100:
                    item["chance"] = "Always"
                else:
                    item["chance"] = "{0:05.2f}%".format(item['chance'])
                if item["max"] > 1:
                    item["count"] = "({min}-{max})".format(**item)
                else:
                    item["count"] = ""
                loot_string += "{chance} {item} {count}\n".format(**item)
            split_loot = split_message(loot_string, FIELD_VALUE_LIMIT)
            for loot in split_loot:
                if loot == split_loot[0]:
                    name = "Loot"
                else:
                    name = "\u200F"
                embed.add_field(name=name, value="`" + loot + "`")
        if monster["loot"] and not long:
            ask_channel = ctx.bot.get_channel_by_name(config.ask_channel_name,
                                                      ctx.message.guild)
            if ask_channel:
                askchannel_string = " or use #" + ask_channel.name
            else:
                askchannel_string = ""
            embed.set_footer(
                text="To see more, PM me{0}.".format(askchannel_string))
        return embed
Esempio n. 5
0
    def get_monster_embed(ctx: NabCtx, monster, long):
        """Gets the monster embeds to show in /mob command
        The message is split in two embeds, the second contains loot only and is only shown if long is True"""
        embed = discord.Embed(title=monster["title"],
                              url=get_article_url(monster["title"]))
        embed.set_author(name="TibiaWiki",
                         icon_url=WIKI_ICON,
                         url=get_article_url(monster["title"]))
        hp = "?" if monster["hitpoints"] is None else "{0:,}".format(
            monster["hitpoints"])
        experience = "?" if monster["experience"] is None else "{0:,}".format(
            monster["experience"])
        speed = "?" if monster["speed"] is None else "{0:,}".format(
            monster["speed"])
        embed.description = f"**HP** {hp} | **Exp** {experience} | **Speed** {speed}"

        attributes = {
            "summon": "Summonable",
            "convince": "Convinceable",
            "illusionable": "Illusionable",
            "pushable": "Pushable",
            "paralysable": "Paralysable",
            "see_invisible": "Sees Invisible"
        }

        attributes = "\n".join([
            f"{ctx.tick(monster[x])} {repl}" for x, repl in attributes.items()
            if monster[x] is not None
        ])
        embed.add_field(name="Attributes",
                        value="Unknown" if not attributes else attributes)
        elements = [
            "physical", "holy", "death", "fire", "ice", "energy", "earth"
        ]
        # Iterate through elemental types
        elemental_modifiers = {}
        for element in elements:
            if monster[element] is None or monster[element] == 100:
                continue
            elemental_modifiers[element] = monster[element] - 100
        elemental_modifiers = dict(
            sorted(elemental_modifiers.items(), key=lambda x: x[1]))
        if elemental_modifiers:
            content = ""
            for element, value in elemental_modifiers.items():
                if config.use_elemental_emojis:
                    content += f"\n{config.elemental_emojis[element]} {value:+}%"
                else:
                    content += f"\n{value:+}% {element.title()}"
            embed.add_field(name="Elemental modifiers", value=content)

        if monster["bestiary_class"] is not None:
            difficulties = {
                "Harmless":
                config.difficulty_off_emoji * 4,
                "Trivial":
                config.difficulty_on_emoji + config.difficulty_off_emoji * 3,
                "Easy":
                config.difficulty_on_emoji * 2 +
                config.difficulty_off_emoji * 2,
                "Medium":
                config.difficulty_on_emoji * 3 + config.difficulty_off_emoji,
                "Hard":
                config.difficulty_on_emoji * 4
            }
            occurrences = {
                "Common":
                config.occurrence_on_emoji * 1 +
                config.occurrence_off_emoji * 3,
                "Uncommon":
                config.occurrence_on_emoji * 2 +
                config.occurrence_off_emoji * 2,
                "Rare":
                config.occurrence_on_emoji * 3 +
                config.occurrence_off_emoji * 1,
                "Very Rare":
                config.occurrence_on_emoji * 4,
            }
            kills = {
                "Harmless": 25,
                "Trivial": 250,
                "Easy": 500,
                "Medium": 1000,
                "Hard": 2500
            }
            points = {
                "Harmless": 1,
                "Trivial": 5,
                "Easy": 15,
                "Medium": 25,
                "Hard": 50
            }
            bestiary_info = monster['bestiary_class']
            if monster["bestiary_level"] is not None:
                difficulty = difficulties.get(
                    monster["bestiary_level"],
                    f"({monster['bestiary_level']})")
                required_kills = kills[monster['bestiary_level']]
                given_points = points[monster['bestiary_level']]
                bestiary_info += f"\n{difficulty}"
            if monster["occurrence"] is not None:
                occurrence = occurrences.get(monster["occurrence"], f"")
                if monster['occurrence'] == 'Very Rare':
                    required_kills = 5
                    given_points = max(points[monster['bestiary_level']] * 2,
                                       5)
                bestiary_info += f"\n{occurrence}"
            if monster["bestiary_level"] is not None:

                bestiary_info += f"\n{required_kills:,} kills | {given_points}{config.charms_emoji}️"
            embed.add_field(name="Bestiary Class", value=bestiary_info)

        # If monster drops no loot, we might as well show everything
        if long or not monster["loot"]:
            embed.add_field(
                name="Max damage",
                value="{max_damage:,}".format(
                    **monster) if monster["max_damage"] is not None else "???")
            embed.add_field(name="Abilities",
                            value=monster["abilities"],
                            inline=False)
        if monster["loot"] and long:
            loot_string = ""

            for item in monster["loot"]:
                if item["chance"] is None:
                    item["chance"] = "??.??%"
                elif item["chance"] >= 100:
                    item["chance"] = "Always"
                else:
                    item["chance"] = "{0:05.2f}%".format(item['chance'])
                if item["max"] > 1:
                    item["count"] = "({min}-{max})".format(**item)
                else:
                    item["count"] = ""
                loot_string += "{chance} {item} {count}\n".format(**item)
            split_loot = split_message(loot_string, FIELD_VALUE_LIMIT - 20)
            for loot in split_loot:
                if loot == split_loot[0]:
                    name = "Loot"
                else:
                    name = "\u200F"
                embed.add_field(name=name, value="`" + loot + "`")
        if monster["loot"] and not long:
            ask_channel = ctx.ask_channel_name
            if ask_channel:
                askchannel_string = " or use #" + ask_channel
            else:
                askchannel_string = ""
            embed.set_footer(
                text="To see more, PM me{0}.".format(askchannel_string))
        return embed
Esempio n. 6
0
    async def loot(self, ctx: NabCtx):
        """Scans an image of a container looking for Tibia items and shows an approximate loot value.

        An image must be attached with the message. The prices used are NPC prices only.

        The image requires the following:

        - Must be a screenshot of inventory windows (backpacks, depots, etc).
        - Have the original size, the image can't be scaled up or down, however it can be cropped.
        - The image must show the complete slot.
        - JPG images are usually not recognized.
        - PNG images with low compression settings take longer to be scanned or aren't detected at all.

        The bot shows the total loot value and a list of the items detected, separated into the NPC that buy them.
        """
        if ctx.author.id in self.processing_users and not checks.is_owner_check(
                ctx):
            await ctx.send(
                "I'm already scanning an image for you! Wait for me to finish that one."
            )
            return

        if len(ctx.message.attachments) == 0:
            await ctx.send(
                "You need to upload a picture of your loot and type the command in the comment."
            )
            return

        attachment: discord.Attachment = ctx.message.attachments[0]
        if attachment.height is None:
            await ctx.send("That's not an image!")
            return
        if attachment.size > 2097152:
            await ctx.send(
                "That image was too big! Try splitting it into smaller images, or cropping out anything "
                "irrelevant.")
            return
        if attachment.height < MIN_HEIGHT or attachment.width < MIN_WIDTH:
            await ctx.send("That image is too small to be a loot image.")
            return

        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(attachment.url) as resp:
                    loot_image = await resp.read()
        except aiohttp.ClientError:
            log.exception("loot: Couldn't parse image")
            await ctx.send("I failed to load your image. Please try again.")
            return

        await ctx.send(
            f"I've begun parsing your image, **@{ctx.author.display_name}**. "
            "Please be patient, this may take a few moments.")
        status_msg = await ctx.send("Status: Reading")
        try:
            # Owners are not affected by the limit.
            self.processing_users.append(ctx.author.id)
            start_time = time.time()
            loot_list, loot_image_overlay = await loot_scan(
                ctx, loot_image, status_msg)
            scan_time = time.time() - start_time
        except LootScanException as e:
            await ctx.send(e)
            return
        finally:
            self.processing_users.remove(ctx.author.id)
        embed = discord.Embed(color=discord.Color.blurple())
        embed.set_footer(text=f"Loot scanned in {scan_time:,.2f} seconds.")
        long_message = f"These are the results for your image: [{attachment.filename}]({attachment.url})"

        if len(loot_list) == 0:
            await ctx.send(
                f"Sorry {ctx.author.mention}, I couldn't find any loot in that image. Loot parsing will "
                f"only work on high quality images, so make sure your image wasn't compressed."
            )
            return

        total_value = 0

        unknown = False
        for item in loot_list:
            if loot_list[item]['group'] == "Unknown":
                unknown = loot_list[item]
                break

        groups = []
        for item in loot_list:
            if not loot_list[item]['group'] in groups and loot_list[item][
                    'group'] != "Unknown":
                groups.append(loot_list[item]['group'])
        has_marketable = False
        for group in groups:
            value = ""
            group_value = 0
            for item in loot_list:
                if loot_list[item]['group'] == group and loot_list[item][
                        'group'] != "Unknown":
                    if group == "No Value":
                        value += f"x{loot_list[item]['count']} {item}\n"
                    else:
                        with closing(tibiaDatabase.cursor()) as c:
                            c.execute(
                                "SELECT name FROM items, items_attributes "
                                "WHERE name LIKE ? AND id = item_id AND attribute = 'imbuement'"
                                " LIMIT 1", (item, ))
                            result = c.fetchone()
                        if result:
                            has_marketable = True
                            emoji = "💎"
                        else:
                            emoji = ""
                        value += "x{1} {0}{3} \u2192 {2:,}gp total\n".format(
                            item, loot_list[item]['count'],
                            loot_list[item]['count'] *
                            loot_list[item]['value_sell'], emoji)

                    total_value += loot_list[item]['count'] * loot_list[item][
                        'value_sell']
                    group_value += loot_list[item]['count'] * loot_list[item][
                        'value_sell']
            if group == "No Value":
                name = group
            else:
                name = f"{group} - {group_value:,} gold"
            # Split into multiple fields if they exceed field max length
            split_group = split_message(value, FIELD_VALUE_LIMIT)
            for subgroup in split_group:
                if subgroup != split_group[0]:
                    name = "\u200F"
                embed.add_field(name=name, value=subgroup, inline=False)

        if unknown:
            long_message += f"\n**There were {unknown['count']} unknown items.**\n"

        long_message += f"\nThe total loot value is: **{total_value:,}** gold coins."
        if has_marketable:
            long_message += f"\n💎 Items marked with this are used in imbuements and might be worth " \
                            f"more in the market."
        embed.description = long_message
        embed.set_image(url="attachment://results.png")

        # Short message
        short_message = f"I've finished parsing your image {ctx.author.mention}." \
                        f"\nThe total value is {total_value:,} gold coins."
        if not ctx.long:
            short_message += "\nI've also sent you a PM with detailed information."

        # Send on ask_channel or PM
        if ctx.long:
            await ctx.send(short_message,
                           embed=embed,
                           file=discord.File(loot_image_overlay,
                                             "results.png"))
        else:
            try:
                await ctx.author.send(file=discord.File(
                    loot_image_overlay, "results.png"),
                                      embed=embed)
            except discord.Forbidden:
                await ctx.send(
                    f"{ctx.tick(False)} {ctx.author.mention}, I tried pming you to send you the results, "
                    f"but you don't allow private messages from this server.\n"
                    f"Enable the option and try again, or try the command channel"
                )
            else:
                await ctx.send(short_message)