Python asyncio bot with MongoDB - python

My experience in developing bots is very small and I recently had some problems... I need your help, options. This would be a great experience for me.
I need to make an asyncio Bot with automatic points for users (about 20000 users) every 10 minutes without lags and stopping the bot. My solution is to use Thread (library threading), but it takes a long time (about 20 minutes).
Question: Is there a better solution than this, since I'm pretty sure it's not the most efficient way to solve my problem?
while True:
# Thread for function to accrual points
pay_users = threading.Thread(
target=pay_function, args=[state, money])
# Thread start
pay_users.start()
while True:
# Check the end of thread
if pay_users.is_alive() is True:
await asyncio.sleep(5)
else:
# If Thread has been completed - join this Thread
pay_users.join()
break
# Every 10 minutes
await asyncio.sleep(600)
By the way, there is 1 other question that worries me. Let's say each user has a certain bonus that can be used(activated) every 24 hours. After the user has taken the bonus, he can click on the button and check how much time is left to restore the bonus. I want to make it so that when the user's ability to activate the bonus is restored, he will be informed about it. I thought about it and I came up with a trivial solution - at the moment of clicking on the bonus change the date field in the database to the date of the click, but I don't know how to make a notification with this solution about restoring the bonus.
Question: Is there any way to make an individual counter for each user, at the end of which to send a message to the function to notify the user about it?
Thank you very much in advance for your attention and trying to help me!

Related

Can someone explain to me how asyncio and regular time.sleep() works?

I'm making discord bot in python. I want it to move quickly mentioned user between two chanels and then return to channel that he was before. mentioned_member is member that i get from message mention, channel_one and channel_two are channels to move aruond and channel_to_return is assigned few lines earlier as users current voice channel. It works, but not as intended - user is being moved between two channels as I wanted, but last move to channel_to_return executes after few seconds. I think this is because moving is corutine and time.sleep is executed in normal, synchronus way. Could someone explain how this exactly works?
'''
for i in range(5):
await mentioned_member.move_to(channel_one)
time.sleep(0.2)
await mentioned_member.move_to(channel_two)
time.sleep(0.2)
await mentioned_member.move_to(channel_to_return)
'''
You haven't detailed how you don't understand this. Yes, the final move can be executed in a few seconds. sleep is synchronous. await simply waits for the asynchronous call to return -- in short, it turns that asynchronous call into a normal synchronous one, as far as this level of execution is concerned.
You loop through this 5 times, executing a total of 10 sleep delays, 0.2 seconds each. That's a 2-second delay built in. Add in the execution times of your eleven move_to operations, and you have your total time for this block of code.
The delay in seconds is simply
10 * 0.2 + 11 * <delay of move_to>
Does that clear up your understanding?

Doing hour long tasks in a non-blocking way. [Python: Reddit PRAW]

I'm creating a Reddit bot using PRAW (Python Reddit API Wrapper) for a subreddit I moderate that comments on new submissions requesting that the poster comments on their post in order to comply with the posting rules. If the poster has not commented on their post within 1 hour then the bot should remove the post. The sequence of events looks like this:
A post is made.
The bot comments on the post telling the poster that they have one hour to add a comment to the post.
An hour passes.
If the user has not commented on their post then the post is removed. Otherwise, no action is taken.
The problem I have is with waiting for one hour. I cannot use sleep() to block for one hour because the bot will need to process other posts that have been made in that time frame (i.e. posts are made every fifteen minutes but using sleep() for one hour would cause the bot to fall behind). I also don't think I can use polling as checking for submissions is blocks the thread. To elaborate, I am checking for new submissions using for submission in subreddit.stream.submissions(skip_existing=True): where subreddit.stream.submissions() is a generator/ stream that yields whenever someone submits a post to the subreddit (Documentation here).
So at this point, I'm completely lost on where to go. I need to create a new task for every post that is made that runs through steps 1 to 4 without blocking more identical tasks being made whenever a post is submitted. If you could provide a pointer on which direction to go or how I might do this I would be grateful. In case you missed it, I'm using Python.
You might want to use 'RQ' (Redis Task Queue). It will add a new dependency in your application but you will get what you want. You can refer to the Docs here.
For me this task looks like job for threading.Timer. Example usage
import threading
def do_action(x):
print(f'Doing {x}')
t1 = threading.Timer(30.0, do_action, ['A'])
t1.start()
t2 = threading.Timer(20.0, do_action, ['B'])
t2.start()
t3 = threading.Timer(10.0, do_action, ['C'])
t3.start()
will print Doing C, Doing B, Doing A with 10 second between each action.

