2015-11-16 13:05:57 +01:00
|
|
|
import logging
|
|
|
|
|
|
|
|
from telegram import Update, NullHandler
|
2016-02-09 21:14:48 +01:00
|
|
|
from future.utils import bytes_to_native_str
|
2015-11-24 15:39:00 +01:00
|
|
|
from threading import Lock
|
2015-11-16 13:05:57 +01:00
|
|
|
import json
|
|
|
|
try:
|
|
|
|
import BaseHTTPServer
|
|
|
|
except ImportError:
|
|
|
|
import http.server as BaseHTTPServer
|
|
|
|
|
|
|
|
|
2016-03-15 02:56:20 +01:00
|
|
|
logging.getLogger(__name__).addHandler(NullHandler())
|
2015-11-16 13:05:57 +01:00
|
|
|
|
|
|
|
|
2016-02-09 21:14:48 +01:00
|
|
|
class _InvalidPost(Exception):
|
|
|
|
|
|
|
|
def __init__(self, http_code):
|
|
|
|
self.http_code = http_code
|
|
|
|
super(_InvalidPost, self).__init__()
|
|
|
|
|
|
|
|
|
2015-11-16 20:35:27 +01:00
|
|
|
class WebhookServer(BaseHTTPServer.HTTPServer, object):
|
2015-11-16 13:05:57 +01:00
|
|
|
def __init__(self, server_address, RequestHandlerClass, update_queue,
|
|
|
|
webhook_path):
|
2015-11-16 20:43:35 +01:00
|
|
|
super(WebhookServer, self).__init__(server_address,
|
|
|
|
RequestHandlerClass)
|
2015-11-21 23:09:19 +01:00
|
|
|
self.logger = logging.getLogger(__name__)
|
2015-11-16 13:05:57 +01:00
|
|
|
self.update_queue = update_queue
|
|
|
|
self.webhook_path = webhook_path
|
2015-11-21 21:22:11 +01:00
|
|
|
self.is_running = False
|
2015-11-24 15:39:00 +01:00
|
|
|
self.server_lock = Lock()
|
|
|
|
self.shutdown_lock = Lock()
|
2015-11-21 21:22:11 +01:00
|
|
|
|
|
|
|
def serve_forever(self, poll_interval=0.5):
|
2015-11-24 15:39:00 +01:00
|
|
|
with self.server_lock:
|
|
|
|
self.is_running = True
|
2016-03-15 02:56:20 +01:00
|
|
|
self.logger.debug('Webhook Server started.')
|
2015-11-24 15:39:00 +01:00
|
|
|
super(WebhookServer, self).serve_forever(poll_interval)
|
2016-03-15 02:56:20 +01:00
|
|
|
self.logger.debug('Webhook Server stopped.')
|
2015-11-21 21:22:11 +01:00
|
|
|
|
|
|
|
def shutdown(self):
|
2015-11-24 15:39:00 +01:00
|
|
|
with self.shutdown_lock:
|
|
|
|
if not self.is_running:
|
2016-03-15 02:56:20 +01:00
|
|
|
self.logger.warn('Webhook Server already stopped.')
|
2015-11-24 15:39:00 +01:00
|
|
|
return
|
|
|
|
else:
|
|
|
|
super(WebhookServer, self).shutdown()
|
|
|
|
self.is_running = False
|
2015-11-16 13:05:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
# WebhookHandler, process webhook calls
|
|
|
|
# Based on: https://github.com/eternnoir/pyTelegramBotAPI/blob/master/
|
|
|
|
# examples/webhook_examples/webhook_cpython_echo_bot.py
|
2015-11-24 19:33:03 +01:00
|
|
|
class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
|
2016-03-15 02:56:20 +01:00
|
|
|
server_version = 'WebhookHandler/1.0'
|
2015-11-16 13:05:57 +01:00
|
|
|
|
|
|
|
def __init__(self, request, client_address, server):
|
2015-11-21 23:09:19 +01:00
|
|
|
self.logger = logging.getLogger(__name__)
|
2015-11-16 20:35:27 +01:00
|
|
|
super(WebhookHandler, self).__init__(request, client_address, server)
|
2015-11-16 13:05:57 +01:00
|
|
|
|
|
|
|
def do_HEAD(self):
|
|
|
|
self.send_response(200)
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
def do_GET(self):
|
|
|
|
self.send_response(200)
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
def do_POST(self):
|
2016-03-15 02:56:20 +01:00
|
|
|
self.logger.debug('Webhook triggered')
|
2016-02-09 21:14:48 +01:00
|
|
|
try:
|
|
|
|
self._validate_post()
|
|
|
|
clen = self._get_content_len()
|
|
|
|
except _InvalidPost as e:
|
|
|
|
self.send_error(e.http_code)
|
|
|
|
self.end_headers()
|
|
|
|
else:
|
|
|
|
buf = self.rfile.read(clen)
|
|
|
|
json_string = bytes_to_native_str(buf)
|
2015-11-16 13:05:57 +01:00
|
|
|
|
|
|
|
self.send_response(200)
|
|
|
|
self.end_headers()
|
|
|
|
|
2016-03-15 02:56:20 +01:00
|
|
|
self.logger.debug('Webhook received data: ' + json_string)
|
2015-11-21 23:09:19 +01:00
|
|
|
|
2015-11-16 13:05:57 +01:00
|
|
|
update = Update.de_json(json.loads(json_string))
|
2016-03-15 02:56:20 +01:00
|
|
|
self.logger.debug('Received Update with ID %d on Webhook' %
|
|
|
|
update.update_id)
|
2015-11-16 13:05:57 +01:00
|
|
|
self.server.update_queue.put(update)
|
|
|
|
|
2016-02-09 21:14:48 +01:00
|
|
|
def _validate_post(self):
|
|
|
|
if not (self.path == self.server.webhook_path and
|
|
|
|
'content-type' in self.headers and
|
|
|
|
self.headers['content-type'] == 'application/json'):
|
|
|
|
raise _InvalidPost(403)
|
|
|
|
|
|
|
|
def _get_content_len(self):
|
|
|
|
clen = self.headers.get('content-length')
|
|
|
|
if clen is None:
|
|
|
|
raise _InvalidPost(411)
|
|
|
|
try:
|
|
|
|
clen = int(clen)
|
|
|
|
except ValueError:
|
|
|
|
raise _InvalidPost(403)
|
|
|
|
if clen < 0:
|
|
|
|
raise _InvalidPost(403)
|
|
|
|
return clen
|