-
Notifications
You must be signed in to change notification settings - Fork 385
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
MSC1951: Custom sticker packs and emoji (mk II) #1951
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
# Custom emoji and sticker packs in Matrix | ||
|
||
This proposal is an iteration on [MSC1256](https://github.com/matrix-org/matrix-doc/issues/1256) which | ||
uses pre-existing Matrix concepts (namely rooms) to achieve the same goals. | ||
|
||
Having the ability to use custom emojis and stickers are often requested in various support channels, | ||
and the show of support on MSC1256 in +1's alone reinforces just how much this feature is desired by | ||
the community. Through some experimentation by the author, it is already possible to create custom | ||
sticker packs when using the author's integration manager (Dimension) and dedicated bot - this | ||
experimentation is a large driving factor for this proposal. | ||
|
||
For scope, this proposal aims to give users the ability to define their own custom emoji and sticker | ||
packs, share them, and actually use them. Rooms should also be able to recommend custom emoji and | ||
sticker packs for members of the room to use. In future, communities/groups should be able to do the | ||
same thing. | ||
|
||
Some issues, related projects, and documentation on the matter are: | ||
* https://github.com/vector-im/riot-web/issues/2950 | ||
* https://github.com/vector-im/riot-web/issues/2648 | ||
* https://github.com/vector-im/riot-web/issues/5157#issuecomment-340364320 | ||
* https://github.com/matrix-org/matrix-doc/pull/1174 - n.b. this is incorporated in this proposal. | ||
* https://github.com/turt2live/matrix-dimension/blob/master/docs/custom_stickerpacks.md - documentation | ||
on using Dimension to achieve this, with links to matrix-sticker-manager which handles the curation | ||
aspect. Implementation is within the same repo. | ||
|
||
## Proposal | ||
|
||
Custom emoji and sticker packs are the same thing technically, just handled differently in the client. | ||
Stickers in this proposal are the same `m.sticker`s found in the current Client-Server API specification. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be good to link for the spec for |
||
Emoji in this proposal are intended to be rendered by clients in a similar fashion to how they'd render | ||
regular unicode emoji in terms of sizing, positioning, etc. A "pack" is a collection of either emoji | ||
or stickers. | ||
|
||
Packs are to be mapped to dedicated rooms with the following semantics: | ||
* The room name is the pack's display name. | ||
* The room topic is the pack's description. | ||
* The room avatar is a thumbnail for the pack. If not present, the first sticker/emoji should be used. | ||
* The join rules and guest access control who is able to see the pack. For example, an invite only room | ||
would not be sharable without inviting people directly. | ||
* Specific state events (defined later) represent other metadata for the pack, such as the stickers/emoji | ||
and who created it. | ||
* The power levels define who is able to edit the pack. | ||
* Deleting a pack would be done by making the room invite only and kicking everyone. | ||
|
||
The room is just a plain Matrix room, therefore concepts like ACLs and upgrades still apply. As a note | ||
for implementations, pack metadata should be transferred when room upgrades happen to preserve the pack. | ||
Implementations should use the event timeline as a sort of comment section for the pack. | ||
|
||
|
||
#### Sharing packs with other users | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This sort of mechanism was designed in an era where it might be theoretically useful. It turned out not to be useful - it should be removed from this MSC. |
||
|
||
Sharing is achieved by sharing a link to the room, or inviting someone. There is no explicit grammar for | ||
what the room's alias should be as the presence of the pack metadata state events is an indicator that | ||
the room is a pack. | ||
|
||
Sharing can also be done through distributing plain `http` or `https` URLs which are able to point to the | ||
room in which the pack is defined. The intent is that 3rd party services can create sticker packs and | ||
distribute user friendly or SEO optimized URLs, such as `https://packs.example.org/1234/official-sheltie-pack`. | ||
When approached with `Accept: application/json` or have `.json` appended to the end of the URL, the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does the client know which to do? Do you need to try both? I think that a single method (probably If we really want the alternative URL we may allow returning HTML with |
||
following JSON body should be returned: | ||
```json | ||
{ | ||
"room_uri": "https://matrix.to/#/%23_stickerpack_example%3Aexample.org" | ||
} | ||
``` | ||
|
||
The `room_uri` is a URI which references a Matrix room. Currently that means a `matrix.to` URI however | ||
with future proposals that could be `mx://` or similar style schemes. When not approached with the | ||
intent of JSON, the response is up to the application serving the content. For example, this would be a | ||
good opportunity to render the sticker pack for viewing by a human. | ||
|
||
|
||
#### Recommending which packs to use in a room | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we now have Spaces, which can feature children. If packs are children, they can be featured the same way. |
||
|
||
Rooms can recommend which packs to use for members viewing the room by setting a state event which looks | ||
like the following: | ||
```json | ||
{ | ||
"type": "m.room.recommended_packs", | ||
"sender": "@travis:t2l.io", | ||
"content": { | ||
"packs": [ | ||
"https://matrix.to/#/%23_stickerpack_sample1%3Aexample.org", | ||
"https://matrix.to/#/%23_stickerpack_sample2%3Aexample.org" | ||
] | ||
}, | ||
"state_key": "", | ||
"origin_server_ts": 1554344486692, | ||
"event_id": "$D4h43bYVTf48tDTfkja6ROV13yJrzGUNGj7pySOVj6E", | ||
"room_id": "!gdRMqOrTFdOCYHNwOo:example.org" | ||
} | ||
``` | ||
|
||
If a client is making use of an embedded picker (such as the stickerpicker widget), it will need to send | ||
this information to the picker when it is opened. Building on [MSC1236](https://github.com/matrix-org/matrix-doc/issues/1236), | ||
when a widget is approved the `m.sticker` capability, a `toWidget` action of `recommended_packs` is sent | ||
roughly around the same time a `visibility` action would be sent to show the picker. The `data` of | ||
`recommended_packs` is: | ||
```json | ||
{ | ||
"room_id": "!gdRMqOrTFdOCYHNwOo:example.org", | ||
"packs": [ | ||
"https://matrix.to/#/%23_stickerpack_sample1%3Aexample.org", | ||
"https://matrix.to/#/%23_stickerpack_sample2%3Aexample.org" | ||
] | ||
} | ||
``` | ||
|
||
The `room_id` is the room ID where the packs are recommended and `packs` is verbatim from the | ||
`m.room.recommended_packs` state event content. The picker is then responsible for showing the packs to | ||
the user. The picker must acknowledge the `recommended_packs` action with an empty `response` object. | ||
Clients making use of their own picker are responsible for showing the packs to the user. In either | ||
case (built-in picker or widget), the application is not required to automatically show the whole pack | ||
to the user - a step where the user approves the use of the pack (potentially enabling it for all rooms) | ||
is encouraged. | ||
|
||
There is no current specification for an emoji picker widget, however future proposals which add this | ||
are encouraged to support the `recommended_packs` action. | ||
|
||
|
||
#### Tracking changes in sticker packs | ||
|
||
To track changes to packs, the interested application would join the pack's room. In the case of clients | ||
using their own picker, the logged in user would join the room (potentially with the client's assistance | ||
through dedicated UI/UX) - the client should hide the pack's room from the user where applicable, such as | ||
by not showing it in the room list. Pickers would perform the same action, however instead of using the | ||
logged in user's account they would use a bot account in the background to keep track of the pack. How | ||
this is accomplished is left as an implementation detail. | ||
|
||
How the application tracks changes is up to the application. For instance, this may be done with the `/sync` | ||
API or via Application Services. | ||
|
||
|
||
#### State events in the pack room | ||
|
||
There are two kinds of special events which appear in a pack's dedicated room: `m.pack.metadata` for | ||
the pack's metadata and zero or more `m.pack.item` for the stickers/emoji themselves. Both event types | ||
are state events. The presence of a `m.pack.metadata` event indicates that it is a pack room. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doing something like https://github.com/matrix-org/matrix-doc/issues/423 would be too big of a can of worms I suppose? Would be nice to have one way of identifying a non-IM room once such a thing lands ... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, clients will need to be able to switch a normal room to a pack room with this approach, as they can't make any assumptions when the Just an idea, but making this information part of the createRoom call / There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixing #423 would be out of scope here, but adding a flag to the create event is easily doable. It'd also probably fix the concern of moderators taking over rooms as pack rooms. |
||
|
||
A `m.pack.item` event looks as follows: | ||
```json | ||
{ | ||
"type": "m.pack.item", | ||
"sender": "@travis:t2l.io", | ||
"content": { | ||
"uri": "mxc://example.org/media_id", | ||
"description": "This is where a short sentence explaining the sticker goes", | ||
"shortcodes": [ | ||
":sample:", | ||
":cool_sticker:" | ||
] | ||
}, | ||
"state_key": "ThisIsTheItemIdToDistinguishItFromAnotherItem", | ||
"origin_server_ts": 1554344486692, | ||
"event_id": "$D4h43bYVTf48tDTfkja6ROV13yJrzGUNGj7pySOVj6E", | ||
"room_id": "!gdRMqOrTFdOCYHNwOo:example.org" | ||
} | ||
``` | ||
|
||
`shortcodes` are best explained as tags which can be used to find the item in the pack. They are arbitrary | ||
strings which clients (and pickers) should use to recommend things to the user. In the case of emoji, these | ||
will typically be surrounded in colons whereas stickers may simply have emoji like 🙂 and 😥. `shortcodes` | ||
are optional. | ||
|
||
The `description` is required and should be a human representation of the emoji or sticker. The description | ||
should be a single short sentence, but has no length limit. | ||
|
||
The `uri` is required and is a Matrix Content URI to the represented emoji or sticker. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an existing problem with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is a client expected to re-thumbnail images every time? Or does it make sense to include a common thumbnail here? |
||
|
||
The `state_key` of the item is the ID for the item. This is referenced by the `m.pack.metadata` event for | ||
listing which items are active. | ||
|
||
Items which have empty content or do not meet the schema should be considered inactive, even if the | ||
`m.pack.metadata` event recommends otherwise. This allows for an item to be deleted more easily from the | ||
pack, with the understanding that state in Matrix is permanent. Redacted `m.pack.item` events should be | ||
handled in this same way. | ||
|
||
|
||
A `m.pack.metadata` event looks as follows: | ||
```json | ||
{ | ||
"type": "m.pack.metadata", | ||
"sender": "@travis:t2l.io", | ||
"content": { | ||
"active_items": [ | ||
"ThisIsTheItemIdToDistinguishItFromAnotherItem-1", | ||
"ThisIsTheItemIdToDistinguishItFromAnotherItem-2" | ||
], | ||
"creator": "https://matrix.to/#/%40alice%3Aexample.org", | ||
"author": "https://matrix.to/#/%40alice%3Aexample.org", | ||
"license": "CC-BY-NC-SA-4.0", | ||
"kind": "m.stickers" | ||
}, | ||
"state_key": "ThisIsTheItemIdToDistinguishItFromAnotherItem", | ||
"origin_server_ts": 1554344486692, | ||
"event_id": "$D4h43bYVTf48tDTfkja6ROV13yJrzGUNGj7pySOVj6E", | ||
"room_id": "!gdRMqOrTFdOCYHNwOo:example.org" | ||
} | ||
``` | ||
|
||
`active_items` (required) are the `state_key`s of the `m.pack.item` events which should be considered | ||
active in the pack, provided they meet the criteria previously mentioned. | ||
|
||
The `creator` (required) is the person who created the pack, but not necessarily authored it. This is | ||
generally used by third party services which allow users to upload items which they have not created. | ||
This must be a valid `http`, `https`, or `matrix.to` (in future `mx://`) URI. | ||
|
||
The `author` is optional and if not provided is assumed to be the same as the `creator`. This is the | ||
person who authored the items contained within the pack. For example, if the pack was commissioned by | ||
the `creator`, this would be the person who did the artwork. This must be a valid `http`, `https`, or | ||
`matrix.to` (in future `mx://`) URI. | ||
|
||
The `license` is required and must be a valid SPDX identifier. This applies to all the items contained | ||
within the pack. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not distributing software, and often, the licensing for non-software has different concerns. As such, I find it a little strange that we're tying licensing to a system specifically for software (it's in the name!) While I think it's ok to just use the SPDX identifiers to facilitate CC usage, I think this needs a little more attention in the direction of custom licensing, which doesn't seem to be possible right now. |
||
|
||
The `kind` must be either `m.stickers` for a sticker pack or `m.emoji` for emoji. This is the hint used | ||
by applications to determine how best to handle items. | ||
|
||
While the presence of an `m.pack.metadata` event signifies that the room is a pack room, if the event | ||
is invalid then it should be treated as an error by applications. | ||
|
||
|
||
#### Representation of packs on `m.sticker` events | ||
|
||
To cover [matrix-doc#1174](https://github.com/matrix-org/matrix-doc/pull/1174), a new optional field | ||
is to be added to `m.sticker` events: `pack_url`. This is the sharable URI for the pack which contains | ||
the sticker, if the sticker is contained within a known pack. For example, this may be a `matrix.to` | ||
(or `mx://` in future) URI or a URL which the application can resolve to a room as per earlier in this | ||
proposal. | ||
|
||
Because access control is handled by Matrix itself, applications should not omit the field if the pack | ||
is private and instead send it anyways. | ||
Comment on lines
+231
to
+232
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I couldn't actually find any explanation of how the access control is supposed to work - could you clarify? |
||
|
||
|
||
#### Representation of custom emoji in messages | ||
|
||
Emoji are intended to be represented inline as `<img>` elements in the HTML body. For the plain text | ||
representation, the shortcode the user used (or the first one) should be used. Using the `mxc://` URI | ||
for the item, the `img` element should look similar to: | ||
```html | ||
<img src="mxc://example.org/media_id" width="16" height="16" | ||
alt="This is the item's description" | ||
data-mx-pack="https://matrix.to/#/%23_stickerpack_sample1%3Aexample.org" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you intending to use the existence of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that would be the plan, yup. I apparently didn't say that though - will add. |
||
``` | ||
|
||
The `src` is the `mxc://` URI of the item, and the `alt` is the description of the item. The `width` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. under MSC3911 it would need to be the URI of a copy of the item. |
||
and `height` are provided as a fallback for clients which do not support custom emoji. Clients which | ||
do support custom emoji can ignore the dimensions and use whatever is appropriate for their purposes. | ||
The `data-mx-pack` attribute has the same requirements as the `pack_url` for stickers mentioned above. | ||
|
||
|
||
## Tradeoffs | ||
|
||
This proposal does not define an emoji picker widget similar to the existing sticker picker widget. An | ||
emoji picker would be best served as a dedicated proposal due to the complexity involved in defining | ||
its behaviour, such as how it interacts with a client's potential autocomplete mechanics. Similarly, | ||
the sticker picker widget is minimally extended in this proposal to push the problem of enhanced client | ||
interaction to a future proposal. Clients are likely to want to handle custom emoji themselves anyhow | ||
for integration with their autocomplete mechanisms and for general performance. | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can I use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stickers will not cause any backwards incompatible change, so directly testing on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
## Security considerations | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I understand it, users who want to use a sticker pack will have to join the "virtual" room of that pack. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally we get peeking over federation before then. |
||
|
||
Due to the heuristics of `m.pack.metadata` events, it is possible for moderators to hide rooms from | ||
existing members by applying the state event. Moderators already have several opportunities to damage | ||
a room, and are already considered in a place of trust to not do so maliciously - therefore, this | ||
risk of mitigated by having trusted moderators and administrators in a room, just like with any other | ||
new state event which would be added. | ||
|
||
The sharable URL for a pack could leak information about who created it or what it contains, potentially | ||
subjecting the user to personal risk. It is expected that implementations handle this appropriately | ||
where possible, such as by giving the creator the option to create private packs with randomly generated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Randomly generating a room alias is all well and good on matrix.org, but is kind of pointless on a one user HS? I am not sure I can see a way out of that though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For single user homeservers it sounds like it would get into MSC1228 territory. |
||
room aliases to hide their contents from outsiders. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@steef435 said (#1951 (comment)):
(sorry, we use threads to do this sort of thing to track replies)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed none of this is encrypted, by design. Users don't have to join the room themselves either: they can rely on an integration manager which would join for them (and everyone else using that instance) and therefore hide their identity. This is the preferred approach.
In future, Matrix will support peeking over federation which means you won't even have to join the room.
As for the files themselves: By nature of this being shared information it's not reasonable to add encryption, as it would impede other people's ability to use the stickers (and you'd risk clients forwarding the encrypted info, which might disclose some metadata about the creator). There's nothing stopping this proposal from getting encryption support in the future, though it is intentionally out of scope for this iteration.
In general, this feature is meant for sharing sticker packs, not defining how they must be stored. A privacy-focused client would likely support a feature for encrypted sticker packs that didn't use this MSC, which would be completely reasonable. That client would potentially have some sort of sharing mechanism, though encryption and sharing do not mesh well by design.
To be clear: the intention is that if someone wants to privately share stickers, they do so through some other mechanism. This MSC is intentionally designed and written to support the more common case of publicly shareable information, similar to what you'd stick on a billboard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks! I hope there will be room for secret sticker in the spec in the future.
Small reply:
The way I think about it is that once you'd receive a sticker, you'd get access to the pack uri and the encryption key through the sticker event. So they would be shareable enough but still confined. I'm not too sure about what you mean by creator metadata, it's fine if a keypair is created and used solely for one pack right?
I now get this is not the scope though. I'll read up on the integration manager concept, I thought that was used to add "things" to rooms, not to clients. Anyway, thanks for your response!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is best to implement encryption here. I see a number of benifits:
TL;DR I think we should encrypte all sticker packs (and stickers). This means that we have one implementation that is unsurprising with regards to privacy.