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