mirror of
https://github.com/miloszowi/everyone-mention-telegram-bot.git
synced 2025-05-20 09:14:07 +00:00
Dynamic mentioning added, removed some unwanted properties, updated README
This commit is contained in:
parent
f9643f052e
commit
6790426ba2
@ -1,6 +1,12 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
All notable changes to this project will be documented in this file.
|
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
|
## [0.2.0] - 26.10.2021
|
||||||
### Added
|
### Added
|
||||||
- Inline Mode for `join`, `leave` & `everyone`
|
- Inline Mode for `join`, `leave` & `everyone`
|
||||||
|
36
README.md
36
README.md
@ -5,6 +5,8 @@
|
|||||||
# Contents
|
# Contents
|
||||||
|
|
||||||
* [Description](#description)
|
* [Description](#description)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Dynamic Mentioning](#dynamic-mentioning)
|
||||||
* [Commands](#commands)
|
* [Commands](#commands)
|
||||||
* [`/join`](#join)
|
* [`/join`](#join)
|
||||||
* [`/leave`](#leave)
|
* [`/leave`](#leave)
|
||||||
@ -12,8 +14,7 @@
|
|||||||
* [`/groups`](#groups)
|
* [`/groups`](#groups)
|
||||||
* [`/start`](#start)
|
* [`/start`](#start)
|
||||||
* [Example command flow](#example-command-flow)
|
* [Example command flow](#example-command-flow)
|
||||||
* [Inline Mode](#inline-mode)
|
* [Inline Mode Usage](#inline-mode-usage)
|
||||||
* [Usage](#usage)
|
|
||||||
* [Getting started.](#getting-started)
|
* [Getting started.](#getting-started)
|
||||||
* [Requirements](#requirements)
|
* [Requirements](#requirements)
|
||||||
* [Installation](#installation)
|
* [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.
|
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
|
## Commands
|
||||||
*Important*: `{group-name}` is not required, if not given, it will be set to `default`.
|
*Important*: `{group-name}` is not required, if not given, it will be set to `default`.
|
||||||
### `/join`
|
### `/join`
|
||||||
@ -85,10 +112,7 @@ Start & Help message
|
|||||||
### Example command flow
|
### Example command flow
|
||||||

|

|
||||||
|
|
||||||
## Inline Mode
|
### Inline Mode Usage
|
||||||
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
|
|
||||||
To use inline mode, type `@everyone_mention_bot` in telegram message input or click on the `Inline Mode` button from `/start` command.
|
To use inline mode, type `@everyone_mention_bot` in telegram message input or click on the `Inline Mode` button from `/start` command.
|
||||||
|
|
||||||

|

|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
'abstractHandler', 'everyoneHandler', 'groupsHandler',
|
'abstractHandler', 'everyoneHandler', 'groupsHandler',
|
||||||
'inlineQueryHandler', 'joinHandler', 'leaveHandler',
|
'inlineQueryHandler', 'joinHandler', 'leaveHandler',
|
||||||
'startHandler'
|
'startHandler', 'dynamicMentionHandler'
|
||||||
]
|
]
|
||||||
|
@ -9,6 +9,7 @@ from bot.message.replier import Replier
|
|||||||
from exception.actionNotAllowedException import ActionNotAllowedException
|
from exception.actionNotAllowedException import ActionNotAllowedException
|
||||||
from exception.invalidActionException import InvalidActionException
|
from exception.invalidActionException import InvalidActionException
|
||||||
from exception.invalidArgumentException import InvalidArgumentException
|
from exception.invalidArgumentException import InvalidArgumentException
|
||||||
|
from exception.notFoundException import NotFoundException
|
||||||
from logger import Logger
|
from logger import Logger
|
||||||
|
|
||||||
|
|
||||||
@ -30,6 +31,8 @@ class AbstractHandler:
|
|||||||
Logger.action(self.inbound, self.action)
|
Logger.action(self.inbound, self.action)
|
||||||
except (InvalidActionException, InvalidArgumentException, ActionNotAllowedException) as e:
|
except (InvalidActionException, InvalidArgumentException, ActionNotAllowedException) as e:
|
||||||
Replier.markdown(update, str(e))
|
Replier.markdown(update, str(e))
|
||||||
|
except NotFoundException:
|
||||||
|
pass # probably just mentioning user
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Logger.exception(e)
|
Logger.exception(e)
|
||||||
|
|
||||||
|
28
src/bot/handler/dynamicMentionHandler.py
Normal file
28
src/bot/handler/dynamicMentionHandler.py
Normal 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))
|
@ -8,24 +8,21 @@ from config.contents import mention_failed
|
|||||||
from exception.invalidActionException import InvalidActionException
|
from exception.invalidActionException import InvalidActionException
|
||||||
from exception.notFoundException import NotFoundException
|
from exception.notFoundException import NotFoundException
|
||||||
from repository.chatRepository import ChatRepository
|
from repository.chatRepository import ChatRepository
|
||||||
from repository.userRepository import UserRepository
|
|
||||||
from utils.messageBuilder import MessageBuilder
|
from utils.messageBuilder import MessageBuilder
|
||||||
|
|
||||||
|
|
||||||
class EveryoneHandler(AbstractHandler):
|
class EveryoneHandler(AbstractHandler):
|
||||||
bot_handler: CommandHandler
|
bot_handler: CommandHandler
|
||||||
chat_repository: ChatRepository
|
chat_repository: ChatRepository
|
||||||
user_repository: UserRepository
|
|
||||||
action: str = 'everyone'
|
action: str = 'everyone'
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.bot_handler = CommandHandler(self.action, self.wrap)
|
self.bot_handler = CommandHandler(self.action, self.wrap)
|
||||||
self.chat_repository = ChatRepository()
|
self.chat_repository = ChatRepository()
|
||||||
self.user_repository = UserRepository()
|
|
||||||
|
|
||||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||||
try:
|
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))
|
Replier.markdown(update, MessageBuilder.mention_message(users))
|
||||||
except NotFoundException as e:
|
except NotFoundException as e:
|
||||||
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import names
|
import names
|
||||||
|
import re
|
||||||
from telegram.ext.callbackcontext import CallbackContext
|
from telegram.ext.callbackcontext import CallbackContext
|
||||||
from telegram.update import Update
|
from telegram.update import Update
|
||||||
|
|
||||||
@ -27,11 +28,20 @@ class InboundMessage:
|
|||||||
chat_id = str(update.effective_chat.id)
|
chat_id = str(update.effective_chat.id)
|
||||||
group_name = InboundMessage.default_group
|
group_name = InboundMessage.default_group
|
||||||
|
|
||||||
|
# done upon resolving a command action
|
||||||
if context.args and context.args[0] and group_specific:
|
if context.args and context.args[0] and group_specific:
|
||||||
group_name = str(context.args[0]).lower()
|
group_name = str(context.args[0]).lower()
|
||||||
|
|
||||||
GroupNameValidator.validate(group_name)
|
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
|
username = update.effective_user.username or update.effective_user.first_name
|
||||||
|
|
||||||
if not username:
|
if not username:
|
||||||
|
@ -10,13 +10,16 @@ no_groups = 'There are no groups for this chat'
|
|||||||
start_text = """
|
start_text = """
|
||||||
Hello!
|
Hello!
|
||||||
@everyone_mention_bot here.
|
@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.
|
I am here to help you with multiple user mentions.
|
||||||
|
|
||||||
<b>Usage</b>:
|
<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>:
|
<b>Commands</b>:
|
||||||
<pre>/join {group-name}</pre>
|
<pre>/join {group-name}</pre>
|
||||||
@ -36,6 +39,4 @@ Show start & help text
|
|||||||
|
|
||||||
<b>Please note</b>
|
<b>Please note</b>
|
||||||
<code>{group-name}</code> is not required, <code>default</code> if not given.
|
<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.
|
|
||||||
"""
|
"""
|
||||||
|
@ -39,12 +39,17 @@ class ChatRepository(AbstractRepository):
|
|||||||
|
|
||||||
return Chat.from_mongo_document(chat)
|
return Chat.from_mongo_document(chat)
|
||||||
|
|
||||||
def get_users_for_group(self, chat_id: str, group: str) -> Iterable[User]:
|
def get_users_for_group(self, inbound: InboundMessage) -> Iterable[User]:
|
||||||
chat = self.get(chat_id)
|
chat = self.get(inbound.chat_id)
|
||||||
if not chat.groups.get(group):
|
if not chat.groups.get(inbound.group_name):
|
||||||
raise NotFoundException
|
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:
|
def save(self, chat: Chat) -> None:
|
||||||
self.database_client.save(
|
self.database_client.save(
|
||||||
|
@ -5,6 +5,7 @@ from exception.invalidArgumentException import InvalidArgumentException
|
|||||||
|
|
||||||
class GroupNameValidator:
|
class GroupNameValidator:
|
||||||
MAX_GROUP_NAME_LENGTH: int = 40
|
MAX_GROUP_NAME_LENGTH: int = 40
|
||||||
|
FORBIDDEN_GROUP_NAMES = ['all', 'channel', 'chat', 'everyone', 'group', 'here']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate(group: str) -> None:
|
def validate(group: str) -> None:
|
||||||
@ -15,3 +16,6 @@ class GroupNameValidator:
|
|||||||
|
|
||||||
if len(group) > GroupNameValidator.MAX_GROUP_NAME_LENGTH:
|
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}.'))
|
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.'))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user