diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8046c8a..24d47a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,14 @@
# 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`
+### Deleted
+- mentioning user that performed `/everyone` or dynamic mention in bot response
## [0.2.0] - 26.10.2021
### Added
- Inline Mode for `join`, `leave` & `everyone`
diff --git a/README.md b/README.md
index 2204f60..8fcbbf1 100755
--- a/README.md
+++ b/README.md
@@ -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

-## 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.

diff --git a/src/bot/handler/__init__.py b/src/bot/handler/__init__.py
index 3a55bbb..b5eceda 100644
--- a/src/bot/handler/__init__.py
+++ b/src/bot/handler/__init__.py
@@ -1,5 +1,5 @@
__all__ = [
'abstractHandler', 'everyoneHandler', 'groupsHandler',
'inlineQueryHandler', 'joinHandler', 'leaveHandler',
- 'startHandler'
+ 'startHandler', 'dynamicMentionHandler'
]
diff --git a/src/bot/handler/abstractHandler.py b/src/bot/handler/abstractHandler.py
index d686924..308ad58 100755
--- a/src/bot/handler/abstractHandler.py
+++ b/src/bot/handler/abstractHandler.py
@@ -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)
diff --git a/src/bot/handler/dynamicMentionHandler.py b/src/bot/handler/dynamicMentionHandler.py
new file mode 100644
index 0000000..d5dfe6e
--- /dev/null
+++ b/src/bot/handler/dynamicMentionHandler.py
@@ -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))
diff --git a/src/bot/handler/everyoneHandler.py b/src/bot/handler/everyoneHandler.py
index a31feac..047ae65 100755
--- a/src/bot/handler/everyoneHandler.py
+++ b/src/bot/handler/everyoneHandler.py
@@ -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:
diff --git a/src/bot/message/inboundMessage.py b/src/bot/message/inboundMessage.py
index c0f5505..685dfe8 100644
--- a/src/bot/message/inboundMessage.py
+++ b/src/bot/message/inboundMessage.py
@@ -3,9 +3,11 @@ from __future__ import annotations
from dataclasses import dataclass
import names
+import re
from telegram.ext.callbackcontext import CallbackContext
from telegram.update import Update
+from exception.invalidActionException import InvalidActionException
from validator.accessValidator import AccessValidator
from validator.groupNameValidator import GroupNameValidator
@@ -24,14 +26,25 @@ class InboundMessage:
user_id = str(update.effective_user.id)
AccessValidator.validate(user_id)
+ message_content = update.edited_message.text if update.edited_message else update.message.text
+ message_content = message_content.replace("\n", " ")
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 message_content:
+ searched_message_part = [part for part in message_content.split(' ') if '@' in part][0]
+ group_name = re.sub(r'\W+', '', searched_message_part).lower()
+
+ 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:
diff --git a/src/config/contents.py b/src/config/contents.py
index 952a2bc..ac1eac7 100755
--- a/src/config/contents.py
+++ b/src/config/contents.py
@@ -10,13 +10,16 @@ no_groups = 'There are no groups for this chat'
start_text = """
Hello!
@everyone_mention_bot here.
-
-Description:
-I do not have access to your messages!
I am here to help you with multiple user mentions.
Usage:
-Users that joined the group by /join
command, can be mentioned after calling /everyone
command.
+Users that joined the group by /join
command,
+can be mentioned after typing one of those in your message:
+@all
, @channel
, @chat
, @everyone
, @group
or @here
.
+
+If you did create a group named gaming
, simply use @gaming
to call users from that group.
+
+You can also use /everyone
command.
Commands:
/join {group-name}@@ -36,6 +39,4 @@ Show start & help text Please note
{group-name}
is not required, default
if not given.
-
-If your chat does have multiple bots I might not receive your command according to policy of bots with privacy mode enabled - use Inline Mode
to avoid this.
"""
diff --git a/src/repository/chatRepository.py b/src/repository/chatRepository.py
index c8786f3..83a7a70 100644
--- a/src/repository/chatRepository.py
+++ b/src/repository/chatRepository.py
@@ -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(
diff --git a/src/validator/groupNameValidator.py b/src/validator/groupNameValidator.py
index c910ae6..82ec899 100644
--- a/src/validator/groupNameValidator.py
+++ b/src/validator/groupNameValidator.py
@@ -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.'))