예제 #1
0
    async def search(self, ctx, **flags):
        """Search pokémon from auctions."""

        if flags["page"] < 1:
            return await ctx.send("Page must be positive!")

        aggregations = await self.bot.get_cog("Pokemon").create_filter(
            flags, ctx, order_by=flags["order"]
        )

        if aggregations is None:
            return

        # Filter pokemon

        now = datetime.utcnow()

        def padn(p, n):
            return " " * (len(str(n)) - len(str(p.id))) + str(p.id)

        def prepare_page(menu, items):
            menu.maxn = max(x.id for x in items)

        def format_item(menu, x):
            if x.bidder_id is not None:
                return (
                    f"`{padn(x, menu.maxn)}` **{x.pokemon:Li}** • "
                    f"{x.pokemon.iv_total / 186:.2%} • CB: {x.current_bid:,} • "
                    f"BI: {x.bid_increment:,} pc • {converters.strfdelta(x.ends - now, max_len=1)}"
                )
            else:
                return (
                    f"`{padn(x, menu.maxn)}` **{x.pokemon:Li}** • "
                    f"{x.pokemon.iv_total / 186:.2%} • SB: {x.current_bid + x.bid_increment:,} pc • "
                    f"{converters.strfdelta(x.ends - now, max_len=1)}"
                )

        count = await self.bot.mongo.fetch_auction_count(ctx.guild, aggregations)
        pokemon = self.bot.mongo.fetch_auction_list(ctx.guild, aggregations)

        pages = pagination.ContinuablePages(
            pagination.AsyncListPageSource(
                pokemon,
                title=f"Auctions in {ctx.guild.name}",
                prepare_page=prepare_page,
                format_item=format_item,
                per_page=15,
                count=count,
            )
        )
        pages.current_page = flags["page"] - 1
        self.bot.menus[ctx.author.id] = pages

        try:
            await pages.start(ctx)
        except IndexError:
            await ctx.send("No auctions found.")
예제 #2
0
파일: help.py 프로젝트: vehmir/poketwo
    async def send_bot_help(self, mapping):
        ctx = self.context
        ctx.invoked_with = "help"
        bot = ctx.bot

        def get_category(command):
            cog = command.cog
            return cog.qualified_name if cog is not None else "\u200bNo Category"

        embed_pages = []
        total = 0

        filtered = await self.filter_commands(bot.commands,
                                              sort=True,
                                              key=get_category)

        for cog_name, commands in itertools.groupby(filtered,
                                                    key=get_category):
            commands = sorted(commands, key=lambda c: c.name)

            if len(commands) == 0:
                continue

            total += len(commands)
            cog = bot.get_cog(cog_name)
            description = ((cog and cog.description) if
                           (cog and cog.description) is not None else
                           discord.Embed.Empty)
            embed_pages.append((cog, description, commands))

        async def get_page(source, menu, pidx):
            cogs = embed_pages[min(len(embed_pages) - 1, pidx *
                                   6):min(len(embed_pages) - 1, pidx * 6 + 6)]

            embed = self.make_default_embed(
                cogs,
                title=
                f"Pokétwo Command Categories (Page {pidx+1}/{len(embed_pages)//6+1})",
                description=
                (f"Use `{self.clean_prefix}help <command>` for more info on a command.\n"
                 f"Use `{self.clean_prefix}help <category>` for more info on a category."
                 ),
            )

            return embed

        pages = pagination.ContinuablePages(
            pagination.FunctionPageSource(math.ceil(len(embed_pages) / 6),
                                          get_page))
        ctx.bot.menus[ctx.author.id] = pages
        await pages.start(ctx)
