Source code for permabots.views.hooks.telegram_hook

from rest_framework.views import APIView
from permabots.serializers import UpdateSerializer
from permabots.models import TelegramBot, TelegramUser, TelegramChat, TelegramMessage, TelegramUpdate, TelegramCallbackQuery
from rest_framework.response import Response
from rest_framework import status
import logging
from permabots.tasks import handle_update
from datetime import datetime
from permabots import caching
import sys
import traceback


logger = logging.getLogger(__name__)

[docs]class OnlyTextMessages(Exception): pass
[docs]class TelegramHookView(APIView): """ View for Telegram webhook """
[docs] def create_update(self, serializer, bot): if 'message' in serializer.data: try: user = caching.get_or_set(TelegramUser, serializer.data['message']['from']['id']) except TelegramUser.DoesNotExist: user, _ = TelegramUser.objects.get_or_create(**serializer.data['message']['from']) try: chat = caching.get_or_set(TelegramChat, serializer.data['message']['chat']['id']) except TelegramChat.DoesNotExist: chat, _ = TelegramChat.objects.get_or_create(**serializer.data['message']['chat']) if 'text' not in serializer.data['message']: raise OnlyTextMessages message, _ = TelegramMessage.objects.get_or_create(message_id=serializer.data['message']['message_id'], from_user=user, date=datetime.fromtimestamp(serializer.data['message']['date']), chat=chat, text=serializer.data['message']['text']) update, _ = TelegramUpdate.objects.get_or_create(bot=bot, update_id=serializer.data['update_id'], message=message) elif 'callback_query' in serializer.data: # Message may be not present if it is very old if 'message' in serializer.data['callback_query']: try: user = caching.get_or_set(TelegramUser, serializer.data['callback_query']['message']['from']['id']) except TelegramUser.DoesNotExist: user, _ = TelegramUser.objects.get_or_create(**serializer.data['callback_query']['message']['from']) try: chat = caching.get_or_set(TelegramChat, serializer.data['callback_query']['message']['chat']['id']) except TelegramChat.DoesNotExist: chat, _ = TelegramChat.objects.get_or_create(**serializer.data['callback_query']['message']['chat']) message, _ = TelegramMessage.objects.get_or_create(message_id=serializer.data['callback_query']['message']['message_id'], from_user=user, date=datetime.fromtimestamp(serializer.data['callback_query']['message']['date']), chat=chat, text=serializer.data['callback_query']['message']['text']) else: message = None try: user = caching.get_or_set(TelegramUser, serializer.data['callback_query']['from']['id']) except TelegramUser.DoesNotExist: user, _ = TelegramUser.objects.get_or_create(**serializer.data['callback_query']['from']) callback_query, _ = TelegramCallbackQuery.objects.get_or_create(callback_id=serializer.data['callback_query']['id'], from_user=user, message=message, data=serializer.data['callback_query']['data']) update, _ = TelegramUpdate.objects.get_or_create(bot=bot, update_id=serializer.data['update_id'], callback_query=callback_query) else: logger.error("Not valid message %s" % serializer.data) raise OnlyTextMessages caching.set(update) return update
[docs] def post(self, request, hook_id): """ Process Telegram webhook. 1. Serialize Telegram message 2. Get an enabled Telegram bot 3. Create :class:`Update <permabots.models.telegram_api.Update>` 5. Delay processing to a task 6. Response provider """ serializer = UpdateSerializer(data=request.data) if serializer.is_valid(): try: bot = caching.get_or_set(TelegramBot, hook_id) except TelegramBot.DoesNotExist: logger.warning("Hook id %s not associated to an bot" % hook_id) return Response(serializer.errors, status=status.HTTP_404_NOT_FOUND) try: update = self.create_update(serializer, bot) if bot.enabled: logger.debug("Telegram Bot %s attending request %s" % (bot.token, request.data)) handle_update.delay(update.id, bot.id) else: logger.error("Update %s ignored by disabled bot %s" % (update, bot.token)) except OnlyTextMessages: logger.warning("Not text message %s for bot %s" % (request.data, hook_id)) return Response(status=status.HTTP_200_OK) except: exc_info = sys.exc_info() traceback.print_exception(*exc_info) logger.error("Error processing %s for bot %s" % (request.data, hook_id)) return Response(serializer.errors, status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: return Response(serializer.data, status=status.HTTP_200_OK) logger.error("Validation error: %s from message %s" % (serializer.errors, request.data)) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)