Python Telegram Bot get the bot to respond to message - python

I am currently using python-telegram-bot library to make a telegram bot. My problem is I am trying to have my bot respond back when using the inline command. So when a user sends the bot #botname 'text', I want the to store the 'text' as a string and then have my bot send something back with that variable.
For some reason I can not get this to work. I tried the code below, but it doesn't work...I also posted the example from the github that works but not in the way i want.
My code
def inlinequery(update, context):
"""Handle the inline query."""
query = update.inline_query.query
text = query.message_text
print(text)
update.message.reply_text(text)
Example Code
#Sends message when #botname is used
def inlinequery(update, context):
"""Handle the inline query."""
query = update.inline_query.query
results = [
InlineQueryResultArticle(
id=uuid4(),
title="Caps",
input_message_content=InputTextMessageContent(
query.upper())),
InlineQueryResultArticle(
id=uuid4(),
title="Bold",
input_message_content=InputTextMessageContent(
"*{}*".format(escape_markdown(query)),
parse_mode=ParseMode.MARKDOWN)),
InlineQueryResultArticle(
id=uuid4(),
title="Italic",
input_message_content=InputTextMessageContent(
"_{}_".format(escape_markdown(query)),
parse_mode=ParseMode.MARKDOWN))]
update.inline_query.answer(results)
def main():
# Get the dispatcher to register handlers
dp = updater.dispatcher
dp.add_handler(InlineQueryHandler(inlinequery))
# Start the Bot
updater.start_polling()
if __name__ == '__main__':
main()

You can use the User object of the inline query to send them a message. Keep in mind that the user has to have started a private chat with the bot before the bot can send them messages.
I modified your attempt. It should work, but i have not tested it:
def inlinequery(update, context):
"""Handle the inline query."""
query = update.inline_query
text = query.query
print(text)
query.from_user.send_message(text)
Related docs:
InlineQuery.user
User.send_message

Related

How to send a random string via Inline Telegram commands?

I have created a bot (using python-telegram-bot) that upon choosing a type of query, the bot should randomly choose one of the available strings as the reply.
My function to create replies is as follows:
def generate_reply():
replies = """
Hello
Goodbye
Thanks!
Your welcome!
See you around!""".splitlines()
r = random.choice(replies).strip()
return r
And the functions to reply to the users are as follows:
#Inline Reply
def inlinequery(update, context):
query = update.inline_query.query
results = [InlineQueryResultArticle(id=uuid4(), title="Interact",
input_message_content=InputTextMessageContent(
generate_reply()))]
update.inline_query.answer(results)
#Normal reply
def reply(update, context):
update.message.reply_text(generate_reply())
And after creating the bot I add it to the bot using:
dp.add_handler(CommandHandler("reply", reply))
dp.add_handler(InlineQueryHandler(inlinequery))
when I use /reply in chat it works as intended, but wherever I use an inline command in a chat with another user or a group, the random choice apparently stops working.How can I get around this problem?
I found out the answer to my question. Apparently Telegram caches the answers to similar inline queries for some time. For this to work correctly you should set cache_time to something you'd like, in my case 0.
#Inline Reply
def inlinequery(update, context):
query = update.inline_query.query
results = [InlineQueryResultArticle(id=uuid4(), title="Interact",
input_message_content=InputTextMessageContent(
generate_reply()))]
update.inline_query.answer(results, cache_time=0)

Best practice in sending message in python telegram bot from a 3d party listener

