• Each .py file represents a module
  • Each folder contains .py files represents a package.
  • A __init__.py file is required inside each package folder.

Read More about Python Modules

After refactoring, the app will have below structure

app/
    __init__.py/
    handlers/
        __init__.py/
        base.py/
        bot.py/
static/
    images/
        favicon.ico/
app.yaml/
main.py/

__init__.py

# it's an empty file.

base.py

import webapp2


class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('Hello, This\' Telegram Bot website!')


class AboutPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('I\'m Telegram Bot')

bot.py

import webapp2
import urllib
import urllib2
import json
import logging

from google.appengine.api import urlfetch

TOKEN = 'xxxxxx:YOUR_TELEGRAM_BOT_TOKEN'
BASE_URL = 'https://api.telegram.org/bot' + TOKEN


class SetWebhookHandler(webapp2.RequestHandler):
    def get(self):
        urlfetch.set_default_fetch_deadline(60)
        url = self.request.get('url')
        if url:
            requestUrl = '{}/{}'.format(BASE_URL, 'setWebhook')
            requestParam = urllib.urlencode({'url': url})
            response = urllib2.urlopen(requestUrl, requestParam)
            self.response.write(json.dumps(json.load(response)))
        else:
            self.response.write('Invalid webhook URL: {}<br>'.format(url))


class GetWebhookHandler(webapp2.RequestHandler):
    def get(self):
        urlfetch.set_default_fetch_deadline(60)
        requestUrl = '{}/{}'.format(BASE_URL, 'getWebhookInfo')
        response = urllib2.urlopen(requestUrl)
        self.response.write(json.dumps(json.load(response)))


class WebhookHandler(webapp2.RequestHandler):
    def post(self):

        def defaultReply(message=None):
            global reply_method
            reply_method = 'sendMessage'
            message_id = message['message_id']
            chat_id = message['chat']['id']
            msg = 'I understand only text and sticker messages'
            return urllib.urlencode({
                'chat_id': str(chat_id),
                'text': msg.encode('utf-8'),
                'reply_to_message_id': str(message_id),
            })

        def generateTextReply(message=None):
            global reply_method
            reply_method = 'sendMessage'
            message_id = message['message_id']
            chat_id = message['chat']['id']
            first_name = message['from']['first_name']
            msg = 'Yo {} I got your message'.format(first_name)
            return urllib.urlencode({
                'chat_id': str(chat_id),
                'text': msg.encode('utf-8'),
                'reply_to_message_id': str(message_id),
            })

        def generateStickerReply(message=None):
            global reply_method
            reply_method = 'sendSticker'
            chat_id = message['chat']['id']
            file_id = message['sticker']['file_id']
            return urllib.urlencode({
                'chat_id': str(chat_id),
                'sticker': file_id.encode('utf-8'),
            })

        def generateReply(json_body=None):
            if json_body:
                try:
                    message = json_body['message']
                except:
                    message = json_body['edited_message']
                if message:
                    if 'sticker' in message:
                        return generateStickerReply(message)
                    elif 'text' in message:
                        return generateTextReply(message)
                    else:
                        return defaultReply(message)
                else:
                    logging.info('fail to generate reply')
                    return None

            else:
                logging.info('no json_body from telegram update')
                return None

        def sendReply(data=None):
            global reply_method
            if data:
                requestUrl = '{}/{}'.format(BASE_URL, reply_method)
                resp = urllib2.urlopen(requestUrl, data).read()
                logging.info('sendReply response: {0}'.format(resp))
            else:
                logging.info('fail to reply')
                resp = None

        reply_method = 'sendMessage'
        urlfetch.set_default_fetch_deadline(60)
        body = json.loads(self.request.body)
        logging.info('Update body: {}'.format(body))
        replyData = generateReply(body)
        sendReply(replyData)

main.py

import webapp2

from app.handlers import base, bot

app = webapp2.WSGIApplication([
    ('/', base.MainPage),
    ('/about', base.AboutPage),
    ('/set_webhook', bot.SetWebhookHandler),
    ('/get_webhook', bot.GetWebhookHandler),
    ('/webhook', bot.WebhookHandler),
], debug=True)