Python flask application webhook for Telegram bot not working - python

This is my code for a flask application. I am running it on pythonanywhere free tier.
from telegram import Update
from telegram.ext import (ApplicationBuilder, ContextTypes, CommandHandler,MessageHandler, filters)
from flask import Flask, request, Response
import os
import telegram
API_KEY = 'telegram token'
app = Flask(__name__)
SECRET = "flask secret key"
CERT = 'path to cert.pem'
CERT_KEY = 'path to private.key'
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_message(chat_id=update.effective_chat.id, text="No one allowed to use persian characters while I'm running.")
application = ApplicationBuilder().token(API_KEY).build()
start_handler = CommandHandler('start', start)
application.add_handler(start_handler)
#app.route('/', methods=['GET'])
def default():
return Response('Get ok', status=200)
#app.route('/{}'.format(SECRET), methods=["POST"])
def telegram_webhook():
application.run_webhook(
listen='0.0.0.0',
port=8443,
secret_token= SECRET,
key = CERT_KEY,
cert = CERT,
ip_address='35.173.69.207',
webhook_url='https://pouya74.pythonanywhere.com:8443/{}'.format(SECRET),
)
print(request.get_json())
if request.method == 'POST':
update = request.get_json()
if "message" in update:
chat_id = update["message"]["chat"]["id"]
if "text" in update["message"]:
return Response(update, status=200)
return Response(f'Message was not in update object.\n{update}', status=200)
When I try to request from '/' it gives me response for this url.
But for the next route I try to run a webhook, but in error logs of the server it gives me runtime error 'Event loop is closed.'
Also I tried to place this code block
application.run_webhook(
listen='0.0.0.0',
port=8443,
secret_token= SECRET,
key = CERT_KEY,
cert = CERT,
ip_address='35.173.69.207',
webhook_url='https://pouya74.pythonanywhere.com:8443/{}'.format(SECRET),
)
out of the route function 'telegram_webhook' but it gives me a sock.bind() error which says 'address already in use'.
I am wondering what is the proper way to run webhook for telegram bot.

Application.run_webhook is mostly meant as convenience function for use cases where you only run a bot in your script. For running multiple asyncio-frameworks in the same script, it's better to call the methods directly that run_webhook calls. We also have a dedicited wiki section on that.
Disclaimer: I'm currently the maintainer of python-telegram-bot.

Related

Spotify API invalid redirect URI