I have a custom code that does its routine and I want to send a message to myself in Telegram if something goes wrong. In my case I use python-telegram-bot library along with apscheduler and its listeners, where certain events could be catched.
I came up with such working code, but my question is: is it possible to make it better, namely without using global variable? This was done to overcome the problem that listeners do not accept arguments needed for bot to send a message.
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from telegram.ext import Updater, CommandHandler
import copy
import my_custom_library
saved_update = None
def my_listener(event): # not related with bot
if event.exception:
if saved_update is not None:
alert(saved_update, 'Scheduler threw event.exception.') # should have bot related args
else:
record = event.retval # get returned value from my_custom_library.repetitive_function
try:
processed_record = my_custom_library.my_unsafe_business_logic(record) # something might go wrong here
my_custom_library.add_to_db(processed_record) # and here
except Exception as e:
if saved_update is not None:
alert(saved_update, e) # should have bot related args
def start(update, context):
global saved_update
saved_update = copy.deepcopy(update) # this is what I don't like
update.message.reply_text('You have subscribed for notifications.')
def alert(update, reason):
update.message.reply_text('Something went wrong: {}'.format(reason))
def main():
scheduler = BackgroundScheduler()
scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
scheduler.add_job(my_custom_library.repetitive_function, args=(my_args,), trigger='interval', minutes=1)
scheduler.start()
# bot
updater = Updater(TOKEN, use_context=True)
dp = updater.dispatcher
dp.add_handler(CommandHandler("start", callback=start))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
The Telegram Bot API is fairly simple, you just ned to send an HTTP GET Request to this URL:
https://api.telegram.org/bot_token_/sendMessage?chat_id=123&text=Hello%20World!
Just create a bot with Botfather and send the Bot a message.
With the specified Token from Botfather and this URL:
https://api.telegram.org/bot_token_/getUpdates
You can get the messages which were sent to the Bot and the chat_id.
The simplest way would be to use the requests module and send the output of the updater to the first URL as the text parameter.
In my scripts I often use callbacks. It's a clean solution that provides separation between the two scripts. The send_message function can accept kwargs, so you can essentially create a dictionary, and update it when sending the message. The first part (channel ID) is something you know on the bot-side, and the second part (the text itself) is something you know one the third-party side.
In your third-party, provide a set_cb function that accepts a callback and a dictionary. Like so:
def set_cb(self, callback, params):
self.callback = callback
self.callback_params = params
In your bot script, set the callback before updater.idle()
# Start the Bot
updater = Updater("TOKEN")
updater.start_polling()
# Set the callback
r.set_cb(updater.bot.send_message, {"chat_id": 123456})
Then, in your third-party, once you want to send a message, simply add the message text call the following:
self.callback_params.update({"text": text})
self.callback(**self.callback_params) # converts the dict to kwargs

Django channels: pass form data to consumer

I'm learning Django and I'm working in a webpage where I need to offer the user the possibility to log in to an external service. I can't simply use the traditional Django views system because otherwise, I would lose the connection with a simple refresh. For that reason, I thought about using Django Channels.
My problem now is how do I send the data to the consumer class? Using the consumers.py given in the tutorial, I would like to send data from a form submission to the connect function and then make the connection if the login to the external service is ok. Then, in that scenario, I could use the clientinstance and the methods from these external services.
So, in short, is it possible to send form data the consumer? And would this be ok, with respect to security in case of sensitive data?
from channels.generic.websocket import AsyncWebsocketConsumer
import json
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
######
## login to external service
######
#get login data from form submited when the websockted is initiated
username = ...
pass = ...
self.client = Client(username, password)
if client:
await self.accept()
# Receive message from room group
async def chat_message(self, event):
message = event['message']
self.client.send(event['message'])
Update:
To clear the explanation: I can't save the user username and pass of the external service, and that I want to offer the user the possibility to use this [sms service](https://clxcommunications.github.io/sdk-xms-python/tutorial.html) with a text field and phone number.
So the problem is that even if I create a form and the username and password to log in (in the view) with
client = clx.xms.Client('myserviceplan', 'mytoken')
then in next request, I would lose the client instance. That's why I thought about Django Channels. But I'm not really sure if it is the best solution...
This will help you.
consumers.py
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
import json
class EventConsumer(WebsocketConsumer):
def connect(self):
# self.room_name = self.scope['url_route']['kwargs']['room_name']
# self.room_group_name = 'chat_%s' % self.room_name
self.room_name = 'event'
self.room_group_name = self.room_name+"_sharif"
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
print(self.room_group_name)
self.accept()
print("#######CONNECTED############")
def disconnect(self, code):
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
print("DISCONNECED CODE: ",code)
def receive(self, text_data=None, bytes_data=None):
print(" MESSAGE RECEIVED")
data = json.loads(text_data)
message = data['message']
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,{
"type": 'send_message_to_frontend',
"message": message
}
)
def send_message_to_frontend(self,event):
print("EVENT TRIGERED")
# Receive message from room group
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message
}))
then call the function outside/anywhere from your app like
def event_triger():
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
'event_sharif',
{
'type': 'send_message_to_frontend',
'message': "event_trigered_from_views"
}
)
# here 'event_sharif' is your room_group_name as i defined before in consumer
# 'type' is like a command, for which method you wants to trigger in your consumer
for more you may take a look Send message using Django Channels from outside Consumer class
Generally, you can call a method in the consumer from an external code in the following way:
from channels.layers import get_channel_layer
channel_layer = get_channel_layer()
await self.channel_layer.send(
'<channel_name>',
{
'type': '<method_name>',
}
)
But as you can see, this requires that you specify the channel name which you can only get after the client has connected. In other words, you should not try to call connect but some other method in the consumer. Also, your client should first connect to the websocket before you eventually access it. I don't understand your use case completey but I hope this gives you an idea

