How to properly use getUpdates method from Telegram API - python

Let's say I would like to create a simple "listener" bot which will print out the result when I send the message "say *". I would imagine that it should look like this:
import requests
import time
key = 'iknowthatyouarenotsupposedtodoitlikethat'
start_time = time.time()
while True:
result = requests.get(f'https://api.telegram.org/bot{key}/getUpdates?timeout=1').json()
if result['result'][-1]['message']['date'] < start_time: continue # Ignore old messages
message = result['result'][-1]['message']['text'].split()
if message[0] == 'say':
print(' '.join(message[1:]))
break
This is by no means an example of a great approach, but it should work fine :).
The problem here is that the result variable is filled like it is supposed to with messages from the last 24 hours, but starting with the second iteration it only receives the one or two most distant messages, which is super weird. I have found that doing time.sleep(.25) after each iteration seems to fix the issue, but this looks like such a dumb fix which may not be reliable. If this is simply rate limiting, there at least should be some indication of the error, but the code is always 200 and there are no indications of the problem.
The same happens when you try doing the request by directly inserting the link into the browser and start mashing F5, which is obvious, but it is easier to see what I am talking about this way.
After looking into the documentation I have found that this issue may be caused by short polling which is "only recommended for testing purposes", but this should be fixed by the timeout argument which I have.
I don't know how to further approach this issue, so maybe there a solution that I am not seeing?

Related

Precisely simulating an xbox controller for Unity game

I'm trying to simulate a controller via python but having issues with timing.
I've successfully implemented reading the inputs into a dictionary, and then collecting all of them into a list. I know that the game(in Unity) doesn't recognise inputs faster than 0.2s. Using the following function, I've managed write at 0.2s with an error of less than 0.001s with the following function:
def feed(line: list, interval: float = 0.1.99):
start = time_ns()
wait_ns = interval * 10**9
for moment in list:
while (time_ns() < start + wait_ns):
pass
for key, val in moment:
controller.set_value(key, val) #this feeds the keys to the game via pyxinput.
I'm sampling the input using a similar function as above. But yet, I'm not getting the correct behaviour, it seems to be a timing problem. I tried reading the synthetic inputs, and they're being input with the correct timing, i.e. the interval that I pass in(and the aforementioned error).
For reference, the following, hard coded input works correctly.
controller.set_value('AxisLy', -1) #push the stick down
sleep(0.2)
controller.set_value('AxisLy', 0) #return it to neutral
I'm at my wit's end after fighting with this for the past few days. So, any ideas on what could be going wrong, or how I may debug it?
Instead of hard coding in a sleep interval, try using Invoke() or a coroutine. I find them very simple, useful and easy to understand when you're wanting to wait a certain period of time. Alternatively, use Time.time to track the start time of a button press, and then check if (Time.time >= startTime + interval) for system clock time.
Sorry for the C#, it's what i'm more familiar with. But you should be able to get the gist of it.

Data-logging from an i2c-connected Atlas Scientific Sensor to a CSV file