예제 #3
0
    async def moveset(self, ctx, *, search: str):
        """View all moves for your pokémon and how to get them."""

        search = search.strip()

        if len(search) > 0 and search[0] in "Nn#" and search[1:].isdigit():
            species = self.bot.data.species_by_number(int(search[1:]))
        else:
            species = self.bot.data.species_by_name(search)

            if species is None:
                converter = converters.PokemonConverter(raise_errors=False)
                pokemon = await converter.convert(ctx, search)
                if pokemon is not None:
                    species = pokemon.species

        if species is None:
            raise commands.BadArgument(
                "Please either enter the name of a pokémon species, nothing for your selected pokémon, a number for "
                "a specific pokémon, `latest` for your latest pokémon. ", )

        async def get_page(source, menu, pidx):
            pgstart = pidx * 20
            pgend = min(pgstart + 20, len(species.moves))

            # Send embed

            embed = discord.Embed(color=0x9CCFFF)
            embed.title = f"{species} — Moveset"

            embed.set_footer(
                text=
                f"Showing {pgstart + 1}–{pgend} out of {len(species.moves)}.")

            for move in species.moves[pgstart:pgend]:
                embed.add_field(name=move.move.name, value=move.text)

            for i in range(-pgend % 3):
                embed.add_field(name="‎", value="‎")

            return embed

        pages = pagination.ContinuablePages(
            pagination.FunctionPageSource(math.ceil(len(species.moves) / 20),
                                          get_page))
        self.bot.menus[ctx.author.id] = pages
        await pages.start(ctx)
예제 #4
0
    async def pokemon(self, ctx, **flags):
        """View or filter the pokémon in your collection."""

        if flags["page"] < 1:
            return await ctx.send("Page must be positive!")

        member = await self.bot.mongo.fetch_member_info(ctx.author)

        aggregations = await self.create_filter(flags,
                                                ctx,
                                                order_by=member.order_by)
        if aggregations is None:
            return

        # Filter pokemon

        def padn(p, n):
            return " " * (len(str(n)) - len(str(p.idx))) + str(p.idx)

        def prepare_page(menu, items):
            menu.maxn = max(x.idx for x in items)

        def format_item(menu, p):
            return f"`{padn(p, menu.maxn)}` **{p:nif}** • Lvl. {p.level} • {p.iv_total / 186:.2%}"

        count = await self.bot.mongo.fetch_pokemon_count(
            ctx.author, aggregations)
        pokemon = self.bot.mongo.fetch_pokemon_list(ctx.author, aggregations)

        pages = pagination.ContinuablePages(
            pagination.AsyncListPageSource(
                pokemon,
                title="Your pokémon",
                prepare_page=prepare_page,
                format_item=format_item,
                per_page=20,
                count=count,
            ))
        pages.current_page = flags["page"] - 1
        self.bot.menus[ctx.author.id] = pages

        try:
            await pages.start(ctx)
        except IndexError:
            await ctx.send("No pokémon found.")
예제 #5
0
    async def search(self, ctx, **flags):
        """Search pokémon from the marketplace."""
        def map_field(field):
            if field == "_id":
                return f"market_data._id"
            return field

        aggregations = await self.bot.get_cog("Pokemon").create_filter(
            flags, ctx, order_by=flags["order"], map_field=map_field)

        if aggregations is None:
            return

        # Filter pokemon

        def padn(p, n):
            return " " * (len(str(n)) - len(str(p))) + str(p)

        def prepare_page(menu, items):
            menu.maxn = max(x["market_data"]["_id"] for x in items)

        def format_item(menu, x):
            pokemon = self.bot.mongo.Pokemon.build_from_mongo(x)
            return f"`{padn(x['market_data']['_id'], menu.maxn)}` **{pokemon:li}** • {pokemon.iv_total / 186:.2%} • {x['market_data']['price']:,} pc"

        pokemon = self.bot.mongo.fetch_market_list(aggregations)

        pages = pagination.ContinuablePages(
            pagination.AsyncListPageSource(
                pokemon,
                title=f"Pokétwo Marketplace",
                prepare_page=prepare_page,
                format_item=format_item,
                per_page=20,
            ),
            allow_last=False,
            allow_go=False,
        )
        self.bot.menus[ctx.author.id] = pages

        try:
            await pages.start(ctx)
        except IndexError:
            await ctx.send("No listings found.")