How to work with slackbot in python?

I am trying to build a slackbot for my group , I tried sample codes and some other things but its not sending message to the group.
first i tried via terminal
export SLACK_API_TOKEN="my_token_id"
Then
from slackclient import SlackClient
import os
slack_token = os.environ["SLACK_API_TOKEN"]
sc = SlackClient(slack_token)
sc.api_call(
"chat.postMessage",
channel="#random",
text="Hello from Python! :tada:",
thread_ts="283.5127(dummy_id)",
reply_broadcast=False
)
print(sc)
#<slackclient.client.SlackClient object at 0x109b77ba8>
But there is no message in slack group.
I tried with this code:
from slackclient import SlackClient
import os
slack_token = os.environ['SLACK_API_TOKEN']
sc = SlackClient(slack_token)
print(sc.api_call("channels.list"))
its retuning :
{'error': 'invalid_auth', 'ok': False}
I am not getting what i am doing wrong , Access token is correct , i want to post some messages via a bot , so how i can create a bot on slack and using that bot i can send messages via python ?
I had similar issues when I implemented a slack bot with php & symfony.
It's not that simple to create and configure the slack app, bot and OAuth permissions properly.
I explained all these configurations in this blog post if you need it: https://blog.eleven-labs.com/en/en/replace-erp-by-slack-bot-with-dialogflow-and-symfony/
Also my code in PHP is very similar to what you need to parse Slack requests and post to its API.
Summary, TL;DR:
Go to https://api.slack.com/apps and click on 'Create New App'.
In this app configuration, go to the left menu 'Bot Users' or from 'Basic Information' > 'Add features and functionality' > 'Bots'.
Still in this app config, go to the menu 'OAuth & Permissions' and allow the scope 'chat:write:bot' and copy the value of 'OAuth Access Token'
From your code, call 'chat.postMessage' API method with an 'Authorization' header using previous token value.
built this from some examples found on the web: liza daly - brobot : github.com
and
How to Build Your First Slack Bot with Python : fullstackpython.com
certainly not the best implementation but it functions as an appropriate answer to (i think)
import random
import time
import re
from slackclient import SlackClient
bot_id = None
slack_token = 'xoxb-no.more.mister.nice.gui'
sc = SlackClient(slack_token)
# constants
RTM_READ_DELAY = 1 # 1 second delay between reading from RTM
DEFAULT_RESPONSE = "greetings: 'hello', 'hi', 'greetings', 'sup', 'what's up' / commands: 'do'"
DEFAULT_COMMAND = "do"
MENTION_REGEX = "^<#(|[WU].+?)>(.*)"
def parse_bot_commands(slack_events):
"""
parses a list of events coming from the slack rtm api to find bot commands
:param slack_events:
:return:
"""
for event in slack_events:
if event["type"] == "message" and not "subtype" in event:
user_id, message = parse_direct_mention(event["text"])
if user_id == bot_id:
return message, event["channel"]
return None, None
def parse_direct_mention(message_text):
"""
finds direct message and returns user id
:param message_text:
:return:
"""
matches = re.search(MENTION_REGEX, message_text)
# the first group contains the user name, the second group contains
# the remaining message
return (matches.group(1), matches.group(2).strip()) if matches else (None, None)
def handle_command(command, channel):
"""
executes bot command if the command is known
:param command:
:param channel:
:return:
"""
GREETING_KEYWORDS = ("hello", "hi", "greetings", "sup", "what's up",)
GREETING_RESPONSES = ["'sup brah", "hey", "*headnod*", "didjageddathingahsencha?"]
# default response is help text for the user
default_response = "Not sure what you mean. Try *{}*.".format(DEFAULT_RESPONSE)
# finds and executes the given command, filling the response
response = None
#implement more commands below this line
if command in GREETING_KEYWORDS:
response = random.choice(GREETING_RESPONSES)
else:
if command.startswith(DEFAULT_COMMAND):
response = "Sure...write some more code and I'll do that"
# Sends the response back to the channel
sc.api_call(
"chat.postMessage",
channel="#the_danger_room",
as_user="true:",
text=response or default_response)
if __name__ == "__main__":
if sc.rtm_connect(with_team_state=False):
print("Connected and running!")
#call web api method auth.test to get bot usre id
bot_id = sc.api_call("auth.test")["user_id"]
while True:
command, channel = parse_bot_commands(sc.rtm_read())
if command:
handle_command(command, channel)
time.sleep(RTM_READ_DELAY)
else:
print("Connection failed. Exception traceback printed above.")

