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.
Related
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.
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.
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.
I am making a custom discord bot in python. I am trying to add a !report command.
I am very confused and cant find the answer anywhere. Can anyone help me make it?
I want any user to be able to do !report #example reason.
and save it in a database such as excel or sql3 or preferably in a staff channel.
how would I do this?
I have tried to use on_message()
You could use the on_message() command:
#client.event
async def on_message(message):
if message.content.startswith("!report"):
report_channel = client.get_channel(channel id)
member = text.split(" ")[1]
reason = ' '.join(text.split(" ")[1:])
await report_channel.send(f"Member: {member}, Reason: {reason}")
So the first thing is to look to see if the person used the "!report" command with an if statement.
Next, you find the member by taking the second word of the message.
After that, you find the reason by taking the rest of the words in the message.
Then you send it to the pre-defined report channel on discord.
In Python-telegram-bot how to get, if possible, the complete list of all participants of the group at which the bot was added?
You can't with current API but you could the join/exit of user members via it's API.
If you check the Message object you find :
new_chat_participant: A new member was added to the group, information about them (this member may be the bot itself)
left_chat_participant: A member was removed from the group, information about them (this member may be the bot itself)
So with this two information you can track the total number of users in your chat and who they are.
The basic strategy would be to store somewhere (like a database) the occurrences of joining and exiting of users from the group.
When a user join the chat store the object User to the storage.
When a user exit the chat delete the object User from the storage.
Well then do the logic as you need.
Also, latest API update allows you to:
telegram.get_chat_members_count(chat_id): Use this method to get the number of members in a chat.
telegram.get_chat_member(chat_id, user_id): Use this method to get information about a member of a chat.
You can combine with new_chat_participant and left_chat_participant strategy, to build information about a group.
More information here:
https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.get_chat_members_count
https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.get_chat_member
As stated by the others before, it's not possible with the bot API (for now) hence you have to go the telegram API way. Start via https://core.telegram.org/api/obtaining_api_id
I had the same problem and solved it via telethon. A snipped for you to start from:
from telethon import TelegramClient
import asyncio
api_id = 1234 # Your API_ID
api_hash = "1a2b3c456" # Your APP_ID
bot_token = "87319a123b12e321ab1cd" # (via botfather, you can alternatively sign in as a user)
goupid = -120304101020
async def get_users(client, group_id):
async for user in client.iter_participants(group_id):
if not user.deleted:
print("id:", user.id, "username:", user.username)
bot = TelegramClient('bot', api_id, api_hash).start(bot_token=bot_token)
with bot:
asyncio.get_event_loop().run_until_complete(get_users(bot, group_id))
(Example via https://gist.github.com/rokibhasansagar/d727fb30ef5a274cf536bea73260887c)