Skip to main content

nextcord-ext-menus

Discord server invite PyPI version info PyPI supported Python versions

A Nextcord extension that makes working with reaction menus and button component menus a bit easier.

pages

Installing

Requirements

Python 3.8 or higher is required

It is necessary to first install Nextcord

Then install the extension by running one of the following commands:

# Linux/macOS
python3 -m pip install -U nextcord-ext-menus

# Windows
py -3 -m pip install -U nextcord-ext-menus

Reaction Menus

To whet your appetite, the following examples show the fundamentals on how to create menus.

The first example shows a basic menu that has a stop button and two reply buttons:

from nextcord.ext import menus

class MyMenu(menus.Menu):
    async def send_initial_message(self, ctx, channel):
        return await channel.send(f'Hello {ctx.author}')

    @menus.button('\N{THUMBS UP SIGN}')
    async def on_thumbs_up(self, payload):
        await self.message.edit(content=f'Thanks {self.ctx.author}!')

    @menus.button('\N{THUMBS DOWN SIGN}')
    async def on_thumbs_down(self, payload):
        await self.message.edit(content=f"That's not nice {self.ctx.author}...")

    @menus.button('\N{BLACK SQUARE FOR STOP}\ufe0f')
    async def on_stop(self, payload):
        self.stop()

Now, within a command we just instantiate it and we start it like so:

@bot.command()
async def menu_example(ctx):
    await MyMenu().start(ctx)

If an error happens then an exception of type menus.MenuError is raised.

This second example shows a confirmation menu and how we can compose it and use it later:

from nextcord.ext import menus

class Confirm(menus.Menu):
    def __init__(self, msg):
        super().__init__(timeout=30.0, delete_message_after=True)
        self.msg = msg
        self.result = None

    async def send_initial_message(self, ctx, channel):
        return await channel.send(self.msg)

    @menus.button('\N{WHITE HEAVY CHECK MARK}')
    async def do_confirm(self, payload):
        self.result = True
        self.stop()

    @menus.button('\N{CROSS MARK}')
    async def do_deny(self, payload):
        self.result = False
        self.stop()

    async def prompt(self, ctx):
        await self.start(ctx, wait=True)
        return self.result

Then when it comes time to use it we can do it like so:

@bot.command()
async def delete_things(ctx):
    confirm = await Confirm('Delete everything?').prompt(ctx)
    if confirm:
        await ctx.send('deleted...')

Pagination

The meat of the library is the Menu class but a MenuPages class is provided for the common use case of actually making a pagination session.

The MenuPages works similar to Menu except things are separated into a PageSource. The actual MenuPages rarely needs to be modified, instead we pass in a PageSource that deals with the data representation and formatting of the data we want to paginate.

The library comes with a few built-in page sources:

  • ListPageSource: The basic source that deals with a list of items.
  • GroupByPageSource: A page source that groups a list into multiple sublists similar to itertools.groupby.
  • AsyncIteratorPageSource: A page source that works with async iterators for lazy fetching of data.

None of these page sources deal with formatting of data, leaving that up to you.

For the sake of example, here's a basic list source that is paginated:

from nextcord.ext import menus

class MySource(menus.ListPageSource):
    def __init__(self, data):
        super().__init__(data, per_page=4)

    async def format_page(self, menu, entries):
        offset = menu.current_page * self.per_page
        return '\n'.join(f'{i}. {v}' for i, v in enumerate(entries, start=offset))

# somewhere else:
pages = menus.MenuPages(source=MySource(range(1, 100)), clear_reactions_after=True)
await pages.start(ctx)

The format_page can return either a str for content, nextcord.Embed for an embed, or a dict to pass into the kwargs of Message.edit.

Some more examples using GroupByPageSource:

from nextcord.ext import menus

class Test:
    def __init__(self, key, value):
        self.key = key
        self.value = value

data = [
    Test(key=key, value=value)
    for key in ['test', 'other', 'okay']
    for value in range(20)
]

class Source(menus.GroupByPageSource):
    async def format_page(self, menu, entry):
        joined = '\n'.join(f'{i}. <Test value={v.value}>' for i, v in enumerate(entry.items, start=1))
        return f'**{entry.key}**\n{joined}\nPage {menu.current_page + 1}/{self.get_max_pages()}'

