Telegram Bot presses Inline button too long - telegram

I made a bot that should click on inline buttons.
My problem is that one click on the buttons takes a whole 15 seconds.
How can this process be accelerated?
from telethon.sync import TelegramClient
from telethon import functions, types
from telethon import TelegramClient, sync
import time
AllLogs = open('logs.txt')
for logs in AllLogs:
api_id, api_hash = logs.split(';')
client = TelegramClient(f'session_{api_id}', api_id, api_hash)
client.start()
messages = client.get_messages(int(ChannelId), ids=int(MessageId))
start_time1 = time.time()
messages.click(0)
print("--- %s seconds ---" % (time.time() - start_time1))
client.disconnect()

You cannot accelerate this process. The click method sends a request to Telegram, which then sends an update to the bot. The bot must answer the update to Telegram, which will then "forward" the result back to your original click.
In essence, click must wait for the bot to answer the query, and if it does not answer to it fast enough, it will be slow. The bot may need to be fixed to answer faster (or at all; if it doesn't answer, Telegram times out and click returns None).
What you can do is spawn a new asyncio task to run the click "in the background", however, it's not good practice, as one should always await all spawned tasks, and if the code depends on the bot's response, you will have to wait for it one way or another. I don't recommend this approach, but it technically "works".
import asyncio
...
asyncio.create_task(messages.click(0))

Related

Can I share an object between telegram commands of a bot?

I want to create an object when the user press /start in a Telegram bot, and then share this object among all the commands of the bot. Is this possible? As far as I understand, there's only one thread of your bot running in your server. However, I see that there is a context in the command functions. Can I pass this object as a kind of context? For example:
'''
This is a class object that I created to store data from the user and configure the texts I'll display depending on
the user language but maybe I fill it also with info about something it will buy in the bot
'''
import configuration
from telegram import Update, ForceReply
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackContext
# Commands of the bot
def start(update: Update, context: CallbackContext) -> None:
"""Send a message when the command /start is issued."""
s = configuration.conf(update) #Create the object I'm saying
update.message.reply_markdown_v2(s.text[s.lang_active],
reply_markup=ForceReply(selective=True),
)
def check(update: Update, context: CallbackContext) -> None:
"""Send a message when the command /start is issued."""
s = configuration.conf(update) # I want to avoid this!
update.message.reply_markdown_v2(s.text[s.lang_active],
reply_markup=ForceReply(selective=True),
)
... REST OF THE BOT
python-telegram-bot already comes with a built-in mechanism for storing data. You can do something like
try:
s = context.user_data['config']
except KeyError:
s = configuration.confi(update)
context.user_data['config'] = s
This doesn't have to be repeated in every callback - you can e.g.
use a TypeHandler in a low group to create the config if needed. then in all handlers in higher groups, you don't need to worry about it
use a custom implementation of CallbackContext that adds a property context.user_config
Disclaimer: I'm currently the maintainer of python-telegram-bot.

pyTelegramBotAPI message handlers can't see photo

I need my telegram bot to forward messages from the private chat to the customer care staff group.
I run this code:
#bot.message_handler(func=lambda message: message.chat.type=='private')
def forwarder(message):
bot.forward_message(group, message.chat.id, message.id)
bot.send_message(group, '#id'+str(message.chat.id))
It works smoothly with text messages, but does nothing with photos.
Even if I remove all previous message handlers, so there is no conflict with them to handle photos, it still keeps doing nothing.
If I use the get.updates() method I can check "manually" if the photo has arrived and I find it.
Edit: Even if i just run this code only
import telebot
bot = telebot.TeleBot("MY TOKEN")
#bot.message_handler(func=lambda message: True)
def trivial(message):
print('yes')
bot.polling()
I get 'yes' for text messages and absolutely nothing, not even raised exceptions for photos.
If you add content_types as a parameter you will receive whatever is specified in the argument (which takes an array of strings). Probably the default value for that parameter is set to ['text']. Which will only listen for 'text' messages.
To get the results you're looking for
your test code will look like:
import telebot
bot = telebot.TeleBot("MY TOKEN")
#bot.message_handler(func=lambda, message: True, content_types=['photo','text'])
def trivial(message):
print('yes')
bot.polling()
And your working code:
#bot.message_handler(func=lambda, message: message.chat.type=='private', content_types=['photo','text'])
def forwarder(message):
bot.forward_message(group, message.chat.id, message.id)
bot.send_message(group, '#id'+str(message.chat.id))
In Telegram Bot API there is a resource /sendphoto
See:
Sending message in telegram bot with images
Try to find the related method in the PyTelegramBotApi.
Or implement the function to send a photo your own (e.g. using requests library in Python). Then you can use it in the message-handlers of your bot, to forward images.

How to get discord bot to handle separate processes/ link to another bot

I am trying to create something of an application bot. I need the bot to be triggered in a generic channel and then continue the application process in a private DM channel with the applicant.
My issue is this : The bot can have only one on_message function defined. I find it extremely complicated (and inefficient) to check everytime if the on_message was triggered by a message from a DM channel vs the generic channel. Also, makes it difficult to keep track of an applicants answers. I want to check if the following is possible : Have the bot respond to messages from the generic channel as usual. If it receives an application prompt, start a new subprocess (or bot?) that handles the DMs with the applicant separately.
Is the above possible? if not, is there an alternative to handling this in a better way ?
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.channel.type==discord.ChannelType.private:
await dm_channel.send("Whats your age?") ## Question 2
elif message.channel.type == discord.ChannelType.text:
if message.content.startswith('$h'):
member = message.author
if "apply" in message.content:
await startApply(member)
else:
await message.channel.send('Hello!')
# await message.reply('Hello!', mention_author=True)
async def startApply(member):
dm_channel = await member.create_dm()
await dm_channel.send("Whats your name?") ## Question 1
I have the above code as of now. I want the startApply function to trigger a new bot/subprocess to handle the DMs with an applicant.
Option 1
Comparatively speaking, a single if check like that is not too much overhead, but there are a few different solutions. First, you could try your hand at slash commands. This is library built as an extension for the discord.py library for slash commands. You could make one that only works in DM's, and then have it run from there with continuous slash commands.
Option 2
Use a webhook to start up a new bot. This is most likely more complicated, as youll have to get a domain or find some sort of free service to catch webhooks. You could use a webhook like this though to 'wake up' a bot and have it chat with the user in dm's.
Option 3 (Recommended)
Create functions that handle the text depending on the channel, and keep that if - elif in there. As i said, one if isn't that bad. If you had functions that are called in your code that handled everything, it actually should be fairly easy to deal with:
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.channel.type==discord.ChannelType.private:
respondToPrivate(message)
elif message.channel.type == discord.ChannelType.text:
repondToText(message)
In terms of keeping track of the data, if this is a smaller personal project, MySQL is great and easy to learn. You can have each function store whatever data needed to the database so that you can have it stored to be looked at / safe in case of bot crash & then it will also be out of memory.

context.job_queue.run_once not working in Python Telegram BOT API

I'm trying to setup a bot which:
Receives the keywords in /search_msgs userkey command from a TG group
Search in DB for that userkey and send back appropriate text back
I'm getting two errors
None type object has no attribute args, in callback_search_msgs(context), see code snippet
AttributeError: 'int' object has no attribute 'job_queue', in search_msgs(update, context), see code snippet.
Telegram's official documents is way too difficult for me to read and understand. Couldn't find even one place where Updater, update, Commandhandler, context are all explained together with examples.
How to fix this code?
import telegram
from telegram.ext import Updater,CommandHandler, JobQueue
token = "Token"
bot = telegram.Bot(token=token)
# Search specific msgs on user request
def search_msgs(update, context):
context.job_queue.run_once(callback_search_msgs, context=update.message.chat_id)
def callback_search_msgs(context):
print('In TG, args', context.args)
chat_id = context.job.context
search_msgs(context, chat_id)
def main():
updater = Updater(token, use_context=True)
dp = updater.dispatcher
dp.add_handler(CommandHandler("search_msgs",search_msgs, pass_job_queue=True,
pass_user_data=True))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
Let me first try & clear something up:
Telegram's official documents is way too difficult for me to read and understand. Couldn't find even one place where Updater, update, Commandhandler, context are all explained together with examples.
I'm guessing that by "Telegram's official documents" you mean the docs at https://core.telegram.org/bots/api. However, Updater, CommandHandler and context are concepts of python-telegram-bot, which is one (of many) python libraries that provides a wrapper for the bot api. python-telegram-bot provides a tutorial, examples, a wiki where a lots of the features are explained and documentation.
Now to your code:
In context.job_queue.run_once(callback_search_msgs, context=update.message.chat_id) you're not telling job_queue when to run the the job. You must pass an integer or a datetime.(date)time object as second argument.
in
def callback_search_msgs(context):
print('In TG, args', context.args)
chat_id = context.job.context
search_msgs(context, chat_id)
you are passing context and chat_id to search_msgs. However, that function treats context as an instance of telegram.ext.CallbackContext, while you pass an integer instead. Also, even if that worked, this would just schedule another job in an infinite loop.
Finally, I don't understand what scheduling jobs has to do with looking up a key in a database. All you have to do for that is something like
def search_msgs(update, context):
userkey = context.args[0]
result = look_up_key_in_db(userkey)
# this assumes that result is a string:
update.effective_message.reply_text(result)
To understand context.args better, have a look at this wiki page.
Disclaimer: I'm currently the maintainer of python-telegram-bot.

How do you Identify the request that a QNetworkReply finished signal is emitted in response to when you are doing multiple requests In QtNetwork?

I have a project will load a HTTP page, parse it, and then open other pages based on the data it received from the first page.
Since Qt's QNetworkAccessManager works asyncronusly, it seems I should be able to load more than one page at a time by continuing to make HTTP requests, and then taking care of the response would happen in the order the replies come back and would be handled by the even loop.
I'm a having a few problems figuring out how to do this though:
First, I read somewhere on stackoverflow that you should use only one QNetworkAccess manager. I do not know if that is true.
The problem is that I'm connecting to the finished slot on the single QNetworkAccess manager. If I do more than one request at a time, I don't know what request the finished signal is in response to. I don't know if there is a way to inspect the QNetworkReply object that is passed from the signal to know what reply it is in response to? Or should I actually be using a different QNetworkAccessManager for each request?
Here is an example of how I'm chaining stuff together right now. But I know this won't work when I'm doing more than one request at at time:
from PyQt4 import QtCore,QtGui,QtNetwork
class Example(QtCore.QObject):
def __init__(self):
super().__init__()
self.QNetworkAccessManager_1 = QtNetwork.QNetworkAccessManager()
self.QNetworkCookieJar_1 = QtNetwork.QNetworkCookieJar()
self.QNetworkAccessManager_1.setCookieJar(self.QNetworkCookieJar_1)
self.app = QtGui.QApplication([])
def start_request(self):
QUrl_1 = QtCore.QUrl('https://erikbandersen.com/')
QNetworkRequest_1 = QtNetwork.QNetworkRequest(QUrl_1)
#
self.QNetworkAccessManager_1.finished.connect(self.someurl_finshed)
self.QNetworkAccessManager_1.get(QNetworkRequest_1)
def someurl_finshed(self, NetworkReply):
# I do this so that this function won't get called for a diffent request
# But it will only work if I'm doing one request at a time
self.QNetworkAccessManager_1.finished.disconnect(self.someurl_finshed)
page = bytes(NetworkReply.readAll())
# Do something with it
print(page)
QUrl_1 = QtCore.QUrl('https://erikbandersen.com/ipv6/')
QNetworkRequest_1 = QtNetwork.QNetworkRequest(QUrl_1)
#
self.QNetworkAccessManager_1.finished.connect(self.someurl2_finshed)
self.QNetworkAccessManager_1.get(QNetworkRequest_1)
def someurl2_finshed(self, NetworkReply):
page = bytes(NetworkReply.readAll())
# Do something with it
print(page)
kls = Example()
kls.start_request()
I am not familiar to PyQt but from general Qt programming point of view
Using only one QNetworkAccessManager is right design choice
finished signal provides QNetworkReply*, with that we can identify corresponding request using request().
I hope this will solve your problem with one manager and multiple requests.
This is a C++ example doing the same.

Resources