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