changed mongoDb collection structure, removed group entity & repository, updated README.md, changed folder names to singular forms

This commit is contained in:
miloszowi 2021-09-25 16:49:11 +02:00
parent baa8a78cad
commit cd8e3507ca
27 changed files with 347 additions and 393 deletions

View File

@ -3,53 +3,61 @@
<p align="center"> simple, but useful telegram bot to gather all of group members attention! <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
* [Getting started.](#getting-started) * [Getting started.](#getting-started)
* [Installation](#installation)
* [Requirements](#requirements) * [Requirements](#requirements)
* [Env file](#env-file) * [Installation](#installation)
* [Logs](#logs)
* [Env files](#env-files)
* [Commands](#commands) * [Commands](#commands)
* [`/in`](#in) * [`/in`](#in)
* [`/out`](#out) * [`/out`](#out)
* [`/everyone`](#everyone) * [`/everyone`](#everyone)
### Getting started ## Getting started
#### Installation
### Requirements
- `docker-compose` in version `1.25.0`
- `docker` in version `20.10.7`
### Installation
```bash ```bash
git clone https://github.com/miloszowi/everyone-mention-telegram-bot.git git clone https://github.com/miloszowi/everyone-mention-telegram-bot.git
pip install -r requirements.txt
python entrypoint.py
``` ```
after that, you need to copy env files and fulfill it with correct values
#### Requirements
- `python` with version specified in `runtime.txt`
- `pip` with version `20.0.2`
#### Env files
First, copy env files for database and app containers
```bash ```bash
cp docker/config/app/app.dist.env docker/config/app/app.env cp docker/config/app.dist.env docker/config/app.env
cp docker/config/database/database.dist.env docker/config/app/app.env cp docker/config/database.dist.env docker/config/app.env
``` ```
and then fulfill copied `.env` files with required values and finally, you can run the bot by launching docker containers
```bash
docker-compose up -d
```
(`-d` flag will run containers in detached mode)
### Logs
You can use
```bash
docker/logs <container>
```
to check container logs
### Env files
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)
- `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 `port` - 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` - log file - `MONDODB_LOG_DIR` - path to logs storage
### Commands ## Commands
#### `/in` ### `/in`
Will sign you in for everyone-mentions. Will sign you in for everyone-mentions.
![in command example](docs/in_command.png) ![in command example](docs/in_command.png)
@ -58,7 +66,7 @@ If you have already opted-in before, alternative reply will be displayed.
![in command when someone already opted in example](docs/in_command_already_opted_in.png) ![in command when someone already opted in example](docs/in_command_already_opted_in.png)
#### `/out` ### `/out`
Will sign you off for everyone-mentions. Will sign you off for everyone-mentions.
![out command example](docs/out_command.png) ![out command example](docs/out_command.png)
@ -67,10 +75,10 @@ If you haven't opted-in before, alternative reply will be displayed.
![out command when someone did not opt in example](docs/out_command_did_not_opt_in_before.png) ![out command when someone did not opt in example](docs/out_command_did_not_opt_in_before.png)
#### `/everone` ### `/everyone`
Will mention everyone that opted-in for everyone-mentions separated by spaces. Will mention everyone that opted-in for everyone-mentions separated by spaces.
If user does not have nickname, it will assign random name from `names` python library to his ID If user does not have nickname, it will first try to assign his firstname, then random firstname from `names` python library
![everybody command example](docs/everyone_command.png) ![everybody command example](docs/everyone_command.png)

View File

@ -6,7 +6,7 @@ services:
image: mongo:4.0.8 image: mongo:4.0.8
restart: unless-stopped restart: unless-stopped
env_file: env_file:
- ./docker/config/database/database.env - ./docker/config/database.env
volumes: volumes:
- db-data:/data/db - db-data:/data/db
ports: ports:
@ -18,7 +18,7 @@ services:
build: . build: .
command: python app.py command: python app.py
env_file: env_file:
- ./docker/config/app/app.env - ./docker/config/app.env
volumes: volumes:
- ./src:/src - ./src:/src
ports: ports:

View File

@ -1,15 +1,17 @@
from config.credentials import bot_token
from config.handlers import handlers
from handlers.handlerInterface import HandlerInterface
from telegram.ext.dispatcher import Dispatcher
from telegram.ext import Updater from telegram.ext import Updater
from telegram.ext.dispatcher import Dispatcher
from config.credentials import BOT_TOKEN
from handler.abstractHandler import AbstractHandler
from handler import (inHandler, mentionHandler, outHandler)
class App: class App:
updater: Updater updater: Updater
dispatcher: Dispatcher dispatcher: Dispatcher
def __init__(self): def __init__(self):
self.updater = Updater(bot_token) self.updater = Updater(BOT_TOKEN)
def run(self) -> None: def run(self) -> None:
self.registerHandlers() self.registerHandlers()
@ -18,11 +20,10 @@ class App:
self.updater.idle() self.updater.idle()
def registerHandlers(self) -> None: def registerHandlers(self) -> None:
for handler in handlers: for handler in AbstractHandler.__subclasses__():
if not isinstance(handler, HandlerInterface): self.updater.dispatcher.add_handler(
raise Exception('Invalid list of handlers provided. Handler must implement HandlerInterface') handler().getBotHandler()
)
self.updater.dispatcher.add_handler(handler.getBotHandler())
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,8 +1,8 @@
import re import re
# These are MarkdownV2 python-telegram-bot specific # These are MarkdownV2 python-telegram-bot specific
opted_in_successfully = re.escape('You have opted-in for everyone-mentions.') opted_in = re.escape('You have opted-in for everyone-mentions.')
opted_in_failed = re.escape('You already opted-in for everyone-mentions.') opted_in_failed = re.escape('You already opted-in for everyone-mentions.')
opted_off_successfully = re.escape('You have opted-off for everyone-mentions.') opted_off = re.escape('You have opted-off for everyone-mentions.')
opted_off_failed = re.escape('You need to opt-in first before processing this command.') 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.') mention_failed = re.escape('There are no users to mention.')

View File

@ -1,9 +1,10 @@
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv() load_dotenv()
bot_token = os.environ['bot_token'] BOT_TOKEN = os.environ['BOT_TOKEN']
MONGODB_DATABASE=os.environ['MONGODB_DATABASE'] MONGODB_DATABASE=os.environ['MONGODB_DATABASE']
MONGODB_USERNAME=os.environ['MONGODB_USERNAME'] MONGODB_USERNAME=os.environ['MONGODB_USERNAME']

View File

@ -1,9 +0,0 @@
from handlers.inHandler import InHandler
from handlers.outHandler import OutHandler
from handlers.mentionHandler import MentionHandler
handlers = [
InHandler(),
OutHandler(),
MentionHandler()
]

View File

@ -1,10 +1,13 @@
from pymongo.errors import ServerSelectionTimeoutError
from config.credentials import MONGODB_USERNAME, MONGODB_PASSWORD, MONGODB_DATABASE, MONGODB_HOSTNAME, MONGODB_PORT
from pymongo import MongoClient
from pymongo.database import Database
from urllib.parse import quote_plus from urllib.parse import quote_plus
class DatabaseClient(): from config.credentials import (MONGODB_DATABASE, MONGODB_HOSTNAME,
MONGODB_PASSWORD, MONGODB_PORT,
MONGODB_USERNAME)
from pymongo import MongoClient
from pymongo.database import Database
class Client():
mongoClient: MongoClient mongoClient: MongoClient
database: Database database: Database
@ -17,14 +20,17 @@ class DatabaseClient():
self.mongoClient = MongoClient(uri) self.mongoClient = MongoClient(uri)
self.database = self.mongoClient[MONGODB_DATABASE] self.database = self.mongoClient[MONGODB_DATABASE]
def insert(self, collection: str, data: dict) -> None: def insertOne(self, collection: str, data: dict) -> None:
self.database.get_collection(collection).insert_one(data) self.database.get_collection(collection).insert_one(data)
def find(self, collection: str, query: dict) -> dict:
return self.database.get_collection(collection).find(query)
def findOne(self, collection: str, query: dict) -> dict: def findOne(self, collection: str, query: dict) -> dict:
return self.database.get_collection(collection).find_one(query) return self.database.get_collection(collection).find_one(query)
def remove(self, collection: str, data: dict) -> None: def findMany(self, collection: str, filter: dict) -> dict:
self.database.get_collection(collection).remove(data) return self.database.get_collection(collection).find(filter)
def updateOne(self, collection: str, filter: dict, data: dict) -> None:
self.database.get_collection(collection).update_one(
filter,
{ "$set" : data }
)

View File

@ -1,28 +0,0 @@
from __future__ import annotations
from typing import Optional
class Chat():
id: str
def __init__(self, id: str) -> None:
self.id = id
def getId(self) -> str:
return self.id
def toDict(self) -> dict:
return {
'_id': self.id
}
@staticmethod
def getMongoRoot() -> str:
return 'chat'
@staticmethod
def fromDocument(document: Optional[dict]) -> Optional[Chat]:
if not document:
return None
return Chat(document['_id'])

View File

@ -1,34 +0,0 @@
from __future__ import annotations
from typing import Optional
class ChatPerson():
chat_id: str
person_id: str
def __init__(self, chatId: str, personId: str) -> None:
self.chat_id = chatId
self.person_id = personId
def getChatId(self) -> str:
return self.chat_id
def getPersonId(self) -> str:
return self.person_id
def toDict(self) -> dict:
return {
'_id': f'{self.chat_id}-{self.person_id}',
'chat_id': self.chat_id,
'person_id': self.person_id
}
@staticmethod
def getMongoRoot() -> str:
return 'chat_person'
@staticmethod
def fromDocument(document: Optional[dict]) -> Optional[ChatPerson]:
if not document:
return None
return ChatPerson(document['chat_id'], document['person_id'])

View File

@ -1,44 +0,0 @@
from __future__ import annotations
from abc import abstractmethod
from typing import Optional
import names
class Person():
id: str
username: str
def __init__(self, id: str, username: Optional[str] = None) -> None:
self.id = id
if not username:
self.username = names.get_first_name()
else:
self.username = username
def getId(self) -> str:
return self.id
def getUsername(self) -> str:
return self.username
def toDict(self, withUsername: bool = True) -> dict:
result = {
'_id': self.id
}
if withUsername:
result['username'] = self.username
return result
@staticmethod
def getMongoRoot() -> str:
return 'person'
@staticmethod
def fromDocument(document: Optional[dict]) -> Optional[Person]:
if not document:
return None
return Person(document['_id'], document['username'])

52
src/entity/user.py Normal file
View File

@ -0,0 +1,52 @@
from __future__ import annotations
from typing import Iterable
class User():
collection: str = 'users'
idIndex: str = '_id'
chatsIndex: str = 'chats'
usernameIndex: str = 'username'
userId: str
username: str
chats: Iterable[str]
def __init__(self, userId, username, chats) -> None:
self.userId = userId
self.username = username
self.chats = chats
def getUserId(self) -> str:
return self.userId
def getUsername(self) -> str:
return self.username
def getChats(self) -> Iterable[str]:
return self.chats
def isInChat(self, chatId: str) -> bool:
return chatId in self.getChats()
def addToChat(self, chatId: str) -> None:
self.chats.append(chatId)
def removeFromChat(self, chatId: str) -> None:
if chatId in self.getChats():
self.chats.remove(chatId)
def toMongoDocument(self) -> dict:
return {
self.usernameIndex: self.getUsername(),
self.chatsIndex: self.getChats()
}
@staticmethod
def fromMongoDocument(mongoDocument: dict) -> User:
return User(
mongoDocument[User.idIndex],
mongoDocument[User.usernameIndex],
mongoDocument[User.chatsIndex]
)

View File

@ -0,0 +1,2 @@
class AlreadyExistsException(Exception):
pass

View File

@ -0,0 +1,2 @@
class NotFoundException(Exception):
pass

View File

@ -1,10 +1,13 @@
from abc import abstractmethod from abc import abstractmethod
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 handler.vo.updateData import UpdateData
class HandlerInterface:
class AbstractHandler:
def __init__(self) -> None: def __init__(self) -> None:
pass pass
@ -14,8 +17,8 @@ class HandlerInterface:
@abstractmethod @abstractmethod
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 def getUpdateData(self, update: Update) -> UpdateData:
def getCommandName(self) -> str: raise Exception('getCommandName method is not implemented') return UpdateData.createFromUpdate(update)
def reply(self, update: Update, message: str) -> None: def reply(self, update: Update, message: str) -> None:
update.effective_message.reply_markdown_v2(text=message) update.effective_message.reply_markdown_v2(text=message)

38
src/handler/inHandler.py Executable file
View File

@ -0,0 +1,38 @@
from config.contents import opted_in, opted_in_failed
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):
botHandler: CommandHandler
userRepository: UserRepository
def __init__(self) -> None:
self.botHandler = CommandHandler('in', self.handle)
self.userRepository = UserRepository()
def handle(self, update: Update, context: CallbackContext) -> None:
updateData = self.getUpdateData(update)
try:
user = self.userRepository.getById(updateData.getUserId())
if user.isInChat(updateData.getChatId()):
self.reply(update, opted_in_failed)
return
user.addToChat(updateData.getChatId())
self.userRepository.save(user)
except NotFoundException:
self.userRepository.saveByUpdateData(updateData)
self.reply(update, opted_in)
def getBotHandler(self) -> CommandHandler:
return self.botHandler

40
src/handler/mentionHandler.py Executable file
View File

@ -0,0 +1,40 @@
from typing import Iterable
from config.contents import mention_failed
from entity.user import User
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 MentionHandler(AbstractHandler):
botHandler: CommandHandler
userRepository: UserRepository
def __init__(self) -> None:
self.botHandler = CommandHandler('everyone', self.handle)
self.userRepository = UserRepository()
def handle(self, update: Update, context: CallbackContext) -> None:
updateData = self.getUpdateData(update)
users = self.userRepository.getAllForChat(updateData.getChatId())
if users:
self.reply(update, self.buildMentionMessage(users))
return
self.reply(update, mention_failed)
def getBotHandler(self) -> CommandHandler:
return self.botHandler
def buildMentionMessage(self, users: Iterable[User]) -> str:
result = ''
for user in users:
result += f'*[{user.getUsername()}](tg://user?id={user.getUserId()})* '
return result

36
src/handler/outHandler.py Executable file
View File

@ -0,0 +1,36 @@
from config.contents import opted_off, opted_off_failed
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 OutHandler(AbstractHandler):
botHandler: CommandHandler
userRepository: UserRepository
def __init__(self) -> None:
self.botHandler = CommandHandler('out', self.handle)
self.userRepository = UserRepository()
def handle(self, update: Update, context: CallbackContext) -> None:
updateData = self.getUpdateData(update)
try:
user = self.userRepository.getById(updateData.getUserId())
if not user.isInChat(updateData.getChatId()):
raise NotFoundException()
except NotFoundException:
self.reply(update, opted_off_failed)
return
user.removeFromChat(updateData.getChatId())
self.userRepository.save(user)
self.reply(update, opted_off)
def getBotHandler(self) -> CommandHandler:
return self.botHandler

View File

@ -0,0 +1,36 @@
from __future__ import annotations
import names
from telegram.update import Update
class UpdateData():
userId: str
chatId: str
username: str
def __init__(self, userId: str, chatId: str, username: str) -> None:
self.userId = userId
self.chatId = chatId
self.username = username
def getUserId(self) -> str:
return self.userId
def getChatId(self) -> str:
return self.chatId
def getUsername(self) -> str:
return self.username
@staticmethod
def createFromUpdate(update: Update) -> UpdateData:
userId = str(update.effective_user.id)
chatId = str(update.effective_chat.id)
chatId = "-284685928"
username = update.effective_user.username or update.effective_user.first_name
if not username:
username = names.get_first_name()
return UpdateData(userId, chatId, username)

View File

@ -1,38 +0,0 @@
from config.contents import opted_in_successfully, opted_in_failed
from repositories.relationRepository import RelationRepository
from database.databaseClient import DatabaseClient
from handlers.handlerInterface import HandlerInterface
from telegram.ext.callbackcontext import CallbackContext
from telegram.ext.commandhandler import CommandHandler
from telegram.update import Update
class InHandler(HandlerInterface):
botHandler: CommandHandler
commandName: str = 'in'
def __init__(self) -> None:
self.botHandler = CommandHandler(
self.getCommandName(),
self.handle
)
def handle(self, update: Update, context: CallbackContext) -> None:
personId = update.effective_user.id
chatId = update.effective_chat.id
username = update.effective_user.username
relationRepository = RelationRepository()
relation = relationRepository.get(chatId, personId)
if relation:
self.reply(update, opted_in_failed)
return
relationRepository.save(chatId, personId, username)
self.reply(update, opted_in_successfully)
def getBotHandler(self) -> CommandHandler:
return self.botHandler
def getCommandName(self) -> str:
return self.commandName

View File

@ -1,45 +0,0 @@
from typing import Iterable
from config.contents import mention_failed
from entities.person import Person
from handlers.handlerInterface import HandlerInterface
from repositories.relationRepository import RelationRepository
from repositories.personRepository import PersonRepository
from telegram.ext.callbackcontext import CallbackContext
from telegram.ext.commandhandler import CommandHandler
from telegram.update import Update
class MentionHandler(HandlerInterface):
botHandler: CommandHandler
commandName: str = 'everyone'
def __init__(self) -> None:
self.botHandler = CommandHandler(
self.getCommandName(),
self.handle
)
def handle(self, update: Update, context: CallbackContext) -> None:
relationRepository = RelationRepository()
persons = relationRepository.getPersonsForChat(update.effective_chat.id)
if not persons:
self.reply(update, mention_failed)
return
self.reply(update, self.buildMentionMessage(persons))
def getBotHandler(self) -> CommandHandler:
return self.botHandler
def getCommandName(self) -> str:
return self.commandName
def buildMentionMessage(self, persons: Iterable[Person]) -> str:
result = ''
for person in persons:
result += f'*[{person.getUsername()}](tg://user?id={person.getId()})* '
return result

View File

@ -1,38 +0,0 @@
from config.contents import opted_off_successfully, opted_off_failed
from handlers.handlerInterface import HandlerInterface
from telegram.ext.callbackcontext import CallbackContext
from telegram.ext.commandhandler import CommandHandler
from telegram.update import Update
from repositories.relationRepository import RelationRepository
class OutHandler(HandlerInterface):
botHandler: CommandHandler
commandName: str = 'out'
def __init__(self) -> None:
self.botHandler = CommandHandler(
self.getCommandName(),
self.handle
)
def handle(self, update: Update, context: CallbackContext) -> None:
personId = update.effective_user.id
chatId = update.effective_chat.id
relationRepository = RelationRepository()
relation = relationRepository.get(chatId, personId)
if not relation:
self.reply(update, opted_off_failed)
return
relationRepository.remove(relation)
self.reply(update, opted_off_successfully)
def getBotHandler(self) -> CommandHandler:
return self.botHandler
def getCommandName(self) -> str:
return self.commandName

View File

@ -1,18 +0,0 @@
from database.databaseClient import DatabaseClient
from entities.chat import Chat
from typing import Optional
class ChatRepository:
database: DatabaseClient
def __init__(self) -> None:
self.database = DatabaseClient()
def get(self, id: str) -> Optional[Chat]:
chat = Chat(id)
search = self.database.findOne(Chat.getMongoRoot(), chat.toDict())
return Chat.fromDocument(search)
def save(self, chat: Chat) -> None:
self.database.insert(Chat.getMongoRoot(), chat.toDict())

View File

@ -1,27 +0,0 @@
from database.databaseClient import DatabaseClient
from entities.person import Person
from typing import Iterable, Optional
class PersonRepository:
database: DatabaseClient
def __init__(self) -> None:
self.database = DatabaseClient()
def get(self, id: str) -> Optional[Person]:
person = Person(id)
search = self.database.findOne(Person.getMongoRoot(), person.toDict(False))
return Person.fromDocument(search)
def find(self, query: dict) -> Iterable[Person]:
result = []
search = self.database.find(Person.getMongoRoot(), query)
for document in search:
result.append(Person.fromDocument(document))
return result
def save(self, person: Person) -> None:
self.database.insert(Person.getMongoRoot(), person.toDict())

View File

@ -1,56 +0,0 @@
from typing import Iterable, Optional
from database.databaseClient import DatabaseClient
from entities.chat import Chat
from entities.chatPerson import ChatPerson
from entities.person import Person
from repositories.personRepository import PersonRepository
from repositories.chatRepository import ChatRepository
class RelationRepository():
client: DatabaseClient
def __init__(self) -> None:
self.client = DatabaseClient()
def get(self, chatId: str, personId: str) -> Optional[ChatPerson]:
relation = ChatPerson(chatId, personId)
search = self.client.findOne(ChatPerson.getMongoRoot(), relation.toDict())
return ChatPerson.fromDocument(search)
def save(self, chatId: str, personId: str, username: Optional[str] = None) -> None:
relation = ChatPerson(chatId, personId)
self.client.insert(ChatPerson.getMongoRoot(), relation.toDict())
personRepository = PersonRepository()
person = personRepository.get(personId)
if not person:
person = Person(personId, username)
personRepository.save(person)
chatRepository = ChatRepository()
chat = chatRepository.get(chatId)
if not chat:
chat = Chat(chatId)
chatRepository.save(chat)
def getPersonsForChat(self, chatId: str) -> Iterable[ChatPerson]:
result = []
relations = self.client.find(ChatPerson.getMongoRoot(), {'chat_id': chatId})
search = {}
for relation in relations:
search['_id'] = relation['person_id']
if not search:
return result
personRepository = PersonRepository()
return personRepository.find(search)
def remove(self, relation: ChatPerson) -> None:
self.client.remove(ChatPerson.getMongoRoot() ,relation.toDict())

View File

@ -0,0 +1,66 @@
from typing import Iterable, Optional
from database.client import Client
from entity.user import User
from exception.notFoundException import NotFoundException
from handler.vo.updateData import UpdateData
class UserRepository():
client: Client
def __init__(self) -> None:
self.client = Client()
def getById(self, id: str) -> User:
user = self.client.findOne(
User.collection,
{
User.idIndex: id
}
)
if not user:
raise NotFoundException(f'Could not find user with "{id}" id')
return User(
user[User.idIndex],
user[User.usernameIndex],
user[User.chatsIndex]
)
def save(self, user: User) -> None:
self.client.updateOne(
User.collection,
{ User.idIndex: user.getUserId() },
user.toMongoDocument()
)
def saveByUpdateData(self, data: UpdateData) -> None:
self.client.insertOne(
User.collection,
{
User.idIndex: data.getUserId(),
User.usernameIndex: data.getUsername(),
User.chatsIndex: [data.getChatId()]
}
)
def getAllForChat(self, chatId: str) -> Iterable[User]:
result = []
users = self.client.findMany(
User.collection,
{
User.chatsIndex: {
"$in" : [chatId]
}
}
)
for record in users:
result.append(User.fromMongoDocument(record))
return result