nats.io python client does not flush on close - python

I have a leetle test program which does not work unless I do a client.flush() before client.close(). A naive reading of nats/aio/client.py seems to suggest that close() does some kind of flush(), so I do not understand why my test program fails. Is it necessary to always flush() before close()? Example programs don't seem to indicate so.
I can see that close() calls _flush_pending(), but this does something quite different from flush():
flush() calls _send_ping() to send a PING and waits for a PONG response. _send_ping() writes directly to self._io_writer, and then calls _flush_pending().
_flush_pending() pushes a None (anything will do, I guess) into self._flush_queue. This presumably wakes the _flusher() and causes it to write everything in self._pending to self._io_writer.
publish() calls _send_command() to push the messages onto self._pending, and then also calls _flush_pending() to cause the _flusher() to write everything.
The test program:
#!/usr/bin/env python3.5
import asyncio
import nats.aio.client
import nats.aio.errors
async def send_message(loop):
mq_url = "nats://nats:password#127.0.0.1:4222"
client = nats.aio.client.Client()
await client.connect(io_loop=loop, servers=[mq_url])
await client.publish("test_subject", "test1".encode())
#await client.flush()
await client.close()
def main():
loop = asyncio.get_event_loop()
loop.run_until_complete(send_message(loop))
loop.close()
if __name__ == '__main__':
main()
FWIW if I send a large number of messages then, at some point (haven't yet worked out the exact conditions), messages are sent. We noticed the behaviour when processing a collection of files and sending a message for each line in the file: some files were making it through, others weren't, and it turned out it was the larger files (more lines) that made it. So it looks as though some internal buffer is filled and this forces a flush.

Looks like was probably a bug which has just been fixed: https://github.com/nats-io/asyncio-nats/pull/35

Related

pyttsx3 wait to finish?

im using pyttsx3 for my telegram bot. i have this code.
def TTSBot(message):
print("TTS")
Engine.save_to_file(message.text, "Voice.mp3")
Engine.runAndWait()
FBot.send_audio(message.chat.id, open("Voice.mp3", "rb"), reply_to_message_id=message.id)
print("done")
this executes when the command is called but it sends the previous command. it dosnt wait for Engine to finish it's work. how do i make it wait?
i tried using Engine.runAndWain() inside the send_audio bot it dosnt return the Voice file.
also if you know a way to not save the file and just directly input it into the commands let me know.
Please specify your operating system next time you deal with audio in case you are on linux and you simply need to install a few packages to fix this.
Now, the problem you requested is not regarding pyttsx3 but regarding FBot.
I assume you are using python-telegram bot because the API looks alike. Within its docs the function bot.send_audio is async which means you must mark your function as asynchronous to await the function FBot.send_audio().
You can, of course, put the event loop inside TTSBot() but it's better to put it in their caller.
This is the complete code for reference.
import asyncio
# import other libraries
# other code
async def TTSBot(message):
# print("TTS") (it is not advised to put simple messages like this, instead use a logger)
Engine.save_to_file(message.text, "Voice.mp3")
Engine.runAndWait()
await FBot.send_audio(message.chat.id, open("Voice.mp3", "rb"), reply_to_message_id=message.id)
#print("done") (it is not advised to put simple messages like this, instead use a logger)
def caller(): # put real function
# other code
asyncio.run(TTSBot(msg))
# if you are using Python3.6 or lower, comment the previous line and uncomment the following lines
# loop = asyncio.get_event_loop()
# loop.run_until_complete(TTSBot(msg))
# loop.close()
# other code
UPDATE
Since you are using pytelegrambotapi, in its documention the audio file shall not be of type str but of type telebot.types.InputFile. Thus, you must pass it using that class.
def TTSBot(message):
Engine.save_to_file(message.text, "Voice.mp3")
Engine.runAndWait()
FBot.send_audio(message.chat.id, InputFile("Voice.mp3"), reply_to_message_id=message.id)
And there's no need to modify the caller now.
Also I don't know how your caller works but I'd also like to give you a note that the function send_audio returns the message.

Carry bots tasks out over restarts and shutdowns

I have a simple python bot within discord.
Lets say my code is as follows:
#client.command(pass_context=True)
async def test(ctx):
await asyncio.sleep(500)
await ctx.send("Hello!")
This code will have the bot wait 500 seconds before sending "Hello! in chat. If I shutdown my bot and restart it, this timer will obviously be lost and unsaved.
How can I carry this function out across restarts and such?
The way I implement something like this is with a try-finally loop, the finally part executes even if you stop/restart your bot (not with every method of killing the process)
try:
client.run(TOKEN)
finally:
save('data.json', data)
To make this work you would expand your command that requires restart with a part that saves to a global dictionary/list. Something like (hellosave is the global list here):
hellosave.append((time.time() + 500, ctx.channel.id))
await asyncio.sleep(500)
await ctx.send("Hello!")
hellosave.pop(0)
As save() you would take that global dict/list and save it in some format (you can do it any way you'd like, from pickling to printing it in a .txt file [I chose json here cuz I needed it to be readable, that might not be the case in your case])
Next time you boot up your bot, in the on_ready() event you look for the contents of that file and restart the function based on the data saved in it, like for example your hello case you'd check if the time in the saved event has already passed, if it has you send it immediately (or not at all) if it hasnt you create a task with asyncio.create_task that waits for the amount it needs (time.time() - hellotime) [to make this work you have to save the channel id, like I did in my example]

"on_message" of Discord.py can't work after I implemented "schedule" into it

I'm not familiar with Python so I'm afraid that this could be a silly question for all of you but I have already tried my best to solve it.
I worked on a self-project of making a bot to do some silly task just for fun.
First, I have done a function to let it receive a message with "on_message" event, which works well. here's the code
import discord
import os
discordClient = discord.Client()
#discordClient.event #event register
async def on_ready():
print('Logged in as {0.user}'.format(discordClient))
await routineAnnouncer.createDisocrdRoutine(discordClient)
#discordClient.event
async def on_message(message):
checked = False
if message.author == discordClient.user:
checked = True
return
else:
print("Get Message {0.content} from {0.author.name} ({0.author.id}) #{0.channel.name} ({0.channel.id})".
format(message))
#-------------------------- RUN ----------------------
print('Registering EVENTS COMPLETE')
discordClient.run(os.environ['discordToken'])
I code it on Repl.it and "os.environ" is its way to retrieve .env.
here are the code in "routineAnnouncer" that is used in the "on_ready" event
import os #ใช้ดึง .env (secret)
import schedule
import time
def scheduleAnnouncer(discordClient,announceType,announceDetail):
print("Announcing {} {}".format(announceType,announceDetail))
lzChannel = discordClient.get_channel(int(os.environ['lz_token_test']))
discordClient.loop.create_task(lzChannel.send("Announcing {} {}".format(announceType,announceDetail)))
async def createDisocrdRoutine(discordClient):
print("Registering CRONJOBS...")
scheduleAnnouncer(discordClient,"headerTest","descTest")
schedule.every(5).seconds.do(scheduleAnnouncer,discordClient,"header1","desc1")
schedule.every(10).seconds.do(scheduleAnnouncer,discordClient,"header2","desc2")
# while True:
# schedule.run_pending()
# time.sleep(5)
So, it supposed to do createDisocrdRoutine() after the connection is ready and set the scheduling text.
As you can see the last section of the code is commented. It is supposed to be a loop that triggers the scheduled text to be sent via discord to a designated channel.id.
To this point. The code works fine. the "on_message(message)" section able to print out whatever is sent to a channel.
The function "scheduleAnnouncer" also works fine. It can send message to the channel.
Discord Screen Repl.it Screen
After a loop "while: True" is uncommented in order to let the schedule work.
The loop works fine. It prints out the text as shown in the loop. but discord can't detect any text that is sent to the same channel before. Even the function "scheduleAnnouncer" that supposed to send a message is broken. It feels like anything involved with discord is broken as soon as the "while: True" is uncommented.
Discord Screen Repl.it Screen
I tried to separate the scheduling into another thread but it didn't work. I tried to use other cronjob managements like cronjob, sched or something else. Most of them let me face other problem.
I need to send argument into the task (discordClient, announceType, announceDetail).
I need to use the specific date/time to send. Not the interval like every 5 seconds. (The text that needs to be sent differ from time and day. Monday's text is not that same as Friday's text)
From both criteria. The "schedule" fits well and suppose to works fine.
Thank you in advance. I don't know what to do or check next. Every time I try to solve it. I always loop back to "schedule" and try to works with it over and over.
I hope that these pieces of information are enough. Thank you so much for your time.
time.sleep() is not async so it will freeze your program. Use await asyncio.sleep(). This is because it is pausing the whole program, not just that function.
The reason you’d want to use wait() here is because wait() is non-blocking, whereas time.sleep() is blocking. What this means is that when you use time.sleep(), you’ll block the main thread from continuing to run while it waits for the sleep() call to end. wait() solves this problem.
Quote From RealPython

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.

Twisted non-blocking method - how to?

My code looks like this:
... # class Site(Resource)
def render_POST(self,request)
otherclass.doAssync(request.args)
print '1'
return "done" #that returns the HTTP response, always the same.
...
def doAssync(self,msg):
d = defer.Deferred()
reactor.callLater(0,self.doStuff,d,msg)
d.addCallback(self.sucess)
def doStuff(self,d,msg):
# do some stuff
time.sleep(2) #just for example
d.callback('ok')
def sucess(msg):
print msg
The output:
1
ok
So far, so good, but, the HTTP response (return 'done'), only happens after the delay (time.sleep(2)).
I can tell this, because the browser keeps 'loading' for 2 seconds.
What am I doing wrong?
What you are doing wrong is running a blocking call (time.sleep(2)), while Twisted expects you to only perform non-blocking operations. Things that don't wait. Because you have that time.sleep(2) in there, Twisted can't do anything else while that function is sleeping. So it can't send any data to the browser, either.
In the case of time.sleep(2), you would replace that with another reactor.callLater call. Assuming you actually meant for the time.sleep(2) call to be some other blocking operation, how to fix it depends on the operation. If you can do the operation in a non-blocking way, do that. For many such operations (like database interaction) Twisted already comes with non-blocking alternatives. If the thing you're doing has no non-blocking interface and Twisted doesn't have an alternative to it, you may have to run the code in a separate thread (using for example twisted.internet.threads.deferToThread), although that requires your code is actually thread-safe.

Categories

Resources