Packages

An unofficial Elixir client for Whatsapp Cloud API.

Current section

Files

Jump to
whatsapp_cloud_api_wrapper lib whatsapp_cloud_api_wrapper.ex
Raw

lib/whatsapp_cloud_api_wrapper.ex

defmodule WhatsappCloudApiWrapper do
@moduledoc """
Documentation for `WhatsappCloudApiWrapper`.
"""
@doc """
Hello world.
## Examples
iex> WhatsappCloudApiWrapper.hello()
:world
"""
# Todo: comment it. Use docs.
@default_headers %{
"Content-Type": "application/json",
"Accept-Language": "en_US",
Accept: "application/json"
}
defp get_base_url(%{"version" => version} = params) do
SENDER_PHONE_NUMBER = Application.get_env(:whatsapp_cloud_api_wrapper, "META_WA_SENDER_PHONE_NUMBER_ID")
"https://graph.facebook.com/#{version}/#{SENDER_PHONE_NUMBER}/messages"
end
defp get_base_url do
SENDER_PHONE_NUMBER = Application.get_env(:whatsapp_cloud_api_wrapper, "META_WA_SENDER_PHONE_NUMBER_ID")
"https://graph.facebook.com/v18.0/#{SENDER_PHONE_NUMBER}/messages"
end
defp blank_default(val, default_val) do
if val == nil, do: default_val, else: val
end
defp validate_and_format_interactive_msg_buttons_list(btn_list) do
is_valid = true
error_msg = nil
formatted_list = []
Enum.each(btn_list, fn btn ->
%{"title" => title, "id" => id} = btn
if !title || length(title) > 20 || !id || length(id) > 256 do
is_valid = false
if !title do
error_msg = "title is missing."
end
if length(title) > 20 do
error_msg = "The button title must be between 1 and 20 characters long."
end
if !id do
error_msg = "id is missing"
end
if length(id) > 256 do
error_msg = "The button id must be between 1 and 256 characters long."
end
end
if is_valid do
List.insert_at(formatted_list, 0, %{
"type" => "reply",
"reply" => %{"title" => title, "id" => id}
})
end
end)
if is_valid do
{:ok, formatted_list}
else
{:error, error_msg}
end
end
defp validate_and_format_interactive_msg_sections_list(section_list) do
is_valid = true
error_msg = nil
Enum.each(section_list, fn %{"title" => section_title, "rows" => rows} = section ->
if !section_title do
error_msg = "title of a section is required in list of radio buttons"
end
if length(rows) > 10 do
error_msg = "The number of items in the rows must be equal or less than 10."
end
Enum.each(rows, fn %{"id" => id, "title" => title, "description" => description} ->
if !title || !id || !description || length(title) > 24 || length(id) > 200 || !description ||
length(description) > 72 do
is_valid = false
if !title do
error_msg = "row title is missing."
end
if !id do
error_msg = "row id is missing"
end
if !description do
error_msg = "row description is missing"
end
if length(title) > 24 do
error_msg = "The row title must be between 1 and 24 characters long."
end
if length(id) > 256 do
error_msg = "The row id must be between 1 and 200 characters long."
end
if length(description) > 72 do
error_msg = "The row description must be between 1 and 72 characters long."
end
end
end)
end)
if is_valid do
{:ok, section_list}
else
{:error, error_msg}
end
end
# defp upload_media(%{file_path, file_name} = params) do
# # todo
# end
def mark_msg_as_read(msg_id) do
if msg_id != nil do
Req.post!(
url: get_base_url(),
headers: @default_headers,
json: %{
"messaging_product" => "whatsapp",
"status" => "read",
"message_id" => msg_id
},
auth: {:bearer, Application.get_env(:whatsapp_cloud_api_wrapper, "META_WA_SENDER_PHONE_NUMBER_ID")}
)
end
end
def send_req(%{"to" => to} = params) do
Req.post!(
url: get_base_url(),
headers: @default_headers,
json:
Map.merge(
%{
"messaging_product" => "whatsapp",
"recipient_type" => "individual",
"to" => to
},
params
),
auth: {:bearer, Application.get_env(:whatsapp_cloud_api_wrapper, "META_WA_SENDER_PHONE_NUMBER_ID")}
)
end
def send_text_msg(%{"message" => message, "recipientPhone" => recipient_phone} = params) do
send_req(Map.put(params, "text", %{"body" => message, "preview_url" => false}))
end
def send_template_msg(%{"templateName" => name, "templateComponents" => components} = params) do
req_body = %{
"type" => "template",
"template" => %{
"name" => name,
"language" => %{
"code" => "en_US"
},
"components" => components
}
}
send_req(req_body)
end
def send_interactive_msg(%{"message" => message, "listOfButtons" => listOfButtons} = params) do
case validate_and_format_interactive_msg_buttons_list(listOfButtons) do
{:ok, btn_list} ->
send_req(
Map.merge(
params,
%{
"type" => "interactive",
"interactive" => %{
"type" => "button",
"body" => %{"text" => message},
"action" => %{"buttons" => btn_list}
}
}
)
)
{:error, error} ->
# todo: handle error better
raise error
_ ->
# todo: handle error better
raise ""
end
end
def send_interactive_msg(
%{
"listOfSections" => listOfSections,
"headerText" => headerText,
"bodyText" => bodyText,
"footerText" => footerText,
# Todo: with check if this works with default text as "Select"
"actionBtnText" => actionBtnText
} = params
) do
case validate_and_format_interactive_msg_sections_list(listOfSections) do
{:ok, section_list} ->
send_req(
Map.merge(
params,
%{
"type" => "interactive",
"interactive" => %{
"type" => "list",
"header" => %{
"type" => "text",
"text" => headerText
},
"body" => %{"text" => bodyText},
"footer" => %{"text" => footerText},
"action" => %{
"button" => blank_default(actionBtnText, "Select"),
"section" => section_list
}
}
}
)
)
{:error, error} ->
# todo: handle error better
raise error
_ ->
# todo: handle error better
raise ""
end
end
# with url instead of file_path
def send_media_msg(
%{
"recipientPhone" => recipientPhone,
"caption" => caption,
"fileName" => fileName,
"url" => url,
"type" => type
} = params
) do
req_body = %{
"type" => type,
type => %{
"caption" => caption,
"link" => url
}
}
send_req(req_body)
end
# with filePath instead of url
def send_media_msg(
%{
"recipientPhone" => recipientPhone,
"caption" => caption,
"filePath" => filePath,
"fileName" => fileName
} = params
) do
# todo:
end
def send_location_msg() do
# todo
end
def send_contact() do
# todo
end
def send_message(%{"type" => type, "recipientPhone" => recipientPhone} = params) do
case type do
"text" ->
send_text_msg(params)
"template" ->
send_template_msg(params)
"radio_buttons" ->
send_interactive_msg(params)
"simple_buttons" ->
send_interactive_msg(params)
"image" ->
send_media_msg(params)
"video" ->
send_media_msg(params)
"audio" ->
send_media_msg(params)
"contacts" ->
send_contact()
"location" ->
send_location_msg()
_ ->
raise "no type matched."
end
end
def send_message(_) do
# todo: missing params like message, recipient_phone, type etc.; raise and handle gracefully.
end
end