require 'google/cloud/dialogflow/v2' class Integrations::Dialogflow::ProcessorService < Integrations::BotProcessorService SUPPORTED_LANGUAGE_CODES = %w[ ar en-US en-GB es-ES es-419 fr-FR de-DE pt-BR pt-PT it-IT ja-JP ko-KR zh-CN zh-TW hi-IN ru-RU nl-NL pl-PL tr-TR th-TH vi-VN id-ID ].freeze AUTO_LANGUAGE_CODE_MAP = { 'ar' => 'ar', 'de' => 'de-DE', 'en' => 'en-US', 'es' => 'es-ES', 'fr' => 'fr-FR', 'hi' => 'hi-IN', 'id' => 'id-ID', 'it' => 'it-IT', 'ja' => 'ja-JP', 'ko' => 'ko-KR', 'nl' => 'nl-NL', 'pl' => 'pl-PL', 'pt' => 'pt-BR', 'ru' => 'ru-RU', 'th' => 'th-TH', 'tr' => 'tr-TR', 'vi' => 'vi-VN', 'zh' => 'zh-CN' }.freeze pattr_initialize [:event_name!, :hook!, :event_data!] private def message_content(message) # TODO: might needs to change this to a way that we fetch the updated value from event data instead # cause the message.updated event could be that that the message was deleted return message.content_attributes['submitted_values']&.first&.dig('value') if event_name == 'message.updated' message.content end def get_response(session_id, message_content) if hook.settings['credentials'].blank? Rails.logger.warn "Account: #{hook.try(:account_id)} Hook: #{hook.id} credentials are not present." && return end configure_dialogflow_client_defaults detect_intent(session_id, message_content) rescue Google::Cloud::PermissionDeniedError => e Rails.logger.warn "DialogFlow Error: (account-#{hook.try(:account_id)}, hook-#{hook.id}) #{e.message}" hook.prompt_reauthorization! hook.disable end def process_response(message, response) fulfillment_messages = response.query_result['fulfillment_messages'] fulfillment_messages.each do |fulfillment_message| content_params = generate_content_params(fulfillment_message) if content_params['action'].present? process_action(message, content_params['action']) else create_conversation(message, content_params) end end end def generate_content_params(fulfillment_message) text_response = fulfillment_message['text'].to_h content_params = { content: text_response[:text].first } if text_response[:text].present? content_params ||= fulfillment_message['payload'].to_h content_params end def create_conversation(message, content_params) return if content_params.blank? conversation = message.conversation conversation.messages.create!( content_params.merge( { message_type: :outgoing, account_id: conversation.account_id, inbox_id: conversation.inbox_id } ) ) end def configure_dialogflow_client_defaults ::Google::Cloud::Dialogflow::V2::Sessions::Client.configure do |config| config.timeout = 10.0 config.credentials = hook.settings['credentials'] config.endpoint = dialogflow_endpoint end end def normalized_region region = hook.settings['region'].to_s.strip (region.presence || 'global') end def dialogflow_endpoint region = normalized_region return 'dialogflow.googleapis.com' if region == 'global' "#{region}-dialogflow.googleapis.com" end def detect_intent(session_id, message) client = ::Google::Cloud::Dialogflow::V2::Sessions::Client.new session = build_session_path(session_id) query_input = { text: { text: message, language_code: dialogflow_language_code } } client.detect_intent session: session, query_input: query_input end def build_session_path(session_id) project_id = hook.settings['project_id'] region = normalized_region if region == 'global' "projects/#{project_id}/agent/sessions/#{session_id}" else "projects/#{project_id}/locations/#{region}/agent/sessions/#{session_id}" end end def dialogflow_language_code configured_language = hook.settings['language_code'].to_s.strip return 'en-US' if configured_language.blank? return configured_language if configured_language != 'auto' normalized_contact_language_code(conversation&.contact&.additional_attributes&.dig('language_code')) || 'en-US' end def normalized_contact_language_code(language_code) canonicalized_language_code = canonical_language_code(language_code) return if canonicalized_language_code.blank? return canonicalized_language_code if SUPPORTED_LANGUAGE_CODES.include?(canonicalized_language_code) AUTO_LANGUAGE_CODE_MAP[canonicalized_language_code] || AUTO_LANGUAGE_CODE_MAP[canonicalized_language_code.split('-', 2).first] end def canonical_language_code(language_code) normalized_language_code = language_code.to_s.tr('_', '-').strip return if normalized_language_code.blank? language, region = normalized_language_code.split('-', 2) return language.downcase if region.blank? "#{language.downcase}-#{region.upcase}" end end