mirror of
https://github.com/miloszowi/everyone-mention-telegram-bot.git
synced 2025-05-20 09:14:07 +00:00
AbstractHandler wrap method addition, updated start text content and added buttons, changed MessageData to InboundMessage, better logging, code quality improvements, changed env files naming
This commit is contained in:
parent
fb223556cb
commit
ea2fddff40
18
CHANGELOG.md
18
CHANGELOG.md
@ -1,22 +1,16 @@
|
||||
# Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [UNRELEASED] - 07.10.2021
|
||||
## [UNRELEASED] - 11.10.2021
|
||||
### Added
|
||||
- Inline Query for join/leave/everyone
|
||||
- Group name validator
|
||||
- Banned users env
|
||||
- Access validator
|
||||
- ActionNotAllowedException
|
||||
|
||||
- Inline Query for `join`, `leave` & `everyone`
|
||||
- Banned users
|
||||
- Buttons for `start` message
|
||||
### Changed
|
||||
- code quality improvements
|
||||
|
||||
- Code quality improvements
|
||||
- `start` text
|
||||
### Deleted
|
||||
- `/silent` command
|
||||
|
||||
### Updated
|
||||
- start command content
|
||||
## [0.1.0] - 06.10.2021
|
||||
### Features
|
||||
- `/join` command
|
||||
|
@ -34,8 +34,8 @@ git clone https://github.com/miloszowi/everyone-mention-telegram-bot.git
|
||||
- copy environment files and fulfill empty values
|
||||
```bash
|
||||
cp .env.local .env
|
||||
cp docker/config/app.dist.env docker/config/app.env
|
||||
cp docker/config/database.dist.env docker/config/app.env
|
||||
cp docker/config/app.env.local docker/config/app.env
|
||||
cp docker/config/database.env.local docker/config/app.env
|
||||
```
|
||||
- start the project (`-d` flag will run containers in detached mode)
|
||||
```bash
|
||||
|
@ -3,7 +3,7 @@ from telegram.ext.dispatcher import Dispatcher
|
||||
|
||||
from bot.handler import *
|
||||
from bot.handler.abstractHandler import AbstractHandler
|
||||
from config.credentials import BOT_TOKEN, PORT, WEBHOOK_URL
|
||||
from config.envs import BOT_TOKEN, PORT, WEBHOOK_URL
|
||||
from logger import Logger
|
||||
|
||||
|
||||
|
@ -4,10 +4,31 @@ from telegram.ext import Handler
|
||||
from telegram.ext.callbackcontext import CallbackContext
|
||||
from telegram.update import Update
|
||||
|
||||
from bot.message.inboundMessage import InboundMessage
|
||||
from bot.message.replier import Replier
|
||||
from exception.actionNotAllowedException import ActionNotAllowedException
|
||||
from exception.invalidArgumentException import InvalidArgumentException
|
||||
from logger import Logger
|
||||
|
||||
|
||||
class AbstractHandler:
|
||||
bot_handler: Handler
|
||||
inbound: InboundMessage
|
||||
|
||||
@abstractmethod
|
||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||
raise Exception('handle method is not implemented')
|
||||
|
||||
def wrap(self, update: Update, context: CallbackContext) -> None:
|
||||
try:
|
||||
group_specific = self.is_group_specific()
|
||||
|
||||
self.inbound = InboundMessage.create(update, context, group_specific)
|
||||
self.handle(update, context)
|
||||
except (ActionNotAllowedException, InvalidArgumentException) as e:
|
||||
Replier.markdown(update, str(e))
|
||||
except Exception as e:
|
||||
Logger.exception(e)
|
||||
|
||||
def is_group_specific(self) -> bool:
|
||||
return True
|
||||
|
@ -3,9 +3,9 @@ from telegram.ext.commandhandler import CommandHandler
|
||||
from telegram.update import Update
|
||||
|
||||
from bot.handler.abstractHandler import AbstractHandler
|
||||
from bot.message.messageData import MessageData
|
||||
from bot.message.replier import Replier
|
||||
from config.contents import mention_failed
|
||||
from exception.notFoundException import NotFoundException
|
||||
from logger import Logger
|
||||
from repository.userRepository import UserRepository
|
||||
from utils.messageBuilder import MessageBuilder
|
||||
@ -17,19 +17,14 @@ class EveryoneHandler(AbstractHandler):
|
||||
action: str = 'everyone'
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.bot_handler = CommandHandler(self.action, self.handle)
|
||||
self.bot_handler = CommandHandler(self.action, self.wrap)
|
||||
self.user_repository = UserRepository()
|
||||
|
||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||
try:
|
||||
message_data = MessageData.create_from_arguments(update, context)
|
||||
except Exception as e:
|
||||
return Replier.markdown(update, str(e))
|
||||
|
||||
users = self.user_repository.get_all_for_chat(message_data.chat_id)
|
||||
|
||||
if users:
|
||||
Replier.markdown(update, MessageBuilder.mention_message(users))
|
||||
return Logger.action(message_data, self.action)
|
||||
users = self.user_repository.get_all_for_chat(self.inbound.chat_id)
|
||||
|
||||
Replier.markdown(update, mention_failed)
|
||||
Replier.markdown(update, MessageBuilder.mention_message(users))
|
||||
Logger.action(self.inbound, self.action)
|
||||
except NotFoundException:
|
||||
Replier.markdown(update, mention_failed)
|
||||
|
@ -3,7 +3,6 @@ from telegram.ext.commandhandler import CommandHandler
|
||||
from telegram.update import Update
|
||||
|
||||
from bot.handler.abstractHandler import AbstractHandler
|
||||
from bot.message.messageData import MessageData
|
||||
from bot.message.replier import Replier
|
||||
from config.contents import no_groups
|
||||
from exception.notFoundException import NotFoundException
|
||||
@ -18,19 +17,17 @@ class GroupsHandler(AbstractHandler):
|
||||
action: str = 'groups'
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.bot_handler = CommandHandler(self.action, self.handle)
|
||||
self.bot_handler = CommandHandler(self.action, self.wrap)
|
||||
self.group_repository = GroupRepository()
|
||||
|
||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||
try:
|
||||
message_data = MessageData.create_from_arguments(update, context, False)
|
||||
except Exception as e:
|
||||
return Replier.markdown(update, str(e))
|
||||
|
||||
try:
|
||||
groups = self.group_repository.get_by_chat_id(message_data.chat_id)
|
||||
groups = self.group_repository.get_by_chat_id(self.inbound.chat_id)
|
||||
Replier.html(update, MessageBuilder.group_message(groups))
|
||||
|
||||
Logger.action(message_data, self.action)
|
||||
Logger.action(self.inbound, self.action)
|
||||
except NotFoundException:
|
||||
Replier.markdown(update, no_groups)
|
||||
|
||||
def is_group_specific(self) -> bool:
|
||||
return False
|
||||
|
@ -3,7 +3,6 @@ from telegram.ext.commandhandler import CommandHandler
|
||||
from telegram.update import Update
|
||||
|
||||
from bot.handler.abstractHandler import AbstractHandler
|
||||
from bot.message.messageData import MessageData
|
||||
from bot.message.replier import Replier
|
||||
from config.contents import joined, not_joined
|
||||
from exception.notFoundException import NotFoundException
|
||||
@ -17,25 +16,20 @@ class JoinHandler(AbstractHandler):
|
||||
action: str = 'join'
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.bot_handler = CommandHandler(self.action, self.handle)
|
||||
self.bot_handler = CommandHandler(self.action, self.wrap)
|
||||
self.user_repository = UserRepository()
|
||||
|
||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||
try:
|
||||
message_data = MessageData.create_from_arguments(update, context)
|
||||
except Exception as e:
|
||||
return Replier.markdown(update, str(e))
|
||||
user = self.user_repository.get_by_id(self.inbound.user_id)
|
||||
|
||||
try:
|
||||
user = self.user_repository.get_by_id(message_data.user_id)
|
||||
if user.is_in_chat(self.inbound.chat_id):
|
||||
return Replier.markdown(update, Replier.interpolate(not_joined, self.inbound))
|
||||
|
||||
if user.is_in_chat(message_data.chat_id):
|
||||
return Replier.markdown(update, Replier.interpolate(not_joined, message_data))
|
||||
|
||||
user.add_to_chat(message_data.chat_id)
|
||||
user.add_to_chat(self.inbound.chat_id)
|
||||
self.user_repository.save(user)
|
||||
except NotFoundException:
|
||||
self.user_repository.save_by_message_data(message_data)
|
||||
self.user_repository.save_by_inbound_message(self.inbound)
|
||||
|
||||
Replier.markdown(update, Replier.interpolate(joined, message_data))
|
||||
Logger.action(message_data, self.action)
|
||||
Replier.markdown(update, Replier.interpolate(joined, self.inbound))
|
||||
Logger.action(self.inbound, self.action)
|
||||
|
@ -3,7 +3,6 @@ from telegram.ext.commandhandler import CommandHandler
|
||||
from telegram.update import Update
|
||||
|
||||
from bot.handler.abstractHandler import AbstractHandler
|
||||
from bot.message.messageData import MessageData
|
||||
from bot.message.replier import Replier
|
||||
from config.contents import left, not_left
|
||||
from exception.notFoundException import NotFoundException
|
||||
@ -17,21 +16,16 @@ class LeaveHandler(AbstractHandler):
|
||||
action: str = 'leave'
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.bot_handler = CommandHandler(self.action, self.handle)
|
||||
self.bot_handler = CommandHandler(self.action, self.wrap)
|
||||
self.user_repository = UserRepository()
|
||||
|
||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||
try:
|
||||
message_data = MessageData.create_from_arguments(update, context)
|
||||
except Exception as e:
|
||||
return Replier.markdown(update, str(e))
|
||||
|
||||
try:
|
||||
user = self.user_repository.get_by_id_and_chat_id(message_data.user_id, message_data.chat_id)
|
||||
user.remove_from_chat(message_data.chat_id)
|
||||
user = self.user_repository.get_by_id_and_chat_id(self.inbound.user_id, self.inbound.chat_id)
|
||||
user.remove_from_chat(self.inbound.chat_id)
|
||||
self.user_repository.save(user)
|
||||
|
||||
Replier.markdown(update, Replier.interpolate(left, message_data))
|
||||
Logger.action(message_data, self.action)
|
||||
Replier.markdown(update, Replier.interpolate(left, self.inbound))
|
||||
Logger.action(self.inbound, self.action)
|
||||
except NotFoundException:
|
||||
return Replier.markdown(update, Replier.interpolate(not_left, message_data))
|
||||
return Replier.markdown(update, Replier.interpolate(not_left, self.inbound))
|
||||
|
@ -1,9 +1,9 @@
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram.ext.callbackcontext import CallbackContext
|
||||
from telegram.ext.commandhandler import CommandHandler
|
||||
from telegram.update import Update
|
||||
|
||||
from bot.handler.abstractHandler import AbstractHandler
|
||||
from bot.message.messageData import MessageData
|
||||
from bot.message.replier import Replier
|
||||
from config.contents import start_text
|
||||
from logger import Logger
|
||||
@ -14,12 +14,23 @@ class StartHandler(AbstractHandler):
|
||||
action: str = 'start'
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.bot_handler = CommandHandler(self.action, self.handle)
|
||||
self.bot_handler = CommandHandler(self.action, self.wrap)
|
||||
|
||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||
try:
|
||||
MessageData.create_from_arguments(update, context)
|
||||
except Exception as e:
|
||||
return Replier.markdown(update, str(e))
|
||||
Replier.markdown(update, start_text)
|
||||
Logger.action(MessageData.create_from_arguments(update, context), self.action)
|
||||
markup = InlineKeyboardMarkup(
|
||||
[
|
||||
[
|
||||
InlineKeyboardButton('Inline Mode', switch_inline_query_current_chat='example'),
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton('GitHub', url='https://github.com/miloszowi/everyone-mention-telegram-bot'),
|
||||
InlineKeyboardButton('Creator', url='https://t.me/miloszowi')
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
Replier.html(update, start_text, markup)
|
||||
Logger.action(self.inbound, self.action)
|
||||
|
||||
def is_group_specific(self) -> bool:
|
||||
return False
|
||||
|
@ -12,21 +12,21 @@ from validator.groupNameValidator import GroupNameValidator
|
||||
|
||||
|
||||
@dataclass
|
||||
class MessageData:
|
||||
class InboundMessage:
|
||||
user_id: str
|
||||
chat_id: str
|
||||
group_name: str
|
||||
username: str
|
||||
|
||||
@staticmethod
|
||||
def create_from_arguments(update: Update, context: CallbackContext, include_group: bool = True) -> MessageData:
|
||||
def create(update: Update, context: CallbackContext, group_specific: bool) -> InboundMessage:
|
||||
user_id = str(update.effective_user.id)
|
||||
AccessValidator.validate(user_id)
|
||||
|
||||
chat_id = str(update.effective_chat.id)
|
||||
group_name = Group.default_name
|
||||
|
||||
if context.args and context.args[0] and include_group:
|
||||
if context.args and context.args[0] and group_specific:
|
||||
group_name = str(context.args[0]).lower()
|
||||
|
||||
GroupNameValidator.validate(group_name)
|
||||
@ -39,4 +39,4 @@ class MessageData:
|
||||
if not username:
|
||||
username = names.get_first_name()
|
||||
|
||||
return MessageData(user_id, chat_id, group_name, username)
|
||||
return InboundMessage(user_id, chat_id, group_name, username)
|
@ -1,29 +1,31 @@
|
||||
from telegram import Update
|
||||
from typing import Optional
|
||||
|
||||
from telegram import InlineKeyboardMarkup, Update
|
||||
from telegram.utils.helpers import mention_markdown
|
||||
|
||||
from bot.message.messageData import MessageData
|
||||
from bot.message.inboundMessage import InboundMessage
|
||||
from logger import Logger
|
||||
|
||||
|
||||
class Replier:
|
||||
|
||||
@staticmethod
|
||||
def interpolate(content: str, message_data: MessageData):
|
||||
def interpolate(content: str, inbound_message: InboundMessage):
|
||||
return content.format(
|
||||
mention_markdown(message_data.user_id, message_data.username),
|
||||
message_data.group_name
|
||||
mention_markdown(inbound_message.user_id, inbound_message.username),
|
||||
inbound_message.group_name
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def markdown(update: Update, message: str) -> None:
|
||||
def markdown(update: Update, message: str, reply_markup: Optional[InlineKeyboardMarkup] = None) -> None:
|
||||
try:
|
||||
update.effective_message.reply_markdown_v2(message)
|
||||
update.effective_message.reply_markdown_v2(message, reply_markup=reply_markup)
|
||||
except Exception as err:
|
||||
Logger.error(str(err))
|
||||
|
||||
@staticmethod
|
||||
def html(update: Update, html: str) -> None:
|
||||
def html(update: Update, html: str, reply_markup: Optional[InlineKeyboardMarkup] = None) -> None:
|
||||
try:
|
||||
update.effective_message.reply_html(html)
|
||||
update.effective_message.reply_html(html, reply_markup=reply_markup, disable_web_page_preview=True)
|
||||
except Exception as err:
|
||||
Logger.error(str(err))
|
||||
|
@ -1,3 +1,4 @@
|
||||
# markdownv2 python-telegram-bot specific
|
||||
joined = '{} joined group `{}`'
|
||||
not_joined = '{} is already in group `{}`'
|
||||
left = '{} left group `{}`'
|
||||
@ -5,27 +6,38 @@ not_left = '{} did not join group `{}` before'
|
||||
mention_failed = 'There are no users to mention'
|
||||
no_groups = 'There are no groups for this chat'
|
||||
|
||||
|
||||
# html python-telegram-bot specific
|
||||
start_text = """
|
||||
Hello there
|
||||
I am @everyone\_mention\_bot
|
||||
I am here to help you with mass notifies
|
||||
Hello!
|
||||
@everyone_mention_bot here.
|
||||
I am here to help you with multiple user mentions.
|
||||
|
||||
Please take a look at available commands
|
||||
`<group-name>` is not required, if not given, it is set to `default`
|
||||
Using <code>Inline Mode</code> is recommended because <a href="https://core.telegram.org/bots/faq#what-messages-will-my-bot-get">policy of bots with privacy mode enabled</a> says that command trigger is sent (without mentioning the bot) only to the last mentioned bot. So if you do have multiple bots in current chat, I might not receive your command!
|
||||
|
||||
To join group:
|
||||
`/join <group-name>`
|
||||
for example: `/join games`
|
||||
Available commands:
|
||||
<b>Please note</b>
|
||||
<code>{group-name}</code> is not required, <code>default</code> if not given.
|
||||
|
||||
To leave group:
|
||||
`/leave <group-name>`
|
||||
<b>Join</b>
|
||||
Joins (or creates if group did not exist before) group.
|
||||
<pre>/join {group-name}</pre>
|
||||
|
||||
To gather everyone attention use:
|
||||
`/everyone <group-name>`
|
||||
<b>Leave</b>
|
||||
Leaves (or deletes if no other users are left) the group
|
||||
<pre>/leave {group-name}</pre>
|
||||
|
||||
To see all available groups use:
|
||||
`/groups`
|
||||
<b>Everyone</b>
|
||||
Mentions everyone that joined the group.
|
||||
<pre>/everyone {group-name}</pre>
|
||||
|
||||
You can also try to tag me @everyone\_mention\_bot and then enter group name
|
||||
Possible results will be displayed
|
||||
<b>Groups</b>
|
||||
Show all created groups in this chat.
|
||||
<pre>/groups</pre>
|
||||
|
||||
<b>Start</b>
|
||||
Show start & help text
|
||||
<pre>/start</pre>
|
||||
|
||||
Reach out to <a href="https://t.me/miloszowi">Creator</a> in case of any issues/questions regarding my usage.
|
||||
"""
|
||||
|
@ -3,9 +3,9 @@ from urllib.parse import quote_plus
|
||||
from pymongo import MongoClient
|
||||
from pymongo.database import Database
|
||||
|
||||
from config.credentials import (MONGODB_DATABASE, MONGODB_HOSTNAME,
|
||||
MONGODB_PASSWORD, MONGODB_PORT,
|
||||
MONGODB_USERNAME)
|
||||
from config.envs import (MONGODB_DATABASE, MONGODB_HOSTNAME,
|
||||
MONGODB_PASSWORD, MONGODB_PORT,
|
||||
MONGODB_USERNAME)
|
||||
|
||||
|
||||
class Client:
|
||||
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
import os
|
||||
|
||||
from bot.message.messageData import MessageData
|
||||
from bot.message.inboundMessage import InboundMessage
|
||||
|
||||
|
||||
# noinspection SpellCheckingInspection
|
||||
@ -41,17 +41,21 @@ class Logger:
|
||||
Logger()
|
||||
|
||||
@staticmethod
|
||||
def get_logger(logger_name) -> logging.Logger:
|
||||
def get(logger_name: str) -> logging.Logger:
|
||||
return logging.getLogger(logger_name)
|
||||
|
||||
@staticmethod
|
||||
def info(message: str) -> None:
|
||||
Logger.get_logger(Logger.action_logger).info(message)
|
||||
Logger.get(Logger.action_logger).info(message)
|
||||
|
||||
@staticmethod
|
||||
def error(message: str) -> None:
|
||||
Logger.get_logger(Logger.main_logger).error(message)
|
||||
Logger.get(Logger.main_logger).error(message)
|
||||
|
||||
@staticmethod
|
||||
def action(message_data: MessageData, action: str) -> None:
|
||||
Logger.info(f'User {message_data.username}({message_data.user_id}) called {action.upper()} for {message_data.chat_id}')
|
||||
def exception(exception: Exception) -> None:
|
||||
Logger.get(Logger.main_logger).exception(exception)
|
||||
|
||||
@staticmethod
|
||||
def action(inbound: InboundMessage, action: str) -> None:
|
||||
Logger.info(f'User {inbound.username}({inbound.user_id}) called {action.upper()} for {inbound.chat_id}')
|
||||
|
@ -1,6 +1,6 @@
|
||||
from typing import Iterable
|
||||
|
||||
from bot.message.messageData import MessageData
|
||||
from bot.message.inboundMessage import InboundMessage
|
||||
from database.client import Client
|
||||
from entity.user import User
|
||||
from exception.notFoundException import NotFoundException
|
||||
@ -44,13 +44,13 @@ class UserRepository:
|
||||
user.to_mongo_document()
|
||||
)
|
||||
|
||||
def save_by_message_data(self, data: MessageData) -> None:
|
||||
def save_by_inbound_message(self, inbound_message: InboundMessage) -> None:
|
||||
self.client.insert_one(
|
||||
User.collection,
|
||||
{
|
||||
User.id_index: data.user_id,
|
||||
User.username_index: data.username,
|
||||
User.chats_index: [data.chat_id]
|
||||
User.id_index: inbound_message.user_id,
|
||||
User.username_index: inbound_message.username,
|
||||
User.chats_index: [inbound_message.chat_id]
|
||||
}
|
||||
)
|
||||
|
||||
@ -68,4 +68,7 @@ class UserRepository:
|
||||
for record in users:
|
||||
result.append(User.from_mongo_document(record))
|
||||
|
||||
if not result:
|
||||
raise NotFoundException
|
||||
|
||||
return result
|
||||
|
@ -1,4 +1,4 @@
|
||||
from config.credentials import BANNED_USERS
|
||||
from config.envs import BANNED_USERS
|
||||
from exception.actionNotAllowedException import ActionNotAllowedException
|
||||
|
||||
|
||||
@ -7,4 +7,4 @@ class AccessValidator:
|
||||
@staticmethod
|
||||
def validate(user_id: str) -> None:
|
||||
if user_id in BANNED_USERS:
|
||||
raise ActionNotAllowedException('You are banned')
|
||||
raise ActionNotAllowedException('User is banned')
|
||||
|
Loading…
x
Reference in New Issue
Block a user