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.
Related
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
I am new to pyhton APIs. I can not get the script to return a value. Could anyone give me a direction please. I can not get the lambda function to work properly. I am trying to save the streamed data into variables to use with a set of operations.
from tda.auth import easy_client
from tda.client import Client
from tda.streaming import StreamClient
import asyncio
import json
import config
import pathlib
import math
import pandas as pd
client = easy_client(
api_key=config.API_KEY,
redirect_uri=config.REDIRECT_URI,
token_path=config.TOKEN_PATH)
stream_client = StreamClient(client, account_id=config.ACCOUNT_ID)
async def read_stream():
login = asyncio.create_task(stream_client.login())
await login
service = asyncio.create_task(stream_client.quality_of_service(StreamClient.QOSLevel.EXPRESS))
await service
book_snapshots = {}
def my_nasdaq_book_handler(msg):
book_snapshots.update(msg)
stream_client.add_nasdaq_book_handler(my_nasdaq_book_handler)
stream = stream_client.nasdaq_book_subs(['GOOG','AAPL','FB'])
await stream
while True:
await stream_client.handle_message()
print(book_snapshots)
asyncio.run(read_stream())
Callbacks
This (wrong) assumption
stream_client.add_nasdaq_book_handler() contains all the trade data.
shows difficulties in understanding the callback concept. Typically the naming pattern add handler indicates that this concept is being used. There is also the comment in the boiler plate code from the Streaming Client docs
# Always add handlers before subscribing because many streams start sending
# data immediately after success, and messages with no handlers are dropped.
that consistently talks about subscribing - also this word is a strong indicator.
The basic principle of a callback is that instead you pull the information from a service (and being blocked until it's available), you enable that service to push that information to you when it's available. You do this typically be first registering one (or more) interest(s) with the service and after then wait for the things to come.
In section Handling Messages they give an example for function (to provide by you) as follows:
def sample_handler(msg):
print(json.dumps(msg, indent=4))
which takes a str argument which is dumped in JSON format to the console. The lambda in your example does exactly the same.
Lambdas
it's not possible to return a value from a lambda function because it is anonymous
This is not correct. If lambda functions wouldn't be able to return values, they wouldn't play such an important role. See 4.7.6. Lambda Expressions in the Python 3 docs.
The problem in your case is that both functions don't do anything you want, both just print to console. Now you need to get into these functions to tell what to do.
Control
Actually, your program runs within this loop
while True:
await stream_client.handle_message()
each stream_client.handle_message() call finally causes a call to the function you registered by calling stream_client.add_nasdaq_book_handler. So that's the point: your script defines what to do when messages arrive before it gets waiting.
For example, your function could just collect the arriving messages:
book_snapshots = []
def my_nasdaq_book_handler(msg):
book_snapshots.append(msg)
A global object book_snapshots is used in the implementation. You may expand/change this function at will (of course translating the information into JSON format will help you accessing it in a structured way). This line will register your function:
stream_client.add_nasdaq_book_handler(my_nasdaq_book_handler)
I am relatively new to coding and to python. I am trying to make a telegram bot using telebot.
I have a flow in which I have to go. eg: after /start, I should get a text. So I created a running_work_list in which my workflow is arranged in order and after completion, I delete the index 0 elements. 'start' is the first element in the array.
So I tried to implement something like this so that I can comfortably code each step rather than looking at input and deciding.
But despite putting inside an if statement, '#bot.message_handler()' is running even if the condition fails.
if running_work_list[0]=='start':
print('inside if')
#bot.message_handler(commands=['start','new test'])
def start(message):
print('user sent start')
bot.send_message(message.chat.id,welcome_message)
running_work_list.pop(0)
print(f'work flow deciding list {running_work_list}')
#bot.message_handler(content_types=['text','photo','poll'])
def bot_intro(message):
print('here')
print(f'user sent {message.text}')
bot.send_message(message.chat.id,BOT_INTRO_MSG)
Below are the outputs I got. The user had sent /start first and then some other random text.
inside if
user sent start
work flow deciding list ['test_name', 'test_description', 'test_time', 'test_image', 'test_poll']
here
user sent fdf
Is it like the '#bot.message_handler()' will run even if we put it inside an if statement?
Executing #bot.message_handler decorator marks the function to be called when the message matches the filters that you've passed as arguments (content_types or commands). They are called by the telebot library directly and do not execute the if statement after they have been registered.
The proper way to do this is to invert the logic: define the message handler functions at the top level of the program and decide what you should do in the if within the function.
I am trying to use keypress detection in my code these days and it seems hard to push a keypress detection function to the discord.py bot loop function
Apparently this is what I have been trying to do
import pyscreenshot as ps
import keyboard
from PIL import Image
from discord.ext import commands
client=commands.Bot("!")
async def keypress_detector():
while True:
try:
if keyboard.is_pressed('['):#if key '[' is pressed
im=ps.grab()
im.save("screenie.png")
image_obj = Image.open("screenie.png")
cropped_image = image_obj.crop((130, 445, 580, 812))
cropped_image.save("updated.png")
await client.send_file(client.get_channel('id-here'), "updated.png")
else:
pass
except Exception as ex:
return print(str(ex))
if __name__ == "__main__":
client.loop.create_task(keypress_detector())
client.run("TOKEN")
The function works pretty well if i just want to run it normally(outside the bot loop, although it doesnt send the screenshot to the channel), i want the bot to send a screenshot to the chat when i press a certain key. If there is any other way, please tell me.
UPDATE
Got it fixed, didnt work for the first few times but now it does.
Due to while True causing an infinite loop that is not asynchronous (discord.py relies on this) you will run into problems. I'm not sure if what you are trying to achieve is actually possible in the realms of python, or any language for that matter. I'm not sure what you're trying to achieve here either, none the less correct me if you know I'm wrong.
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.