mirror of
https://github.com/miloszowi/everyone-mention-telegram-bot.git
synced 2025-05-21 17:54:09 +00:00
Compare commits
No commits in common. "main" and "0.2.0" have entirely different histories.
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,20 +1,6 @@
|
|||||||
# 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.2] - 28.02.2023
|
|
||||||
### Changed
|
|
||||||
- fixed bug with missing '+' in replier
|
|
||||||
## [0.3.1] - 28.02.2023
|
|
||||||
### Changed
|
|
||||||
- fixed markdown replier to respect restricted characters provided in the [api docs](https://core.telegram.org/bots/api#markdownv2-style)
|
|
||||||
## [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
|
## [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,8 +5,6 @@
|
|||||||
# Contents
|
# Contents
|
||||||
|
|
||||||
* [Description](#description)
|
* [Description](#description)
|
||||||
* [Usage](#usage)
|
|
||||||
* [Dynamic Mentioning](#dynamic-mentioning)
|
|
||||||
* [Commands](#commands)
|
* [Commands](#commands)
|
||||||
* [`/join`](#join)
|
* [`/join`](#join)
|
||||||
* [`/leave`](#leave)
|
* [`/leave`](#leave)
|
||||||
@ -14,7 +12,8 @@
|
|||||||
* [`/groups`](#groups)
|
* [`/groups`](#groups)
|
||||||
* [`/start`](#start)
|
* [`/start`](#start)
|
||||||
* [Example command flow](#example-command-flow)
|
* [Example command flow](#example-command-flow)
|
||||||
* [Inline Mode Usage](#inline-mode-usage)
|
* [Inline Mode](#inline-mode)
|
||||||
|
* [Usage](#usage)
|
||||||
* [Getting started.](#getting-started)
|
* [Getting started.](#getting-started)
|
||||||
* [Requirements](#requirements)
|
* [Requirements](#requirements)
|
||||||
* [Installation](#installation)
|
* [Installation](#installation)
|
||||||
@ -25,32 +24,6 @@ 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`
|
||||||
@ -112,7 +85,10 @@ Start & Help message
|
|||||||
### Example command flow
|
### Example command flow
|
||||||

|

|
||||||
|
|
||||||
### Inline Mode Usage
|
## 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
|
||||||
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', 'dynamicMentionHandler'
|
'startHandler'
|
||||||
]
|
]
|
||||||
|
@ -9,7 +9,6 @@ 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
|
||||||
|
|
||||||
|
|
||||||
@ -31,8 +30,6 @@ 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)
|
||||||
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
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:
|
|
||||||
if hasattr(update, 'message_reaction'):
|
|
||||||
return
|
|
||||||
|
|
||||||
users = self.chat_repository.get_users_for_group(self.inbound)
|
|
||||||
|
|
||||||
Replier.markdown(update, MessageBuilder.mention_message(users))
|
|
@ -8,21 +8,24 @@ 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)
|
users = self.chat_repository.get_users_for_group(self.inbound.chat_id, self.inbound.group_name)
|
||||||
|
|
||||||
Replier.markdown(update, MessageBuilder.mention_message(users))
|
Replier.markdown(update, MessageBuilder.mention_message(users))
|
||||||
except NotFoundException as e:
|
except NotFoundException as e:
|
||||||
|
@ -3,11 +3,9 @@ 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
|
||||||
|
|
||||||
from exception.invalidActionException import InvalidActionException
|
|
||||||
from validator.accessValidator import AccessValidator
|
from validator.accessValidator import AccessValidator
|
||||||
from validator.groupNameValidator import GroupNameValidator
|
from validator.groupNameValidator import GroupNameValidator
|
||||||
|
|
||||||
@ -26,25 +24,14 @@ class InboundMessage:
|
|||||||
user_id = str(update.effective_user.id)
|
user_id = str(update.effective_user.id)
|
||||||
AccessValidator.validate(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)
|
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 message_content and '@everyone_mention_bot' not 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
|
username = update.effective_user.username or update.effective_user.first_name
|
||||||
|
|
||||||
if not username:
|
if not username:
|
||||||
|
@ -8,30 +8,24 @@ from logger import Logger
|
|||||||
|
|
||||||
|
|
||||||
class Replier:
|
class Replier:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def interpolate(content: str, inbound_message: InboundMessage):
|
def interpolate(content: str, inbound_message: InboundMessage):
|
||||||
formatted = content.format(
|
return content.format(
|
||||||
mention_markdown(inbound_message.user_id, inbound_message.username),
|
mention_markdown(inbound_message.user_id, inbound_message.username),
|
||||||
inbound_message.group_name
|
inbound_message.group_name
|
||||||
)
|
)
|
||||||
|
|
||||||
telegramRestrictionCharacters = ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!']
|
|
||||||
|
|
||||||
for character in telegramRestrictionCharacters:
|
|
||||||
formatted.replace(character, r'\{character}')
|
|
||||||
|
|
||||||
return formatted
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def markdown(update: Update, message: str, reply_markup: Optional[InlineKeyboardMarkup] = None) -> None:
|
def markdown(update: Update, message: str, reply_markup: Optional[InlineKeyboardMarkup] = None) -> None:
|
||||||
try:
|
try:
|
||||||
update.effective_message.reply_markdown_v2(message, reply_markup=reply_markup)
|
update.effective_message.reply_markdown_v2(message, reply_markup=reply_markup)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
Logger.error("replier.markdown error: " + str(err))
|
Logger.error(str(err))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def html(update: Update, html: str, reply_markup: Optional[InlineKeyboardMarkup] = None) -> None:
|
def html(update: Update, html: str, reply_markup: Optional[InlineKeyboardMarkup] = None) -> None:
|
||||||
try:
|
try:
|
||||||
update.effective_message.reply_html(html, reply_markup=reply_markup, disable_web_page_preview=True)
|
update.effective_message.reply_html(html, reply_markup=reply_markup, disable_web_page_preview=True)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
Logger.error("replier.html error: " + str(err))
|
Logger.error(str(err))
|
||||||
|
@ -10,16 +10,13 @@ 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,
|
Users that joined the group by <code>/join</code> command, can be mentioned after calling <code>/everyone</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>
|
||||||
@ -39,4 +36,6 @@ 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,17 +39,12 @@ class ChatRepository(AbstractRepository):
|
|||||||
|
|
||||||
return Chat.from_mongo_document(chat)
|
return Chat.from_mongo_document(chat)
|
||||||
|
|
||||||
def get_users_for_group(self, inbound: InboundMessage) -> Iterable[User]:
|
def get_users_for_group(self, chat_id: str, group: str) -> Iterable[User]:
|
||||||
chat = self.get(inbound.chat_id)
|
chat = self.get(chat_id)
|
||||||
if not chat.groups.get(inbound.group_name):
|
if not chat.groups.get(group):
|
||||||
raise NotFoundException
|
raise NotFoundException
|
||||||
|
|
||||||
users = [self.user_repository.get(user_id) for user_id in chat.groups.get(inbound.group_name) if user_id != inbound.user_id]
|
return [self.user_repository.get(user_id) for user_id in chat.groups.get(group)]
|
||||||
|
|
||||||
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,7 +5,6 @@ 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:
|
||||||
@ -16,6 +15,3 @@ 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