How to receive file_id through python-telegram-bot?

I'm making a Telegram bot using python-telegram-bot, and I need some way to receive voice messages. For that, I need to download them, and to do that, I have to get their file_ids. However, the MessageHandler handles... well, messages, and Handler gives me a NotImplementedError. Is there a way to get the file_id?
I know this question is old but I was facing a problem with this in the latest version (12+)
So it appears that the bot- pass_user_data in the callback function is deprecated and from now on you should use context based callbacks.
CallbackContext is an object that contains all the extra context
information regarding an Update, error or Job.
to the new style using CallbackContext:
def voice_handler(update: Update, context: CallbackContext):
file = context.bot.getFile(update.message.audio.file_id)
file.download('./voice.ogg')
You can read more in the Transition-guide-to-Version-12.0
The easiest way to download voice messages is to register a MessageHandler with a voice filter. The Docs provide more information on Filters and the voice module.
import telegram
from telegram.ext import Updater
def voice_handler(bot, update):
file = bot.getFile(update.message.voice.file_id)
print ("file_id: " + str(update.message.voice.file_id))
file.download('voice.ogg')
updater = Updater(token='TOKEN')
dispatcher = updater.dispatcher
dispatcher.add_handler(MessageHandler(Filters.voice, voice_handler))
In version 13+, you need to use update.message.voice.file_id instead of update.message.audio.file_id. So the code will be:
def voice_handler(update: Update, context: CallbackContext):
file = context.bot.getFile(update.message.voice.file_id)
file.download('./voice.ogg')
I'll show you an example with a photo file, but it works for any file (you'll just need to change the parameters)
from telegram.ext import Updater, CommandHandler
from telegram.ext.callbackcontext import CallbackContext
from telegram.update import Update
def start (update: Update, context: CallbackContext):
# getting chat_id:
chatID = update.effective_chat.id
# sending the photo to discover its file_id:
photo1 = context.bot.send_photo(chat_id=chatID, photo=open('photo1.jpg','rb'))
photo1_fileID = photo1.photo[-1].file_id
context.bot.send_message(chat_id=update.effective_chat.id, text=('file_id photo1.jpg = ' + photo1_fileID))
def main():
updater = Updater(token='TOKEN', use_context=True)
dispatcher = updater.dispatcher
dispatcher.add_handler(CommandHandler('start', start))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()

Categories