예제 #6
0
파일: market.py 프로젝트: vehmir/poketwo
    async def search(self, ctx, **flags):
        """Search pokémon from the marketplace."""

        if flags["page"] < 1:
            return await ctx.send("Page must be positive!")

        aggregations = await self.bot.get_cog("Pokemon").create_filter(
            flags, ctx, order_by=flags["order"])

        if aggregations is None:
            return

        # Filter pokemon

        def padn(p, n):
            return " " * (len(str(n)) - len(str(p.id))) + str(p.id)

        def prepare_page(menu, items):
            menu.maxn = max(x.id for x in items)

        def format_item(menu, x):
            return f"`{padn(x, menu.maxn)}` **{x.pokemon:li}** • {x.pokemon.iv_total / 186:.2%} • {x.price:,} pc"

        pokemon = self.bot.mongo.fetch_market_list(aggregations)

        pages = pagination.ContinuablePages(
            pagination.AsyncListPageSource(
                pokemon,
                title=f"Pokétwo Marketplace",
                prepare_page=prepare_page,
                format_item=format_item,
                per_page=20,
            ),
            allow_last=False,
            allow_go=False,
        )
        pages.current_page = flags["page"] - 1
        self.bot.menus[ctx.author.id] = pages

        try:
            await pages.start(ctx)
        except IndexError:
            await ctx.send("No listings found.")
예제 #7
0
    async def info(self, ctx, *, pokemon: converters.PokemonConverter):
        """View a specific pokémon from your collection."""

        if pokemon is None:
            return await ctx.send("Couldn't find that pokémon!")

        ## Hacky way using 0=first, 1=prev, 2=curr, 3=next, 4=last page LOL

        async def get_page(source, menu, pidx):
            nonlocal pokemon

            menu.current_page = 2

            agg = None

            if pidx == 4:
                agg = [{"$sort": {"idx": -1}}]
            elif pidx == 3:
                agg = [{"$match": {"idx": {"$gt": pokemon.idx}}}]
            elif pidx == 1:
                agg = [
                    {
                        "$match": {
                            "idx": {
                                "$lt": pokemon.idx
                            }
                        }
                    },
                    {
                        "$sort": {
                            "idx": -1
                        }
                    },
                ]
            elif pidx == 0:
                agg = []

            if agg is not None:
                it = self.bot.mongo.fetch_pokemon_list(ctx.author, agg)
                async for x in it:
                    pokemon = x
                    break

            embed = self.bot.Embed(color=pokemon.color or 0x9CCFFF)
            embed.title = f"{pokemon:lnf}"

            if pokemon.shiny:
                embed.set_image(url=pokemon.species.shiny_image_url)
            else:
                embed.set_image(url=pokemon.species.image_url)

            embed.set_thumbnail(url=ctx.author.avatar_url)

            info = (
                f"**XP:** {pokemon.xp}/{pokemon.max_xp}",
                f"**Nature:** {pokemon.nature}",
            )

            embed.add_field(name="Details",
                            value="\n".join(info),
                            inline=False)

            stats = (
                f"**HP:** {pokemon.hp} – IV: {pokemon.iv_hp}/31",
                f"**Attack:** {pokemon.atk} – IV: {pokemon.iv_atk}/31",
                f"**Defense:** {pokemon.defn} – IV: {pokemon.iv_defn}/31",
                f"**Sp. Atk:** {pokemon.satk} – IV: {pokemon.iv_satk}/31",
                f"**Sp. Def:** {pokemon.sdef} – IV: {pokemon.iv_sdef}/31",
                f"**Speed:** {pokemon.spd} – IV: {pokemon.iv_spd}/31",
                f"**Total IV:** {pokemon.iv_percentage * 100:.2f}%",
            )

            embed.add_field(name="Stats", value="\n".join(stats), inline=False)

            if pokemon.held_item:
                item = self.bot.data.item_by_number(pokemon.held_item)
                emote = ""
                if item.emote is not None:
                    emote = getattr(self.bot.sprites, item.emote) + " "
                embed.add_field(name="Held Item",
                                value=f"{emote}{item.name}",
                                inline=False)

            embed.set_footer(
                text=f"Displaying pokémon {pokemon.idx}.\nID: {pokemon.id}")

            return embed

        pages = pagination.ContinuablePages(pagination.FunctionPageSource(
            5, get_page),
                                            allow_go=False)
        pages.current_page = 2
        ctx.bot.menus[ctx.author.id] = pages
        await pages.start(ctx)
