Discord.py returns to the discord bot scene

83
Discord.py returns to the discord bot scene

It is been 6 months since I offered my departure from the ecosystem and lots has took place during that point.

For the interval of the closing 2 weeks, many contributors and I came together and seen the recount of the Python Discord bot ecosystem changed into principally on hearth. After some taking into consideration, with the abet of others, I came to the conclusion that vogue might simply aloof resume. For the interval of these two weeks rather a few labor changed into spent into catching up and imposing many things to the discord.py venture and to at closing birth a v2.0 liberate. We’re on a closing date but we purchased rather a few labor done.

Why did you return?

About three weeks within the past, Discord offered that it can decommission API versions 6 and 7 on May perhaps presumably perhaps 1st, 2022. While the present beta model, v2.0, is on model 9, the present get model of discord.py, v1.7.3, is on model 7 of the API. This implies that Discord‘s thought to decommission v6 and 7 will result in all bots on the get model of discord.py ceasing to work as of May perhaps presumably perhaps 1st, 2022. This wasn’t one thing I envisioned happening so quickly.

I attempted to talk about the impact of the decommission with Discord, and while they acknowledged my concerns, they gave me no guarantees that the decommission date would be postponed. discord.py stays the 2nd most well-most favorite library within the ecosystem. If the decommission goes forward as planned, it would be a rather more catastrophic blow to the ecosystem than the genuine privileged message intent requirement. In recount to minimise hurt of this alternate, I felt esteem the library mandatory to be updated.

Likewise, as time went on it grew to became more and more definite that the Python bot ecosystem has been too fragmented. I changed into hopeful that after half of a twelve months that there would be a clear more than just a few library that most customers can transparently migrate to, but apparently this never ended up materialising. In recount to prevent more turbulence within the ecosystem, it made sense to desire a stare upon to resolve the present, uncomfortable, recount.

Collect you direct Discord is enhancing now?

With the April 30th closing date impending, I aloof don’t in particular feel esteem Discord has realized a sufficient thought for migrating the ecosystem as a full, nor make I basically feel esteem they’re doing an exemplary job. Within the closing 6 months there haven’t been that many things which had been added to Discord‘s API:

  • Member timeouts
    • Restricted to 28 days, but overall a factual feature.
  • Role icons
    • Enhance very top
  • Server avatar and bio
    • Enhance very top
  • Cut recount file attachments
    • This selection had points before all the pieces, but I direct they’re now fastened.
  • Ephemeral give a steal to for attachments.
    • Here is factual.
  • Element modals
    • Incredibly restricted of their current construct, very top textual speak parts are allowed.
  • Autocomplete
    • This selection is barely cool.
  • Alt textual speak for attachments
  • Editing attachments on messages

That is steadily it. There is two months to switch till the closing date and basic things comparable to permissions and the so-known as “slate v2” are aloof missing even supposing they’re planned for testing at some level within the end.

No longer to gift that of us are aloof making an strive forward to their message speak intent functions after 1-3 months of waiting despite the so-known as promised 5-day SLA. Every little thing stays a huge number and pretending that it is not basically would not seem conducive.

By manner of non-public verbal replace with Discord, I’m in a position to’t precisely say that it has improved since I dropped vogue, rather verbal replace suffered. After vogue ceased I did no longer recount to any Discord developer and they did not recount to me. The ideal construct of contact came from me contacting them to talk about the decommission.

N.B.: “Permissions v2”, “Slate v2”, and Cut Tell Localisation are aspects planned to be launched at some level within the end.

Novel Parts

These are the things we absorb now been laborious at work for the past two weeks.

Member Timeouts

There is a brand fresh timed_out_until attribute in Member. This works in Member.edit to time them out. There is also Member.is_timed_out to test if a member is timed out.

Role Icons

Equally straightforward. Role.icon and Role.display_icon. There is Role.edit give a steal to for display_icon whilst you need to alternate it.

Alt-textual speak for Attachments

Moreover straightforward, true skedaddle the description kwarg to File. It will likely be a property to read in Attachment.

v10 of the API

The library now supports the Intents.message_content flag and makes disclose of API v10 by default. We absorb now chanced on that right here is surprisingly prohibitive because it requires customers to absorb the message speak intent earlier than the closing date by April 30th, so it is a ways suggested to enable Intents.message_content both within the bot and within the bot utility page whether it is a ways vital to your bot to honest.

I possess this desires repeating on account of importance, on account of bumping API versions, that you just might must enable the message speak intent in both your code and the developer portal to your bot to honest if it desires message speak. This requirement is sadly being imposed by Discord and is out of my withhold an eye on.

