1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Mix.Tasks.Pleroma.Emoji do
8 @shortdoc "Manages emoji packs"
14 mix pleroma.emoji ls-packs [OPTION...]
16 Lists the emoji packs and metadata specified in the manifest.
20 - `-m, --manifest PATH/URL` - path to a custom manifest, it can
21 either be an URL starting with `http`, in that case the
22 manifest will be fetched from that address, or a local path
26 mix pleroma.emoji get-packs [OPTION...] PACKS
28 Fetches, verifies and installs the specified PACKS from the
29 manifest into the `STATIC-DIR/emoji/PACK-NAME`
33 - `-m, --manifest PATH/URL` - same as ls-packs
37 mix pleroma.emoji gen-pack PACK-URL
39 Creates a new manifest entry and a file list from the specified
40 remote pack file. Currently, only .zip archives are recognized
41 as remote pack files and packs are therefore assumed to be zip
42 archives. This command is intended to run interactively and will
43 first ask you some basic questions about the pack, then download
44 the remote file and generate an SHA256 checksum for it, then
45 generate an emoji file list for you.
47 The manifest entry will either be written to a newly created
48 `index.json` file or appended to the existing one, *replacing*
49 the old pack with the same name if it was in the file previously.
51 The file list will be written to the file specified previously,
52 *replacing* that file. You _should_ check that the file list doesn't
53 contain anything you don't need in the pack, that is, anything that is
54 not an emoji (the whole pack is downloaded, but only emoji files
58 @default_manifest Pleroma.Config.get!([:emoji, :default_manifest])
60 def run(["ls-packs" | args]) do
61 Application.ensure_all_started(:hackney)
63 {options, [], []} = parse_global_opts(args)
66 fetch_manifest(if options[:manifest], do: options[:manifest], else: @default_manifest)
68 Enum.each(manifest, fn {name, info} ->
71 {"Homepage", info["homepage"]},
72 {"Description", info["description"]},
73 {"License", info["license"]},
74 {"Source", info["src"]}
77 for {param, value} <- to_print do
78 IO.puts(IO.ANSI.format([:bright, param, :normal, ": ", value]))
86 def run(["get-packs" | args]) do
87 Application.ensure_all_started(:hackney)
89 {options, pack_names, []} = parse_global_opts(args)
91 manifest_url = if options[:manifest], do: options[:manifest], else: @default_manifest
93 manifest = fetch_manifest(manifest_url)
95 for pack_name <- pack_names do
96 if Map.has_key?(manifest, pack_name) do
97 pack = manifest[pack_name]
112 binary_archive = Tesla.get!(client(), src_url).body
113 archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
115 sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
117 if archive_sha == String.upcase(pack["src_sha256"]) do
118 IO.puts(IO.ANSI.format(sha_status_text ++ [:green, "OK"]))
120 IO.puts(IO.ANSI.format(sha_status_text ++ [:red, "BAD"]))
122 raise "Bad SHA256 for #{pack_name}"
125 # The url specified in files should be in the same directory
126 files_url = Path.join(Path.dirname(manifest_url), pack["files"])
130 "Fetching the file list for ",
140 files = Tesla.get!(client(), files_url).body |> Poison.decode!()
142 IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
146 Pleroma.Config.get!([:instance, :static_dir]),
154 fn {_, f} -> to_charlist(f) end
158 :zip.unzip(binary_archive,
160 file_list: files_to_unzip
163 IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
168 fn {shortcode, path} ->
169 emojo_path = Path.join("/emoji/#{pack_name}", path)
170 "#{shortcode}, #{emojo_path}"
175 File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
177 IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
182 def run(["gen-pack", src]) do
183 Application.ensure_all_started(:hackney)
185 proposed_name = Path.basename(src) |> Path.rootname()
186 name = String.trim(IO.gets("Pack name [#{proposed_name}]: "))
187 # If there's no name, use the default one
188 name = if String.length(name) > 0, do: name, else: proposed_name
190 license = String.trim(IO.gets("License: "))
191 homepage = String.trim(IO.gets("Homepage: "))
192 description = String.trim(IO.gets("Description: "))
194 proposed_files_name = "#{name}.json"
195 files_name = String.trim(IO.gets("Save file list to [#{proposed_files_name}]: "))
196 files_name = if String.length(files_name) > 0, do: files_name, else: proposed_files_name
198 default_exts = [".png", ".gif"]
199 default_exts_str = Enum.join(default_exts, " ")
203 IO.gets("Emoji file extensions (separated with spaces) [#{default_exts_str}]: ")
207 if String.length(exts) > 0 do
208 String.split(exts, " ")
209 |> Enum.filter(fn e -> e |> String.trim() |> String.length() > 0 end)
214 IO.puts("Downloading the pack and generating SHA256")
216 binary_archive = Tesla.get!(client(), src).body
217 archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
219 IO.puts("SHA256 is #{archive_sha}")
225 description: description,
227 src_sha256: archive_sha,
232 tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
240 emoji_map = Pleroma.Emoji.make_shortcode_to_file_map(tmp_pack_dir, exts)
242 File.write!(files_name, Poison.encode!(emoji_map, pretty: true))
246 #{files_name} has been created and contains the list of all found emojis in the pack.
247 Please review the files in the remove those not needed.
250 if File.exists?("index.json") do
251 existing_data = File.read!("index.json") |> Poison.decode!()
264 IO.puts("index.json file has been update with the #{name} pack")
266 File.write!("index.json", Poison.encode!(pack_json, pretty: true))
268 IO.puts("index.json has been created with the #{name} pack")
272 defp fetch_manifest(from) do
274 if String.starts_with?(from, "http") do
275 Tesla.get!(client(), from).body
282 defp parse_global_opts(args) do
296 {Tesla.Middleware.FollowRedirects, [max_redirects: 3]}
299 Tesla.client(middleware)