defmodule Glific.Dialogflow.Sessions do
@moduledoc """
Helper to help manage intents
"""
require Logger
alias Glific.{
Dialogflow,
Dialogflow.SessionWorker,
Flows.FlowContext,
GCS.GcsWorker,
Messages,
Messages.Message,
Repo
}
@doc """
Add message to queue worker to detect the intent
"""
@spec detect_intent(Message.t(), non_neg_integer, String.t()) :: :ok
def detect_intent(nil, _, _), do: :ok
def detect_intent(message, context_id, result_name) do
%{
path: message.session_uuid,
locale: message.contact.language.locale,
message: Message.to_minimal_map(message),
context_id: context_id,
result_name: result_name
}
|> SessionWorker.new()
|> Oban.insert()
:ok
end
@doc """
Function to communicate with dialogflow to detect the intent of the request
"""
@spec make_request(map(), String.t(), String.t(), Keyword.t()) :: :ok | {:error, :string}
def make_request(message, session_id, language \\ "en", opts) do
Repo.put_process_state(message.organization_id)
body = request_body(message, language)
Dialogflow.request(
message.organization_id,
:post,
"sessions/#{session_id}:detectIntent",
body
)
|> handle_response(opts[:context_id], opts[:result_name])
end
defp request_body(%{type: "text"} = message, language),
do: %{
queryInput: %{
text: %{
text: message.body,
languageCode: language
}
},
queryParams: %{
timeZone: "Asia/Calcutta",
sentimentAnalysisRequestConfig: %{
analyzeQueryTextSentiment: true
}
}
}
defp request_body(%{type: "audio"} = message, language),
do: %{
queryInput: %{
audioConfig: %{
audioEncoding: "AUDIO_ENCODING_OGG_OPUS",
sampleRateHertz: 16_000,
languageCode: language
}
},
inputAudio: format_audio_file(message)
}
defp request_body(_, _),
do: %{
queryInput: %{
text: %{
text: "Unknown",
languageCode: "en"
}
}
}
defp format_audio_file(message) do
# first retrieve the audio file as a string
tmp_file_name = System.tmp_dir!() <> "glific_msg_#{message.id}_media_#{message.media_id}.ogg"
GcsWorker.download_file_to_temp(
message.source_url,
tmp_file_name,
message.organization_id
)
# encode the file in base64
tmp_file_name
|> File.read!()
|> Base.encode64()
end
@spec handle_response(tuple(), non_neg_integer, String.t()) :: :ok | {:error, :string}
defp handle_response({:ok, response}, context_id, result_name) do
intent = get_in(response, ["queryResult", "intent", "displayName"])
context =
Repo.get!(FlowContext, context_id)
|> Repo.preload(:flow)
{context, message} =
if is_nil(intent) do
{
context,
Messages.create_temp_message(context.organization_id, "Failure")
}
else
# update the context with the results from webhook return values
confidence = get_in(response, ["queryResult", "intentDetectionConfidence"])
response = get_in(response, ["queryResult", "fulfillmentText"])
data = %{
intent: intent,
confidence: confidence,
response: response,
inserted_at: DateTime.utc_now()
}
{
FlowContext.update_results(context, %{result_name => data}),
Messages.create_temp_message(context.organization_id, "Success")
|> Map.put(:extra, data)
}
end
FlowContext.wakeup_one(context, message)
:ok
end
## we don't want to kill the flow in case intent doesn't match or something else goes wrong
defp handle_response(error, context_id, _) do
context = Repo.get!(FlowContext, context_id) |> Repo.preload(:flow)
Logger.error("Error while detecting intent:#{inspect(error)}")
message =
Messages.create_temp_message(context.organization_id, "Failure")
|> Map.put(:extra, %{
intent: "Unknown",
confidence: 0,
response: "#{inspect(error)}",
inserted_at: DateTime.utc_now()
})
FlowContext.wakeup_one(context, message)
{:error, error}
end
end