Modals

Enhance for discord.ui.TextInput and discord.ui.Modal has been added. The syntax for right here is same to discord.ui.Behold with slight differences, since the whole lot can no longer absorb particular person callbacks. A straightforward instance:

import discord
from discord import ui

class Questionnaire(ui.Modal, title='Questionnaire Response'):
    name = ui.TextInput(model='Name')
    solution = ui.TextInput(model='Respond', model=discord.TextStyle.paragraph)

    async def on_submit(self, interplay: discord.Interaction):
        live up for interplay.response.send_message(f'Thanks to your response, {self.name}!', ephemeral=Appropriate)

In recount to ship a modal, Interaction.response.send_modal is outmoded because it requires a special interplay response form. You can’t ship a message and a modal at the identical time. You would take into narrative the utilization of an Interaction.followup.ship if that is highest.

Interaction Enhancements

  • Interaction.shopper changed into added to bag the shopper in case it is mandatory.
  • Interaction.response.defer now supports pondering=Appropriate or pondering=Fraudulent within the event you wish the “Bot is pondering…” UI when deferring. This corresponds to InteractionType.deferred_channel_message.
  • Interaction.response.send_message now supports sending recordsdata. This also supports ephemeral recordsdata.
  • Add low-stage Interaction.response.autocomplete helper for auto full responses in utility instructions.

Cut Instructions and Context Menu Instructions

After some invent work and taking into consideration, I utilized gash instructions the utilization of a syntax that will likely be a subset of the discord.ext.instructions kit. They stay in a brand fresh namespace, discord.app_commands and honest barely within the same vogue. In recount to open registering instructions, you wish a brand fresh form known as a app_commands.CommandTree which takes a Client as its very top argument when developing it.

import discord
from discord import app_commands

intents = discord.Intents.default()
intents.message_content = Appropriate

shopper = discord.Client(intents=intents)
tree = app_commands.CommandTree(shopper)

After developing your tree, including a recount is principally the identical because the recount extension:

@tree.recount(guild=discord.Object(identification=MY_GUILD_ID))
async def gash(interplay: discord.Interaction, number: int, string: str):
    live up for interplay.response.send_message(f'{number=} {string=}', ephemeral=Appropriate)

# Describing parameters...
@tree.recount(guild=discord.Object(identification=MY_GUILD_ID))
@app_commands.record(attachment='The file so that you just might add')
async def add(interplay: discord.Interaction, attachment: discord.Attachment):
    live up for interplay.response.send_message(f'Thanks for uploading {attachment.filename}!', ephemeral=Appropriate)

By omitting the guild key phrase argument it is added as a world recount as a substitute. Within the event you do not are making an strive to disclose a decorator, then it is same to the following code:

@app_commands.recount()
async def gash(interplay: discord.Interaction, number: int, string: str):
    live up for interplay.response.send_message(f'{number=} {string=}', ephemeral=Appropriate)

# May perhaps presumably perhaps additionally specify a guild right here, but this situation chooses no longer to.
tree.add_command(gash)

Teams

Working with teams functions within the same vogue to cogs which of us are conversant in:

class Permissions(app_commands.Neighborhood):
    """Address permissions of a member."""

    def get_permissions_embed(self, permissions: discord.Permissions) -> discord.Embed:
        embed = discord.Embed(title='Permissions', colour=discord.Coloration.blurple())
        permissions = [
            (name.replace('_', ' ').title(), value)
            for name, value in permissions
        ]

        allowed = [name for name, value in permissions if value]
        denied = [name for name, value in permissions if not value]

        embed.add_field(name='Granted', cost='n'.be half of(allowed), inline=Appropriate)
        embed.add_field(name='Denied', cost='n'.be half of(denied), inline=Appropriate)
        return embed

    @app_commands.recount()
    @app_commands.record(procedure='The member or honest to bag permissions of')
    async def bag(self, interplay: discord.Interaction, procedure: Union[discord.Member, discord.Role]):
        """Secure permissions for a member or honest"""
        
        if isinstance(procedure, discord.Member):
            direct procedure.resolved_permissions is no longer None
            embed = self.get_permissions_embed(procedure.resolved_permissions)
            embed.set_author(name=procedure.display_name, url=procedure.display_avatar)
        else:
            embed = self.get_permissions_embed(procedure.permissions)

        live up for interplay.response.send_message(embed=embed)

    @app_commands.recount(name='in')
    @app_commands.record(channel='The channel to bag permissions in')
    @app_commands.record(member='The member to bag permissions of')
    async def _in(
        self, 
        interplay: discord.Interaction, 
        channel: Union[discord.TextChannel, discord.VoiceChannel],
        member: No longer mandatory[discord.Member] = None,
    ):
        """Secure permissions for you or every other member in a explicit channel."""
        embed = self.get_permissions_embed(channel.permissions_for(member or interplay.particular person))
        live up for interplay.response.send_message(embed=embed)


