i've just started working with line-bot and followed the tutorial here: https://developers.line.biz/en/docs/messaging-api/building-bot/
However, I still don't understand how I can connect with my line app account, to send messages, and have these messages appear back in python.
The below is the script I copied from line tutorial.
from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, TextSendMessage
app = Flask(__name__)
line_bot_api = LineBotApi('foo', timeout=20)
handler = WebhookHandler('bar')
user_profile = 'far'
#app.route("/", methods=['GET'])
def home():
profile = line_bot_api.get_profile(user_profile)
print(profile.display_name)
print(profile.user_id)
print(profile.picture_url)
print(profile.status_message)
return '<div><h1>ok</h1></div>'
#app.route("/callback", methods=['POST'])
def callback():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
#handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text='hello world'))
if __name__ == "__main__":
app.run(debug=True)
What am I missing, or how can I connect with the line app to send and receive messages?
I followed that tutorial and was able to successfully create a bot that just echoes messages in uppercase:
Your question is how to "connect" your bot's code with the LINE app. The three most important parts of the tutorial are probably:
Adding the bot as a friend, you do this by scanning its QR code with the LINE app
When you create a channel for your bot, you need to enable "Webhooks" and provide an https endpoint which is where LINE will send the interaction events your bot receives. To keep this simple for the purposes of this answer, I created an AWS Lambda function and exposed it via API Gateway as an endpoint that looked something like:
https://asdfasdf.execute-api.us-east-1.amazonaws.com/default/MyLineBotFunction. This is what I entered as the Webhook URL for my bot.
Once you are successfully receiving message events, responding simply requires posting to the LINE API with the unique replyToken that came with the message.
Here is the Lambda function code for my simple yell-back-in-caps bot:
import json
from botocore.vendored import requests
def lambda_handler(event, context):
if 'body' in event:
message_event = json.loads(event['body'])['events'][0]
reply_token = message_event['replyToken']
message_text = message_event['message']['text']
requests.post('https://api.line.me/v2/bot/message/reply',
data=json.dumps({
'replyToken': reply_token,
'messages': [{'type': 'text', 'text': message_text.upper()}]
}),
headers={
# TODO: Put your channel access token in the Authorization header
'Authorization': 'Bearer YOUR_CHANNEL_ACCESS_TOKEN_HERE',
'Content-Type': 'application/json'
}
)
return {
'statusCode': 200
}
Related
I'm a noobie at Python and was just messing around, but now I'm really curious why it isn't working. I'm currently trying to build a telegram bot that generates an image based on the text given to the bot. I think there might be a problem with my DeepAI api? When I click this link: https://api.deepai.org/api/text2img i always get the error
{"err": "error processing given inputs from request"}
. The bot is linked but gives me the error as in the code below. My code below:
import requests
import json
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
def text_to_image(update, context):
# Get the text from the user
text = update.message.text
# Set up the DeepAI API request
api_key = "{{ api }}"
headers = {
"api-key": api_key
}
data = {
"text": text
}
# Make the request to the DeepAI API
response = requests.post("https://api.deepai.org/api/text2img", headers=headers, json=data)
if response.status_code != 200:
update.message.reply_text("An error occurred while generating the image. Please try again later.")
return
# Get the generated image from the response
response_json = response.json()
image_url = response_json["output_url"]
# Send the generated image to the user
context.bot.send_photo(chat_id=update.effective_chat.id, photo=image_url)
def main():
# Set up the Telegram bot
updater = Updater(token="{{ api }}", use_context=True)
dispatcher = updater.dispatcher
# Add the text_to_image handler
text_to_image_handler = MessageHandler(Filters.text, text_to_image)
dispatcher.add_handler(text_to_image_handler)
# Start the bot
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
I've tried changing code and different things but nothing seems to work, i'm really stuck right now
From the looks of it, it's an error on the API side of things. The status code of the API server depends on traffic and account/key.
Things to do:
Check if your API key is valid and wait when server works on google, then try to run your code
I'm trying to broadcast a message to all the users using flask and angular, I'm trying to achieve this using socketio but since I'm new to socketio, I'm unable to implement it as expected.
Basic idea is to pick a message from db table and broadcast to all users using via API call.
The server should show the new message without the website being refreshed by the user.
Python Code:
import socketio
from aiohttp import web
app = Flask(__name__)
sio = socketio.AsyncServer(async_mode='aiohttp', logger=True, engineio_logger=True)
sio.attach(app)
#sio.on('join_room', namespace='/maintenance')
def handle_message(sid, room):
sio.enter_room(sid, room=room, namespace='/maintenance')
#sio.on('getMessage', namespace='/maintenance')
async def handle_message(sid, maintenance_room):
await sio.emit('getMaintenanceResponse', 'json_object', namespace='/maintenance', room=maintenance_room)
#sio.on('addMessage', namespace='/maintenance')
async def add_message(sid, message_data, maintenance_room):
await sio.emit('getMaintenanceResponse', 'json_object', namespace='/maintenance', room=maintenance_room)
Angular Code:
this.socket.createMaintenanceRoom('maintenance');
this.socket.getMaintenance('maintenance').subscribe(response => {
if (response) {
this.maintenanceNotesData = response;
}
},
err => console.error('Observer got an error: ' + err),
() => console.log('Observer got a complete notification'));
This is driving me absolutely crazy and preventing me from being able to do local dev/test.
I have a flask app that uses authlib (client capabilities only). When a user hits my home page, my flask backend redirects them to /login which in turn redirects to Google Auth. Google Auth then posts them back to my app's /auth endpoint.
For months, I have been experiencing ad-hoc issues with authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF Warning! State not equal in request and response. It feels like a cookie problem and most of the time, I just open a new browser window or incognito or try to clear cache and eventually, it sort of works.
However, I am now running the exact same application inside of a docker container and at one stage this was working. I have no idea what I have changed but whenever I browse to localhost/ or 127.0.0.1/ and go through the auth process (clearing cookies each time to ensure i'm not auto-logged in), I am constantly redirected back to localhost/auth?state=blah blah blah and I experience this issue:
authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF Warning! State not equal in request and response.
I think the relevant part of my code is:
#app.route("/", defaults={"path": ""})
#app.route("/<path:path>")
def catch_all(path: str) -> Union[flask.Response, werkzeug.Response]:
if flask.session.get("user"):
return app.send_static_file("index.html")
return flask.redirect("/login")
#app.route("/auth")
def auth() -> Union[Tuple[str, int], werkzeug.Response]:
token = oauth.google.authorize_access_token()
user = oauth.google.parse_id_token(token)
flask.session["user"] = user
return flask.redirect("/")
#app.route("/login")
def login() -> werkzeug.Response:
return oauth.google.authorize_redirect(flask.url_for("auth", _external=True))
I would hugely appreciate any help.
When I run locally, I start with:
export FLASK_APP=foo && flask run
When I run inside docker container, i start with:
.venv/bin/gunicorn -b :8080 --workers 16 foo
Issue was that SECRET_KEY was being populated using os.random which yielded different values for different workers and thus, couldn't access the session cookie.
#adamcunnington here is how you can debug it:
#app.route("/auth")
def auth() -> Union[Tuple[str, int], werkzeug.Response]:
# Check these two values
print(flask.request.args.get('state'), flask.session.get('_google_authlib_state_'))
token = oauth.google.authorize_access_token()
user = oauth.google.parse_id_token(token)
flask.session["user"] = user
return flask.redirect("/")
Check the values in request.args and session to see what's going on.
Maybe it is because Flask session not persistent across requests in Flask app with Gunicorn on Heroku
How I Fix My Issue
install old version of authlib it work fine with fastapi and flask
Authlib==0.14.3
For Fastapi
uvicorn==0.11.8
starlette==0.13.6
Authlib==0.14.3
fastapi==0.61.1
Imporantt if using local host for Google auth make sure get https certifcate
install chocolatey and setup https check this tutorial
https://dev.to/rajshirolkar/fastapi-over-https-for-development-on-windows-2p7d
ssl_keyfile="./localhost+2-key.pem" ,
ssl_certfile= "./localhost+2.pem"
--- My Code ---
from typing import Optional
from fastapi import FastAPI, Depends, HTTPException
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
from starlette.config import Config
from starlette.requests import Request
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, JSONResponse, RedirectResponse
from authlib.integrations.starlette_client import OAuth
# Initialize FastAPI
app = FastAPI(docs_url=None, redoc_url=None)
app.add_middleware(SessionMiddleware, secret_key='!secret')
#app.get('/')
async def home(request: Request):
# Try to get the user
user = request.session.get('user')
if user is not None:
email = user['email']
html = (
f'<pre>Email: {email}</pre><br>'
'documentation<br>'
'logout'
)
return HTMLResponse(html)
# Show the login link
return HTMLResponse('login')
# --- Google OAuth ---
# Initialize our OAuth instance from the client ID and client secret specified in our .env file
config = Config('.env')
oauth = OAuth(config)
CONF_URL = 'https://accounts.google.com/.well-known/openid-configuration'
oauth.register(
name='google',
server_metadata_url=CONF_URL,
client_kwargs={
'scope': 'openid email profile'
}
)
#app.get('/login', tags=['authentication']) # Tag it as "authentication" for our docs
async def login(request: Request):
# Redirect Google OAuth back to our application
redirect_uri = request.url_for('auth')
print(redirect_uri)
return await oauth.google.authorize_redirect(request, redirect_uri)
#app.route('/auth/google')
async def auth(request: Request):
# Perform Google OAuth
token = await oauth.google.authorize_access_token(request)
user = await oauth.google.parse_id_token(request, token)
# Save the user
request.session['user'] = dict(user)
return RedirectResponse(url='/')
#app.get('/logout', tags=['authentication']) # Tag it as "authentication" for our docs
async def logout(request: Request):
# Remove the user
request.session.pop('user', None)
return RedirectResponse(url='/')
# --- Dependencies ---
# Try to get the logged in user
async def get_user(request: Request) -> Optional[dict]:
user = request.session.get('user')
if user is not None:
return user
else:
raise HTTPException(status_code=403, detail='Could not validate credentials.')
return None
# --- Documentation ---
#app.route('/openapi.json')
async def get_open_api_endpoint(request: Request, user: Optional[dict] = Depends(get_user)): # This dependency protects our endpoint!
response = JSONResponse(get_openapi(title='FastAPI', version=1, routes=app.routes))
return response
#app.get('/docs', tags=['documentation']) # Tag it as "documentation" for our docs
async def get_documentation(request: Request, user: Optional[dict] = Depends(get_user)): # This dependency protects our endpoint!
response = get_swagger_ui_html(openapi_url='/openapi.json', title='Documentation')
return response
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, port=8000,
log_level='debug',
ssl_keyfile="./localhost+2-key.pem" ,
ssl_certfile= "./localhost+2.pem"
)
.env file
GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=""
Google Console Setup
I am writing these two following function in my code to be able to process incoming messages and respond back to the user on Messenger via a bot:
#app.route('/', methods=['post'])
def webhook():
# endpoint for processing incoming messaging events
data = request.get_json()
print(data) # you may not want to log every incoming message in production, but it's good for testing
if data["object"] == "page":
for entry in data["entry"]:
for messaging_event in entry["messaging"]:
if messaging_event.get("message"): # someone sent us a message
sender_id = messaging_event["sender"]["id"] # the Facebook ID of the person sending you the message
recipient_id = messaging_event["recipient"]["id"] # the recipient's ID, which should be your page's facebook ID
message_text = messaging_event["message"]["text"] # the message's text
responseai = response(message_text, sender_id)
send_message(sender_id, responseai)
if messaging_event.get("delivery"): # delivery confirmation
pass
if messaging_event.get("optin"): # optin confirmation
pass
if messaging_event.get("postback"): # user clicked/tapped "postback" button in earlier message
pass
return "Ok", 200
#app.route('/', methods=['GET'])
def verify():
# when the endpoint is registered as a web hook, it must echo back
# the 'hub.challenge' value it receives in the query arguments
if request.args.get("hub.mode") == "subscribe" and request.args.get("hub.challenge"):
if not request.args.get("hub.verify_token") == os.environs["VERIFY_TOKEN"]:
return "Verification token mismatch", 403
return request.args["hub.challenge"], 200
return "Hello World", 200
When I access my localhost:5000 where my Flask is, only Hello World appears on the browser. How would I know that the function web-hook is working? Should it also display the 'Ok'?
Your browser will not send a POST request, not without some additional HTML and Javascript coding. The simplest way to test if your hook works is by using the curl command-line client.
It can send both GET and POST requests. Testing if your GET handler works correctly:
curl -X GET "localhost:5000/?hub.verify_token=<YOUR_VERIFY_TOKEN>&hub.challenge=CHALLENGE_ACCEPTED&hub.mode=subscribe"
should produce CHALLENGE_ACCEPTED as the output. Then test the POST handler with:
curl -H "Content-Type: application/json" -X POST "localhost:5000/" -d '{"sender":{"id":"<PSID>"}, "recipient":{"id":"<PAGE_ID>"}, "timestamp":1458692752478, "message":{"mid":"mid.1457764197618:41d102a3e1ae206a38", "text":"hello, world!", "quick_reply": {"payload": "<DEVELOPER_DEFINED_PAYLOAD>"}}}'
See the Setting Up Your Webhook section of the Messenger Platform Getting Started documentation, and the message received event for details on what to expect when handling a message event.
Another option is to write Python tests to cover the same:
import os
import pytest
import your_flask_module
#pytest.fixture
def client():
your_flask_module.app.config['TESTING'] = True
yield your_flask_module.app.test_client()
def test_register(client):
args = {
'hub.verify_token': os.environ["VERIFY_TOKEN"],
'hub.challenge': 'CHALLENGE_ACCEPTED',
'hub.mode': 'subscribe',
}
rv = client.get('/', params=args)
assert b'CHALLANGE_ACCEPTED' in rv.data
def test_message_event(client):
event = {
"sender": {"id": "<PSID>"},
"recipient": {"id":"<PAGE_ID>"},
"timestamp": 1458692752478,
"message": {
"mid": "mid.1457764197618:41d102a3e1ae206a38",
"text": "hello, world!",
"quick_reply": {
"payload": "<DEVELOPER_DEFINED_PAYLOAD>"
}
}
}
rv = client.post('/', json=event)
assert rv.status_code == 200
I've tried to create my first telegram bot hosting the code as an amazon lambda instance, i suppose i should return something to the webhook 'cause it keep saying "Wrong response from the webhook: 502 Bad Gateway".
Here is part of my code:
def msgSend(text, chat_id):
url = URL + "sendMessage?text={}&chat_id={}".format(text, chat_id)
response = requests.get(url)
content = response.content.decode("utf8")
return content
def handle(msg):
sender = msg['from']['username']
id_gruppo = msg['chat']['id']
if sender == NAME:
testo = msg['text']
usernames = [x.replace('#','') for x in rx.findall(text)]
map(foo, usernames)
msgSend(confirm_mess, id_group)
return
def main(event, context):
response = ast.literal_eval(event['body'])
handle(response['message'])
return {
'something': 'something'
}
Actually the process works fine enough, the messages are received by my lambda and everything works like a charm, except for one thing, the confirmation message is sent over and over endlessly and the webhooks never marks the messages as read.
here is the response of getWebHookInfo:
{"ok":true,"result":{"url":"https://t2rt9guj3h.execute-api.us-west-2.amazonaws.com/prod/instabot","has_custom_certificate":false,"pending_update_count":19,"last_error_date":1489331750,"last_error_message":"Wrong response from the webhook: 502 Bad Gateway","max_connections":40}}
According to the bot helper the wh requires a 2XX code response...
Any idea about that?
According to the bot helper the wh requires a 2XX code response...
This is true. Your last statement should be
return {
statusCode: 200
}
If you don't return a successful response code, Telegram won't know what to do with it, which is why you see the HTTP 502 Bad Gateway. I was hitting this for awhile also :)