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 either be an URL
21 starting with `http`, in that case the manifest will be fetched from that address,
26 mix pleroma.emoji get-packs [OPTION...] PACKS
28 Fetches, verifies and installs the specified PACKS from the manifest into
29 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 remote pack file.
40 Currently, only .zip archives are recognized as remote pack files and packs are therefore
41 assumed to be zip archives. This command is intended to run interactively and
42 will first ask you some basic questions about the pack, then download the remote
43 file and generate an MD5 signature for it, then generate an emoji file list for you.
45 The manifest entry will either be written to a newly created `index.json` file or appended to the existing one,
46 *replacing* the old pack with the same name if it was in the file previously.
48 The file list will be written to the file specified previously, *replacing* that file.
49 You _should_ check that the file list doesn't contain anything you don't need in the pack, that is,
50 anything that is not an emoji (the whole pack is downloaded, but only emoji files are extracted).
54 @default_manifest Pleroma.Config.get!([:emoji, :default_manifest])
56 def run(["ls-packs" | args]) do
57 Application.ensure_all_started(:hackney)
59 {options, [], []} = parse_global_opts(args)
62 fetch_manifest(if options[:manifest], do: options[:manifest], else: @default_manifest)
64 Enum.each(manifest, fn {name, info} ->
67 {"Homepage", info["homepage"]},
68 {"Description", info["description"]},
69 {"License", info["license"]},
70 {"Source", info["src"]}
73 for {param, value} <- to_print do
74 IO.puts(IO.ANSI.format([:bright, param, :normal, ": ", value]))
82 def run(["get-packs" | args]) do
83 Application.ensure_all_started(:hackney)
85 {options, pack_names, []} = parse_global_opts(args)
87 manifest_url = if options[:manifest], do: options[:manifest], else: @default_manifest
89 manifest = fetch_manifest(manifest_url)
91 for pack_name <- pack_names do
92 if Map.has_key?(manifest, pack_name) do
93 pack = manifest[pack_name]
108 binary_archive = Tesla.get!(src_url).body
109 archive_md5 = :crypto.hash(:md5, binary_archive) |> Base.encode16()
111 md5_status_text = ["MD5 of ", :bright, pack_name, :normal, " source file is ", :bright]
113 if archive_md5 == String.upcase(pack["src_md5"]) do
114 IO.puts(IO.ANSI.format(md5_status_text ++ [:green, "OK"]))
116 IO.puts(IO.ANSI.format(md5_status_text ++ [:red, "BAD"]))
118 raise "Bad MD5 for #{pack_name}"
121 # The url specified in files should be in the same directory
122 files_url = Path.join(Path.dirname(manifest_url), pack["files"])
126 "Fetching the file list for ",
136 files = Tesla.get!(files_url).body |> Poison.decode!()
138 IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
140 static_path = Path.join(:code.priv_dir(:pleroma), "static")
145 Pleroma.Config.get!([:instance, :static_dir]),
153 fn {_, f} -> to_charlist(f) end
157 :zip.unzip(binary_archive,
159 file_list: files_to_unzip
162 IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
167 Pleroma.Config.get!([:instance, :static_dir]),
175 fn {shortcode, path} ->
176 "#{shortcode}, #{Path.join(common_pack_path, path)}"
181 File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
183 IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
188 def run(["gen-pack", src]) do
189 Application.ensure_all_started(:hackney)
191 proposed_name = Path.basename(src) |> Path.rootname()
192 name = String.trim(IO.gets("Pack name [#{proposed_name}]: "))
193 # If there's no name, use the default one
194 name = if String.length(name) > 0, do: name, else: proposed_name
196 license = String.trim(IO.gets("License: "))
197 homepage = String.trim(IO.gets("Homepage: "))
198 description = String.trim(IO.gets("Description: "))
200 proposed_files_name = "#{name}.json"
201 files_name = String.trim(IO.gets("Save file list to [#{proposed_files_name}]: "))
202 files_name = if String.length(files_name) > 0, do: files_name, else: proposed_files_name
204 default_exts = [".png", ".gif"]
205 default_exts_str = Enum.join(default_exts, " ")
209 IO.gets("Emoji file extensions (separated with spaces) [#{default_exts_str}]: ")
213 if String.length(exts) > 0 do
214 String.split(exts, " ")
215 |> Enum.filter(fn e -> e |> String.trim() |> String.length() > 0 end)
220 IO.puts("Downloading the pack and generating MD5")
222 binary_archive = Tesla.get!(src).body
223 archive_md5 = :crypto.hash(:md5, binary_archive) |> Base.encode16()
225 IO.puts("MD5 is #{archive_md5}")
231 description: description,
233 src_md5: archive_md5,
238 tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
246 emoji_map = Pleroma.Emoji.make_shortcode_to_file_map(tmp_pack_dir, exts)
248 File.write!(files_name, Poison.encode!(emoji_map, pretty: true))
252 #{files_name} has been created and contains the list of all found emojis in the pack.
253 Please review the files in the remove those not needed.
256 if File.exists?("index.json") do
257 existing_data = File.read!("index.json") |> Poison.decode!()
270 IO.puts("index.json file has been update with the #{name} pack")
272 File.write!("index.json", Poison.encode!(pack_json, pretty: true))
274 IO.puts("index.json has been created with the #{name} pack")
278 defp fetch_manifest(from) do
280 if String.starts_with?(from, "http") do
281 Tesla.get!(from).body
288 defp parse_global_opts(args) do