Dynamic mentioning added, removed some unwanted properties, updated README

This commit is contained in:
miloszowi 2021-11-12 12:23:54 +01:00
parent f9643f052e
commit 6790426ba2
10 changed files with 99 additions and 21 deletions

View File

@ -1,6 +1,12 @@
# Change Log
All notable changes to this project will be documented in this file.
## [0.3.0] - 12.11.2021
### Added
- Dynamic mentioning by `@` character
### Changed
- `start` text
- Group name validation - those are forbidden now - `all`, `channel`, `chat`, `everyone`, `group`, `here`
## [0.2.0] - 26.10.2021
### Added
- Inline Mode for `join`, `leave` & `everyone`

View File

@ -5,6 +5,8 @@
# Contents
* [Description](#description)
* [Usage](#usage)
* [Dynamic Mentioning](#dynamic-mentioning)
* [Commands](#commands)
* [`/join`](#join)
* [`/leave`](#leave)
@ -12,8 +14,7 @@
* [`/groups`](#groups)
* [`/start`](#start)
* [Example command flow](#example-command-flow)
* [Inline Mode](#inline-mode)
* [Usage](#usage)
* [Inline Mode Usage](#inline-mode-usage)
* [Getting started.](#getting-started)
* [Requirements](#requirements)
* [Installation](#installation)
@ -24,6 +25,32 @@ Everyone Mention Bot is simple, but useful telegram bot to gather group members
You can create groups per chat to mention every user that joined the group by calling one command instead of mentioning them one by one.
## Usage
First, users need to join the group to let mentioning them, to do that, they simply need to join specific group.
It can be done in 2 ways:
- Command [`/join`](#join)
- [Inline Mode](#inline-mode-usage)
Users that have joined the group can be mentioned in 3 ways:
- [Dynaminc Mentioning](#dynamic-mentioning) by `@`, for example `@everyone`
- Command [`/everyone`](#everyone)
- [Inline Mode](#inline-mode-usage)
To leave the group use one of the two ways:
- Command [`/leave`](#leave)
- [Inline Mode](#inline-mode-usage)
To display available groups:
- Command [`/groups`](#groups)
## Dynamic mentioning
You can use `@` character (as you would mention a user) to mention specific group.
All the below will mention users from `default` group.
`@all`, `@channel`, `@chat`, `@everyone`, `@group`, `@here`.
If you did create a group named `gaming`, you can simply use `@gaming` in your text to mention them all.
## Commands
*Important*: `{group-name}` is not required, if not given, it will be set to `default`.
### `/join`
@ -85,10 +112,7 @@ Start & Help message
### Example command flow
![example command flow](docs/flow_command.png)
## Inline Mode
Using Inline Mode is recommended because policy of bots with privacy mode enabled (https://core.telegram.org/bots/faq#what-messages-will-my-bot-get) 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!
### Usage
### Inline Mode Usage
To use inline mode, type `@everyone_mention_bot` in telegram message input or click on the `Inline Mode` button from `/start` command.
![inline popup](docs/inline_mode_1.png)

View File

@ -1,5 +1,5 @@
__all__ = [
'abstractHandler', 'everyoneHandler', 'groupsHandler',
'inlineQueryHandler', 'joinHandler', 'leaveHandler',
'startHandler'
'startHandler', 'dynamicMentionHandler'
]

View File

@ -9,6 +9,7 @@ from bot.message.replier import Replier
from exception.actionNotAllowedException import ActionNotAllowedException
from exception.invalidActionException import InvalidActionException
from exception.invalidArgumentException import InvalidArgumentException
from exception.notFoundException import NotFoundException
from logger import Logger
@ -30,6 +31,8 @@ class AbstractHandler:
Logger.action(self.inbound, self.action)
except (InvalidActionException, InvalidArgumentException, ActionNotAllowedException) as e:
Replier.markdown(update, str(e))
except NotFoundException:
pass # probably just mentioning user
except Exception as e:
Logger.exception(e)

View File

@ -0,0 +1,28 @@
import re
from telegram.ext import Filters, MessageHandler
from telegram.ext.callbackcontext import CallbackContext
from telegram.update import Update
from bot.handler.abstractHandler import AbstractHandler
from bot.message.replier import Replier
from repository.chatRepository import ChatRepository
from utils.messageBuilder import MessageBuilder
class DynamicMentionHandler(AbstractHandler):
bot_handler: MessageHandler
chat_repository: ChatRepository
action: str = 'dynamic-mention'
def __init__(self) -> None:
self.bot_handler = MessageHandler(
Filters.regex(re.compile(r'@[^ ]')),
self.wrap
)
self.chat_repository = ChatRepository()
def handle(self, update: Update, context: CallbackContext) -> None:
users = self.chat_repository.get_users_for_group(self.inbound)
Replier.markdown(update, MessageBuilder.mention_message(users))

View File

@ -8,24 +8,21 @@ from config.contents import mention_failed
from exception.invalidActionException import InvalidActionException
from exception.notFoundException import NotFoundException
from repository.chatRepository import ChatRepository
from repository.userRepository import UserRepository
from utils.messageBuilder import MessageBuilder
class EveryoneHandler(AbstractHandler):
bot_handler: CommandHandler
chat_repository: ChatRepository
user_repository: UserRepository
action: str = 'everyone'
def __init__(self) -> None:
self.bot_handler = CommandHandler(self.action, self.wrap)
self.chat_repository = ChatRepository()
self.user_repository = UserRepository()
def handle(self, update: Update, context: CallbackContext) -> None:
try:
users = self.chat_repository.get_users_for_group(self.inbound.chat_id, self.inbound.group_name)
users = self.chat_repository.get_users_for_group(self.inbound)
Replier.markdown(update, MessageBuilder.mention_message(users))
except NotFoundException as e:

View File

@ -3,6 +3,7 @@ from __future__ import annotations
from dataclasses import dataclass
import names
import re
from telegram.ext.callbackcontext import CallbackContext
from telegram.update import Update
@ -27,11 +28,20 @@ class InboundMessage:
chat_id = str(update.effective_chat.id)
group_name = InboundMessage.default_group
# done upon resolving a command action
if context.args and context.args[0] and group_specific:
group_name = str(context.args[0]).lower()
GroupNameValidator.validate(group_name)
# done upon resolving a message handler action
if '@' in update.message.text:
searched_message_part = [part for part in update.message.text.split(' ') if '@' in part][0]
group_name = re.sub(r'\W+', '', searched_message_part)
if group_name in GroupNameValidator.FORBIDDEN_GROUP_NAMES:
group_name = InboundMessage.default_group
username = update.effective_user.username or update.effective_user.first_name
if not username:

View File

@ -10,13 +10,16 @@ no_groups = 'There are no groups for this chat'
start_text = """
Hello!
@everyone_mention_bot here.
<b>Description</b>:
I <b>do not</b> have access to your messages!
I am here to help you with multiple user mentions.
<b>Usage</b>:
Users that joined the group by <code>/join</code> command, can be mentioned after calling <code>/everyone</code> command.
Users that joined the group by <code>/join</code> command,
can be mentioned after typing one of those in your message:
<code>@all</code>, <code>@channel</code>, <code>@chat</code>, <code>@everyone</code>, <code>@group</code> or <code>@here</code>.
If you did create a group named <code>gaming</code>, simply use <code>@gaming</code> to call users from that group.
You can also use <code>/everyone</code> command.
<b>Commands</b>:
<pre>/join {group-name}</pre>
@ -36,6 +39,4 @@ Show start & help text
<b>Please note</b>
<code>{group-name}</code> is not required, <code>default</code> if not given.
If your chat does have multiple bots <b>I might not receive your command</b> according to <a href="https://core.telegram.org/bots/faq#what-messages-will-my-bot-get">policy of bots with privacy mode enabled</a> - use <code>Inline Mode</code> to avoid this.
"""

View File

@ -39,12 +39,17 @@ class ChatRepository(AbstractRepository):
return Chat.from_mongo_document(chat)
def get_users_for_group(self, chat_id: str, group: str) -> Iterable[User]:
chat = self.get(chat_id)
if not chat.groups.get(group):
def get_users_for_group(self, inbound: InboundMessage) -> Iterable[User]:
chat = self.get(inbound.chat_id)
if not chat.groups.get(inbound.group_name):
raise NotFoundException
return [self.user_repository.get(user_id) for user_id in chat.groups.get(group)]
users = [self.user_repository.get(user_id) for user_id in chat.groups.get(inbound.group_name) if user_id != inbound.user_id]
if not users:
raise NotFoundException
return users
def save(self, chat: Chat) -> None:
self.database_client.save(

View File

@ -5,6 +5,7 @@ from exception.invalidArgumentException import InvalidArgumentException
class GroupNameValidator:
MAX_GROUP_NAME_LENGTH: int = 40
FORBIDDEN_GROUP_NAMES = ['all', 'channel', 'chat', 'everyone', 'group', 'here']
@staticmethod
def validate(group: str) -> None:
@ -15,3 +16,6 @@ class GroupNameValidator:
if len(group) > GroupNameValidator.MAX_GROUP_NAME_LENGTH:
raise InvalidArgumentException(re.escape(f'Group name length can not be greater than {GroupNameValidator.MAX_GROUP_NAME_LENGTH}.'))
if group in GroupNameValidator.FORBIDDEN_GROUP_NAMES:
raise InvalidArgumentException(re.escape(f'This group name is forbidden, please try with other name.'))