Re-ordering messages with discord bot - discord.py

I am writting a bot and i want to work out how to efficiently re order old messages.
For example
the sequence of messages on a channel where only the bot posts, or commands are given to a bot might be
Bot: "1"
Bot: "2"
Bot: "3"
Bot: "4"
Bot: "5"
Then the user uses the command $cycle
I want the messages on the channel to then read
Bot: "2"
Bot: "3"
Bot: "4"
Bot: "5"
Bot: "1"
[I'm not writing the users comand in the aove snippet as there are a few things you could do with it - but I'm most interested in what we are doing with the bots message sequence]
I can see a couple of ways of doing this, but as discord limits the rate at which you can post an edit messages, I'm not sure which would be most efficent.
The bot could edit each message - but that would hit their speed limit. Alternately could send the messages again - but that would also hit their speed limit. Ideally it would be nice to edit the order they are placed in but this appears not to be allowed by the API.
Here is a bot command that you can use to generate a sequence of messages like the above.
#bot.command(name='repeat', help='type a sequence of words seperated by spaces and they will be repeated back to you as a sequence of messages')
async def repeat(ctx, *lines):
print("repeating")
await asyncio.gather(*[ctx.send(line) for line in lines])
Here is my attempt at making a cycle command
#bot.command(name='cycle', help='help me to understand bots')
async def cycle(ctx):
#prepare to read in the most recent sequence of messages from the bot
message = ctx.message
channel =message.channel
messages = []
#read them in
async for other_message in channel.history(before=message, limit=1000):
messages.append(other_message)
if other_message.author.name!= bot.user.name:
break
#missing code
The above reads in the messages so you can edit them into the new order, resend them in a new order or do something else. What would the fastest implementation of the cycle command be?

Related

discord.py save every message in specific channel (using channel id)

How can a python bot read every message that is sent in a certain channel or read every message that was sent in one using a command like $save 421345187663708161. Thanks in advance, havent been able to find the answer anywhere.
I made this in an on_message Function which scans the message content for "$save" on the beginning, then gets the channel per the given ID.
#client.event
async def on_message(message):
if message.content.startswith("$save"):
splittedcontent = message.content.split()
channel = client.get_channel(splittedcontent[1])
all_messages = channel.history()
What your job now is, is to understand this code, maybe inform about the things that are going on here (like channel.history) and implement this to your code (preferably also with some try/except cases).

How do I make my discord.py bot count the amount of messages sent by a certain person?

I am trying to make a Discord bot that is similar to mee6 in the sense that it counts the messages sent by a user in my Discord server at certain intervals. I have scoured the web and can't find what I'm looking for even though there are similar questions. For example, I was able to find some code that counts the number of messages sent in one specific channel. I was also able to find something that I am basically looking for, which is total messages sent in a guild, but it was written in Java. I hope this narrows my question, and thank you in advance.
You can store the author and the message count in a Dictionary like this:
messageCount = {}
#client.event
async def on_message(ctx):
author = str(ctx.author)
if author in messageCount:
messageCount[author] += 1
else:
messageCount[author] = 1
await client.process_commands(ctx)
So, the dictionary would look something like this:
messageCount = {
'user#1532': 52,
'user#0864': 742,
'user#0067': 662,
...
}
Note: Once the bot goes offline, all the data will be erased, so i'll be a lot safer to store this data in an external file or a database. I'm hoping this answer will give you a gist on how to get started.

How to make your discord bot "remember" messages before he as activated?