예제 #8
0
    async def pokedex(self, ctx, **flags):
        """View your pokédex, or search for a pokémon species."""

        search_or_page = " ".join(flags["page"])

        if flags["orderd"] and flags["ordera"]:
            return await ctx.send(
                "You can use either --orderd or --ordera, but not both.")

        if flags["caught"] and flags["uncaught"]:
            return await ctx.send(
                "You can use either --caught or --uncaught, but not both.")

        if flags["mythical"] + flags["legendary"] + flags["ub"] > 1:
            return await ctx.send("You can't use more than one rarity flag!")

        if search_or_page is None:
            search_or_page = "1"

        if search_or_page.isdigit():
            pgstart = (int(search_or_page) - 1) * 20

            if pgstart >= 809 or pgstart < 0:
                return await ctx.send("There are no pokémon on this page.")

            num = await self.bot.mongo.fetch_pokedex_count(ctx.author)

            do_emojis = (ctx.guild is None or ctx.guild.me.permissions_in(
                ctx.channel).external_emojis)

            member = await self.bot.mongo.fetch_pokedex(ctx.author, 0, 810)
            pokedex = member.pokedex

            if not flags["uncaught"] and not flags["caught"]:
                for i in range(1, 810):
                    if str(i) not in pokedex:
                        pokedex[str(i)] = 0
            elif flags["uncaught"]:
                for i in range(1, 810):
                    if str(i) not in pokedex:
                        pokedex[str(i)] = 0
                    else:
                        del pokedex[str(i)]

            def include(key):
                if flags[
                        "legendary"] and key not in self.bot.data.list_legendary:
                    return False
                if flags["mythical"] and key not in self.bot.data.list_mythical:
                    return False
                if flags["ub"] and key not in self.bot.data.list_ub:
                    return False

                if flags["type"] and key not in self.bot.data.list_type(
                        flags["type"]):
                    return False

                return True

            pokedex = {
                int(k): v
                for k, v in pokedex.items() if include(int(k))
            }

            if flags["ordera"]:
                pokedex = sorted(pokedex.items(), key=itemgetter(1))
            elif flags["orderd"]:
                pokedex = sorted(pokedex.items(),
                                 key=itemgetter(1),
                                 reverse=True)
            else:
                pokedex = sorted(pokedex.items(), key=itemgetter(0))

            async def get_page(source, menu, pidx):
                pgstart = pidx * 20
                pgend = min(pgstart + 20, len(pokedex))

                # Send embed

                embed = self.bot.Embed(color=0x9CCFFF)
                embed.title = f"Your pokédex"
                embed.description = f"You've caught {num} out of 809 pokémon!"

                embed.set_footer(
                    text=f"Showing {pgstart + 1}–{pgend} out of {len(pokedex)}."
                )

                # embed.description = (
                #     f"You've caught {len(member.pokedex)} out of 809 pokémon!"
                # )

                for k, v in pokedex[pgstart:pgend]:
                    species = self.bot.data.species_by_number(k)

                    if do_emojis:
                        text = f"{self.bot.sprites.cross} Not caught yet!"
                    else:
                        text = "Not caught yet!"

                    if v > 0:
                        if do_emojis:
                            text = f"{self.bot.sprites.check} {v} caught!"
                        else:
                            text = f"{v} caught!"

                    if do_emojis:
                        emoji = self.bot.sprites.get(k) + " "
                    else:
                        emoji = ""

                    embed.add_field(
                        name=f"{emoji}{species.name} #{species.id}",
                        value=text)

                if pgend != 809:
                    embed.add_field(name="‎", value="‎")

                return embed

            pages = pagination.ContinuablePages(
                pagination.FunctionPageSource(math.ceil(809 / 20), get_page))
            pages.current_page = int(search_or_page) - 1
            self.bot.menus[ctx.author.id] = pages
            await pages.start(ctx)

        else:
            shiny = False

            if search_or_page[0] in "Nn#" and search_or_page[1:].isdigit():
                species = self.bot.data.species_by_number(
                    int(search_or_page[1:]))

            else:
                search = search_or_page

                if search_or_page.lower().startswith("shiny "):
                    shiny = True
                    search = search_or_page[6:]

                species = self.bot.data.species_by_name(search)
                if species is None:
                    return await ctx.send(
                        f"Could not find a pokemon matching `{search_or_page}`."
                    )

            member = await self.bot.mongo.fetch_pokedex(
                ctx.author, species.dex_number, species.dex_number + 1)

            embed = self.bot.Embed(color=0x9CCFFF)
            embed.title = f"#{species.dex_number} — {species}"

            if species.description:
                embed.description = species.description.replace("\n", " ")

            if species.evolution_text:
                embed.add_field(name="Evolution",
                                value=species.evolution_text,
                                inline=False)

            if shiny:
                embed.title = f"#{species.dex_number} — ✨ {species}"
                embed.set_image(url=species.shiny_image_url)
            else:
                embed.set_image(url=species.image_url)

            base_stats = (
                f"**HP:** {species.base_stats.hp}",
                f"**Attack:** {species.base_stats.atk}",
                f"**Defense:** {species.base_stats.defn}",
                f"**Sp. Atk:** {species.base_stats.satk}",
                f"**Sp. Def:** {species.base_stats.sdef}",
                f"**Speed:** {species.base_stats.spd}",
            )

            embed.add_field(
                name="Names",
                value="\n".join(f"{x} {y}" for x, y in species.names),
                inline=False,
            )
            embed.add_field(name="Base Stats", value="\n".join(base_stats))
            embed.add_field(
                name="Appearance",
                value=
                f"Height: {species.height} m\nWeight: {species.weight} kg",
            )
            embed.add_field(name="Types", value="\n".join(species.types))

            text = "You haven't caught this pokémon yet!"
            if str(species.dex_number) in member.pokedex:
                text = f"You've caught {member.pokedex[str(species.dex_number)]} of this pokémon!"

            embed.set_footer(text=text)

            await ctx.send(embed=embed)
