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