I have made a discord bot that has the saves the id of a specific msg on a JSON file and when someone reacts to the msg he sends a message back.
My problem is when I do something which is going to turn the bot off for a period of time (restart, update, change host and more), the bot "forgets" all the messages that were made before he was activated again.
Is there a way to make the bot remember all the messages that happened before he was turned off?
(I tried channel.history loop and the bot did find his previous made message (I tested if the id is the same)
but after I tried to react to the message again, the bot still didn't recognize that a reaction has been made because the message was made before he was turned on.
side note:
There is a work around you can do that instead of trying to remember the message id, you need to remember the channel id that the msg has been sent in as well.
So after you have found your previous message, you can delete it and make a new message.
The thing is, I wanna know if there is a rather easier way to do it or if there is a way to "remember" previous messages.
So what I would do is I would save the messages into an SQL database and on start up recover all the messages.
Like so:
# Start up (run this code first)
try:
with open(path, "x") as file: pass
except: pass
global conn, c
conn = sqlite3.connect(path)
c = conn.cursor()
c.execute("""CREATE TABLE IF NOT EXISTS Messages (
messages string NOT NULL
)""")
# Gets message history
c.execute("SELECT * FROM Messages")
history = c.fetchall()
This will not work unless you save the messages when you receive them, so add these lines once you receive a message you want to be able to get later
# Saves message
c.execute("INSERT INTO messages VALUES (?)", (message,))
conn.commit()

Put a user on hold with Amazon Lex

We are using Amazon Connect, Lex and Lambda to create a phone bot. One use case we have is that we need to put the user on hold while we find information in other systems. So the conversation will be something like this:
- bot: hi, what can I do for you?
- user: i want to make a reservation
- bot: wait a minute while I fetch information about available rooms
... after 5 seconds ...
- bot: I found a free room blah blah
I don't see a way to send the wait a minute... message and keep control of the conversation. How can we achieve that?
You can accomplish this inside a single Lex bot by setting the intent to be fulfilled by a lambda function, the response of the function would play a message saying “please wait” and then chain another internet to perform the search using the data from the original intent.
See this link for information about sharing data between intents.
You can chain or switch to the next intent by passing the confirmIntent dialog action back in the lambda response. See this link for more information on the lambda input and response format.
You can use wait block in aws connect https://docs.aws.amazon.com/connect/latest/adminguide/flow-control-actions-wait.html
By using this block you can set time to 5 secs . after time expired you can play prompt.
This is a very common problem typically when we want to do backend lookups in an IVR. The problem is lex does not provide any means to just play prompts.
One way to do it is:
Create a dummy slot in your intent (the reservation intent from your example above) with any type (e.g. AMAZON.NUMBER), we don't really care what the value is in this slot
From the lex code-hook for the intent, return ElicitSlot for this dummy slot with prompt as "Wait a minute while I fetch available rooms... "
If you do only this much, the problem you will face is that Lex will expect input from caller and will wait for around 4 seconds before passing control back to the Init and Validation Lambda, so there will be unnecessary delay. To overcome this, you need to set timeout properties as session attribute in "Get Customer Input" block from connect.
Property1:
Lex V2 Property name: x-amz-lex:audio:start-timeout-ms:[intentName]:[slotToElicit]
Lex Classic Property name x-amz-lex:start-silence-threshold-ms:[intentName]:[slotToElicit]
value: 10 (or any small number, this is in millseconds)
Property2:
Only available in Lex Classic, to disable barge-in on Lex V2, you can do it for required slot from lex console
Property name: x-amz-lex:barge-in-enabled:[intentName]:[slotToElicit]
Value: false
If barge-in is not disabled, there is a chance user may speak in middle of your "Please wait..." prompt and it will not be played completely.
Official documentation for these properties:
https://docs.aws.amazon.com/connect/latest/adminguide/get-customer-input.html
https://docs.aws.amazon.com/lexv2/latest/dg/session-attribs-speech.html
Another way:
Whenever such a prompt needs to be played, store the lex context temporarily either as a contact attribute after serialization, or if too big in size to be stored as contact attribute in a store like dynamodb.
Return control back to connect, play the prompt using 'Play prompt' module in connect. To give control back to bot, you will need invoke a lambda to re-initialize Lex with the full lex context again- using PostText API and then again passing control to same bot using 'Get Customer Input'
I have implemented option1 and it works well. You can even create cover-prompt which gets played if the backend lookup takes longer than expected. The actual lookup could be delegated to another lambda so that the code-hook lambda can continue doing customer interaction ever x (say 5) seconds to keep them informed that you are still looking up information.

How Can I Use xmpp4r To Detect The Online/Offline Status Of A Given Jabber ID?

What is the proper xmpp4r way to know if a given contact is online before sending them a message?
Can you post sample xmpp4r code for doing this?
Here is my use case:
If contact online, send :normal message
Else, email contact
Here are things I have working code for:
Send messages of various types
Get a roster/contact list
Register a call back to detect changes in presence
However, I can't find a place that directly addresses a work flow like this:
Loop through each JID in your roster
If jid.is_online? == true, send IM
Else, send email
I've read that you should send a JID a message of type :headline and if that fails, you know the user is offline. In my tests, if the user is ONLINE, they'll receive a message of type headline. This is suboptimal, as users should only receive messages to read, not noise to determine online status.
I've read that on sign on, all of your contacts will bounce a presence status back at you, and that status is the sole indication that they are online - assuming that there isn't a disconnect or presence change you've yet to receive. So you should register a presence call back, record the initial users who ping you back, and then add or remove from the list based on your running roster presence callback.
If this is truly the way to do it:
Can I get some example code of how to collect all the "I'm here" presence confirmations on sign on via xmpp4r?
Why, oh why, was xmpp designed this way and why is this better than offering an "is_online_and_available" method?
So the answer here is adding a message call back and checking inside the block for the type:
m = Message.new(to, body)
cl.send(m)
cl.add_message_callback do |m|
if m.type == :error
puts "type: #{m.type}"
else
puts "not an error"
end
end
This requires threading as you have to be listening for the response.

Resources