Merge branch 'tests/prismo-url-map' into 'develop'
[akkoma] / lib / pleroma / emoji.ex
1 defmodule Pleroma.Emoji do
2 @moduledoc """
3 The emojis are loaded from:
4
5 * the built-in Finmojis (if enabled in configuration),
6 * the files: `config/emoji.txt` and `config/custom_emoji.txt`
7 * glob paths
8
9 This GenServer stores in an ETS table the list of the loaded emojis, and also allows to reload the list at runtime.
10 """
11 use GenServer
12 @ets __MODULE__.Ets
13 @ets_options [:set, :protected, :named_table, {:read_concurrency, true}]
14
15 @doc false
16 def start_link() do
17 GenServer.start_link(__MODULE__, [], name: __MODULE__)
18 end
19
20 @doc "Reloads the emojis from disk."
21 @spec reload() :: :ok
22 def reload() do
23 GenServer.call(__MODULE__, :reload)
24 end
25
26 @doc "Returns the path of the emoji `name`."
27 @spec get(String.t()) :: String.t() | nil
28 def get(name) do
29 case :ets.lookup(@ets, name) do
30 [{_, path}] -> path
31 _ -> nil
32 end
33 end
34
35 @doc "Returns all the emojos!!"
36 @spec get_all() :: [{String.t(), String.t()}, ...]
37 def get_all() do
38 :ets.tab2list(@ets)
39 end
40
41 @doc false
42 def init(_) do
43 @ets = :ets.new(@ets, @ets_options)
44 GenServer.cast(self(), :reload)
45 {:ok, nil}
46 end
47
48 @doc false
49 def handle_cast(:reload, state) do
50 load()
51 {:noreply, state}
52 end
53
54 @doc false
55 def handle_call(:reload, _from, state) do
56 load()
57 {:reply, :ok, state}
58 end
59
60 @doc false
61 def terminate(_, _) do
62 :ok
63 end
64
65 @doc false
66 def code_change(_old_vsn, state, _extra) do
67 load()
68 {:ok, state}
69 end
70
71 defp load() do
72 emojis =
73 (load_finmoji(Keyword.get(Application.get_env(:pleroma, :instance), :finmoji_enabled)) ++
74 load_from_file("config/emoji.txt") ++
75 load_from_file("config/custom_emoji.txt") ++
76 load_from_globs(
77 Keyword.get(Application.get_env(:pleroma, :emoji, []), :shortcode_globs, [])
78 ))
79 |> Enum.reject(fn value -> value == nil end)
80
81 true = :ets.insert(@ets, emojis)
82 :ok
83 end
84
85 @finmoji [
86 "a_trusted_friend",
87 "alandislands",
88 "association",
89 "auroraborealis",
90 "baby_in_a_box",
91 "bear",
92 "black_gold",
93 "christmasparty",
94 "crosscountryskiing",
95 "cupofcoffee",
96 "education",
97 "fashionista_finns",
98 "finnishlove",
99 "flag",
100 "forest",
101 "four_seasons_of_bbq",
102 "girlpower",
103 "handshake",
104 "happiness",
105 "headbanger",
106 "icebreaker",
107 "iceman",
108 "joulutorttu",
109 "kaamos",
110 "kalsarikannit_f",
111 "kalsarikannit_m",
112 "karjalanpiirakka",
113 "kicksled",
114 "kokko",
115 "lavatanssit",
116 "losthopes_f",
117 "losthopes_m",
118 "mattinykanen",
119 "meanwhileinfinland",
120 "moominmamma",
121 "nordicfamily",
122 "out_of_office",
123 "peacemaker",
124 "perkele",
125 "pesapallo",
126 "polarbear",
127 "pusa_hispida_saimensis",
128 "reindeer",
129 "sami",
130 "sauna_f",
131 "sauna_m",
132 "sauna_whisk",
133 "sisu",
134 "stuck",
135 "suomimainittu",
136 "superfood",
137 "swan",
138 "the_cap",
139 "the_conductor",
140 "the_king",
141 "the_voice",
142 "theoriginalsanta",
143 "tomoffinland",
144 "torillatavataan",
145 "unbreakable",
146 "waiting",
147 "white_nights",
148 "woollysocks"
149 ]
150 defp load_finmoji(true) do
151 Enum.map(@finmoji, fn finmoji ->
152 {finmoji, "/finmoji/128px/#{finmoji}-128.png"}
153 end)
154 end
155
156 defp load_finmoji(_), do: []
157
158 defp load_from_file(file) do
159 if File.exists?(file) do
160 load_from_file_stream(File.stream!(file))
161 else
162 []
163 end
164 end
165
166 defp load_from_file_stream(stream) do
167 stream
168 |> Stream.map(&String.strip/1)
169 |> Stream.map(fn line ->
170 case String.split(line, ~r/,\s*/) do
171 [name, file] -> {name, file}
172 _ -> nil
173 end
174 end)
175 |> Enum.to_list()
176 end
177
178 defp load_from_globs(globs) do
179 static_path = Path.join(:code.priv_dir(:pleroma), "static")
180
181 paths =
182 Enum.map(globs, fn glob ->
183 Path.join(static_path, glob)
184 |> Path.wildcard()
185 end)
186 |> Enum.concat()
187
188 Enum.map(paths, fn path ->
189 shortcode = Path.basename(path, Path.extname(path))
190 external_path = Path.join("/", Path.relative_to(path, static_path))
191 {shortcode, external_path}
192 end)
193 end
194 end