""" AUTHOR: Khushal P Soonderji DATE: Wednesday, 15th Jan., 2025. OBJECTIVE: To handle all chat-related behaviour from one place. The initially known clients are WhatsApp and Telegram. REFERENCES: N/A DOWNLOADS: N/A """ # ***************************************************************************************************************** # ***** **** # *** IMPORT *** # ***** **** # ***************************************************************************************************************** # To make sibling directories accessible for imports: import sys sys.path.append(".") sys.path.append("..") # My async utils: from utils_v2.database.async_mysql_v2 import AsyncMySQL from utils_v2.database.async_mongo_v2 import AsyncMongo from utils_v2.cache.async_redis_cache_v2 import AsyncRedisCache # Controllers: from controllers_v2.core.message import CoreMessageController # Models: from models.core.auth_token import CoreAuthTokenModel from models.message.chat.send import ( NimbusWhatsAppMessage, ChatSendOneResult, ChatSendManyResults ) # Chat clients: from utils_v2.whatsapp.nimbus.controllers.async_nimbus_whatsapp import AsyncNimbusWhatsapp # To work with datatypes: from typing import List, Any # To make HTTP requests: import httpx # to work with MongoDB: from bson.objectid import ObjectId # To make abstract classes: from abc import ABC, abstractmethod # ***************************************************************************************************************** # ***** **** # *** MACROS / ONE-TIME INIT *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** VARIABLES *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** FUNCTIONS *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** CLASSES *** # ***** **** # ***************************************************************************************************************** class ChatController(CoreMessageController, ABC): # ┏┓┓ ┓┏ # ┃ ┃┏┓┏┏ ┃┃┏┓┏┓┏ # ┗┛┗┗┻┛┛ ┗┛┗┻┛ ┛ SERVICE_TYPE = "chat" # ┏┓ # ┃ ┏┓┏┓┏╋┏┓┓┏┏╋┏┓┏┓ # ┗┛┗┛┛┗┛┗┛ ┗┻┗┗┗┛┛ def __init__( self, cache: AsyncRedisCache = None, http_client: httpx.AsyncClient = None, alert_url: str = None, base_filter: dict = None, debug: bool = True, debug_prefix: str = "Chat (C) | ", debug_only_errors: bool = True ): """ This is the foundational controller for all chat services. This is built on top of the core message controller, and, in turn, all individual chat client controllers must be built on top of this. :param cache: The object to use for caching results from database calls. :param http_client: The HTTP client :param base_filter: The basic filter that will be applied to all fetching/updating queries. WARNING: THE BASE FILTER WILL ALWAYS BE APPLIED AUTOMATICALLY. SET THIS UP WISELY. :param debug: Whether, or not, you would like to print debugging messages: :param debug_prefix: The prefix to print with the debugging messages. :param debug_only_errors: Whether you would like to print only error messages or all messages. :return: None. """ # Prepare the combined base filter: sms_filter = {} for k, v in (base_filter or {}).items(): sms_filter[k] = v sms_filter["serviceType"] = self.SERVICE_TYPE # Invoke the parent's constructor: CoreMessageController.__init__( self, cache = cache, alert_url = alert_url, http_client = http_client, base_filter = sms_filter, debug = debug, debug_prefix = debug_prefix, debug_only_errors = debug_only_errors ) # Init a variable in a parent: self._service_type = self.SERVICE_TYPE # ┏┓ ┓ ┳┳┓ # ┗┓┏┓┏┓┏┫ ┃┃┃┏┓┏┏┏┓┏┓┏┓┏ # ┗┛┗ ┛┗┗┻ ┛ ┗┗ ┛┛┗┻┗┫┗ ┛ # ┛ @abstractmethod async def send_one_message( self, sql_conn: AsyncMySQL, mongo_data_conn: AsyncMongo, auth_token: CoreAuthTokenModel, client: AsyncNimbusWhatsapp, message: NimbusWhatsAppMessage, tags: List[Any] ) -> ChatSendOneResult: """ To send one message from the third-party client. :param sql_conn: The connection to the database to use for this operation. :param mongo_data_conn: The connection to the database to use for this operation. :param auth_token: The auth-token model for the account from which the message has to be sent. :param client: The connection/instance of the third-party client to use to perform this operation. :param message: The message that you want to send to the recipient. :param tags: Any tags that you would like to attach to the message. To be used later for internal filtering. :return: The structured response model to describe the operation. """ pass @abstractmethod async def send_many_messages( self, sql_conn: AsyncMySQL, mongo_data_conn: AsyncMongo, http_client: httpx.AsyncClient, auth_token: CoreAuthTokenModel, client: AsyncNimbusWhatsapp | None, messages: List[NimbusWhatsAppMessage], tags: List[Any] ) -> ChatSendManyResults: """ To send many chat messages in one go. :param sql_conn: The connection to the database to use for this operation. :param mongo_data_conn: The connection to the database to use for this operation. :param http_client: An HTTP client to use to make API calls through the third-party client's class. :param auth_token: The auth-token model for the account from which the message has to be sent. :param client: The connection/instance of the third-party client to use to perform this operation. :param messages: The messages that you want to send to the recipients. :param tags: Any tags that you would like to attach to the message. To be used later for internal filtering. :return: The structured response model to describe the operation. """ pass # ┳┳ ┓ ┏┳┓ # ┃┃┏┓┏┫┏┓╋┏┓ ┃ ┏┓┏┓┏ # ┗┛┣┛┗┻┗┻┗┗ ┻ ┗┻┗┫┛ # ┛ ┛ # We cannot modify the SMS messages themselves, but we can set/unset tags on them for internal referencing and # filtering. This will help the users organize their inboxes well. async def update_chat_message_tags( self, mongo_data_conn: AsyncMongo, message_id: ObjectId | str, unset_tags: List[str] = None, set_tags: List[str] = None ) -> bool: """ To set and unset tags on one chat message. :param mongo_data_conn: The database connection to use to perform this action. :param message_id: The ObjectId of the document in MongoDb that holds the message. :param unset_tags: The list of tags to unset (done before setting new tags). :param set_tags: The list of tags to set (done after unsetting old tags). :return: True if successful, else False. """ # Simply call the core model: return await self.update_message_tags( mongo_data_conn = mongo_data_conn, message_id = message_id, unset_tags = unset_tags, set_tags = set_tags ) # ***************************************************************************************************************** # ***** **** # *** MAIN PROGRAM *** # ***** **** # ***************************************************************************************************************** if __name__ == "__main__": pass