summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrinpatch <rinpatch@sdf.org>2019-11-14 08:47:10 +0000
committerrinpatch <rinpatch@sdf.org>2019-11-14 08:47:10 +0000
commit6085c71bd17e2b676e0820bf3fd93f81a1902ac3 (patch)
treea92e6aaa3fd96150d54d04309558c7cd5cae7416
parentf74bb1d0fc123ba8616d81a80dcf5d64b25aa4ed (diff)
parent7a322713c33e8ef2fdc326f9e35d1fcbe7590c93 (diff)
Merge branch 'reactions' into 'develop'
Emoji Reactions See merge request pleroma/pleroma!1662
-rw-r--r--CHANGELOG.md2
-rw-r--r--docs/API/pleroma_api.md32
-rw-r--r--lib/pleroma/constants.ex2
-rw-r--r--lib/pleroma/emoji-data.txt769
-rw-r--r--lib/pleroma/emoji.ex31
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex26
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex73
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex93
-rw-r--r--lib/pleroma/web/common_api/common_api.ex19
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex46
-rw-r--r--lib/pleroma/web/router.ex8
-rw-r--r--priv/static/schemas/litepub-0.1.jsonld3
-rw-r--r--test/emoji_test.exs8
-rw-r--r--test/fixtures/emoji-reaction.json30
-rw-r--r--test/fixtures/misskey-like.json14
-rw-r--r--test/web/activity_pub/activity_pub_test.exs72
-rw-r--r--test/web/activity_pub/transmogrifier_test.exs88
-rw-r--r--test/web/common_api/common_api_test.exs27
-rw-r--r--test/web/pleroma_api/controllers/pleroma_api_controller_test.exs60
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)