feat: notify assigned agent if whatsapp message is received
This commit is contained in:
parent
7b08359624
commit
ace509dd80
@ -21,14 +21,10 @@ def notify_mentions(doc):
|
||||
if doctype.startswith("CRM "):
|
||||
doctype = doctype[4:].lower()
|
||||
notification_text = f"""
|
||||
<div class="mb-2 space-x-1 leading-5 text-gray-600">
|
||||
<span class="font-medium text-gray-900">
|
||||
{ owner }
|
||||
</span>
|
||||
<div class="mb-2 leading-5 text-gray-600">
|
||||
<span class="font-medium text-gray-900">{ owner }</span>
|
||||
<span>{ _('mentioned you in {0}').format(doctype) }</span>
|
||||
<span class="font-medium text-gray-900">
|
||||
{ doc.reference_name }
|
||||
</span>
|
||||
<span class="font-medium text-gray-900">{ doc.reference_name }</span>
|
||||
</div>
|
||||
"""
|
||||
values = frappe._dict(
|
||||
|
||||
@ -1,224 +1,314 @@
|
||||
import frappe
|
||||
import json
|
||||
from frappe import _
|
||||
from crm.api.doc import get_assigned_users
|
||||
|
||||
|
||||
def validate(doc, method):
|
||||
if doc.type == "Incoming" and doc.get("from"):
|
||||
name, doctype = get_lead_or_deal_from_number(doc.get("from"))
|
||||
doc.reference_doctype = doctype
|
||||
doc.reference_name = name
|
||||
if doc.type == "Incoming" and doc.get("from"):
|
||||
name, doctype = get_lead_or_deal_from_number(doc.get("from"))
|
||||
doc.reference_doctype = doctype
|
||||
doc.reference_name = name
|
||||
|
||||
|
||||
def on_update(doc, method):
|
||||
frappe.publish_realtime("whatsapp_message", {
|
||||
'reference_doctype': doc.reference_doctype,
|
||||
'reference_name': doc.reference_name,
|
||||
})
|
||||
frappe.publish_realtime(
|
||||
"whatsapp_message",
|
||||
{
|
||||
"reference_doctype": doc.reference_doctype,
|
||||
"reference_name": doc.reference_name,
|
||||
},
|
||||
)
|
||||
|
||||
notify_agent(doc)
|
||||
|
||||
|
||||
def notify_agent(doc):
|
||||
if doc.type == "Incoming":
|
||||
doctype = doc.reference_doctype
|
||||
if doctype.startswith("CRM "):
|
||||
doctype = doctype[4:].lower()
|
||||
notification_text = f"""
|
||||
<div class="mb-2 leading-5 text-gray-600">
|
||||
<span class="font-medium text-gray-900">{ _('You') }</span>
|
||||
<span>{ _('received a whatsapp message in {0}').format(doctype) }</span>
|
||||
<span class="font-medium text-gray-900">{ doc.reference_name }</span>
|
||||
</div>
|
||||
"""
|
||||
assigned_users = get_assigned_users(doc.reference_doctype, doc.reference_name)
|
||||
for user in assigned_users:
|
||||
values = frappe._dict(
|
||||
doctype="CRM Notification",
|
||||
from_user=doc.owner,
|
||||
to_user=user,
|
||||
type="WhatsApp",
|
||||
message=doc.message,
|
||||
notification_text=notification_text,
|
||||
notification_type_doctype="WhatsApp Message",
|
||||
notification_type_doc=doc.name,
|
||||
reference_doctype=doc.reference_doctype,
|
||||
reference_name=doc.reference_name,
|
||||
)
|
||||
|
||||
if frappe.db.exists("CRM Notification", values):
|
||||
return
|
||||
frappe.get_doc(values).insert(ignore_permissions=True)
|
||||
|
||||
|
||||
def get_lead_or_deal_from_number(number):
|
||||
"""Get lead/deal from the given number.
|
||||
"""
|
||||
"""Get lead/deal from the given number."""
|
||||
|
||||
def find_record(doctype, mobile_no, where=''):
|
||||
mobile_no = parse_mobile_no(mobile_no)
|
||||
|
||||
query = f"""
|
||||
def find_record(doctype, mobile_no, where=""):
|
||||
mobile_no = parse_mobile_no(mobile_no)
|
||||
|
||||
query = f"""
|
||||
SELECT name, mobile_no
|
||||
FROM `tab{doctype}`
|
||||
WHERE CONCAT('+', REGEXP_REPLACE(mobile_no, '[^0-9]', '')) = {mobile_no}
|
||||
"""
|
||||
|
||||
data = frappe.db.sql(query + where, as_dict=True)
|
||||
return data[0].name if data else None
|
||||
data = frappe.db.sql(query + where, as_dict=True)
|
||||
return data[0].name if data else None
|
||||
|
||||
doctype = "CRM Deal"
|
||||
doctype = "CRM Deal"
|
||||
|
||||
doc = find_record(doctype, number) or None
|
||||
if not doc:
|
||||
doctype = "CRM Lead"
|
||||
doc = find_record(doctype, number, 'AND converted is not True')
|
||||
if not doc:
|
||||
doc = find_record(doctype, number)
|
||||
doc = find_record(doctype, number) or None
|
||||
if not doc:
|
||||
doctype = "CRM Lead"
|
||||
doc = find_record(doctype, number, "AND converted is not True")
|
||||
if not doc:
|
||||
doc = find_record(doctype, number)
|
||||
|
||||
return doc, doctype
|
||||
|
||||
return doc, doctype
|
||||
|
||||
def parse_mobile_no(mobile_no: str):
|
||||
"""Parse mobile number to remove spaces, brackets, etc.
|
||||
>>> parse_mobile_no('+91 (766) 667 6666')
|
||||
... '+917666676666'
|
||||
"""
|
||||
return ''.join([c for c in mobile_no if c.isdigit() or c == '+'])
|
||||
"""Parse mobile number to remove spaces, brackets, etc.
|
||||
>>> parse_mobile_no('+91 (766) 667 6666')
|
||||
... '+917666676666'
|
||||
"""
|
||||
return "".join([c for c in mobile_no if c.isdigit() or c == "+"])
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def is_whatsapp_enabled():
|
||||
if not frappe.db.exists('DocType', 'WhatsApp Settings'):
|
||||
return False
|
||||
return frappe.get_cached_value('WhatsApp Settings', 'WhatsApp Settings', 'enabled')
|
||||
if not frappe.db.exists("DocType", "WhatsApp Settings"):
|
||||
return False
|
||||
return frappe.get_cached_value("WhatsApp Settings", "WhatsApp Settings", "enabled")
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_whatsapp_messages(reference_doctype, reference_name):
|
||||
if not frappe.db.exists('DocType', 'WhatsApp Message'):
|
||||
return []
|
||||
messages = frappe.get_all(
|
||||
"WhatsApp Message",
|
||||
filters={
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name
|
||||
},
|
||||
fields=[
|
||||
'name',
|
||||
'type',
|
||||
'to',
|
||||
'from',
|
||||
'content_type',
|
||||
'message_type',
|
||||
'attach',
|
||||
'template',
|
||||
'use_template',
|
||||
'message_id',
|
||||
'is_reply',
|
||||
'reply_to_message_id',
|
||||
'creation',
|
||||
'message',
|
||||
'status',
|
||||
'reference_doctype',
|
||||
'reference_name',
|
||||
'template_parameters',
|
||||
'template_header_parameters',
|
||||
],
|
||||
)
|
||||
if not frappe.db.exists("DocType", "WhatsApp Message"):
|
||||
return []
|
||||
messages = frappe.get_all(
|
||||
"WhatsApp Message",
|
||||
filters={
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name,
|
||||
},
|
||||
fields=[
|
||||
"name",
|
||||
"type",
|
||||
"to",
|
||||
"from",
|
||||
"content_type",
|
||||
"message_type",
|
||||
"attach",
|
||||
"template",
|
||||
"use_template",
|
||||
"message_id",
|
||||
"is_reply",
|
||||
"reply_to_message_id",
|
||||
"creation",
|
||||
"message",
|
||||
"status",
|
||||
"reference_doctype",
|
||||
"reference_name",
|
||||
"template_parameters",
|
||||
"template_header_parameters",
|
||||
],
|
||||
)
|
||||
|
||||
# Filter messages to get only Template messages
|
||||
template_messages = [message for message in messages if message['message_type'] == 'Template']
|
||||
# Filter messages to get only Template messages
|
||||
template_messages = [
|
||||
message for message in messages if message["message_type"] == "Template"
|
||||
]
|
||||
|
||||
# Iterate through template messages
|
||||
for template_message in template_messages:
|
||||
# Find the template that this message is using
|
||||
template = frappe.get_doc("WhatsApp Templates", template_message['template'])
|
||||
|
||||
# If the template is found, add the template details to the template message
|
||||
if template:
|
||||
template_message['template_name'] = template.template_name
|
||||
if template_message['template_parameters']:
|
||||
parameters = json.loads(template_message['template_parameters'])
|
||||
template.template = parse_template_parameters(template.template, parameters)
|
||||
# Iterate through template messages
|
||||
for template_message in template_messages:
|
||||
# Find the template that this message is using
|
||||
template = frappe.get_doc("WhatsApp Templates", template_message["template"])
|
||||
|
||||
template_message['template'] = template.template
|
||||
if template_message['template_header_parameters']:
|
||||
header_parameters = json.loads(template_message['template_header_parameters'])
|
||||
template.header = parse_template_parameters(template.header, header_parameters)
|
||||
template_message['header'] = template.header
|
||||
template_message['footer'] = template.footer
|
||||
# If the template is found, add the template details to the template message
|
||||
if template:
|
||||
template_message["template_name"] = template.template_name
|
||||
if template_message["template_parameters"]:
|
||||
parameters = json.loads(template_message["template_parameters"])
|
||||
template.template = parse_template_parameters(
|
||||
template.template, parameters
|
||||
)
|
||||
|
||||
# Filter messages to get only reaction messages
|
||||
reaction_messages = [message for message in messages if message['content_type'] == 'reaction']
|
||||
template_message["template"] = template.template
|
||||
if template_message["template_header_parameters"]:
|
||||
header_parameters = json.loads(
|
||||
template_message["template_header_parameters"]
|
||||
)
|
||||
template.header = parse_template_parameters(
|
||||
template.header, header_parameters
|
||||
)
|
||||
template_message["header"] = template.header
|
||||
template_message["footer"] = template.footer
|
||||
|
||||
# Iterate through reaction messages
|
||||
for reaction_message in reaction_messages:
|
||||
# Find the message that this reaction is reacting to
|
||||
reacted_message = next((m for m in messages if m['message_id'] == reaction_message['reply_to_message_id']), None)
|
||||
|
||||
# If the reacted message is found, add the reaction to it
|
||||
if reacted_message:
|
||||
reacted_message['reaction'] = reaction_message['message']
|
||||
# Filter messages to get only reaction messages
|
||||
reaction_messages = [
|
||||
message for message in messages if message["content_type"] == "reaction"
|
||||
]
|
||||
|
||||
for message in messages:
|
||||
from_name = get_from_name(message) if message['from'] else _('You')
|
||||
message['from_name'] = from_name
|
||||
# Filter messages to get only replies
|
||||
reply_messages = [message for message in messages if message['is_reply']]
|
||||
# Iterate through reaction messages
|
||||
for reaction_message in reaction_messages:
|
||||
# Find the message that this reaction is reacting to
|
||||
reacted_message = next(
|
||||
(
|
||||
m
|
||||
for m in messages
|
||||
if m["message_id"] == reaction_message["reply_to_message_id"]
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
# Iterate through reply messages
|
||||
for reply_message in reply_messages:
|
||||
# Find the message that this message is replying to
|
||||
replied_message = next((m for m in messages if m['message_id'] == reply_message['reply_to_message_id']), None)
|
||||
|
||||
# If the replied message is found, add the reply details to the reply message
|
||||
from_name = get_from_name(reply_message) if replied_message['from'] else _('You')
|
||||
if replied_message:
|
||||
message = replied_message['message']
|
||||
if replied_message['message_type'] == 'Template':
|
||||
message = replied_message['template']
|
||||
reply_message['reply_message'] = message
|
||||
reply_message['header'] = replied_message.get('header') or ''
|
||||
reply_message['footer'] = replied_message.get('footer') or ''
|
||||
reply_message['reply_to'] = replied_message['name']
|
||||
reply_message['reply_to_type'] = replied_message['type']
|
||||
reply_message['reply_to_from'] = from_name
|
||||
# If the reacted message is found, add the reaction to it
|
||||
if reacted_message:
|
||||
reacted_message["reaction"] = reaction_message["message"]
|
||||
|
||||
for message in messages:
|
||||
from_name = get_from_name(message) if message["from"] else _("You")
|
||||
message["from_name"] = from_name
|
||||
# Filter messages to get only replies
|
||||
reply_messages = [message for message in messages if message["is_reply"]]
|
||||
|
||||
# Iterate through reply messages
|
||||
for reply_message in reply_messages:
|
||||
# Find the message that this message is replying to
|
||||
replied_message = next(
|
||||
(
|
||||
m
|
||||
for m in messages
|
||||
if m["message_id"] == reply_message["reply_to_message_id"]
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
# If the replied message is found, add the reply details to the reply message
|
||||
from_name = (
|
||||
get_from_name(reply_message) if replied_message["from"] else _("You")
|
||||
)
|
||||
if replied_message:
|
||||
message = replied_message["message"]
|
||||
if replied_message["message_type"] == "Template":
|
||||
message = replied_message["template"]
|
||||
reply_message["reply_message"] = message
|
||||
reply_message["header"] = replied_message.get("header") or ""
|
||||
reply_message["footer"] = replied_message.get("footer") or ""
|
||||
reply_message["reply_to"] = replied_message["name"]
|
||||
reply_message["reply_to_type"] = replied_message["type"]
|
||||
reply_message["reply_to_from"] = from_name
|
||||
|
||||
return [message for message in messages if message["content_type"] != "reaction"]
|
||||
|
||||
return [message for message in messages if message['content_type'] != 'reaction']
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_whatsapp_message(reference_doctype, reference_name, message, to, attach, reply_to, content_type="text"):
|
||||
doc = frappe.new_doc("WhatsApp Message")
|
||||
def create_whatsapp_message(
|
||||
reference_doctype,
|
||||
reference_name,
|
||||
message,
|
||||
to,
|
||||
attach,
|
||||
reply_to,
|
||||
content_type="text",
|
||||
):
|
||||
doc = frappe.new_doc("WhatsApp Message")
|
||||
|
||||
if reply_to:
|
||||
reply_doc = frappe.get_doc("WhatsApp Message", reply_to)
|
||||
doc.update({
|
||||
"is_reply": True,
|
||||
"reply_to_message_id": reply_doc.message_id,
|
||||
})
|
||||
if reply_to:
|
||||
reply_doc = frappe.get_doc("WhatsApp Message", reply_to)
|
||||
doc.update(
|
||||
{
|
||||
"is_reply": True,
|
||||
"reply_to_message_id": reply_doc.message_id,
|
||||
}
|
||||
)
|
||||
|
||||
doc.update(
|
||||
{
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name,
|
||||
"message": message or attach,
|
||||
"to": to,
|
||||
"attach": attach,
|
||||
"content_type": content_type,
|
||||
}
|
||||
)
|
||||
doc.insert(ignore_permissions=True)
|
||||
return doc.name
|
||||
|
||||
doc.update({
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name,
|
||||
"message": message or attach,
|
||||
"to": to,
|
||||
"attach": attach,
|
||||
"content_type": content_type,
|
||||
})
|
||||
doc.insert(ignore_permissions=True)
|
||||
return doc.name
|
||||
|
||||
@frappe.whitelist()
|
||||
def send_whatsapp_template(reference_doctype, reference_name, template, to):
|
||||
doc = frappe.new_doc("WhatsApp Message")
|
||||
doc.update({
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name,
|
||||
"message_type": "Template",
|
||||
"message": "Template message",
|
||||
"content_type": "text",
|
||||
"use_template": True,
|
||||
"template": template,
|
||||
"to": to,
|
||||
})
|
||||
doc.insert(ignore_permissions=True)
|
||||
return doc.name
|
||||
doc = frappe.new_doc("WhatsApp Message")
|
||||
doc.update(
|
||||
{
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name,
|
||||
"message_type": "Template",
|
||||
"message": "Template message",
|
||||
"content_type": "text",
|
||||
"use_template": True,
|
||||
"template": template,
|
||||
"to": to,
|
||||
}
|
||||
)
|
||||
doc.insert(ignore_permissions=True)
|
||||
return doc.name
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def react_on_whatsapp_message(emoji, reply_to_name):
|
||||
reply_to_doc = frappe.get_doc("WhatsApp Message", reply_to_name)
|
||||
to = reply_to_doc.type == "Incoming" and reply_to_doc.get("from") or reply_to_doc.to
|
||||
doc = frappe.new_doc("WhatsApp Message")
|
||||
doc.update({
|
||||
"reference_doctype": reply_to_doc.reference_doctype,
|
||||
"reference_name": reply_to_doc.reference_name,
|
||||
"message": emoji,
|
||||
"to": to,
|
||||
"reply_to_message_id": reply_to_doc.message_id,
|
||||
"content_type": "reaction",
|
||||
})
|
||||
doc.insert(ignore_permissions=True)
|
||||
return doc.name
|
||||
reply_to_doc = frappe.get_doc("WhatsApp Message", reply_to_name)
|
||||
to = reply_to_doc.type == "Incoming" and reply_to_doc.get("from") or reply_to_doc.to
|
||||
doc = frappe.new_doc("WhatsApp Message")
|
||||
doc.update(
|
||||
{
|
||||
"reference_doctype": reply_to_doc.reference_doctype,
|
||||
"reference_name": reply_to_doc.reference_name,
|
||||
"message": emoji,
|
||||
"to": to,
|
||||
"reply_to_message_id": reply_to_doc.message_id,
|
||||
"content_type": "reaction",
|
||||
}
|
||||
)
|
||||
doc.insert(ignore_permissions=True)
|
||||
return doc.name
|
||||
|
||||
|
||||
def parse_template_parameters(string, parameters):
|
||||
for i, parameter in enumerate(parameters, start=1):
|
||||
placeholder = "{{" + str(i) + "}}"
|
||||
string = string.replace(placeholder, parameter)
|
||||
for i, parameter in enumerate(parameters, start=1):
|
||||
placeholder = "{{" + str(i) + "}}"
|
||||
string = string.replace(placeholder, parameter)
|
||||
|
||||
return string
|
||||
|
||||
return string
|
||||
|
||||
def get_from_name(message):
|
||||
doc = frappe.get_doc(message['reference_doctype'], message['reference_name'])
|
||||
from_name = ''
|
||||
if message['reference_doctype'] == "CRM Deal":
|
||||
if doc.get("contacts"):
|
||||
for c in doc.get("contacts"):
|
||||
if c.is_primary:
|
||||
from_name = c.full_name or c.mobile_no
|
||||
break
|
||||
else:
|
||||
from_name = doc.get("lead_name")
|
||||
else:
|
||||
from_name = doc.get("first_name") + " " + doc.get("last_name")
|
||||
return from_name
|
||||
doc = frappe.get_doc(message["reference_doctype"], message["reference_name"])
|
||||
from_name = ""
|
||||
if message["reference_doctype"] == "CRM Deal":
|
||||
if doc.get("contacts"):
|
||||
for c in doc.get("contacts"):
|
||||
if c.is_primary:
|
||||
from_name = c.full_name or c.mobile_no
|
||||
break
|
||||
else:
|
||||
from_name = doc.get("lead_name")
|
||||
else:
|
||||
from_name = doc.get("first_name") + " " + doc.get("last_name")
|
||||
return from_name
|
||||
|
||||
@ -69,6 +69,7 @@
|
||||
{
|
||||
"fieldname": "message",
|
||||
"fieldtype": "HTML Editor",
|
||||
"in_list_view": 1,
|
||||
"label": "Message"
|
||||
},
|
||||
{
|
||||
@ -115,7 +116,7 @@
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-04-25 13:55:23.014268",
|
||||
"modified": "2024-04-25 16:26:07.484857",
|
||||
"modified_by": "Administrator",
|
||||
"module": "FCRM",
|
||||
"name": "CRM Notification",
|
||||
|
||||
@ -55,7 +55,11 @@
|
||||
class="h-[5px] w-[5px] rounded-full"
|
||||
:class="[n.read ? 'bg-transparent' : 'bg-gray-900']"
|
||||
/>
|
||||
<UserAvatar :user="n.from_user.name" size="lg" />
|
||||
<WhatsAppIcon
|
||||
v-if="n.type == 'WhatsApp'"
|
||||
class="size-7 rounded-full"
|
||||
/>
|
||||
<UserAvatar v-else :user="n.from_user.name" size="lg" />
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="n.notification_text" v-html="n.notification_text" />
|
||||
@ -89,6 +93,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
|
||||
import MarkAsDoneIcon from '@/components/Icons/MarkAsDoneIcon.vue'
|
||||
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user