Nextcord buttons pagination - discord

I'm trying to do a help command with buttons, I'm using nextcord-ext-menus. But, I can't find a way of switching between embeds without editing 2 embeds when one button is pressed.
I want one button to go to the previous page,one to stop the whole help command/embed & one that will go to the next page. (im new to buttons btw)
I can explain in more detail if you don't understand.
My code:
class MyButtonMenu(menus.ButtonMenu):
def __init__(self):
super().__init__(disable_buttons_after=True)
async def send_initial_message(self, ctx, channel):
return await channel.send(f'Hello {ctx.author}', view=self)
#nextcord.ui.button(style=nextcord.ButtonStyle.blurple,emoji="<:left:968157460626047026>")
async def on_thumbs_up(self, button, interaction):
embe = nextcord.Embed(description=f"is it working? /1")
await self.message.edit(embed=embe)
#nextcord.ui.button(emoji="\N{THUMBS DOWN SIGN}")
async def on_thumbs_down(self, button, interaction):
emb = nextcord.Embed(description="WOAH TY !!!")
await self.message.edit(embed=emb)
#nextcord.ui.button(emoji="\N{BLACK SQUARE FOR STOP}\ufe0f")
async def on_stop(self, button, interaction):
self.stop()
#bot.command()
async def pages(ctx):
await MyButtonMenu().start(ctx)

Related

discord.py button on_interaction

