From 91831bd7f58c0e6a5508056221defd2c2f26e7c0 Mon Sep 17 00:00:00 2001 From: khushal Date: Wed, 4 Dec 2024 14:34:21 +0530 Subject: [PATCH] (20241204) Trying display name and display picture fetch from GMail. --- api/main.py | 11 +- utils_v2/sms/nimbus/async_nimbus.py | 20 +- utils_v2/sms/savvy_bulk_sms/__init__.py | 0 .../savvy_bulk_sms/async_savvy_bulk_sms.py | 259 ++++++++++++++++++ 4 files changed, 282 insertions(+), 8 deletions(-) create mode 100644 utils_v2/sms/savvy_bulk_sms/__init__.py create mode 100644 utils_v2/sms/savvy_bulk_sms/async_savvy_bulk_sms.py diff --git a/api/main.py b/api/main.py index 7c11cd7..a004d37 100644 --- a/api/main.py +++ b/api/main.py @@ -194,13 +194,15 @@ async def app_startup(**kwargs): max_keepalive_connections = 50, # ... Maximum number of connections that can be kept alive. ), timeout = httpx.Timeout( - connect = 2.5, # ... Shorter connection timeout. - read = 2.5, # ...... Like what EasyEcom gives. + pool = 120.0, # .... Time to wait for a free connection from the pool. + connect = 2.5, # ... Time to wait for establishing a connection to the server. write = 10.0, # .... Time to wait for sending data. - pool = 120.0 # ..... Time to wait for a free connection from the pool. + read = 2.5 # ....... Time to wait for receiving data. ) ) + print("Here!") + # ┏┓ ┓ ┓ ┳┓ # ┃ ┏┓┏┓┏┫ ┏┓┏┓┏┫ ┃┃┏┓╋┏┓ # ┗┛┛ ┗ ┗┻ ┗┻┛┗┗┻ ┻┛┗┻┗┗┻ @@ -218,6 +220,9 @@ async def app_startup(**kwargs): ) current_app.script_data = response.json().get("data") + print("CRED:", script_cred) + print("DATA:", current_app.script_data) + # ┏┓┏┓┳┳ ┏┓┏┏• • # ┃ ┃┃┃┃ ┣┫╋╋┓┏┓┓╋┓┏ # ┗┛┣┛┗┛ ┛┗┛┛┗┛┗┗┗┗┫ diff --git a/utils_v2/sms/nimbus/async_nimbus.py b/utils_v2/sms/nimbus/async_nimbus.py index 058d9f2..18fd70a 100644 --- a/utils_v2/sms/nimbus/async_nimbus.py +++ b/utils_v2/sms/nimbus/async_nimbus.py @@ -88,6 +88,7 @@ class AsyncNimbusSMS: sender_id, user_id, api_key, + http_client: httpx.AsyncClient = None, debug = True, debug_prefix = "Nimbus SMS | " ): @@ -98,6 +99,8 @@ class AsyncNimbusSMS: :param sender_id: The 6-char code like "HDFCBK", "NSESMS", "ZRODHA" that you see in your SMS inbox. :param user_id: The 6-digit id that Nimbus has assigned to you. :param api_key: The API key generated through Nimbus's portal. + :param http_client: An instance of 'httpx' library's AsyncClient. If not given, one will be instantiated + internally. It is recommended that, for multi-auth use cases, you provide a common HTTP client from outside. :param debug: Whether, or not, you would like to show debugging messages (can be changed on the fly). :param debug_prefix: The prefix text to show with the debug string. """ @@ -107,12 +110,19 @@ class AsyncNimbusSMS: if not debug: self.__printer.disable() # Create an HTTP client to work with: - limits = httpx.Limits( - max_connections = 5, - max_keepalive_connections = 5, - keepalive_expiry = 3600 + if http_client: self.__http_client = http_client + else: self.__http_client = httpx.AsyncClient( + limits = httpx.Limits( + max_connections = 100, # ............ Maximum number of connections allowed in the pool. + max_keepalive_connections = 50, # ... Maximum number of connections that can be kept alive. + ), + timeout = httpx.Timeout( + pool = 120.0, # .... Time to wait for a free connection from the pool. + connect = 2.5, # ... Time to wait for establishing a connection to the server. + write = 10.0, # .... Time to wait for sending data. + read = 2.5 # ....... Time to wait for receiving data. + ) ) - self.__http_client = httpx.AsyncClient(limits = limits, timeout = 120) # Capture the input config: self.__entity_id = entity_id diff --git a/utils_v2/sms/savvy_bulk_sms/__init__.py b/utils_v2/sms/savvy_bulk_sms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils_v2/sms/savvy_bulk_sms/async_savvy_bulk_sms.py b/utils_v2/sms/savvy_bulk_sms/async_savvy_bulk_sms.py new file mode 100644 index 0000000..058d9f2 --- /dev/null +++ b/utils_v2/sms/savvy_bulk_sms/async_savvy_bulk_sms.py @@ -0,0 +1,259 @@ +""" + + AUTHOR: + + Khushal P Soonderji + + DATE: + + Monday, 9th Sept., 2024 + + OBJECTIVE: + + To be able to send SMSs from Nimbus's API and manage the templates and other things from one place. + + REFERENCES: + + 01. https://github.com/innovativevijay/SmsHitApiSample + 02. https://nimbusit.net/appforms/apimanual.php + + DOWNLOADS: + + N/A + + WEB-PORTAL: + + 01. http://nimbusit.net/ + +""" + + +# ***************************************************************************************************************** +# ***** **** +# *** IMPORT *** +# ***** **** +# ***************************************************************************************************************** + + +# To make sibling directories accessible for imports: +import sys +sys.path.append(".") +sys.path.append("..") + +# To make API Calls: +import httpx + +# For debugging: +from icecream import IceCreamDebugger + + +# ***************************************************************************************************************** +# ***** **** +# *** MACROS / ONE-TIME INIT *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing yet + + +# ***************************************************************************************************************** +# ***** **** +# *** VARIABLES *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** CLASSES *** +# ***** **** +# ***************************************************************************************************************** + + +class AsyncNimbusSMS: + + MESSAGE_TYPE_REGULAR = 0 + MESSAGE_TYPE_UNICODE = 1 + + MESSAGE_TYPE_NOT_FLASH = 0 + MESSAGE_TYPE_FLASH = 1 + + def __init__( + self, + entity_id, + sender_id, + user_id, + api_key, + debug = True, + debug_prefix = "Nimbus SMS | " + ): + + """ + Sets up an instance of the SMS sender through Nimbus IT. + :param entity_id: The entity id as registered with DLT. + :param sender_id: The 6-char code like "HDFCBK", "NSESMS", "ZRODHA" that you see in your SMS inbox. + :param user_id: The 6-digit id that Nimbus has assigned to you. + :param api_key: The API key generated through Nimbus's portal. + :param debug: Whether, or not, you would like to show debugging messages (can be changed on the fly). + :param debug_prefix: The prefix text to show with the debug string. + """ + + # Create the debugging tools: + self.__printer = IceCreamDebugger(prefix = debug_prefix, includeContext = True) + if not debug: self.__printer.disable() + + # Create an HTTP client to work with: + limits = httpx.Limits( + max_connections = 5, + max_keepalive_connections = 5, + keepalive_expiry = 3600 + ) + self.__http_client = httpx.AsyncClient(limits = limits, timeout = 120) + + # Capture the input config: + self.__entity_id = entity_id + self.__sender_id = sender_id + self.__user_id = user_id, + self.__api_key = api_key + + async def get_balance(self): + + """ + Checks the balance in the Nimbus wallet. + :return: The balance (float) if the request was successful, else None. + """ + + balance = None + + try: + + # Call the API: + response = await self.__http_client.get( + url = r"http://nimbusit.net/api/balance", + params = {"user": self.__user_id, "authkey": self.__api_key} + ) + + # The response of a successful API call looks like "BALANCE:599". We need just the number: + if response.status_code in [200]: balance = float(response.content.decode().split(":")[-1].strip()) + + except Exception as exception: + self.__printer(exception) + + return balance + + async def send_sms( + self, + template_id, + recipient_number, + message, + message_type = MESSAGE_TYPE_REGULAR, + flash = MESSAGE_TYPE_NOT_FLASH + ): + + """ + Sends one SMS through Nimbus IT's system. The text of the message must match the template that had been + submitted. A mismatch may cause the message to fail at best, and raise troubles in the real-world with + government bodies at worst. Be careful. + :param template_id: The id of the SMS template as registered on Nimbus's portal. + :param recipient_number: The phone number of the recipient. You can send an array of numbers, too, BUT IT IS + STRONGLY RECOMMENDED TO NOT DO THAT TO AVOID BEING BLOCKED BY DLT. + :param message: The message to send to the recipient. Should match the template that is being sent. + :param message_type: Choose between 'AsyncNimbusSMS.MESSAGE_TYPE_REGULAR' (default) and + 'AsyncNimbusSMS.MESSAGE_TYPE_UNICODE' based on the type of characters being sent. Both are class variables. + :param flash: to choose between a regular inbox SMS, or a flash SMS. + :return: The dict of all the details of the message that was sent including whether, or not, it was successfully + sent. Other details depend on the service provider (Nimbus IT in this case). + """ + + # Construct the basic structure of the response of this method: + summary = { + "success": False, + "info": None, + "sender": self.__sender_id, + "recipient": recipient_number, + "message": message, + "length": len(message), + "template_id": template_id, + "raw": None, + "isFlash": True if flash else False + } + + try: + + # Pre-process the recipient's number: + if not isinstance(recipient_number, (list, set, tuple)): recipient_number = [recipient_number] + + # Call the API: + response = await self.__http_client.get( + url = r"http://nimbusit.net/api/pushsms", + params = { + "user": self.__user_id, + "authkey": self.__api_key, + "sender": self.__sender_id, + "mobile": ",".join([str(num) for num in recipient_number]), + "text": message, + "entityid": self.__entity_id, + "templateid": template_id, + "type": message_type, + "flash": flash + } + ) + + # For a successful API call: + if response.status_code == 200: + response_json = response.json() + summary["success"] = True if response_json.get("STATUS", "ERROR").lower() in ["ok"] else False + summary["info"] = response_json.get("RESPONSE", {}).get("INFO") + summary["raw"] = { + "http_code": response.status_code, + "response": response_json, + } + + # For any other code that indicates some form of failure: + else: summary["raw"] = { + "http_code": response.status_code, + "response": response.content.decode() + } + + except Exception as exception: + self.__printer(exception) + + return summary + + +# ***************************************************************************************************************** +# ***** **** +# *** MAIN PROGRAM *** +# ***** **** +# ***************************************************************************************************************** + + +if __name__ == "__main__": + + import asyncio + + async def main(): + + sender = AsyncNimbusSMS( + entity_id = "", + sender_id = "", + user_id = "", + api_key = "" + ) + + response = await sender.send_sms( + template_id = "", + recipient_number = "", + message = "" + ) + print("SMS API RESPONSE:", response) + + my_balance = await sender.get_balance() + print("REMAINING BALANCE:", my_balance) + + + asyncio.run(main())