Slash command autocompletion in pycord - python

I want to start working with SlashCommand autocompletion in pycord. Since I think that my request in the previous question is impossible, I thought to myself that maybe I can change the autocomplete based on the user's choice in one of the slash-command entries and display the desired entry to the user earlier.
I'm asking this because I don't found any good resource for learning this and I don't understand docs

there is an example from the Pycord documents, I think if you experiment with this part of the code, you will understand how it can work
https://github.com/Pycord-Development/pycord/blob/master/examples/app_commands/slash_autocomplete.py
If you want use them in cogs
there's my part for cogs
import discord
from discord import option
from discord.ext import commands
mycolors = ["red", "white", "yellow"]
class ExampleAutocompletion(commands.Cog):
ctx_parse = discord.ApplicationContext
def __init__(self, bot: discord.Bot):
self.bot = bot.user
#staticmethod
def colorAutocomplete(self: discord.AutocompleteContext):
return mycolors
#staticmethod
def flowertypeAutocomplete(self: discord.AutocompleteContext):
chosen_color = self.options["color"]
match chosen_color:
case "red":
return ["Rose", "Georgina"]
case "white":
return ["Chrisantem", "Gortensia"]
case "yellow":
return ["Sunflower", "Narciss"]
case _:
return ["There is no flower with this color"]
#commands.slash_command(
guild_ids=["Your Guild ID for test"],
description="")
#option("color", description="What's your favourite color?",
autocomplete=colorAutocomplete)
#option("flowertype", description="and that's your flower shape!",
autocomplete=flowertypeAutocomplete)
async def beautyflowers(self, ctx: ctx_parse,
color: str, flowertype: str):
await ctx.respond(f"My flower is {flowertype} and its color is {color}!")
def setup(bot):
bot.add_cog(ExampleAutocompletion(bot))

Related

Discord Prediction Bot

I'm trying to create a bot that my friends and I can do predictions through for the upcoming Soccer World Championship.
Now I want this bot to be able to ask the result of each match as a question, with a button for answer.
Creating one question works perfectly, but then moving on to the next question is what I have issues with.
I found the following online:
"Each message is a different view, but you don't need to program each view. You can create a class that inherits from discord.ui.button where it takes a team name as a string, then pass that button (plus another for the 2nd team) to a class that inherits from discord.ui.View"
I'm a beginner when it comes to python and I have no clue how I actually do this. Any tips?
What I want:
Message 1: "Team A vs Team B"
Button 1: Team A
Button 2: Team B
Message 2: "Team C vs Team C"
Button 1: Team C
Button 2: Team D
The code I currently already have:
class WorldsGeneral(discord.ui.View):
def __init__(self, *, timeout=10):
super().__init__(timeout=timeout)
#discord.ui.button(label="Groups", style=discord.ButtonStyle.red)
async def Finals(self, interaction: discord.Interaction, button: discord.ui.Button):
await interaction.response.send_message()
# The messages for predictions would have to be send by pressing this button ^
#discord.ui.button(label="Standings")
async def Standings(
self, interaction: discord.Interaction, button: discord.ui.Button
):
await interaction.response.send_message()
#bot.command(aliases=["poule", "predictions])
async def worlds(ctx):
embed = discord.Embed(
colour=0x0433f3,
title=":trophy: Worlds Poule 2022 :trophy:",
)
embed.set_image(
url=f"https://thumbs.gfycat.com/AccomplishedAdorableClumber-size_restricted.gif"
)
view = WorldsGeneral()
msg = await ctx.send(
":construction: Major Work In Progress :construction:", view=view
)
view.orig_mes = msg
You have an issue with your worlds command: there's a missing quotation at the very top. Here's how the fixed code should look:
#bot.command(aliases=["poule", "predictions"]) # Missing quotation right here
async def worlds(ctx):
embed = discord.Embed(
colour=0x0433f3,
title=":trophy: Worlds Poule 2022 :trophy:",
)
embed.set_image(
url=f"https://thumbs.gfycat.com/AccomplishedAdorableClumber-size_restricted.gif"
)
view = WorldsGeneral()
msg = await ctx.send(
":construction: Major Work In Progress :construction:", view=view
)
view.orig_mes = msg

