0.1.0 Version : changed in to join and out to leave, folder structure and naming changes
13
CHANGELOG.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Change Log
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [0.1.0] - 06.10.2021
|
||||||
|
### Features
|
||||||
|
- `/join` command
|
||||||
|
- `/leave` command
|
||||||
|
- `/groups` command
|
||||||
|
- `/everyone` command
|
||||||
|
- `/start` command
|
||||||
|
- `/silent` command
|
||||||
|
- possibility to have multiple contexts for one chat
|
||||||
|
- docker setup & docker commands
|
106
README.md
@ -1,121 +1,117 @@
|
|||||||
# <p align="center"> [everyone-mention-telegram-bot](http://t.me/everyone_mention_bot)
|
# <p align="center"> [everyone-mention-telegram-bot](http://t.me/everyone_mention_bot)
|
||||||
<p align="center"> <img src="docs/logo.png" width="150"/>
|
<p align="center"> <img src="docs/logo.png" width="150"/>
|
||||||
<p align="center"> simple, but useful telegram bot to gather all of group members attention!
|
|
||||||
<!-- Icon made by https://www.freepik.com from https://www.flaticon.com/ -->
|
<!-- Icon made by https://www.freepik.com from https://www.flaticon.com/ -->
|
||||||
|
|
||||||
# Contents
|
# Contents
|
||||||
|
|
||||||
|
* [Description](#description)
|
||||||
* [Getting started.](#getting-started)
|
* [Getting started.](#getting-started)
|
||||||
* [Requirements](#requirements)
|
* [Requirements](#requirements)
|
||||||
* [Installation](#installation)
|
* [Installation](#installation)
|
||||||
* [Logs](#logs)
|
* [Logs](#logs)
|
||||||
* [Env files](#env-files)
|
* [Env files](#env-files)
|
||||||
* [Commands](#commands)
|
* [Commands](#commands)
|
||||||
* [`/in`](#in)
|
* [`/join`](#join)
|
||||||
* [`/out`](#out)
|
* [`/leave`](#leave)
|
||||||
* [`/everyone`](#everyone)
|
* [`/everyone`](#everyone)
|
||||||
* [`/groups`](#groups)
|
* [`/groups`](#groups)
|
||||||
* [`/silent`](#silent)
|
* [`/silent`](#silent)
|
||||||
|
* [`/start`](#start)
|
||||||
|
## Description
|
||||||
|
Everyone Mention Bot is simple, but useful telegram bot to gather group members attention.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
- `docker-compose` in version `1.25.0`
|
- `docker-compose` in version `1.25.0`
|
||||||
- `docker` in version `20.10.7`
|
- `docker` in version `20.10.7`
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
- copy the repository
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/miloszowi/everyone-mention-telegram-bot.git
|
git clone https://github.com/miloszowi/everyone-mention-telegram-bot.git
|
||||||
```
|
```
|
||||||
after that, you need to copy env files and fulfill it with correct values
|
- copy environment files and fulfill empty values
|
||||||
```bash
|
```bash
|
||||||
cp .env.local .env
|
cp .env.local .env
|
||||||
cp docker/config/app.dist.env docker/config/app.env
|
cp docker/config/app.dist.env docker/config/app.env
|
||||||
cp docker/config/database.dist.env docker/config/app.env
|
cp docker/config/database.dist.env docker/config/app.env
|
||||||
```
|
```
|
||||||
and finally, you can run the bot by launching docker containers
|
- start the project (`-d` flag will run containers in detached mode)
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
(`-d` flag will run containers in detached mode)
|
|
||||||
### Logs
|
### Logs
|
||||||
You can use
|
|
||||||
```bash
|
```bash
|
||||||
docker/logs <container>
|
docker/logs <container>
|
||||||
```
|
```
|
||||||
to check container logs
|
|
||||||
### Env files
|
### Env files
|
||||||
`.env`
|
- `.env`
|
||||||
- `MONGODB_INTERNAL_PORT` - Mongodb internal port (should be the same as declared in `app.env`)
|
- `MONGODB_INTERNAL_PORT` - Mongodb internal port (should be the same as declared in `app.env`)
|
||||||
- `APP_INTERNAL_PORT` - App internal port (should be the same as declared in `app.env`)
|
- `APP_INTERNAL_PORT` - App internal port (should be the same as declared in `app.env`)
|
||||||
- `APP_EXPOSED_PORT` - App exposed port (if you are not using any reverse proxy it should be also the same as declared in `app.env`)
|
- `APP_EXPOSED_PORT` - App exposed port (if you are not using any reverse proxy it should be also the same as declared in `app.env`)
|
||||||
|
|
||||||
|
|
||||||
`app.env`
|
- `app.env`
|
||||||
- `BOT_TOKEN` - your telegram bot token from [BotFather](https://telegram.me/BotFather)
|
- `BOT_TOKEN` - your telegram bot token from [BotFather](https://telegram.me/BotFather)
|
||||||
- `WEBHOOK_URL` - url for telegram webhooks (withour the bot token)
|
- `WEBHOOK_URL` - url for telegram webhooks (withour the bot token)
|
||||||
- `PORT` - port used for initializing webhook & app
|
- `PORT` - port used for initializing webhook & app
|
||||||
- `MONGODB_DATABASE` - MongoDB database name
|
- `MONGODB_DATABASE` - MongoDB database name
|
||||||
- `MONGODB_USERNAME` - MongoDB username
|
- `MONGODB_USERNAME` - MongoDB username
|
||||||
- `MONGODB_PASSWORD` - MongoDB password
|
- `MONGODB_PASSWORD` - MongoDB password
|
||||||
- `MONGODB_HOSTNAME` - MongoDB host (default `database` - container name)
|
- `MONGODB_HOSTNAME` - MongoDB host (default `database` - container name)
|
||||||
- `MONGODB_PORT` - MongoDB port (default `27017` - given in docker-compose configuration)
|
- `MONGODB_PORT` - MongoDB port (default `27017` - given in docker-compose configuration)
|
||||||
|
|
||||||
`database.env`
|
- `database.env`
|
||||||
- `MONGO_INITDB_ROOT_USERNAME` - conf from `app.env`
|
- `MONGO_INITDB_ROOT_USERNAME` - conf from `app.env`
|
||||||
- `MONGO_INITDB_ROOT_PASSWORD` - conf from `app.env`
|
- `MONGO_INITDB_ROOT_PASSWORD` - conf from `app.env`
|
||||||
- `MONGO_INITDB_DATABASE` - conf from `app.env`
|
- `MONGO_INITDB_DATABASE` - conf from `app.env`
|
||||||
- `MONGODB_DATA_DIR` - directory to store MongoDB documents (inside a container)
|
- `MONGODB_DATA_DIR` - directory to store MongoDB documents (inside a container)
|
||||||
- `MONDODB_LOG_DIR` - path to logs storage
|
- `MONDODB_LOG_DIR` - path to logs storage
|
||||||
## Commands
|
## Commands
|
||||||
### `/in`
|
### `/join`
|
||||||
```
|
```
|
||||||
/in <group_name>
|
/join <group_name>
|
||||||
```
|
```
|
||||||
(blank `group_name` will assign you to `default` group)
|
Joins the group (and create if it did not exist before) given in message (`default` if not given)
|
||||||
|
|
||||||
Will sign you in for everyone-mentions.
|

|
||||||
|
|
||||||

|
### `/leave`
|
||||||
|
|
||||||
If you have already opted-in before, alternative reply will be displayed.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### `/out`
|
|
||||||
```
|
```
|
||||||
/out <group_name>
|
/leave <group_name>
|
||||||
```
|
```
|
||||||
|
|
||||||
Will sign you off for everyone-mentions.
|
Leaves the group given in message (`default` if not given)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you haven't opted-in before, alternative reply will be displayed.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### `/everyone`
|
### `/everyone`
|
||||||
```
|
```
|
||||||
/everyone <group_id>
|
/everyone <group_id>
|
||||||
```
|
```
|
||||||
Will mention everyone that opted-in for everyone-mentions separated by spaces.
|
Will mention every member of given group (`default` if not given).
|
||||||
|
|
||||||
If user does not have nickname, it will first try to assign his firstname, then random firstname from `names` python library
|
If user does not have nickname, it will first try to assign his firstname, then random firstname from `names` python library
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If there are no users that opted-in for mentioning, alternative reply will be displayed.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### `/groups`
|
### `/groups`
|
||||||
Will display available groups for this chat as well with members count that opted-in for specific group
|
Will display available groups for this chat as well with members count.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### `/silent`
|
### `/silent`
|
||||||
```
|
```
|
||||||
/silent <group_name>
|
/silent <group_name>
|
||||||
```
|
```
|
||||||
|
|
||||||
Will display all users that opted-in but without notyfing them.
|
Will display all every member of given group (`default` if not given) but without notyfing them.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### `/start`
|
||||||
|
Start & Help message
|
||||||
|
|
||||||
|

|
||||||
|
Before Width: | Height: | Size: 28 KiB |
BIN
docs/everyone.png
Normal file
After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 8.7 KiB |
BIN
docs/groups.png
Normal file
After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 9.3 KiB |
BIN
docs/join.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
docs/leave.png
Normal file
After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 9.7 KiB |
BIN
docs/silent.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
docs/start.png
Normal file
After Width: | Height: | Size: 62 KiB |
@ -5,9 +5,9 @@ from telegram.ext.dispatcher import Dispatcher
|
|||||||
|
|
||||||
from logger import Logger
|
from logger import Logger
|
||||||
from config.credentials import BOT_TOKEN, PORT, WEBHOOK_URL
|
from config.credentials import BOT_TOKEN, PORT, WEBHOOK_URL
|
||||||
from handler import (groupsHandler, inHandler, mentionHandler, outHandler,
|
from bot.handler import (groupsHandler, joinHandler, mentionHandler, leaveHandler,
|
||||||
silentMentionHandler, startHandler)
|
silentMentionHandler, startHandler)
|
||||||
from handler.abstractHandler import AbstractHandler
|
from bot.handler.abstractHandler import AbstractHandler
|
||||||
|
|
||||||
|
|
||||||
class App:
|
class App:
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
from bot.message.messageData import MessageData
|
||||||
from logger import Logger
|
from logger import Logger
|
||||||
from telegram.ext.callbackcontext import CallbackContext
|
from telegram.ext.callbackcontext import CallbackContext
|
||||||
from telegram.ext.handler import Handler
|
from telegram.ext.handler import Handler
|
||||||
from telegram.update import Update
|
from telegram.update import Update
|
||||||
|
from telegram.utils.helpers import mention_markdown
|
||||||
from handler.vo.updateData import UpdateData
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractHandler:
|
class AbstractHandler:
|
||||||
@ -16,10 +16,13 @@ class AbstractHandler:
|
|||||||
def handle(self, update: Update, context: CallbackContext) -> None: raise Exception('handle method is not implemented')
|
def handle(self, update: Update, context: CallbackContext) -> None: raise Exception('handle method is not implemented')
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def log_action(self, update_data: UpdateData) -> None: raise Exception('log_action method is not implemented')
|
def log_action(self, message_data: MessageData) -> None: raise Exception('log_action method is not implemented')
|
||||||
|
|
||||||
def get_update_data(self, update: Update, context: CallbackContext) -> UpdateData:
|
def interpolate_reply(self, reply: str, message_data: MessageData):
|
||||||
return UpdateData.create_from_arguments(update, context)
|
return reply.format(
|
||||||
|
mention_markdown(message_data.user_id, message_data.username),
|
||||||
|
message_data.group_name
|
||||||
|
)
|
||||||
|
|
||||||
def reply_markdown(self, update: Update, message: str) -> None:
|
def reply_markdown(self, update: Update, message: str) -> None:
|
||||||
try:
|
try:
|
@ -1,17 +1,16 @@
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
import prettytable as pt
|
import prettytable as pt
|
||||||
|
from bot.handler.abstractHandler import AbstractHandler
|
||||||
|
from bot.message.messageData import MessageData
|
||||||
from config.contents import no_groups
|
from config.contents import no_groups
|
||||||
from entity.group import Group
|
from entity.group import Group
|
||||||
from handler.vo.updateData import UpdateData
|
|
||||||
from logger import Logger
|
from logger import Logger
|
||||||
from repository.groupRepository import GroupRepository
|
from repository.groupRepository import GroupRepository
|
||||||
from telegram.ext.callbackcontext import CallbackContext
|
from telegram.ext.callbackcontext import CallbackContext
|
||||||
from telegram.ext.commandhandler import CommandHandler
|
from telegram.ext.commandhandler import CommandHandler
|
||||||
from telegram.update import Update
|
from telegram.update import Update
|
||||||
|
|
||||||
from handler.abstractHandler import AbstractHandler
|
|
||||||
|
|
||||||
|
|
||||||
class GroupsHandler(AbstractHandler):
|
class GroupsHandler(AbstractHandler):
|
||||||
bot_handler: CommandHandler
|
bot_handler: CommandHandler
|
||||||
@ -22,21 +21,21 @@ class GroupsHandler(AbstractHandler):
|
|||||||
self.group_repository = GroupRepository()
|
self.group_repository = GroupRepository()
|
||||||
|
|
||||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||||
update_data = UpdateData.create_from_arguments(update, context, False)
|
message_data = MessageData.create_from_arguments(update, context, False)
|
||||||
|
|
||||||
groups = self.group_repository.get_by_chat_id(update_data.chat_id)
|
groups = self.group_repository.get_by_chat_id(message_data.chat_id)
|
||||||
|
|
||||||
if groups:
|
if groups:
|
||||||
self.reply_html(update, self.build_groups_message(groups))
|
self.reply_html(update, self.build_groups_message(groups))
|
||||||
return self.log_action(update_data)
|
return self.log_action(message_data)
|
||||||
|
|
||||||
self.reply_markdown(update, no_groups)
|
self.reply_markdown(update, no_groups)
|
||||||
|
|
||||||
def get_bot_handler(self) -> CommandHandler:
|
def get_bot_handler(self) -> CommandHandler:
|
||||||
return self.bot_handler
|
return self.bot_handler
|
||||||
|
|
||||||
def log_action(self, update_data: UpdateData) -> None:
|
def log_action(self, message_data: MessageData) -> None:
|
||||||
Logger.info(f'User {update_data.username} called /groups for {update_data.chat_id}')
|
Logger.info(f'User {message_data.username} called /groups for {message_data.chat_id}')
|
||||||
|
|
||||||
def build_groups_message(self, groups: Iterable[Group]) -> str:
|
def build_groups_message(self, groups: Iterable[Group]) -> str:
|
||||||
resultTable = pt.PrettyTable(['Name', 'Members'])
|
resultTable = pt.PrettyTable(['Name', 'Members'])
|
46
src/bot/handler/joinHandler.py
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
from telegram.utils.helpers import mention_markdown
|
||||||
|
from bot.handler.abstractHandler import AbstractHandler
|
||||||
|
from bot.message.messageData import MessageData
|
||||||
|
from config.contents import joined, not_joined
|
||||||
|
from exception.invalidArgumentException import InvalidArgumentException
|
||||||
|
from exception.notFoundException import NotFoundException
|
||||||
|
from logger import Logger
|
||||||
|
from repository.userRepository import UserRepository
|
||||||
|
from telegram.ext.callbackcontext import CallbackContext
|
||||||
|
from telegram.ext.commandhandler import CommandHandler
|
||||||
|
from telegram.update import Update
|
||||||
|
|
||||||
|
|
||||||
|
class JoinHandler(AbstractHandler):
|
||||||
|
bot_handler: CommandHandler
|
||||||
|
user_repository: UserRepository
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.bot_handler = CommandHandler('join', self.handle)
|
||||||
|
self.user_repository = UserRepository()
|
||||||
|
|
||||||
|
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
try:
|
||||||
|
message_data = MessageData.create_from_arguments(update, context)
|
||||||
|
except InvalidArgumentException as e:
|
||||||
|
return self.reply_markdown(update, str(e))
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = self.user_repository.get_by_id(message_data.user_id)
|
||||||
|
|
||||||
|
if user.is_in_chat(message_data.chat_id):
|
||||||
|
return self.reply_markdown(update, self.interpolate_reply(not_joined, message_data))
|
||||||
|
|
||||||
|
user.add_to_chat(message_data.chat_id)
|
||||||
|
self.user_repository.save(user)
|
||||||
|
except NotFoundException:
|
||||||
|
self.user_repository.save_by_message_data(message_data)
|
||||||
|
|
||||||
|
self.reply_markdown(update, self.interpolate_reply(joined, message_data))
|
||||||
|
self.log_action(message_data)
|
||||||
|
|
||||||
|
def get_bot_handler(self) -> CommandHandler:
|
||||||
|
return self.bot_handler
|
||||||
|
|
||||||
|
def log_action(self, message_data: MessageData) -> None:
|
||||||
|
Logger.info(f'User {message_data.username} joined {message_data.chat_id}')
|
@ -1,46 +1,45 @@
|
|||||||
from config.contents import opted_off, opted_off_failed
|
from bot.handler.abstractHandler import AbstractHandler
|
||||||
|
from bot.message.messageData import MessageData
|
||||||
|
from config.contents import left, not_left
|
||||||
from exception.invalidArgumentException import InvalidArgumentException
|
from exception.invalidArgumentException import InvalidArgumentException
|
||||||
from exception.notFoundException import NotFoundException
|
from exception.notFoundException import NotFoundException
|
||||||
from handler.vo.updateData import UpdateData
|
|
||||||
from logger import Logger
|
from logger import Logger
|
||||||
from repository.userRepository import UserRepository
|
from repository.userRepository import UserRepository
|
||||||
from telegram.ext.callbackcontext import CallbackContext
|
from telegram.ext.callbackcontext import CallbackContext
|
||||||
from telegram.ext.commandhandler import CommandHandler
|
from telegram.ext.commandhandler import CommandHandler
|
||||||
from telegram.update import Update
|
from telegram.update import Update
|
||||||
|
|
||||||
from handler.abstractHandler import AbstractHandler
|
|
||||||
|
|
||||||
|
class LeaveHandler(AbstractHandler):
|
||||||
class OutHandler(AbstractHandler):
|
|
||||||
bot_handler: CommandHandler
|
bot_handler: CommandHandler
|
||||||
user_repository: UserRepository
|
user_repository: UserRepository
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.bot_handler = CommandHandler('out', self.handle)
|
self.bot_handler = CommandHandler('leave', self.handle)
|
||||||
self.user_repository = UserRepository()
|
self.user_repository = UserRepository()
|
||||||
|
|
||||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||||
try:
|
try:
|
||||||
update_data = self.get_update_data(update, context)
|
message_data = MessageData.create_from_arguments(update, context)
|
||||||
except InvalidArgumentException as e:
|
except InvalidArgumentException as e:
|
||||||
return self.reply_markdown(update, str(e))
|
return self.reply_markdown(update, str(e))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user = self.user_repository.get_by_id(update_data.user_id)
|
user = self.user_repository.get_by_id(message_data.user_id)
|
||||||
|
|
||||||
if not user.is_in_chat(update_data.chat_id):
|
if not user.is_in_chat(message_data.chat_id):
|
||||||
raise NotFoundException()
|
raise NotFoundException()
|
||||||
except NotFoundException:
|
except NotFoundException:
|
||||||
return self.reply_markdown(update, opted_off_failed)
|
return self.reply_markdown(update, self.interpolate_reply(not_left, message_data))
|
||||||
|
|
||||||
user.remove_from_chat(update_data.chat_id)
|
user.remove_from_chat(message_data.chat_id)
|
||||||
self.user_repository.save(user)
|
self.user_repository.save(user)
|
||||||
|
|
||||||
self.reply_markdown(update, opted_off)
|
self.reply_markdown(update, self.interpolate_reply(left, message_data))
|
||||||
self.log_action(update_data)
|
self.log_action(message_data)
|
||||||
|
|
||||||
def get_bot_handler(self) -> CommandHandler:
|
def get_bot_handler(self) -> CommandHandler:
|
||||||
return self.bot_handler
|
return self.bot_handler
|
||||||
|
|
||||||
def log_action(self, update_data: UpdateData) -> None:
|
def log_action(self, message_data: MessageData) -> None:
|
||||||
Logger.info(f'User {update_data.username} left {update_data.chat_id}')
|
Logger.info(f'User {message_data.username} left {message_data.chat_id}')
|
@ -1,17 +1,18 @@
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
|
from telegram.utils.helpers import mention_markdown
|
||||||
|
|
||||||
|
from bot.handler.abstractHandler import AbstractHandler
|
||||||
|
from bot.message.messageData import MessageData
|
||||||
from config.contents import mention_failed
|
from config.contents import mention_failed
|
||||||
from entity.user import User
|
from entity.user import User
|
||||||
from exception.invalidArgumentException import InvalidArgumentException
|
from exception.invalidArgumentException import InvalidArgumentException
|
||||||
from handler.vo.updateData import UpdateData
|
|
||||||
from logger import Logger
|
from logger import Logger
|
||||||
from repository.userRepository import UserRepository
|
from repository.userRepository import UserRepository
|
||||||
from telegram.ext.callbackcontext import CallbackContext
|
from telegram.ext.callbackcontext import CallbackContext
|
||||||
from telegram.ext.commandhandler import CommandHandler
|
from telegram.ext.commandhandler import CommandHandler
|
||||||
from telegram.update import Update
|
from telegram.update import Update
|
||||||
|
|
||||||
from handler.abstractHandler import AbstractHandler
|
|
||||||
|
|
||||||
|
|
||||||
class MentionHandler(AbstractHandler):
|
class MentionHandler(AbstractHandler):
|
||||||
bot_handler: CommandHandler
|
bot_handler: CommandHandler
|
||||||
@ -23,28 +24,24 @@ class MentionHandler(AbstractHandler):
|
|||||||
|
|
||||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||||
try:
|
try:
|
||||||
update_data = self.get_update_data(update, context)
|
message_data = MessageData.create_from_arguments(update, context)
|
||||||
except InvalidArgumentException as e:
|
except InvalidArgumentException as e:
|
||||||
return self.reply_markdown(update, str(e))
|
return self.reply_markdown(update, str(e))
|
||||||
|
|
||||||
users = self.user_repository.get_all_for_chat(update_data.chat_id)
|
users = self.user_repository.get_all_for_chat(message_data.chat_id)
|
||||||
|
|
||||||
if users:
|
if users:
|
||||||
self.reply_markdown(update, self.build_mention_message(users))
|
self.reply_markdown(update, self.build_mention_message(users))
|
||||||
return self.log_action(update_data)
|
return self.log_action(message_data)
|
||||||
|
|
||||||
self.reply_markdown(update, mention_failed)
|
self.reply_markdown(update, mention_failed)
|
||||||
|
|
||||||
def get_bot_handler(self) -> CommandHandler:
|
def get_bot_handler(self) -> CommandHandler:
|
||||||
return self.bot_handler
|
return self.bot_handler
|
||||||
|
|
||||||
def log_action(self, update_data: UpdateData) -> None:
|
def log_action(self, message_data: MessageData) -> None:
|
||||||
Logger.info(f'User {update_data.username} called /everyone for {update_data.chat_id}')
|
Logger.info(f'User {message_data.username} called /everyone for {message_data.chat_id}')
|
||||||
|
|
||||||
def build_mention_message(self, users: Iterable[User]) -> str:
|
def build_mention_message(self, users: Iterable[User]) -> str:
|
||||||
result = ''
|
return ' '.join([mention_markdown(user.user_id, user.username) for user in users])
|
||||||
|
|
||||||
for user in users:
|
|
||||||
result += f'*[{user.username}](tg://user?id={user.user_id})* '
|
|
||||||
|
|
||||||
return result
|
|
21
src/bot/handler/silentMentionHandler.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
from entity.user import User
|
||||||
|
from logger import Logger
|
||||||
|
from telegram.ext.commandhandler import CommandHandler
|
||||||
|
|
||||||
|
from bot.handler.abstractHandler import AbstractHandler
|
||||||
|
from bot.handler.mentionHandler import MentionHandler
|
||||||
|
from bot.message.messageData import MessageData
|
||||||
|
|
||||||
|
|
||||||
|
class MentionHandler(MentionHandler, AbstractHandler):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.bot_handler = CommandHandler('silent', self.handle)
|
||||||
|
|
||||||
|
def build_mention_message(self, users: Iterable[User]) -> str:
|
||||||
|
return ' '.join([user.username for user in users])
|
||||||
|
|
||||||
|
def log_action(self, message_data: MessageData) -> None:
|
||||||
|
Logger.info(f'User {message_data.username} called /silent for {message_data.chat_id}')
|
@ -4,8 +4,8 @@ from telegram.ext.callbackcontext import CallbackContext
|
|||||||
from telegram.ext.commandhandler import CommandHandler
|
from telegram.ext.commandhandler import CommandHandler
|
||||||
from telegram.update import Update
|
from telegram.update import Update
|
||||||
|
|
||||||
from handler.abstractHandler import AbstractHandler
|
from bot.handler.abstractHandler import AbstractHandler
|
||||||
from handler.vo.updateData import UpdateData
|
from bot.message.messageData import MessageData
|
||||||
|
|
||||||
|
|
||||||
class StartHandler(AbstractHandler):
|
class StartHandler(AbstractHandler):
|
||||||
@ -16,10 +16,10 @@ class StartHandler(AbstractHandler):
|
|||||||
|
|
||||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
def handle(self, update: Update, context: CallbackContext) -> None:
|
||||||
self.reply_markdown(update, start_text)
|
self.reply_markdown(update, start_text)
|
||||||
self.log_action(UpdateData.create_from_arguments(update, context))
|
self.log_action(MessageData.create_from_arguments(update, context))
|
||||||
|
|
||||||
def get_bot_handler(self) -> CommandHandler:
|
def get_bot_handler(self) -> CommandHandler:
|
||||||
return self.bot_handler
|
return self.bot_handler
|
||||||
|
|
||||||
def log_action(self, update_data: UpdateData) -> None:
|
def log_action(self, message_data: MessageData) -> None:
|
||||||
Logger.info(f'User {update_data.username} called /start for {update_data.chat_id}')
|
Logger.info(f'User {message_data.username} called /start for {message_data.chat_id}')
|
@ -1,26 +1,27 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
import re
|
import re
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import names
|
import names
|
||||||
|
from entity.group import Group
|
||||||
|
from exception.invalidArgumentException import InvalidArgumentException
|
||||||
from telegram.ext.callbackcontext import CallbackContext
|
from telegram.ext.callbackcontext import CallbackContext
|
||||||
from telegram.update import Update
|
from telegram.update import Update
|
||||||
from entity.group import Group
|
|
||||||
|
|
||||||
from exception.invalidArgumentException import InvalidArgumentException
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class UpdateData():
|
class MessageData():
|
||||||
user_id: str
|
user_id: str
|
||||||
chat_id: str
|
chat_id: str
|
||||||
|
group_name: str
|
||||||
username: str
|
username: str
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_from_arguments(update: Update, context: CallbackContext, include_group: bool = True) -> UpdateData:
|
def create_from_arguments(update: Update, context: CallbackContext, include_group: bool = True) -> MessageData:
|
||||||
chat_id = str(update.effective_chat.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 include_group:
|
||||||
group_name = str(context.args[0]).lower()
|
group_name = str(context.args[0]).lower()
|
||||||
if not re.match(r"^[A-Za-z]+$", group_name):
|
if not re.match(r"^[A-Za-z]+$", group_name):
|
||||||
@ -41,4 +42,4 @@ class UpdateData():
|
|||||||
if not username:
|
if not username:
|
||||||
username = names.get_first_name()
|
username = names.get_first_name()
|
||||||
|
|
||||||
return UpdateData(user_id, chat_id, username)
|
return MessageData(user_id, chat_id, group_name, username)
|
@ -1,28 +1,25 @@
|
|||||||
import re
|
joined = '{} joined group `{}`'
|
||||||
|
not_joined = '{} is already in group `{}`'
|
||||||
# These are MarkdownV2 python-telegram-bot specific
|
left = '{} left group `{}`'
|
||||||
opted_in = re.escape('You have opted-in for everyone-mentions.')
|
not_left = '{} did not join group `{}` before'
|
||||||
opted_in_failed = re.escape('You already opted-in for everyone-mentions.')
|
mention_failed = 'There are no users to mention'
|
||||||
opted_off = re.escape('You have opted-off for everyone-mentions.')
|
no_groups = 'There are no groups for this chat'
|
||||||
opted_off_failed = re.escape('You need to opt-in first before processing this command.')
|
|
||||||
mention_failed = re.escape('There are no users to mention.')
|
|
||||||
no_groups = re.escape('There are no groups for this chat.')
|
|
||||||
|
|
||||||
|
|
||||||
start_text = re.escape("""
|
start_text = """
|
||||||
Hello there.
|
Hello there
|
||||||
I am `@everyone_mention_bot`.
|
I am @everyone\_mention\_bot
|
||||||
I am here to help you with mass notifies.
|
I am here to help you with mass notifies
|
||||||
|
|
||||||
Please take a look at available commands.
|
Please take a look at available commands
|
||||||
Parameter `<group-name>` is not required, if not given, I will assign you to `default` group.
|
`<group-name>` is not required, if not given, it is set to `default`
|
||||||
|
|
||||||
To opt-in for everyone-mentions use:
|
To join group:
|
||||||
`/in <group-name>`
|
`/join <group-name>`
|
||||||
for example: `/in gaming`
|
for example: `/join games`
|
||||||
|
|
||||||
To opt-off for everyone mentions use:
|
To leave group:
|
||||||
`/out <group-name>`
|
`/leave <group-name>`
|
||||||
|
|
||||||
To gather everyone attention use:
|
To gather everyone attention use:
|
||||||
`/everyone <group-name>`
|
`/everyone <group-name>`
|
||||||
@ -30,8 +27,6 @@ To gather everyone attention use:
|
|||||||
To see all available groups use:
|
To see all available groups use:
|
||||||
`/groups`
|
`/groups`
|
||||||
|
|
||||||
To display all users that opted-in for everyone-mentions use:
|
To display all members in a group:
|
||||||
`/silent <group-name>`
|
`/silent <group-name>`
|
||||||
|
"""
|
||||||
In case questions regarding my usage please reach out to @miloszowi
|
|
||||||
""")
|
|
@ -1,46 +0,0 @@
|
|||||||
from handler.vo.updateData import UpdateData
|
|
||||||
from logger import Logger
|
|
||||||
from config.contents import opted_in, opted_in_failed
|
|
||||||
from exception.invalidArgumentException import InvalidArgumentException
|
|
||||||
from exception.notFoundException import NotFoundException
|
|
||||||
from repository.userRepository import UserRepository
|
|
||||||
from telegram.ext.callbackcontext import CallbackContext
|
|
||||||
from telegram.ext.commandhandler import CommandHandler
|
|
||||||
from telegram.update import Update
|
|
||||||
|
|
||||||
from handler.abstractHandler import AbstractHandler
|
|
||||||
|
|
||||||
|
|
||||||
class InHandler(AbstractHandler):
|
|
||||||
bot_handler: CommandHandler
|
|
||||||
user_repository: UserRepository
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.bot_handler = CommandHandler('in', self.handle)
|
|
||||||
self.user_repository = UserRepository()
|
|
||||||
|
|
||||||
def handle(self, update: Update, context: CallbackContext) -> None:
|
|
||||||
try:
|
|
||||||
update_data = self.get_update_data(update, context)
|
|
||||||
except InvalidArgumentException as e:
|
|
||||||
return self.reply_markdown(update, str(e))
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = self.user_repository.get_by_id(update_data.user_id)
|
|
||||||
|
|
||||||
if user.is_in_chat(update_data.chat_id):
|
|
||||||
return self.reply_markdown(update, opted_in_failed)
|
|
||||||
|
|
||||||
user.add_to_chat(update_data.chat_id)
|
|
||||||
self.user_repository.save(user)
|
|
||||||
except NotFoundException:
|
|
||||||
self.user_repository.save_by_update_data(update_data)
|
|
||||||
|
|
||||||
self.reply_markdown(update, opted_in)
|
|
||||||
self.log_action(update_data)
|
|
||||||
|
|
||||||
def get_bot_handler(self) -> CommandHandler:
|
|
||||||
return self.bot_handler
|
|
||||||
|
|
||||||
def log_action(self, update_data: UpdateData) -> None:
|
|
||||||
Logger.info(f'User {update_data.username} joined {update_data.chat_id}')
|
|
@ -1,26 +0,0 @@
|
|||||||
from typing import Iterable
|
|
||||||
|
|
||||||
from entity.user import User
|
|
||||||
from logger import Logger
|
|
||||||
from telegram.ext.commandhandler import CommandHandler
|
|
||||||
|
|
||||||
from handler.abstractHandler import AbstractHandler
|
|
||||||
from handler.mentionHandler import MentionHandler
|
|
||||||
from handler.vo.updateData import UpdateData
|
|
||||||
|
|
||||||
|
|
||||||
class MentionHandler(MentionHandler, AbstractHandler):
|
|
||||||
def __init__(self) -> None:
|
|
||||||
super().__init__()
|
|
||||||
self.bot_handler = CommandHandler('silent', self.handle)
|
|
||||||
|
|
||||||
def build_mention_message(self, users: Iterable[User]) -> str:
|
|
||||||
result = ''
|
|
||||||
|
|
||||||
for user in users:
|
|
||||||
result += f'*{user.username}\({user.user_id}\)*\n'
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def log_action(self, update_data: UpdateData) -> None:
|
|
||||||
Logger.info(f'User {update_data.username} called /silent for {update_data.chat_id}')
|
|
@ -1,9 +1,9 @@
|
|||||||
from typing import Iterable, Optional
|
from typing import Iterable, Optional
|
||||||
|
|
||||||
|
from bot.message.messageData import MessageData
|
||||||
from database.client import Client
|
from database.client import Client
|
||||||
from entity.user import User
|
from entity.user import User
|
||||||
from exception.notFoundException import NotFoundException
|
from exception.notFoundException import NotFoundException
|
||||||
from handler.vo.updateData import UpdateData
|
|
||||||
|
|
||||||
|
|
||||||
class UserRepository():
|
class UserRepository():
|
||||||
@ -36,7 +36,7 @@ class UserRepository():
|
|||||||
user.to_mongo_document()
|
user.to_mongo_document()
|
||||||
)
|
)
|
||||||
|
|
||||||
def save_by_update_data(self, data: UpdateData) -> None:
|
def save_by_message_data(self, data: MessageData) -> None:
|
||||||
self.client.insert_one(
|
self.client.insert_one(
|
||||||
User.collection,
|
User.collection,
|
||||||
{
|
{
|
||||||
|