Merge branch 'bugfix/notification-nil-actor' into 'develop'
[akkoma] / lib / mix / tasks / 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 Mix.Tasks.Pleroma.Emoji do
6 use Mix.Task
7
8 @shortdoc "Manages emoji packs"
9 @moduledoc File.read!("docs/administration/CLI_tasks/emoji.md")
10
11 def run(["ls-packs" | args]) do
12 Application.ensure_all_started(:hackney)
13
14 {options, [], []} = parse_global_opts(args)
15
16 manifest =
17 fetch_manifest(if options[:manifest], do: options[:manifest], else: default_manifest())
18
19 Enum.each(manifest, fn {name, info} ->
20 to_print = [
21 {"Name", name},
22 {"Homepage", info["homepage"]},
23 {"Description", info["description"]},
24 {"License", info["license"]},
25 {"Source", info["src"]}
26 ]
27
28 for {param, value} <- to_print do
29 IO.puts(IO.ANSI.format([:bright, param, :normal, ": ", value]))
30 end
31
32 # A newline
33 IO.puts("")
34 end)
35 end
36
37 def run(["get-packs" | args]) do
38 Application.ensure_all_started(:hackney)
39
40 {options, pack_names, []} = parse_global_opts(args)
41
42 manifest_url = if options[:manifest], do: options[:manifest], else: default_manifest()
43
44 manifest = fetch_manifest(manifest_url)
45
46 for pack_name <- pack_names do
47 if Map.has_key?(manifest, pack_name) do
48 pack = manifest[pack_name]
49 src_url = pack["src"]
50
51 IO.puts(
52 IO.ANSI.format([
53 "Downloading ",
54 :bright,
55 pack_name,
56 :normal,
57 " from ",
58 :underline,
59 src_url
60 ])
61 )
62
63 binary_archive = Tesla.get!(client(), src_url).body
64 archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
65
66 sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
67
68 if archive_sha == String.upcase(pack["src_sha256"]) do
69 IO.puts(IO.ANSI.format(sha_status_text ++ [:green, "OK"]))
70 else
71 IO.puts(IO.ANSI.format(sha_status_text ++ [:red, "BAD"]))
72
73 raise "Bad SHA256 for #{pack_name}"
74 end
75
76 # The url specified in files should be in the same directory
77 files_url = Path.join(Path.dirname(manifest_url), pack["files"])
78
79 IO.puts(
80 IO.ANSI.format([
81 "Fetching the file list for ",
82 :bright,
83 pack_name,
84 :normal,
85 " from ",
86 :underline,
87 files_url
88 ])
89 )
90
91 files = Tesla.get!(client(), files_url).body |> Jason.decode!()
92
93 IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
94
95 pack_path =
96 Path.join([
97 Pleroma.Config.get!([:instance, :static_dir]),
98 "emoji",
99 pack_name
100 ])
101
102 files_to_unzip =
103 Enum.map(
104 files,
105 fn {_, f} -> to_charlist(f) end
106 )
107
108 {:ok, _} =
109 :zip.unzip(binary_archive,
110 cwd: pack_path,
111 file_list: files_to_unzip
112 )
113
114 IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
115
116 emoji_txt_str =
117 Enum.map(
118 files,
119 fn {shortcode, path} ->
120 emojo_path = Path.join("/emoji/#{pack_name}", path)
121 "#{shortcode}, #{emojo_path}"
122 end
123 )
124 |> Enum.join("\n")
125
126 File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
127 else
128 IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
129 end
130 end
131 end
132
133 def run(["gen-pack", src]) do
134 Application.ensure_all_started(:hackney)
135
136 proposed_name = Path.basename(src) |> Path.rootname()
137 name = String.trim(IO.gets("Pack name [#{proposed_name}]: "))
138 # If there's no name, use the default one
139 name = if String.length(name) > 0, do: name, else: proposed_name
140
141 license = String.trim(IO.gets("License: "))
142 homepage = String.trim(IO.gets("Homepage: "))
143 description = String.trim(IO.gets("Description: "))
144
145 proposed_files_name = "#{name}.json"
146 files_name = String.trim(IO.gets("Save file list to [#{proposed_files_name}]: "))
147 files_name = if String.length(files_name) > 0, do: files_name, else: proposed_files_name
148
149 default_exts = [".png", ".gif"]
150 default_exts_str = Enum.join(default_exts, " ")
151
152 exts =
153 String.trim(
154 IO.gets("Emoji file extensions (separated with spaces) [#{default_exts_str}]: ")
155 )
156
157 exts =
158 if String.length(exts) > 0 do
159 String.split(exts, " ")
160 |> Enum.filter(fn e -> e |> String.trim() |> String.length() > 0 end)
161 else
162 default_exts
163 end
164
165 IO.puts("Downloading the pack and generating SHA256")
166
167 binary_archive = Tesla.get!(client(), src).body
168 archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
169
170 IO.puts("SHA256 is #{archive_sha}")
171
172 pack_json = %{
173 name => %{
174 license: license,
175 homepage: homepage,
176 description: description,
177 src: src,
178 src_sha256: archive_sha,
179 files: files_name
180 }
181 }
182
183 tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
184
185 {:ok, _} =
186 :zip.unzip(
187 binary_archive,
188 cwd: tmp_pack_dir
189 )
190
191 emoji_map = Pleroma.Emoji.Loader.make_shortcode_to_file_map(tmp_pack_dir, exts)
192
193 File.write!(files_name, Jason.encode!(emoji_map, pretty: true))
194
195 IO.puts("""
196
197 #{files_name} has been created and contains the list of all found emojis in the pack.
198 Please review the files in the remove those not needed.
199 """)
200
201 if File.exists?("index.json") do
202 existing_data = File.read!("index.json") |> Jason.decode!()
203
204 File.write!(
205 "index.json",
206 Jason.encode!(
207 Map.merge(
208 existing_data,
209 pack_json
210 ),
211 pretty: true
212 )
213 )
214
215 IO.puts("index.json file has been update with the #{name} pack")
216 else
217 File.write!("index.json", Jason.encode!(pack_json, pretty: true))
218
219 IO.puts("index.json has been created with the #{name} pack")
220 end
221 end
222
223 defp fetch_manifest(from) do
224 Jason.decode!(
225 if String.starts_with?(from, "http") do
226 Tesla.get!(client(), from).body
227 else
228 File.read!(from)
229 end
230 )
231 end
232
233 defp parse_global_opts(args) do
234 OptionParser.parse(
235 args,
236 strict: [
237 manifest: :string
238 ],
239 aliases: [
240 m: :manifest
241 ]
242 )
243 end
244
245 defp client do
246 middleware = [
247 {Tesla.Middleware.FollowRedirects, [max_redirects: 3]}
248 ]
249
250 Tesla.client(middleware)
251 end
252
253 defp default_manifest, do: Pleroma.Config.get!([:emoji, :default_manifest])
254 end