[telegram] Add telegram bot

This commit is contained in:
Cathy Hu 2020-07-10 23:53:24 +02:00 committed by dl6tom
parent 7a18d36217
commit 4b6a211c09
5 changed files with 178 additions and 0 deletions

View file

View file

@ -0,0 +1,89 @@
# Copyright (C) 2020 by Cathy Hu <cathy.hu@fau.de>
#
# SPDX-License-Identifier: 0BSD
from aiogram import Bot, Dispatcher, exceptions, types
from asyncio import gather, sleep
from kibicara.config import config
from kibicara.platformapi import Censor, Message, Spawner
from kibicara.platforms.telegram.model import Telegram, TelegramUser
from logging import getLogger
logger = getLogger(__name__)
class TelegramBot(Censor):
def __init__(self, telegram_model):
super().__init__(telegram_model.hood)
self.telegram_model = telegram_model
self.bot = Bot(token=telegram_model.api_token)
self.dp = Dispatcher(self.bot)
self.dp.register_message_handler(self._send_welcome, commands=['start'])
self.dp.register_message_handler(self._receive_message)
async def run(self):
await gather(self.dp.start_polling(), self.push())
async def push(self):
while True:
message = await self.receive()
logger.debug(
'Received message from censor (%s): %s'
% (self.telegram_model.hood.name, message.text)
)
for user in await TelegramUser.objects.filter(
bot=self.telegram_model
).all():
await self._send_message(user.user_id, message.text)
async def _send_message(self, user_id, message):
try:
await self.bot.send_message(user_id, message, disable_notification=False)
except exceptions.BotBlocked:
logger.error(
'Target [ID:%s] (%s): blocked by user'
% (user_id, self.telegram_model.hood.name)
)
except exceptions.ChatNotFound:
logger.error(
'Target [ID:%s] (%s): invalid user ID'
% (user_id, self.telegram_model.hood.name)
)
except exceptions.RetryAfter as e:
logger.error(
'Target [ID:%s] (%s): Flood limit is exceeded. Sleep %d seconds.'
% (user_id, self.telegram_model.hood.name, e.timeout)
)
await sleep(e.timeout)
return await self._send_message(user_id, text)
except exceptions.UserDeactivated:
logger.error(
'Target [ID:%s] (%s): user is deactivated'
% (user_id, self.telegram_model.hood.name)
)
except exceptions.TelegramAPIError:
logger.exception(
'Target [ID:%s] (%s): failed' % (user_id, self.telegram_model.hood.name)
)
async def _send_welcome(self, message: types.Message):
try:
if message.from_user.is_bot:
await message.reply('Error: Bots can not join here.')
return
await TelegramUser.objects.create(
user_id=message.from_user.id, bot=self.telegram_model
)
await message.reply(self.telegram_model.welcome_message)
except IntegrityError:
await message.reply('Error: You are already registered.')
async def _receive_message(self, message: types.Message):
if not message.text:
await message.reply('Error: Only text messages are allowed.')
return
await self.publish(Message(message.text))
spawner = Spawner(Telegram, TelegramBot)

View file

@ -0,0 +1,26 @@
# Copyright (C) 2020 by Cathy Hu <cathy.hu@fau.de>
#
# SPDX-License-Identifier: 0BSD
from kibicara.model import Hood, Mapping
from ormantic import Boolean, Integer, ForeignKey, Model, Text
class Telegram(Model):
id: Integer(primary_key=True) = None
hood: ForeignKey(Hood)
api_token: Text()
welcome_message: Text()
class Mapping(Mapping):
table_name = 'telegrambots'
class TelegramUser(Model):
id: Integer(primary_key=True) = None
user_id: Integer(unique=True)
# TODO unique
bot: ForeignKey(Telegram)
class Mapping(Mapping):
table_name = 'telegramusers'

View file

@ -0,0 +1,59 @@
# Copyright (C) 2020 by Cathy Hu <cathy.hu@fau.de>
#
# SPDX-License-Identifier: 0BSD
from fastapi import APIRouter, Depends, HTTPException, Response, status
from kibicara.config import config
from kibicara.platforms.telegram.bot import spawner
from kibicara.platforms.telegram.model import Telegram
from kibicara.webapi.hoods import get_hood
from logging import getLogger
from sqlite3 import IntegrityError
from ormantic.exceptions import NoMatch
from pydantic import BaseModel
logger = getLogger(__name__)
class BodyTelegram(BaseModel):
api_token: str
welcome_message: str = 'Welcome!'
async def get_telegram(telegram_id: int, hood=Depends(get_hood)):
try:
return await Telegram.objects.get(id=telegram_id, hood=hood)
except NoMatch:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
router = APIRouter()
telegram_callback_router = APIRouter()
@router.get('/')
async def telegram_read_all(hood=Depends(get_hood)):
return await Telegram.objects.filter(hood=hood).all()
@router.get('/{telegram_id}')
async def telegram_read(telegram=Depends(get_telegram)):
return telegram
@router.delete('/{telegram_id}', status_code=status.HTTP_204_NO_CONTENT)
async def telegram_delete(telegram=Depends(get_telegram)):
spawner.stop(telegram)
await telegram.delete()
@router.post('/', status_code=status.HTTP_201_CREATED)
async def telegram_create(values: BodyTelegram, hood=Depends(get_hood)):
try:
telegram = await Telegram.objects.create(hood=hood, **values.__dict__)
spawner.start(telegram)
response.headers['Location'] = '%d' % telegram.id
return telegram
except IntegrityError:
raise HTTPException(status_code=status.HTTP_409_CONFLICT)

View file

@ -5,6 +5,7 @@
from fastapi import APIRouter
from kibicara.platforms.test.webapi import router as test_router
from kibicara.platforms.telegram.webapi import router as telegram_router
from kibicara.platforms.twitter.webapi import router as twitter_router
from kibicara.platforms.twitter.webapi import twitter_callback_router
from kibicara.webapi.admin import router as admin_router
@ -18,6 +19,9 @@ router.include_router(admin_router, prefix='/admin', tags=['admin'])
hoods_router.include_router(triggers_router, prefix='/{hood_id}/triggers')
hoods_router.include_router(badwords_router, prefix='/{hood_id}/badwords')
hoods_router.include_router(test_router, prefix='/{hood_id}/test', tags=['test'])
hoods_router.include_router(
telegram_router, prefix='/{hood_id}/telegram', tags=['telegram']
)
hoods_router.include_router(
twitter_router, prefix='/{hood_id}/twitter', tags=['twitter']
)