# HG changeset patch # User Matthew Wild # Date 1584628754 0 # Node ID 1f90e333b1d89139de585d06dcff4cee50d63789 # Parent 2a5b42e4db0792824fa87091c9934da3b464102f mod_muc_hats_api: New API-only module for managing user hats in MUCs diff -r 2a5b42e4db07 -r 1f90e333b1d8 mod_muc_hats_api/README.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_hats_api/README.markdown Thu Mar 19 14:39:14 2020 +0000 @@ -0,0 +1,129 @@ +--- +summary: API for managing MUC hats +--- + +# Introduction + +This module provides an internal API (i.e. to other modules) to manage +'hats' for users in MUC rooms. + +Hats (first defined in XEP-0317, currently deferred) are additional identifiers +that can be attached to users in a group chat. For example in an educational +context, you may have a 'Teacher' hat that allows students to identify their +teachers. + +Hats consist of a machine-readable unique identifier (a URI), and optionally +a human-readable label. + +XEP-0317 suggests a protocol for users to manage their own hats, but though the +API in this module allows for both user-managed and system-managed hats, there is +currently no protocol implemented for users to manage their own hats, which is +rarely desired in real-world implementations. + +The rest of this documentation is designed for developers who use this module. + +## Data model + +### User + +``` +{ + "hats": { + "urn:uuid:164c41a2-7461-4cff-bdae-3f93078a6607": { + "active": false + }, + "http://example.com/hats/foo": { + "active": true, + "required": true, + "title": "Awesome" + } +} +``` + +| Field | Type | Description | +|-------|--------|--------------------------------------------------------------| +| hats | object | An object where mapping hat ids (key) to attachments (value) | + +Hat IDs must be a URI that uniquely identifies the hat. + +### Attachment + +``` +{ + "active": true, + "required": true, + "title": "My Awesome Hat" +} +``` + +| Field | Type | Description | +|----------|---------|-------------------------------------------------------------| +| active | boolean | If true, indicates the user is currently displaying the hat | +| required | boolean | If true, indicates the user is not able to remove the hat | +| title | string | A human-readable display name or label for the hat | + +All fields are optional, omitted boolean values are equivalent to false. + +## API + +All methods return 'nil, err' on failure as standard throughout the Prosody codebase. + +Example of using this module from another module: + +``` +local muc_hats = module:depends("muc_hats_api"); + +muc_hats.add_user_hat("user@localhost", "room@conference.localhost", "urn:uuid:164c41a2-7461-4cff-bdae-3f93078a6607", { active = true }); +``` + +Note that the module only works when loaded on a MUC host, which generally means any +module that uses it must also be loaded on the MUC host that it is managing. + +### add_user_hat + +`add_user_hat(user_jid, room_jid, hat_id, attachment)` + +Adds the identified hat to a user's... wardrobe? The user must already +have an affiliation with the room (i.e. member, admin or owner). + +If `attachment` is omitted, it defaults to `{}`. + +#### Error cases + +item-not-found +: Supplied room JID was not found on the current host + +item-not-found +: Supplied user JID was not affiliated with the room + +### remove_user_hat + +`remove_user_hat(user_jid, room_jid, hat_id)` + +If the identified hat is currently available to the user, it is removed. + +#### Error cases + +item-not-found +: Supplied room JID was not found on the current host + +item-not-found +: Supplied user JID was not affiliated with the room + +### set_user_hats + +`set_user_hats(user_jid, room_jid, hats)` + +Ensures the listed hats are the hats available to a user, automatically +adding/removing as necessary. + +The `hats` parameter should be an object mapping hat ids (keys) to attachment +objects (values). + +#### Error cases + +item-not-found +: Supplied room JID was not found on the current host + +item-not-found +: Supplied user JID was not affiliated with the room diff -r 2a5b42e4db07 -r 1f90e333b1d8 mod_muc_hats_api/mod_muc_hats_api.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_hats_api/mod_muc_hats_api.lua Thu Mar 19 14:39:14 2020 +0000 @@ -0,0 +1,75 @@ +local mod_muc = module:depends("muc"); + +function add_user_hat(user_jid, room_jid, hat_id, attachment) + local room = mod_muc.get_room_from_jid(room_jid); + if not room then + return nil, "item-not-found", "no such room"; + end + local user_aff = room:get_affiliation(user_jid); + if not user_aff then + return nil, "item-not-found", "user not affiliated with room"; + end + local aff_data = room:get_affiliation_data(user_jid) or {}; + local hats = aff_data.hats; + if not hats then + hats = {}; + aff_data.hats = hats; + end + + hats[hat_id] = { + active = attachment.active; + required = attachment.required; + title = attachment.title; + }; + + local ok, err = room:set_affiliation(true, user_jid, user_aff, nil, aff_data); + if not ok then + return nil, err; + end + return true; +end + +function remove_user_hat(user_jid, room_jid, hat_id) + local room = mod_muc.get_room_from_jid(room_jid); + if not room then + return nil, "item-not-found", "no such room"; + end + local user_aff = room:get_affiliation(user_jid); + if not user_aff then + return nil, "item-not-found", "user not affiliated with room"; + end + local aff_data = room:get_affiliation_data(user_jid); + local hats = aff_data and aff_data.hats; + if not hats then + return true; + end + + hats[hat_id] = nil; + + local ok, err = room:set_affiliation(true, user_jid, user_aff, nil, aff_data); + if not ok then + return nil, err; + end + return true; +end + +function set_user_hats(user_jid, room_jid, new_hats) + local room = mod_muc.get_room_from_jid(room_jid); + if not room then + return nil, "item-not-found", "no such room"; + end + local user_aff = room:get_affiliation(user_jid); + if not user_aff then + return nil, "item-not-found", "user not affiliated with room"; + end + local aff_data = room:get_affiliation_data(user_jid) or {}; + + aff_data.hats = new_hats; + + local ok, err = room:set_affiliation(true, user_jid, user_aff, nil, aff_data); + if not ok then + return nil, err; + end + return true; +end +