Merge branch 'rework-emoji-management' into 'develop'
Remove finmoji and add a way to download emojis in packs Closes #817 and #821 See merge request pleroma/pleroma!1073
|
@ -24,6 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mastodon API: [Reports](https://docs.joinmastodon.org/api/rest/reports/)
|
- Mastodon API: [Reports](https://docs.joinmastodon.org/api/rest/reports/)
|
||||||
- ActivityPub C2S: OAuth endpoints
|
- ActivityPub C2S: OAuth endpoints
|
||||||
- Metadata RelMe provider
|
- Metadata RelMe provider
|
||||||
|
- Emoji packs and emoji pack manager
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
|
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
|
||||||
|
@ -50,6 +51,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mastodon API: Remove attachment limit in the Status entity
|
- Mastodon API: Remove attachment limit in the Status entity
|
||||||
- Deps: Updated Cowboy to 2.6
|
- Deps: Updated Cowboy to 2.6
|
||||||
- Deps: Updated Ecto to 3.0.7
|
- Deps: Updated Ecto to 3.0.7
|
||||||
|
- Don't ship finmoji by default, they can be installed as an emoji pack
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Followers counter not being updated when a follower is blocked
|
- Followers counter not being updated when a follower is blocked
|
||||||
|
|
7
COPYING
|
@ -39,10 +39,3 @@ does not include the right to compile photos from Unsplash to replicate
|
||||||
a similar or competing service.
|
a similar or competing service.
|
||||||
|
|
||||||
priv/static/images/city.jpg
|
priv/static/images/city.jpg
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
The files present under the priv/static/finmoji directory are copyright
|
|
||||||
Finland <https://finland.fi/emoji/>, and are distributed under the Creative
|
|
||||||
Commons Attribution-NonCommercial-NoDerivatives 4.0 International license, you
|
|
||||||
should have received a copy of the license file as CC-BY-NC-ND-4.0.
|
|
||||||
|
|
|
@ -100,9 +100,9 @@
|
||||||
shortcode_globs: ["/emoji/custom/**/*.png"],
|
shortcode_globs: ["/emoji/custom/**/*.png"],
|
||||||
groups: [
|
groups: [
|
||||||
# Put groups that have higher priority than defaults here. Example in `docs/config/custom_emoji.md`
|
# Put groups that have higher priority than defaults here. Example in `docs/config/custom_emoji.md`
|
||||||
Finmoji: "/finmoji/128px/*-128.png",
|
Custom: ["/emoji/*.png", "/emoji/**/*.png"]
|
||||||
Custom: ["/emoji/*.png", "/emoji/custom/*.png"]
|
],
|
||||||
]
|
default_manifest: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json"
|
||||||
|
|
||||||
config :pleroma, :uri_schemes,
|
config :pleroma, :uri_schemes,
|
||||||
valid_schemes: [
|
valid_schemes: [
|
||||||
|
@ -223,7 +223,6 @@
|
||||||
"text/html",
|
"text/html",
|
||||||
"text/markdown"
|
"text/markdown"
|
||||||
],
|
],
|
||||||
finmoji_enabled: true,
|
|
||||||
mrf_transparency: true,
|
mrf_transparency: true,
|
||||||
autofollowed_nicknames: [],
|
autofollowed_nicknames: [],
|
||||||
max_pinned_statuses: 1,
|
max_pinned_statuses: 1,
|
||||||
|
|
|
@ -87,7 +87,6 @@ config :pleroma, Pleroma.Emails.Mailer,
|
||||||
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
|
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
|
||||||
* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json``
|
* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json``
|
||||||
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML)
|
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML)
|
||||||
* `finmoji_enabled`: Whenether to enable the finmojis in the custom emojis.
|
|
||||||
* `mrf_transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
* `mrf_transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
||||||
* `scope_copy`: Copy the scope (private/unlisted/public) in replies to posts by default.
|
* `scope_copy`: Copy the scope (private/unlisted/public) in replies to posts by default.
|
||||||
* `subject_line_behavior`: Allows changing the default behaviour of subject lines in replies. Valid values:
|
* `subject_line_behavior`: Allows changing the default behaviour of subject lines in replies. Valid values:
|
||||||
|
|
|
@ -1,15 +1,25 @@
|
||||||
# Custom Emoji
|
# Custom Emoji
|
||||||
|
|
||||||
|
Before you add your own custom emoji, check if they are available in an existing pack.
|
||||||
|
See `Mix.Tasks.Pleroma.Emoji` for information about emoji packs.
|
||||||
|
|
||||||
To add custom emoji:
|
To add custom emoji:
|
||||||
* Add the image file(s) to `priv/static/emoji/custom`
|
* Create the `STATIC-DIR/emoji/` directory if it doesn't exist
|
||||||
* In case of conflicts: add the desired shortcode with the path to `config/custom_emoji.txt`, comma-separated and one per line
|
(`STATIC-DIR` is configurable, `instance/static/` by default)
|
||||||
* Force recompilation (``mix clean && mix compile``)
|
* Create a directory with whatever name you want (custom is a good name to show the purpose of it).
|
||||||
|
This will create a local emoji pack.
|
||||||
|
* Put your `.png` emoji files in that directory. In case of conflicts, you can create an `emoji.txt`
|
||||||
|
file in that directory and specify a custom shortcode using the following format:
|
||||||
|
`shortcode, file-path, tag1, tag2, etc`. One emoji per line. Note that if you do so,
|
||||||
|
you'll have to list all other emojis in the pack too.
|
||||||
|
* Either restart pleroma or connect to the iex session pleroma's running and
|
||||||
|
run `Pleroma.Emoji.reload/0` in it.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
image files (in `/priv/static/emoji/custom`): `happy.png` and `sad.png`
|
image files (in `instance/static/emoji/custom`): `happy.png` and `sad.png`
|
||||||
|
|
||||||
content of `config/custom_emoji.txt`:
|
content of `emoji.txt`:
|
||||||
```
|
```
|
||||||
happy, /emoji/custom/happy.png, Tag1,Tag2
|
happy, /emoji/custom/happy.png, Tag1,Tag2
|
||||||
sad, /emoji/custom/sad.png, Tag1
|
sad, /emoji/custom/sad.png, Tag1
|
||||||
|
|
293
lib/mix/tasks/pleroma/emoji.ex
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.Emoji do
|
||||||
|
use Mix.Task
|
||||||
|
|
||||||
|
@shortdoc "Manages emoji packs"
|
||||||
|
@moduledoc """
|
||||||
|
Manages emoji packs
|
||||||
|
|
||||||
|
## ls-packs
|
||||||
|
|
||||||
|
mix pleroma.emoji ls-packs [OPTION...]
|
||||||
|
|
||||||
|
Lists the emoji packs and metadata specified in the manifest.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
- `-m, --manifest PATH/URL` - path to a custom manifest, it can
|
||||||
|
either be an URL starting with `http`, in that case the
|
||||||
|
manifest will be fetched from that address, or a local path
|
||||||
|
|
||||||
|
## get-packs
|
||||||
|
|
||||||
|
mix pleroma.emoji get-packs [OPTION...] PACKS
|
||||||
|
|
||||||
|
Fetches, verifies and installs the specified PACKS from the
|
||||||
|
manifest into the `STATIC-DIR/emoji/PACK-NAME
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
- `-m, --manifest PATH/URL` - same as ls-packs
|
||||||
|
|
||||||
|
## gen-pack
|
||||||
|
|
||||||
|
mix pleroma.emoji gen-pack PACK-URL
|
||||||
|
|
||||||
|
Creates a new manifest entry and a file list from the specified
|
||||||
|
remote pack file. Currently, only .zip archives are recognized
|
||||||
|
as remote pack files and packs are therefore assumed to be zip
|
||||||
|
archives. This command is intended to run interactively and will
|
||||||
|
first ask you some basic questions about the pack, then download
|
||||||
|
the remote file and generate an SHA256 checksum for it, then
|
||||||
|
generate an emoji file list for you.
|
||||||
|
|
||||||
|
The manifest entry will either be written to a newly created
|
||||||
|
`index.json` file or appended to the existing one, *replacing*
|
||||||
|
the old pack with the same name if it was in the file previously.
|
||||||
|
|
||||||
|
The file list will be written to the file specified previously,
|
||||||
|
*replacing* that file. You _should_ check that the file list doesn't
|
||||||
|
contain anything you don't need in the pack, that is, anything that is
|
||||||
|
not an emoji (the whole pack is downloaded, but only emoji files
|
||||||
|
are extracted).
|
||||||
|
"""
|
||||||
|
|
||||||
|
@default_manifest Pleroma.Config.get!([:emoji, :default_manifest])
|
||||||
|
|
||||||
|
def run(["ls-packs" | args]) do
|
||||||
|
Application.ensure_all_started(:hackney)
|
||||||
|
|
||||||
|
{options, [], []} = parse_global_opts(args)
|
||||||
|
|
||||||
|
manifest =
|
||||||
|
fetch_manifest(if options[:manifest], do: options[:manifest], else: @default_manifest)
|
||||||
|
|
||||||
|
Enum.each(manifest, fn {name, info} ->
|
||||||
|
to_print = [
|
||||||
|
{"Name", name},
|
||||||
|
{"Homepage", info["homepage"]},
|
||||||
|
{"Description", info["description"]},
|
||||||
|
{"License", info["license"]},
|
||||||
|
{"Source", info["src"]}
|
||||||
|
]
|
||||||
|
|
||||||
|
for {param, value} <- to_print do
|
||||||
|
IO.puts(IO.ANSI.format([:bright, param, :normal, ": ", value]))
|
||||||
|
end
|
||||||
|
|
||||||
|
# A newline
|
||||||
|
IO.puts("")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["get-packs" | args]) do
|
||||||
|
Application.ensure_all_started(:hackney)
|
||||||
|
|
||||||
|
{options, pack_names, []} = parse_global_opts(args)
|
||||||
|
|
||||||
|
manifest_url = if options[:manifest], do: options[:manifest], else: @default_manifest
|
||||||
|
|
||||||
|
manifest = fetch_manifest(manifest_url)
|
||||||
|
|
||||||
|
for pack_name <- pack_names do
|
||||||
|
if Map.has_key?(manifest, pack_name) do
|
||||||
|
pack = manifest[pack_name]
|
||||||
|
src_url = pack["src"]
|
||||||
|
|
||||||
|
IO.puts(
|
||||||
|
IO.ANSI.format([
|
||||||
|
"Downloading ",
|
||||||
|
:bright,
|
||||||
|
pack_name,
|
||||||
|
:normal,
|
||||||
|
" from ",
|
||||||
|
:underline,
|
||||||
|
src_url
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
binary_archive = Tesla.get!(src_url).body
|
||||||
|
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
|
||||||
|
|
||||||
|
sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
|
||||||
|
|
||||||
|
if archive_sha == String.upcase(pack["src_sha256"]) do
|
||||||
|
IO.puts(IO.ANSI.format(sha_status_text ++ [:green, "OK"]))
|
||||||
|
else
|
||||||
|
IO.puts(IO.ANSI.format(sha_status_text ++ [:red, "BAD"]))
|
||||||
|
|
||||||
|
raise "Bad SHA256 for #{pack_name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# The url specified in files should be in the same directory
|
||||||
|
files_url = Path.join(Path.dirname(manifest_url), pack["files"])
|
||||||
|
|
||||||
|
IO.puts(
|
||||||
|
IO.ANSI.format([
|
||||||
|
"Fetching the file list for ",
|
||||||
|
:bright,
|
||||||
|
pack_name,
|
||||||
|
:normal,
|
||||||
|
" from ",
|
||||||
|
:underline,
|
||||||
|
files_url
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
files = Tesla.get!(files_url).body |> Poison.decode!()
|
||||||
|
|
||||||
|
IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
|
||||||
|
|
||||||
|
pack_path =
|
||||||
|
Path.join([
|
||||||
|
Pleroma.Config.get!([:instance, :static_dir]),
|
||||||
|
"emoji",
|
||||||
|
pack_name
|
||||||
|
])
|
||||||
|
|
||||||
|
files_to_unzip =
|
||||||
|
Enum.map(
|
||||||
|
files,
|
||||||
|
fn {_, f} -> to_charlist(f) end
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, _} =
|
||||||
|
:zip.unzip(binary_archive,
|
||||||
|
cwd: pack_path,
|
||||||
|
file_list: files_to_unzip
|
||||||
|
)
|
||||||
|
|
||||||
|
IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
|
||||||
|
|
||||||
|
emoji_txt_str =
|
||||||
|
Enum.map(
|
||||||
|
files,
|
||||||
|
fn {shortcode, path} ->
|
||||||
|
emojo_path = Path.join("/emoji/#{pack_name}", path)
|
||||||
|
"#{shortcode}, #{emojo_path}"
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|> Enum.join("\n")
|
||||||
|
|
||||||
|
File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
|
||||||
|
else
|
||||||
|
IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["gen-pack", src]) do
|
||||||
|
Application.ensure_all_started(:hackney)
|
||||||
|
|
||||||
|
proposed_name = Path.basename(src) |> Path.rootname()
|
||||||
|
name = String.trim(IO.gets("Pack name [#{proposed_name}]: "))
|
||||||
|
# If there's no name, use the default one
|
||||||
|
name = if String.length(name) > 0, do: name, else: proposed_name
|
||||||
|
|
||||||
|
license = String.trim(IO.gets("License: "))
|
||||||
|
homepage = String.trim(IO.gets("Homepage: "))
|
||||||
|
description = String.trim(IO.gets("Description: "))
|
||||||
|
|
||||||
|
proposed_files_name = "#{name}.json"
|
||||||
|
files_name = String.trim(IO.gets("Save file list to [#{proposed_files_name}]: "))
|
||||||
|
files_name = if String.length(files_name) > 0, do: files_name, else: proposed_files_name
|
||||||
|
|
||||||
|
default_exts = [".png", ".gif"]
|
||||||
|
default_exts_str = Enum.join(default_exts, " ")
|
||||||
|
|
||||||
|
exts =
|
||||||
|
String.trim(
|
||||||
|
IO.gets("Emoji file extensions (separated with spaces) [#{default_exts_str}]: ")
|
||||||
|
)
|
||||||
|
|
||||||
|
exts =
|
||||||
|
if String.length(exts) > 0 do
|
||||||
|
String.split(exts, " ")
|
||||||
|
|> Enum.filter(fn e -> e |> String.trim() |> String.length() > 0 end)
|
||||||
|
else
|
||||||
|
default_exts
|
||||||
|
end
|
||||||
|
|
||||||
|
IO.puts("Downloading the pack and generating SHA256")
|
||||||
|
|
||||||
|
binary_archive = Tesla.get!(src).body
|
||||||
|
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
|
||||||
|
|
||||||
|
IO.puts("SHA256 is #{archive_sha}")
|
||||||
|
|
||||||
|
pack_json = %{
|
||||||
|
name => %{
|
||||||
|
license: license,
|
||||||
|
homepage: homepage,
|
||||||
|
description: description,
|
||||||
|
src: src,
|
||||||
|
src_sha256: archive_sha,
|
||||||
|
files: files_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
|
||||||
|
|
||||||
|
{:ok, _} =
|
||||||
|
:zip.unzip(
|
||||||
|
binary_archive,
|
||||||
|
cwd: tmp_pack_dir
|
||||||
|
)
|
||||||
|
|
||||||
|
emoji_map = Pleroma.Emoji.make_shortcode_to_file_map(tmp_pack_dir, exts)
|
||||||
|
|
||||||
|
File.write!(files_name, Poison.encode!(emoji_map, pretty: true))
|
||||||
|
|
||||||
|
IO.puts("""
|
||||||
|
|
||||||
|
#{files_name} has been created and contains the list of all found emojis in the pack.
|
||||||
|
Please review the files in the remove those not needed.
|
||||||
|
""")
|
||||||
|
|
||||||
|
if File.exists?("index.json") do
|
||||||
|
existing_data = File.read!("index.json") |> Poison.decode!()
|
||||||
|
|
||||||
|
File.write!(
|
||||||
|
"index.json",
|
||||||
|
Poison.encode!(
|
||||||
|
Map.merge(
|
||||||
|
existing_data,
|
||||||
|
pack_json
|
||||||
|
),
|
||||||
|
pretty: true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
IO.puts("index.json file has been update with the #{name} pack")
|
||||||
|
else
|
||||||
|
File.write!("index.json", Poison.encode!(pack_json, pretty: true))
|
||||||
|
|
||||||
|
IO.puts("index.json has been created with the #{name} pack")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_manifest(from) do
|
||||||
|
Poison.decode!(
|
||||||
|
if String.starts_with?(from, "http") do
|
||||||
|
Tesla.get!(from).body
|
||||||
|
else
|
||||||
|
File.read!(from)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_global_opts(args) do
|
||||||
|
OptionParser.parse(
|
||||||
|
args,
|
||||||
|
strict: [
|
||||||
|
manifest: :string
|
||||||
|
],
|
||||||
|
aliases: [
|
||||||
|
m: :manifest
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Emoji do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
The emojis are loaded from:
|
The emojis are loaded from:
|
||||||
|
|
||||||
* the built-in Finmojis (if enabled in configuration),
|
* emoji packs in INSTANCE-DIR/emoji
|
||||||
* the files: `config/emoji.txt` and `config/custom_emoji.txt`
|
* the files: `config/emoji.txt` and `config/custom_emoji.txt`
|
||||||
* glob paths, nested folder is used as tag name for grouping e.g. priv/static/emoji/custom/nested_folder
|
* glob paths, nested folder is used as tag name for grouping e.g. priv/static/emoji/custom/nested_folder
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ defmodule Pleroma.Emoji do
|
||||||
"""
|
"""
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
@type pattern :: Regex.t() | module() | String.t()
|
@type pattern :: Regex.t() | module() | String.t()
|
||||||
@type patterns :: pattern() | [pattern()]
|
@type patterns :: pattern() | [pattern()]
|
||||||
@type group_patterns :: keyword(patterns())
|
@type group_patterns :: keyword(patterns())
|
||||||
|
@ -79,95 +81,94 @@ def code_change(_old_vsn, state, _extra) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp load do
|
defp load do
|
||||||
finmoji_enabled = Keyword.get(Application.get_env(:pleroma, :instance), :finmoji_enabled)
|
emoji_dir_path =
|
||||||
|
Path.join(
|
||||||
|
Pleroma.Config.get!([:instance, :static_dir]),
|
||||||
|
"emoji"
|
||||||
|
)
|
||||||
|
|
||||||
|
case File.ls(emoji_dir_path) do
|
||||||
|
{:error, :enoent} ->
|
||||||
|
# The custom emoji directory doesn't exist,
|
||||||
|
# don't do anything
|
||||||
|
nil
|
||||||
|
|
||||||
|
{:error, e} ->
|
||||||
|
# There was some other error
|
||||||
|
Logger.error("Could not access the custom emoji directory #{emoji_dir_path}: #{e}")
|
||||||
|
|
||||||
|
{:ok, packs} ->
|
||||||
|
# Print the packs we've found
|
||||||
|
Logger.info("Found emoji packs: #{Enum.join(packs, ", ")}")
|
||||||
|
|
||||||
|
emojis =
|
||||||
|
Enum.flat_map(
|
||||||
|
packs,
|
||||||
|
fn pack -> load_pack(Path.join(emoji_dir_path, pack)) end
|
||||||
|
)
|
||||||
|
|
||||||
|
true = :ets.insert(@ets, emojis)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Compat thing for old custom emoji handling & default emoji,
|
||||||
|
# it should run even if there are no emoji packs
|
||||||
shortcode_globs = Application.get_env(:pleroma, :emoji)[:shortcode_globs] || []
|
shortcode_globs = Application.get_env(:pleroma, :emoji)[:shortcode_globs] || []
|
||||||
|
|
||||||
emojis =
|
emojis =
|
||||||
(load_finmoji(finmoji_enabled) ++
|
(load_from_file("config/emoji.txt") ++
|
||||||
load_from_file("config/emoji.txt") ++
|
|
||||||
load_from_file("config/custom_emoji.txt") ++
|
load_from_file("config/custom_emoji.txt") ++
|
||||||
load_from_globs(shortcode_globs))
|
load_from_globs(shortcode_globs))
|
||||||
|> Enum.reject(fn value -> value == nil end)
|
|> Enum.reject(fn value -> value == nil end)
|
||||||
|
|
||||||
true = :ets.insert(@ets, emojis)
|
true = :ets.insert(@ets, emojis)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
@finmoji [
|
defp load_pack(pack_dir) do
|
||||||
"a_trusted_friend",
|
pack_name = Path.basename(pack_dir)
|
||||||
"alandislands",
|
|
||||||
"association",
|
|
||||||
"auroraborealis",
|
|
||||||
"baby_in_a_box",
|
|
||||||
"bear",
|
|
||||||
"black_gold",
|
|
||||||
"christmasparty",
|
|
||||||
"crosscountryskiing",
|
|
||||||
"cupofcoffee",
|
|
||||||
"education",
|
|
||||||
"fashionista_finns",
|
|
||||||
"finnishlove",
|
|
||||||
"flag",
|
|
||||||
"forest",
|
|
||||||
"four_seasons_of_bbq",
|
|
||||||
"girlpower",
|
|
||||||
"handshake",
|
|
||||||
"happiness",
|
|
||||||
"headbanger",
|
|
||||||
"icebreaker",
|
|
||||||
"iceman",
|
|
||||||
"joulutorttu",
|
|
||||||
"kaamos",
|
|
||||||
"kalsarikannit_f",
|
|
||||||
"kalsarikannit_m",
|
|
||||||
"karjalanpiirakka",
|
|
||||||
"kicksled",
|
|
||||||
"kokko",
|
|
||||||
"lavatanssit",
|
|
||||||
"losthopes_f",
|
|
||||||
"losthopes_m",
|
|
||||||
"mattinykanen",
|
|
||||||
"meanwhileinfinland",
|
|
||||||
"moominmamma",
|
|
||||||
"nordicfamily",
|
|
||||||
"out_of_office",
|
|
||||||
"peacemaker",
|
|
||||||
"perkele",
|
|
||||||
"pesapallo",
|
|
||||||
"polarbear",
|
|
||||||
"pusa_hispida_saimensis",
|
|
||||||
"reindeer",
|
|
||||||
"sami",
|
|
||||||
"sauna_f",
|
|
||||||
"sauna_m",
|
|
||||||
"sauna_whisk",
|
|
||||||
"sisu",
|
|
||||||
"stuck",
|
|
||||||
"suomimainittu",
|
|
||||||
"superfood",
|
|
||||||
"swan",
|
|
||||||
"the_cap",
|
|
||||||
"the_conductor",
|
|
||||||
"the_king",
|
|
||||||
"the_voice",
|
|
||||||
"theoriginalsanta",
|
|
||||||
"tomoffinland",
|
|
||||||
"torillatavataan",
|
|
||||||
"unbreakable",
|
|
||||||
"waiting",
|
|
||||||
"white_nights",
|
|
||||||
"woollysocks"
|
|
||||||
]
|
|
||||||
|
|
||||||
defp load_finmoji(true) do
|
emoji_txt = Path.join(pack_dir, "emoji.txt")
|
||||||
Enum.map(@finmoji, fn finmoji ->
|
|
||||||
file_name = "/finmoji/128px/#{finmoji}-128.png"
|
if File.exists?(emoji_txt) do
|
||||||
group = match_extra(@groups, file_name)
|
load_from_file(emoji_txt)
|
||||||
{finmoji, file_name, to_string(group)}
|
else
|
||||||
|
Logger.info(
|
||||||
|
"No emoji.txt found for pack \"#{pack_name}\", assuming all .png files are emoji"
|
||||||
|
)
|
||||||
|
|
||||||
|
make_shortcode_to_file_map(pack_dir, [".png"])
|
||||||
|
|> Enum.map(fn {shortcode, rel_file} ->
|
||||||
|
filename = Path.join("/emoji/#{pack_name}", rel_file)
|
||||||
|
|
||||||
|
{shortcode, filename, [to_string(match_extra(@groups, filename))]}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp load_finmoji(_), do: []
|
def make_shortcode_to_file_map(pack_dir, exts) do
|
||||||
|
find_all_emoji(pack_dir, exts)
|
||||||
|
|> Enum.map(&Path.relative_to(&1, pack_dir))
|
||||||
|
|> Enum.map(fn f -> {f |> Path.basename() |> Path.rootname(), f} end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_all_emoji(dir, exts) do
|
||||||
|
Enum.reduce(
|
||||||
|
File.ls!(dir),
|
||||||
|
[],
|
||||||
|
fn f, acc ->
|
||||||
|
filepath = Path.join(dir, f)
|
||||||
|
|
||||||
|
if File.dir?(filepath) do
|
||||||
|
acc ++ find_all_emoji(filepath, exts)
|
||||||
|
else
|
||||||
|
acc ++ [filepath]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|> Enum.filter(fn f -> Path.extname(f) in exts end)
|
||||||
|
end
|
||||||
|
|
||||||
defp load_from_file(file) do
|
defp load_from_file(file) do
|
||||||
if File.exists?(file) do
|
if File.exists?(file) do
|
||||||
|
@ -182,11 +183,11 @@ defp load_from_file_stream(stream) do
|
||||||
|> Stream.map(&String.trim/1)
|
|> Stream.map(&String.trim/1)
|
||||||
|> Stream.map(fn line ->
|
|> Stream.map(fn line ->
|
||||||
case String.split(line, ~r/,\s*/) do
|
case String.split(line, ~r/,\s*/) do
|
||||||
[name, file, tags] ->
|
|
||||||
{name, file, tags}
|
|
||||||
|
|
||||||
[name, file] ->
|
[name, file] ->
|
||||||
{name, file, to_string(match_extra(@groups, file))}
|
{name, file, [to_string(match_extra(@groups, file))]}
|
||||||
|
|
||||||
|
[name, file | tags] ->
|
||||||
|
{name, file, tags}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
nil
|
nil
|
||||||
|
@ -209,7 +210,7 @@ defp load_from_globs(globs) do
|
||||||
tag = match_extra(@groups, Path.join("/", Path.relative_to(path, static_path)))
|
tag = match_extra(@groups, Path.join("/", Path.relative_to(path, static_path)))
|
||||||
shortcode = Path.basename(path, Path.extname(path))
|
shortcode = Path.basename(path, Path.extname(path))
|
||||||
external_path = Path.join("/", Path.relative_to(path, static_path))
|
external_path = Path.join("/", Path.relative_to(path, static_path))
|
||||||
{shortcode, external_path, to_string(tag)}
|
{shortcode, external_path, [to_string(tag)]}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ defp mastodonized_emoji do
|
||||||
"static_url" => url,
|
"static_url" => url,
|
||||||
"visible_in_picker" => true,
|
"visible_in_picker" => true,
|
||||||
"url" => url,
|
"url" => url,
|
||||||
"tags" => String.split(tags, ",")
|
"tags" => tags
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -286,7 +286,7 @@ def emoji(conn, _params) do
|
||||||
emoji =
|
emoji =
|
||||||
Emoji.get_all()
|
Emoji.get_all()
|
||||||
|> Enum.map(fn {short_code, path, tags} ->
|
|> Enum.map(fn {short_code, path, tags} ->
|
||||||
{short_code, %{image_url: path, tags: String.split(tags, ",")}}
|
{short_code, %{image_url: path, tags: tags}}
|
||||||
end)
|
end)
|
||||||
|> Enum.into(%{})
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 225 KiB |
Before Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 236 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 125 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 113 KiB |
Before Width: | Height: | Size: 165 KiB |
Before Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 7.1 KiB |