{"id":16649488,"url":"https://github.com/rapptz/discord-ext-menus","last_synced_at":"2025-04-04T20:11:36.592Z","repository":{"id":66092933,"uuid":"231684722","full_name":"Rapptz/discord-ext-menus","owner":"Rapptz","description":null,"archived":false,"fork":false,"pushed_at":"2022-05-30T02:31:17.000Z","size":35,"stargazers_count":235,"open_issues_count":12,"forks_count":87,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-10-12T09:10:48.370Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Rapptz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-04T00:03:28.000Z","updated_at":"2024-10-07T19:11:50.000Z","dependencies_parsed_at":"2023-02-24T07:45:17.180Z","dependency_job_id":null,"html_url":"https://github.com/Rapptz/discord-ext-menus","commit_stats":{"total_commits":32,"total_committers":9,"mean_commits":"3.5555555555555554","dds":0.4375,"last_synced_commit":"8686b5d1bbc1d3c862292eb436ab630d6e9c9b53"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rapptz%2Fdiscord-ext-menus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rapptz%2Fdiscord-ext-menus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rapptz%2Fdiscord-ext-menus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rapptz%2Fdiscord-ext-menus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rapptz","download_url":"https://codeload.github.com/Rapptz/discord-ext-menus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247242678,"owners_count":20907134,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-12T09:10:37.934Z","updated_at":"2025-04-04T20:11:36.571Z","avatar_url":"https://github.com/Rapptz.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"### discord-ext-menus\n\nAn experimental extension menu that makes working with reaction menus a bit easier.\n\n**There are no front-facing docs for this and it's not on PyPI. As this is meant to be a repository for testing**\n\n## Installing\n\nInstalling is done purely via git:\n\n```py\npython -m pip install -U git+https://github.com/Rapptz/discord-ext-menus\n```\n\n## Getting Started\n\nTo whet your appetite, the following examples show the fundamentals on how to create menus.\n\nThe first example shows a basic menu that has a stop button and two reply buttons:\n\n```py\nfrom discord.ext import menus\n\nclass MyMenu(menus.Menu):\n    async def send_initial_message(self, ctx, channel):\n        return await channel.send(f'Hello {ctx.author}')\n\n    @menus.button('\\N{THUMBS UP SIGN}')\n    async def on_thumbs_up(self, payload):\n        await self.message.edit(content=f'Thanks {self.ctx.author}!')\n\n    @menus.button('\\N{THUMBS DOWN SIGN}')\n    async def on_thumbs_down(self, payload):\n        await self.message.edit(content=f\"That's not nice {self.ctx.author}...\")\n\n    @menus.button('\\N{BLACK SQUARE FOR STOP}\\ufe0f')\n    async def on_stop(self, payload):\n        self.stop()\n```\n\nNow, within a command we just instantiate it and we start it like so:\n\n```py\n@bot.command()\nasync def menu_example(ctx):\n    m = MyMenu()\n    await m.start(ctx)\n```\n\nIf an error happens then an exception of type `menus.MenuError` is raised.\n\nThis second example shows a confirmation menu and how we can compose it and use it later:\n\n```py\nfrom discord.ext import menus\n\nclass Confirm(menus.Menu):\n    def __init__(self, msg):\n        super().__init__(timeout=30.0, delete_message_after=True)\n        self.msg = msg\n        self.result = None\n\n    async def send_initial_message(self, ctx, channel):\n        return await channel.send(self.msg)\n\n    @menus.button('\\N{WHITE HEAVY CHECK MARK}')\n    async def do_confirm(self, payload):\n        self.result = True\n        self.stop()\n\n    @menus.button('\\N{CROSS MARK}')\n    async def do_deny(self, payload):\n        self.result = False\n        self.stop()\n\n    async def prompt(self, ctx):\n        await self.start(ctx, wait=True)\n        return self.result\n```\n\nThen when it comes time to use it we can do it like so:\n\n```py\n@bot.command()\nasync def delete_things(ctx):\n    confirm = await Confirm('Delete everything?').prompt(ctx)\n    if confirm:\n        await ctx.send('deleted...')\n```\n\n### Pagination\n\nThe 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.\n\nThe `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.\n\nThe library comes with a few built-in page sources:\n\n- `ListPageSource`: The basic source that deals with a list of items.\n- `GroupByPageSource`: A page source that groups a list into multiple sublists similar to `itertools.groupby`.\n- `AsyncIteratorPageSource`: A page source that works with async iterators for lazy fetching of data.\n\nNone of these page sources deal with formatting of data, leaving that up to you.\n\nFor the sake of example, here's a basic list source that is paginated:\n\n```py\nfrom discord.ext import menus\n\nclass MySource(menus.ListPageSource):\n    def __init__(self, data):\n        super().__init__(data, per_page=4)\n\n    async def format_page(self, menu, entries):\n        offset = menu.current_page * self.per_page\n        return '\\n'.join(f'{i}. {v}' for i, v in enumerate(entries, start=offset))\n\n# somewhere else:\npages = menus.MenuPages(source=MySource(range(1, 100)), clear_reactions_after=True)\nawait pages.start(ctx)\n```\n\nThe `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`.\n\nSome more examples using `GroupByPageSource`:\n\n```py\nfrom discord.ext import menus\n\nclass Test:\n    def __init__(self, key, value):\n        self.key = key\n        self.value = value\n\ndata = [\n    Test(key=key, value=value)\n    for key in ['test', 'other', 'okay']\n    for value in range(20)\n]\n\nclass Source(menus.GroupByPageSource):\n    async def format_page(self, menu, entry):\n        joined = '\\n'.join(f'{i}. \u003cTest value={v.value}\u003e' for i, v in enumerate(entry.items, start=1))\n        return f'**{entry.key}**\\n{joined}\\nPage {menu.current_page + 1}/{self.get_max_pages()}'\n\npages = menus.MenuPages(source=Source(data, key=lambda t: t.key, per_page=12), clear_reactions_after=True)\nawait pages.start(ctx)\n```\n\nAnother one showing `AsyncIteratorPageSource`:\n\n```py\nfrom discord.ext import menus\n\nclass Test:\n    def __init__(self, value):\n        self.value = value\n\n    def __repr__(self):\n        return f'\u003cTest value={self.value}\u003e'\n\nasync def generate(number):\n    for i in range(number):\n        yield Test(i)\n\nclass Source(menus.AsyncIteratorPageSource):\n    def __init__(self):\n        super().__init__(generate(9), per_page=4)\n\n    async def format_page(self, menu, entries):\n        start = menu.current_page * self.per_page\n        return f'\\n'.join(f'{i}. {v!r}' for i, v in enumerate(entries, start=start))\n\npages = menus.MenuPages(source=Source(), clear_reactions_after=True)\nawait pages.start(ctx)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frapptz%2Fdiscord-ext-menus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frapptz%2Fdiscord-ext-menus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frapptz%2Fdiscord-ext-menus/lists"}