How can discord bot send message and attachment to a specific user after a job done - discord.py

I am having a discord bot which takes job requests from user DM.
The job may take a few minutes and produce a file which should be sent back to the user.
I can fetch message.author.id from the async def on_message(message), and the job will be execute once the message received. Due to the job takes a while, the bot would report error as it is hanging on the job process. Therefore I have used subprocess to run the job, but now, I have not got any idea how the job could communicate to the bot and let it know as completed and send the message and attachment to the specific user.
And how about if the user is offline, would the message and attachment get delivered??
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content == None or message.content == "":
return
msg_ = message.content
author_ = str(message.author)
return_code = subprocess.call("start JOB.py msg_ author_", shell=True)

I think one potential way of doing it is keeping track of which jobs are running/have been triggered by users. Either through a file or database. The file and DB options would allow you to possibly decouple the job running/discord part as well.
You could then use a task that runs periodically to check the dict/file/db for finished jobs and then send a message to the user that it's done. It can then either delete the job or set a "message_sent" flag or equivalent so it doesn't send the notification multiple times.
The job script as part of it's cleanup/finish can then mark itself as complete within the file/db so that the "notify when job finished task" can pick that up and inform the user.

Related

Creating an automatic closing time for registrations with a discord bot

I've been learning how to make a discord bot for a while and for the most part I've grasped the fundamentals already and I'm working my way through the more advanced concepts.
At the moment, I'm stuck on formulating the logic on making a time out with registrations.
So for example, I set up a tournament with a set deadline for registrations, what would be a good approach to close the registrations?
At the moment I already have the deadline saved in the database and whenever users register via a command, it checks if the current date is > the deadline date. But this I want the bot to be able to send a message by itself and prompt in the channel that "Registrations are closed".
I realized wait_for only waits for a single command. If I put that in a loop, I have to set how many registrations I should wait for (but with this I can use reacts).
A scheduler would have to loop every few minutes/hours and check if current datetime is > deadline which isn't very accurate.
The good thing is, there can only be one tournament at a time running.
You can fetch current event from your database in on_ready, wait for that event and then send a message to channel (You also can store channel_id in your database).
Check the example below.
#bot.event
async def on_ready():
event_data = something # fetch current event data from your database
if event_data[‘end_time’] > datetime.utcnow().tinestamp():
await asyncio.sleep(event_data[‘end_time'] - datetime.utcnow().timestamp()) # wait for event
channel = bot.get_channel(event_data[‘channel_id’])
if channel is not None:
await channel.send(“Event has been finished!”)
# you can delete this event from your database here
You also can use scheduler to schedule a task instead of asyncio.wait(). For example APScheduler.

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).

storing messages with a discord bot

I'm trying to store messages with a discord bot, so that I can learn how the elements of messages vary between messages.
However i am new to some aspects of this coding- i.e. decorators. Currently the piece of my bots code that interacts with messages is this:
messages=[]
#bot.event
async def on_message(message,messages):
print("someone said something")
messages=messages+message
if message.author == bot.user:
return messages
I think this is wrong. What I am trying to do is add a message to messages each time the event happens, so that I can later go through that variable and see how the different elements of messages change.
How do i change the above to allow that?
You can use only 1 parameter in on_message event. Also, you can't append things to a list with +. And Also storing data in variable is not a good idea because whenever you restart the bot, it'll be deleted. You can simply store them in a txt file.
#bot.event
async def on_message(message):
print("someone said something")
file = open('messages.txt', 'a')
file.write(message.content + '\n')
file.close()
EDIT
If you want to store all the information of the message, you can do:
file.write(f'{message}\n')
or
file.write(str(message) + '\n')

How do I get a bot to forward its DMs

So my bot DM's users and asks them questions, but I need to be able to see their response in a channel.
So far I have this:
#bot.event
async def on_message(message):
channel = bot.get_channel(642759168247463937)
await channel.send('message')
but it begins when any message is sent in any channel and it also responds to itself, causing an endless loop of spam.
I'm new to Discord.py so I honestly don't have a clue on how to resolve this issue.
My approach would be to check if the message came from DMs:
#bot.event
async def on_message(message):
if not message.guild:
return
channel = bot.get_channel(642759168247463937)
await channel.send('message')
This works because message.guild is None if the message was sent in DMs. This prevents the spam problem, since the message is not forwarded to the bot's DMs.
However, to avoid the spam in general, it is good practice to avoid responding to bots altogether, unless necessary. Luckily, User and Member objects have an attribute called bot that helps us here:
if message.author.bot:
return
Note that this is done automatically for commands using the ext.commands framework.

Rasa Chatbot: Handling repeated scenario

I am working in follow up bot, each user has many tasks, when user ask about his/her tasks, the Bot will fetch tasks using API then the bot will displayed the tasks one by one and going to ask the user if he/she able to finish it today. if user said yes the the the task will marked as completed if no the bot will ask the user about finished date.
I tired many solution in Action by iterate over tasks and dispatch template but after dispatching the loop stop and never go back again.
class ActionRequestTasks(Action):
def name(self):
return "action_request_tasks"
#staticmethod
def json2obj(data):
return json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
def run(self, dispatcher, tracker: DialogueStateTracker, domain):
response = requests.get('url', headers=headers)
tasks_wrapper = self.json2obj(response.text)
data = tasks_wrapper.Data
first_message = "You have {} delayed tasks, I will help you to go through all of them".format(len(data))
dispatcher.utter_message(first_message)
for task in data:
task_message = "Task Title {}\nComplete percentage {}\nStart Date {}\nFinish Date{}".format(task.Title,
task.PercentComplete,
task.StartDate,
task.FinishDate)
dispatcher.utter_message(task_message)
dispatcher.utter_template("utter_able_to_finish", tracker)
return []
This sounds like the perfect application for a Form. You can make the API call in the required_slots() method, then use validation to fill the slots dependent on the user's response. The form will run until all slots are filled, then you can decide what to do with the slots in the submit() method (for instance, updating the task status for each one via another request).
I recommend reading the docs on Form setup and also checking out the code for formbot to see a working implementation

Resources