cgi: "https://mdii.sakura.ne.jp/mdii-post.cgi",
files: "https://mdii.sakura.ne.jp"
-config :pleroma, :emoji, shortcode_globs: ["/emoji/custom/**/*.png"]
+config :pleroma, :emoji,
+ shortcode_globs: ["/emoji/custom/**/*.png"],
+ custom_tag: "Custom",
+ finmoji_tag: "Finmoji",
+ emoji_tag: "Emoji",
+ custom_emoji_tag: "Custom"
config :pleroma, :uri_schemes,
valid_schemes: [
-firefox, /emoji/Firefox.gif
-blank, /emoji/blank.png
+firefox, /emoji/Firefox.gif, Gif,Fun
+blank, /emoji/blank.png, Fun
f_00b, /emoji/f_00b.png
f_00b11b, /emoji/f_00b11b.png
f_00b33b, /emoji/f_00b33b.png
f_33b22b, /emoji/f_33b22b.png
f_33h, /emoji/f_33h.png
f_33t, /emoji/f_33t.png
-
* Authentication: not required
* Params: none
* Response: JSON
-* Example response: `{"kalsarikannit_f":"/finmoji/128px/kalsarikannit_f-128.png","perkele":"/finmoji/128px/perkele-128.png","blobdab":"/emoji/blobdab.png","happiness":"/finmoji/128px/happiness-128.png"}`
+* Example response: `[{"kalsarikannit_f":{"tags":["Finmoji"],"image_url":"/finmoji/128px/kalsarikannit_f-128.png"}},{"perkele":{"tags":["Finmoji"],"image_url":"/finmoji/128px/perkele-128.png"}},{"blobdab":{"tags":["SomeTag"],"image_url":"/emoji/blobdab.png"}},"happiness":{"tags":["Finmoji"],"image_url":"/finmoji/128px/happiness-128.png"}}]`
* Note: Same data as Mastodon API’s `/api/v1/custom_emojis` but in a different format
## `/api/pleroma/follow_import`
* Method: `GET`
* Authentication: not required
* Params: none
-* Response: Provider specific JSON, the only guaranteed parameter is `type`
+* Response: Provider specific JSON, the only guaranteed parameter is `type`
* Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint"}`
## `/api/pleroma/delete_account`
### Delete an account
* Method `POST`
* Authentication: required
-* Params:
+* Params:
* `password`: user's password
* Response: JSON. Returns `{"status": "success"}` if the deletion was successful, `{"error": "[error message]"}` otherwise
* Example response: `{"error": "Invalid password."}`
content of `config/custom_emoji.txt`:
```
-happy, /emoji/custom/happy.png
-sad, /emoji/custom/sad.png
+happy, /emoji/custom/happy.png, Tag1,Tag2
+sad, /emoji/custom/sad.png, Tag1
+foo, /emoji/custom/foo.png
```
The files should be PNG (APNG is okay with `.png` for `image/png` Content-type) and under 50kb for compatibility with mastodon.
+
+# Emoji tags
+
+Changing default tags:
+
+* For `Finmoji`, `emoji.txt` and `custom_emoji.txt` are added default tags, which can be configured in the `config.exs`:
+* For emoji loaded from globs:
+ - `priv/static/emoji/custom/*.png` - `custom_tag`, can be configured in `config.exs`
+ - `priv/static/emoji/custom/TagName/*.png` - folder (`TagName`) is used as tag
+
+
+```
+config :pleroma, :emoji,
+ shortcode_globs: ["/emoji/custom/**/*.png"],
+ custom_tag: "Custom", # Default tag for emoji in `priv/static/emoji/custom` path
+ finmoji_tag: "Finmoji", # Default tag for Finmoji
+ emoji_tag: "Emoji", # Default tag for emoji.txt
+ custom_emoji_tag: "Custom" # Default tag for custom_emoji.txt
+```
* the built-in Finmojis (if enabled in configuration),
* the files: `config/emoji.txt` and `config/custom_emoji.txt`
- * glob paths
+ * glob paths, nested folder is used as tag name for grouping e.g. priv/static/emoji/custom/nested_folder
This GenServer stores in an ETS table the list of the loaded emojis, and also allows to reload the list at runtime.
"""
"woollysocks"
]
defp load_finmoji(true) do
+ tag = Keyword.get(Application.get_env(:pleroma, :emoji), :finmoji_tag)
+
Enum.map(@finmoji, fn finmoji ->
- {finmoji, "/finmoji/128px/#{finmoji}-128.png"}
+ {finmoji, "/finmoji/128px/#{finmoji}-128.png", tag}
end)
end
end
defp load_from_file_stream(stream) do
+ default_tag =
+ stream.path
+ |> Path.basename(".txt")
+ |> get_default_tag()
+
stream
|> Stream.map(&String.trim/1)
|> Stream.map(fn line ->
case String.split(line, ~r/,\s*/) do
- [name, file] -> {name, file}
- _ -> nil
+ [name, file, tags] ->
+ {name, file, tags}
+
+ [name, file] ->
+ {name, file, default_tag}
+
+ _ ->
+ nil
end
end)
|> Enum.to_list()
end
+ @spec get_default_tag(String.t()) :: String.t()
+ defp get_default_tag(file_name) when file_name in ["emoji", "custom_emojii"] do
+ Keyword.get(
+ Application.get_env(:pleroma, :emoji),
+ String.to_existing_atom(file_name <> "_tag")
+ )
+ end
+
+ defp get_default_tag(_), do: Keyword.get(Application.get_env(:pleroma, :emoji), :custom_tag)
+
defp load_from_globs(globs) do
static_path = Path.join(:code.priv_dir(:pleroma), "static")
paths =
Enum.map(globs, fn glob ->
+ static_part =
+ Path.dirname(glob)
+ |> String.replace_trailing("**", "")
+
Path.join(static_path, glob)
|> Path.wildcard()
+ |> Enum.map(fn path ->
+ custom_folder =
+ path
+ |> Path.relative_to(Path.join(static_path, static_part))
+ |> Path.dirname()
+
+ [path, custom_folder]
+ end)
end)
|> Enum.concat()
- Enum.map(paths, fn path ->
+ Enum.map(paths, fn [path, custom_folder] ->
+ tag =
+ case custom_folder do
+ "." -> Keyword.get(Application.get_env(:pleroma, :emoji), :custom_tag)
+ tag -> tag
+ end
+
shortcode = Path.basename(path, Path.extname(path))
external_path = Path.join("/", Path.relative_to(path, static_path))
- {shortcode, external_path}
+ {shortcode, external_path, tag}
end)
end
end
def emojify(text, nil), do: text
def emojify(text, emoji, strip \\ false) do
- Enum.reduce(emoji, text, fn {emoji, file}, text ->
- emoji = HTML.strip_tags(emoji)
- file = HTML.strip_tags(file)
+ Enum.reduce(emoji, text, fn emoji_data, text ->
+ emoji = HTML.strip_tags(elem(emoji_data, 0))
+ file = HTML.strip_tags(elem(emoji_data, 1))
html =
if not strip do
def demojify(text, nil), do: text
def get_emoji(text) when is_binary(text) do
- Enum.filter(Emoji.get_all(), fn {emoji, _} -> String.contains?(text, ":#{emoji}:") end)
+ Enum.filter(Emoji.get_all(), fn {emoji, _, _} -> String.contains?(text, ":#{emoji}:") end)
end
def get_emoji(_), do: []
object,
"emoji",
(Formatter.get_emoji(status) ++ Formatter.get_emoji(data["spoiler_text"]))
- |> Enum.reduce(%{}, fn {name, file}, acc ->
+ |> Enum.reduce(%{}, fn {name, file, _}, acc ->
Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}")
end)
) do
def emoji_from_profile(%{info: _info} = user) do
(Formatter.get_emoji(user.bio) ++ Formatter.get_emoji(user.name))
- |> Enum.map(fn {shortcode, url} ->
+ |> Enum.map(fn {shortcode, url, _} ->
%{
"type" => "Emoji",
"icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}#{url}"},
defp mastodonized_emoji do
Pleroma.Emoji.get_all()
- |> Enum.map(fn {shortcode, relative_url} ->
+ |> Enum.map(fn {shortcode, relative_url, tags} ->
url = to_string(URI.merge(Web.base_url(), relative_url))
%{
"shortcode" => shortcode,
"static_url" => url,
"visible_in_picker" => true,
- "url" => url
+ "url" => url,
+ "tags" => String.split(tags, ",")
}
end)
end
end
def emoji(conn, _params) do
- json(conn, Enum.into(Emoji.get_all(), %{}))
+ emoji =
+ Emoji.get_all()
+ |> Enum.map(fn {short_code, path, tags} ->
+ %{short_code => %{image_url: path, tags: String.split(tags, ",")}}
+ end)
+
+ json(conn, emoji)
end
def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
--- /dev/null
+defmodule Pleroma.EmojiTest do
+ use ExUnit.Case, async: true
+ alias Pleroma.Emoji
+
+ describe "get_all/0" do
+ setup do
+ emoji_list = Emoji.get_all()
+ {:ok, emoji_list: emoji_list}
+ end
+ test "first emoji", %{emoji_list: emoji_list} do
+ [emoji | _others] = emoji_list
+ {code, path, tags} = emoji
+
+ assert tuple_size(emoji) == 3
+ assert is_binary(code)
+ assert is_binary(path)
+ assert is_binary(tags)
+ end
+
+ test "random emoji", %{emoji_list: emoji_list} do
+ emoji = Enum.random(emoji_list)
+ {code, path, tags} = emoji
+
+ assert tuple_size(emoji) == 3
+ assert is_binary(code)
+ assert is_binary(path)
+ assert is_binary(tags)
+ end
+ end
+end
test "it returns the emoji used in the text" do
text = "I love :moominmamma:"
- assert Formatter.get_emoji(text) == [{"moominmamma", "/finmoji/128px/moominmamma-128.png"}]
+ tag = Keyword.get(Application.get_env(:pleroma, :emoji), :finmoji_tag)
+ assert Formatter.get_emoji(text) == [{"moominmamma", "/finmoji/128px/moominmamma-128.png", tag}]
end
test "it returns a nice empty result when no emojis are present" do
assert link_header =~ ~r/max_id=#{notification1.id}/
end
end
+
+ describe "custom emoji" do
+ test "with tags", %{conn: conn} do
+ [emoji | _body] =
+ conn
+ |> get("/api/v1/custom_emojis")
+ |> json_response(200)
+
+ assert Map.has_key?(emoji, "shortcode")
+ assert Map.has_key?(emoji, "static_url")
+ assert Map.has_key?(emoji, "tags")
+ assert is_list(emoji["tags"])
+ assert Map.has_key?(emoji, "url")
+ assert Map.has_key?(emoji, "visible_in_picker")
+ end
+ end
end
assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!()
end
end
+
+ describe "/api/pleroma/emoji" do
+ test "returns json with custom emoji with tags", %{conn: conn} do
+ [emoji | _body] =
+ conn
+ |> get("/api/pleroma/emoji")
+ |> json_response(200)
+
+ [key] = Map.keys(emoji)
+
+ %{
+ ^key => %{
+ "image_url" => url,
+ "tags" => tags
+ }
+ } = emoji
+
+ assert is_binary(url)
+ assert is_list(tags)
+ end
+ end
end