commit: 6085c71bd17e2b676e0820bf3fd93f81a1902ac3
parent f74bb1d0fc123ba8616d81a80dcf5d64b25aa4ed
Author: rinpatch <rinpatch@sdf.org>
Date: Thu, 14 Nov 2019 08:47:10 +0000
Merge branch 'reactions' into 'develop'
Emoji Reactions
See merge request pleroma/pleroma!1662
Diffstat:
19 files changed, 1400 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
@@ -53,6 +53,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity
- OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/)
- Metadata Link: Atom syndication Feed
+- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
- Mastodon API: Add `exclude_visibilities` parameter to the timeline and notification endpoints
- Admin API: `/users/:nickname/toggle_activation` endpoint is now deprecated in favor of: `/users/activate`, `/users/deactivate`, both accept `nicknames` array
- Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
@@ -61,6 +62,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
- Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations`
- Configuration: `feed` option for user atom feed.
+- Pleroma API: Add Emoji reactions
</details>
### Fixed
diff --git a/docs/API/pleroma_api.md b/docs/API/pleroma_api.md
@@ -479,3 +479,35 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
* `artist`: the artist of the media playing [optional]
* `length`: the length of the media playing [optional]
* Response: the newly created media metadata entity representing the Listen activity
+
+# Emoji Reactions
+
+Emoji reactions work a lot like favourites do. They make it possible to react to a post with a single emoji character.
+
+## `POST /api/v1/pleroma/statuses/:id/react_with_emoji`
+### React to a post with a unicode emoji
+* Method: `POST`
+* Authentication: required
+* Params: `emoji`: A single character unicode emoji
+* Response: JSON, the status.
+
+## `POST /api/v1/pleroma/statuses/:id/unreact_with_emoji`
+### Remove a reaction to a post with a unicode emoji
+* Method: `POST`
+* Authentication: required
+* Params: `emoji`: A single character unicode emoji
+* Response: JSON, the status.
+
+## `GET /api/v1/pleroma/statuses/:id/emoji_reactions_by`
+### Get an object of emoji to account mappings with accounts that reacted to the post
+* Method: `GET`
+* Authentication: optional
+* Params: None
+* Response: JSON, a map of emoji to account list mappings.
+* Example Response:
+```json
+{
+ "๐" => [{"id" => "xyz.."...}, {"id" => "zyx..."}],
+ "๐ก" => [{"id" => "abc..."}]
+}
+```
diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex
@@ -9,6 +9,8 @@ defmodule Pleroma.Constants do
const(object_internal_fields,
do: [
+ "reactions",
+ "reaction_count",
"likes",
"like_count",
"announcements",
diff --git a/lib/pleroma/emoji-data.txt b/lib/pleroma/emoji-data.txt
@@ -0,0 +1,769 @@
+# emoji-data.txt
+# Date: 2019-01-15, 12:10:05 GMT
+# ยฉ 2019 Unicodeยฎ, Inc.
+# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+#
+# Emoji Data for UTS #51
+# Version: 12.0
+#
+# For documentation and usage, see http://www.unicode.org/reports/tr51
+#
+# Format:
+# <codepoint(s)> ; <property> # <comments>
+# Note: there is no guarantee as to the structure of whitespace or comments
+#
+# Characters and sequences are listed in code point order. Users should be shown a more natural order.
+# See the CLDR collation order for Emoji.
+
+
+# ================================================
+
+# All omitted code points have Emoji=No
+# @missing: 0000..10FFFF ; Emoji ; No
+
+0023 ; Emoji # 1.1 [1] (#๏ธ) number sign
+002A ; Emoji # 1.1 [1] (*๏ธ) asterisk
+0030..0039 ; Emoji # 1.1 [10] (0๏ธ..9๏ธ) digit zero..digit nine
+00A9 ; Emoji # 1.1 [1] (ยฉ๏ธ) copyright
+00AE ; Emoji # 1.1 [1] (ยฎ๏ธ) registered
+203C ; Emoji # 1.1 [1] (โผ๏ธ) double exclamation mark
+2049 ; Emoji # 3.0 [1] (โ๏ธ) exclamation question mark
+2122 ; Emoji # 1.1 [1] (โข๏ธ) trade mark
+2139 ; Emoji # 3.0 [1] (โน๏ธ) information
+2194..2199 ; Emoji # 1.1 [6] (โ๏ธ..โ๏ธ) left-right arrow..down-left arrow
+21A9..21AA ; Emoji # 1.1 [2] (โฉ๏ธ..โช๏ธ) right arrow curving left..left arrow curving right
+231A..231B ; Emoji # 1.1 [2] (โ..โ) watch..hourglass done
+2328 ; Emoji # 1.1 [1] (โจ๏ธ) keyboard
+23CF ; Emoji # 4.0 [1] (โ๏ธ) eject button
+23E9..23F3 ; Emoji # 6.0 [11] (โฉ..โณ) fast-forward button..hourglass not done
+23F8..23FA ; Emoji # 7.0 [3] (โธ๏ธ..โบ๏ธ) pause button..record button
+24C2 ; Emoji # 1.1 [1] (โ๏ธ) circled M
+25AA..25AB ; Emoji # 1.1 [2] (โช๏ธ..โซ๏ธ) black small square..white small square
+25B6 ; Emoji # 1.1 [1] (โถ๏ธ) play button
+25C0 ; Emoji # 1.1 [1] (โ๏ธ) reverse button
+25FB..25FE ; Emoji # 3.2 [4] (โป๏ธ..โพ) white medium square..black medium-small square
+2600..2604 ; Emoji # 1.1 [5] (โ๏ธ..โ๏ธ) sun..comet
+260E ; Emoji # 1.1 [1] (โ๏ธ) telephone
+2611 ; Emoji # 1.1 [1] (โ๏ธ) check box with check
+2614..2615 ; Emoji # 4.0 [2] (โ..โ) umbrella with rain drops..hot beverage
+2618 ; Emoji # 4.1 [1] (โ๏ธ) shamrock
+261D ; Emoji # 1.1 [1] (โ๏ธ) index pointing up
+2620 ; Emoji # 1.1 [1] (โ ๏ธ) skull and crossbones
+2622..2623 ; Emoji # 1.1 [2] (โข๏ธ..โฃ๏ธ) radioactive..biohazard
+2626 ; Emoji # 1.1 [1] (โฆ๏ธ) orthodox cross
+262A ; Emoji # 1.1 [1] (โช๏ธ) star and crescent
+262E..262F ; Emoji # 1.1 [2] (โฎ๏ธ..โฏ๏ธ) peace symbol..yin yang
+2638..263A ; Emoji # 1.1 [3] (โธ๏ธ..โบ๏ธ) wheel of dharma..smiling face
+2640 ; Emoji # 1.1 [1] (โ๏ธ) female sign
+2642 ; Emoji # 1.1 [1] (โ๏ธ) male sign
+2648..2653 ; Emoji # 1.1 [12] (โ..โ) Aries..Pisces
+265F..2660 ; Emoji # 1.1 [2] (โ๏ธ..โ ๏ธ) chess pawn..spade suit
+2663 ; Emoji # 1.1 [1] (โฃ๏ธ) club suit
+2665..2666 ; Emoji # 1.1 [2] (โฅ๏ธ..โฆ๏ธ) heart suit..diamond suit
+2668 ; Emoji # 1.1 [1] (โจ๏ธ) hot springs
+267B ; Emoji # 3.2 [1] (โป๏ธ) recycling symbol
+267E..267F ; Emoji # 4.1 [2] (โพ๏ธ..โฟ) infinity..wheelchair symbol
+2692..2697 ; Emoji # 4.1 [6] (โ๏ธ..โ๏ธ) hammer and pick..alembic
+2699 ; Emoji # 4.1 [1] (โ๏ธ) gear
+269B..269C ; Emoji # 4.1 [2] (โ๏ธ..โ๏ธ) atom symbol..fleur-de-lis
+26A0..26A1 ; Emoji # 4.0 [2] (โ ๏ธ..โก) warning..high voltage
+26AA..26AB ; Emoji # 4.1 [2] (โช..โซ) white circle..black circle
+26B0..26B1 ; Emoji # 4.1 [2] (โฐ๏ธ..โฑ๏ธ) coffin..funeral urn
+26BD..26BE ; Emoji # 5.2 [2] (โฝ..โพ) soccer ball..baseball
+26C4..26C5 ; Emoji # 5.2 [2] (โ..โ
) snowman without snow..sun behind cloud
+26C8 ; Emoji # 5.2 [1] (โ๏ธ) cloud with lightning and rain
+26CE ; Emoji # 6.0 [1] (โ) Ophiuchus
+26CF ; Emoji # 5.2 [1] (โ๏ธ) pick
+26D1 ; Emoji # 5.2 [1] (โ๏ธ) rescue workerโs helmet
+26D3..26D4 ; Emoji # 5.2 [2] (โ๏ธ..โ) chains..no entry
+26E9..26EA ; Emoji # 5.2 [2] (โฉ๏ธ..โช) shinto shrine..church
+26F0..26F5 ; Emoji # 5.2 [6] (โฐ๏ธ..โต) mountain..sailboat
+26F7..26FA ; Emoji # 5.2 [4] (โท๏ธ..โบ) skier..tent
+26FD ; Emoji # 5.2 [1] (โฝ) fuel pump
+2702 ; Emoji # 1.1 [1] (โ๏ธ) scissors
+2705 ; Emoji # 6.0 [1] (โ
) check mark button
+2708..2709 ; Emoji # 1.1 [2] (โ๏ธ..โ๏ธ) airplane..envelope
+270A..270B ; Emoji # 6.0 [2] (โ..โ) raised fist..raised hand
+270C..270D ; Emoji # 1.1 [2] (โ๏ธ..โ๏ธ) victory hand..writing hand
+270F ; Emoji # 1.1 [1] (โ๏ธ) pencil
+2712 ; Emoji # 1.1 [1] (โ๏ธ) black nib
+2714 ; Emoji # 1.1 [1] (โ๏ธ) check mark
+2716 ; Emoji # 1.1 [1] (โ๏ธ) multiplication sign
+271D ; Emoji # 1.1 [1] (โ๏ธ) latin cross
+2721 ; Emoji # 1.1 [1] (โก๏ธ) star of David
+2728 ; Emoji # 6.0 [1] (โจ) sparkles
+2733..2734 ; Emoji # 1.1 [2] (โณ๏ธ..โด๏ธ) eight-spoked asterisk..eight-pointed star
+2744 ; Emoji # 1.1 [1] (โ๏ธ) snowflake
+2747 ; Emoji # 1.1 [1] (โ๏ธ) sparkle
+274C ; Emoji # 6.0 [1] (โ) cross mark
+274E ; Emoji # 6.0 [1] (โ) cross mark button
+2753..2755 ; Emoji # 6.0 [3] (โ..โ) question mark..white exclamation mark
+2757 ; Emoji # 5.2 [1] (โ) exclamation mark
+2763..2764 ; Emoji # 1.1 [2] (โฃ๏ธ..โค๏ธ) heart exclamation..red heart
+2795..2797 ; Emoji # 6.0 [3] (โ..โ) plus sign..division sign
+27A1 ; Emoji # 1.1 [1] (โก๏ธ) right arrow
+27B0 ; Emoji # 6.0 [1] (โฐ) curly loop
+27BF ; Emoji # 6.0 [1] (โฟ) double curly loop
+2934..2935 ; Emoji # 3.2 [2] (โคด๏ธ..โคต๏ธ) right arrow curving up..right arrow curving down
+2B05..2B07 ; Emoji # 4.0 [3] (โฌ
๏ธ..โฌ๏ธ) left arrow..down arrow
+2B1B..2B1C ; Emoji # 5.1 [2] (โฌ..โฌ) black large square..white large square
+2B50 ; Emoji # 5.1 [1] (โญ) star
+2B55 ; Emoji # 5.2 [1] (โญ) hollow red circle
+3030 ; Emoji # 1.1 [1] (ใฐ๏ธ) wavy dash
+303D ; Emoji # 3.2 [1] (ใฝ๏ธ) part alternation mark
+3297 ; Emoji # 1.1 [1] (ใ๏ธ) Japanese โcongratulationsโ button
+3299 ; Emoji # 1.1 [1] (ใ๏ธ) Japanese โsecretโ button
+1F004 ; Emoji # 5.1 [1] (๐) mahjong red dragon
+1F0CF ; Emoji # 6.0 [1] (๐) joker
+1F170..1F171 ; Emoji # 6.0 [2] (๐
ฐ๏ธ..๐
ฑ๏ธ) A button (blood type)..B button (blood type)
+1F17E ; Emoji # 6.0 [1] (๐
พ๏ธ) O button (blood type)
+1F17F ; Emoji # 5.2 [1] (๐
ฟ๏ธ) P button
+1F18E ; Emoji # 6.0 [1] (๐) AB button (blood type)
+1F191..1F19A ; Emoji # 6.0 [10] (๐..๐) CL button..VS button
+1F1E6..1F1FF ; Emoji # 6.0 [26] (๐ฆ..๐ฟ) regional indicator symbol letter a..regional indicator symbol letter z
+1F201..1F202 ; Emoji # 6.0 [2] (๐..๐๏ธ) Japanese โhereโ button..Japanese โservice chargeโ button
+1F21A ; Emoji # 5.2 [1] (๐) Japanese โfree of chargeโ button
+1F22F ; Emoji # 5.2 [1] (๐ฏ) Japanese โreservedโ button
+1F232..1F23A ; Emoji # 6.0 [9] (๐ฒ..๐บ) Japanese โprohibitedโ button..Japanese โopen for businessโ button
+1F250..1F251 ; Emoji # 6.0 [2] (๐..๐) Japanese โbargainโ button..Japanese โacceptableโ button
+1F300..1F320 ; Emoji # 6.0 [33] (๐..๐ ) cyclone..shooting star
+1F321 ; Emoji # 7.0 [1] (๐ก๏ธ) thermometer
+1F324..1F32C ; Emoji # 7.0 [9] (๐ค๏ธ..๐ฌ๏ธ) sun behind small cloud..wind face
+1F32D..1F32F ; Emoji # 8.0 [3] (๐ญ..๐ฏ) hot dog..burrito
+1F330..1F335 ; Emoji # 6.0 [6] (๐ฐ..๐ต) chestnut..cactus
+1F336 ; Emoji # 7.0 [1] (๐ถ๏ธ) hot pepper
+1F337..1F37C ; Emoji # 6.0 [70] (๐ท..๐ผ) tulip..baby bottle
+1F37D ; Emoji # 7.0 [1] (๐ฝ๏ธ) fork and knife with plate
+1F37E..1F37F ; Emoji # 8.0 [2] (๐พ..๐ฟ) bottle with popping cork..popcorn
+1F380..1F393 ; Emoji # 6.0 [20] (๐..๐) ribbon..graduation cap
+1F396..1F397 ; Emoji # 7.0 [2] (๐๏ธ..๐๏ธ) military medal..reminder ribbon
+1F399..1F39B ; Emoji # 7.0 [3] (๐๏ธ..๐๏ธ) studio microphone..control knobs
+1F39E..1F39F ; Emoji # 7.0 [2] (๐๏ธ..๐๏ธ) film frames..admission tickets
+1F3A0..1F3C4 ; Emoji # 6.0 [37] (๐ ..๐) carousel horse..person surfing
+1F3C5 ; Emoji # 7.0 [1] (๐
) sports medal
+1F3C6..1F3CA ; Emoji # 6.0 [5] (๐..๐) trophy..person swimming
+1F3CB..1F3CE ; Emoji # 7.0 [4] (๐๏ธ..๐๏ธ) person lifting weights..racing car
+1F3CF..1F3D3 ; Emoji # 8.0 [5] (๐..๐) cricket game..ping pong
+1F3D4..1F3DF ; Emoji # 7.0 [12] (๐๏ธ..๐๏ธ) snow-capped mountain..stadium
+1F3E0..1F3F0 ; Emoji # 6.0 [17] (๐ ..๐ฐ) house..castle
+1F3F3..1F3F5 ; Emoji # 7.0 [3] (๐ณ๏ธ..๐ต๏ธ) white flag..rosette
+1F3F7 ; Emoji # 7.0 [1] (๐ท๏ธ) label
+1F3F8..1F3FF ; Emoji # 8.0 [8] (๐ธ..๐ฟ) badminton..dark skin tone
+1F400..1F43E ; Emoji # 6.0 [63] (๐..๐พ) rat..paw prints
+1F43F ; Emoji # 7.0 [1] (๐ฟ๏ธ) chipmunk
+1F440 ; Emoji # 6.0 [1] (๐) eyes
+1F441 ; Emoji # 7.0 [1] (๐๏ธ) eye
+1F442..1F4F7 ; Emoji # 6.0[182] (๐..๐ท) ear..camera
+1F4F8 ; Emoji # 7.0 [1] (๐ธ) camera with flash
+1F4F9..1F4FC ; Emoji # 6.0 [4] (๐น..๐ผ) video camera..videocassette
+1F4FD ; Emoji # 7.0 [1] (๐ฝ๏ธ) film projector
+1F4FF ; Emoji # 8.0 [1] (๐ฟ) prayer beads
+1F500..1F53D ; Emoji # 6.0 [62] (๐..๐ฝ) shuffle tracks button..downwards button
+1F549..1F54A ; Emoji # 7.0 [2] (๐๏ธ..๐๏ธ) om..dove
+1F54B..1F54E ; Emoji # 8.0 [4] (๐..๐) kaaba..menorah
+1F550..1F567 ; Emoji # 6.0 [24] (๐..๐ง) one oโclock..twelve-thirty
+1F56F..1F570 ; Emoji # 7.0 [2] (๐ฏ๏ธ..๐ฐ๏ธ) candle..mantelpiece clock
+1F573..1F579 ; Emoji # 7.0 [7] (๐ณ๏ธ..๐น๏ธ) hole..joystick
+1F57A ; Emoji # 9.0 [1] (๐บ) man dancing
+1F587 ; Emoji # 7.0 [1] (๐๏ธ) linked paperclips
+1F58A..1F58D ; Emoji # 7.0 [4] (๐๏ธ..๐๏ธ) pen..crayon
+1F590 ; Emoji # 7.0 [1] (๐๏ธ) hand with fingers splayed
+1F595..1F596 ; Emoji # 7.0 [2] (๐..๐) middle finger..vulcan salute
+1F5A4 ; Emoji # 9.0 [1] (๐ค) black heart
+1F5A5 ; Emoji # 7.0 [1] (๐ฅ๏ธ) desktop computer
+1F5A8 ; Emoji # 7.0 [1] (๐จ๏ธ) printer
+1F5B1..1F5B2 ; Emoji # 7.0 [2] (๐ฑ๏ธ..๐ฒ๏ธ) computer mouse..trackball
+1F5BC ; Emoji # 7.0 [1] (๐ผ๏ธ) framed picture
+1F5C2..1F5C4 ; Emoji # 7.0 [3] (๐๏ธ..๐๏ธ) card index dividers..file cabinet
+1F5D1..1F5D3 ; Emoji # 7.0 [3] (๐๏ธ..๐๏ธ) wastebasket..spiral calendar
+1F5DC..1F5DE ; Emoji # 7.0 [3] (๐๏ธ..๐๏ธ) clamp..rolled-up newspaper
+1F5E1 ; Emoji # 7.0 [1] (๐ก๏ธ) dagger
+1F5E3 ; Emoji # 7.0 [1] (๐ฃ๏ธ) speaking head
+1F5E8 ; Emoji # 7.0 [1] (๐จ๏ธ) left speech bubble
+1F5EF ; Emoji # 7.0 [1] (๐ฏ๏ธ) right anger bubble
+1F5F3 ; Emoji # 7.0 [1] (๐ณ๏ธ) ballot box with ballot
+1F5FA ; Emoji # 7.0 [1] (๐บ๏ธ) world map
+1F5FB..1F5FF ; Emoji # 6.0 [5] (๐ป..๐ฟ) mount fuji..moai
+1F600 ; Emoji # 6.1 [1] (๐) grinning face
+1F601..1F610 ; Emoji # 6.0 [16] (๐..๐) beaming face with smiling eyes..neutral face
+1F611 ; Emoji # 6.1 [1] (๐) expressionless face
+1F612..1F614 ; Emoji # 6.0 [3] (๐..๐) unamused face..pensive face
+1F615 ; Emoji # 6.1 [1] (๐) confused face
+1F616 ; Emoji # 6.0 [1] (๐) confounded face
+1F617 ; Emoji # 6.1 [1] (๐) kissing face
+1F618 ; Emoji # 6.0 [1] (๐) face blowing a kiss
+1F619 ; Emoji # 6.1 [1] (๐) kissing face with smiling eyes
+1F61A ; Emoji # 6.0 [1] (๐) kissing face with closed eyes
+1F61B ; Emoji # 6.1 [1] (๐) face with tongue
+1F61C..1F61E ; Emoji # 6.0 [3] (๐..๐) winking face with tongue..disappointed face
+1F61F ; Emoji # 6.1 [1] (๐) worried face
+1F620..1F625 ; Emoji # 6.0 [6] (๐ ..๐ฅ) angry face..sad but relieved face
+1F626..1F627 ; Emoji # 6.1 [2] (๐ฆ..๐ง) frowning face with open mouth..anguished face
+1F628..1F62B ; Emoji # 6.0 [4] (๐จ..๐ซ) fearful face..tired face
+1F62C ; Emoji # 6.1 [1] (๐ฌ) grimacing face
+1F62D ; Emoji # 6.0 [1] (๐ญ) loudly crying face
+1F62E..1F62F ; Emoji # 6.1 [2] (๐ฎ..๐ฏ) face with open mouth..hushed face
+1F630..1F633 ; Emoji # 6.0 [4] (๐ฐ..๐ณ) anxious face with sweat..flushed face
+1F634 ; Emoji # 6.1 [1] (๐ด) sleeping face
+1F635..1F640 ; Emoji # 6.0 [12] (๐ต..๐) dizzy face..weary cat
+1F641..1F642 ; Emoji # 7.0 [2] (๐..๐) slightly frowning face..slightly smiling face
+1F643..1F644 ; Emoji # 8.0 [2] (๐..๐) upside-down face..face with rolling eyes
+1F645..1F64F ; Emoji # 6.0 [11] (๐
..๐) person gesturing NO..folded hands
+1F680..1F6C5 ; Emoji # 6.0 [70] (๐..๐
) rocket..left luggage
+1F6CB..1F6CF ; Emoji # 7.0 [5] (๐๏ธ..๐๏ธ) couch and lamp..bed
+1F6D0 ; Emoji # 8.0 [1] (๐) place of worship
+1F6D1..1F6D2 ; Emoji # 9.0 [2] (๐..๐) stop sign..shopping cart
+1F6D5 ; Emoji # 12.0 [1] (๐) hindu temple
+1F6E0..1F6E5 ; Emoji # 7.0 [6] (๐ ๏ธ..๐ฅ๏ธ) hammer and wrench..motor boat
+1F6E9 ; Emoji # 7.0 [1] (๐ฉ๏ธ) small airplane
+1F6EB..1F6EC ; Emoji # 7.0 [2] (๐ซ..๐ฌ) airplane departure..airplane arrival
+1F6F0 ; Emoji # 7.0 [1] (๐ฐ๏ธ) satellite
+1F6F3 ; Emoji # 7.0 [1] (๐ณ๏ธ) passenger ship
+1F6F4..1F6F6 ; Emoji # 9.0 [3] (๐ด..๐ถ) kick scooter..canoe
+1F6F7..1F6F8 ; Emoji # 10.0 [2] (๐ท..๐ธ) sled..flying saucer
+1F6F9 ; Emoji # 11.0 [1] (๐น) skateboard
+1F6FA ; Emoji # 12.0 [1] (๐บ) auto rickshaw
+1F7E0..1F7EB ; Emoji # 12.0 [12] (๐ ..๐ซ) orange circle..brown square
+1F90D..1F90F ; Emoji # 12.0 [3] (๐ค..๐ค) white heart..pinching hand
+1F910..1F918 ; Emoji # 8.0 [9] (๐ค..๐ค) zipper-mouth face..sign of the horns
+1F919..1F91E ; Emoji # 9.0 [6] (๐ค..๐ค) call me hand..crossed fingers
+1F91F ; Emoji # 10.0 [1] (๐ค) love-you gesture
+1F920..1F927 ; Emoji # 9.0 [8] (๐ค ..๐คง) cowboy hat face..sneezing face
+1F928..1F92F ; Emoji # 10.0 [8] (๐คจ..๐คฏ) face with raised eyebrow..exploding head
+1F930 ; Emoji # 9.0 [1] (๐คฐ) pregnant woman
+1F931..1F932 ; Emoji # 10.0 [2] (๐คฑ..๐คฒ) breast-feeding..palms up together
+1F933..1F93A ; Emoji # 9.0 [8] (๐คณ..๐คบ) selfie..person fencing
+1F93C..1F93E ; Emoji # 9.0 [3] (๐คผ..๐คพ) people wrestling..person playing handball
+1F93F ; Emoji # 12.0 [1] (๐คฟ) diving mask
+1F940..1F945 ; Emoji # 9.0 [6] (๐ฅ..๐ฅ
) wilted flower..goal net
+1F947..1F94B ; Emoji # 9.0 [5] (๐ฅ..๐ฅ) 1st place medal..martial arts uniform
+1F94C ; Emoji # 10.0 [1] (๐ฅ) curling stone
+1F94D..1F94F ; Emoji # 11.0 [3] (๐ฅ..๐ฅ) lacrosse..flying disc
+1F950..1F95E ; Emoji # 9.0 [15] (๐ฅ..๐ฅ) croissant..pancakes
+1F95F..1F96B ; Emoji # 10.0 [13] (๐ฅ..๐ฅซ) dumpling..canned food
+1F96C..1F970 ; Emoji # 11.0 [5] (๐ฅฌ..๐ฅฐ) leafy green..smiling face with hearts
+1F971 ; Emoji # 12.0 [1] (๐ฅฑ) yawning face
+1F973..1F976 ; Emoji # 11.0 [4] (๐ฅณ..๐ฅถ) partying face..cold face
+1F97A ; Emoji # 11.0 [1] (๐ฅบ) pleading face
+1F97B ; Emoji # 12.0 [1] (๐ฅป) sari
+1F97C..1F97F ; Emoji # 11.0 [4] (๐ฅผ..๐ฅฟ) lab coat..flat shoe
+1F980..1F984 ; Emoji # 8.0 [5] (๐ฆ..๐ฆ) crab..unicorn
+1F985..1F991 ; Emoji # 9.0 [13] (๐ฆ
..๐ฆ) eagle..squid
+1F992..1F997 ; Emoji # 10.0 [6] (๐ฆ..๐ฆ) giraffe..cricket
+1F998..1F9A2 ; Emoji # 11.0 [11] (๐ฆ..๐ฆข) kangaroo..swan
+1F9A5..1F9AA ; Emoji # 12.0 [6] (๐ฆฅ..๐ฆช) sloth..oyster
+1F9AE..1F9AF ; Emoji # 12.0 [2] (๐ฆฎ..๐ฆฏ) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji # 11.0 [10] (๐ฆฐ..๐ฆน) red hair..supervillain
+1F9BA..1F9BF ; Emoji # 12.0 [6] (๐ฆบ..๐ฆฟ) safety vest..mechanical leg
+1F9C0 ; Emoji # 8.0 [1] (๐ง) cheese wedge
+1F9C1..1F9C2 ; Emoji # 11.0 [2] (๐ง..๐ง) cupcake..salt
+1F9C3..1F9CA ; Emoji # 12.0 [8] (๐ง..๐ง) beverage box..ice cube
+1F9CD..1F9CF ; Emoji # 12.0 [3] (๐ง..๐ง) person standing..deaf person
+1F9D0..1F9E6 ; Emoji # 10.0 [23] (๐ง..๐งฆ) face with monocle..socks
+1F9E7..1F9FF ; Emoji # 11.0 [25] (๐งง..๐งฟ) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji # 12.0 [4] (๐ฉฐ..๐ฉณ) ballet shoes..shorts
+1FA78..1FA7A ; Emoji # 12.0 [3] (๐ฉธ..๐ฉบ) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji # 12.0 [3] (๐ช..๐ช) yo-yo..parachute
+1FA90..1FA95 ; Emoji # 12.0 [6] (๐ช..๐ช) ringed planet..banjo
+
+# Total elements: 1311
+
+# ================================================
+
+# All omitted code points have Emoji_Presentation=No
+# @missing: 0000..10FFFF ; Emoji_Presentation ; No
+
+231A..231B ; Emoji_Presentation # 1.1 [2] (โ..โ) watch..hourglass done
+23E9..23EC ; Emoji_Presentation # 6.0 [4] (โฉ..โฌ) fast-forward button..fast down button
+23F0 ; Emoji_Presentation # 6.0 [1] (โฐ) alarm clock
+23F3 ; Emoji_Presentation # 6.0 [1] (โณ) hourglass not done
+25FD..25FE ; Emoji_Presentation # 3.2 [2] (โฝ..โพ) white medium-small square..black medium-small square
+2614..2615 ; Emoji_Presentation # 4.0 [2] (โ..โ) umbrella with rain drops..hot beverage
+2648..2653 ; Emoji_Presentation # 1.1 [12] (โ..โ) Aries..Pisces
+267F ; Emoji_Presentation # 4.1 [1] (โฟ) wheelchair symbol
+2693 ; Emoji_Presentation # 4.1 [1] (โ) anchor
+26A1 ; Emoji_Presentation # 4.0 [1] (โก) high voltage
+26AA..26AB ; Emoji_Presentation # 4.1 [2] (โช..โซ) white circle..black circle
+26BD..26BE ; Emoji_Presentation # 5.2 [2] (โฝ..โพ) soccer ball..baseball
+26C4..26C5 ; Emoji_Presentation # 5.2 [2] (โ..โ
) snowman without snow..sun behind cloud
+26CE ; Emoji_Presentation # 6.0 [1] (โ) Ophiuchus
+26D4 ; Emoji_Presentation # 5.2 [1] (โ) no entry
+26EA ; Emoji_Presentation # 5.2 [1] (โช) church
+26F2..26F3 ; Emoji_Presentation # 5.2 [2] (โฒ..โณ) fountain..flag in hole
+26F5 ; Emoji_Presentation # 5.2 [1] (โต) sailboat
+26FA ; Emoji_Presentation # 5.2 [1] (โบ) tent
+26FD ; Emoji_Presentation # 5.2 [1] (โฝ) fuel pump
+2705 ; Emoji_Presentation # 6.0 [1] (โ
) check mark button
+270A..270B ; Emoji_Presentation # 6.0 [2] (โ..โ) raised fist..raised hand
+2728 ; Emoji_Presentation # 6.0 [1] (โจ) sparkles
+274C ; Emoji_Presentation # 6.0 [1] (โ) cross mark
+274E ; Emoji_Presentation # 6.0 [1] (โ) cross mark button
+2753..2755 ; Emoji_Presentation # 6.0 [3] (โ..โ) question mark..white exclamation mark
+2757 ; Emoji_Presentation # 5.2 [1] (โ) exclamation mark
+2795..2797 ; Emoji_Presentation # 6.0 [3] (โ..โ) plus sign..division sign
+27B0 ; Emoji_Presentation # 6.0 [1] (โฐ) curly loop
+27BF ; Emoji_Presentation # 6.0 [1] (โฟ) double curly loop
+2B1B..2B1C ; Emoji_Presentation # 5.1 [2] (โฌ..โฌ) black large square..white large square
+2B50 ; Emoji_Presentation # 5.1 [1] (โญ) star
+2B55 ; Emoji_Presentation # 5.2 [1] (โญ) hollow red circle
+1F004 ; Emoji_Presentation # 5.1 [1] (๐) mahjong red dragon
+1F0CF ; Emoji_Presentation # 6.0 [1] (๐) joker
+1F18E ; Emoji_Presentation # 6.0 [1] (๐) AB button (blood type)
+1F191..1F19A ; Emoji_Presentation # 6.0 [10] (๐..๐) CL button..VS button
+1F1E6..1F1FF ; Emoji_Presentation # 6.0 [26] (๐ฆ..๐ฟ) regional indicator symbol letter a..regional indicator symbol letter z
+1F201 ; Emoji_Presentation # 6.0 [1] (๐) Japanese โhereโ button
+1F21A ; Emoji_Presentation # 5.2 [1] (๐) Japanese โfree of chargeโ button
+1F22F ; Emoji_Presentation # 5.2 [1] (๐ฏ) Japanese โreservedโ button
+1F232..1F236 ; Emoji_Presentation # 6.0 [5] (๐ฒ..๐ถ) Japanese โprohibitedโ button..Japanese โnot free of chargeโ button
+1F238..1F23A ; Emoji_Presentation # 6.0 [3] (๐ธ..๐บ) Japanese โapplicationโ button..Japanese โopen for businessโ button
+1F250..1F251 ; Emoji_Presentation # 6.0 [2] (๐..๐) Japanese โbargainโ button..Japanese โacceptableโ button
+1F300..1F320 ; Emoji_Presentation # 6.0 [33] (๐..๐ ) cyclone..shooting star
+1F32D..1F32F ; Emoji_Presentation # 8.0 [3] (๐ญ..๐ฏ) hot dog..burrito
+1F330..1F335 ; Emoji_Presentation # 6.0 [6] (๐ฐ..๐ต) chestnut..cactus
+1F337..1F37C ; Emoji_Presentation # 6.0 [70] (๐ท..๐ผ) tulip..baby bottle
+1F37E..1F37F ; Emoji_Presentation # 8.0 [2] (๐พ..๐ฟ) bottle with popping cork..popcorn
+1F380..1F393 ; Emoji_Presentation # 6.0 [20] (๐..๐) ribbon..graduation cap
+1F3A0..1F3C4 ; Emoji_Presentation # 6.0 [37] (๐ ..๐) carousel horse..person surfing
+1F3C5 ; Emoji_Presentation # 7.0 [1] (๐
) sports medal
+1F3C6..1F3CA ; Emoji_Presentation # 6.0 [5] (๐..๐) trophy..person swimming
+1F3CF..1F3D3 ; Emoji_Presentation # 8.0 [5] (๐..๐) cricket game..ping pong
+1F3E0..1F3F0 ; Emoji_Presentation # 6.0 [17] (๐ ..๐ฐ) house..castle
+1F3F4 ; Emoji_Presentation # 7.0 [1] (๐ด) black flag
+1F3F8..1F3FF ; Emoji_Presentation # 8.0 [8] (๐ธ..๐ฟ) badminton..dark skin tone
+1F400..1F43E ; Emoji_Presentation # 6.0 [63] (๐..๐พ) rat..paw prints
+1F440 ; Emoji_Presentation # 6.0 [1] (๐) eyes
+1F442..1F4F7 ; Emoji_Presentation # 6.0[182] (๐..๐ท) ear..camera
+1F4F8 ; Emoji_Presentation # 7.0 [1] (๐ธ) camera with flash
+1F4F9..1F4FC ; Emoji_Presentation # 6.0 [4] (๐น..๐ผ) video camera..videocassette
+1F4FF ; Emoji_Presentation # 8.0 [1] (๐ฟ) prayer beads
+1F500..1F53D ; Emoji_Presentation # 6.0 [62] (๐..๐ฝ) shuffle tracks button..downwards button
+1F54B..1F54E ; Emoji_Presentation # 8.0 [4] (๐..๐) kaaba..menorah
+1F550..1F567 ; Emoji_Presentation # 6.0 [24] (๐..๐ง) one oโclock..twelve-thirty
+1F57A ; Emoji_Presentation # 9.0 [1] (๐บ) man dancing
+1F595..1F596 ; Emoji_Presentation # 7.0 [2] (๐..๐) middle finger..vulcan salute
+1F5A4 ; Emoji_Presentation # 9.0 [1] (๐ค) black heart
+1F5FB..1F5FF ; Emoji_Presentation # 6.0 [5] (๐ป..๐ฟ) mount fuji..moai
+1F600 ; Emoji_Presentation # 6.1 [1] (๐) grinning face
+1F601..1F610 ; Emoji_Presentation # 6.0 [16] (๐..๐) beaming face with smiling eyes..neutral face
+1F611 ; Emoji_Presentation # 6.1 [1] (๐) expressionless face
+1F612..1F614 ; Emoji_Presentation # 6.0 [3] (๐..๐) unamused face..pensive face
+1F615 ; Emoji_Presentation # 6.1 [1] (๐) confused face
+1F616 ; Emoji_Presentation # 6.0 [1] (๐) confounded face
+1F617 ; Emoji_Presentation # 6.1 [1] (๐) kissing face
+1F618 ; Emoji_Presentation # 6.0 [1] (๐) face blowing a kiss
+1F619 ; Emoji_Presentation # 6.1 [1] (๐) kissing face with smiling eyes
+1F61A ; Emoji_Presentation # 6.0 [1] (๐) kissing face with closed eyes
+1F61B ; Emoji_Presentation # 6.1 [1] (๐) face with tongue
+1F61C..1F61E ; Emoji_Presentation # 6.0 [3] (๐..๐) winking face with tongue..disappointed face
+1F61F ; Emoji_Presentation # 6.1 [1] (๐) worried face
+1F620..1F625 ; Emoji_Presentation # 6.0 [6] (๐ ..๐ฅ) angry face..sad but relieved face
+1F626..1F627 ; Emoji_Presentation # 6.1 [2] (๐ฆ..๐ง) frowning face with open mouth..anguished face
+1F628..1F62B ; Emoji_Presentation # 6.0 [4] (๐จ..๐ซ) fearful face..tired face
+1F62C ; Emoji_Presentation # 6.1 [1] (๐ฌ) grimacing face
+1F62D ; Emoji_Presentation # 6.0 [1] (๐ญ) loudly crying face
+1F62E..1F62F ; Emoji_Presentation # 6.1 [2] (๐ฎ..๐ฏ) face with open mouth..hushed face
+1F630..1F633 ; Emoji_Presentation # 6.0 [4] (๐ฐ..๐ณ) anxious face with sweat..flushed face
+1F634 ; Emoji_Presentation # 6.1 [1] (๐ด) sleeping face
+1F635..1F640 ; Emoji_Presentation # 6.0 [12] (๐ต..๐) dizzy face..weary cat
+1F641..1F642 ; Emoji_Presentation # 7.0 [2] (๐..๐) slightly frowning face..slightly smiling face
+1F643..1F644 ; Emoji_Presentation # 8.0 [2] (๐..๐) upside-down face..face with rolling eyes
+1F645..1F64F ; Emoji_Presentation # 6.0 [11] (๐
..๐) person gesturing NO..folded hands
+1F680..1F6C5 ; Emoji_Presentation # 6.0 [70] (๐..๐
) rocket..left luggage
+1F6CC ; Emoji_Presentation # 7.0 [1] (๐) person in bed
+1F6D0 ; Emoji_Presentation # 8.0 [1] (๐) place of worship
+1F6D1..1F6D2 ; Emoji_Presentation # 9.0 [2] (๐..๐) stop sign..shopping cart
+1F6D5 ; Emoji_Presentation # 12.0 [1] (๐) hindu temple
+1F6EB..1F6EC ; Emoji_Presentation # 7.0 [2] (๐ซ..๐ฌ) airplane departure..airplane arrival
+1F6F4..1F6F6 ; Emoji_Presentation # 9.0 [3] (๐ด..๐ถ) kick scooter..canoe
+1F6F7..1F6F8 ; Emoji_Presentation # 10.0 [2] (๐ท..๐ธ) sled..flying saucer
+1F6F9 ; Emoji_Presentation # 11.0 [1] (๐น) skateboard
+1F6FA ; Emoji_Presentation # 12.0 [1] (๐บ) auto rickshaw
+1F7E0..1F7EB ; Emoji_Presentation # 12.0 [12] (๐ ..๐ซ) orange circle..brown square
+1F90D..1F90F ; Emoji_Presentation # 12.0 [3] (๐ค..๐ค) white heart..pinching hand
+1F910..1F918 ; Emoji_Presentation # 8.0 [9] (๐ค..๐ค) zipper-mouth face..sign of the horns
+1F919..1F91E ; Emoji_Presentation # 9.0 [6] (๐ค..๐ค) call me hand..crossed fingers
+1F91F ; Emoji_Presentation # 10.0 [1] (๐ค) love-you gesture
+1F920..1F927 ; Emoji_Presentation # 9.0 [8] (๐ค ..๐คง) cowboy hat face..sneezing face
+1F928..1F92F ; Emoji_Presentation # 10.0 [8] (๐คจ..๐คฏ) face with raised eyebrow..exploding head
+1F930 ; Emoji_Presentation # 9.0 [1] (๐คฐ) pregnant woman
+1F931..1F932 ; Emoji_Presentation # 10.0 [2] (๐คฑ..๐คฒ) breast-feeding..palms up together
+1F933..1F93A ; Emoji_Presentation # 9.0 [8] (๐คณ..๐คบ) selfie..person fencing
+1F93C..1F93E ; Emoji_Presentation # 9.0 [3] (๐คผ..๐คพ) people wrestling..person playing handball
+1F93F ; Emoji_Presentation # 12.0 [1] (๐คฟ) diving mask
+1F940..1F945 ; Emoji_Presentation # 9.0 [6] (๐ฅ..๐ฅ
) wilted flower..goal net
+1F947..1F94B ; Emoji_Presentation # 9.0 [5] (๐ฅ..๐ฅ) 1st place medal..martial arts uniform
+1F94C ; Emoji_Presentation # 10.0 [1] (๐ฅ) curling stone
+1F94D..1F94F ; Emoji_Presentation # 11.0 [3] (๐ฅ..๐ฅ) lacrosse..flying disc
+1F950..1F95E ; Emoji_Presentation # 9.0 [15] (๐ฅ..๐ฅ) croissant..pancakes
+1F95F..1F96B ; Emoji_Presentation # 10.0 [13] (๐ฅ..๐ฅซ) dumpling..canned food
+1F96C..1F970 ; Emoji_Presentation # 11.0 [5] (๐ฅฌ..๐ฅฐ) leafy green..smiling face with hearts
+1F971 ; Emoji_Presentation # 12.0 [1] (๐ฅฑ) yawning face
+1F973..1F976 ; Emoji_Presentation # 11.0 [4] (๐ฅณ..๐ฅถ) partying face..cold face
+1F97A ; Emoji_Presentation # 11.0 [1] (๐ฅบ) pleading face
+1F97B ; Emoji_Presentation # 12.0 [1] (๐ฅป) sari
+1F97C..1F97F ; Emoji_Presentation # 11.0 [4] (๐ฅผ..๐ฅฟ) lab coat..flat shoe
+1F980..1F984 ; Emoji_Presentation # 8.0 [5] (๐ฆ..๐ฆ) crab..unicorn
+1F985..1F991 ; Emoji_Presentation # 9.0 [13] (๐ฆ
..๐ฆ) eagle..squid
+1F992..1F997 ; Emoji_Presentation # 10.0 [6] (๐ฆ..๐ฆ) giraffe..cricket
+1F998..1F9A2 ; Emoji_Presentation # 11.0 [11] (๐ฆ..๐ฆข) kangaroo..swan
+1F9A5..1F9AA ; Emoji_Presentation # 12.0 [6] (๐ฆฅ..๐ฆช) sloth..oyster
+1F9AE..1F9AF ; Emoji_Presentation # 12.0 [2] (๐ฆฎ..๐ฆฏ) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji_Presentation # 11.0 [10] (๐ฆฐ..๐ฆน) red hair..supervillain
+1F9BA..1F9BF ; Emoji_Presentation # 12.0 [6] (๐ฆบ..๐ฆฟ) safety vest..mechanical leg
+1F9C0 ; Emoji_Presentation # 8.0 [1] (๐ง) cheese wedge
+1F9C1..1F9C2 ; Emoji_Presentation # 11.0 [2] (๐ง..๐ง) cupcake..salt
+1F9C3..1F9CA ; Emoji_Presentation # 12.0 [8] (๐ง..๐ง) beverage box..ice cube
+1F9CD..1F9CF ; Emoji_Presentation # 12.0 [3] (๐ง..๐ง) person standing..deaf person
+1F9D0..1F9E6 ; Emoji_Presentation # 10.0 [23] (๐ง..๐งฆ) face with monocle..socks
+1F9E7..1F9FF ; Emoji_Presentation # 11.0 [25] (๐งง..๐งฟ) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji_Presentation # 12.0 [4] (๐ฉฐ..๐ฉณ) ballet shoes..shorts
+1FA78..1FA7A ; Emoji_Presentation # 12.0 [3] (๐ฉธ..๐ฉบ) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji_Presentation # 12.0 [3] (๐ช..๐ช) yo-yo..parachute
+1FA90..1FA95 ; Emoji_Presentation # 12.0 [6] (๐ช..๐ช) ringed planet..banjo
+
+# Total elements: 1093
+
+# ================================================
+
+# All omitted code points have Emoji_Modifier=No
+# @missing: 0000..10FFFF ; Emoji_Modifier ; No
+
+1F3FB..1F3FF ; Emoji_Modifier # 8.0 [5] (๐ป..๐ฟ) light skin tone..dark skin tone
+
+# Total elements: 5
+
+# ================================================
+
+# All omitted code points have Emoji_Modifier_Base=No
+# @missing: 0000..10FFFF ; Emoji_Modifier_Base ; No
+
+261D ; Emoji_Modifier_Base # 1.1 [1] (โ๏ธ) index pointing up
+26F9 ; Emoji_Modifier_Base # 5.2 [1] (โน๏ธ) person bouncing ball
+270A..270B ; Emoji_Modifier_Base # 6.0 [2] (โ..โ) raised fist..raised hand
+270C..270D ; Emoji_Modifier_Base # 1.1 [2] (โ๏ธ..โ๏ธ) victory hand..writing hand
+1F385 ; Emoji_Modifier_Base # 6.0 [1] (๐
) Santa Claus
+1F3C2..1F3C4 ; Emoji_Modifier_Base # 6.0 [3] (๐..๐) snowboarder..person surfing
+1F3C7 ; Emoji_Modifier_Base # 6.0 [1] (๐) horse racing
+1F3CA ; Emoji_Modifier_Base # 6.0 [1] (๐) person swimming
+1F3CB..1F3CC ; Emoji_Modifier_Base # 7.0 [2] (๐๏ธ..๐๏ธ) person lifting weights..person golfing
+1F442..1F443 ; Emoji_Modifier_Base # 6.0 [2] (๐..๐) ear..nose
+1F446..1F450 ; Emoji_Modifier_Base # 6.0 [11] (๐..๐) backhand index pointing up..open hands
+1F466..1F478 ; Emoji_Modifier_Base # 6.0 [19] (๐ฆ..๐ธ) boy..princess
+1F47C ; Emoji_Modifier_Base # 6.0 [1] (๐ผ) baby angel
+1F481..1F483 ; Emoji_Modifier_Base # 6.0 [3] (๐..๐) person tipping hand..woman dancing
+1F485..1F487 ; Emoji_Modifier_Base # 6.0 [3] (๐
..๐) nail polish..person getting haircut
+1F48F ; Emoji_Modifier_Base # 6.0 [1] (๐) kiss
+1F491 ; Emoji_Modifier_Base # 6.0 [1] (๐) couple with heart
+1F4AA ; Emoji_Modifier_Base # 6.0 [1] (๐ช) flexed biceps
+1F574..1F575 ; Emoji_Modifier_Base # 7.0 [2] (๐ด๏ธ..๐ต๏ธ) man in suit levitating..detective
+1F57A ; Emoji_Modifier_Base # 9.0 [1] (๐บ) man dancing
+1F590 ; Emoji_Modifier_Base # 7.0 [1] (๐๏ธ) hand with fingers splayed
+1F595..1F596 ; Emoji_Modifier_Base # 7.0 [2] (๐..๐) middle finger..vulcan salute
+1F645..1F647 ; Emoji_Modifier_Base # 6.0 [3] (๐
..๐) person gesturing NO..person bowing
+1F64B..1F64F ; Emoji_Modifier_Base # 6.0 [5] (๐..๐) person raising hand..folded hands
+1F6A3 ; Emoji_Modifier_Base # 6.0 [1] (๐ฃ) person rowing boat
+1F6B4..1F6B6 ; Emoji_Modifier_Base # 6.0 [3] (๐ด..๐ถ) person biking..person walking
+1F6C0 ; Emoji_Modifier_Base # 6.0 [1] (๐) person taking bath
+1F6CC ; Emoji_Modifier_Base # 7.0 [1] (๐) person in bed
+1F90F ; Emoji_Modifier_Base # 12.0 [1] (๐ค) pinching hand
+1F918 ; Emoji_Modifier_Base # 8.0 [1] (๐ค) sign of the horns
+1F919..1F91E ; Emoji_Modifier_Base # 9.0 [6] (๐ค..๐ค) call me hand..crossed fingers
+1F91F ; Emoji_Modifier_Base # 10.0 [1] (๐ค) love-you gesture
+1F926 ; Emoji_Modifier_Base # 9.0 [1] (๐คฆ) person facepalming
+1F930 ; Emoji_Modifier_Base # 9.0 [1] (๐คฐ) pregnant woman
+1F931..1F932 ; Emoji_Modifier_Base # 10.0 [2] (๐คฑ..๐คฒ) breast-feeding..palms up together
+1F933..1F939 ; Emoji_Modifier_Base # 9.0 [7] (๐คณ..๐คน) selfie..person juggling
+1F93C..1F93E ; Emoji_Modifier_Base # 9.0 [3] (๐คผ..๐คพ) people wrestling..person playing handball
+1F9B5..1F9B6 ; Emoji_Modifier_Base # 11.0 [2] (๐ฆต..๐ฆถ) leg..foot
+1F9B8..1F9B9 ; Emoji_Modifier_Base # 11.0 [2] (๐ฆธ..๐ฆน) superhero..supervillain
+1F9BB ; Emoji_Modifier_Base # 12.0 [1] (๐ฆป) ear with hearing aid
+1F9CD..1F9CF ; Emoji_Modifier_Base # 12.0 [3] (๐ง..๐ง) person standing..deaf person
+1F9D1..1F9DD ; Emoji_Modifier_Base # 10.0 [13] (๐ง..๐ง) person..elf
+
+# Total elements: 120
+
+# ================================================
+
+# All omitted code points have Emoji_Component=No
+# @missing: 0000..10FFFF ; Emoji_Component ; No
+
+0023 ; Emoji_Component # 1.1 [1] (#๏ธ) number sign
+002A ; Emoji_Component # 1.1 [1] (*๏ธ) asterisk
+0030..0039 ; Emoji_Component # 1.1 [10] (0๏ธ..9๏ธ) digit zero..digit nine
+200D ; Emoji_Component # 1.1 [1] (โ) zero width joiner
+20E3 ; Emoji_Component # 3.0 [1] (โฃ) combining enclosing keycap
+FE0F ; Emoji_Component # 3.2 [1] () VARIATION SELECTOR-16
+1F1E6..1F1FF ; Emoji_Component # 6.0 [26] (๐ฆ..๐ฟ) regional indicator symbol letter a..regional indicator symbol letter z
+1F3FB..1F3FF ; Emoji_Component # 8.0 [5] (๐ป..๐ฟ) light skin tone..dark skin tone
+1F9B0..1F9B3 ; Emoji_Component # 11.0 [4] (๐ฆฐ..๐ฆณ) red hair..white hair
+E0020..E007F ; Emoji_Component # 3.1 [96] (๓ ..๓ ฟ) tag space..cancel tag
+
+# Total elements: 146
+
+# ================================================
+
+# All omitted code points have Extended_Pictographic=No
+# @missing: 0000..10FFFF ; Extended_Pictographic ; No
+
+00A9 ; Extended_Pictographic# 1.1 [1] (ยฉ๏ธ) copyright
+00AE ; Extended_Pictographic# 1.1 [1] (ยฎ๏ธ) registered
+203C ; Extended_Pictographic# 1.1 [1] (โผ๏ธ) double exclamation mark
+2049 ; Extended_Pictographic# 3.0 [1] (โ๏ธ) exclamation question mark
+2122 ; Extended_Pictographic# 1.1 [1] (โข๏ธ) trade mark
+2139 ; Extended_Pictographic# 3.0 [1] (โน๏ธ) information
+2194..2199 ; Extended_Pictographic# 1.1 [6] (โ๏ธ..โ๏ธ) left-right arrow..down-left arrow
+21A9..21AA ; Extended_Pictographic# 1.1 [2] (โฉ๏ธ..โช๏ธ) right arrow curving left..left arrow curving right
+231A..231B ; Extended_Pictographic# 1.1 [2] (โ..โ) watch..hourglass done
+2328 ; Extended_Pictographic# 1.1 [1] (โจ๏ธ) keyboard
+2388 ; Extended_Pictographic# 3.0 [1] (โ) HELM SYMBOL
+23CF ; Extended_Pictographic# 4.0 [1] (โ๏ธ) eject button
+23E9..23F3 ; Extended_Pictographic# 6.0 [11] (โฉ..โณ) fast-forward button..hourglass not done
+23F8..23FA ; Extended_Pictographic# 7.0 [3] (โธ๏ธ..โบ๏ธ) pause button..record button
+24C2 ; Extended_Pictographic# 1.1 [1] (โ๏ธ) circled M
+25AA..25AB ; Extended_Pictographic# 1.1 [2] (โช๏ธ..โซ๏ธ) black small square..white small square
+25B6 ; Extended_Pictographic# 1.1 [1] (โถ๏ธ) play button
+25C0 ; Extended_Pictographic# 1.1 [1] (โ๏ธ) reverse button
+25FB..25FE ; Extended_Pictographic# 3.2 [4] (โป๏ธ..โพ) white medium square..black medium-small square
+2600..2605 ; Extended_Pictographic# 1.1 [6] (โ๏ธ..โ
) sun..BLACK STAR
+2607..2612 ; Extended_Pictographic# 1.1 [12] (โ..โ) LIGHTNING..BALLOT BOX WITH X
+2614..2615 ; Extended_Pictographic# 4.0 [2] (โ..โ) umbrella with rain drops..hot beverage
+2616..2617 ; Extended_Pictographic# 3.2 [2] (โ..โ) WHITE SHOGI PIECE..BLACK SHOGI PIECE
+2618 ; Extended_Pictographic# 4.1 [1] (โ๏ธ) shamrock
+2619 ; Extended_Pictographic# 3.0 [1] (โ) REVERSED ROTATED FLORAL HEART BULLET
+261A..266F ; Extended_Pictographic# 1.1 [86] (โ..โฏ) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
+2670..2671 ; Extended_Pictographic# 3.0 [2] (โฐ..โฑ) WEST SYRIAC CROSS..EAST SYRIAC CROSS
+2672..267D ; Extended_Pictographic# 3.2 [12] (โฒ..โฝ) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
+267E..267F ; Extended_Pictographic# 4.1 [2] (โพ๏ธ..โฟ) infinity..wheelchair symbol
+2680..2685 ; Extended_Pictographic# 3.2 [6] (โ..โ
) DIE FACE-1..DIE FACE-6
+2690..2691 ; Extended_Pictographic# 4.0 [2] (โ..โ) WHITE FLAG..BLACK FLAG
+2692..269C ; Extended_Pictographic# 4.1 [11] (โ๏ธ..โ๏ธ) hammer and pick..fleur-de-lis
+269D ; Extended_Pictographic# 5.1 [1] (โ) OUTLINED WHITE STAR
+269E..269F ; Extended_Pictographic# 5.2 [2] (โ..โ) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
+26A0..26A1 ; Extended_Pictographic# 4.0 [2] (โ ๏ธ..โก) warning..high voltage
+26A2..26B1 ; Extended_Pictographic# 4.1 [16] (โข..โฑ๏ธ) DOUBLED FEMALE SIGN..funeral urn
+26B2 ; Extended_Pictographic# 5.0 [1] (โฒ) NEUTER
+26B3..26BC ; Extended_Pictographic# 5.1 [10] (โณ..โผ) CERES..SESQUIQUADRATE
+26BD..26BF ; Extended_Pictographic# 5.2 [3] (โฝ..โฟ) soccer ball..SQUARED KEY
+26C0..26C3 ; Extended_Pictographic# 5.1 [4] (โ..โ) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+26C4..26CD ; Extended_Pictographic# 5.2 [10] (โ..โ) snowman without snow..DISABLED CAR
+26CE ; Extended_Pictographic# 6.0 [1] (โ) Ophiuchus
+26CF..26E1 ; Extended_Pictographic# 5.2 [19] (โ๏ธ..โก) pick..RESTRICTED LEFT ENTRY-2
+26E2 ; Extended_Pictographic# 6.0 [1] (โข) ASTRONOMICAL SYMBOL FOR URANUS
+26E3 ; Extended_Pictographic# 5.2 [1] (โฃ) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
+26E4..26E7 ; Extended_Pictographic# 6.0 [4] (โค..โง) PENTAGRAM..INVERTED PENTAGRAM
+26E8..26FF ; Extended_Pictographic# 5.2 [24] (โจ..โฟ) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
+2700 ; Extended_Pictographic# 7.0 [1] (โ) BLACK SAFETY SCISSORS
+2701..2704 ; Extended_Pictographic# 1.1 [4] (โ..โ) UPPER BLADE SCISSORS..WHITE SCISSORS
+2705 ; Extended_Pictographic# 6.0 [1] (โ
) check mark button
+2708..2709 ; Extended_Pictographic# 1.1 [2] (โ๏ธ..โ๏ธ) airplane..envelope
+270A..270B ; Extended_Pictographic# 6.0 [2] (โ..โ) raised fist..raised hand
+270C..2712 ; Extended_Pictographic# 1.1 [7] (โ๏ธ..โ๏ธ) victory hand..black nib
+2714 ; Extended_Pictographic# 1.1 [1] (โ๏ธ) check mark
+2716 ; Extended_Pictographic# 1.1 [1] (โ๏ธ) multiplication sign
+271D ; Extended_Pictographic# 1.1 [1] (โ๏ธ) latin cross
+2721 ; Extended_Pictographic# 1.1 [1] (โก๏ธ) star of David
+2728 ; Extended_Pictographic# 6.0 [1] (โจ) sparkles
+2733..2734 ; Extended_Pictographic# 1.1 [2] (โณ๏ธ..โด๏ธ) eight-spoked asterisk..eight-pointed star
+2744 ; Extended_Pictographic# 1.1 [1] (โ๏ธ) snowflake
+2747 ; Extended_Pictographic# 1.1 [1] (โ๏ธ) sparkle
+274C ; Extended_Pictographic# 6.0 [1] (โ) cross mark
+274E ; Extended_Pictographic# 6.0 [1] (โ) cross mark button
+2753..2755 ; Extended_Pictographic# 6.0 [3] (โ..โ) question mark..white exclamation mark
+2757 ; Extended_Pictographic# 5.2 [1] (โ) exclamation mark
+2763..2767 ; Extended_Pictographic# 1.1 [5] (โฃ๏ธ..โง) heart exclamation..ROTATED FLORAL HEART BULLET
+2795..2797 ; Extended_Pictographic# 6.0 [3] (โ..โ) plus sign..division sign
+27A1 ; Extended_Pictographic# 1.1 [1] (โก๏ธ) right arrow
+27B0 ; Extended_Pictographic# 6.0 [1] (โฐ) curly loop
+27BF ; Extended_Pictographic# 6.0 [1] (โฟ) double curly loop
+2934..2935 ; Extended_Pictographic# 3.2 [2] (โคด๏ธ..โคต๏ธ) right arrow curving up..right arrow curving down
+2B05..2B07 ; Extended_Pictographic# 4.0 [3] (โฌ
๏ธ..โฌ๏ธ) left arrow..down arrow
+2B1B..2B1C ; Extended_Pictographic# 5.1 [2] (โฌ..โฌ) black large square..white large square
+2B50 ; Extended_Pictographic# 5.1 [1] (โญ) star
+2B55 ; Extended_Pictographic# 5.2 [1] (โญ) hollow red circle
+3030 ; Extended_Pictographic# 1.1 [1] (ใฐ๏ธ) wavy dash
+303D ; Extended_Pictographic# 3.2 [1] (ใฝ๏ธ) part alternation mark
+3297 ; Extended_Pictographic# 1.1 [1] (ใ๏ธ) Japanese โcongratulationsโ button
+3299 ; Extended_Pictographic# 1.1 [1] (ใ๏ธ) Japanese โsecretโ button
+1F000..1F02B ; Extended_Pictographic# 5.1 [44] (๐..๐ซ) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
+1F02C..1F02F ; Extended_Pictographic# NA [4] (๐ฌ..๐ฏ) <reserved-1F02C>..<reserved-1F02F>
+1F030..1F093 ; Extended_Pictographic# 5.1[100] (๐ฐ..๐) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+1F094..1F09F ; Extended_Pictographic# NA [12] (๐..๐) <reserved-1F094>..<reserved-1F09F>
+1F0A0..1F0AE ; Extended_Pictographic# 6.0 [15] (๐ ..๐ฎ) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
+1F0AF..1F0B0 ; Extended_Pictographic# NA [2] (๐ฏ..๐ฐ) <reserved-1F0AF>..<reserved-1F0B0>
+1F0B1..1F0BE ; Extended_Pictographic# 6.0 [14] (๐ฑ..๐พ) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
+1F0BF ; Extended_Pictographic# 7.0 [1] (๐ฟ) PLAYING CARD RED JOKER
+1F0C0 ; Extended_Pictographic# NA [1] (๐) <reserved-1F0C0>
+1F0C1..1F0CF ; Extended_Pictographic# 6.0 [15] (๐..๐) PLAYING CARD ACE OF DIAMONDS..joker
+1F0D0 ; Extended_Pictographic# NA [1] (๐) <reserved-1F0D0>
+1F0D1..1F0DF ; Extended_Pictographic# 6.0 [15] (๐..๐) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
+1F0E0..1F0F5 ; Extended_Pictographic# 7.0 [22] (๐ ..๐ต) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
+1F0F6..1F0FF ; Extended_Pictographic# NA [10] (๐ถ..๐ฟ) <reserved-1F0F6>..<reserved-1F0FF>
+1F10D..1F10F ; Extended_Pictographic# NA [3] (๐..๐) <reserved-1F10D>..<reserved-1F10F>
+1F12F ; Extended_Pictographic# 11.0 [1] (๐ฏ) COPYLEFT SYMBOL
+1F16C ; Extended_Pictographic# 12.0 [1] (๐
ฌ) RAISED MR SIGN
+1F16D..1F16F ; Extended_Pictographic# NA [3] (๐
ญ..๐
ฏ) <reserved-1F16D>..<reserved-1F16F>
+1F170..1F171 ; Extended_Pictographic# 6.0 [2] (๐
ฐ๏ธ..๐
ฑ๏ธ) A button (blood type)..B button (blood type)
+1F17E ; Extended_Pictographic# 6.0 [1] (๐
พ๏ธ) O button (blood type)
+1F17F ; Extended_Pictographic# 5.2 [1] (๐
ฟ๏ธ) P button
+1F18E ; Extended_Pictographic# 6.0 [1] (๐) AB button (blood type)
+1F191..1F19A ; Extended_Pictographic# 6.0 [10] (๐..๐) CL button..VS button
+1F1AD..1F1E5 ; Extended_Pictographic# NA [57] (๐ญ..๐ฅ) <reserved-1F1AD>..<reserved-1F1E5>
+1F201..1F202 ; Extended_Pictographic# 6.0 [2] (๐..๐๏ธ) Japanese โhereโ button..Japanese โservice chargeโ button
+1F203..1F20F ; Extended_Pictographic# NA [13] (๐..๐) <reserved-1F203>..<reserved-1F20F>
+1F21A ; Extended_Pictographic# 5.2 [1] (๐) Japanese โfree of chargeโ button
+1F22F ; Extended_Pictographic# 5.2 [1] (๐ฏ) Japanese โreservedโ button
+1F232..1F23A ; Extended_Pictographic# 6.0 [9] (๐ฒ..๐บ) Japanese โprohibitedโ button..Japanese โopen for businessโ button
+1F23C..1F23F ; Extended_Pictographic# NA [4] (๐ผ..๐ฟ) <reserved-1F23C>..<reserved-1F23F>
+1F249..1F24F ; Extended_Pictographic# NA [7] (๐..๐) <reserved-1F249>..<reserved-1F24F>
+1F250..1F251 ; Extended_Pictographic# 6.0 [2] (๐..๐) Japanese โbargainโ button..Japanese โacceptableโ button
+1F252..1F25F ; Extended_Pictographic# NA [14] (๐..๐) <reserved-1F252>..<reserved-1F25F>
+1F260..1F265 ; Extended_Pictographic# 10.0 [6] (๐ ..๐ฅ) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
+1F266..1F2FF ; Extended_Pictographic# NA[154] (๐ฆ..๐ฟ) <reserved-1F266>..<reserved-1F2FF>
+1F300..1F320 ; Extended_Pictographic# 6.0 [33] (๐..๐ ) cyclone..shooting star
+1F321..1F32C ; Extended_Pictographic# 7.0 [12] (๐ก๏ธ..๐ฌ๏ธ) thermometer..wind face
+1F32D..1F32F ; Extended_Pictographic# 8.0 [3] (๐ญ..๐ฏ) hot dog..burrito
+1F330..1F335 ; Extended_Pictographic# 6.0 [6] (๐ฐ..๐ต) chestnut..cactus
+1F336 ; Extended_Pictographic# 7.0 [1] (๐ถ๏ธ) hot pepper
+1F337..1F37C ; Extended_Pictographic# 6.0 [70] (๐ท..๐ผ) tulip..baby bottle
+1F37D ; Extended_Pictographic# 7.0 [1] (๐ฝ๏ธ) fork and knife with plate
+1F37E..1F37F ; Extended_Pictographic# 8.0 [2] (๐พ..๐ฟ) bottle with popping cork..popcorn
+1F380..1F393 ; Extended_Pictographic# 6.0 [20] (๐..๐) ribbon..graduation cap
+1F394..1F39F ; Extended_Pictographic# 7.0 [12] (๐..๐๏ธ) HEART WITH TIP ON THE LEFT..admission tickets
+1F3A0..1F3C4 ; Extended_Pictographic# 6.0 [37] (๐ ..๐) carousel horse..person surfing
+1F3C5 ; Extended_Pictographic# 7.0 [1] (๐
) sports medal
+1F3C6..1F3CA ; Extended_Pictographic# 6.0 [5] (๐..๐) trophy..person swimming
+1F3CB..1F3CE ; Extended_Pictographic# 7.0 [4] (๐๏ธ..๐๏ธ) person lifting weights..racing car
+1F3CF..1F3D3 ; Extended_Pictographic# 8.0 [5] (๐..๐) cricket game..ping pong
+1F3D4..1F3DF ; Extended_Pictographic# 7.0 [12] (๐๏ธ..๐๏ธ) snow-capped mountain..stadium
+1F3E0..1F3F0 ; Extended_Pictographic# 6.0 [17] (๐ ..๐ฐ) house..castle
+1F3F1..1F3F7 ; Extended_Pictographic# 7.0 [7] (๐ฑ..๐ท๏ธ) WHITE PENNANT..label
+1F3F8..1F3FA ; Extended_Pictographic# 8.0 [3] (๐ธ..๐บ) badminton..amphora
+1F400..1F43E ; Extended_Pictographic# 6.0 [63] (๐..๐พ) rat..paw prints
+1F43F ; Extended_Pictographic# 7.0 [1] (๐ฟ๏ธ) chipmunk
+1F440 ; Extended_Pictographic# 6.0 [1] (๐) eyes
+1F441 ; Extended_Pictographic# 7.0 [1] (๐๏ธ) eye
+1F442..1F4F7 ; Extended_Pictographic# 6.0[182] (๐..๐ท) ear..camera
+1F4F8 ; Extended_Pictographic# 7.0 [1] (๐ธ) camera with flash
+1F4F9..1F4FC ; Extended_Pictographic# 6.0 [4] (๐น..๐ผ) video camera..videocassette
+1F4FD..1F4FE ; Extended_Pictographic# 7.0 [2] (๐ฝ๏ธ..๐พ) film projector..PORTABLE STEREO
+1F4FF ; Extended_Pictographic# 8.0 [1] (๐ฟ) prayer beads
+1F500..1F53D ; Extended_Pictographic# 6.0 [62] (๐..๐ฝ) shuffle tracks button..downwards button
+1F546..1F54A ; Extended_Pictographic# 7.0 [5] (๐..๐๏ธ) WHITE LATIN CROSS..dove
+1F54B..1F54F ; Extended_Pictographic# 8.0 [5] (๐..๐) kaaba..BOWL OF HYGIEIA
+1F550..1F567 ; Extended_Pictographic# 6.0 [24] (๐..๐ง) one oโclock..twelve-thirty
+1F568..1F579 ; Extended_Pictographic# 7.0 [18] (๐จ..๐น๏ธ) RIGHT SPEAKER..joystick
+1F57A ; Extended_Pictographic# 9.0 [1] (๐บ) man dancing
+1F57B..1F5A3 ; Extended_Pictographic# 7.0 [41] (๐ป..๐ฃ) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
+1F5A4 ; Extended_Pictographic# 9.0 [1] (๐ค) black heart
+1F5A5..1F5FA ; Extended_Pictographic# 7.0 [86] (๐ฅ๏ธ..๐บ๏ธ) desktop computer..world map
+1F5FB..1F5FF ; Extended_Pictographic# 6.0 [5] (๐ป..๐ฟ) mount fuji..moai
+1F600 ; Extended_Pictographic# 6.1 [1] (๐) grinning face
+1F601..1F610 ; Extended_Pictographic# 6.0 [16] (๐..๐) beaming face with smiling eyes..neutral face
+1F611 ; Extended_Pictographic# 6.1 [1] (๐) expressionless face
+1F612..1F614 ; Extended_Pictographic# 6.0 [3] (๐..๐) unamused face..pensive face
+1F615 ; Extended_Pictographic# 6.1 [1] (๐) confused face
+1F616 ; Extended_Pictographic# 6.0 [1] (๐) confounded face
+1F617 ; Extended_Pictographic# 6.1 [1] (๐) kissing face
+1F618 ; Extended_Pictographic# 6.0 [1] (๐) face blowing a kiss
+1F619 ; Extended_Pictographic# 6.1 [1] (๐) kissing face with smiling eyes
+1F61A ; Extended_Pictographic# 6.0 [1] (๐) kissing face with closed eyes
+1F61B ; Extended_Pictographic# 6.1 [1] (๐) face with tongue
+1F61C..1F61E ; Extended_Pictographic# 6.0 [3] (๐..๐) winking face with tongue..disappointed face
+1F61F ; Extended_Pictographic# 6.1 [1] (๐) worried face
+1F620..1F625 ; Extended_Pictographic# 6.0 [6] (๐ ..๐ฅ) angry face..sad but relieved face
+1F626..1F627 ; Extended_Pictographic# 6.1 [2] (๐ฆ..๐ง) frowning face with open mouth..anguished face
+1F628..1F62B ; Extended_Pictographic# 6.0 [4] (๐จ..๐ซ) fearful face..tired face
+1F62C ; Extended_Pictographic# 6.1 [1] (๐ฌ) grimacing face
+1F62D ; Extended_Pictographic# 6.0 [1] (๐ญ) loudly crying face
+1F62E..1F62F ; Extended_Pictographic# 6.1 [2] (๐ฎ..๐ฏ) face with open mouth..hushed face
+1F630..1F633 ; Extended_Pictographic# 6.0 [4] (๐ฐ..๐ณ) anxious face with sweat..flushed face
+1F634 ; Extended_Pictographic# 6.1 [1] (๐ด) sleeping face
+1F635..1F640 ; Extended_Pictographic# 6.0 [12] (๐ต..๐) dizzy face..weary cat
+1F641..1F642 ; Extended_Pictographic# 7.0 [2] (๐..๐) slightly frowning face..slightly smiling face
+1F643..1F644 ; Extended_Pictographic# 8.0 [2] (๐..๐) upside-down face..face with rolling eyes
+1F645..1F64F ; Extended_Pictographic# 6.0 [11] (๐
..๐) person gesturing NO..folded hands
+1F680..1F6C5 ; Extended_Pictographic# 6.0 [70] (๐..๐
) rocket..left luggage
+1F6C6..1F6CF ; Extended_Pictographic# 7.0 [10] (๐..๐๏ธ) TRIANGLE WITH ROUNDED CORNERS..bed
+1F6D0 ; Extended_Pictographic# 8.0 [1] (๐) place of worship
+1F6D1..1F6D2 ; Extended_Pictographic# 9.0 [2] (๐..๐) stop sign..shopping cart
+1F6D3..1F6D4 ; Extended_Pictographic# 10.0 [2] (๐..๐) STUPA..PAGODA
+1F6D5 ; Extended_Pictographic# 12.0 [1] (๐) hindu temple
+1F6D6..1F6DF ; Extended_Pictographic# NA [10] (๐..๐) <reserved-1F6D6>..<reserved-1F6DF>
+1F6E0..1F6EC ; Extended_Pictographic# 7.0 [13] (๐ ๏ธ..๐ฌ) hammer and wrench..airplane arrival
+1F6ED..1F6EF ; Extended_Pictographic# NA [3] (๐ญ..๐ฏ) <reserved-1F6ED>..<reserved-1F6EF>
+1F6F0..1F6F3 ; Extended_Pictographic# 7.0 [4] (๐ฐ๏ธ..๐ณ๏ธ) satellite..passenger ship
+1F6F4..1F6F6 ; Extended_Pictographic# 9.0 [3] (๐ด..๐ถ) kick scooter..canoe
+1F6F7..1F6F8 ; Extended_Pictographic# 10.0 [2] (๐ท..๐ธ) sled..flying saucer
+1F6F9 ; Extended_Pictographic# 11.0 [1] (๐น) skateboard
+1F6FA ; Extended_Pictographic# 12.0 [1] (๐บ) auto rickshaw
+1F6FB..1F6FF ; Extended_Pictographic# NA [5] (๐ป..๐ฟ) <reserved-1F6FB>..<reserved-1F6FF>
+1F774..1F77F ; Extended_Pictographic# NA [12] (๐ด..๐ฟ) <reserved-1F774>..<reserved-1F77F>
+1F7D5..1F7D8 ; Extended_Pictographic# 11.0 [4] (๐..๐) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
+1F7D9..1F7DF ; Extended_Pictographic# NA [7] (๐..๐) <reserved-1F7D9>..<reserved-1F7DF>
+1F7E0..1F7EB ; Extended_Pictographic# 12.0 [12] (๐ ..๐ซ) orange circle..brown square
+1F7EC..1F7FF ; Extended_Pictographic# NA [20] (๐ฌ..๐ฟ) <reserved-1F7EC>..<reserved-1F7FF>
+1F80C..1F80F ; Extended_Pictographic# NA [4] (๐ ..๐ ) <reserved-1F80C>..<reserved-1F80F>
+1F848..1F84F ; Extended_Pictographic# NA [8] (๐ก..๐ก) <reserved-1F848>..<reserved-1F84F>
+1F85A..1F85F ; Extended_Pictographic# NA [6] (๐ก..๐ก) <reserved-1F85A>..<reserved-1F85F>
+1F888..1F88F ; Extended_Pictographic# NA [8] (๐ข..๐ข) <reserved-1F888>..<reserved-1F88F>
+1F8AE..1F8FF ; Extended_Pictographic# NA [82] (๐ขฎ..๐ฃฟ) <reserved-1F8AE>..<reserved-1F8FF>
+1F90C ; Extended_Pictographic# NA [1] (๐ค) <reserved-1F90C>
+1F90D..1F90F ; Extended_Pictographic# 12.0 [3] (๐ค..๐ค) white heart..pinching hand
+1F910..1F918 ; Extended_Pictographic# 8.0 [9] (๐ค..๐ค) zipper-mouth face..sign of the horns
+1F919..1F91E ; Extended_Pictographic# 9.0 [6] (๐ค..๐ค) call me hand..crossed fingers
+1F91F ; Extended_Pictographic# 10.0 [1] (๐ค) love-you gesture
+1F920..1F927 ; Extended_Pictographic# 9.0 [8] (๐ค ..๐คง) cowboy hat face..sneezing face
+1F928..1F92F ; Extended_Pictographic# 10.0 [8] (๐คจ..๐คฏ) face with raised eyebrow..exploding head
+1F930 ; Extended_Pictographic# 9.0 [1] (๐คฐ) pregnant woman
+1F931..1F932 ; Extended_Pictographic# 10.0 [2] (๐คฑ..๐คฒ) breast-feeding..palms up together
+1F933..1F93A ; Extended_Pictographic# 9.0 [8] (๐คณ..๐คบ) selfie..person fencing
+1F93C..1F93E ; Extended_Pictographic# 9.0 [3] (๐คผ..๐คพ) people wrestling..person playing handball
+1F93F ; Extended_Pictographic# 12.0 [1] (๐คฟ) diving mask
+1F940..1F945 ; Extended_Pictographic# 9.0 [6] (๐ฅ..๐ฅ
) wilted flower..goal net
+1F947..1F94B ; Extended_Pictographic# 9.0 [5] (๐ฅ..๐ฅ) 1st place medal..martial arts uniform
+1F94C ; Extended_Pictographic# 10.0 [1] (๐ฅ) curling stone
+1F94D..1F94F ; Extended_Pictographic# 11.0 [3] (๐ฅ..๐ฅ) lacrosse..flying disc
+1F950..1F95E ; Extended_Pictographic# 9.0 [15] (๐ฅ..๐ฅ) croissant..pancakes
+1F95F..1F96B ; Extended_Pictographic# 10.0 [13] (๐ฅ..๐ฅซ) dumpling..canned food
+1F96C..1F970 ; Extended_Pictographic# 11.0 [5] (๐ฅฌ..๐ฅฐ) leafy green..smiling face with hearts
+1F971 ; Extended_Pictographic# 12.0 [1] (๐ฅฑ) yawning face
+1F972 ; Extended_Pictographic# NA [1] (๐ฅฒ) <reserved-1F972>
+1F973..1F976 ; Extended_Pictographic# 11.0 [4] (๐ฅณ..๐ฅถ) partying face..cold face
+1F977..1F979 ; Extended_Pictographic# NA [3] (๐ฅท..๐ฅน) <reserved-1F977>..<reserved-1F979>
+1F97A ; Extended_Pictographic# 11.0 [1] (๐ฅบ) pleading face
+1F97B ; Extended_Pictographic# 12.0 [1] (๐ฅป) sari
+1F97C..1F97F ; Extended_Pictographic# 11.0 [4] (๐ฅผ..๐ฅฟ) lab coat..flat shoe
+1F980..1F984 ; Extended_Pictographic# 8.0 [5] (๐ฆ..๐ฆ) crab..unicorn
+1F985..1F991 ; Extended_Pictographic# 9.0 [13] (๐ฆ
..๐ฆ) eagle..squid
+1F992..1F997 ; Extended_Pictographic# 10.0 [6] (๐ฆ..๐ฆ) giraffe..cricket
+1F998..1F9A2 ; Extended_Pictographic# 11.0 [11] (๐ฆ..๐ฆข) kangaroo..swan
+1F9A3..1F9A4 ; Extended_Pictographic# NA [2] (๐ฆฃ..๐ฆค) <reserved-1F9A3>..<reserved-1F9A4>
+1F9A5..1F9AA ; Extended_Pictographic# 12.0 [6] (๐ฆฅ..๐ฆช) sloth..oyster
+1F9AB..1F9AD ; Extended_Pictographic# NA [3] (๐ฆซ..๐ฆญ) <reserved-1F9AB>..<reserved-1F9AD>
+1F9AE..1F9AF ; Extended_Pictographic# 12.0 [2] (๐ฆฎ..๐ฆฏ) guide dog..probing cane
+1F9B0..1F9B9 ; Extended_Pictographic# 11.0 [10] (๐ฆฐ..๐ฆน) red hair..supervillain
+1F9BA..1F9BF ; Extended_Pictographic# 12.0 [6] (๐ฆบ..๐ฆฟ) safety vest..mechanical leg
+1F9C0 ; Extended_Pictographic# 8.0 [1] (๐ง) cheese wedge
+1F9C1..1F9C2 ; Extended_Pictographic# 11.0 [2] (๐ง..๐ง) cupcake..salt
+1F9C3..1F9CA ; Extended_Pictographic# 12.0 [8] (๐ง..๐ง) beverage box..ice cube
+1F9CB..1F9CC ; Extended_Pictographic# NA [2] (๐ง..๐ง) <reserved-1F9CB>..<reserved-1F9CC>
+1F9CD..1F9CF ; Extended_Pictographic# 12.0 [3] (๐ง..๐ง) person standing..deaf person
+1F9D0..1F9E6 ; Extended_Pictographic# 10.0 [23] (๐ง..๐งฆ) face with monocle..socks
+1F9E7..1F9FF ; Extended_Pictographic# 11.0 [25] (๐งง..๐งฟ) red envelope..nazar amulet
+1FA00..1FA53 ; Extended_Pictographic# 12.0 [84] (๐จ..๐ฉ) NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
+1FA54..1FA5F ; Extended_Pictographic# NA [12] (๐ฉ..๐ฉ) <reserved-1FA54>..<reserved-1FA5F>
+1FA60..1FA6D ; Extended_Pictographic# 11.0 [14] (๐ฉ ..๐ฉญ) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
+1FA6E..1FA6F ; Extended_Pictographic# NA [2] (๐ฉฎ..๐ฉฏ) <reserved-1FA6E>..<reserved-1FA6F>
+1FA70..1FA73 ; Extended_Pictographic# 12.0 [4] (๐ฉฐ..๐ฉณ) ballet shoes..shorts
+1FA74..1FA77 ; Extended_Pictographic# NA [4] (๐ฉด..๐ฉท) <reserved-1FA74>..<reserved-1FA77>
+1FA78..1FA7A ; Extended_Pictographic# 12.0 [3] (๐ฉธ..๐ฉบ) drop of blood..stethoscope
+1FA7B..1FA7F ; Extended_Pictographic# NA [5] (๐ฉป..๐ฉฟ) <reserved-1FA7B>..<reserved-1FA7F>
+1FA80..1FA82 ; Extended_Pictographic# 12.0 [3] (๐ช..๐ช) yo-yo..parachute
+1FA83..1FA8F ; Extended_Pictographic# NA [13] (๐ช..๐ช) <reserved-1FA83>..<reserved-1FA8F>
+1FA90..1FA95 ; Extended_Pictographic# 12.0 [6] (๐ช..๐ช) ringed planet..banjo
+1FA96..1FFFD ; Extended_Pictographic# NA[1384] (๐ช..๐ฟฝ) <reserved-1FA96>..<reserved-1FFFD>
+
+# Total elements: 3793
+
+#EOF
diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex
@@ -98,4 +98,35 @@ defmodule Pleroma.Emoji do
defp update_emojis(emojis) do
:ets.insert(@ets, emojis)
end
+
+ @external_resource "lib/pleroma/emoji-data.txt"
+
+ emojis =
+ @external_resource
+ |> File.read!()
+ |> String.split("\n")
+ |> Enum.filter(fn line -> line != "" and not String.starts_with?(line, "#") end)
+ |> Enum.map(fn line ->
+ line
+ |> String.split(";", parts: 2)
+ |> hd()
+ |> String.trim()
+ |> String.split("..")
+ |> case do
+ [number] ->
+ <<String.to_integer(number, 16)::utf8>>
+
+ [first, last] ->
+ String.to_integer(first, 16)..String.to_integer(last, 16)
+ |> Enum.map(&<<&1::utf8>>)
+ end
+ end)
+ |> List.flatten()
+ |> Enum.uniq()
+
+ for emoji <- emojis do
+ def is_unicode_emoji?(unquote(emoji)), do: true
+ end
+
+ def is_unicode_emoji?(_), do: false
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -322,6 +322,32 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ def react_with_emoji(user, object, emoji, options \\ []) do
+ with local <- Keyword.get(options, :local, true),
+ activity_id <- Keyword.get(options, :activity_id, nil),
+ Pleroma.Emoji.is_unicode_emoji?(emoji),
+ reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
+ {:ok, activity} <- insert(reaction_data, local),
+ {:ok, object} <- add_emoji_reaction_to_object(activity, object),
+ :ok <- maybe_federate(activity) do
+ {:ok, activity, object}
+ end
+ end
+
+ def unreact_with_emoji(user, reaction_id, options \\ []) do
+ with local <- Keyword.get(options, :local, true),
+ activity_id <- Keyword.get(options, :activity_id, nil),
+ user_ap_id <- user.ap_id,
+ %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id),
+ object <- Object.normalize(reaction_activity),
+ unreact_data <- make_undo_data(user, reaction_activity, activity_id),
+ {:ok, activity} <- insert(unreact_data, local),
+ {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
+ :ok <- maybe_federate(activity) do
+ {:ok, activity, object}
+ end
+ end
+
# TODO: This is weird, maybe we shouldn't check here if we can make the activity.
def like(
%User{ap_id: ap_id} = user,
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -566,6 +566,34 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
end
+ @misskey_reactions %{
+ "like" => "๐",
+ "love" => "โค๏ธ",
+ "laugh" => "๐",
+ "hmm" => "๐ค",
+ "surprise" => "๐ฎ",
+ "congrats" => "๐",
+ "angry" => "๐ข",
+ "confused" => "๐ฅ",
+ "rip" => "๐",
+ "pudding" => "๐ฎ",
+ "star" => "โญ"
+ }
+
+ @doc "Rewrite misskey likes into EmojiReactions"
+ def handle_incoming(
+ %{
+ "type" => "Like",
+ "_misskey_reaction" => reaction
+ } = data,
+ options
+ ) do
+ data
+ |> Map.put("type", "EmojiReaction")
+ |> Map.put("content", @misskey_reactions[reaction] || reaction)
+ |> handle_incoming(options)
+ end
+
def handle_incoming(
%{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data,
_options
@@ -581,6 +609,27 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
+ %{
+ "type" => "EmojiReaction",
+ "object" => object_id,
+ "actor" => _actor,
+ "id" => id,
+ "content" => emoji
+ } = data,
+ _options
+ ) do
+ with actor <- Containment.get_actor(data),
+ {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, object} <- get_obj_helper(object_id),
+ {:ok, activity, _object} <-
+ ActivityPub.react_with_emoji(actor, object, emoji, activity_id: id, local: false) do
+ {:ok, activity}
+ else
+ _e -> :error
+ end
+ end
+
+ def handle_incoming(
%{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data,
_options
) do
@@ -718,6 +767,28 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{
"type" => "Undo",
+ "object" => %{"type" => "EmojiReaction", "id" => reaction_activity_id},
+ "actor" => _actor,
+ "id" => id
+ } = data,
+ _options
+ ) do
+ with actor <- Containment.get_actor(data),
+ {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, activity, _} <-
+ ActivityPub.unreact_with_emoji(actor, reaction_activity_id,
+ activity_id: id,
+ local: false
+ ) do
+ {:ok, activity}
+ else
+ _e -> :error
+ end
+ end
+
+ def handle_incoming(
+ %{
+ "type" => "Undo",
"object" => %{"type" => "Block", "object" => blocked},
"actor" => blocker,
"id" => id
@@ -1048,7 +1119,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
Map.put(object, "attachment", attachments)
end
- defp strip_internal_fields(object) do
+ def strip_internal_fields(object) do
object
|> Map.drop(Pleroma.Constants.object_internal_fields())
end
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
@@ -255,6 +255,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Repo.one()
end
+ @doc """
+ Returns like activities targeting an object
+ """
+ def get_object_likes(%{data: %{"id" => id}}) do
+ id
+ |> Activity.Queries.by_object_id()
+ |> Activity.Queries.by_type("Like")
+ |> Repo.all()
+ end
+
@spec make_like_data(User.t(), map(), String.t()) :: map()
def make_like_data(
%User{ap_id: ap_id} = actor,
@@ -286,13 +296,30 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> maybe_put("id", activity_id)
end
+ def make_emoji_reaction_data(user, object, emoji, activity_id) do
+ make_like_data(user, object, activity_id)
+ |> Map.put("type", "EmojiReaction")
+ |> Map.put("content", emoji)
+ end
+
@spec update_element_in_object(String.t(), list(any), Object.t()) ::
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
def update_element_in_object(property, element, object) do
+ length =
+ if is_map(element) do
+ element
+ |> Map.values()
+ |> List.flatten()
+ |> length()
+ else
+ element
+ |> length()
+ end
+
data =
Map.merge(
object.data,
- %{"#{property}_count" => length(element), "#{property}s" => element}
+ %{"#{property}_count" => length, "#{property}s" => element}
)
object
@@ -300,6 +327,38 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Object.update_and_set_cache()
end
+ @spec add_emoji_reaction_to_object(Activity.t(), Object.t()) ::
+ {:ok, Object.t()} | {:error, Ecto.Changeset.t()}
+
+ def add_emoji_reaction_to_object(
+ %Activity{data: %{"content" => emoji, "actor" => actor}},
+ object
+ ) do
+ reactions = object.data["reactions"] || %{}
+ emoji_actors = reactions[emoji] || []
+ new_emoji_actors = [actor | emoji_actors] |> Enum.uniq()
+ new_reactions = Map.put(reactions, emoji, new_emoji_actors)
+ update_element_in_object("reaction", new_reactions, object)
+ end
+
+ def remove_emoji_reaction_from_object(
+ %Activity{data: %{"content" => emoji, "actor" => actor}},
+ object
+ ) do
+ reactions = object.data["reactions"] || %{}
+ emoji_actors = reactions[emoji] || []
+ new_emoji_actors = List.delete(emoji_actors, actor)
+
+ new_reactions =
+ if new_emoji_actors == [] do
+ Map.delete(reactions, emoji)
+ else
+ Map.put(reactions, emoji, new_emoji_actors)
+ end
+
+ update_element_in_object("reaction", new_reactions, object)
+ end
+
@spec add_like_to_object(Activity.t(), Object.t()) ::
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do
@@ -397,6 +456,19 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Repo.one()
end
+ def get_latest_reaction(internal_activity_id, %{ap_id: ap_id}, emoji) do
+ %{data: %{"object" => object_ap_id}} = Activity.get_by_id(internal_activity_id)
+
+ "EmojiReaction"
+ |> Activity.Queries.by_type()
+ |> where(actor: ^ap_id)
+ |> where([activity], fragment("?->>'content' = ?", activity.data, ^emoji))
+ |> Activity.Queries.by_object_id(object_ap_id)
+ |> order_by([activity], fragment("? desc nulls last", activity.id))
+ |> limit(1)
+ |> Repo.one()
+ end
+
#### Announce-related helpers
@doc """
@@ -489,6 +561,25 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> maybe_put("id", activity_id)
end
+ def make_undo_data(
+ %User{ap_id: actor, follower_address: follower_address},
+ %Activity{
+ data: %{"id" => undone_activity_id, "context" => context},
+ actor: undone_activity_actor
+ },
+ activity_id \\ nil
+ ) do
+ %{
+ "type" => "Undo",
+ "actor" => actor,
+ "object" => undone_activity_id,
+ "to" => [follower_address, undone_activity_actor],
+ "cc" => [Pleroma.Constants.as_public()],
+ "context" => context
+ }
+ |> maybe_put("id", activity_id)
+ end
+
@spec add_announce_to_object(Activity.t(), Object.t()) ::
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
def add_announce_to_object(
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
@@ -120,6 +120,25 @@ defmodule Pleroma.Web.CommonAPI do
end
end
+ def react_with_emoji(id, user, emoji) do
+ with %Activity{} = activity <- Activity.get_by_id(id),
+ object <- Object.normalize(activity) do
+ ActivityPub.react_with_emoji(user, object, emoji)
+ else
+ _ ->
+ {:error, dgettext("errors", "Could not add reaction emoji")}
+ end
+ end
+
+ def unreact_with_emoji(id, user, emoji) do
+ with %Activity{} = reaction_activity <- Utils.get_latest_reaction(id, user, emoji) do
+ ActivityPub.unreact_with_emoji(user, reaction_activity.data["id"])
+ else
+ _ ->
+ {:error, dgettext("errors", "Could not remove reaction emoji")}
+ end
+ end
+
def vote(user, %{data: %{"type" => "Question"}} = object, choices) do
with :ok <- validate_not_author(object, user),
:ok <- validate_existing_votes(user, object),
diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
@@ -7,10 +7,15 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
+ alias Pleroma.Activity
alias Pleroma.Conversation.Participation
alias Pleroma.Notification
+ alias Pleroma.Object
alias Pleroma.Plugs.OAuthScopesPlug
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.ConversationView
alias Pleroma.Web.MastodonAPI.NotificationView
alias Pleroma.Web.MastodonAPI.StatusView
@@ -29,6 +34,47 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
+ def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
+ with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
+ %Object{data: %{"reactions" => emoji_reactions}} <- Object.normalize(activity) do
+ reactions =
+ emoji_reactions
+ |> Enum.map(fn {emoji, users} ->
+ users = Enum.map(users, &User.get_cached_by_ap_id/1)
+ {emoji, AccountView.render("index.json", %{users: users, for: user, as: :user})}
+ end)
+ |> Enum.into(%{})
+
+ conn
+ |> json(reactions)
+ else
+ _e ->
+ conn
+ |> json(%{})
+ end
+ end
+
+ def react_with_emoji(%{assigns: %{user: user}} = conn, %{"id" => activity_id, "emoji" => emoji}) do
+ with {:ok, _activity, _object} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
+ activity <- Activity.get_by_id(activity_id) do
+ conn
+ |> put_view(StatusView)
+ |> render("show.json", %{activity: activity, for: user, as: :activity})
+ end
+ end
+
+ def unreact_with_emoji(%{assigns: %{user: user}} = conn, %{
+ "id" => activity_id,
+ "emoji" => emoji
+ }) do
+ with {:ok, _activity, _object} <- CommonAPI.unreact_with_emoji(activity_id, user, emoji),
+ activity <- Activity.get_by_id(activity_id) do
+ conn
+ |> put_view(StatusView)
+ |> render("show.json", %{activity: activity, for: user, as: :activity})
+ end
+ end
+
def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
with %Participation{} = participation <- Participation.get(participation_id),
true <- user.id == participation.user_id do
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
@@ -261,6 +261,12 @@ defmodule Pleroma.Web.Router do
end
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
+ pipe_through(:api)
+
+ get("/statuses/:id/emoji_reactions_by", PleromaAPIController, :emoji_reactions_by)
+ end
+
+ scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
scope [] do
pipe_through(:authenticated_api)
@@ -273,6 +279,8 @@ defmodule Pleroma.Web.Router do
pipe_through(:authenticated_api)
patch("/conversations/:id", PleromaAPIController, :update_conversation)
+ post("/statuses/:id/react_with_emoji", PleromaAPIController, :react_with_emoji)
+ post("/statuses/:id/unreact_with_emoji", PleromaAPIController, :unreact_with_emoji)
post("/notifications/read", PleromaAPIController, :read_notification)
patch("/accounts/update_avatar", AccountController, :update_avatar)
diff --git a/priv/static/schemas/litepub-0.1.jsonld b/priv/static/schemas/litepub-0.1.jsonld
@@ -28,7 +28,8 @@
"oauthRegistrationEndpoint": {
"@id": "litepub:oauthRegistrationEndpoint",
"@type": "@id"
- }
+ },
+ "EmojiReaction": "litepub:EmojiReaction"
}
]
}
diff --git a/test/emoji_test.exs b/test/emoji_test.exs
@@ -6,6 +6,14 @@ defmodule Pleroma.EmojiTest do
use ExUnit.Case, async: true
alias Pleroma.Emoji
+ describe "is_unicode_emoji?/1" do
+ test "tells if a string is an unicode emoji" do
+ refute Emoji.is_unicode_emoji?("X")
+ assert Emoji.is_unicode_emoji?("โ")
+ assert Emoji.is_unicode_emoji?("๐ฅบ")
+ end
+ end
+
describe "get_all/0" do
setup do
emoji_list = Emoji.get_all()
diff --git a/test/fixtures/emoji-reaction.json b/test/fixtures/emoji-reaction.json
@@ -0,0 +1,30 @@
+{
+ "type": "EmojiReaction",
+ "signature": {
+ "type": "RsaSignature2017",
+ "signatureValue": "fdxMfQSMwbC6wP6sh6neS/vM5879K67yQkHTbiT5Npr5wAac0y6+o3Ij+41tN3rL6wfuGTosSBTHOtta6R4GCOOhCaCSLMZKypnp1VltCzLDoyrZELnYQIC8gpUXVmIycZbREk22qWUe/w7DAFaKK4UscBlHDzeDVcA0K3Se5Sluqi9/Zh+ldAnEzj/rSEPDjrtvf5wGNf3fHxbKSRKFt90JvKK6hS+vxKUhlRFDf6/SMETw+EhwJSNW4d10yMUakqUWsFv4Acq5LW7l+HpYMvlYY1FZhNde1+uonnCyuQDyvzkff8zwtEJmAXC4RivO/VVLa17SmqheJZfI8oluVg==",
+ "creator": "http://mastodon.example.org/users/admin#main-key",
+ "created": "2018-02-17T18:57:49Z"
+ },
+ "object": "http://localtesting.pleroma.lol/objects/eb92579d-3417-42a8-8652-2492c2d4f454",
+ "content": "๐",
+ "nickname": "lain",
+ "id": "http://mastodon.example.org/users/admin#reactions/2",
+ "actor": "http://mastodon.example.org/users/admin",
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "toot": "http://joinmastodon.org/ns#",
+ "sensitive": "as:sensitive",
+ "ostatus": "http://ostatus.org#",
+ "movedTo": "as:movedTo",
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+ "conversation": "ostatus:conversation",
+ "atomUri": "ostatus:atomUri",
+ "Hashtag": "as:Hashtag",
+ "Emoji": "toot:Emoji"
+ }
+ ]
+}
diff --git a/test/fixtures/misskey-like.json b/test/fixtures/misskey-like.json
@@ -0,0 +1,14 @@
+{
+ "@context" : [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {"Hashtag" : "as:Hashtag"}
+ ],
+ "_misskey_reaction" : "pudding",
+ "actor": "http://mastodon.example.org/users/admin",
+ "cc" : ["https://testing.pleroma.lol/users/lain"],
+ "id" : "https://misskey.xyz/75149198-2f45-46e4-930a-8b0538297075",
+ "nickname" : "lain",
+ "object" : "https://testing.pleroma.lol/objects/c331bbf7-2eb9-4801-a709-2a6103492a5a",
+ "type" : "Like"
+}
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
@@ -812,6 +812,78 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
end
end
+ describe "react to an object" do
+ test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
+ Pleroma.Config.put([:instance, :federating], true)
+ user = insert(:user)
+ reactor = insert(:user)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
+ assert object = Object.normalize(activity)
+
+ {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "๐ฅ")
+
+ assert called(Pleroma.Web.Federator.publish(reaction_activity))
+ end
+
+ test "adds an emoji reaction activity to the db" do
+ user = insert(:user)
+ reactor = insert(:user)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
+ assert object = Object.normalize(activity)
+
+ {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "๐ฅ")
+
+ assert reaction_activity
+
+ assert reaction_activity.data["actor"] == reactor.ap_id
+ assert reaction_activity.data["type"] == "EmojiReaction"
+ assert reaction_activity.data["content"] == "๐ฅ"
+ assert reaction_activity.data["object"] == object.data["id"]
+ assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]]
+ assert reaction_activity.data["context"] == object.data["context"]
+ assert object.data["reaction_count"] == 1
+ assert object.data["reactions"]["๐ฅ"] == [reactor.ap_id]
+ end
+ end
+
+ describe "unreacting to an object" do
+ test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
+ Pleroma.Config.put([:instance, :federating], true)
+ user = insert(:user)
+ reactor = insert(:user)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
+ assert object = Object.normalize(activity)
+
+ {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "๐ฅ")
+
+ assert called(Pleroma.Web.Federator.publish(reaction_activity))
+
+ {:ok, unreaction_activity, _object} =
+ ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
+
+ assert called(Pleroma.Web.Federator.publish(unreaction_activity))
+ end
+
+ test "adds an undo activity to the db" do
+ user = insert(:user)
+ reactor = insert(:user)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
+ assert object = Object.normalize(activity)
+
+ {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "๐ฅ")
+
+ {:ok, unreaction_activity, _object} =
+ ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
+
+ assert unreaction_activity.actor == reactor.ap_id
+ assert unreaction_activity.data["object"] == reaction_activity.data["id"]
+
+ object = Object.get_by_ap_id(object.data["id"])
+ assert object.data["reaction_count"] == 0
+ assert object.data["reactions"] == %{}
+ end
+ end
+
describe "like an object" do
test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
Pleroma.Config.put([:instance, :federating], true)
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
@@ -339,6 +339,80 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert data["object"] == activity.data["object"]
end
+ test "it works for incoming misskey likes, turning them into EmojiReactions" do
+ user = insert(:user)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
+
+ data =
+ File.read!("test/fixtures/misskey-like.json")
+ |> Poison.decode!()
+ |> Map.put("object", activity.data["object"])
+
+ {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+
+ assert data["actor"] == data["actor"]
+ assert data["type"] == "EmojiReaction"
+ assert data["id"] == data["id"]
+ assert data["object"] == activity.data["object"]
+ assert data["content"] == "๐ฎ"
+ end
+
+ test "it works for incoming misskey likes that contain unicode emojis, turning them into EmojiReactions" do
+ user = insert(:user)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
+
+ data =
+ File.read!("test/fixtures/misskey-like.json")
+ |> Poison.decode!()
+ |> Map.put("object", activity.data["object"])
+ |> Map.put("_misskey_reaction", "โญ")
+
+ {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+
+ assert data["actor"] == data["actor"]
+ assert data["type"] == "EmojiReaction"
+ assert data["id"] == data["id"]
+ assert data["object"] == activity.data["object"]
+ assert data["content"] == "โญ"
+ end
+
+ test "it works for incoming emoji reactions" do
+ user = insert(:user)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
+
+ data =
+ File.read!("test/fixtures/emoji-reaction.json")
+ |> Poison.decode!()
+ |> Map.put("object", activity.data["object"])
+
+ {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+
+ assert data["actor"] == "http://mastodon.example.org/users/admin"
+ assert data["type"] == "EmojiReaction"
+ assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2"
+ assert data["object"] == activity.data["object"]
+ assert data["content"] == "๐"
+ end
+
+ test "it works for incoming emoji reaction undos" do
+ user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
+ {:ok, reaction_activity, _object} = CommonAPI.react_with_emoji(activity.id, user, "๐")
+
+ data =
+ File.read!("test/fixtures/mastodon-undo-like.json")
+ |> Poison.decode!()
+ |> Map.put("object", reaction_activity.data["id"])
+ |> Map.put("actor", user.ap_id)
+
+ {:ok, activity} = Transmogrifier.handle_incoming(data)
+
+ assert activity.actor == user.ap_id
+ assert activity.data["id"] == data["id"]
+ assert activity.data["type"] == "Undo"
+ end
+
test "it returns an error for incoming unlikes wihout a like activity" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
@@ -553,6 +627,20 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
refute Map.has_key?(object.data, "likes")
end
+ test "it strips internal reactions" do
+ user = insert(:user)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
+ {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "๐ข")
+
+ %{object: object} = Activity.get_by_id_with_object(activity.id)
+ assert Map.has_key?(object.data, "reactions")
+ assert Map.has_key?(object.data, "reaction_count")
+
+ object_data = Transmogrifier.strip_internal_fields(object.data)
+ refute Map.has_key?(object_data, "reactions")
+ refute Map.has_key?(object_data, "reaction_count")
+ end
+
test "it works for incoming update activities" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs
@@ -227,6 +227,33 @@ defmodule Pleroma.Web.CommonAPITest do
end
describe "reactions" do
+ test "reacting to a status with an emoji" do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
+
+ {:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "๐")
+
+ assert reaction.data["actor"] == user.ap_id
+ assert reaction.data["content"] == "๐"
+
+ # TODO: test error case.
+ end
+
+ test "unreacting to a status with an emoji" do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
+ {:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "๐")
+
+ {:ok, unreaction, _} = CommonAPI.unreact_with_emoji(activity.id, user, "๐")
+
+ assert unreaction.data["type"] == "Undo"
+ assert unreaction.data["object"] == reaction.data["id"]
+ end
+
test "repeating a status" do
user = insert(:user)
other_user = insert(:user)
diff --git a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs
@@ -7,12 +7,72 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
alias Pleroma.Conversation.Participation
alias Pleroma.Notification
+ alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
+ test "POST /api/v1/pleroma/statuses/:id/react_with_emoji", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
+
+ result =
+ conn
+ |> assign(:user, other_user)
+ |> post("/api/v1/pleroma/statuses/#{activity.id}/react_with_emoji", %{"emoji" => "โ"})
+
+ assert %{"id" => id} = json_response(result, 200)
+ assert to_string(activity.id) == id
+ end
+
+ test "POST /api/v1/pleroma/statuses/:id/unreact_with_emoji", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
+ {:ok, activity, _object} = CommonAPI.react_with_emoji(activity.id, other_user, "โ")
+
+ result =
+ conn
+ |> assign(:user, other_user)
+ |> post("/api/v1/pleroma/statuses/#{activity.id}/unreact_with_emoji", %{"emoji" => "โ"})
+
+ assert %{"id" => id} = json_response(result, 200)
+ assert to_string(activity.id) == id
+
+ object = Object.normalize(activity)
+
+ assert object.data["reaction_count"] == 0
+ end
+
+ test "GET /api/v1/pleroma/statuses/:id/emoji_reactions_by", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by")
+ |> json_response(200)
+
+ assert result == %{}
+
+ {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "๐
")
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by")
+ |> json_response(200)
+
+ [represented_user] = result["๐
"]
+ assert represented_user["id"] == other_user.id
+ end
+
test "/api/v1/pleroma/conversations/:id", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)