diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | docs/API/pleroma_api.md | 32 | ||||
-rw-r--r-- | lib/pleroma/constants.ex | 2 | ||||
-rw-r--r-- | lib/pleroma/emoji-data.txt | 769 | ||||
-rw-r--r-- | lib/pleroma/emoji.ex | 31 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 26 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 73 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/utils.ex | 93 | ||||
-rw-r--r-- | lib/pleroma/web/common_api/common_api.ex | 19 | ||||
-rw-r--r-- | lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex | 46 | ||||
-rw-r--r-- | lib/pleroma/web/router.ex | 8 | ||||
-rw-r--r-- | priv/static/schemas/litepub-0.1.jsonld | 3 | ||||
-rw-r--r-- | test/emoji_test.exs | 8 | ||||
-rw-r--r-- | test/fixtures/emoji-reaction.json | 30 | ||||
-rw-r--r-- | test/fixtures/misskey-like.json | 14 | ||||
-rw-r--r-- | test/web/activity_pub/activity_pub_test.exs | 72 | ||||
-rw-r--r-- | test/web/activity_pub/transmogrifier_test.exs | 88 | ||||
-rw-r--r-- | test/web/common_api/common_api_test.exs | 27 | ||||
-rw-r--r-- | test/web/pleroma_api/controllers/pleroma_api_controller_test.exs | 60 |
19 files changed, 1400 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 945e7dc4d..e04b96281 100644 --- 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 index 6c326dc9b..ad16d027e 100644 --- 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 index 0bf20cdd0..1a432e681 100644 --- 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 new file mode 100644 index 000000000..2fb5c3ff6 --- /dev/null +++ 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 index bafad2ae9..abfd49aaa 100644 --- 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 index 65dd251f3..d0c014e9d 100644 --- 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 index 91a164eff..15612545b 100644 --- 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 index d812fd734..91cfa2194 100644 --- 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 index e57345621..f1937b1ec 100644 --- 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 index 651a99423..8fed3f5bb 100644 --- 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 index ecf5f744c..93e7e45f9 100644 --- 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 index c8e69cab5..8bae42f6d 100644 --- 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 index 1fdbd0fdf..7bdf2b6fa 100644 --- 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 new file mode 100644 index 000000000..3812e43ad --- /dev/null +++ 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 new file mode 100644 index 000000000..84d56f473 --- /dev/null +++ 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 index 0d0281faf..d437ad456 100644 --- 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 index 4645eb39d..0bdd514e9 100644 --- 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 index 8e6fbd7f0..09a3e8dcf 100644 --- 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 index 0c83edb56..b1b59beed 100644 --- 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) |