-
Notifications
You must be signed in to change notification settings - Fork 400
MSC2545: Image Packs (Emoticons & Stickers) #2545
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Sorunome
wants to merge
55
commits into
matrix-org:old_master
Choose a base branch
from
Sorunome:soru/emotes
base: old_master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+593
−0
Open
Changes from 13 commits
Commits
Show all changes
55 commits
Select commit
Hold shift + click to select a range
bc55ff6
add emotes proposal
Sorunome 0f4bd5e
add more stuffs
Sorunome 2d191f6
fix naming thingy
Sorunome fa3a8be
fix typo
Sorunome 96f2864
outline room emote packs and emote rooms
Sorunome 81ccecb
remove comma
Sorunome dc43e79
address things
Sorunome 4435d75
update emotes msc
Sorunome 79aae8a
update
Sorunome 4fea69b
Fix typos and grammar mistakes
Sorunome 0e382e0
m.emote --> m.emoticon, update dedupe and pack priority
Sorunome 72b2174
update stuff
Sorunome 2e2987b
add a paragraph over emojis and stuff
Sorunome e8c2e34
Re-word proposal to sound more firm, address lots of suggestions
Sorunome b41c091
specify that emotes can be animated
Sorunome 4f6d147
update msc to also include stickers and thelike
Sorunome 5351cb3
update a few things
Sorunome 12c8fa8
add a few more words on slugs
Sorunome e8d910f
Update proposals/2545-emotes.md
Sorunome a879bcd
license --> attribution
Sorunome 580a836
Update proposals/2545-emotes.md
Sorunome 43f20ad
Merge branch 'soru/emotes' of github.com:Sorunome/matrix-doc into sor…
Sorunome 739b4c0
Update with "looking further" section and slightly update comparison
Sorunome 51bcec2
add a sentance to suggest image packs from spaces
Sorunome 71a3c55
soru bamboozled herself!
Sorunome a7a4a36
Fix small wording and ensure that emotes from canonical spaces are to…
Sorunome be7d0df
Update spelling mistakes and wording, only suggest deduplication meth…
Sorunome b5ca351
fix some typos
anoadragon453 3517a16
Make reference to spec/client naming disparity timeless
anoadragon453 5d62d52
Require `alt` and `title` attributes for emotes
anoadragon453 e5b9c2c
Link `m.sticker` spec; small wording changes
anoadragon453 44c5ed3
Prevent clients from rendering emotes behind non-MXC uri schemes
anoadragon453 ea50cc7
Clarify client requirements around emote resolution
anoadragon453 3c2e29c
Add a new proposal intro
anoadragon453 c429552
Recommend default of 32px for emote height
anoadragon453 d4e9cd2
Specify image formats are equivalent to m.image
anoadragon453 2366ffd
Add grammar and remove de-duplication req
SkyePrism dd84e7f
Add potential issues section
SkyePrism 76da385
Potential issue individual heading
SkyePrism 1cd8168
Add event size limit in potential issues
SkyePrism 628a0a0
Explicitly allow all non-colon characters in the shortcode
SkyePrism 7d399e6
Add definition for shortcode
SkyePrism 645662b
Note that this MSC has no dependents.
anoadragon453 72888d8
Extend security considerations related to image pack lack of encryption
SkyePrism c243748
Update security considerations with full explanation of E2EE concerns
anoadragon453 5663906
polish the MSC
anoadragon453 aa41f7a
Use inline comments in `m.image_pack` example
anoadragon453 8077e81
Apply suggestions from code review
anoadragon453 bccfa91
Justify 32px default height.
anoadragon453 2f14ae1
Disallow spaces in shortcodes.
anoadragon453 d6e4e39
Add `m.image_pack.rooms` state event to reference packs
anoadragon453 8364b10
Add "linking to image pack" to Future Ideas section
anoadragon453 50512fe
Add "abusive images in packs" security concern
anoadragon453 e601f9e
Reorder shown priority of image packs
anoadragon453 d98ffdf
Apply suggestions from code review
anoadragon453 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
# MSC2545: Emoticons | ||
|
||
Emoticons, or short emotes...we need them! | ||
|
||
## Terminology | ||
Since there is a lot of confusion of how this relates to `m.emote`, why this isn't called custom emoji | ||
etc, there it is: | ||
|
||
`m.emtoe` is for emotion - and it has been incorrectly named this way. `m.action` would have been more | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
appropriate, as you use it to describe *actions*, not *emotions*. E.g. "/me is walking to the gym", or | ||
"/me is happy" and *not* "/me happy". | ||
|
||
That, however, is *not* what this MSC is about. Instead it is about emoticons, also known in short as | ||
emotes. | ||
|
||
Emoticons are just little images or text describing emotions or other things. Emoji are a subset of | ||
emoticons, namely those found within unicode. Custom emoji here would actually refer to a custom emoji | ||
font, that is your own rendering of 🦊, 🐱, etc., *not* new images. New images is what custom emoticons | ||
are for. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Now, a client may chose to name these however they like, we already have a naming disparity between | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
spec and clients with groups vs communities. It is, however, imperative to name things in the spec | ||
accurately after what they are. | ||
|
||
## Proposal | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Emoticons have at least a shortcode and an mxc uri. They are sent as `<img>` tags currently already in | ||
the spec, as such existing clients *should* be able to render them (support for this is sadly poor, | ||
even within riot flavours). To allow clients to distinguish emoticons from other inline images, a new | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
property `data-mx-emoticon` is introduced. A client may chose to ignore the size attributes of emoticons | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
when rendering, and instead pick the size based on other circumstances. This could e.g. be used to | ||
display emoticons in messages with only emoticons and emoji larger than usual, which is commonly found in | ||
messengers. Such an `<img>` tag of a shortcode `:emote:` and an mxc uri `mxc://example.org/emote` | ||
could look as follows: | ||
|
||
```html | ||
<img data-mx-emoticon src="mxc://example.org/emote" alt=":emote:" title=":emote:" height="32" /> | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
Both the `alt` and the `title` attributes are specified as they serve different purposes: `alt` is | ||
displayed if e.g. the emote does not load. `title` is displayed e.g. on mouse hover over the emote. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
The height is just a height that looks good on most devices with the normal, default font size. | ||
No width is displayed as to not weirdly squish non-square emotes. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Emoticon sources | ||
In order to be able to send emotes the client needs to have a list of shortcodes and their corresponding | ||
mxc uris. All the emote sources described here are merely suggestions on where to get emotes from. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
A client is free to implement / design their own way of fetching emotes, e.g. via dropping image files | ||
into a folder and uploading them on-the-fly. For cross-compatibility between clients it'd still be a | ||
good idea to implement the emote sources described here. For this there are two different emote sources: | ||
|
||
#### User emoticons | ||
User emotes are per-user emotes that are defined in the users respective account data. The type for that | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
is `im.ponies.user_emotes` (later: `m.emoticons`). The content is as following: | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```json | ||
{ | ||
"emoticons": { | ||
":emote": { | ||
"url": "mxc://example.org/blah" | ||
}, | ||
":other-emote:": { | ||
"url": "mxc://example.org/other-blah" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
The emotes are defined inside of a dict called `emoticons`. The key is the shortcode of the emoticon. | ||
The value is an object with `url` containing the mxc url of the emote. This is so that it is easier | ||
for clients or future MSCs to define custom metadata to emotes directly. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Some existing implementations using `im.ponies.user_emotes` currently use a dict called `short` which | ||
is just a map of the shortcode to the mxc url. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Room emoticons | ||
Room emotes are per-room emotes that every user of a specific room can use inside of that room. They | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
are set with a state event of type `im.ponies.room_emotes` (later: `m.emoticons`). The state key denotes a possible | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pack, whereas the default one would be a blank state key. E.g. a discord bridge could set as state key | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
`de.sorunome.mx-puppet-bridge.discord` and have all the bridged emotes in said state event, keeping | ||
bridged emotes from matrix emotes separate. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The content extends that of the user emotes: It uses the `emoticons` key, which is a map of the shortcode | ||
of the emote to an object containing its mxc url. Additionally, an optional `pack` key can be set, | ||
which defines meta-information on the pack. The following keys for `pack` are valid: | ||
|
||
- `displayname`: An easy displayname for the pack. Defaults to the room name, if it doesn't exist | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- `avatar_url`: The mxc uri of an avatar/icon to display for the pack. Defaults to the room name, | ||
if it doesn't exist. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- `name`: A short identifier of the pack. Defaults to the normalized state key, and if the state | ||
key is blank it defaults to "room". | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Normalized means here, converting spaces to `-`, taking only alphanumerical characters, `-` and `_`, | ||
and casting it all to lowercase. In regex, this would be `[a-z0-9-_]+`. | ||
|
||
As such, a `im.ponies.room_emotes` (later: `m.emoticons`) state event could look like the following: | ||
|
||
```json | ||
{ | ||
"emoticons": { | ||
":emote:": { | ||
"url": "mxc://example.org/blah" | ||
}, | ||
":other-emote:": { | ||
"url": "mxc://example.org/other-blah" | ||
} | ||
}, | ||
"pack": { | ||
"displayname": "Emotes from Discord!", | ||
"avatar_url": "mxc://example.org/discord_guild", | ||
"name": "some_discord_guild" | ||
} | ||
} | ||
``` | ||
|
||
Similarly as before, there are already existing implementations using `short` which is a map of shortcode | ||
to mxc url. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
anoadragon453 marked this conversation as resolved.
Show resolved
Hide resolved
anoadragon453 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#### Emoticon rooms | ||
While room emotes are specific to a room and are only accessible within that room, emote rooms should | ||
be accessible from everywhere. They do not differentiate themselves from room emotes at all, instead you | ||
set an event in your account data of type `im.ponies.emote_rooms` (later: `m.emoticons.rooms`) which outlines | ||
which room emote states should be globally accessible for that user. For that, a `room` key contains | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
a map of room ids that map to state keys that map to an optional pack definition override. | ||
The the contents of `im.ponies.emote_rooms` (later: `m.emoticons.rooms`) could look like the following: | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```json | ||
{ | ||
"rooms": { | ||
"!someroom:example.org": { | ||
"": {}, | ||
"de.sorunome.mx-puppet-bridge.discord": { | ||
"displayname": "Overriden name", | ||
"short": "new_short_name" | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
}, | ||
"!someotherroom:example.org": { | ||
"": {} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Here three emote packs are globally accessible to the user: Two defined in `!someroom:example.org` | ||
(one with blank state key and one with state key `de.sorunome.mx-puppet-bridge.discord`) and one in | ||
`!someotherroom:example.org`. | ||
|
||
### Emoticon source priority and deduplicating | ||
When giving emoticon suggestions, clients are expected to deduplicate emotes by their mxc url. This | ||
not only ensures that, when viewing a room that you also have in `m.emoticons.rooms`, it won't be | ||
displayed twice, but also if you have e.g. a bot which syncs emotes over multiple rooms, those will | ||
also be deduplicated. | ||
|
||
Lastly, in order to have consistent and expected ordering of emotes when suggesting, they should be | ||
suggested in the following order: | ||
1. User emoticons (emotes set to your own account) | ||
2. Emoticon rooms (rooms whos emotes you enabled to be accessible everywhere) | ||
3. Room emoticons (emotes defined in the currently open room) | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Sending | ||
In places where fancy tab-complete with the emote itself is not possible it is suggested that sending | ||
the shortcode will convert to the img tag, e.g. sending `Hey there :wave:` will convert to `Hey there <img-for-wave>`. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
If there are collisions due to the same emote shortcode being present as both room emote and user emote | ||
a user could specify the emote source by writing e.g. `:room~wave:` or `:user~wave:`. Here the short | ||
pack name is used for room emotes, and "user" for user emotes. If a room pack does not have a short | ||
pack name and has a blank state key, then "room" is used. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Current implementations | ||
### Emote rendering (rendering of the `<img>` tag) | ||
- riot-web | ||
- revolution | ||
- nheko | ||
- fluffychat | ||
### Emote sending, using the mentioned events here | ||
- revolution | ||
- fluffychat | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
## Security Considerations | ||
anoadragon453 marked this conversation as resolved.
Show resolved
Hide resolved
anoadragon453 marked this conversation as resolved.
Show resolved
Hide resolved
anoadragon453 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
When sending an `<img>` tag in an end-to-end encrypted room, the client will make a request to fetch | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
said image, in this case an emote. As there is no way to encrypt content behind `<img>` tags yet, | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this could potentially leak part of the conversation. This is **not** a new security consideration, | ||
it already exists. This, however, isn't much different from posting someone a link in an e2ee chat and | ||
the recipient opens the link. Additionally, images, and thus emotes, are often cached by the client, | ||
not even necessarily leading to an http query. | ||
|
||
## Alternatives | ||
One can easily think of near infinite ways to implement emotes. The aspect of sending an `<img>` tag | ||
and marking it as an emote with `data-mx-emoticon` seems to be pretty universal though, so the question | ||
is mainly on different emote sources. For that, a separate MSC, [MSC1951](https://github.com/matrix-org/matrix-doc/pull/1951) | ||
already exists, so it is reasonable to compare this one with that one: | ||
|
||
### Comparison with MSC1951 | ||
MSC1951 defines a dedicated room as the only emote source. This MSC, however, also allows you to bind emotes | ||
to your own account, offering greater flexibility. In MSC1951 there can also only be a single emote | ||
pack in a room. This could be problematic in e.g. bridged rooms: You set some emotes from the matrix | ||
side and a discord bridge would plop all the discord emotes in another pack in the same room. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
MSC1951 defines a way to recommend using a pack of a different room - this MSC does not have an equivalent | ||
to that. Instead, this MSC allows multiple emote packs for a room, and allows you to enable an emote | ||
pack to be globally available for yourself across all rooms you are in. | ||
|
||
The core difference is how MSC1951 and this MSC define the emotes themselves: In MSC1951 you have to | ||
set one state event *per emote*. While this might seem like a nice idea on the surface, it doesn't | ||
scale well. There are people who easily use and want hundreds or even thousands of emotes accessible. | ||
A simple dict of shortcode to mxc URI seems more appropriate for this. | ||
|
||
Additionally, this MSC has already been used in the wild for roughly two years by some clients, while MSC1951 | ||
only exists in MSC form. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
In general, MSC1951 feels like a heavier approach to emote sourcs, while this MSC is more lightweight | ||
and thus should allow for significantly larger packs. | ||
Sorunome marked this conversation as resolved.
Show resolved
Hide resolved
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.