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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emoji do
@moduledoc """
This GenServer stores in an ETS table the list of the loaded emojis,
and also allows to reload the list at runtime.
"""
use GenServer
alias Pleroma.Emoji.Loader
require Logger
@ets __MODULE__.Ets
@ets_options [
:ordered_set,
:protected,
:named_table,
{:read_concurrency, true}
]
defstruct [:code, :file, :tags, :safe_code, :safe_file]
@doc "Build emoji struct"
def build({code, file, tags}) do
%__MODULE__{
code: code,
file: file,
tags: tags,
safe_code: Pleroma.HTML.strip_tags(code),
safe_file: Pleroma.HTML.strip_tags(file)
}
end
def build({code, file}), do: build({code, file, []})
@doc false
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@doc "Reloads the emojis from disk."
@spec reload() :: :ok
def reload do
GenServer.call(__MODULE__, :reload)
end
@doc "Returns the path of the emoji `name`."
@spec get(String.t()) :: String.t() | nil
def get(name) do
case :ets.lookup(@ets, name) do
[{_, path}] -> path
_ -> nil
end
end
@spec exist?(String.t()) :: boolean()
def exist?(name), do: not is_nil(get(name))
@doc "Returns all the emojos!!"
@spec get_all() :: list({String.t(), String.t(), String.t()})
def get_all do
:ets.tab2list(@ets)
end
@doc "Clear out old emojis"
def clear_all, do: :ets.delete_all_objects(@ets)
@doc false
def init(_) do
@ets = :ets.new(@ets, @ets_options)
GenServer.cast(self(), :reload)
{:ok, nil}
end
@doc false
def handle_cast(:reload, state) do
update_emojis(Loader.load())
{:noreply, state}
end
@doc false
def handle_call(:reload, _from, state) do
update_emojis(Loader.load())
{:reply, :ok, state}
end
@doc false
def terminate(_, _) do
:ok
end
@doc false
def code_change(_old_vsn, state, _extra) do
update_emojis(Loader.load())
{:ok, state}
end
defp update_emojis(emojis) do
:ets.insert(@ets, emojis)
end
@external_resource "lib/pleroma/emoji-test.txt"
regional_indicators =
Enum.map(127_462..127_487, fn codepoint ->
<<codepoint::utf8>>
end)
emojis =
@external_resource
|> File.read!()
|> String.split("\n")
|> Enum.filter(fn line ->
line != "" and not String.starts_with?(line, "#") and
String.contains?(line, "fully-qualified")
end)
|> Enum.map(fn line ->
line
|> String.split(";", parts: 2)
|> hd()
|> String.trim()
|> String.split()
|> Enum.map(fn codepoint ->
<<String.to_integer(codepoint, 16)::utf8>>
end)
|> Enum.join()
end)
|> Enum.uniq()
emojis = emojis ++ regional_indicators
for emoji <- emojis do
def is_unicode_emoji?(unquote(emoji)), do: true
end
def is_unicode_emoji?(_), do: false
end
|