I am relatively new to Python, and programming as a whole. I am progressively getting the hang of it, however I have been stumped as of late in regards to one of my latest projects. I have a set of Atlas Scientific EZO circuits w/ their corresponding sensors hooked up to my Raspberry Pi 3. I can run the i2c script fine, and the majority of the code makes sense to me. However, I would like to pull data from the sensors and log it with a time stamp in a CSV file, taking data points in timed intervals. I am not quite sure how to pull the data from the sensor, and put it into a CSV. Making CSVs in Python is fairly simple, as is filling them with data, but I cannot seem to understand how I would make the data that goes into the CSV the same as what is displayed in the terminal when one runs the Poll function. Attached is the i2c sample code from Atlas' website. I have annotated it a bit more so as to help me understand it better.
I have already attempted to make sense of the poll function, but am confused in regards to the self.file_write and self.file_read methods used throughout the code. I do believe they would be of use in this instance but I am generally stumped in terms of implementation. Below you will find a link to the Python script (i2c.py) written by Atlas Scientific
https://github.com/AtlasScientific/Raspberry-Pi-sample-code/blob/master/i2c.py
I'm guessing by "the polling function" you are referring to this section of the code:
# continuous polling command automatically polls the board
elif user_cmd.upper().startswith("POLL"):
delaytime = float(string.split(user_cmd, ',')[1])
# check for polling time being too short, change it to the minimum timeout if too short
if delaytime < AtlasI2C.long_timeout:
print("Polling time is shorter than timeout, setting polling time to %0.2f" % AtlasI2C.long_timeout)
delaytime = AtlasI2C.long_timeout
# get the information of the board you're polling
info = string.split(device.query("I"), ",")[1]
print("Polling %s sensor every %0.2f seconds, press ctrl-c to stop polling" % (info, delaytime))
try:
while True:
print(device.query("R"))
time.sleep(delaytime - AtlasI2C.long_timeout)
except KeyboardInterrupt: # catches the ctrl-c command, which breaks the loop above
print("Continuous polling stopped")
If this is the case then if looks like you can recycle most of this code for your use. You can grab the string you are seeing in your console with device.query("R"), instead of printing it, grab the return value and write it to your CSV.
I think You should add method to AtlasI2C class which will write data to file
Just type under AtlasI2C init() this method:
def update_file(self, new_data):
with open(self.csv_file, 'a') as data_file:
try:
data = "{}\n".format(str(new_data))
data_file.write(data)
except Exception as e:
print(e)
add to AtlasI2C init statement about csv file name:
self.csv_file = <my_filename>.csv # replace my_filename with ur name
and then under line 51 (char_list = list(map(lambda x: chr(ord(x) & ~0x80), list(response[1:]))) add this line:
self.update_file(''.join(char_list))
Hope its gonna help You.
Cheers,
Fenrir

Python 3 (Bot) script stops working

I'm trying to connect to a TeamSpeak server using the QueryServer to make a bot. I've taken advice from this thread, however I still need help.
This is The TeamSpeak API that I'm using.
Before the edits, this was the summary of what actually happened in my script (1 connection):
It connects.
It checks for channel ID (and it's own client ID)
It joins the channel and starts reading everything
If someone says an specific command, it executes the command and then it disconnects.
How can I make it so it doesn't disconnect? How can I make the script stay in a "waiting" state so it can keep reading after the command is executed?
I am using Python 3.4.1.
I tried learning Threading but either I'm dumb or it doesn't work the way I thought it would. There's another "bug", once waiting for events, if I don't trigger anything with a command, it disconnects after 60 seconds.
#Librerias
import ts3
import threading
import datetime
from random import choice, sample
# Data needed #
USER = "thisisafakename"
PASS = "something"
HOST = "111.111.111.111"
PORT = 10011
SID = 1
class BotPrincipal:
def __init__(self, manejador=False):
self.ts3conn = ts3.query.TS3Connection(HOST, PORT)
self.ts3conn.login(client_login_name=USER, client_login_password=PASS)
self.ts3conn.use(sid=SID)
channelToJoin = Bot.GettingChannelID("TestingBot")
try: #Login with a client that is ok
self.ts3conn.clientupdate(client_nickname="The Reader Bot")
self.MyData = self.GettingMyData()
self.MoveUserToChannel(ChannelToJoin, Bot.MyData["client_id"])
self.suscribirEvento("textchannel", ChannelToJoin)
self.ts3conn.on_event = self.manejadorDeEventos
self.ts3conn.recv_in_thread()
except ts3.query.TS3QueryError: #Name already exists, 2nd client connect with this info
self.ts3conn.clientupdate(client_nickname="The Writer Bot")
self.MyData = self.GettingMyData()
self.MoveUserToChannel(ChannelToJoin, Bot.MyData["client_id"])
def __del__(self):
self.ts3conn.close()
def GettingMyData(self):
respuesta = self.ts3conn.whoami()
return respuesta.parsed[0]
def GettingChannelID(self, nombre):
respuesta = self.ts3conn.channelfind(pattern=ts3.escape.TS3Escape.unescape(nombre))
return respuesta.parsed[0]["cid"]
def MoveUserToChannel(self, idCanal, idUsuario, passCanal=None):
self.ts3conn.clientmove(cid=idCanal, clid=idUsuario, cpw=passCanal)
def suscribirEvento(self, tipoEvento, idCanal):
self.ts3conn.servernotifyregister(event=tipoEvento, id_=idCanal)
def SendTextToChannel(self, idCanal, mensajito="Error"):
self.ts3conn.sendtextmessage(targetmode=2, target=idCanal, msg=mensajito) #This works
print("test") #PROBLEM HERE This doesn't work. Why? the line above did work
def manejadorDeEventos(sender, event):
message = event.parsed[0]['msg']
if "test" in message: #This works
Bot.SendTextToChannel(ChannelToJoin, "This is a test") #This works
if __name__ == "__main__":
Bot = BotPrincipal()
threadprincipal = threading.Thread(target=Bot.__init__)
threadprincipal.start()
Prior to using 2 bots, I tested to launch the SendTextToChannel when it connects and it works perfectly, allowing me to do anything that I want after it sends the text to the channel. The bug that made entire python code stop only happens if it's triggered by the manejadorDeEventos
Edit 1 - Experimenting with threading.
I messed it up big time with threading, getting to the result where 2 clients connect at same time. Somehow i think 1 of them is reading the events and the other one is answering. The script doesn't close itself anymore and that's a win, but having a clone connection doesn't looks good.
Edit 2 - Updated code and actual state of the problem.
I managed to make the double connection works more or less "fine", but it disconnects if nothing happens in the room for 60 seconds. Tried using Threading.timer but I'm unable to make it works. The entire question code has been updated for it.
I would like an answer that helps me to do both reading from the channel and answering to it without the need of connect a second bot for it (like it's actually doing...) And I would give extra points if the answer also helps me to understand an easy way to make a query to the server each 50 seconds so it doesn't disconnects.
From looking at the source, recv_in_thread doesn't create a thread that loops around receiving messages until quit time, it creates a thread that receives a single message and then exits:
def recv_in_thread(self):
"""
Calls :meth:`recv` in a thread. This is useful,
if you used ``servernotifyregister`` and you expect to receive events.
"""
thread = threading.Thread(target=self.recv, args=(True,))
thread.start()
return None
That implies that you have to repeatedly call recv_in_thread, not just call it once.
I'm not sure exactly where to do so from reading the docs, but presumably it's at the end of whatever callback gets triggered by a received event; I think that's your manejadorDeEventos method? (Or maybe it's something related to the servernotifyregister method? I'm not sure what servernotifyregister is for and what on_event is for…)
That manejadorDeEventos brings up two side points:
You've declared manejadorDeEventos wrong. Every method has to take self as its first parameter. When you pass a bound method, like self.manejadorDeEventos, that bound self object is going to be passed as the first argument, before any arguments that the caller passes. (There are exceptions to this for classmethods and staticmethods, but those don't apply here.) Also, within that method, you should almost certainly be accessing self, not a global variable Bot that happens to be the same object as self.
If manejadorDeEventos is actually the callback for recv_in_thread, you've got a race condition here: if the first message comes in before your main threads finishes the on_event assignment, the recv_on_thread won't be able to call your event handler. (This is exactly the kind of bug that often shows up one time in a million, making it a huge pain to debug when you discover it months after deploying or publishing your code.) So, reverse those two lines.
One last thing: a brief glimpse at this library's code is a bit worrisome. It doesn't look like it's written by someone who really knows what they're doing. The method I copied above only has 3 lines of code, but it includes a useless return None and a leaked Thread that can never be joined, not to mention that the whole design of making you call this method (and spawn a new thread) after each event received is weird, and even more so given that it's not really explained. If this is the standard client library for a service you have to use, then you really don't have much choice in the matter, but if it's not, I'd consider looking for a different library.

Optional input() statement

I'm creating an instant messenger program for my school's common drive. I have everything working except for on small detail. In the code below it checks for a new message from a friend and prints the last message they sent. If there are no messages it says so. The problem is when it moves to the next step of the code it waits for the user to put in an input. Until you give an input it won't let you receive any more messages because the program stops reading and searching the while loop and gets caught on the input statement. I want to know if there is anyway to make an input statement optional. To say that it doesn't require an input but if there is an input it will send it and do it's thing. I just can't seem to figure out a way to make the input statement optional. Any ideas or working code would be greatly appreciated. If you need the entire code I don't have a problem with sending it to you or posting it. This is the only bit of code that should really matter for this problem though.
LastMessage = ""
while Message:
Path = "Message"+SendTo+"-"+UserName+".txt"
if path.isfile(Path):
LoadMessage = open(Path, "rb")
NewMessage = pickle.load(LoadMessage)
LoadMessage.close()
else:
NewMessage = "Sorry, No messages found"
if LastMessage != NewMessage:
LastMessage = NewMessage
print(NewMessage)
print("")
SendMessage = raw_input() #--- This is where it gets caught up! ---
Save = open("Message"+UserName+"-"+SendTo+".txt", "wb")
pickle.dump(SendMessage, Save)
Save.close()
You have two main options as I see it:
Simultaneous input and checking (various options, search for e.g. threading or multiprocessing from the standard library); or
Input with timeout and loop (see e.g. How to set time limit on raw_input).
So it sounds like you want to do two separate things at the same time - look for input from a user and poll for new messages from other users. Jonrsharpe gives threading as his first option to solve this and I agree its the most straightforward. What you need to do is something like this:
import threading
class InputMessageThread(threading.Thread):
def run(self):
SendMessage = raw_input() # This thread will hang here for input but thats
# OK as original thread will keep going
Save = open("Message"+UserName+"-"+SendTo+".txt", "wb")
pickle.dump(SendMessage, Save)
Save.close()
inputthread = InputMessageThread()
inputthread.start()
# rest of your code here
While you are at it though you might want to look at some other issues. For example if I understand what you are trying to do correctly you are going to have a file containing a message from a source user to a destination user. But if the source user sends a second message before this file gets processed then the first message will be overwritten. In practice you may never see this but some sort of handshaking to make sure the message has actually been sent before you allow the next to be written would be a more robust approach.

How can I conduct a poll through an irc bot?

I've set up a irc bot using socket. I've added a few commands , but I'd like to add a "poll" function.
Ideally, the bot would get a command with this format:
!poll <name> <opt1> <opt2> <opt3> <time>
How would I go about checking user who voted and ending the poll after a certain time?
Thanks in advance,
Desperate Python Beginner.
EDIT: Thanks a lot for the responses guys, I went with using global vars ( I know, I know ) because I couldn't figure out how to do it otherwise. Again, thanks a lot!
Well, I'm starting to get a little rusty with my Python but I think I can answer that - It may not be the best answer, though.
If you plan to have many polls going at once, you could implement a dictionary containing multiple instances of a custom class like Poll.. Here's a possible solution:
class PollVotes(object):
def __init__(self):
self.votes = []
self.stoptime = "some date/time" #I can't remember how to do this bit ;)
def add_vote(self, vote_value):
self.votes.append(vote_value);
def tally_votes(self):
return self.votes.size()
def has_closed(self):
if time_now >= self.stoptime: # I forget how you'd do this exactly, but it's for sake of example
return True
else:
return False
#then use it something like this
poll_list = {}
#irc processing...
if got_vote_command:
if poll_list["channel_or_poll_name"].has_ended():
send("You can no longer vote.")
else:
poll_list["channel_or_poll_name"].add_vote(persons_vote)
#send the tally
send("%d people have now voted!" % poll_list["channel_or_poll_name"].tally_votes())
Of course, you'd have to edit the poll class to fit your needs, i.e. to allow multiple values in a vote, to record who is voting what (if you want that), etc.
As for checking if the poll has ended, you could edit the poll class to have the stop time, and have a function that returns True/False whether that time has passed or not. Possibly look at the docs for the datetime module...?
Anyway, hope this helps.

Categories

Resources