Textual (python) - how to add click event in simple Text object?

I'm trying to get it so I can add links in text rendered by Textual.
My text may have multiple links, for example:
Hello [#click=hello]World[/] there, how are you?
This is a test of [#click=more] more info[/] being clickable as well.
In this simple sample I made, clicking on the word "World" should hopefully change the background color to red, but it doesn't work.
NOTE: I also bound the "b" key to do pretty much the same thing, so I could see it work
It should change the background color, and the subtitle of the app.
import os
import sys
from rich.console import RenderableType
from rich.panel import Panel
from rich.text import Text
from textual.app import App
from textual.widgets import Header, Footer, ScrollView
from textual.widgets import Placeholder
class MyApp(App):
async def on_load(self) -> None:
await self.bind("b", "color('blue')")
async def on_mount(self) -> None:
await self.view.dock(Header(), size=5, edge="top")
await self.view.dock(Footer(), edge="bottom")
await self.view.dock(ScrollView(Panel("Hello [#click=hello]World[/] more info here")), edge="top")
async def action_color(self, color:str) -> None:
self.app.sub_title = "KEYBOARD"
self.background = f"on {color}"
async def action_hello(self) -> None:
self.app.sub_title = "CLICKED"
self.background = "on red"
MyApp.run(title="Test click", log="textual.log")
I asked this same question in the textual discussions and originally rich discussions, but haven't been able to see how to make this work from the feedback I received there, which was helpful for sure, but I'm missing something here, so thanks for any input.
It seems like #click=action doesn't work in textual (at least I couldn't make it work at all).
After digging up in rich documentation, I stepped upon Text class. That one, has on method, that can create click callback.
It supports __add__, so You can concat multiple Text(s) together with + operator.
Second piece of the puzzle was to find out what to set as a click callback. Again I looked in the source code and found _action_targets in app.py. That contains {"app", "view"} set.
Putting all together, You can create link(s) using Text with on(click="app.callback()"), which will call action_callback method of MyApp Class (instance of textual App). Then creating final panel text by concating other Text(s) and link(s) together.
Here is working example, turning background to red clicking on Hello or green clicking on World.
from rich.panel import Panel
from rich.text import Text
from textual.app import App
from textual.widgets import Header, Footer, ScrollView
class MyApp(App):
async def on_load(self) -> None:
await self.bind("b", "color('blue')")
async def on_mount(self) -> None:
await self.view.dock(Header(), size=5, edge="top")
await self.view.dock(Footer(), edge="bottom")
link1 = Text("Hello").on(click="app.hello()")
link2 = Text("World").on(click="app.world()")
panel_text = link1 + " " + link2 + Text(" more info here")
await self.view.dock(ScrollView(Panel(panel_text)), edge="top")
async def action_color(self, color: str) -> None:
self.app.sub_title = "KEYBOARD"
self.background = f"on {color}"
async def action_hello(self) -> None:
self.app.sub_title = "CLICKED Hello"
self.background = "on red"
async def action_world(self) -> None:
self.app.sub_title = "CLICKED World"
self.background = "on green"
MyApp.run(title="Test click", log="textual.log")
I know it's not ideal solution, but it's closest I could get to what You want.
You can also make a text with different properties with Text.assemble():
async def on_mount(self) -> None:
await self.view.dock(Header(), size=5, edge="top")
await self.view.dock(Footer(), edge="bottom")
panel_text = Text.assemble(
"Hello ",
Text("World","dark_blue u").on({"#click" : "app.hello()"}),
" more ",
Text("info","dark_blue u").on({"#click" : "app.color('blue')"}),
" here")
await self.view.dock(ScrollView(Panel(panel_text)), edge="top")

Discord py command

I want to get the values ​​of two variables in a command function. In the example, I want to use a variable to define par.count(par2)
#commands.command()
async def count(self, ctx, par, par2):
x = par.count(par2)
emBed = discord.Embed(title="try count", description="", color=0x6F9DC3)
emBed.add_field(name="Output", value='text = "{0}"\nx = text.count({1})\nprint(x)' .format(par, par2))
emBed.add_field(name="Result", value='{0}' .format(x))
await ctx.channel.send(embed=emBed)
This is my result on discord embed when I input in discord "count i love you so much love" it defines the letter i as the text variable and love as the count variable, Any ideas to make the user input two variables to make my commands work?
Output Result
text = "i" 0
x = text.count(love)
print(x)
You can use * before the variable that will catch the rest of the command context.
#commands.command()
async def count(self, ctx, word, *, text):
x = text.count(word)
...
Example usage:
count love I love you so much, love
(<command> <word you want to count> <text>)
Hi I am not completely sure what u want but as I understand it the following could help:
#bot.command()
async def test(ctx, *args):
await ctx.send('{} arguments: {}'.format(len(args), ', '.join(args)))
https://discordpy.readthedocs.io/en/stable/ext/commands/commands.html#variable

Multiple Button not showing up in nextcord

I'm currently coding a quizz bot and I came up with this code:
class answers(nextcord.ui.View):
def __init__(self, ans1, ans2, ans3):
super().__init__(timeout=None)
self.ans1 = ans1
self.ans2 = ans2
self.ans3 = ans3
self.value = False
#nextcord.ui.button(label = '1', custom_id="1", style = nextcord.ButtonStyle.red)
async def reaction(self, button: nextcord.ui.Button, interaction: nextcord.Interaction):
await interaction.response.send_message('ans1')
self.value = True
self.stop()
#nextcord.ui.button(label='2', custom_id='2' )
async def reaction(self, button: nextcord.ui.Button, interaction: nextcord.Interaction):
await interaction.response.send_message('ans2')
self.value = True
self.stop()
#nextcord.ui.button(label='3', custom_id='3')
async def reaction(self, button: nextcord.ui.Button, interaction: nextcord.Interaction):
await interaction.response.send_message('ans3')
self.value = True
self.stop()
This is the class that should display 3 buttons. However it displays only the third one. I do not get any errors.
How can I write this code so it does display all three?
You'll need to use different method names for each of the buttons. Changing the names to reaction1, reaction2, reaction3 would fix your issue.
The reason is the ui.button decorator doesn't store the button info elsewhere, instead it attaches the relevant info onto the method and returns the method back. So having the same name for the three method, you're actually reassigning answers.reaction every time you define a button, and only the last one gets kept. discord.py only evaluates all of the components from a view at runtime, so the last button is all it sees—thus it only displays the last button.

Blacklist a role in Discord.py

after trying so many thing I came up with the idea to ask you intelligent people out there! I want to blacklist 3 roles for a single command. But everything I tried failed.
Here's the code I wrote:
member = ctx.message.author
roleA = get(member.guild.roles, name="Rote Birdys")
roleB = get(member.guild.roles, name="Grüne Birdys")
roleC = get(member.guild.roles, name="Blaue Birdys")
if commands.has_any_role(roleA, roleB, roleC):
return
Nothing worked, I tried some more things but they didn't work either. May I'm really stupid but I need to finish this projekt in a few weeks and I'm stuck here.
I hope you can help me :)
This is just the has_any_role check but backwards. I also created a new error like MissingAnyRole:
from discord.ext.commands import check, CheckFailure
import discord.utils
class HasForbiddenRole(CheckFailure):
def __init__(self, forbidden_roles):
self.forbidden_roles = forbidden_roles
forbidden= ["'{}'".format(role) for role in forbidden_roles]
if len(missing) > 2:
fmt = '{}, or {}'.format(", ".join(forbidden[:-1]), forbidden[-1])
else:
fmt = ' or '.join(forbidden)
message = "You have at least one of the forbidden roles: {}".format(fmt)
super().__init__(message)
def lacks_every_role(*items):
def predicate(ctx):
if not isinstance(ctx.channel, discord.abc.GuildChannel):
raise NoPrivateMessage()
getter = functools.partial(discord.utils.get, ctx.author.roles)
if not any(getter(id=item) is not None if isinstance(item, int) else getter(name=item) is not None for item in items):
return True
raise HasForbiddenRole(items)
return check(predicate)
Usage would be like any of the other checks:
#lacks_every_role("Rote Birdys", "Grüne Birdys", "Blaue Birdys") # You could also use role ids
#bot.command()
async def comm(ctx):
...

Categories

Resources