atagen

malice

public
Star0
Watch0
4929dcd3493716eb569d34503d3dee34b610742c
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: MACLTOK_IMAGE_PACKS.md

544a170ead2catagen2026-10-20 11:47 UTC1
# macltok request, custom emote and sticker packs (MSC2545)
544a170ead2catagen2026-10-20 11:47 UTC2
544a170ead2catagen2026-10-20 11:47 UTC3
## Problem
544a170ead2catagen2026-10-20 11:47 UTC4
544a170ead2catagen2026-10-20 11:47 UTC5
Malice is adding an emoji picker for reactions that should also offer the user's custom emotes and stickers. Those live in MSC2545 image packs, spread over three places.
544a170ead2catagen2026-10-20 11:47 UTC6
544a170ead2catagen2026-10-20 11:47 UTC7
1. `im.ponies.user_emotes` global account data, the user's personal pack
544a170ead2catagen2026-10-20 11:47 UTC8
2. `im.ponies.room_emotes` room state, one pack per state key, in the current room
544a170ead2catagen2026-10-20 11:47 UTC9
3. `im.ponies.emote_rooms` global account data, which names other rooms (and state keys) whose packs the user enabled everywhere
544a170ead2catagen2026-10-20 11:47 UTC10
544a170ead2catagen2026-10-20 11:47 UTC11
Merging those, resolving pack vs image `usage`, and following `emote_rooms` into rooms the client may not have state for is protocol work every consumer would repeat, so it belongs in macltok. Malice can't do it today anyway. `global_account_data` covers 1 and 3, but there is no room state getter for 2.
544a170ead2catagen2026-10-20 11:47 UTC12
544a170ead2catagen2026-10-20 11:47 UTC13
ruma-events 0.34 already has the types behind `unstable-msc2545` (`image_pack.rs`, `RoomImagePackEventContent`, `AccountImagePackEventContent`, `ImagePackRoomsEventContent`, `PackUsage`), including the stable `m.image_pack` aliases.
544a170ead2catagen2026-10-20 11:47 UTC14
544a170ead2catagen2026-10-20 11:47 UTC15
## Request
544a170ead2catagen2026-10-20 11:47 UTC16
544a170ead2catagen2026-10-20 11:47 UTC17
A normalised query for the packs usable in a given room.
544a170ead2catagen2026-10-20 11:47 UTC18
544a170ead2catagen2026-10-20 11:47 UTC19
```rust
544a170ead2catagen2026-10-20 11:47 UTC20
impl ClientHandle {
544a170ead2catagen2026-10-20 11:47 UTC21
    /// user pack, then the room's own packs, then emote_rooms packs, deduplicated
544a170ead2catagen2026-10-20 11:47 UTC22
    pub async fn image_packs(&self, room_id: Option<OwnedRoomId>) -> Result<Vec<ImagePack>, Error>;
544a170ead2catagen2026-10-20 11:47 UTC23
}
544a170ead2catagen2026-10-20 11:47 UTC24
544a170ead2catagen2026-10-20 11:47 UTC25
pub struct ImagePack {
544a170ead2catagen2026-10-20 11:47 UTC26
    pub id: String,
544a170ead2catagen2026-10-20 11:47 UTC27
    pub display_name: String,
544a170ead2catagen2026-10-20 11:47 UTC28
    pub avatar_url: Option<OwnedMxcUri>,
544a170ead2catagen2026-10-20 11:47 UTC29
    pub source: ImagePackSource,
544a170ead2catagen2026-10-20 11:47 UTC30
    pub images: Vec<PackImage>,
544a170ead2catagen2026-10-20 11:47 UTC31
}
544a170ead2catagen2026-10-20 11:47 UTC32
544a170ead2catagen2026-10-20 11:47 UTC33
pub enum ImagePackSource {
544a170ead2catagen2026-10-20 11:47 UTC34
    User,
544a170ead2catagen2026-10-20 11:47 UTC35
    Room { room_id: OwnedRoomId, state_key: String },
544a170ead2catagen2026-10-20 11:47 UTC36
}
544a170ead2catagen2026-10-20 11:47 UTC37
544a170ead2catagen2026-10-20 11:47 UTC38
pub struct PackImage {
544a170ead2catagen2026-10-20 11:47 UTC39
    pub shortcode: String,
544a170ead2catagen2026-10-20 11:47 UTC40
    pub url: OwnedMxcUri,
544a170ead2catagen2026-10-20 11:47 UTC41
    pub body: Option<String>,
544a170ead2catagen2026-10-20 11:47 UTC42
    /// image usage if set, else the pack's, else both
544a170ead2catagen2026-10-20 11:47 UTC43
    pub usage: Vec<PackUsage>,
544a170ead2catagen2026-10-20 11:47 UTC44
}
544a170ead2catagen2026-10-20 11:47 UTC45
```
544a170ead2catagen2026-10-20 11:47 UTC46
544a170ead2catagen2026-10-20 11:47 UTC47
`id` only needs to be stable for a pack across calls, e.g. `user` or `room:<room_id>:<state_key>`. Serialised field names should match the rest of macltok's normalised types. Malice's web client already has the matching TypeScript shape in `web/src/lib/types.ts` (`ImagePack`, `PackImage`).
544a170ead2catagen2026-10-20 11:47 UTC48
544a170ead2catagen2026-10-20 11:47 UTC49
It would also help to get a change event when any of the three sources updates, so the picker doesn't go stale, either a dedicated event or folding it into the existing room/account mirror.
544a170ead2catagen2026-10-20 11:47 UTC50
544a170ead2catagen2026-10-20 11:47 UTC51
## Related, reactions with custom emotes (MSC4027)
544a170ead2catagen2026-10-20 11:47 UTC52
544a170ead2catagen2026-10-20 11:47 UTC53
A custom emote reaction uses the image's mxc URI as the `m.annotation` key, with the shortcode in `com.beeper.reaction.shortcode` so clients without the pack can show a label.
544a170ead2catagen2026-10-20 11:47 UTC54
544a170ead2catagen2026-10-20 11:47 UTC55
- `toggle_reaction` only takes a key. An optional shortcode would let malice send the label.
544a170ead2catagen2026-10-20 11:47 UTC56
- `normalise::timeline::reactions` drops the shortcode on receipt. `Reaction` could carry `shortcode: Option<String>`, taken from any sender's annotation for that key.
544a170ead2catagen2026-10-20 11:47 UTC57
544a170ead2catagen2026-10-20 11:47 UTC58
Malice relaxed its own 32-char key limit for `mxc://` keys, so sending already works without the shortcode.