예제 #9
0
    async def send_trade(self, ctx, user: discord.Member):
        # TODO this code is pretty shit. although it does work

        trade = self.bot.trades[user.id]
        a, b = trade["items"].keys()

        done = False

        if trade[a] and trade[b] and not trade["executing"]:
            done = True
            trade["executing"] = True

        a = ctx.guild.get_member(a) or await ctx.guild.fetch_member(a)
        b = ctx.guild.get_member(b) or await ctx.guild.fetch_member(b)

        num_pages = max(
            math.ceil(len(x) / 20) for x in trade["items"].values())

        if done:
            execmsg = await ctx.send("Executing trade...")

        async def get_page(source, menu, pidx):
            embed = self.bot.Embed(color=0x9CCFFF)
            embed.title = f"Trade between {a.display_name} and {b.display_name}."

            if done:
                embed.title = (
                    f"✅ Completed trade between {a.display_name} and {b.display_name}."
                )

            for i, fullside in trade["items"].items():
                mem = ctx.guild.get_member(i) or await ctx.guild.fetch_member(i
                                                                              )

                side = fullside[pidx * 20:(pidx + 1) * 20]

                if mem is None:
                    return await ctx.send("The trade has been canceled.")

                try:
                    maxn = max(x.idx for x in side if type(x) != int)
                except ValueError:
                    maxn = 0

                def padn(idx, n):
                    return " " * (len(str(n)) - len(str(idx))) + str(idx)

                def txt(p):
                    val = f"`{padn(p.idx, maxn)}` **{p.species}**"
                    if p.shiny:
                        val = f"`{padn(p.idx, maxn)}` **✨ {p.species}**"
                    val += f" • Lvl. {p.level} • {p.iv_percentage:.2%}"
                    return val

                val = "\n".join(
                    f"{x:,} Pokécoins" if type(x) == int else txt(x)
                    for x in side)

                if val == "":
                    if len(fullside) == 0:
                        val = "None"
                    else:
                        val = "None on this page"

                sign = "🟢" if trade[i] else "🔴"

                embed.add_field(name=f"{sign} {mem.display_name}", value=val)

            embed.set_footer(
                text=f"Showing page {pidx + 1} out of {num_pages}.")

            return embed

        # Check if done

        embeds = []

        if done:
            try:
                bothsides = list(enumerate(trade["items"].items()))

                for idx, tup in bothsides:
                    i, side = tup
                    mem = ctx.guild.get_member(
                        i) or await ctx.guild.fetch_member(i)
                    member = await self.bot.mongo.fetch_member_info(mem)
                    if member.balance < sum(x for x in side if type(x) == int):
                        await ctx.send(
                            "The trade could not be executed as one user does not have enough Pokécoins."
                        )
                        await self.end_trade(a.id)
                        return

                for idx, tup in bothsides:
                    i, side = tup

                    oidx, otup = bothsides[(idx + 1) % 2]
                    oi, oside = otup

                    mem = ctx.guild.get_member(
                        i) or await ctx.guild.fetch_member(i)
                    omem = ctx.guild.get_member(
                        oi) or await ctx.guild.fetch_member(oi)

                    member = await self.bot.mongo.fetch_member_info(mem)
                    omember = await self.bot.mongo.fetch_member_info(omem)

                    idxs = set()

                    num_pokes = len(list(x for x in side if type(x) != int))
                    idx = await self.bot.mongo.fetch_next_idx(omem, num_pokes)

                    for x in side:
                        if type(x) == int:
                            await self.bot.mongo.update_member(
                                mem, {"$inc": {
                                    "balance": -x
                                }})
                            await self.bot.mongo.update_member(
                                omem, {"$inc": {
                                    "balance": x
                                }})
                        else:

                            pokemon = x

                            if pokemon.idx in idxs:
                                continue

                            idxs.add(pokemon.idx)

                            update = {
                                "$set": {
                                    "owner_id": omem.id,
                                    "idx": idx,
                                }
                            }
                            idx += 1

                            if pokemon.held_item != 13001:
                                evos = [
                                    evo
                                    for evo in pokemon.species.trade_evolutions
                                    if (evo.trigger.item is None or evo.
                                        trigger.item.id == pokemon.held_item)
                                ]

                                if len(evos) > 0:
                                    evo = random.choice(evos)

                                    evo_embed = self.bot.Embed(color=0x9CCFFF)
                                    evo_embed.title = (
                                        f"Congratulations {mem.display_name}!")

                                    name = str(pokemon.species)

                                    if pokemon.nickname is not None:
                                        name += f' "{pokemon.nickname}"'

                                    evo_embed.add_field(
                                        name=f"The {name} is evolving!",
                                        value=
                                        f"The {name} has turned into a {evo.target}!",
                                    )

                                    self.bot.dispatch("evolve", mem, pokemon,
                                                      evo.target)
                                    self.bot.dispatch("evolve", omem, pokemon,
                                                      evo.target)

                                    update["$set"][
                                        "species_id"] = evo.target.id

                                    embeds.append(evo_embed)

                            await self.bot.mongo.update_pokemon(
                                pokemon,
                                update,
                            )

            except:
                await self.end_trade(a.id)
                raise

            try:
                await execmsg.delete()
            except:
                pass

            try:
                await self.bot.mongo.db.logs.insert_one({
                    "event": "trade",
                    "users": [a.id, b.id],
                    "items": {
                        str(a.id): [
                            x if type(x) == int else x.id
                            for x in trade["items"][a.id]
                        ],
                        str(b.id): [
                            x if type(x) == int else x.id
                            for x in trade["items"][b.id]
                        ],
                    },
                })
            except:
                print("Error saving trading logs.")
                pass

            await self.end_trade(a.id)

        # Send msg

        pages = pagination.ContinuablePages(
            pagination.FunctionPageSource(num_pages, get_page))
        self.bot.menus[a.id] = pages
        self.bot.menus[b.id] = pages
        await pages.start(ctx)

        for evo_embed in embeds:
            await ctx.send(embed=evo_embed)
