How to use telegram webhook on Google App Engine? - google-app-engine

My question is very similar to this one:
How to use telegram webhook on google cloud functions?
I have the same identical problem but I want to deploy it on Google App Engine.
I have set up my bot using the python-telegram-bot library (code below) and actually it is hosted on Heroku and works well.
import os
TOKEN = "TOKEN"
PORT = int(os.environ.get('PORT', '8443'))
updater = Updater(TOKEN)
# add example handler
def start(update, context):
context.bot.send_message(chat_id=update.message.chat_id, text="Hello, I am dice bot and I will roll some tasty dice for you.")
start_handler = CommandHandler('start', start)
dispatcher.add_handler(start_handler)
# start webhook polling
updater.start_webhook(listen="0.0.0.0",
port=PORT,
url_path=TOKEN)
updater.bot.set_webhook("https://<appname>.appspot.com/" + TOKEN)
updater.idle()
My Bot is not responding and if I try to browse my app I get 502 ERORR Bad Gateway. Of course I deleted the previous webhook on my Bot.

Related

Authentication using Google Service Account in a flask app and deploying on Google App Engine

Below are my requirements.
Develop a flask app.
Use collections in the firebase in the app.
Deploy this app on Google App Engine using a standard service account
What I have done.
Created a service account
Downloaded the corresponding credentials json; I am calling it as key.json
written a main.py
cred = credentials.Certificate('key.json')
default_app = initialize_app(cred)
db = firestore.client()
user_ref = db.collection_group('Users')
#app.route('/', methods=['GET'])
def home():
return "<h1>Welcome to my first app</h1>"
#app.route('/users', methods=['GET'])
def getUsers():
try:
result = [user.to_dict() for user in user_ref .stream()]
return jsonify(result), 200
except Exception as e:
result = { "message:"failed"}
return jsonify(result), 500
I have tested this locally and also on deployed on Google App Engine.
In both the cases, key.json was in the same directory as the code.
I have verified that if this key.json is modified to store wrong data, then /users endpoint won't work and gives me a 500 error.
So far so good. I want to know if this is even the right approach.
I want the key.json authentication to applied even for the root / endpoint.
i.e., if the user supplies a valid key.json, only then the Welcome to my first app should be displayed.
Else, Unauthorized user message needs to be displayed.
As mentioned by #Gaefan and #DishantMakwana, as well as in this documentation:
An API key only identifies the application and doesn't require user authentication. It is sufficient for accessing public data.
So in order to authenticate/authorize your users you should reconsider your strategy. I would recommend you to follow the instructions in the Authenticating as an end user Documentation.
I have found that we can use Google Cloud Endpoints for API management. Works as a charm.

Google Cloud Pubsub authentication error from App Engine

We're having trouble publishing messages to a Google Cloud PubSub topic on Google AppEngine. Using the Application Default credentials works perfect locally. But once it's deployed on Google AppEngine it gives the following error:
<HttpError 403 when requesting https://pubsub.googleapis.com/v1/projects/our-project-id/topics/our-topic:publish?alt=json returned "The request cannot be identified with a project. Please pass a valid API key with the request.">
I would assume that it's will use the service account of app engine to access the PubSub API. Here is the code we used to create the credentials.
credentials = GoogleCredentials.get_application_default()
if credentials.create_scoped_required():
credentials = credentials.create_scoped(['https://www.googleapis.com/auth/pubsub'])
http = httplib2.Http()
credentials.authorize(http)
pubsub_service = build('pubsub', 'v1', http=http)
The error is thrown when publishing the actual message to PubSub.
pubsub_service.projects().topics().publish(topic="projects/out-project-id/topics/out-topic", body = { 'messages' : [ { 'data': base64.b64encode(request.get_data()) }]}).execute()
Not that the same flow works doing API call's to "BigQuery", so it's not a general Google API problem. It seems to be specific to PubSub...
It's a rare case of the service account without project id embedded in it. We fixed your service account and you should be good to go now. Sorry for the trouble.

Google App Engine connection request timeout error

I am working on a GAE web app which shows movie related data.To get the movie data I am using API from OMDB (http://www.omdbapi.com/) .Below is the code snippet I use to connect to the API.
When i run it locally it works perfectly fine, but doesn't work when deployed on GAE. It throws connection timeout exception, i tried increasing connection timeout period but that didn't work.
String URLstr = "http://www.omdbapi.com/?t="+URLEncoder.encode(Request,"utf-8");
URL url=null;
URLConnection uc = null;
BufferedReader bf = null;
try {
url= new URL(URLstr);
uc = url.openConnection();
uc.setConnectTimeout(15* 1000);
bf = new BufferedReader(new InputStreamReader(uc.getInputStream()));
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
Is my code incorrect? are there some restrictions with GAE that i missed?
Your code looks correct. I am having the exact same issue with OMDB API and Google App Engine as of a few weeks ago. I reached out to Brian who runs OMDB API regarding this and I think it had to do with the App Engine IP range being blocked because of abuse a few weeks ago.
I created the following webapp to figure out what external IP address the url fetch from my app was showing up as to the OMDB servers. I deployed the following to GAE to get the public IP.
import webapp2
import logging
from google.appengine.api import urlfetch
class ifconfig(webapp2.RequestHandler):
def get(self):
url="http://ipecho.net/plain"
urlfetch.set_default_fetch_deadline(60)
result = urlfetch.fetch(url)
logging.debug("I think my external IP is %s " % result.content)
self.response.write(result.content)
app = webapp2.WSGIApplication([
('/ifconfig', ifconfig)
])
In Google App Engine, I went to the instances tab and shutdown the instance, and checked what external IP the new instance had. I did this several times, and in my case it seemed like the external IPs were all coming from 107.178.195.0/24, so I provided this information to OMDB API.
I guess this was in the banned IP block, and Brian was able to unblock that range. This fixed my issue and requests to the API started working again.
This possibly might have resolved the issue for you as well, but if it didn't, you might want to figure out what your public IP is and reach out to Brian to see if it's in an IP range that's being blocked

Sending Mail from Google App Engine

I am trying to send an email from google app engine using the python 2.7 library but I keep getting Unauthorized sender in the logs. I have tried my gmail account I created the application with as the sender, I registered another gmail address as a developer and tried that but still get Unauthorized sender. I am not sure if it matters but I do have a domain name registered to this application.
Here is the code I am trying:
message = mail.EmailMessage()
message.sender = "ron.....#gmail.com"
message.subject = "Inquiry"
message.to = "ron.....#gmail.com"
message.body = "Please work"
message.send()
I have looked at other articles to no avail.
Google Appengine sending emails: [Error] unauthorized sender
InvalidSenderError: Unauthorized sender (Google App Engine)
from google.appengine.api import mail
mail.send_mail(sender="stackoverflow.com Hossam <Hossam#stackoverflow.com>",
to="rsnyder <rsnyder#stackoverflow.com>",
subject="How to send an e-mail using google app engine",
body="""
Dear rsnyder:
This example shows how to send an e-mail using google app engine
Please let me know if this is what you want.
Best regards,
""")
EDIT:
Note that sender must be an administrator of the application, so in case that you are not and administrator, follow these steps from the post google app engine: how to add adminstrator account
I found the issue. It was the wrong version of the code. I switched to a version 2 and didn't realize I had to activate it.

Accessing a Google Drive spreadsheet from Appengine

I have an appengine app that needs to access a single, hard-coded spreadsheet on Google Drive.
Up until now I have been achieving this as follows:
SpreadsheetService service = new SpreadsheetService("myapp");
service.setUserCredentials("myusername#gmail.com", "myhardcodedpassword");
When I tried this today with a new user, I got InvalidCredentialsException even though the username and password were definitely correct. I got an email in my inbox saying suspicions sign-ins had been prevented, and there seems to be no way to enable them again.
I am also aware that hardcoding passwords in source is bad practice.
However, I have read very widely online for how to enable OAuth/OAuth2 for this, and have ended up wasting hours and hours piecing fragments of information from blogs, stackoverflow answers etc, to no avail.
Ideally the solution would involve an initial process to generate a long-lived access token, which could then be hard-coded in to the app.
I want a definitive list of steps for how to achieve this?
EDIT: As Google have redesigned the API Console, the details of the steps below have changed - see comments
OK here goes, step by step
Go to Google Cloud Console and register your project (aka application)
You need to note the Client ID, and Client Secret
Go to the OAuth Playground, click the gear icon and choose and enter your own credentials
You will be reminded that you need to go back to the Cloud COnsole and add the Oauth Playground as a valid callback url. So do that.
Do Step 1, choosing the spreadsheet scope and click authorize
Choose your Google account if prompted and grant auth when prompted
Do Step 2, Click 'Exchange auth code for tokens'
You will see an input box containing a refresh token
The refresh token is the equivalent of your long lived username/password, so this is what you'll hard code (or store someplace secure your app can retrieve it).
When you need to access Google Spreadsheets, you will call
POST https://accounts.google.com/o/oauth2/token
content-type: application/x-www-form-urlencoded
client_secret=************&grant_type=refresh_token&refresh_token=1%2xxxxxxxxxx&client_id=999999999999.apps.googleusercontent.com
which will return you an access token
{
"access_token": "ya29.yyyyyyyyyyyyyyyyyy",
"token_type": "Bearer",
"expires_in": 3600
}
Put the access token into an http header for whenever you access the spreadsheet API
Authorization: Bearer ya29.yyyyyyyyyyyyyyyyy
And you're done
Pinoyyid has indeed provided wonderful help. I wanted to follow up with Python code that will allow access to a Personal (web-accessible) Google Drive.
This runs fine within the Google App Engine (as part of a web app) and also standalone on the desktop (assuming the Google App Engine SDK is installed on your machine [available from: https://developers.google.com/appengine/downloads]).
In my case I added https://www.googleapis.com/auth/drive scope during Pinyyid's process because I wanted access to all my Google Drive files.
After following Pinoyyid's instructions to get your refresh token etc., this little Python script will get a listing of all the files on your Google drive:
import httplib2
import datetime
from oauth2client.client import OAuth2Credentials
from apiclient.discovery import build
API_KEY = 'AIz...' # from "Key for Server Applications" from "Public API Access" section of Google Developers Console
access_token = "ya29..." # from Piinoyyid's instructions
refresh_token = "1/V..." # from Piinoyyid's instructions
client_id = '654....apps.googleusercontent.com' # from "Client ID for web application" from "OAuth" section of Google Developers Console
client_secret = '6Cl...' # from "Client ID for web application" from "OAuth" section of Google Developers Console
token_expiry = datetime.datetime.utcnow() - datetime.timedelta(days=1)
token_uri = 'https://accounts.google.com/o/oauth2/token'
user_agent = 'python urllib (I reckon)'
def main():
service = createDrive()
dirlist = service.files().list(maxResults=30)
print 'dirlist', dirlist
result = dirlist.execute()
print 'result', result
def createDrive():
credentials = OAuth2Credentials(access_token, client_id, client_secret, refresh_token, token_expiry, token_uri, user_agent)
http = httplib2.Http()
http = credentials.authorize(http)
return build('drive', 'v2', http=http, developerKey=API_KEY)
main()
I'm grateful to all who have provided the steps along the way to solving this.

Resources