🚨 Latest Research:Tanstack npm Packages Compromised in Ongoing Mini Shai-Hulud Supply-Chain Attack.Learn More
Socket
Book a DemoSign in
Socket

python-whatsapp-wrapper

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

python-whatsapp-wrapper - pypi Package Compare versions

Comparing version
0.0.11
to
0.0.15
+53
whatsapp/config_account.py
from whatsapp.bot import WhatsappBot
import dataclasses as dc
import requests
from typing import Any, Self
from dataclasses_json import dataclass_json
@dataclass_json
@dc.dataclass
class ProfileCommand:
command_name: str = dc.field()
"Command name. Max 32 characters"
command_description: str = dc.field()
"Command description. Max 256 characters, emojis are not supported"
@dataclass_json
@dc.dataclass
class ProfileComponents:
welcome_message: bool = dc.field(default=False)
"Enable/disable welcome message"
commands: list[ProfileCommand] = dc.field(default_factory=list)
"Commands like '/comamnd'. Max 32 commands"
prompts: list[str] = dc.field(default_factory=list)
@classmethod
def get_current_config (cls, bot: WhatsappBot, bot_number_id: str) -> dict[str, Any]:
headers = { "Authorization": bot.bearer_token }
# TODO: Fix this bad replace
endpoint = bot.external_endpoint(bot_number_id, "fields=conversational_automation")
endpoint = endpoint.replace("/fields", "?fields")
return requests.get(endpoint, headers=headers).json()
@classmethod
def load_profile(cls, bot: WhatsappBot, bot_number_id: str) -> Self:
print(cls.get_current_config(bot, bot_number_id))
return cls.from_dict(cls.get_current_config(bot, bot_number_id))
def set_current_config (self, bot: WhatsappBot, bot_number_id: str):
headers = { "Authorization": bot.bearer_token, "Content-Type": "application/json" }
endpoint = bot.external_endpoint(bot_number_id, "conversational_automation")
data = self.to_json()
return requests.post(endpoint, headers=headers, data=data).json()
def add_command (self, command_name: str, command_description):
"""
Add a command to a given Profile
:param command_name: Command name that will appear when user tap profile or keyboard
:param command_description: Command description for help user
"""
command_name = command_name.lstrip("/")
self.commands.append(ProfileCommand(command_name, command_description))
+1
-1
Metadata-Version: 2.1
Name: python-whatsapp-wrapper
Version: 0.0.11
Version: 0.0.15
Summary: Pure python project for META Whatsapp Business API wrapper.

@@ -5,0 +5,0 @@ Author-email: Sergio Pires <sergiodanpires@gmail.com>

Metadata-Version: 2.1
Name: python-whatsapp-wrapper
Version: 0.0.11
Version: 0.0.15
Summary: Pure python project for META Whatsapp Business API wrapper.

@@ -5,0 +5,0 @@ Author-email: Sergio Pires <sergiodanpires@gmail.com>

@@ -12,4 +12,5 @@ LICENSE

whatsapp/bot.py
whatsapp/config_account.py
whatsapp/error.py
whatsapp/messages.py
whatsapp/utils.py

@@ -1,1 +0,1 @@

__version__ = "0.0.11"
__version__ = "0.0.15"

@@ -100,3 +100,3 @@ import argparse

def external_endpoint (self, bot_number_id: str) -> str:
def external_endpoint (self, bot_number_id: str, service: str) -> str:
"""

@@ -108,3 +108,3 @@ Returns the external endpoint.

"""
return f"{self.endpoint}/{self.api_version}/{bot_number_id}/messages"
return f"{self.endpoint}/{self.api_version}/{bot_number_id}/{service}"

@@ -121,3 +121,3 @@ async def send_message (self, message: dict[str, Any], bot_number_id: str):

response = requests.post(
self.external_endpoint(bot_number_id), data=payload, headers=headers
self.external_endpoint(bot_number_id, "messages"), data=payload, headers=headers
)

@@ -169,3 +169,3 @@

:raises UnknownEvent: When don't reache any trigger
"""
"""
if state not in self._state_handlers:

@@ -186,6 +186,2 @@ self._state_handlers[state] = State()