# With the procedure to add the Neighborhood to your tree...
tree.add_command(Permissions(), guild=discord.Object(identification=MY_GUILD_ID))

Utilizing nested teams (as much as one layer) will likely be doable, make repeat that teams can no longer absorb callbacks hooked as much as them and be invoked on account of a Discord limitation:

class Designate(app_commands.Neighborhood):
    """Get tags by their name"""

    stats = app_commands.Neighborhood(name='stats', description='Secure imprint statistics')

    @app_commands.recount(name='bag')
    @app_commands.record(name='the imprint name')
    async def tag_get(self, interplay: discord.Interaction, name: str):
        """Retrieve a imprint by name"""
        live up for interplay.response.send_message(f'imprint bag {name}', ephemeral=Appropriate)

    @app_commands.recount()
    @app_commands.record(name='the imprint name', speak='the imprint speak')
    async def create(self, interplay: discord.Interaction, name: str, speak: str):
        """Create a imprint"""
        live up for interplay.response.send_message(f'imprint create {name} {speak}', ephemeral=Appropriate)

    @app_commands.recount(name='checklist')
    @app_commands.record(member='the member to bag tags of')
    async def tag_list(self, interplay: discord.Interaction, member: discord.Member):
        """Secure an particular person's checklist of tags"""
        live up for interplay.response.send_message(f'imprint checklist {member}', ephemeral=Appropriate)

    @stats.recount(name='server')
    async def stats_guild(self, interplay: discord.Interaction):
        """Will get the server's imprint statistics"""
        live up for interplay.response.send_message(f'imprint stats server', ephemeral=Appropriate)

    @stats.recount(name='member')
    @app_commands.record(member='the member to bag stats of')
    async def stats_member(self, interplay: discord.Interaction, member: discord.Member):
        """Will get a member's imprint statistics"""
        live up for interplay.response.send_message(f'imprint stats member {member}', ephemeral=Appropriate)

tree.add_command(Designate())

Context Menus

Context menus are also straight forward, true annotate a honest with both discord.Member or discord.Message:

@tree.context_menu(guild=discord.Object(identification=MY_GUILD_ID))
async def bonk(interplay: discord.Interaction, member: discord.Member):
    live up for interplay.response.send_message('Bonk', ephemeral=Appropriate)

@tree.context_menu(name='Translate with Google', guild=discord.Object(identification=MY_GUILD_ID))
async def translate(interplay: discord.Interaction, message: discord.Message):
    if no longer message.speak:
        live up for interplay.response.send_message('No speak!', ephemeral=Appropriate)
        return

    textual speak = live up for google_translate(message.speak)  # Narrate for the reader!
    live up for interplay.response.send_message(textual speak, ephemeral=Appropriate)

Fluctuate

In recount to limit a number by a given vary, we can disclose the app_commands.Fluctuate annotation:

@tree.recount(guild=discord.Object(identification=MY_GUILD_ID))
async def vary(interplay: discord.Interaction, cost: app_commands.Fluctuate[int, 1, 100]):
    live up for interplay.response.send_message(f'Your cost is {cost}', ephemeral=Appropriate)

Choices

Choices are also supported in three various flavours. The basic is the easiest, by process of typing.Literal:

@app_commands.recount()
@app_commands.record(fruits='fruits to desire from')
async def fruit(interplay: discord.Interaction, fruits: Literal['apple', 'banana', 'cherry']):
    live up for interplay.response.send_message(f'Your favorite fruit is {fruits}.')

Within the event you need to join a name to the associated payment, the utilization of an enum.Enum derived class is your next step up:

class Fruits(enum.Enum):
    apple = 1
    banana = 2
    cherry = 3

@app_commands.recount()
@app_commands.record(fruits='fruits to desire from')
async def fruit(interplay: discord.Interaction, fruits: Fruits):
    live up for interplay.response.send_message(f'Your favorite fruit is {fruits}.')

Within the event you wish more withhold an eye on over the staunch checklist of choices, then there is the app_commands.selections decorator:

from discord.app_commands import Different