How to run tasks periodically without interrupting the whole program

I have a program that constantly runs if it receives an input, it'll do a task then go right back to awaiting input. I'm attempting to add a feature that will ping a gaming server every 5 minutes, and if the results every change, it will notify me. Problem is, if I attempt to implement this, the program halts at this function and won't go on to the part where I can then input. I believe I need multithreading/multiprocessing, but I have no experience with that, and after almost 2 hours of researching and wrestling with it, I haven't been able to figure it out.
I have tried to use the recursive program I found here but haven't been able to adapt it properly, but I feel this is where I was closest. I believe I can run this as two separate scripts, but then I have to pipe the data around and it would become messier. It would be best for the rest of the program to keep everything on one script.
'''python
def regular_ping(IP):
last_status = None
while True:
present_status = ping_status(IP) #ping_status(IP) being another
#program that will return info I
#need
if present_status != last_status:
notify_output(present_status) #notify_output(msg) being a
#program that will notify me of
# a change
last_status = present_status
time.sleep(300)
'''
I would like this bit of code to run on its own, notifying me of a change (if there is one) every 5 minutes, while the rest of my program also runs and accepts inputs. Instead, the program stops at this function and won't run past it. Any help would be much appreciated, thanks!
You can use a thread or a process for this. But since this is not a CPU bound operation, overhead of dedicating a process is not worth it. So a thread would be enough. You can implement it as follows:
import threading
thread = threading.Thread(target=regular_ping, args=(ip,))
thread.start()
# Rest of the program
thread.join()

Executing code after waiting for time in background (Python)

I'm making a personal assistant like Google Assistant or Siri, and I want the user to be able to set reminders. For example, if they type "Remind me to wash the dishes at 5pm" I would like it to pop up later and remind them. However I also want code to be able to run while waiting, so you could set multiple reminders or check the weather.
time.sleep simply stops the program. I'm pretty sure there's a way to do it with threads but I'm not sure how. Please help!
Python threading has a Timer which does exactly what you ask for:
from datetime import datetime
from threading import Timer
def create_notification(time, name):
delay = (time - datetime.now()).total_seconds()
Timer(delay, show_notification, args=[name]).start()
def show_notification(name):
print(f'notification: {name}!')
create_notification(datetime(2034, 1, 1), 'Hello future!')
One thing to watch out for is this approach creates a single thread for each event (which doesn't scale well for lots of events). This also suffers from the problem that if the user closes your program, your program crashes, computer shuts down, power loss, etc. you lose all of your notifications. If you need to handle this, then save them to a file. If you need the notifications to show up even when your program isn't running look into solutions provided by the OS like cronjobs.

How to detect user inactivity time in python application (Linux)?

I am wondering if there is any way that when I ask for user input, if there is a way to detect how many seconds have passed since asking for user input?
I would like to be able to have some kind of timer, that if the user hasn't entered an answer and pressed enter for 15 minutes, to jump to another function, or set the user input to some default value and continue on.
Here is the input for the user:
res = input('Test result? "(P)ass,(F)ail,(R)etry,(S)ave and Quit,(Q)uit": ')
after 15 minutes(900 secs), set res = "S" and continue on.
Or something similar.
My investigation into something similar has lead me to believe the solution is likely not cross-platform. And I am running this script in Red Hat Linux. Also, I am accessing and executing the script via PuTTY.
Any help would be greatly appreciated.
Thank you.
It is actually easy to do this in a cross platform way.
You should be able to do this by launching a thread to handle the user input, and monitoring both the thread and the time from the main loop.
Some relevant points:
The threading module's doc can be found here.
Your code
res = input('Test result? "(P)ass,(F)ail,(R)etry,(S)ave and Quit,(Q)uit": ')
should run from the child thread.
For the resolution you need, you can monitor the time via the time module.

Categories