I know how to use the time.sleep() command to create a delay in a script, but I'm developing a chatango bot and I want to prevent it from flooding the chat by putting a 3 second delay after each command. However, I'm not sure how to make that happen exactly, and my current attempt just makes the script itself delay for 3 seconds. This is what I have as an example:
s = message.body
if 'test' in s:
print(room.message("This is a sample."))
import time
time.sleep(3)
I'm not quite sure what to do from here, so any help would be appreciated, thank you.
NOTE: This is NOT the same question as "How can I make a time delay in Python?" No answers on that question helped me in any way, it's a different thing I'm asking for.
NOTE AGAIN: People keep implying it's the same question as another one and IT IS NOT. It is a DIFFERENT QUESTION. Why is that so hard for you people to grasp?
Save time of the last reply into instance variable inside of onMessage(). Renew it each time you print message into the room. Then only reply if :
import time
class Bot(ch.RoomManager):
def onMessage(self, room, user, message):
NOW = time.time()
s = message.body
if 'test' in s AND NOW - 3 > self._last_sent:
print(room.message("This is a sample."))
self._last_sent = NOW
This is a method i use for a game command on my bot to make a time-delay on a command.
import time
time_dict = dict()
class Bot(ch.RoomManager)
def onMessage(self, room, user, message):
s = message.body
if 'test' in s:
try:
if room in time_dict:
timeset = time_dict[room]
else:
timeset = time.time()-3
time_dict[room] = timeset
if time.time() - float(timeset) < 150:
return
else:
room.message("This is a sample.")
Try this:
import ch
import time
z = dict()
class bot(ch.RoomManager):
def onMessage(self, room, user, message):
s = message.body
if 'test' in s:
if room.name in z:
if time.time() < z[room.name]:
return
else:
z[room.name] = time.time()+3
print(room.message("This is a sample."))
else:
z[room.name] = time.time()+3
print(room.message("This is a sample."))
if __name__ == "__main__":
bot.easy_start()
Related
Can anyone help me out, I'm trying to get the program to pause if the condition is met. But as of now, its not sleeping at all. And I can't wrap my head around why. Im completely new to asyncio
time.sleep() doesnt really work either, so I would prefer to use asyncio. Thanks alot!
from python_graphql_client import GraphqlClient
import asyncio
import os
import requests
loop = asyncio.get_event_loop()
def print_handle(data):
print(data["data"]["liveMeasurement"]["timestamp"]+" "+str(data["data"]["liveMeasurement"]["power"]))
tall = (data["data"]["liveMeasurement"]["power"])
if tall >= 1000:
print("OK")
# schedule async task from sync code
asyncio.create_task(send_push_notification(data))
print("msg sent")
asyncio.create_task(sleep())
client = GraphqlClient(endpoint="wss://api.tibber.com/v1-beta/gql/subscriptions")
query = """
subscription{
liveMeasurement(homeId:"fd73a8a6ca"){
timestamp
power
}
}
"""
query2 = """
mutation{
sendPushNotification(input: {
title: "Advarsel! Høyt forbruk",
message: "Du bruker 8kw eller mer",
screenToOpen: CONSUMPTION
}){
successful
pushedToNumberOfDevices
}
}
"""
async def sleep():
await asyncio.sleep(10)
async def send_push_notification(data):
#maybe update your query with the received data here
await client.execute_async(query=query2,headers={'Authorization': "2bTCaFx74"})
async def main():
await client.subscribe(query=query, headers={'Authorization': "2bTCaFxDiYdHlxBSt074"}, handle=print_handle)
asyncio.run(main())
If I understand correctly, you want to observe broadcasts of some data, and react to those broadcasts, keeping the right to pause those reactions. Something like:
async def monitor(read_broadcast):
while True:
data = await read_broadcast()
print(data["data"]["liveMeasurement"]["timestamp"]+" "+str(data["data"]["liveMeasurement"]["power"]))
tall = (data["data"]["liveMeasurement"]["power"])
if tall >= 1000:
print("OK")
await send_push_notification(data)
print("msg sent")
# sleep for a while before sending another one
await asyncio.sleep(10)
To implement read_broadcast, we can use a "future":
# client, query, query2, send_push_notification defined as before
async def main():
broadcast_fut = None
def update_broadcast_fut(_fut=None):
nonlocal broadcast_fut
broadcast_fut = asyncio.get_event_loop().create_future()
broadcast_fut.add_done_callback(update_broadcast_fut)
update_broadcast_fut()
def read_broadcast():
return broadcast_fut
asyncio.create_task(monitor(read_broadcast))
await client.subscribe(
query=query, headers={'Authorization': "2bTCaFxDiYdHlxBSt074"},
handle=lambda data: broadcast_fut.set_result(data),
)
asyncio.run(main())
Note that I haven't tested the above code, so there could be typos.
I think the easiest way to reduce the number of messages being sent, is to define a minimum interval in which no notification is sent while the value is still over the threshold.
import time
last_notification_timestamp = 0
NOTIFICATION_INTERVAL = 5 * 60 # 5 min
def print_handle(data):
global last_notification_timestamp
print(
data["data"]["liveMeasurement"]["timestamp"]
+ " "
+ str(data["data"]["liveMeasurement"]["power"])
)
tall = data["data"]["liveMeasurement"]["power"]
current_time = time.time()
if (
tall >= 1000
and current_time - NOTIFICATION_INTERVAL > last_notification_timestamp
):
print("OK")
# schedule async task from sync code
asyncio.create_task(send_push_notification(data))
last_notification_timestamp = current_time
print("msg sent")
The timestamp of the last message sent needs to be stored somewhere, so we'll define a variable in the global scope to hold it and use the global keyword within print_handle() to be able to write to it from within the function. In the function we will then check, if the value is above the threshold and enough time passed after the last message. This way you will still keep your subscription alive as well as limit the number of notifications you receive. This is simple enough but probably you will soon want to extend the range of what you want to do with your received data. Just keep in mind that print_handle() is a sync callback and should be as short as possible.
I recognize this may be a very 101 type question, but I'm still having trouble understanding functional programming in general, and have a particular code snippet that I can't make sense of:
Full code, but leaving out most of the function definitions:
import blpapi
import sys
SESSION_STARTED = blpapi.Name("SessionStarted")
SESSION_STARTUP_FAILURE = blpapi.Name("SessionStartupFailure")
SERVICE_OPENED = blpapi.Name("ServiceOpened")
SERVICE_OPEN_FAILURE = blpapi.Name("ServiceOpenFailure")
ERROR_INFO = blpapi.Name("ErrorInfo")
GET_FILLS_RESPONSE = blpapi.Name("GetFillsResponse")
d_service="//blp/emsx.history"
d_host="localhost"
d_port=8194
bEnd=False
class SessionEventHandler():
def processEvent(self, event, session):
try:
if event.eventType() == blpapi.Event.SESSION_STATUS:
self.processSessionStatusEvent(event,session)
elif event.eventType() == blpapi.Event.SERVICE_STATUS:
self.processServiceStatusEvent(event,session)
elif event.eventType() == blpapi.Event.RESPONSE:
self.processResponseEvent(event)
else:
self.processMiscEvents(event)
except:
print ("Exception: %s" % sys.exc_info()[0])
return False
def processSessionStatusEvent(self,event,session):
print ("Processing SESSION_STATUS event")
for msg in event:
pass
def processServiceStatusEvent(self,event,session):
print ("Processing SERVICE_STATUS event")
for msg in event:
pass
def processResponseEvent(self, event):
print ("Processing RESPONSE event")
for msg in event:
global bEnd
bEnd = True
def processMiscEvents(self, event):
print ("Processing " + event.eventType() + " event")
for msg in event:
print ("MESSAGE: %s" % (msg.tostring()))
def main():
sessionOptions = blpapi.SessionOptions()
sessionOptions.setServerHost(d_host)
sessionOptions.setServerPort(d_port)
print ("Connecting to %s:%d" % (d_host,d_port))
eventHandler = SessionEventHandler()
session = blpapi.Session(sessionOptions, eventHandler.processEvent)
if not session.startAsync():
print ("Failed to start session.")
return
global bEnd
while bEnd==False:
pass
session.stop()
I can follow the code up to here:
session = blpapi.Session(sessionOptions, eventHandler.processEvent)
Here, I see I'm calling "Session" from the blpapi library, and passing it some options as well as my eventHandler.processEvent. Here is where I get lost. I look at that particular function, and see:
def processEvent(self, event, session):
try:
if event.eventType() == blpapi.Event.SESSION_STATUS:
self.processSessionStatusEvent(event,session)
elif event.eventType() == blpapi.Event.SERVICE_STATUS:
self.processServiceStatusEvent(event,session)
elif event.eventType() == blpapi.Event.RESPONSE:
self.processResponseEvent(event)
else:
self.processMiscEvents(event)
except:
print ("Exception: %s" % sys.exc_info()[0])
return False
I see that the function is attempting to discern what type of event has been passed in, and will execute a different function within the class depending on that event type. The trouble is, I can't figure out where the event is ever specified! Where does "event" come from? I see it as an argument in that particular function, but no event argument was passed to:
session = blpapi.Session(sessionOptions, eventHandler.processEvent)
So how does it know what to do at this point? How did this "event" object magically appear?
Thanks for entertaining my dumb questions
session = blpapi.Session(sessionOptions, eventHandler.processEvent)
Note that processEvent here lacks parentheses () after it. This means you are passing the function itself as a parameter to the Session class. This class will later call processEvent with appropriate parameters.
Side Note:
I'm still having trouble understanding functional programming
"Functional programming" has a very specific definition and this example isn't it. If you are interested, you can google "functional programming" or read the Wikipedia article to find out more. However, this isn't really important at this stage in your learning process.
I'm coding a pretty simple Twitch Chatbot in Python and I have all my "commands" running in if statements (basically if the chatbot sees something in chat it activates).
However, I want to add a cooldown of about 3 seconds or really an amount of time per command (or if statement), so that I can customize it depending. To do that I tried this (pseudo code)
if command is seen in chat
newtimestamp = create timestamp at now
if (newtimestamp - previoustimestamp) > 30
execute the command
make (previoustimestamp)
else
return
but that didn't work because obviously it does not detect the (previoustimestamp) on the first run since it's not declared yet, but when you declare it, it declares it every time the command is run.
So is there a way to make a declaration only once at the beginning of an if statement and then ignore it subsequently every other time? Or any other ideas? I'm still a pretty novice programmer so I apologize.
Here's an example code that I would want the cooldown for, something quite simple
if "!redb match" in message:
x = str(randint(1, 100))
realmessage = message.replace('!redb ship ', '')
array = realmessage.split(' ')
person1 = array[0]
person2 = array[1]
if ((9 - 2) > 5):
sendMessage(s, person1 + " and " + person2 + "have a " + x + "% love compatibility <3")
else:
break
I'd use a dictionary for the timestamps, accessing it like previous_timestamp = command_timestamps.get('!redb match', 0). This will give you the stored timestamp if it's in the dictionary, and 0 (Jan 1 of 1970 if you're using time.time() for your timestamps, which is hopefully far enough in the past that it won't foul up any of your cooldowns).
You could write a decorator for a function that stores the last time it was called, and return None if that was too close to now
from functools import wraps
from datetime import datetime, timedelta
def cooldown(*delta_args, **delta_kwargs):
delta = timedelta(*delta_args, **delta_kwargs)
def decorator(func):
last_called = None
#wraps(func)
def wrapper(*args, **kwargs):
nonlocal last_called
now = datetime.now()
if last_called and (now - last_called < delta):
return
last_called = now
return func(*args, **kwargs)
return wrapper
return decorator
#cooldown(seconds=5)
def foo():
print("Run")
foo() # Run
time.sleep(1)
foo()
time.sleep(4)
foo() # Run
The arguments to cooldown are sent to timedelta, so you should read the documentation more on those objects
You could use time.sleep(3) to wait 3 seconds if a certain condition triggers for example. This will pause the execution for 3 seconds.
Edit: Since you want these if statements to have their own cooldowns you could try putting this within a control loop which is a bit more complex
cooldowns_dict = {"trigger_1": 0, "trigger_2":0}
while(True):
loop_start = time.time()
if ("!redb match" in message) and cooldowns_dict["trigger_1"] <= 0:
# Send message etc.
cooldowns_dict["trigger_1"] = 3 # Set a 3 second cooldown
time.sleep(0.1) # Just to stop this looping too fast
loop_end = time.time()
# update the cooldowns
for key, value in cooldowns_dict.items():
if value > 0:
cooldowns_dict[key] -= (loop_end - loop_start)
else:
cooldowns_dict[key] = 0
I'm quite new on python and working on a school project with this logic: Users have to answer a series of questions as fast as they can, within the given time.
For instance, the time allotted is 30 seconds, I wood loop through a dictionary of questions and get the answer. On timeout, the loop will start, even if the script is still waiting for an input.
def start_test():
for item on questions:
print(item)
answers.append(input(' : '))
I've tried using multiprocessing and multithreading, but I found out that stdin doesn't work subprocesses.
I'm looking for something like:
while duration > 0:
start_test()
def countdown():
global duration
while duration > 0:
duration -= 1
time.sleep(1)
# something lime start_test().stop()
But I can't figure out how to run the countdown function in parallel with the start_test function.
Any ideas?
So as far as I know the input is accessible via main thread only. I might be wrong.
However if that is the case, you need a non-blocking input.
Check this blog. The answer below is based on that.
Note: This is a really quick and dirty solution.
I have checked this on Linux.
If it doesn't work on Windows try this
link for further reference.
import _thread
import sys
import select
import time
def start_test():
questions = ['1','2','3']
answers = []
for item in questions:
print(item)
# Input in a non-blocking way
loop_flag = True
while loop_flag:
# Read documenation and examples on select
ready = select.select([sys.stdin], [], [], 0)[0]
if not ready:
# Check if timer has expired
if timeout:
return answers
else:
for file in ready:
line = file.readline()
if not line: # EOF, input is closed
loop_flag = False
break
elif line.rstrip():
# We have some input
answers.append(line)
# So as to get out of while
loop_flag = False
# Breaking out of for
break
return answers
def countdown():
global timeout
time.sleep(30)
timeout = True
# Global Timeout Flag
timeout = False
timer = _thread.start_new_thread(countdown, ())
answers = start_test()
print(answers)
In a previous question, the issue was how to implement a conditional loop (a simple for loop) to affect the onMessage event calling function, which was resolved. However, my current issue is in trying to implement time as the determinant in the conditional loop.
Currently, the code runs but only seems to do the while loop that rechecks the time, ignoring the code before it. I suspect that the issue is in the location that I have placed the time rechecking loop?
Any clarity on this issue would be appreciated!
import ch
import time
class bot(ch.RoomManager):
timeleft = 0
starttime = 0
def onMessage(self, room, user, message):
print("[{0}] {1}: {2}".format(room.name, user.name.title(), message.body))
if 100 >= timeleft >= 1:
print('success')
else:
print('fail')
loop = 1
while loop == 1 :
timeleft = starttime - int(time.clock())
if timeleft <=0:
timeleft = 200
starttime = 200
rooms = ["testgroup444"]
username = "user"
password = "name"
bot.easy_start(rooms,username,password)
For clarity on the methods used, the entire ch.py library can be found here: https://pastebin.com/8ukS4VR1
I think you still have an issue with the Python syntax. In Python whitespace is very important as it defines scope. The way your code is written, the while loop is executed at the time the class is defined, before anything is started.
Further, the code gets into an infinite loop, as while loop == 1 will be always True. This is why you see that your code gets stuck. From the discussion in the comments, I imagine you want to write something like:
import ch
import time
import enum
class State(enum.Enum):
idle = 0
nominating = 1
voting = 2
watching = 3
class bot(ch.RoomManager):
state = State.idle
movie_length = 20
def updateState(self):
if self.state in [State.idle, State.nominating]:
self.state = State.voting
timeout = 10
elif self.state == voting:
self.state = State.watching
timeout = self.movie_length - 15
else: # if self.state == watching
self.state = State.nominating
timeout = 15
self.setTimeout(timeout*60, bot.updateState, self)
def onConnect(self, room):
# Redirect through event loop, not strictly nessecary
self.setTimeout(0, bot.updateState, self)
def onMessage(self, room, user, message):
print("[{0}] {1}: {2}".format(room.name, user.name.title(), message.body))
print("Current state is {0}".format(self.state))
rooms = ["testgroup444"]
username = "user"
password = "name"
bot.easy_start(rooms,username,password)
Here, the setTimeout method defined in the ch (bot) class is used, to allow messages to be passed at certain times. At every timeout the state is updated. The actual state is then available in all internal methods, e.g. in onMessage and updateState.
As I do not use the chat network or client I cannot guarantee that the solution works, though.