defmodule Client.Custom do @moduledoc """ A Microsoft Directline Client """ alias Direxion.{Config, Util} @behaviour Direxion.ClientBehaviour @doc """ Get the authorization token for bots request Returns a tuple containing the status of request and the conversation ID """ @spec get_authorization_token() :: tuple() def get_authorization_token() do url = "https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token" headers = [{"Content-Type", "application/x-www-form-urlencoded"}] msa_credentials = Config.get_msa_credentials() client_id = Map.get(msa_credentials, :client_id) params = msa_credentials |> Map.put("grant_type", "client_credentials") |> Map.put("scope", "#{client_id}/.default") |> Map.put("atver", 1) |> URI.encode_query() case HTTPoison.post(url, params, headers) do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> access_token = body |> Poison.decode!() |> Map.get("access_token") {:ok, access_token} {:ok, _} -> {:error, "Error Getting Authorization Token"} {:error, %HTTPoison.Error{reason: reason}} -> {:error, reason} end end @doc """ Send an activity to a bot Parameters: * access_token - Microsoft bot authorization token * activity - The activity we want reponse to send Returns the assigned activity id. """ @spec send_activity_to_bot(String.t(), map()) :: tuple() def send_activity_to_bot(access_token, activity) do request_endpoint = Config.get_bot_request_endpoint() headers = Util.generate_request_header(access_token) activity_id = Util.generate_uuid() modified_activity = Util.modify_activity(activity, activity_id) case HTTPoison.post(request_endpoint, modified_activity, headers) do {:ok, %HTTPoison.Response{status_code: 200, body: _}} -> {:ok, activity_id} {:ok, _} -> {:error, "Error Sending Activity"} {:error, %HTTPoison.Error{reason: reason}} -> {:error, reason} end end @doc """ Receive the response from a bot response service. Parameters: * activity_id - The Activity ID we want reponse to Returns a Response object. """ @spec receive_response(String.t()) :: tuple() def receive_response(activity_id) do response_endpoint = Config.get_bot_response_endpoint() case HTTPoison.get("#{response_endpoint}/activity/#{activity_id}") do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> {:ok, Poison.decode!(body)} {:ok, _} -> {:error, "Error Receiving Activities"} {:error, %HTTPoison.Error{reason: reason}} -> {:error, reason} end end @doc """ Send an activity through our response handler the response is the Directline reponse. Parameters: * input_token - Directline Token to be used, Will be overridden by `DIRECTLINE_TOKEN` in path if found * activity - The activity to send Returns a Directline Response """ @spec message(map()) :: map() def message(activity) do timeout = Config.get_request_timeout() with {:ok, access_token} <- Task.await( Task.async(fn -> get_authorization_token() end), timeout ), response <- message_bot(access_token, activity) do response end end @doc """ Send an activity to a conversation and return its the Directline reponse. Parameters: * access_token - Microsoft Bot framework access token * activity - The activity to send Returns the bot Response """ @spec message_bot(String.t(), map()) :: map() def message_bot(access_token, activity) do timeout = Config.get_request_timeout() with {:ok, activity_id} <- Task.await( Task.async(fn -> send_activity_to_bot(access_token, activity) end), timeout ), _ <- Process.sleep(1000), {:ok, response} <- Task.await( Task.async(fn -> receive_response(activity_id) end), timeout ) do response end end end