pages = menus.MenuPages(source=Source(data, key=lambda t: t.key, per_page=12), clear_reactions_after=True)
await pages.start(ctx)

Another one showing AsyncIteratorPageSource:

from nextcord.ext import menus

class Test:
    def __init__(self, value):
        self.value = value

    def __repr__(self):
        return f'<Test value={self.value}>'

async def generate(number):
    for i in range(number):
        yield Test(i)

class Source(menus.AsyncIteratorPageSource):
    def __init__(self):
        super().__init__(generate(9), per_page=4)

    async def format_page(self, menu, entries):
        start = menu.current_page * self.per_page
        return f'\n'.join(f'{i}. {v!r}' for i, v in enumerate(entries, start=start))

pages = menus.MenuPages(source=Source(), clear_reactions_after=True)
await pages.start(ctx)

Button Component Menus

Here is a button implementation of a basic menu that has a stop button and two reply reactions.

Note that the ButtonMenu class is used instead of Menu in order to make it a View. ButtonMenu is a subclass of Menu and it therefore has all the same attributes and methods.

Also note that view=self is passed with the initial message and nextcord.ui.button is used instead of menus.button.

ButtonMenu.disable can be used to disable all buttons in the menu.

ButtonMenu.enable can be used to enable all buttons in the menu.

import nextcord
from nextcord.ext import menus

class MyButtonMenu(menus.ButtonMenu):
    async def send_initial_message(self, ctx, channel):
        return await channel.send(f'Hello {ctx.author}', view=self)

    @nextcord.ui.button(emoji="\N{THUMBS UP SIGN}")
    async def on_thumbs_up(self, button, interaction):
        await self.message.edit(content=f"Thanks {interaction.user}!")

    @nextcord.ui.button(emoji="\N{THUMBS DOWN SIGN}")
    async def on_thumbs_down(self, button, interaction):
        await self.message.edit(content=f"That's not nice {interaction.user}...")

    @nextcord.ui.button(emoji="\N{BLACK SQUARE FOR STOP}\ufe0f")
    async def on_stop(self, button, interaction):
        await self.disable()
        self.stop()

Instantiation is the same as above.

await MyButtonMenu().start(ctx)

Pagination

A ButtonMenuPages class is provided for pagination with button components.

ButtonMenuPages works the same way as the MenuPages class found above, but with button components instead of reactions.

A ButtonStyle can optionally be passed in to customize the appearance of the buttons.

MySource is the same as defined above, but the menu is instantiated with:

pages = menus.ButtonMenuPages(source=MySource(range(1, 100)), clear_buttons_after=True, 
                              style=nextcord.ButtonStyle.primary)
await pages.start(ctx)

License

Copyright (c) 2021 The Nextcord Developers
Copyright (c) 2015-2020 Danny Y. (Rapptz)

Metadata

Release files for nextcord-ext-menus 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nextcord-ext-menus 1.2.0
File Size Uploaded
nextcord-ext-menus-1.2.0.tar.gz 19.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nextcord-ext-menus 1.2.0
File Interpreter ABI Platform
nextcord_ext_menus-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 38.7 kB

Release files / nextcord-ext-menus-1.2.0.tar.gz

Download URL nextcord-ext-menus-1.2.0.tar.gz
Size 19.4 kB
Tags Source
SHA-256 checksum
How to use checksums
8b40993c15a74fbf8c2d4725e6f13a1d34e2a4231ba834cc24b3a1873c07e623
BLAKE2b-256 checksum
How to use checksums
9e07f3d9e46e9464e1ccfb88563d984bfadbac9c287a11732e60b1eee0df95a8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.2 CPython/3.9.7

Release files / nextcord_ext_menus-1.2.0-py3-none-any.whl

Download URL nextcord_ext_menus-1.2.0-py3-none-any.whl
Size 19.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7896844d58dbe608095b715322957e3898fd1cb7274b9373931ddc197fbeee0e
BLAKE2b-256 checksum
How to use checksums
4ba4ca409623af9e3623c62b6a523eddc3a756596a540f64f90641b812c34656
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.2 CPython/3.9.7

Release history Release notifications | RSS feed

1.5.7

2 release files

1.5.6

2 release files

1.5.5

2 release files

1.5.4

2 release files

1.5.3

2 release files

1.5.2

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.2

2 release files

1.2.1

2 release files

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page