class Whatsapp::SendOnWhatsappService < Base::SendOnChannelService private def channel_class Channel::Whatsapp end def perform_reply # can reply checks if 24 hour limit has passed. if message.conversation.can_reply? send_on_whatsapp else send_template_message end end def send_template_message name, namespace, lang_code, processed_parameters = processable_channel_message_template return if name.blank? message_id = channel.send_template(message.conversation.contact_inbox.source_id, { name: name, namespace: namespace, lang_code: lang_code, parameters: processed_parameters }) message.update!(source_id: message_id) if message_id.present? end def processable_channel_message_template # see if we can match the message content to a template # An example template may look like "Your package has been shipped. It will be delivered in {{1}} business days. # We want to iterate over these templates with our message body and see if we can fit it to any of the templates # Then we use regex to parse the template varibles and convert them into the proper payload channel.message_templates.each do |template| match_obj = template_match_object(template) next if match_obj.blank? # we have a match, now we need to parse the template variables and convert them into the wa recommended format processed_parameters = match_obj.captures.map { |x| { type: 'text', text: x } } # no need to look up further end the search return [template['name'], template['namespace'], template['language'], processed_parameters] end [nil, nil, nil, nil] end def template_match_object(template) body_object = validated_body_object(template) return if body_object.blank? template_match_regex = build_template_match_regex(body_object['text']) message.content.match(template_match_regex) end def build_template_match_regex(template_text) # Converts the whatsapp template to a comparable regex string to check against the message content # the variables are of the format {{num}} ex:{{1}} # transform the template text into a regex string # we need to replace the {{num}} with matchers that can be used to capture the variables template_text = template_text.gsub(/{{\d}}/, '(.*)') # escape if there are regex characters in the template text template_text = Regexp.escape(template_text) # ensuring only the variables remain as capture groups template_text = template_text.gsub(Regexp.escape('(.*)'), '(.*)') template_match_string = "^#{template_text}$" Regexp.new template_match_string end def validated_body_object(template) # we don't care if its not approved template return if template['status'] != 'approved' # we only care about text body object in template. if not present we discard the template # we don't support other forms of templates template['components'].find { |obj| obj['type'] == 'BODY' && obj.key?('text') } end def send_on_whatsapp message_id = channel.send_message(message.conversation.contact_inbox.source_id, message) message.update!(source_id: message_id) if message_id.present? end end