-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathintercom.ex
More file actions
74 lines (67 loc) · 2.48 KB
/
intercom.ex
File metadata and controls
74 lines (67 loc) · 2.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
defmodule Intercom do
alias ESTree.Tools.Builder
alias ESTree.Tools.Generator
alias Intercom.Escaping
def to_javascript_object(dict) when is_map(dict) do
props = Enum.map(dict, fn {k, v} ->
Builder.property(
Builder.identifier(k),
v |> Escaping.html_escape |> Builder.literal
)
end)
Builder.object_expression(props)
end
def boot({:ok, opts}), do: {:ok, boot(opts)}
def boot({:error, reason}), do: {:error, reason}
def boot(opts) when is_map(opts) do
Builder.call_expression(
Builder.identifier(:Intercom),
[
Builder.literal(:boot),
to_javascript_object(opts)
]
)
end
def inject_user_hash(secret, opts) when is_map(opts) do
key = cond do
Map.has_key?(opts, :user_id) -> :user_id
Map.has_key?(opts, :email) -> :email
true -> {:error, "email or user_id required"}
end
case key do
{:error, reason} -> {:error, reason}
key ->
{:ok, case secret do
nil -> opts
_ ->
Map.merge(opts, %{
:user_hash =>
:sha256
|> hmac(secret, Map.fetch!(opts, key))
|> Base.encode16()
|> String.downcase()
})
end}
end
end
def snippet(props, opts) when is_map(props) do
app_id = opts[:app_id]
secret = opts[:secret]
nonce_attr = case opts[:csp_nonce] do
nil -> ""
nonce -> " nonce=\"#{nonce}\""
end
base = "<script#{nonce_attr}>(function(){var w=window;var ic=w.Intercom;if(typeof ic===\"function\"){ic('reattach_activator');ic('update',intercomSettings);}else{var d=document;var i=function(){i.c(arguments)};i.q=[];i.c=function(args){i.q.push(args)};w.Intercom=i;function l(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/#{app_id}';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);}if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}};})();"
ast = secret |> inject_user_hash(Map.merge(props, %{:app_id => app_id})) |> boot
case ast do
{:ok, tree} ->
{:ok, base <> Generator.generate(tree) <> ";</script>"}
x -> x
end
end
if Code.ensure_loaded?(:crypto) and function_exported?(:crypto, :mac, 4) do
defp hmac(digest, key, payload), do: :crypto.mac(:hmac, digest, key, payload)
else
defp hmac(digest, key, payload), do: :crypto.hmac(digest, key, payload)
end
end