[telegram] Add telegram bot
This commit is contained in:
parent
7a18d36217
commit
4b6a211c09
0
kibicara/platforms/telegram/__init__.py
Normal file
0
kibicara/platforms/telegram/__init__.py
Normal file
89
kibicara/platforms/telegram/bot.py
Normal file
89
kibicara/platforms/telegram/bot.py
Normal 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)
|
26
kibicara/platforms/telegram/model.py
Normal file
26
kibicara/platforms/telegram/model.py
Normal 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'
|
59
kibicara/platforms/telegram/webapi.py
Normal file
59
kibicara/platforms/telegram/webapi.py
Normal 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)
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from kibicara.platforms.test.webapi import router as test_router
|
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 router as twitter_router
|
||||||
from kibicara.platforms.twitter.webapi import twitter_callback_router
|
from kibicara.platforms.twitter.webapi import twitter_callback_router
|
||||||
from kibicara.webapi.admin import router as admin_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(triggers_router, prefix='/{hood_id}/triggers')
|
||||||
hoods_router.include_router(badwords_router, prefix='/{hood_id}/badwords')
|
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(test_router, prefix='/{hood_id}/test', tags=['test'])
|
||||||
|
hoods_router.include_router(
|
||||||
|
telegram_router, prefix='/{hood_id}/telegram', tags=['telegram']
|
||||||
|
)
|
||||||
hoods_router.include_router(
|
hoods_router.include_router(
|
||||||
twitter_router, prefix='/{hood_id}/twitter', tags=['twitter']
|
twitter_router, prefix='/{hood_id}/twitter', tags=['twitter']
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue