diff --git a/kairon/chat/converters/channels/messenger.py b/kairon/chat/converters/channels/messenger.py
index 73cdd9b87..f0f77234f 100644
--- a/kairon/chat/converters/channels/messenger.py
+++ b/kairon/chat/converters/channels/messenger.py
@@ -1,3 +1,5 @@
+import json
+
from kairon.chat.converters.channels.responseconverter import ElementTransformerOps
from kairon.shared.constants import ElementTypes
@@ -34,6 +36,25 @@ def link_transformer(self, message):
except Exception as ex:
raise Exception(f" Error in MessengerResponseConverter::link_transformer {str(ex)}")
+ def paragraph_transformer(self, message):
+ try:
+ message_template = ElementTransformerOps.getChannelConfig(self.channel, self.message_type)
+ paragraph_template = json.loads(message_template)
+ jsoniterator = ElementTransformerOps.json_generator(message)
+ final_text = ""
+ for item in jsoniterator:
+ if item.get("type") == "paragraph":
+ children = ElementTransformerOps.json_generator(item.get("children", []))
+ for child in children:
+ text = child.get("text", "")
+ final_text += text
+ final_text += "\n"
+
+ paragraph_template["text"] = final_text
+ return paragraph_template
+ except Exception as ex:
+ raise Exception(f"Error in MessengerResponseConverter::paragraph_transformer {str(ex)}")
+
def button_transformer(self, message):
try:
button_json_temp = {}
@@ -89,5 +110,7 @@ async def messageConverter(self, message):
return self.button_transformer(message)
elif self.message_type == ElementTypes.QUICK_REPLY.value:
return self.quick_reply_transformer(message)
+ elif self.message_type == ElementTypes.FORMAT_TEXT.value:
+ return self.paragraph_transformer(message)
except Exception as ex:
raise Exception(f"Error in MessengerResponseConverter::messageConverter {str(ex)}")
diff --git a/kairon/chat/converters/channels/telegram.py b/kairon/chat/converters/channels/telegram.py
index 9b5b83d72..735bda41d 100644
--- a/kairon/chat/converters/channels/telegram.py
+++ b/kairon/chat/converters/channels/telegram.py
@@ -1,3 +1,5 @@
+import json
+
from kairon.chat.converters.channels.responseconverter import ElementTransformerOps
from kairon.shared.constants import ElementTypes
@@ -34,6 +36,34 @@ def link_transformer(self, message):
except Exception as ex:
raise Exception(f" Error in TelegramResponseConverter::link_transformer {str(ex)}")
+ def paragraph_transformer(self, message):
+ try:
+ message_template = ElementTransformerOps.getChannelConfig(self.channel, self.message_type)
+ paragraph_template = json.loads(message_template)
+ jsoniterator = ElementTransformerOps.json_generator(message)
+ final_text = ""
+ for item in jsoniterator:
+ if item.get("type") == "paragraph":
+ children = ElementTransformerOps.json_generator(item.get("children", []))
+ for child in children:
+ text = child.get("text", "")
+ leading_spaces = len(text) - len(text.lstrip())
+ trailing_spaces = len(text) - len(text.rstrip())
+ text = text.strip()
+ if child.get("bold"):
+ text = f"{text}"
+ if child.get("italic"):
+ text = f"{text}"
+ if child.get("strikethrough"):
+ text = f"{text}"
+ final_text += f"{' ' * leading_spaces}{text}{' ' * trailing_spaces}"
+ final_text += "\n"
+
+ paragraph_template["text"] = final_text
+ return paragraph_template
+ except Exception as ex:
+ raise Exception(f"Error in TelegramResponseConverter::paragraph_transformer {str(ex)}")
+
def button_transformer(self, message):
try:
jsoniterator = ElementTransformerOps.json_generator(message)
@@ -65,5 +95,7 @@ async def messageConverter(self, message):
return super().video_transformer(message)
elif self.message_type == ElementTypes.BUTTON.value:
return self.button_transformer(message)
+ elif self.message_type == ElementTypes.FORMAT_TEXT.value:
+ return self.paragraph_transformer(message)
except Exception as ex:
raise Exception(f"Error in TelegramResponseConverter::messageConverter {str(ex)}")
diff --git a/kairon/chat/converters/channels/whatsapp.py b/kairon/chat/converters/channels/whatsapp.py
index 8448fee64..1cd9135b8 100644
--- a/kairon/chat/converters/channels/whatsapp.py
+++ b/kairon/chat/converters/channels/whatsapp.py
@@ -1,6 +1,6 @@
+
from kairon.chat.converters.channels.responseconverter import ElementTransformerOps
import ujson as json
-
from kairon.shared.constants import ElementTypes
@@ -34,6 +34,34 @@ def link_transformer(self, message):
except Exception as ex:
raise Exception(f"Error in WhatsappResponseConverter::link_transformer {str(ex)}")
+ def paragraph_transformer(self, message):
+ try:
+ message_template = ElementTransformerOps.getChannelConfig(self.channel, self.message_type)
+ paragraph_template = json.loads(message_template)
+ jsoniterator = ElementTransformerOps.json_generator(message)
+ final_text = ""
+ for item in jsoniterator:
+ if item.get("type") == "paragraph":
+ children = ElementTransformerOps.json_generator(item.get("children", []))
+ for child in children:
+ text = child.get("text", "")
+ leading_spaces = len(text) - len(text.lstrip())
+ trailing_spaces = len(text) - len(text.rstrip())
+ text = text.strip()
+ if child.get("bold"):
+ text = f"*{text}*"
+ if child.get("italic"):
+ text = f"_{text}_"
+ if child.get("strikethrough"):
+ text = f"~{text}~"
+ final_text += f"{' ' * leading_spaces}{text}{' ' * trailing_spaces}"
+ final_text += "\n"
+
+ paragraph_template["body"] = final_text
+ return paragraph_template
+ except Exception as ex:
+ raise Exception(f"Error in WhatsappResponseConverter::paragraph_transformer {str(ex)}")
+
def button_transformer(self, message):
try:
message_template = ElementTransformerOps.getChannelConfig(self.channel, self.message_type)
@@ -121,5 +149,7 @@ async def messageConverter(self, message):
return self.button_transformer(message)
elif self.message_type == ElementTypes.DROPDOWN.value:
return self.dropdown_transformer(message)
+ elif self.message_type == ElementTypes.FORMAT_TEXT.value:
+ return self.paragraph_transformer(message)
except Exception as ex:
raise Exception(f"Error in WhatsappResponseConverter::messageConverter {str(ex)}")
diff --git a/kairon/chat/handlers/channels/telegram.py b/kairon/chat/handlers/channels/telegram.py
index bace014d2..e48f2cd8c 100644
--- a/kairon/chat/handlers/channels/telegram.py
+++ b/kairon/chat/handlers/channels/telegram.py
@@ -156,7 +156,7 @@ async def send_custom_json(
del response["photo"]
api_call = getattr(self, send_functions[("photo",)])
api_call(recipient_id, *response_list, **response)
- elif ops_type in ["link", "video"]:
+ elif ops_type in ["link", "video", "formatText"]:
response_list.append(response.get("text"))
del response["text"]
api_call = getattr(self, send_functions[("text",)])
diff --git a/kairon/chat/handlers/channels/whatsapp.py b/kairon/chat/handlers/channels/whatsapp.py
index cf8cac718..2cd24f135 100644
--- a/kairon/chat/handlers/channels/whatsapp.py
+++ b/kairon/chat/handlers/channels/whatsapp.py
@@ -233,7 +233,7 @@ async def send_custom_json(
message = json_message.get("data")
messagetype = json_message.get("type")
content_type = {"link": "text", "video": "video", "image": "image", "button": "interactive",
- "dropdown": "interactive", "audio": "audio"}
+ "dropdown": "interactive", "audio": "audio", "formatText": "text"}
if messagetype is not None and messagetype in type_list:
messaging_type = content_type.get(messagetype)
from kairon.chat.converters.channels.response_factory import ConverterFactory
diff --git a/kairon/shared/constants.py b/kairon/shared/constants.py
index 36e23ee55..c9e53f37b 100644
--- a/kairon/shared/constants.py
+++ b/kairon/shared/constants.py
@@ -126,6 +126,7 @@ class ElementTypes(str, Enum):
BUTTON = "button"
DROPDOWN = "dropdown"
QUICK_REPLY = "quick_reply"
+ FORMAT_TEXT = "formatText"
class WhatsappBSPTypes(str, Enum):
diff --git a/metadata/message_template.yml b/metadata/message_template.yml
index 5533b115e..aa99e6533 100644
--- a/metadata/message_template.yml
+++ b/metadata/message_template.yml
@@ -1,4 +1,4 @@
-type_list: ["image","link","video","button","quick_reply","dropdown","audio"]
+type_list: ["image","link","video","button","quick_reply","dropdown","audio", "formatText"]
slack:
image: '{
"blocks": [
@@ -88,6 +88,7 @@ messenger:
video: '{"text":""}'
button: '{"text":""}'
body_message: "Please select from quick buttons:"
+ formatText: '{"text":""}'
whatsapp:
image: '{
@@ -112,6 +113,10 @@ whatsapp:
"action":""
}'
body_message: "Please select from quick buttons:"
+ formatText: '{
+ "preview_url": true,
+ "body":""
+ }'
dropdown: '{
"type": "list",
@@ -128,6 +133,13 @@ telegram:
"reply_to_message_id":0}'
video: '{"text":""}'
body_message: 'Please select from quick buttons:'
+ formatText: '{
+ "text":"",
+ "parse_mode":"HTML",
+ "disable_web_page_preview":false,
+ "disable_notification":false,
+ "reply_to_message_id":0
+ }'
msteams:
body_message: "Please select from quick buttons:"
diff --git a/tests/unit_test/utility_test.py b/tests/unit_test/utility_test.py
index e9ca44ee1..3754bcff8 100644
--- a/tests/unit_test/utility_test.py
+++ b/tests/unit_test/utility_test.py
@@ -8,6 +8,9 @@
from io import BytesIO
from unittest.mock import patch, MagicMock
from urllib.parse import urlencode
+
+from kairon.chat.converters.channels.messenger import MessengerResponseConverter
+from kairon.chat.converters.channels.whatsapp import WhatsappResponseConverter
from kairon.shared.utils import Utility, MailUtility
Utility.load_system_metadata()
@@ -32,11 +35,11 @@
from kairon.chat.converters.channels.telegram import TelegramResponseConverter
from kairon.exceptions import AppException
from kairon.shared.augmentation.utils import AugmentationUtils
-from kairon.shared.constants import GPT3ResourceTypes, LLMResourceProvider
+from kairon.shared.constants import ElementTypes
from kairon.shared.data.audit.data_objects import AuditLogData
from kairon.shared.data.audit.processor import AuditDataProcessor
-from kairon.shared.data.constant import DEFAULT_SYSTEM_PROMPT, STORY_EVENT
-from kairon.shared.data.data_objects import EventConfig, Slots, LLMSettings, DemoRequestLogs
+from kairon.shared.data.constant import STORY_EVENT
+from kairon.shared.data.data_objects import EventConfig, Slots, DemoRequestLogs
from kairon.shared.data.processor import MongoProcessor
from kairon.shared.data.utils import DataUtility
from kairon.shared.models import TemplateType
@@ -3333,3 +3336,202 @@ def test_comma_sep_string_to_list(self):
# Test input with spaces
assert Utility.string_to_list("apple, banana, orange") == ["apple", "banana", "orange"]
+
+
+ def test_whatsapp_paragraph_transformer_basic(self):
+ whatsapp_response_converter = WhatsappResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value,
+ channel_type="whatsapp")
+ message = [
+ {
+ "type": "paragraph",
+ "children": [
+ {"text": "This is a test paragraph."}
+ ]
+ }
+ ]
+ expected_output = {"body": "This is a test paragraph.\n"}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig',
+ return_value='{"body": ""}'):
+ response = whatsapp_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_whatsapp_paragraph_transformer_with_formatting(self):
+ whatsapp_response_converter = WhatsappResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value,
+ channel_type="whatsapp")
+ message = [
+ {
+ "type": "paragraph",
+ "children": [
+ {"text": "Bold text", "bold": True},
+ {"text": " and normal text."}
+ ]
+ }
+ ]
+ expected_output = {"body": "*Bold text* and normal text.\n"}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig',
+ return_value='{"body": ""}'):
+ response = whatsapp_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_whatsapp_paragraph_transformer_with_spaces(self):
+ whatsapp_response_converter = WhatsappResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value,
+ channel_type="whatsapp")
+ message = [
+ {
+ "type": "paragraph",
+ "children": [
+ {"text": " Text with leading and trailing spaces "}
+ ]
+ }
+ ]
+ expected_output = {"body": " Text with leading and trailing spaces \n"}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig',
+ return_value='{"body": ""}'):
+ response = whatsapp_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_whatsapp_paragraph_transformer_empty_message(self):
+ whatsapp_response_converter = WhatsappResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value,
+ channel_type="whatsapp")
+ message = []
+ expected_output = {"body": ""}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig',
+ return_value='{"body": ""}'):
+ response = whatsapp_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_whatsapp_paragraph_transformer_exception_handling(self):
+ whatsapp_response_converter = WhatsappResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value,
+ channel_type="whatsapp")
+ message = None
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig',
+ side_effect=Exception("Test Exception")):
+ with pytest.raises(Exception) as excinfo:
+ whatsapp_response_converter.paragraph_transformer(message)
+ assert "Error in WhatsappResponseConverter::paragraph_transformer" in str(excinfo.value)
+
+
+ def test_telegram_paragraph_transformer_basic(self):
+ telegram_response_converter = TelegramResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="telegram")
+ message = [
+ {
+ "type": "paragraph",
+ "children": [
+ {"text": "This is a test paragraph."}
+ ]
+ }
+ ]
+ expected_output = {"text": "This is a test paragraph.\n"}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', return_value='{"text": ""}'):
+ response = telegram_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_telegram_paragraph_transformer_with_formatting(self):
+ telegram_response_converter = TelegramResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="telegram")
+ message = [
+ {
+ "type": "paragraph",
+ "children": [
+ {"text": "Bold text", "bold": True},
+ {"text": " and normal text."}
+ ]
+ }
+ ]
+ expected_output = {"text": "Bold text and normal text.\n"}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', return_value='{"text": ""}'):
+ response = telegram_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_telegram_paragraph_transformer_with_spaces(self):
+ telegram_response_converter = TelegramResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="telegram")
+ message = [
+ {
+ "type": "paragraph",
+ "children": [
+ {"text": " Text with leading and trailing spaces "}
+ ]
+ }
+ ]
+ expected_output = {"text": " Text with leading and trailing spaces \n"}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', return_value='{"text": ""}'):
+ response = telegram_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_telegram_paragraph_transformer_empty_message(self):
+ telegram_response_converter = TelegramResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="telegram")
+ message = []
+ expected_output = {"text": ""}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', return_value='{"text": ""}'):
+ response = telegram_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_telegram_paragraph_transformer_exception_handling(self):
+ telegram_response_converter = TelegramResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="telegram")
+ message = None
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', side_effect=Exception("Test Exception")):
+ with pytest.raises(Exception) as excinfo:
+ telegram_response_converter.paragraph_transformer(message)
+ assert "Error in TelegramResponseConverter::paragraph_transformer" in str(excinfo.value)
+
+
+ def test_messenger_client_paragraph_transformer_basic(self):
+ messenger_response_converter = MessengerResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="messenger")
+ message = [
+ {
+ "type": "paragraph",
+ "children": [
+ {"text": "This is a test paragraph."}
+ ]
+ }
+ ]
+ expected_output = {"text": "This is a test paragraph.\n"}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', return_value='{"text": ""}'):
+ response = messenger_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_messenger_client_paragraph_transformer_with_formatting(self):
+ messenger_response_converter = MessengerResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="messenger")
+ message = [
+ {
+ "type": "paragraph",
+ "children": [
+ {"text": "Bold text", "bold": True},
+ {"text": " and normal text."}
+ ]
+ }
+ ]
+ expected_output = {"text": "Bold text and normal text.\n"}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', return_value='{"text": ""}'):
+ response = messenger_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_messenger_client_paragraph_transformer_with_spaces(self):
+ messenger_response_converter = MessengerResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="messenger")
+ message = [
+ {
+ "type": "paragraph",
+ "children": [
+ {"text": " Text with leading and trailing spaces "}
+ ]
+ }
+ ]
+ expected_output = {"text": " Text with leading and trailing spaces \n"}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', return_value='{"text": ""}'):
+ response = messenger_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_messenger_client_paragraph_transformer_empty_message(self):
+ messenger_response_converter = MessengerResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="messenger")
+ message = []
+ expected_output = {"text": ""}
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', return_value='{"text": ""}'):
+ response = messenger_response_converter.paragraph_transformer(message)
+ assert response == expected_output
+
+ def test_messenger_client_paragraph_transformer_exception_handling(self):
+ messenger_response_converter = MessengerResponseConverter(message_type=ElementTypes.FORMAT_TEXT.value, channel_type="messenger")
+ message = None
+ with patch('kairon.chat.converters.channels.responseconverter.ElementTransformerOps.getChannelConfig', side_effect=Exception("Test Exception")):
+ with pytest.raises(Exception) as excinfo:
+ messenger_response_converter.paragraph_transformer(message)
+ assert "Error in MessengerResponseConverter::paragraph_transformer" in str(excinfo.value)
\ No newline at end of file