• Stars
    star
    232
  • Rank 172,847 (Top 4 %)
  • Language
    Python
  • License
    MIT License
  • Created almost 5 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

discord-ext-menus

An experimental extension menu that makes working with reaction menus a bit easier.

There are no front-facing docs for this and it's not on PyPI. As this is meant to be a repository for testing

Installing

Installing is done purely via git:

python -m pip install -U git+https://github.com/Rapptz/discord-ext-menus

Getting Started

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 discord.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):
    m = MyMenu()
    await m.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 discord.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 discord.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, discord.Embed for an embed, or a dict to pass into the kwargs of Message.edit.

Some more examples using GroupByPageSource:

from discord.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 discord.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)

More Repositories

1

discord.py

An API wrapper for Discord written in Python.
Python
14,368
star
2

RoboDanny

A discord bot for servers that I like.
Python
797
star
3

sol

A C++11 Lua wrapper
C++
205
star
4

asqlite

A simple async wrapper for sqlite3
Python
112
star
5

cpp-sublime-snippet

C++11 Sublime Text Snippets
JavaScript
92
star
6

eos

A simple and easy to use datetime library for Rust
Rust
51
star
7

jimaku

A site for hosting (Japanese) subtitles
Rust
38
star
8

discord-event-bot

Python
34
star
9

discord-ext-native-voice

Rust
33
star
10

Reddit-Imgur-Scraper

Python script that scraps images from imgur on reddit.
Python
30
star
11

DoxyDoc

A sublime text plugin for C++ document autocompletion
Python
27
star
12

power-tabs

Vertical tab groups for Firefox
JavaScript
25
star
13

Gears

C++ Utilities
C++
25
star
14

jsonpp

C++11 JSON parser and writer
C++
21
star
15

aoc2021

Solutions for Advent of Code 2021
Rust
15
star
16

open-collective-discord-auth

The server component for Open Collective Linked Role integration
TypeScript
15
star
17

gum

Thin C++11 SDL 2 wrapper with other nice things.
C++
15
star
18

google-lens-ocr

A simple CLI to invoke Google Lens OCR
Rust
6
star
19

Sky

SFML tools to aid in game development.
C++
5
star
20

sqlitexx

A C++14 SQLite3 wrapper
C++
4
star
21

Lia

List comprehensions for C++
C++
3
star
22

hacktober-cheese

Cheese
3
star
23

Shinobi

A meta build system for ninja
C++
2
star
24

sub-tools

Rust
1
star
25

git-utils

Scripts that I use for custom git commands.
Shell
1
star
26

compo-two

2nd Great Lounge Game Jam.
Lua
1
star
27

rapptz.github.io

Github page for my projects.
CSS
1
star
28

sen.py

A pseudo meta build system for ninja.
Python
1
star
29

splatoon

Splatoon data related nonsense.
1
star
30

Project-Euler

Project Euler solutions
C++
1
star
31

anitomy-rs

A pure Rust port of anitomy, an anime filename parser
Rust
1
star
32

libnet

A C++11 library for networking.
C++
1
star