2126588b1ae83b0a475a8639279d015d72c2444c
[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 "https://git.pleroma.social/vaartis/emoji-index/raw/master/index.json"
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 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!(src_url).body
64 archive_md5 = :crypto.hash(:md5, binary_archive) |> Base.encode16()
65
66 md5_status_text = ["MD5 of ", :bright, pack_name, :normal, " source file is ", :bright]
67 if archive_md5 == String.upcase(pack["src_md5"]) do
68 IO.puts(IO.ANSI.format(md5_status_text ++ [:green, "OK"]))
69 else
70 IO.puts(IO.ANSI.format(md5_status_text ++ [:red, "BAD"]))
71
72 raise "Bad MD5 for #{pack_name}"
73 end
74
75 # The url specified in files should be in the same directory
76 files_url = Path.join(Path.dirname(manifest_url), pack["files"])
77
78 IO.puts(
79 IO.ANSI.format([
80 "Fetching the file list for ",
81 :bright,
82 pack_name,
83 :normal,
84 " from ",
85 :underline,
86 files_url
87 ])
88 )
89
90 files = Tesla.get!(files_url).body |> Poison.decode!()
91
92 IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
93
94 static_path = Path.join(:code.priv_dir(:pleroma), "static")
95
96 pack_path =
97 Path.join([
98 static_path,
99 Pleroma.Config.get!([:instance, :static_dir]),
100 "emoji",
101 pack_name
102 ])
103
104 files_to_unzip =
105 Enum.map(
106 files,
107 fn {_, f} -> to_charlist(f) end
108 )
109
110 {:ok, _} =
111 :zip.unzip(binary_archive,
112 cwd: pack_path,
113 file_list: files_to_unzip
114 )
115
116 IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
117
118 emoji_txt_str =
119 Enum.map(
120 files,
121 fn {shortcode, path} ->
122 "#{shortcode}, /instance/static/emoji/#{pack_name}/#{path}"
123 end
124 )
125 |> Enum.join("\n")
126
127 File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
128 else
129 IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
130 end
131 end
132 end
133
134 def run(["gen-pack", src]) do
135 Application.ensure_all_started(:hackney)
136
137 proposed_name = Path.basename(src) |> Path.rootname()
138 name = String.trim(IO.gets("Pack name [#{proposed_name}]: "))
139 # If there's no name, use the default one
140 name = if String.length(name) > 0, do: name, else: proposed_name
141
142 license = String.trim(IO.gets("License: "))
143 homepage = String.trim(IO.gets("Homepage: "))
144 description = String.trim(IO.gets("Description: "))
145
146 proposed_files_name = "#{name}.json"
147 files_name = String.trim(IO.gets("Save file list to [#{proposed_files_name}]: "))
148 files_name = if String.length(files_name) > 0, do: files_name, else: proposed_files_name
149
150 default_exts = [".png", ".gif"]
151 default_exts_str = Enum.join(default_exts, " ")
152 exts =
153 String.trim(IO.gets("Emoji file extensions (separated with spaces) [#{default_exts_str}]: "))
154 exts = if String.length(exts) > 0 do
155 String.split(exts, " ") |> Enum.filter(fn e -> (e |> String.trim() |> String.length()) > 0 end)
156 else
157 default_exts
158 end
159
160 IO.puts "Downloading the pack and generating MD5"
161
162 binary_archive = Tesla.get!(src).body
163 archive_md5 = :crypto.hash(:md5, binary_archive) |> Base.encode16()
164
165 IO.puts "MD5 is #{archive_md5}"
166
167 pack_json = %{
168 name => %{
169 license: license,
170 homepage: homepage,
171 description: description,
172 src: src,
173 src_md5: archive_md5,
174 files: files_name
175 }
176 }
177
178 tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
179 {:ok, _} =
180 :zip.unzip(
181 binary_archive,
182 cwd: tmp_pack_dir
183 )
184
185 emoji_map =
186 find_all_emoji(tmp_pack_dir, exts) |>
187 Enum.map(&Path.relative_to(&1, tmp_pack_dir)) |>
188 Enum.map(fn f -> {f |> Path.basename() |> Path.rootname(), f} end) |>
189 Enum.into(%{})
190
191 File.write!(files_name, Poison.encode!(emoji_map, pretty: true))
192
193 IO.puts """
194
195 #{files_name} has been created and contains the list of all found emojis in the pack.
196 Please review the files in the remove those not needed.
197 """
198
199 if File.exists?("index.json") do
200 existing_data = File.read!("index.json") |> Poison.decode!()
201
202 File.write!(
203 "index.json",
204 Poison.encode!(
205 Map.merge(
206 existing_data,
207 pack_json
208 ),
209 pretty: true
210 )
211 )
212
213 IO.puts "index.json file has been update with the #{name} pack"
214 else
215 File.write!("index.json", Poison.encode!(pack_json, pretty: true))
216
217 IO.puts "index.json has been created with the #{name} pack"
218 end
219
220 end
221
222 defp find_all_emoji(dir, exts) do
223 Enum.reduce(
224 File.ls!(dir),
225 [],
226 fn f, acc ->
227 filepath = Path.join(dir, f)
228 if File.dir?(filepath) do
229 acc ++ find_all_emoji(filepath, exts)
230 else
231 acc ++ [filepath]
232 end
233 end
234 ) |> Enum.filter(fn f -> Path.extname(f) in exts end)
235 end
236
237 defp fetch_manifest(from) do
238 Tesla.get!(from).body |> Poison.decode!()
239 end
240
241 defp parse_global_opts(args) do
242 OptionParser.parse(
243 args,
244 strict: [
245 manifest: :string
246 ],
247 aliases: [
248 m: :manifest
249 ]
250 )
251 end
252 end