@app_commands.recount()
@app_commands.record(fruits='fruits to desire from')
@app_commands.selections(fruits=[
    Choice(name='apple', value=1),
    Choice(name='banana', value=2),
    Choice(name='cherry', value=3),
])
async def fruit(interplay: discord.Interaction, fruits: Different[int]):
    live up for interplay.response.send_message(f'Your favorite fruit is {fruits.name}.')

Uncover that that you just might give you the option to also disclose naked int as an annotation right here whilst you make no longer care about the name.

Autocomplete

The library also won give a steal to for auto full the utilization of two various decorator syntaxes:

@app_commands.recount()
async def fruits(interplay: discord.Interaction, fruits: str):
    live up for interplay.response.send_message(f'Your favorite fruit seems to be {fruits}')

@fruits.autocomplete('fruits')
async def fruits_autocomplete(
    interplay: discord.Interaction,
    current: str,
    namespace: app_commands.Namespace
) -> Record[app_commands.Choice[str]]:
    fruits = ['Banana', 'Pineapple', 'Apple', 'Watermelon', 'Melon', 'Cherry']
    return [
        app_commands.Choice(name=fruit, value=fruit)
        for fruit in fruits if current.lower() in fruit.lower()
    ]

Or, alternatively:

@app_commands.recount()
@app_commands.autocomplete(fruits=fruits_autocomplete)
async def fruits(interplay: discord.Interaction, fruits: str):
    live up for interplay.response.send_message(f'Your favorite fruit seems to be {fruits}')

async def fruits_autocomplete(
    interplay: discord.Interaction,
    current: str,
    namespace: app_commands.Namespace
) -> Record[app_commands.Choice[str]]:
    fruits = ['Banana', 'Pineapple', 'Apple', 'Watermelon', 'Melon', 'Cherry']
    return [
        app_commands.Choice(name=fruit, value=fruit)
        for fruit in fruits if current.lower() in fruit.lower()
    ]

Syncing

The library does no longer supply automatic syncing. The particular person is accountable for this. In recount to sync our instructions we can disclose

live up for tree.sync(guild=discord.Object(identification=MY_GUILD_ID))

# Or, to sync world instructions
live up for tree.sync()

Uncover that there is explicitly no manner to sync every guild recount since that will incur too many requests, likewise the library makes a awake effort to no longer construct HTTP requests within the background without being explicitly suggested to make so by the particular person.

Miscellaneous

There is plot more to it, for instance transformers (the same of converters, for these familiar) and on_error handlers but this half is already too prolonged.

There is aloof lots that desires to be done, we absorb now fashioned a working community to if truth be told construct a handbook to construct discord.py more accessible and more easy to be taught. Within the event you need to desire half in these items, please be at liberty. It is a first-rate effort that will disclose abet.

These are the kindly things which might perhaps be currently planned, even supposing it is not identified if they’ll basically happen:

  • Engaged on a handbook for discord.py that has more prose pages and easy to absorb a examine documentation.
  • Refactoring to allow utilization of asyncio.urge as a substitute of preserving loop objects hostage.
    • This permits discord.py to meet more standard asyncio invent.
  • Refactoring occasions to desire single parameter “event objects” as an more than just a few of multi parameter.
    • Here is more or much less making every event a “raw event” with helper methods for the richer API.
  • Alternate Behold parameter recount to be more in step with the fresh app_commands namespace, i.e. interplay might simply aloof continuously come first.

We’re aloof on a factual closing date! Moreover, please repeat that the aspects explained might undergo changes as more strategies is on the market in.

I would select to deeply thank each person fervent who helped in some construct (in alphabetical recount)

  • devon#4089
  • Eviee#0666
  • Gobot1234#2435
  • Imayhaveborkedit#6049
  • Jackenmen#6607
  • Josh#6734
  • Kaylynn#0001
  • Kowlin#2536
  • LostLuma#7931
  • Maya#9000
  • mniip#9046
  • NCPlayz#7941
  • Orangutan#9393
  • Palm__#0873
  • Predä#1001
  • SebbyLaw#2597
  • TrustyJAID#0001
  • Twentysix#5252
  • Umbra#0009
  • Vaskel#0001

This positively couldn’t had been refrained from your abet and I very much prefer it πŸ™‚

Read More

Vanic
WRITTEN BY

Vanic

β€œSimplicity, patience, compassion.
These three are your greatest treasures.
Simple in actions and thoughts, you return to the source of being.
Patient with both friends and enemies,
you accord with the way things are.
Compassionate toward yourself,
you reconcile all beings in the world.”
― Lao Tzu, Tao Te Ching

you're currently offline