# Just compiles the regex pattern
else:
handler_trigger = re.compile(handler_trigger)
elif (

@@ -208,5 +204,5 @@ isinstance(handler_trigger, tuple)

:param state: State name
:param on_invalid_state_func: function to handle state
:param on_invalid_state_func: function to handle state
:raises UnknownEvent: If state are not valid
"""
"""
if state not in self._state_handlers:

@@ -225,3 +221,3 @@ raise UnknownEvent(f"{state} is not a valid state")

:return: String challenge
"""
"""
try:

@@ -303,3 +299,3 @@ mode = request.args["hub.mode"]

time.sleep(interval)
incoming_update = self._server_queue.get()
incoming_update = self.get_update()

@@ -327,2 +323,10 @@ if incoming_update is None:

def get_update (self) -> Incoming | None:
"""
Gets an update from the queue (In-memory, mongodb, redis etc).
:return: Incoming message
"""
return self._server_queue.get()
async def process_update (self, incoming: Incoming):

@@ -329,0 +333,0 @@ """

@@ -9,2 +9,3 @@ import dataclasses as dc

from dataclasses_json import config, dataclass_json
import json

@@ -31,3 +32,3 @@ from whatsapp.error import NotImplementedMsgType

:return: Same object
"""
"""
def _decorator (cls: RECEIVED_MESSAGE_T) -> RECEIVED_MESSAGE_T:

@@ -118,3 +119,3 @@ RECEIVED_MSG_TYPE_TO_OBJECT[msg_type] = cls

display_phone_number: str = dc.field()
"Phone number that cusomer will see in chat"
"Phone number that customer will see in chat"
phone_number_id: str = dc.field()

@@ -221,7 +222,7 @@ "Phone number id. Need to be used to respond an message "

"Arbitrary string"
conversation: Conversation = dc.field(default=None)
conversation: Conversation | None = dc.field(default=None)
"Conversation info"
errors: list[Errors] = dc.field(default_factory=list)
"List of objects describing errors"
pricing: Pricing = dc.field(default=None)
pricing: Pricing | None = dc.field(default=None)
"Pricing information"

@@ -335,3 +336,3 @@

Supoorted audio formats: aac, amr, mp3, mp4 audio, ogg (opus codecs, not audio/ogg)
Supoorted audio formats: aac, amr, mp3, mp4 audio, ogg (opus codecs, not audio/ogg)
Max size: 16 MB

@@ -386,3 +387,3 @@ """

Max Size: 100 MB
Max Size: 100 MB
Supported MIMEs (message custom logo):

@@ -474,5 +475,35 @@ - text/plain

class ButtonUrlMessage (ReceivedMessage):
...
@dataclass_json
@dc.dataclass
class ButtonUrlMessage:
header: str = dc.field(kw_only=True)
"Header text"
body: str = dc.field(kw_only=True)
"Body Text"
footer: str = dc.field(kw_only=True)
"Message footer text"
button_display_text: str = dc.field(kw_only=True)
"Button text"
button_url: str = dc.field(kw_only=True)
"URL that browser will open when user tap the button"
def to_send (self, to: str) -> dict[str, str]:
return {
**ReceivedMessage.default_body_to_send(to, MessageTypes.INTERACTIVE),
"interactive": {
"type": "cta_url",
"header": { "type": "text", "text": self.header },
"body": { "text": self.body },
"footer": { "text": self.footer },
"action": {
"name": "cta_url",
"parameters": {
"display_text": self.button_display_text,
"url": self.button_url
}
}
}
}
class FlowMessage (ReceivedMessage):

@@ -488,4 +519,4 @@ # TODO Implement FlowMessage when I receive API access

footer: str = dc.field(kw_only=True)
button_title: str = dc.field(kw_only=True)
sections: list[Section] = dc.field(kw_only=True)

@@ -520,4 +551,10 @@

def list_reply(self) -> Item:
return Item.from_dict(self.interactive["list_reply"])
item = self.interactive["list_reply"]
if isinstance(item, str):
item = item.replace("'", "\"")
item = json.loads(item)
return Item.from_dict(item)
@list_reply.setter

@@ -608,5 +645,19 @@ def list_reply(self, value: Item):

@dc.dataclass
class AskForLocationMessage (ReceivedMessage):
...
class AskForLocationMessage:
"""
Max body text size: 4096 characters
"""
body: str = dc.field(kw_only=True)
"Text body of location request (Accepts URL link). Max 4096 characters"
def to_send (self, to: str) -> dict[str, str]:
return {
**ReceivedMessage.default_body_to_send(to, MessageTypes.INTERACTIVE),
"interactive": {
"type": "location_request_message",
"body": { "text": self.body },
"action": { "name": "send_location" }
}
}
@dataclass_json

@@ -665,5 +716,5 @@ @dc.dataclass

"""
Text message. Contains information about sent and received texts.
Text message. Contains information about sent and received texts.
Shows a website preview if message has a url (startswith http:// or https://)
and preview_url is True, the url will be previewed.
and preview_url is True, the url will be previewed.
Max text size: 4096 characters

@@ -687,3 +738,3 @@ """

:return: Dictionary representing the reply message
"""
"""
output_msg = cls.default_body_to_send(to, MessageTypes.TEXT)

@@ -703,4 +754,4 @@ output_msg["text"] = { "body": message }

Video message with an optinal caption.
Supoorted audio formats: 3gp and mp4
Supoorted audio formats: 3gp and mp4
Max size: 16 MB

@@ -755,3 +806,3 @@ """

:return: List of converted messages.
"""
"""
if messages is None:

@@ -758,0 +809,0 @@ return []

@@ -9,4 +9,4 @@ import logging

from whatsapp.error import MissingParameters, UnknownEvent, VerificationFailed
from whatsapp.messages import Incoming, ReadMessage
def middleware(f: Callable):

@@ -41,1 +41,22 @@ """

return _middleware
def read_message(f: Callable):
"""
Decorator read messages when state are reached
:param f: State handler function
:return: Decorated state handler function
"""
@wraps(f)
# TODO: Fix circular import for this type hint
async def _read_message(bot, incoming: Incoming):
try:
await bot.send_message(
ReadMessage.to_send(incoming.message.id), incoming.metadata.phone_number_id
)
except:
logging.error(traceback.format_exc())
await f(bot, incoming)
return _read_message