├── .gitattributes ├── .gitignore ├── README.md ├── bot.py ├── requirements.txt └── tgbot ├── banned_list.py ├── config.py ├── filters ├── __init__.py ├── admin_filter.py ├── banned_filter.py └── message_length.py ├── handlers ├── __init__.py ├── admin.py ├── banning.py └── user_message.py ├── models ├── __init__.py └── users_model.py └── utils └── extract_id.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # feedback bot 2 | A feedback bot for user-admin communication. 3 | 4 | Library used: [pyTelegramBotAPI](https://github.com/eternnoir/pyTelegramBotAPI) 5 | 6 | - Requires version of pyTelegramBotAPI above 4.6.0(currently github version, 15.07.2022) 7 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | # filters 4 | from tgbot.filters.admin_filter import AdminFilter 5 | from tgbot.filters.message_length import MessageLengthFilter 6 | from tgbot.filters.banned_filter import IsBannedFilter 7 | from telebot.asyncio_filters import IsReplyFilter 8 | 9 | # handlers 10 | from tgbot.handlers.admin import admin_user 11 | from tgbot.handlers.user_message import ( 12 | user_text_message, user_media_message, message_is_too_long, 13 | user_is_banned 14 | ) 15 | from tgbot.handlers.banning import ban_user, unban_user 16 | 17 | 18 | # telebot 19 | from telebot.async_telebot import AsyncTeleBot 20 | 21 | # config 22 | from tgbot import config 23 | 24 | 25 | MEDIA = ['photo', 'video', 'audio', 'document', 'voice', 'animation'] 26 | TEXT_MEDIA = ['text', 'photo', 'video', 'audio', 'document', 'voice', 'animation'] 27 | 28 | bot = AsyncTeleBot(config.TOKEN) 29 | def register_handlers(): 30 | 31 | bot.register_message_handler(ban_user, admin=True, pass_bot=True, is_reply=True, commands=['ban']) 32 | 33 | bot.register_message_handler(unban_user, admin=True, pass_bot=True, is_reply=True, commands=['unban']) 34 | 35 | bot.register_message_handler(admin_user, admin=True, pass_bot=True, is_reply=True, content_types=TEXT_MEDIA) 36 | 37 | bot.register_message_handler(user_text_message, content_types=['text'], pass_bot=True, admin=False, length=config.MAX_MESSAGE_LENGTH, banned=False) 38 | 39 | bot.register_message_handler(user_media_message, content_types=MEDIA,pass_bot=True, admin=False, length=config.MAX_CAPTION, banned=False) 40 | 41 | bot.register_message_handler(message_is_too_long, content_types=TEXT_MEDIA, pass_bot=True, admin=False, banned=False) 42 | 43 | bot.register_message_handler(user_is_banned, content_types=TEXT_MEDIA, pass_bot=True, admin=False, banned=True) 44 | 45 | 46 | register_handlers() 47 | 48 | 49 | 50 | # custom filters 51 | bot.add_custom_filter(AdminFilter()) 52 | bot.add_custom_filter(MessageLengthFilter()) 53 | bot.add_custom_filter(IsBannedFilter()) 54 | bot.add_custom_filter(IsReplyFilter()) 55 | 56 | 57 | async def run(): 58 | await bot.polling(non_stop=True) 59 | 60 | 61 | asyncio.run(run()) 62 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyTelegramBotAPI 2 | -------------------------------------------------------------------------------- /tgbot/banned_list.py: -------------------------------------------------------------------------------- 1 | """ 2 | A file which contains banned users. 3 | """ 4 | 5 | 6 | 7 | 8 | blocked_users = [] 9 | -------------------------------------------------------------------------------- /tgbot/config.py: -------------------------------------------------------------------------------- 1 | # any configuration should be stored here 2 | 3 | TOKEN = 'TOKEN' # configure env if you need; 4 | 5 | MAX_CAPTION = 950 # max length of caption (1024 in telegram) 6 | MAX_MESSAGE_LENGTH = 4000 # 4096 in telegram -------------------------------------------------------------------------------- /tgbot/filters/__init__.py: -------------------------------------------------------------------------------- 1 | # register filters here or in different folders. -------------------------------------------------------------------------------- /tgbot/filters/admin_filter.py: -------------------------------------------------------------------------------- 1 | from telebot.asyncio_filters import SimpleCustomFilter 2 | from tgbot.models.users_model import Admin 3 | 4 | 5 | class AdminFilter(SimpleCustomFilter): 6 | """ 7 | Filter for admin users 8 | """ 9 | 10 | key = 'admin' 11 | async def check(self, message): 12 | 13 | return int(message.chat.id) == int(Admin.ADMIN.value) -------------------------------------------------------------------------------- /tgbot/filters/banned_filter.py: -------------------------------------------------------------------------------- 1 | from telebot.asyncio_filters import SimpleCustomFilter 2 | from telebot.types import Message 3 | 4 | 5 | # list of banned users 6 | from tgbot.banned_list import blocked_users 7 | 8 | 9 | 10 | 11 | class IsBannedFilter(SimpleCustomFilter): 12 | """ 13 | Filter to check whether the user is banned or not. 14 | """ 15 | 16 | key = 'banned' 17 | 18 | async def check(self, message: Message): 19 | return message.chat.id in blocked_users -------------------------------------------------------------------------------- /tgbot/filters/message_length.py: -------------------------------------------------------------------------------- 1 | from telebot.asyncio_filters import AdvancedCustomFilter 2 | from telebot.types import Message 3 | 4 | 5 | 6 | class MessageLengthFilter(AdvancedCustomFilter): 7 | """ 8 | Filter to check the length of the message. 9 | """ 10 | 11 | key = 'length' 12 | 13 | async def check(self, message: Message, text): 14 | if message.text: 15 | return len(message.text) <= text 16 | return len(message.caption if message.caption else "") <= text 17 | -------------------------------------------------------------------------------- /tgbot/handlers/__init__.py: -------------------------------------------------------------------------------- 1 | # Create files for handlers in this folder. -------------------------------------------------------------------------------- /tgbot/handlers/admin.py: -------------------------------------------------------------------------------- 1 | from telebot.async_telebot import AsyncTeleBot 2 | from telebot.types import Message 3 | from telebot.asyncio_helper import ApiTelegramException 4 | from telebot import logger 5 | 6 | 7 | from tgbot.utils.extract_id import extract_id 8 | 9 | async def admin_user(message: Message, bot: AsyncTeleBot): 10 | """ 11 | Send the message to the admin. 12 | """ 13 | user_id = extract_id(message.reply_to_message.text if message.reply_to_message.text else message.reply_to_message.caption) 14 | if not user_id: 15 | await bot.reply_to( 16 | message, 17 | text='Please reply to a user\'s message.' 18 | ) 19 | return 20 | 21 | try: 22 | await bot.copy_message( 23 | chat_id=user_id, 24 | from_chat_id=message.chat.id, 25 | message_id=message.message_id 26 | ) 27 | except ApiTelegramException as e: 28 | if 'blocked' in e.description: 29 | await bot.reply_to( 30 | message, 31 | text='User has blocked the bot.' 32 | ) 33 | else: 34 | logger.error(e) -------------------------------------------------------------------------------- /tgbot/handlers/banning.py: -------------------------------------------------------------------------------- 1 | from telebot.async_telebot import AsyncTeleBot 2 | from telebot.types import Message 3 | 4 | from tgbot.banned_list import blocked_users 5 | from tgbot.utils.extract_id import extract_id 6 | 7 | 8 | async def ban_user(message: Message, bot: AsyncTeleBot): 9 | """ 10 | Bans the user. 11 | """ 12 | user_id = extract_id(message.reply_to_message.caption if message.reply_to_message.caption else message.reply_to_message.text) 13 | if user_id not in blocked_users: 14 | blocked_users.append(user_id) 15 | 16 | await bot.reply_to( 17 | message, 18 | text='Banned the user.' 19 | ) 20 | 21 | await bot.send_message( 22 | chat_id=user_id, 23 | text='You have been banned by the admin.' 24 | ) 25 | return 26 | await bot.reply_to( 27 | message, 28 | text='User is already banned.' 29 | ) 30 | 31 | async def unban_user(message: Message, bot: AsyncTeleBot): 32 | """ 33 | Unbans the user. 34 | """ 35 | 36 | user_id = extract_id(message.reply_to_message.caption if message.reply_to_message.caption else message.reply_to_message.text) 37 | 38 | if user_id in blocked_users: 39 | blocked_users.remove(user_id) 40 | await bot.reply_to( 41 | message, 42 | text='Unbanned the user.' 43 | ) 44 | await bot.send_message( 45 | chat_id=user_id, 46 | text='You have been unbanned by the admin.' 47 | ) 48 | return 49 | await bot.reply_to( 50 | message, 51 | text='The requested user is not banned. Unbanning was skipped.' 52 | ) -------------------------------------------------------------------------------- /tgbot/handlers/user_message.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles messages sent by user. 3 | Then sends the messge to models.Admin.ADMIN. 4 | """ 5 | 6 | 7 | from telebot.async_telebot import AsyncTeleBot 8 | from telebot.types import Message 9 | 10 | from telebot import formatting 11 | 12 | # import models 13 | from tgbot.models.users_model import Admin 14 | 15 | blocked_users = [] # if you want, you can rewrite the bot with database. 16 | 17 | 18 | async def user_text_message(message: Message, bot: AsyncTeleBot): 19 | """ 20 | Handles messages sent by user. 21 | Then sends the message to models.Admin.ADMIN. 22 | 23 | This handler only detects text messages. 24 | """ 25 | await bot.send_message( 26 | chat_id=Admin.ADMIN.value, text=f'#{str(message.chat.id)}\nMessage: {message.text}') 27 | 28 | 29 | async def user_media_message(message: Message, bot: AsyncTeleBot): 30 | """ 31 | Handles messages sent by user. 32 | Then sends the message to models.Admin.ADMIN. 33 | 34 | This handler only detects media messages. 35 | """ 36 | await bot.copy_message( 37 | chat_id=Admin.ADMIN.value, from_chat_id=message.chat.id, 38 | message_id=message.message_id, 39 | caption=formatting.format_text( 40 | f'#{message.chat.id}', 41 | 'Caption:', 42 | formatting.hitalic(message.caption if message.caption else 'No caption') 43 | ), 44 | parse_mode='HTML' 45 | ) 46 | 47 | 48 | async def user_is_banned(message: Message, bot: AsyncTeleBot): 49 | """ 50 | Show the user that he/she is banned. 51 | """ 52 | await bot.send_message( 53 | chat_id=message.chat.id, 54 | text='You were banned some time ago. You cannot write anymore.' 55 | ) 56 | 57 | 58 | async def message_is_too_long(message: Message, bot: AsyncTeleBot): 59 | """ 60 | Show the user that his/her message is too long. 61 | """ 62 | await bot.send_message( 63 | chat_id=message.chat.id, 64 | text='Text or caption of the message is too long. Please try to shorten it.' 65 | ) -------------------------------------------------------------------------------- /tgbot/models/__init__.py: -------------------------------------------------------------------------------- 1 | # Register models in this folder. -------------------------------------------------------------------------------- /tgbot/models/users_model.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | # Admin role 5 | class Admin(Enum): 6 | ADMIN = -10000000 # your admin id or group id 7 | # where the bot should get all replies from 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tgbot/utils/extract_id.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | def extract_id(text: str) -> int: 8 | """ 9 | Extracts the id from a string. 10 | """ 11 | try: 12 | return int(text.split()[0][1:]) 13 | except: 14 | return None --------------------------------------------------------------------------------