예제 #10
0
파일: trading.py 프로젝트: jinai/poketwo
    async def send_trade(self, ctx, user: discord.Member):
        # TODO this code is pretty shit. although it does work

        trade = self.bot.trades[user.id]
        a, b = trade["users"]

        done = False

        if trade[a.id] and trade[b.id] and not trade["executing"]:
            done = True
            trade["executing"] = True

        num_pages = max(math.ceil(len(x) / 20) for x in trade["pokemon"].values())

        if done:
            execmsg = await ctx.send("Executing trade...")

        users = {k: [("p", x) for x in v] for k, v in trade["pokemon"].items()}
        for x in users:
            if trade["redeems"][x] > 0:
                users[x].insert(0, ("r", trade["redeems"][x]))
            if trade["pokecoins"][x] > 0:
                users[x].insert(0, ("c", trade["pokecoins"][x]))

        embed_pages = list(zip_longest(*[list(chunks(x, 20)) for x in users.values()]))

        if len(embed_pages) == 0:
            embed_pages = [[[], []]]

        async def get_page(source, menu, pidx):
            embed = self.bot.Embed(title=f"Trade between {a.display_name} and {b.display_name}.")

            if done:
                embed.title = f"✅ Completed trade between {a.display_name} and {b.display_name}."

            for mem, page in zip((a, b), embed_pages[pidx]):
                try:
                    maxn = max(x.idx for t, x in page or [] if t == "p")
                except ValueError:
                    maxn = 0

                def padn(idx, n):
                    return " " * (len(str(n)) - len(str(idx))) + str(idx)

                def txt(p):
                    val = f"`{padn(p.idx, maxn)}` **{p.species}**"
                    if p.shiny:
                        val = f"`{padn(p.idx, maxn)}` **✨ {p.species}**"
                    val += f" • Lvl. {p.level} • {p.iv_percentage:.2%}"
                    return val

                val = "\n".join(
                    f"{x:,} Pokécoins" if t == "c" else f"{x:,} redeems" if t == "r" else txt(x) for t, x in page or []
                )

                if val == "":
                    if len(users[mem.id]) == 0:
                        val = "None"
                    else:
                        val = "None on this page"

                sign = "🟢" if trade[mem.id] else "🔴"

                embed.add_field(name=f"{sign} {mem.display_name}", value=val)

            embed.set_footer(
                text=f"Showing page {pidx + 1} out of {num_pages}.\nReminder: Trading Pokécoins or Pokémon for real-life currencies or items in other bots is prohibited and will result in the suspension of your Pokétwo account!"
            )

            return embed

        # Check if done

        embeds = []

        if done:
            try:
                bothsides = list(enumerate(trade["pokemon"].items()))

                for u in trade["users"]:
                    member = await self.bot.mongo.fetch_member_info(u)
                    if member.balance < trade["pokecoins"][u.id]:
                        await ctx.send("The trade could not be executed as one user does not have enough Pokécoins.")
                        await self.end_trade(a.id)
                        return
                    if member.redeems < trade["redeems"][u.id]:
                        await ctx.send("The trade could not be executed as one user does not have enough redeems.")
                        await self.end_trade(a.id)
                        return

                for idx, tup in bothsides:
                    i, side = tup

                    oidx, otup = bothsides[(idx + 1) % 2]
                    oi, oside = otup

                    mem = ctx.guild.get_member(i) or await ctx.guild.fetch_member(i)
                    omem = ctx.guild.get_member(oi) or await ctx.guild.fetch_member(oi)

                    member = await self.bot.mongo.fetch_member_info(mem)
                    omember = await self.bot.mongo.fetch_member_info(omem)

                    idxs = set()

                    num_pokes = len(list(x for x in side if type(x) != int))
                    idx = await self.bot.mongo.fetch_next_idx(omem, num_pokes)

                    if trade["pokecoins"][i] > 0:
                        await self.bot.mongo.update_member(mem, {"$inc": {"balance": -trade["pokecoins"][i]}})
                        await self.bot.mongo.update_member(omem, {"$inc": {"balance": trade["pokecoins"][i]}})

                    if trade["redeems"][i] > 0:
                        await self.bot.mongo.update_member(mem, {"$inc": {"redeems": -trade["redeems"][i]}})
                        await self.bot.mongo.update_member(omem, {"$inc": {"redeems": trade["redeems"][i]}})

                    for x in side:
                        pokemon = x

                        if pokemon.idx in idxs:
                            continue

                        idxs.add(pokemon.idx)

                        update = {
                            "$set": {
                                "owner_id": omem.id,
                                "idx": idx,
                            }
                        }
                        idx += 1

                        if pokemon.held_item != 13001:
                            evos = [
                                evo
                                for evo in pokemon.species.trade_evolutions
                                if (evo.trigger.item is None or evo.trigger.item.id == pokemon.held_item)
                            ]

                            if len(evos) > 0:
                                evo = random.choice(evos)

                                evo_embed = self.bot.Embed(title=f"Congratulations {omem.display_name}!")

                                name = str(pokemon.species)

                                if pokemon.nickname is not None:
                                    name += f' "{pokemon.nickname}"'

                                evo_embed.add_field(
                                    name=f"The {name} is evolving!",
                                    value=f"The {name} has turned into a {evo.target}!",
                                )

                                self.bot.dispatch("evolve", mem, pokemon, evo.target)
                                self.bot.dispatch("evolve", omem, pokemon, evo.target)

                                update["$set"]["species_id"] = evo.target.id

                                embeds.append(evo_embed)

                        await self.bot.mongo.update_pokemon(
                            pokemon,
                            update,
                        )

            except:
                await self.end_trade(a.id)
                raise

            try:
                await execmsg.delete()
            except:
                pass

            try:
                await self.bot.mongo.db.logs.insert_one(
                    {
                        "event": "trade",
                        "users": [a.id, b.id],
                        "pokemon": {
                            str(a.id): [x.id for x in trade["pokemon"][a.id]],
                            str(b.id): [x.id for x in trade["pokemon"][b.id]],
                        },
                        "pokecoins": {
                            str(a.id): trade["pokecoins"][a.id],
                            str(b.id): trade["pokecoins"][b.id],
                        },
                        "redeems": {
                            str(a.id): trade["redeems"][a.id],
                            str(b.id): trade["redeems"][b.id],
                        },
                    }
                )
            except:
                pass

            await self.end_trade(a.id)

        # Send msg

        pages = pagination.ContinuablePages(pagination.FunctionPageSource(num_pages, get_page))
        self.bot.menus[a.id] = pages
        self.bot.menus[b.id] = pages
        if menu := trade.get("menu"):
            menu.stop()
            await menu.message.delete()