from discord.ext import commands
from discord import app_commands
import json
from discord.ui import Button
with open('.\config.json') as f:
data = json.load(f)
class button(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
#discord.ui.button(label="Button", style=discord.ButtonStyle.success, custom_id="ver")
async def verify(self, interaction: discord.Interaction, button:discord.ui.Button):
user = interaction.user
role = interaction.guild.get_role(data['verify_role'])
await user.add_roles(role)
await interaction.response.send_message(f"success", ephemeral = True)
class verify(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
#commands.Cog.listener()
async def on_interaction(self, interaction: discord.Interaction):
green = await bot.wait_for("button_click", check = lambda k: k.custom_id == "ver")
await green.send(content="Button smashed!", ephemeral=False)
#app_commands.command(name = "ver", description = "ver")
async def verify(self, interaction: discord.Interaction) -> None:
await interaction.response.send_message(view=button())
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(verify(bot))
I made a verify button, but when I reload the bot, I have to command it again. I want to use on_interaction to prevent him, but I don't have enough understanding of it. How do I get the button to work when the bot is reloaded?
You need to add the view when the cog gets loaded for it to be a persistent view
class verify(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
bot.add_view(button())
Read more on bot.add_view(): https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Bot.add_view
Example of persistent views:
https://github.com/Rapptz/discord.py/blob/master/examples/views/persistent.py

Discord.py, buttons on message dont work after bot restarts

The buttons work after the command is sent, they show up on the embed, but once the bot restarts and I press the button that was on the previous embed, the discord app gives me an error that says interaction failed, here's the discord.py code.
async def ticket(ctx, *title):
button = Button(label="Ready to start buying?",
style=discord.ButtonStyle.green)
guild = ctx.guild
dropperrole = discord.utils.get(guild.roles, id=1003171723207774218)
topdropperrole = discord.utils.get(guild.roles, id=1004989656137465917)
title = list(title)
title = " ".join(map(str, title))
view = View()
view.add_item(button)
async def button_callback(interaction):
await interaction.response.send_message("Creating channel...",
ephemeral=True)
await asyncio.sleep(3)
await interaction.edit_original_message(
content=
f"Channel successfully created #ticket-{(interaction.user.name).lower()} !"
)
overwrites = {
guild.default_role:
discord.PermissionOverwrite(view_channel=False),
interaction.user: discord.PermissionOverwrite(view_channel=True),
guild.me: discord.PermissionOverwrite(view_channel=True),
dropperrole: discord.PermissionOverwrite(view_channel=True),
topdropperrole: discord.PermissionOverwrite(view_channel=True)
}
chan = await ticket_category.create_text_channel(
f"ticket-{(interaction.user.name).lower()}", overwrites=overwrites)
id = discord.utils.get(
ctx.guild.channels,
name=f"ticket-{(interaction.user.name).lower()}")
await interaction.edit_original_message(
content=f"Channel successfully created {id.mention}!")
ccEmbed = discord.Embed(title=f"Hello {interaction.user.name}!",
color=0x2ecc71)
ccEmbed.add_field(
name="Ready to buy?",
value="Type `$dhc` to buy DHC, or `$buygen` to buy Gen! type `$fastpass` to buy fastpass, this allows you to skip all orders! To view prices, type `$prices`!",
inline=True)
ccEmbed.set_footer(text="discord.gg/dhcc | Rvse's Gen")
await chan.send(embed=ccEmbed)
button.callback = button_callback
You need to setup persistence so the bot will restart the view of your button when it launches. I think your are using Rapptz discord library? If so here is an example you can checkout:
https://github.com/Rapptz/discord.py/blob/master/examples/views/persistent.py

how to get new data at run time from django?

I am working on a Django project with ReactJs frontend. I have to built a simple chat application in that project where users can communicate with each other.
in django views I have following function to read messages
Views.py
class MessagesListAPI(GenericAPIView, ListModelMixin ):
def get_queryset(self):
condition1 = Q(sender=15) & Q(receiver=11)
condition2 = Q(sender=11) & Q(receiver=15)
return Messages.objects.filter(condition1 | condition2)
serializer_class = MessagesSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.list(request, *args, **kwargs)
this gives me all the messages between user 11 and user 15.
on frontend I am getting these messages from rest Api by calling above function
frontend
const chatApi = axios.create({
baseURL: 'http://localhost:8000/chat/'
})
const getMessages = async() => {
let data = await chatApi.get(`MessageRead/`).then(({data})=>data);
setMessages(data);
}
I am calling this function in an onClick event of button. and displaying the messages by maping messages array.
problem is that at the time when these messages are open to the user. at that time if a new message(data) is added in database. I have to press that button again and call getMessages function again to display that message.
Is there a way to automatically get that new message(record) without calling this function again by pressing a button?

Discord.py. How can I wait_for person's reaction inside "on_raw_reaction_add" function?

I need to get the reaction of the user from his private channel. Is this the right way to do that?
#bot.event
async def on_raw_reaction_add(payload):
reactioneer = payload.member
emoji = payload.emoji
if emoji.name == "🔰":
msg = await reactioneer.send("Hi! Which greeting would you like to hear every morning? Click the reaction of it. Hello - 1, Morning - 2...")
await msg.add_reaction(:one:)
await msg.add_reaction(:two:)
def check(user, payload):
return user == payload.member and message.channel.type == discord.ChannelType.private
await bot.wait_for("payload", check = check)
### some things with data bases (does not matter now)
await reactioneer.send("Alright, I will send this greeting every morning!")
await bot.wait_for("raw_reaction_add", check=check)
bot.wait_for() waits for a WebSocket event to be dispatched.

Whats the proper way to transmit updating data to a client via Django websockets?

Currently, I have an element that when clicked, sets up a global cooldown timer that effects all clients with the use of Django websockets. My issue is that while initially the websocket value is converted to state in my React client via componentDidMount, the websocket doesn't run again when its value changes in real time.
Heres how it works in detail.
The timer is updated via a django model, which I broadcast via my websocket to my React front-end with:
consumer.py
class TestConsumer(AsyncConsumer):
async def websocket_connect(self, event):
print("connected", event)
await self.send({
"type":"websocket.accept",
})
#correct way to grab the value btw, just work on outputting it so its streaming
#database_sync_to_async
def get_timer_val():
val = Timer.objects.order_by('-pk')[0]
return val.time
await self.send({
"type": "websocket.send",
"text": json.dumps({
'timer':await get_timer_val(),
})
})
async def websocket_receive(self, event):
print("received", event)
async def websocket_disconnect(self, event):
print("disconnected", event)
This works initially, as my React client boots up and converts the value to state, with:
component.jsx
//handles connecting to the webclient
componentDidMount() {
client.onopen = () => {
console.log("WebSocket Client Connected");
};
client.onmessage = (message) => {
const myObj = JSON.parse(message.data);
console.log(myObj.timer);
this.setState({ timestamp: myObj.timer });
};
}
//handles submitting the new timer upon clicking on element
handleTimer = () => {
// handles making PUT request with updated cooldown timer upon submission,
const timestamp = moment().add(30, "minutes");
const curr_time = { time: timestamp };
axios
.put(URL, curr_time, {
auth: {
username: USR,
password: PWD,
},
})
.then((res) => {
console.log(res);
});
};
//button that prompts the PUT request
<button
type="submit"
onClick={(e) => {
this.handleTimer();
//unrelated submit function
this.handleSubmit(e);
}}
>
Button
</button>
However, when a user clicks the rigged element and the database model changes, the web socket value doesn't until I refresh the page. I think the issue is that I'm only sending the websocket data during connection, but I don't know how to keep that "connection" open so any changes automatically get sent to the client server. I've looked through tons of links to find what the best way to implement real time is, but most of them are either about socket.io or implementing a chat app. All I want to do is stream a django models value to the front-end in real time.
When you want to send updates triggered by some other code to the websocket connection, the channels part of django-channels comes into play. It works like this:
On connection, you add the websocket to some named group
When the value of Timer changes, you send the event (via the channels layer) with certain type to this group, from the code that triggered the changes.
Django-channels then invoke the method of the Consumer named after the type of the event for each websocket in the group
And finally, in this method, your code sends the message to the client
You need to configure then channels layer with the Redis. https://channels.readthedocs.io/en/stable/topics/channel_layers.html
Now, step by step. I'll omit irrelevant parts.
1
async def websocket_connect(self, event):
await self.send({
"type":"websocket.accept"
})
await self.channel_layer.group_add('timer_observers', self.channel_name)
2 Here I am sending the event inside model, but you can do this in the view, or via django signals, however you want it. Also I am not checking whether the value actually changed, and I am assuming there is only one instance of Timer in the DB.
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
class Timer(models.Model):
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
async_to_sync(get_channel_layer().send)(
'timer_observers', {"type": "timer.changed"}
)
3+4
I have extracted the time-sending code to reuse it
class TestConsumer(AsyncConsumer):
async def websocket_connect(self, event):
print("connected", event)
await self.send({
"type": "websocket.accept",
})
await self.channel_layer.group_add('timer_observers', self.channel_name)
await self.send_current_timer()
async def timer_changed(self, event):
await self.send_current_timer()
async def send_current_timer(self):
#database_sync_to_async
def get_timer_val():
val = Timer.objects.order_by('-pk')[0]
return val.time
await self.send({
"type": "websocket.send",
"text": json.dumps({
'timer': await get_timer_val(),
})
})
The idea here is that you handle internal events generated by your application the same way as external events from the client, i.e. websocket.connect -> async def websocket_connect. So the channels layer kinda "sends" you a "websocket message", and you respond (but to the actual client).
I hope that helps to understand the concepts. Probably what you are doing is overkill, but I assume that's just a learning exercise =)
I am not 100% sure this will work, so don't hesitate to ask additional questions.

Resources