I am running python and flask, when it starts to run I encounter this error message image of error message I have looked at other forums of people encoutering the same error to no avail
#here is the code
'''
from flask import Flask, request, url_for, session, redirect
import spotipy
from spotipy.oauth2 import SpotifyOAuth
app = Flask(__name__)
app.secret_key = ""
app.config['SESSION_COOKIE_NAME'] = 'Spotify cookie'
#app.route('/')
def login():
sp_oauth = create_spotify_oauth()
auth_url = sp_oauth.get_authorize_url()
return redirect(auth_url)
#app.route('/redirect')
def redirectPage():
return 'redirect'
#app.route('/getTracks')
def getTracks():
return 'PAIN'
def create_spotify_oauth():
return SpotifyOAuth(
client_id="",
client_secret="",
redirect_uri=url_for('redirectPage', _external=True),
scope="user-library-read")
i have added the redirect URIs to the settings in spotify for developers
image of redirect URIs added, I have pressed save
i am expecting this to pop up image of spotify Oauth pop up
Looks like you have a typo in the Redirect URI, in the one being sent to Spotify it is
http://127.0.0.1:5000/redirect
Note the spelling of redirect, from the Screenshot from the Spotify Dashboard this is "redierect" and also the lack of a forward slash on the one being passed to Spotify as this has to match exactly also so you just need to add that URI to the Spotify Dashboard as is as the URL needs to be exactly correct for it not to show that error for the Invalid Redirect URI so add this one to see if it works
http://127.0.0.1:5000/redirect

Setting a parameter in Post body to header when accessing Get endpoint in Flask?

I am working on a simple service with my show_greeting endpoint handling Get request while set_greeting is my Post.
The purpose of this app is that when "header_message: {header parameter}" is sent to set_greeting, {header parameter} will be returned in the header for responses to show_greeting and to reset {header parameter}, "clear" would reset header_message and header.
I have tried using global variables but encountered an error with shadowing from outside the scope and am not sure which approach to take for this. For now, I would like to learn how to return {header parameter} from my /show_greeting endpoint.
Edit: The /show_greeting endpoint returns holiday_message from the request. The header that I would like to send in addition to holiday_message is "header_message".
My code is as follows:
from flask import Flask, request, make_response, Response
app = Flask(__name__)
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
(I do not know how to set header here from header_message in set_greeting)
return received['holiday_message']
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.args
if 'header_message' in posted:
(I attempted save_message = posted['header_message'] here but this approach failed)
return "Header Message Set"
else:
return "Please Send A Header Message"
if __name__ == '__main__':
app.run()
My recommendation is to use the session object. It stores the data in a cookie, which is sent with every request.
If a cookie is not desired, there are other options for saving sessions. For this, however, an extension will be necessary.
Saving with global variables should also work, but is not recommended.
A file or a database can also be used if the data is to be saved across multiple requests from many users.
The data of the post body can be accessed via request.form, while the url parameters of a get request can be queried via request.args.
from flask import Flask
from flask import request, session
app = Flask(__name__)
app.secret_key = b'your secret here'
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
# get the saved message or an empty string if no message is saved
header_message = session.get('header_message', '')
return f"{received['holiday_message']} - {header_message}"
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.form
if 'header_message' in posted:
# store the message
session['header_message'] = posted['header_message']
return "Header Message Set"
else:
# clear the message
session.pop('header_message', None)
return "Please Send A Header Message"
Much success in your further steps.
If I understood your problem, you can work with "g" the flask global object.
Check this code, I expect it will fix your issue.
from flask import g # Added
from flask import Flask, request, make_response, Response
app = Flask(__name__)
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
return g.saved_message # Modified
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.args
if 'message' in posted:
g.saved_message = posted['request'] # Added
return "Message Set"
else:
return "Please Send A Greeting Message"
if __name__ == '__main__':
app.run()

Python and AzureAD with Flask - ms-identity-python-webapp example only logging in intermittently

I'm attempting to set up the Azure AD authentication example from here:
https://github.com/Azure-Samples/ms-identity-python-webapp
I have all configurations set, and permissions on AzureAD, but I'm getting intermittent problems with being able to log in.
I have chased the problem to the session keys either not writing or reading correctly within session.get('user'):
def index():
if not session.get("user"):
return redirect(url_for("login"))
return render_template('index.html', user=session["user"], version=msal.__version__)
additionally,
if request.args.get('state') != session.get("state"):
return redirect(url_for("index")) # No-OP. Goes back to Index page
never returns true, but if I comment it out then sometimes all will work, it will log in, and allow access to the graph app.
I've set the flask_session directory locally, and it is creating caches (around 8kb) for each login.
What seems to help is clearing out the flask_session folder and restarting the application, but not reliably.
Any help would be most gratefully recieved!
full example copied here for convenience:
import uuid
import requests
from flask import Flask, render_template, session, request, redirect, url_for
from flask_session import Session # https://pythonhosted.org/Flask-Session
import msal
import app_config
app = Flask(__name__)
app.config.from_object(app_config)
Session(app)
# This section is needed for url_for("foo", _external=True) to automatically
# generate http scheme when this sample is running on localhost,
# and to generate https scheme when it is deployed behind reversed proxy.
# See also https://flask.palletsprojects.com/en/1.0.x/deploying/wsgi-standalone/#proxy-setups
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
#app.route("/")
def index():
if not session.get("user"):
return redirect(url_for("login"))
return render_template('index.html', user=session["user"], version=msal.__version__)
#app.route("/login")
def login():
session["state"] = str(uuid.uuid4())
# Technically we could use empty list [] as scopes to do just sign in,
# here we choose to also collect end user consent upfront
auth_url = _build_auth_url(scopes=app_config.SCOPE, state=session["state"])
return render_template("login.html", auth_url=auth_url, version=msal.__version__)
#app.route(app_config.REDIRECT_PATH) # Its absolute URL must match your app's redirect_uri set in AAD
def authorized():
if request.args.get('state') != session.get("state"):
return redirect(url_for("index")) # No-OP. Goes back to Index page
if "error" in request.args: # Authentication/Authorization failure
return render_template("auth_error.html", result=request.args)
if request.args.get('code'):
cache = _load_cache()
result = _build_msal_app(cache=cache).acquire_token_by_authorization_code(
request.args['code'],
scopes=app_config.SCOPE, # Misspelled scope would cause an HTTP 400 error here
redirect_uri=url_for("authorized", _external=True))
if "error" in result:
return render_template("auth_error.html", result=result)
session["user"] = result.get("id_token_claims")
_save_cache(cache)
return redirect(url_for("index"))
#app.route("/logout")
def logout():
session.clear() # Wipe out user and its token cache from session
return redirect( # Also logout from your tenant's web session
app_config.AUTHORITY + "/oauth2/v2.0/logout" +
"?post_logout_redirect_uri=" + url_for("index", _external=True))
#app.route("/graphcall")
def graphcall():
token = _get_token_from_cache(app_config.SCOPE)
if not token:
return redirect(url_for("login"))
graph_data = requests.get( # Use token to call downstream service
app_config.ENDPOINT,
headers={'Authorization': 'Bearer ' + token['access_token']},
).json()
return render_template('display.html', result=graph_data)
def _load_cache():
cache = msal.SerializableTokenCache()
if session.get("token_cache"):
cache.deserialize(session["token_cache"])
return cache
def _save_cache(cache):
if cache.has_state_changed:
session["token_cache"] = cache.serialize()
def _build_msal_app(cache=None, authority=None):
return msal.ConfidentialClientApplication(
app_config.CLIENT_ID, authority=authority or app_config.AUTHORITY,
client_credential=app_config.CLIENT_SECRET, token_cache=cache)
def _build_auth_url(authority=None, scopes=None, state=None):
return _build_msal_app(authority=authority).get_authorization_request_url(
scopes or [],
state=state or str(uuid.uuid4()),
redirect_uri=url_for("authorized", _external=True))
def _get_token_from_cache(scope=None):
cache = _load_cache() # This web app maintains one cache per session
cca = _build_msal_app(cache=cache)
accounts = cca.get_accounts()
if accounts: # So all account(s) belong to the current signed-in user
result = cca.acquire_token_silent(scope, account=accounts[0])
_save_cache(cache)
return result
app.jinja_env.globals.update(_build_auth_url=_build_auth_url) # Used in template
if __name__ == "__main__":
app.run()
I am one of the maintainer of the Azure AD authentication example from here: https://github.com/Azure-Samples/ms-identity-python-webapp
and I am the author of my workaround repo https://github.com/rayluo/flask-session
As of this writing, an indirect upstream module, cachelib, has released a bugfix for this 2 days ago, at about the same time you encountered this issue. So, to any future readers here: you should not have to explicitly switch to use the aforementioned workaround. That Azure AD web app sample will properly declare the right dependencies it needs.
I found the solution, or more accuratly my failing:
This repo:
https://github.com/rayluo/flask-session
Is not the same as this one:
https://github.com/fengsp/flask-session
The included requirements.txt loads the newer/maintained flask-session from rayluo (thanks!) rather than the one from pip.
So for anyone else finding this, make sure you use the right version!

what does url_for('.outbound', _external=True) means?

I have a python Twilio code like this(Click to Call method in twilio):
from flask import Flask
from flask import jsonify
#from flask import render_template
#from flask import request
from flask import url_for
from twilio.twiml.voice_response import VoiceResponse
from twilio.rest import Client
app = Flask(__name__)
# Voice Request URL
#app.route('/call')
def call():
# Get phone number we need to call
phone_number = request.form.get('phoneNumber', None)
try:
twilio_client = Client(app.config['TWILIO_ACCOUNT_SID'],
app.config['TWILIO_AUTH_TOKEN'])
except Exception as e:
msg = 'Missing configuration variable: {0}'.format(e)
return jsonify({'error': msg})
try:
twilio_client.calls.create(from_=app.config['TWILIO_CALLER_ID'],
to=phone_number,
url=url_for('.outbound', _external=True))
except Exception as e:
app.logger.error(e)
return jsonify({'error': str(e)})
return jsonify({'message': 'Call incoming!'})
#app.route('/outbound', methods=['POST'])
def outbound():
response = VoiceResponse()
response.say("Thank you for contacting our sales department. If this "
"click to call application was in production, we would "
"dial out to your sales team with the Dial verb.",
voice='alice')
response.number("+16518675309")
return str(response)
if __name__ == '__main__':
app.run()
When i try run run this from browser by calling : http://localhost:5000/call
i am getting ERROR: Unable to create record: Url is not a valid url:
How to call the Outbound function in the url and start the conversation between two people.
Instead of url_for('.outbound', _external=True) you should use url_for('outbound'). The docs linked by stamaimer say:
In case blueprints are active you can shortcut references to the same blueprint by prefixing the local endpoint with a dot (.).
You do not need a dot at the beginning. Check how url building is handled in flask.

Twitter oauth with flask_oauthlib, Failed to generate request token

I tried to use flask_oauthlib to access my twitter api, but all I get is the error : Failed to generate request token. Here is the code.
from flask_oauthlib.client import OAuth
from flask import Flask, url_for, request, jsonify
app = Flask(__name__)
oauth = OAuth()
twitter = oauth.remote_app(
'twitter',
base_url='https://api.twitter.com/1/',
request_token_url='https://api.twitter.com/oauth/request_token',
access_token_url='https://api.twitter.com/oauth/access_token',
authorize_url='https://api.twitter.com/oauth/authorize',
consumer_key='dOJjyxB6gxXWTjdtfPUZcZPjl',
consumer_secret='im not telling you',
)
#app.route('/login')
def login():
return twitter.authorize(callback=url_for('authorized',
next=request.args.get('next') or request.referrer or None))
#app.route('/authorized')
#twitter.authorized_handler
def authorized(resp):
if resp is None:
return 'Access denied: error=%s' % (
request.args['error']
)
if 'oauth_token' in resp:
# session['example_oauth'] = resp
print(resp)
return jsonify(resp)
return str(resp)
if __name__ == '__main__':
app.run(port=8000, debug=True)
This didn't work while using http://term.ie/oauth/example/client.php, I managed to get a request token.
I inspired myself with https://github.com/lepture/example-oauth1-server/blob/master/client.py and http://flask-oauthlib.readthedocs.io/en/latest/client.html
EDIT
Weird fact : I tried the code here : https://github.com/lepture/flask-oauthlib/blob/master/example/twitter.py
I didn't changed the key and secret and it worked.
So I tried to change them for my own credentials, and it stopped working. I really can't understand...
Ok I found the problem. It appears that the callback URL is mandatory when using flask-oauthlib. So I added a fake one since i'm still on localhost, and it solved this problem.
In case anyone found this issue. I'm the author of Flask-OAuthlib. I suggest that you use Authlib instead, browser the source code at https://github.com/lepture/authlib. There are many built-in social connections in https://github.com/authlib/loginpass.

Categories