├── .formatter.exs ├── .gitignore ├── README.md ├── config └── config.exs ├── lib ├── onesignal_elixir.ex └── onesignal_elixir │ ├── api.ex │ ├── builder.ex │ ├── filter.ex │ ├── notification.ex │ └── util.ex ├── mix.exs ├── mix.lock └── test ├── onesignal_elixir ├── builder_test.exs └── filter_test.exs ├── onesignal_elixir_test.exs └── test_helper.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | /.elixir_ls/ 4 | 5 | # If you run "mix test --cover", coverage assets end up here. 6 | /cover/ 7 | 8 | # The directory Mix downloads your dependencies sources to. 9 | /deps/ 10 | 11 | # Where 3rd-party dependencies like ExDoc output generated docs. 12 | /doc/ 13 | 14 | # Ignore .fetch files in case you like to edit your project deps locally. 15 | /.fetch 16 | 17 | # If the VM crashes, it generates a dump, let's ignore it too. 18 | erl_crash.dump 19 | 20 | # Also ignore archive artifacts (built via "mix archive.build"). 21 | *.ez 22 | 23 | # Ignore package tarball (built via "mix hex.build"). 24 | onesignal_elixir-*.tar 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OnesignalElixir 2 | 3 | This project is a wrapper for using the awesome Onesignal APIs. 4 | Currently supports the following Onesignal APIs 5 | Create Notification 6 | 7 | ## Installation 8 | 9 | The package is [available in Hex](https://hexdocs.pm/onesignal_elixir), and can be installed 10 | by adding `onesignal_elixir` to your list of dependencies in `mix.exs`: 11 | 12 | ```elixir 13 | def deps do 14 | [ 15 | {:onesignal_elixir, "~> 0.1.5"} 16 | ] 17 | end 18 | ``` 19 | Config 20 | ```elixir 21 | config :onesignal_elixir, 22 | rest_api_key: "YOUR-ONESIGNAL-REST-API-KEY", 23 | app_id: "YOUR-ONESIGNAL-APP-ID" 24 | ``` 25 | Usage Examples 26 | ```elixir 27 | iex(1)> alias OnesignalElixir.{Notification,Builder,Filter} 28 | [OnesignalElixir.Notification, OnesignalElixir.Builder, OnesignalElixir.Filter] 29 | 30 | iex(2)> filters = Filter.new() |> Filter.last_session(">","1.2") |> Filter.add_operator("AND") |> Filter.tag("exists", "email") 31 | [ %{field: "last_session", hours_ago: "1.2", relation: ">"}, 32 | %{operator: "AND"}, 33 | %{field: "tag", key: "email", relation: "exists"} 34 | ] 35 | iex(3)> body = OnesignalElixir.new() |> Builder.add_content(:en, "Welcome to One Signal") |> Builder.add_content(:es, "Bienvenido a One Signal")|> Builder.add_heading(:en, "Hello") |> Builder.add_heading(:es, "Hola") |> Builder.add_subtitle(:en, "Welcome") |> Builder.add_subtitle(:es, "Bienvenido") |> Builder.add_filters(filters) 36 | %{ 37 | __struct__: OnesignalElixir.Notification, 38 | app_id: "YOUR-ONESIGNAL-APP-ID", 39 | contents: %{en: "Welcome to One Signal", es: "Bienvenido a One Signal"}, 40 | filters: [ 41 | %{field: "last_session", hours_ago: "1.2", relation: ">"}, 42 | %{operator: "AND"}, 43 | %{field: "tag", key: "email", relation: "exists"} 44 | ], 45 | headings: %{en: "Hello", es: "Hola"}, 46 | subtitles: %{en: "Welcome", es: "Bienvenido"} 47 | } 48 | iex(4)> OnesignalElixir.send_notification(body) 49 | {:ok, %{ 50 | "external_id" => nil, 51 | "id" => "GENERATED_NOTIFICATION_ID", 52 | "recipients" => 6 53 | }} 54 | 55 | iex(5)> body = OnesignalElixir.new() |> Builder.add_content(:en, "Welcome to One Signal") |> Builder.add_content(:es, "Bienvenido a One Signal")|> Builder.add_heading(:en, "Hello") |> Builder.add_heading(:es, "Hola") |> Builder.add_subtitle(:en, "Welcome") |> Builder.add_subtitle(:es, "Bienvenido") |> Builder.include_segment("Active Users") 56 | %{ 57 | __struct__: OnesignalElixir.Notification, 58 | app_id: "YOUR-ONESIGNAL-APP-ID", 59 | contents: %{en: "Welcome to One Signal", es: "Bienvenido a One Signal"}, 60 | headings: %{en: "Hello", es: "Hola"}, 61 | included_segments: ["Active Users"], 62 | subtitles: %{en: "Welcome", es: "Bienvenido"} 63 | } 64 | 65 | iex(6)> OnesignalElixir.send_notification(body) 66 | {:ok, 67 | %{ 68 | "external_id" => nil, 69 | "id" => "GENERATED_NOTIFICATION_ID", 70 | "recipients" => 5 71 | }} 72 | 73 | iex(2)> body = OnesignalElixir.new() |> Builder.include_player_ids(["6e1a1b95-47e9-4978-b2f2-e2e42207e7b5"]) |> Builder.add_heading(:en, "Hello") |> Builder.add_content(:en, "This is the text content") 74 | %{ 75 | __struct__: OnesignalElixir.Notification, 76 | app_id: "a9ec9a20-d595-47b6-8efa-be94ecd4abaa", 77 | contents: %{en: "This is the text content"}, 78 | headings: %{en: "Hello"}, 79 | include_player_ids: ["6e1a1b95-47e9-4978-b2f2-e2e42207e7b5"] 80 | } 81 | 82 | iex(3)> OnesignalElixir.send_notification(body) 83 | {:ok, 84 | %{ 85 | "external_id" => nil, 86 | "id" => "f796da55-2d8b-4abf-afa5-86c98c9f185b", 87 | "recipients" => 1 88 | }} 89 | 90 | iex(4)> body = OnesignalElixir.new() |> Builder.include_external_user_ids(["xxxxx@example.com"]) |> Builder.add_heading(:en, "Hello") |> Builder.add_content(:en, "This is the text content") 91 | %{ 92 | __struct__: OnesignalElixir.Notification, 93 | app_id: "a9ec9a20-d595-47b6-8efa-be94ecd4abaa", 94 | contents: %{en: "This is the text content"}, 95 | headings: %{en: "Hello"}, 96 | include_external_user_ids: ["xxxxx@example.com"] 97 | } 98 | 99 | iex(5)> OnesignalElixir.send_notification(body) 100 | {:ok, 101 | %{ 102 | "external_id" => nil, 103 | "id" => "0c49f257-d955-428a-849c-7d9768b210cf", 104 | "recipients" => 1 105 | }} 106 | ``` 107 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) 108 | and published on [HexDocs](https://hexdocs.pm). Once published, the docs can 109 | be found at [https://hexdocs.pm/onesignal_elixir](https://hexdocs.pm/onesignal_elixir). 110 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure your application as: 12 | # 13 | # config :onesignal_elixir, key: :value 14 | # 15 | # and access this configuration in your application as: 16 | # 17 | # Application.get_env(:onesignal_elixir, :key) 18 | # 19 | # You can also configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | # import_config "#{Mix.env}.exs" 31 | -------------------------------------------------------------------------------- /lib/onesignal_elixir.ex: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixir do 2 | @moduledoc """ 3 | Documentation for OnesignalElixir. 4 | 5 | ## Configuration 6 | def deps do 7 | [ 8 | {:onesignal_elixir, "~> 0.1.0"} 9 | ] 10 | end 11 | 12 | config :onesignal_elixir, 13 | rest_api_key: "YOUR-ONESIGNAL-REST-API-KEY", 14 | app_id: "YOUR-ONESIGNAL-APP-ID" 15 | 16 | ## Usage 17 | iex(1)> alias OnesignalElixir.{Notification,Builder,Filter} 18 | [OnesignalElixir.Notification, OnesignalElixir.Builder, OnesignalElixir.Filter] 19 | 20 | iex(2)> filters = Filter.new() |> Filter.last_session(">","1.2") |> Filter.add_operator("AND") |> Filter.tag("exists", "email") [ %{field: "last_session", hours_ago: "1.2", relation: ">"}, 21 | %{operator: "AND"}, 22 | %{field: "tag", key: "email", relation: "exists"} 23 | ] 24 | iex(3)> body = OnesignalElixir.new() |> Builder.add_content(:en, "Welcome to One Signal") |> Builder.add_content(:es, "Bienvenido a One Signal")|> Builder.add_heading(:en, "Hello") |> Builder.add_heading(:es, "Hola") |> Builder.add_subtitle(:en, "Welcome") |> Builder.add_subtitle(:es, "Bienvenido") |> Builder.add_filters(filters) 25 | %{ 26 | __struct__: OnesignalElixir.Notification, 27 | app_id: "YOUR-ONESIGNAL-APP-ID", 28 | contents: %{en: "Welcome to One Signal", es: "Bienvenido a One Signal"}, 29 | filters: [ 30 | %{field: "last_session", hours_ago: "1.2", relation: ">"}, 31 | %{operator: "AND"}, 32 | %{field: "tag", key: "email", relation: "exists"} 33 | ], 34 | headings: %{en: "Hello", es: "Hola"}, 35 | subtitles: %{en: "Welcome", es: "Bienvenido"} 36 | } 37 | iex(4)> OnesignalElixir.send_notification(body) {:ok, %{ 38 | "external_id" => nil, 39 | "id" => "GENERATED_NOTIFICATION_ID", 40 | "recipients" => 6 41 | }} 42 | 43 | iex(5)> body = OnesignalElixir.new() |> Builder.add_content(:en, "Welcome to One Signal") |> Builder.add_content(:es, "Bienvenido a One Signal")|> Builder.add_heading(:en, "Hello") |> Builder.add_heading(:es, "Hola") |> Builder.add_subtitle(:en, "Welcome") |> Builder.add_subtitle(:es, "Bienvenido") |> Builder.include_segment("Active Users") 44 | %{ 45 | __struct__: OnesignalElixir.Notification, 46 | app_id: "YOUR-ONESIGNAL-APP-ID", 47 | contents: %{en: "Welcome to One Signal", es: "Bienvenido a One Signal"}, 48 | headings: %{en: "Hello", es: "Hola"}, 49 | included_segments: ["Active Users"], 50 | subtitles: %{en: "Welcome", es: "Bienvenido"} 51 | } 52 | 53 | iex(6)> OnesignalElixir.send_notification(body) 54 | {:ok, 55 | %{ 56 | "external_id" => nil, 57 | "id" => "GENERATED_NOTIFICATION_ID", 58 | "recipients" => 5 59 | }} 60 | 61 | """ 62 | alias OnesignalElixir.{Notification,API} 63 | 64 | @doc """ 65 | Create new Notification structure 66 | """ 67 | def new() do 68 | %Notification{ 69 | app_id: Notification.get_app_id(), 70 | contents: %{} 71 | } 72 | end 73 | 74 | @doc """ 75 | Send notification 76 | Accept notification data in the body part. 77 | Attach default header based on onesignal config 78 | Make a POST call 79 | """ 80 | def send_notification(body) do 81 | headers = Notification.build_header() 82 | url = Notification.build_url() 83 | API.post(url, body, headers) 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/onesignal_elixir/api.ex: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixir.API do 2 | def post(url, body, headers, _options \\ []) do 3 | body = Poison.encode!(body) 4 | 5 | HTTPoison.post!(url, body, headers) 6 | |> handle_response 7 | end 8 | 9 | defp handle_response(%HTTPoison.Response{body: body, status_code: code}) 10 | when code in 200..299 do 11 | {:ok, Poison.decode!(body)} 12 | end 13 | 14 | defp handle_response(%HTTPoison.Response{body: body, status_code: _}) do 15 | {:error, Poison.decode!(body)} 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/onesignal_elixir/builder.ex: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixir.Builder do 2 | require Logger 3 | alias OnesignalElixir.Util 4 | 5 | @doc """ 6 | OnesignalElixir.new() 7 | |> Builder.include_segment("Active Users") 8 | 9 | %{included_segments: ["Active Users"]} 10 | """ 11 | def include_segment(notification, segment_name) do 12 | Map.get_and_update(notification, :included_segments, fn current_value -> 13 | {current_value, Enum.concat(Util.prepare_param_enum(current_value), [segment_name])} 14 | end) 15 | |> elem(1) 16 | end 17 | 18 | @doc """ 19 | OnesignalElixir.new() 20 | |> Builder.include_segments(["Active Users", "Paid Users"]) 21 | 22 | %{included_segments: ["Active Users"]} 23 | """ 24 | def include_segments(notification, segments) do 25 | Map.get_and_update(notification, :included_segments, fn current_value -> 26 | {current_value, Enum.concat(Util.prepare_param_enum(current_value), segments)} 27 | end) 28 | |> elem(1) 29 | end 30 | 31 | @doc """ 32 | OnesignalElixir.new() 33 | |> Builder.exclude_segment("Active Users") 34 | 35 | %{excluded_segments: ["Active Users"]} 36 | """ 37 | def exclude_segment(notification, segment_name) do 38 | Map.get_and_update(notification, :excluded_segments, fn current_value -> 39 | {current_value, Enum.concat(Util.prepare_param_enum(current_value), [segment_name])} 40 | end) 41 | |> elem(1) 42 | end 43 | 44 | @doc """ 45 | OnesignalElixir.new() 46 | |> Builder.exclude_segments(["Active Users", "Paid Users"]) 47 | 48 | %{excluded_segments: ["Active Users", "Paid Users"]} 49 | """ 50 | def exclude_segments(notification, segments) do 51 | Map.get_and_update(notification, :excluded_segments, fn current_value -> 52 | {current_value, Enum.concat(Util.prepare_param_enum(current_value), segments)} 53 | end) 54 | |> elem(1) 55 | end 56 | 57 | @doc """ 58 | OnesignalElixir.new() 59 | |> Builder.add_content(:en, "Welcome to One Signal") 60 | |> Builder.add_content(:es, "Bienvenido a One Signal") 61 | 62 | %{contents: %{en: "Welcome to One Signal", es: "Bienvenido a One Signal"}} 63 | """ 64 | def add_content(notification, language, content) do 65 | Map.get_and_update(notification, :contents, fn current_value -> 66 | {current_value, Map.put_new(current_value, language, content)} 67 | end) 68 | |> elem(1) 69 | end 70 | 71 | @doc """ 72 | OnesignalElixir.new() 73 | |> Builder.add_heading(:en, "Hello") 74 | |> Builder.add_heading(:es, "Hola") 75 | 76 | %{headings: %{en: "Hello", es: "Hola"}} 77 | """ 78 | def add_heading(notification, language, heading) do 79 | Map.get_and_update(notification, :headings, fn current_value -> 80 | {current_value, Map.put_new(Util.prepare_param_map(current_value), language, heading)} 81 | end) 82 | |> elem(1) 83 | end 84 | 85 | @doc """ 86 | OnesignalElixir.new() 87 | |> Builder.add_subtitle(:en, "Welcome") 88 | |> Builder.add_subtitle(:es, "Bienvenido") 89 | 90 | %{subtitles: %{en: "Welcome", es: "Bienvenido"}} 91 | """ 92 | def add_subtitle(notification, language, subtitle) do 93 | Map.get_and_update(notification, :subtitles, fn current_value -> 94 | {current_value, Map.put_new(Util.prepare_param_map(current_value), language, subtitle)} 95 | end) 96 | |> elem(1) 97 | end 98 | 99 | @doc """ 100 | OnesignalElixir.new() 101 | |> Builder.add_filters(filters) 102 | 103 | %{filters: [ 104 | %{field: "last_session", hours_ago: "1.2", relation: ">"}, 105 | %{operator: "AND"}, 106 | %{field: "app_version", relation: "=", value: "2"}, 107 | %{operator: "OR"}, 108 | %{field: "first_session", hours_ago: "1.2", relation: "<"}, 109 | %{operator: "AND"}, 110 | %{field: "tag", key: "key2", relation: "=", value: "value2"} 111 | ]} 112 | """ 113 | def add_filters(notification, filters) do 114 | Map.put(notification, :filters, filters) 115 | end 116 | 117 | @doc """ 118 | OnesignalElixir.new() 119 | |> Builder.add_url("https://xcelerator.ninja") 120 | 121 | %{url: "https://xcelerator.ninja"} 122 | """ 123 | def add_url(notification, url) do 124 | Map.put(notification, :url, url) 125 | end 126 | 127 | @doc """ 128 | OnesignalElixir.new() 129 | |> Builder.add_app_url("https://xcelerator.ninja") 130 | 131 | %{app_url: "https://xcelerator.ninja"} 132 | """ 133 | def add_app_url(notification, app_url) do 134 | Map.put(notification, :app_url, app_url) 135 | end 136 | 137 | @doc """ 138 | OnesignalElixir.new() 139 | |> Builder.big_picture("https://xcelerator.ninja/static/media/main-logo.ff98436a.png") 140 | 141 | %{big_picture: "https://xcelerator.ninja/static/media/main-logo.ff98436a.png"} 142 | """ 143 | def big_picture(notification, big_picture) do 144 | Map.put(notification, :big_picture, big_picture) 145 | end 146 | 147 | @doc """ 148 | OnesignalElixir.new() 149 | |> Builder.buttons([{"Share","ic_menu_share"},{"Send","ic_menu_send"}]) 150 | 151 | %{buttons: [ 152 | %{icon: "ic_menu_share", id: "id0", text: "Share"}, 153 | %{icon: "ic_menu_send", id: "id1", text: "Send"} 154 | ]} 155 | """ 156 | def buttons(notification, buttons_data) do 157 | buttons = 158 | Enum.map_reduce(buttons_data, 0, fn button_data, acc -> 159 | {%{ 160 | id: "id" <> Integer.to_string(acc), 161 | text: elem(button_data, 0), 162 | icon: elem(button_data, 1) 163 | }, acc + 1} 164 | end) 165 | |> elem(0) 166 | 167 | Map.put(notification, :buttons, buttons) 168 | end 169 | 170 | @doc """ 171 | 10 being highest priority 172 | OnesignalElixir.new() 173 | |> Builder.set_priority(10) 174 | 175 | %{priority: 10} 176 | """ 177 | def set_priority(notification, priority) do 178 | Map.put(notification, :priority, priority) 179 | end 180 | 181 | @doc """ 182 | The default is 259,200 seconds (3 days). 183 | Max value to set is 2419200 seconds (28 days). 184 | OnesignalElixir.new() 185 | |> Builder.set_ttl(86400) 186 | 187 | %{ttl: 86400} 188 | """ 189 | def set_ttl(notification, ttl) do 190 | Map.put(notification, :ttl, ttl) 191 | end 192 | 193 | @doc """ 194 | "2015-09-24 14:00:00 GMT-0700" 195 | Max value to set is 2419200 seconds (28 days). 196 | OnesignalElixir.new() 197 | |> Builder.set_send_after("2015-09-24 14:00:00 GMT-0700") 198 | 199 | %{send_after: "2015-09-24 14:00:00 GMT-0700"} 200 | """ 201 | def set_send_after(notification, send_after) do 202 | Map.put(notification, :send_after, send_after) 203 | end 204 | 205 | @doc """ 206 | OnesignalElixir.new() 207 | |> Builder.set_grouping("campaign_123") 208 | 209 | %{android_group: "campaign_123"} 210 | """ 211 | def set_grouping(notification, group) do 212 | Map.put(notification, :android_group, group) 213 | end 214 | 215 | @doc """ 216 | OnesignalElixir.new() 217 | |> Builder.add_data(%{entity: "user", id: 23}) 218 | 219 | %{data: %{entity: "user", id: 23}} 220 | """ 221 | def add_data(notification, data_map) do 222 | if is_map(data_map) do 223 | Map.put(notification, :data, data_map) 224 | else 225 | notification 226 | end 227 | end 228 | 229 | @doc """ 230 | OnesignalElixir.new() 231 | |> Builder.include_external_user_ids(["user-id-1", "user-id-2"]) 232 | 233 | %{include_external_user_ids: ["user-id-1"]} 234 | """ 235 | def include_external_user_ids(notification, user_ids) do 236 | Map.get_and_update(notification, :include_external_user_ids, fn current_value -> 237 | {current_value, Enum.concat(Util.prepare_param_enum(current_value), user_ids)} 238 | end) 239 | |> elem(1) 240 | end 241 | 242 | @doc """ 243 | OnesignalElixir.new() 244 | |> Builder.include_player_ids(["player-id-1", "player-id-2"]) 245 | 246 | %{include_player_ids: ["player-id-1"]} 247 | """ 248 | def include_player_ids(notification, player_ids) do 249 | Map.get_and_update(notification, :include_player_ids, fn current_value -> 250 | {current_value, Enum.concat(Util.prepare_param_enum(current_value), player_ids)} 251 | end) 252 | |> elem(1) 253 | end 254 | 255 | def add_android_channel_id(notification, channel_id) do 256 | Map.put(notification, :android_channel_id, channel_id) 257 | end 258 | end 259 | -------------------------------------------------------------------------------- /lib/onesignal_elixir/filter.ex: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixir.Filter do 2 | require Logger 3 | 4 | def new() do 5 | [] 6 | end 7 | 8 | @doc """ 9 | filters = Filter.new() 10 | |> Filter.last_session(">","5.2") 11 | """ 12 | def last_session(current_filters, relation, hours_ago) do 13 | combine_filters(current_filters, %{ 14 | field: "last_session", 15 | relation: relation, 16 | hours_ago: hours_ago 17 | }) 18 | end 19 | 20 | @doc """ 21 | filters = Filter.new() 22 | |> Filter.first_session(">","1.2") 23 | """ 24 | def first_session(current_filters, relation, hours_ago) do 25 | combine_filters(current_filters, %{ 26 | field: "first_session", 27 | relation: relation, 28 | hours_ago: hours_ago 29 | }) 30 | end 31 | 32 | @doc """ 33 | filters = Filter.new() 34 | |> Filter.session_count("=","20") 35 | """ 36 | def session_count(current_filters, relation, value) do 37 | combine_filters(current_filters, %{field: "session_count", relation: relation, value: value}) 38 | end 39 | 40 | @doc """ 41 | filters = Filter.new() 42 | |> Filter.session_time(">","2.5") 43 | """ 44 | def session_time(current_filters, relation, value) do 45 | combine_filters(current_filters, %{field: "session_time", relation: relation, value: value}) 46 | end 47 | 48 | @doc """ 49 | filters = Filter.new() 50 | |> Filter.amount_spent(">","1.2") 51 | """ 52 | def amount_spent(current_filters, relation, value) do 53 | combine_filters(current_filters, %{field: "amount_spent", relation: relation, value: value}) 54 | end 55 | 56 | @doc """ 57 | filters = Filter.new() 58 | |> Filter.bought_sku(">","com.xcelerator.ninja","1499") 59 | """ 60 | def bought_sku(current_filters, relation, key, value) do 61 | combine_filters(current_filters, %{ 62 | field: "bought_sku", 63 | relation: relation, 64 | key: key, 65 | value: value 66 | }) 67 | end 68 | 69 | @doc """ 70 | filters = Filter.new() 71 | |> Filter.language("!=","es") 72 | """ 73 | def language(current_filters, relation, value) do 74 | combine_filters(current_filters, %{field: "language", relation: relation, value: value}) 75 | end 76 | 77 | @doc """ 78 | filters = Filter.new() 79 | |> Filter.app_version("=","3") 80 | """ 81 | def app_version(current_filters, relation, value) do 82 | combine_filters(current_filters, %{field: "app_version", relation: relation, value: value}) 83 | end 84 | 85 | @doc """ 86 | filters = Filter.new() 87 | |> Filter.location("1000", "12.972442","77.580643") 88 | """ 89 | def location(current_filters, radius, latitude, longitude) do 90 | combine_filters(current_filters, %{ 91 | field: "location", 92 | radius: radius, 93 | lat: latitude, 94 | long: longitude 95 | }) 96 | end 97 | 98 | @doc """ 99 | filters = Filter.new() 100 | |> Filter.email("abc@xyzdomain.com") 101 | """ 102 | def email(current_filters, value) do 103 | combine_filters(current_filters, %{field: "email", value: value}) 104 | end 105 | 106 | @doc """ 107 | filters = Filter.new() 108 | |> Filter.country("=","in",) 109 | """ 110 | def country(current_filters, relation, value) do 111 | combine_filters(current_filters, %{field: "country", value: value, relation: relation}) 112 | end 113 | 114 | @doc """ 115 | filters = Filter.new() 116 | |> Filter.tag("=", "vip", "true") 117 | filters = Filter.new() 118 | |> Filter.tag("exists", "username") 119 | """ 120 | def tag(current_filters, relation, key, value \\ nil) do 121 | filter = %{field: "tag", relation: relation, key: key} 122 | 123 | filter = 124 | case relation do 125 | "exists" -> filter 126 | "not_exists" -> filter 127 | _ -> Map.put(filter, :value, value) 128 | end 129 | 130 | combine_filters(current_filters, filter) 131 | end 132 | 133 | # operator("OR") 134 | # %{operator: "OR"} 135 | defp operator(operator) do 136 | %{operator: operator} 137 | end 138 | 139 | @doc """ 140 | filters = Filter.new() 141 | |> Filter.app_version("=","2") 142 | |> Filter.add_operator("OR") 143 | |> Filter.first_session("<","1.2") 144 | 145 | [%{field: "app_version", relation: "=", value: "2"}, 146 | %{operator: "OR"}, 147 | %{field: "first_session", hours_ago: "1.2", relation: "<"}] 148 | """ 149 | def add_operator(current_filters, operator) do 150 | if length(current_filters) > 0 do 151 | current_filters ++ [operator(operator)] 152 | else 153 | current_filters 154 | end 155 | end 156 | 157 | # If existing filter is empty, then just takes new filter, creates a list 158 | defp combine_filters(_current_filters = [], filter) do 159 | [filter] 160 | end 161 | 162 | # Takes existing filter and appends new filter to the list 163 | defp combine_filters(current_filters = [_ | _], filter) do 164 | current_filters ++ [filter] 165 | end 166 | 167 | @doc """ 168 | #Todo 169 | Need to validate the filter expression created 170 | """ 171 | def validate_filter_expression(expression) do 172 | expression 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /lib/onesignal_elixir/notification.ex: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixir.Notification do 2 | defstruct app_id: :init, contents: %{} 3 | 4 | @doc """ 5 | Onesignal Rest API endpoint 6 | Url 7 | """ 8 | def build_url() do 9 | "https://onesignal.com/api/v1/notifications" 10 | end 11 | 12 | @doc """ 13 | Build Header using respective rest_api_key of onesignal account 14 | rest-api-key 15 | """ 16 | def build_header() do 17 | token = get_rest_api_key() 18 | [Authorization: "Basic #{token}", "Content-Type": "Application/json; Charset=utf-8"] 19 | end 20 | 21 | @doc """ 22 | Get rest_api_key as per the config 23 | """ 24 | def get_rest_api_key() do 25 | Application.get_env(:onesignal_elixir, :rest_api_key) 26 | end 27 | 28 | @doc """ 29 | Get app_id as per the config 30 | """ 31 | def get_app_id() do 32 | Application.get_env(:onesignal_elixir, :app_id) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/onesignal_elixir/util.ex: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixir.Util do 2 | @doc """ 3 | If param is nil returns and empty array 4 | Else returns params 5 | """ 6 | def prepare_param_enum(param) do 7 | if is_nil(param) do 8 | [] 9 | else 10 | param 11 | end 12 | end 13 | 14 | @doc """ 15 | If param is nil returns and empty map 16 | Else returns params 17 | """ 18 | def prepare_param_map(param) do 19 | if is_nil(param) do 20 | %{} 21 | else 22 | param 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixir.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :onesignal_elixir, 7 | version: "0.2.0", 8 | elixir: "~> 1.6", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps(), 11 | description: description(), 12 | package: package(), 13 | name: "Onesignal Elixir", 14 | source_url: "https://github.com/aniruddhasd/onesignal_elixir" 15 | ] 16 | end 17 | 18 | defp description do 19 | """ 20 | This project is a wrapper for using the awesome Onesignal APIs. Currently supports Onesignal's Create Notification API. 21 | """ 22 | end 23 | 24 | defp package do 25 | [ 26 | files: ["lib", "mix.exs", "README.md"], 27 | maintainers: ["Aniruddha Deshpande"], 28 | licenses: ["Apache 2.0"], 29 | links: %{ 30 | "GitHub" => "https://github.com/aniruddhasd/onesignal_elixir", 31 | "Docs" => "https://hexdocs.pm/onesignal_elixir" 32 | } 33 | ] 34 | end 35 | 36 | # Run "mix help compile.app" to learn about applications. 37 | def application do 38 | [ 39 | extra_applications: [:logger] 40 | ] 41 | end 42 | 43 | # Run "mix help deps" to learn about dependencies. 44 | defp deps do 45 | [ 46 | {:ex_doc, "~> 0.18.0", only: :dev, runtime: false}, 47 | {:httpoison, "~> 1.4"}, 48 | {:poison, "~> 4.0"} 49 | ] 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, 3 | "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm", "e3be2bc3ae67781db529b80aa7e7c49904a988596e2dbff897425b48b3581161"}, 4 | "ex_doc": {:hex, :ex_doc, "0.18.4", "4406b8891cecf1352f49975c6d554e62e4341ceb41b9338949077b0d4a97b949", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm", "9dbe1ce1d711dc5362e3b3280e92989ad61413ce423bc4e9f76d5fcc51ab8d6b"}, 5 | "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "c2790c9f0f7205f4a362512192dee8179097394400e745e4d20bab7226a8eaad"}, 6 | "httpoison": {:hex, :httpoison, "1.5.0", "71ae9f304bdf7f00e9cd1823f275c955bdfc68282bc5eb5c85c3a9ade865d68e", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "e9d994aea63fab9e29307920492ab95f87339b56fbc5c8c4b1f65ea20d3ba9a4"}, 7 | "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, 8 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, 9 | "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, 10 | "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, 11 | "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, 12 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm", "603561dc0fd62f4f2ea9b890f4e20e1a0d388746d6e20557cafb1b16950de88c"}, 13 | "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, 14 | } 15 | -------------------------------------------------------------------------------- /test/onesignal_elixir/builder_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixir.BuilderTest do 2 | use ExUnit.Case 3 | alias OnesignalElixir.Builder 4 | 5 | test "include a segment" do 6 | notification = 7 | OnesignalElixir.new() 8 | |> Builder.include_segment("Active Users") 9 | 10 | refute Enum.empty?(notification.included_segments) 11 | 12 | assert Enum.all?( 13 | notification.included_segments, 14 | &(&1 in ["Active Users"]) 15 | ) 16 | end 17 | 18 | test "include multiple segments" do 19 | notification = 20 | OnesignalElixir.new() 21 | |> Builder.include_segments(["Active Users", "Paid Users"]) 22 | 23 | refute Enum.empty?(notification.included_segments) 24 | 25 | assert Enum.all?( 26 | notification.included_segments, 27 | &(&1 in ["Active Users", "Paid Users"]) 28 | ) 29 | end 30 | 31 | test "exclude a segment" do 32 | notification = 33 | OnesignalElixir.new() 34 | |> Builder.exclude_segment("Active Users") 35 | 36 | refute Enum.empty?(notification.excluded_segments) 37 | 38 | assert Enum.all?( 39 | notification.excluded_segments, 40 | &(&1 in ["Active Users"]) 41 | ) 42 | end 43 | 44 | test "exclude multiple segments" do 45 | notification = 46 | OnesignalElixir.new() 47 | |> Builder.exclude_segments(["Active Users", "Paid Users"]) 48 | 49 | refute Enum.empty?(notification.excluded_segments) 50 | 51 | assert Enum.all?( 52 | notification.excluded_segments, 53 | &(&1 in ["Active Users", "Paid Users"]) 54 | ) 55 | end 56 | 57 | test "adding content to notification" do 58 | notification = 59 | OnesignalElixir.new() 60 | |> Builder.add_content(:en, "Welcome to One Signal") 61 | |> Builder.add_content(:es, "Bienvenido a One Signal") 62 | 63 | assert notification.contents == %{ 64 | :en => "Welcome to One Signal", 65 | :es => "Bienvenido a One Signal" 66 | } 67 | end 68 | 69 | test "adding title to notification" do 70 | notification = 71 | OnesignalElixir.new() 72 | |> Builder.add_heading(:en, "Hello") 73 | |> Builder.add_heading(:es, "Hola") 74 | 75 | assert notification.headings == %{:en => "Hello", :es => "Hola"} 76 | end 77 | 78 | test "adding subtitle to notification" do 79 | notification = 80 | OnesignalElixir.new() 81 | |> Builder.add_subtitle(:en, "Welcome") 82 | |> Builder.add_subtitle(:es, "Bienvenido") 83 | 84 | assert notification.subtitles == %{:en => "Welcome", :es => "Bienvenido"} 85 | end 86 | 87 | test "adding url to notification" do 88 | notification = 89 | OnesignalElixir.new() 90 | |> Builder.add_url("https://xcelerator.ninja") 91 | 92 | assert notification.url == "https://xcelerator.ninja" 93 | end 94 | 95 | test "adding app url to notification" do 96 | notification = 97 | OnesignalElixir.new() 98 | |> Builder.add_app_url("https://xcelerator.ninja/home") 99 | 100 | assert notification.app_url == "https://xcelerator.ninja/home" 101 | end 102 | 103 | test "adding big picture to notification" do 104 | notification = 105 | OnesignalElixir.new() 106 | |> Builder.big_picture("https://xcelerator.ninja/static/media/main-logo.ff98436a.png") 107 | 108 | assert notification.big_picture == 109 | "https://xcelerator.ninja/static/media/main-logo.ff98436a.png" 110 | end 111 | 112 | test "add buttons to notification" do 113 | notification = 114 | OnesignalElixir.new() 115 | |> Builder.buttons([{"Share", "ic_menu_share"}, {"Send", "ic_menu_send"}]) 116 | 117 | refute Enum.empty?(notification.buttons) 118 | 119 | assert Enum.all?( 120 | notification.buttons, 121 | &(&1 in [ 122 | %{icon: "ic_menu_share", id: "id0", text: "Share"}, 123 | %{icon: "ic_menu_send", id: "id1", text: "Send"} 124 | ]) 125 | ) 126 | end 127 | 128 | test "setting priority to notification" do 129 | notification = 130 | OnesignalElixir.new() 131 | |> Builder.set_priority(10) 132 | 133 | assert notification.priority == 10 134 | end 135 | 136 | test "setting ttl to notification" do 137 | notification = 138 | OnesignalElixir.new() 139 | |> Builder.set_ttl(86400) 140 | 141 | assert notification.ttl == 86400 142 | end 143 | 144 | test "setting send after to notification" do 145 | notification = 146 | OnesignalElixir.new() 147 | |> Builder.set_send_after("2015-09-24 14:00:00 GMT-0700") 148 | 149 | assert notification.send_after == "2015-09-24 14:00:00 GMT-0700" 150 | end 151 | 152 | test "set grouping to notification" do 153 | notification = 154 | OnesignalElixir.new() 155 | |> Builder.set_grouping("campaign_123") 156 | 157 | assert notification.android_group == "campaign_123" 158 | end 159 | 160 | test "add data to notification" do 161 | notification = 162 | OnesignalElixir.new() 163 | |> Builder.add_data(%{entity: "user", id: 23}) 164 | 165 | assert notification.data == %{entity: "user", id: 23} 166 | end 167 | 168 | test "include player ids" do 169 | notification = 170 | OnesignalElixir.new() 171 | |> Builder.include_player_ids(["player-id-1", "player-id-2"]) 172 | 173 | refute Enum.empty?(notification.include_player_ids) 174 | assert notification.include_player_ids == ["player-id-1", "player-id-2"] 175 | end 176 | 177 | test "include external user ids" do 178 | notification = 179 | OnesignalElixir.new() 180 | |> Builder.include_external_user_ids(["user-id-1", "user-id-2"]) 181 | 182 | refute Enum.empty?(notification.include_external_user_ids) 183 | assert notification.include_external_user_ids == ["user-id-1", "user-id-2"] 184 | end 185 | end 186 | -------------------------------------------------------------------------------- /test/onesignal_elixir/filter_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixir.FilterTest do 2 | use ExUnit.Case 3 | alias OnesignalElixir.{Filter} 4 | 5 | test "include last session filter" do 6 | filters = 7 | Filter.new() 8 | |> Filter.last_session(">", "5.2") 9 | 10 | refute Enum.empty?(filters) 11 | 12 | assert Enum.all?( 13 | filters, 14 | &(&1 in [%{field: "last_session", hours_ago: "5.2", relation: ">"}]) 15 | ) 16 | end 17 | 18 | test "include first session filter" do 19 | filters = 20 | Filter.new() 21 | |> Filter.first_session("<", "1") 22 | 23 | refute Enum.empty?(filters) 24 | 25 | assert Enum.all?( 26 | filters, 27 | &(&1 in [%{field: "first_session", hours_ago: "1", relation: "<"}]) 28 | ) 29 | end 30 | 31 | test "include session count filter" do 32 | filters = 33 | Filter.new() 34 | |> Filter.session_count("=", "2") 35 | 36 | refute Enum.empty?(filters) 37 | 38 | assert Enum.all?( 39 | filters, 40 | &(&1 in [%{field: "session_count", value: "2", relation: "="}]) 41 | ) 42 | end 43 | 44 | test "include session time filter" do 45 | filters = 46 | Filter.new() 47 | |> Filter.session_time(">", "10") 48 | 49 | refute Enum.empty?(filters) 50 | 51 | assert Enum.all?( 52 | filters, 53 | &(&1 in [%{field: "session_time", value: "10", relation: ">"}]) 54 | ) 55 | end 56 | 57 | test "include amount spent filter" do 58 | filters = 59 | Filter.new() 60 | |> Filter.amount_spent(">", "100") 61 | 62 | refute Enum.empty?(filters) 63 | 64 | assert Enum.all?( 65 | filters, 66 | &(&1 in [%{field: "amount_spent", value: "100", relation: ">"}]) 67 | ) 68 | end 69 | 70 | test "include sku bought filter" do 71 | filters = 72 | Filter.new() 73 | |> Filter.bought_sku("<", "com.xcelerator.ninja", "1499") 74 | 75 | refute Enum.empty?(filters) 76 | 77 | assert Enum.all?( 78 | filters, 79 | &(&1 in [ 80 | %{field: "bought_sku", key: "com.xcelerator.ninja", value: "1499", relation: "<"} 81 | ]) 82 | ) 83 | end 84 | 85 | test "include language filter" do 86 | filters = 87 | Filter.new() 88 | |> Filter.language("!=", "es") 89 | 90 | refute Enum.empty?(filters) 91 | 92 | assert Enum.all?( 93 | filters, 94 | &(&1 in [%{field: "language", value: "es", relation: "!="}]) 95 | ) 96 | end 97 | 98 | test "include app version filter" do 99 | filters = 100 | Filter.new() 101 | |> Filter.app_version("=", "3") 102 | 103 | refute Enum.empty?(filters) 104 | 105 | assert Enum.all?( 106 | filters, 107 | &(&1 in [%{field: "app_version", value: "3", relation: "="}]) 108 | ) 109 | end 110 | 111 | test "include location filter" do 112 | filters = 113 | Filter.new() 114 | |> Filter.location("1000", "12.972442", "77.580643") 115 | 116 | refute Enum.empty?(filters) 117 | 118 | assert Enum.all?( 119 | filters, 120 | &(&1 in [%{field: "location", radius: "1000", lat: "12.972442", long: "77.580643"}]) 121 | ) 122 | end 123 | 124 | test "include email filter" do 125 | filters = 126 | Filter.new() 127 | |> Filter.email("abc@xyzdomain.com") 128 | 129 | refute Enum.empty?(filters) 130 | 131 | assert Enum.all?( 132 | filters, 133 | &(&1 in [%{field: "email", value: "abc@xyzdomain.com"}]) 134 | ) 135 | end 136 | 137 | test "include country filter" do 138 | filters = 139 | Filter.new() 140 | |> Filter.country("=", "in") 141 | 142 | refute Enum.empty?(filters) 143 | 144 | assert Enum.all?( 145 | filters, 146 | &(&1 in [%{field: "country", value: "in", relation: "="}]) 147 | ) 148 | end 149 | 150 | test "add simple tag filter" do 151 | filters = 152 | Filter.new() 153 | |> Filter.tag("=", "vip", "true") 154 | 155 | refute Enum.empty?(filters) 156 | 157 | assert Enum.all?( 158 | filters, 159 | &(&1 in [%{field: "tag", key: "vip", value: "true", relation: "="}]) 160 | ) 161 | end 162 | 163 | test "add exists tag filter" do 164 | filters = 165 | Filter.new() 166 | |> Filter.tag("exists", "username") 167 | 168 | refute Enum.empty?(filters) 169 | 170 | assert Enum.all?( 171 | filters, 172 | &(&1 in [%{field: "tag", key: "username", relation: "exists"}]) 173 | ) 174 | end 175 | 176 | test "add a filter expression" do 177 | filters = 178 | Filter.new() 179 | |> Filter.app_version("=", "2") 180 | |> Filter.add_operator("OR") 181 | |> Filter.first_session("<", "1.2") 182 | 183 | refute Enum.empty?(filters) 184 | 185 | assert Enum.all?( 186 | filters, 187 | &(&1 in [ 188 | %{field: "app_version", relation: "=", value: "2"}, 189 | %{operator: "OR"}, 190 | %{field: "first_session", hours_ago: "1.2", relation: "<"} 191 | ]) 192 | ) 193 | end 194 | end 195 | -------------------------------------------------------------------------------- /test/onesignal_elixir_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OnesignalElixirTest do 2 | use ExUnit.Case 3 | doctest OnesignalElixir.Builder 4 | end 5 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------