diff --git a/api/check_examples.py b/api/check_examples.py index f08b2dc1..ee3c773c 100755 --- a/api/check_examples.py +++ b/api/check_examples.py @@ -51,8 +51,8 @@ def check_parameter(filepath, request, parameter): schema['id'] = fileurl jsonschema.validate(example, schema) except Exception as e: - raise ValueError("Error validating JSON schema for %r %r" % ( - request, code + raise ValueError("Error validating JSON schema for %r" % ( + request ), e) diff --git a/api/client-server/v1/definitions/push_condition.json b/api/client-server/v1/definitions/push_condition.json new file mode 100644 index 00000000..1d84955c --- /dev/null +++ b/api/client-server/v1/definitions/push_condition.json @@ -0,0 +1,9 @@ +{ + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": ["event_match", "profile_tag", "contains_display_name", "room_member_count"] + } + } +} \ No newline at end of file diff --git a/api/client-server/v1/definitions/push_rule.json b/api/client-server/v1/definitions/push_rule.json new file mode 100644 index 00000000..4df93f67 --- /dev/null +++ b/api/client-server/v1/definitions/push_rule.json @@ -0,0 +1,20 @@ +{ + "type": "object", + "properties": { + "default": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "rule_id": { + "type": "string" + }, + "actions": { + "items": { + "type": ["object", "string"] + }, + "type": "array" + } + } +} \ No newline at end of file diff --git a/api/client-server/v1/definitions/push_ruleset.json b/api/client-server/v1/definitions/push_ruleset.json new file mode 100644 index 00000000..e0372701 --- /dev/null +++ b/api/client-server/v1/definitions/push_ruleset.json @@ -0,0 +1,60 @@ +{ + "type": "object", + "properties": { + "content": { + "items": { + "type": "object", + "allOf": [ + { + "$ref": "push_rule.json" + } + ] + }, + "type": "array" + }, + "override": { + "items": { + "type": "object", + "allOf": [ + { + "$ref": "push_rule.json" + } + ] + }, + "type": "array" + }, + "sender": { + "items": { + "type": "object", + "allOf": [ + { + "$ref": "push_rule.json" + } + ] + }, + "type": "array" + }, + "underride": { + "items": { + "type": "object", + "allOf": [ + { + "$ref": "push_rule.json" + } + ] + }, + "type": "array" + }, + "room": { + "items": { + "type": "object", + "allOf": [ + { + "$ref": "push_rule.json" + } + ] + }, + "type": "array" + } + } +} \ No newline at end of file diff --git a/api/client-server/v1/push_notifier.yaml b/api/client-server/v1/push_notifier.yaml new file mode 100644 index 00000000..be3b5f74 --- /dev/null +++ b/api/client-server/v1/push_notifier.yaml @@ -0,0 +1,192 @@ +swagger: '2.0' +info: + title: "Matrix Push Notification API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/push/v1 +consumes: + - application/json +produces: + - application/json +paths: + "/notify": + post: + summary: Notify a push gateway about an event. + description: |- + This endpoint is invoked by HTTP pushers to notify a push gateway about + an event. + *NB: Notifications are sent to the URL configured when the pusher is + created. This means that the HTTP path may be different depending on the + push gateway.* + parameters: + - in: body + name: notification + description: Information about the push notification. + required: true + schema: + type: object + example: |- + { + "notification": { + "id": "$3957tyerfgewrf384", + "room_id": "!slw48wfj34rtnrf:example.com", + "type": "m.room.message", + "sender": "@exampleuser:matrix.org", + "sender_display_name": "Major Tom", + "room_name": "Mission Control", + "room_alias": "#exampleroom:matrix.org", + "prio": "high", + "content": { + "msgtype": "m.text", + "body": "I'm floating in a most peculiar way." + } + }, + "counts": { + "unread" : 2, + "missed_calls": 1 + }, + "devices": [ + { + "app_id": "org.matrix.matrixConsole.ios", + "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", + "pushkey_ts": 12345678, + "data" : {}, + "tweaks": { + "sound": "bing" + } + } + ] + } + required: ["notification", "counts", "devices"] + properties: + notification: + type: object + description: Information about the push notification + required: ["id", "room_id", "type", "sender"] + properties: + id: + type: string + description: |- + An identifier for this notification that may be used to + detect duplicate notification requests. This is not + necessarily the ID of the event that triggered the + notification. + room_id: + type: string + description: The ID of the room in which this event occurred. + type: + type: string + description: The type of the event as in the event's ``type`` field. + sender: + type: string + description: The sender of the event as in the corresponding event field. + sender_display_name: + type: string + description: |- + The current display name of the sender in the room in which + the event occurred. + room_name: + type: string + description: The name of the room in which the event occurred. + room_alias: + type: string + description: An alias to display for the room in which the event occurred. + user_is_target: + type: boolean + description: |- + This is true if the user receiving the notification is the + subject of a member event (i.e. the ``state_key`` of the + member event is equal to the user's Matrix ID). + prio: + type: string + enum: ["high", "low"] + description: |- + The priority of the notification. If omitted, ``high`` is + assumed. This may be used by push gateways to deliver less + time-sensitive notifications in a way that will preserve + battery power on mobile devices. + content: + type: object + title: EventContent + description: |- + The ``content`` field from the event, if present. If the + event had no content field, this field is omitted. + counts: + type: object + description: |- + This is a dictionary of the current number of unacknowledged + communications for the recipient user. Counts whose value is + zero are omitted. + properties: + unread: + type: integer + description: |- + The number of unread messages a user has across all of the + rooms they are a member of. + missed_calls: + type: integer + description: |- + The number of unacknowledged missed calls a user has + across all rooms of which they are a member. + devices: + type: array + title: Devices + description: |- + This is an array of devices that the notification should be sent to. + items: + type: object + properties: + app_id: + type: string + description: |- + The app_id given when the pusher was created. + pushkey: + type: string + description: The pushkey given when the pusher was created. + pushkey_ts: + type: integer + description: |- + The unix timestamp (in seconds) when the + pushkey was last updated. + data: + type: object + title: PusherData + description: |- + A dictionary of additional pusher-specific data. For + 'http' pushers, this is the data dictionary passed in at + pusher creation minus the ``url`` key. + tweaks: + type: object + title: Tweaks + description: |- + A dictionary of customisations made to the way this + notification is to be presented. These are added by push rules. + responses: + 200: + description: A list of rejected push keys. + examples: + application/json: |- + { + "rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ] + } + schema: + type: object # empty json object + properties: + rejected: + type: array + description: |- + A list of all pushkeys given in the notification request that + are not valid. These could have been rejected by an upstream + gateway because they have expired or have never been valid. + Homeservers must cease sending notification requests for these + pushkeys and remove the associated pushers. It may not + necessarily be the notification in the request that failed: + it could be that a previous notification to the same pushkey + failed. + items: + type: string + description: A pushkey + diff --git a/api/client-server/v1/pusher.yaml b/api/client-server/v1/pusher.yaml new file mode 100644 index 00000000..8c243f2b --- /dev/null +++ b/api/client-server/v1/pusher.yaml @@ -0,0 +1,144 @@ +swagger: '2.0' +info: + title: "Matrix Client-Server v1 Push API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/api/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + accessToken: + type: apiKey + description: The user_id or application service access_token + name: access_token + in: query +paths: + "/pushers/set": + post: + summary: Modify a pusher for this user on the homeserver. + description: |- + This endpoint allows the creation, modification and deletion of `pushers`_ + for this user ID. The behaviour of this endpoint varies depending on the + values in the JSON body. + security: + - accessToken: [] + parameters: + - in: body + name: pusher + description: The pusher information + required: true + schema: + type: object + example: |- + { + "lang": "en", + "kind": "http", + "app_display_name": "Mat Rix", + "device_display_name": "iPhone 9", + "app_id": "com.example.app.ios", + "profile_tag": "4bea66906d0111e59d70feff819cdc9f", + "pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ", + "data": { + "url": "https://push-gateway.location.here" + }, + "append": false + } + properties: + pushkey: + type: string + description: |- + This is a unique identifier for this pusher. The value you + should use for this is the routing or destination address + information for the notification, for example, the APNS token + for APNS or the Registration ID for GCM. If your notification + client has no such concept, use any unique identifier. + Max length, 512 bytes. + kind: + type: string + enum: ["http", null] + description: |- + The kind of pusher to configure. ``"http"`` makes a pusher that + sends HTTP pokes. ``null`` deletes the pusher. + profile_tag: + type: string + description: |- + This is a string that determines what set of device rules will + be matched when evaluating push rules for this pusher. It is + an arbitrary string. Multiple devices may use the same + ``profile_tag``. It is advised that when an app's data is + copied or restored to a different device, this value remain + the same. Client apps should offer ways to change the + ``profile_tag``, optionally copying rules from the old + profile tag. Max length, 32 bytes. + app_id: + type: string + description: |- + This is a reverse-DNS style identifier for the application. + It is recommended that this end with the platform, such that + different platform versions get different app identifiers. + Max length, 64 chars. + app_display_name: + type: string + description: |- + A string that will allow the user to identify what application + owns this pusher. + device_display_name: + type: string + description: |- + A string that will allow the user to identify what device owns + this pusher. + lang: + type: string + description: |- + The preferred language for receiving notifications (e.g. 'en' + or 'en-US') + data: + type: object + description: |- + A dictionary of information for the pusher implementation + itself. If ``kind`` is ``http``, this should contain ``url`` + which is the URL to use to send notifications to. + properties: + url: + type: string + description: |- + Required if ``kind`` is ``http``. The URL to use to send + notifications to. + append: + type: boolean + description: |- + If true, the homeserver should add another pusher with the + given pushkey and App ID in addition to any others with + different user IDs. Otherwise, the Home Server must remove any + other pushers with the same App ID and pushkey for different + users. The default is ``false``. + required: ['profile_tag', 'kind', 'app_id', 'app_display_name', + 'device_display_name', 'pushkey', 'lang', 'data'] + responses: + 200: + description: The pusher was set. + examples: + application/json: |- + {} + schema: + type: object # empty json object + 400: + description: One or more of the pusher values were invalid. + examples: + application/json: |- + { + "error": "Missing parameters: lang, data", + "errcode": "M_MISSING_PARAM" + } + schema: + type: object + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/error.yaml" + diff --git a/api/client-server/v1/pushrules.yaml b/api/client-server/v1/pushrules.yaml new file mode 100644 index 00000000..31e84f55 --- /dev/null +++ b/api/client-server/v1/pushrules.yaml @@ -0,0 +1,488 @@ +swagger: '2.0' +info: + title: "Matrix Client-Server v1 Push Rules API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/api/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + accessToken: + type: apiKey + description: The user_id or application service access_token + name: access_token + in: query +paths: + "/pushrules/": + get: + summary: Retrieve all push rulesets. + description: |- + Retrieve all push rulesets for this user. Clients can "drill-down" on + the rulesets by suffixing a ``scope`` to this path e.g. + ``/pushrules/global/``. This will return a subset of this data under the + specified key e.g. the ``global`` key. + security: + - accessToken: [] + responses: + 200: + description: All the push rulesets for this user. + schema: + type: object + required: ["device", "global"] + properties: + device: + type: object + title: Devices + description: A dictionary of profile tags to rulesets. + additionalProperties: + x-pattern: "$PROFILE_TAG" + type: object + description: The ruleset for this profile tag. + title: Ruleset + allOf: [ + "$ref": "definitions/push_ruleset.json" + ] + global: + type: object + description: The global ruleset. + title: Ruleset + allOf: [ + "$ref": "definitions/push_ruleset.json" + ] + examples: + application/json: |- + { + "device": {}, + "global": { + "content": [ + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak": "highlight" + } + ], + "default": true, + "enabled": true, + "pattern": "alice", + "rule_id": ".m.rule.contains_user_name" + } + ], + "override": [ + { + "actions": [ + "dont_notify" + ], + "conditions": [], + "default": true, + "enabled": false, + "rule_id": ".m.rule.master" + }, + { + "actions": [ + "dont_notify" + ], + "conditions": [ + { + "key": "content.msgtype", + "kind": "event_match", + "pattern": "m.notice" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.suppress_notices" + } + ], + "room": [], + "sender": [], + "underride": [ + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "ring" + }, + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.call.invite" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.call" + }, + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak": "highlight" + } + ], + "conditions": [ + { + "kind": "contains_display_name" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.contains_display_name" + }, + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "is": "2", + "kind": "room_member_count" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.room_one_to_one" + }, + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.member" + }, + { + "key": "content.membership", + "kind": "event_match", + "pattern": "invite" + }, + { + "key": "state_key", + "kind": "event_match", + "pattern": "@alice:example.com" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.invite_for_me" + }, + { + "actions": [ + "notify", + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.member" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.member_event" + }, + { + "actions": [ + "notify", + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.message" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.message" + } + ] + } + } + "/pushrules/{scope}/{kind}/{ruleId}": + get: + summary: Retrieve a push rule. + description: |- + Retrieve a single specified push rule. + security: + - accessToken: [] + parameters: + - in: path + type: string + name: scope + required: true + x-example: "global" + description: |- + Either ``global`` or ``device/`` to specify global + rules or device rules for the given ``profile_tag``. + - in: path + type: string + name: kind + required: true + x-example: room + enum: ["override", "underride", "sender", "room", "content"] + description: | + The kind of rule + - in: path + type: string + name: ruleId + required: true + x-example: "#spam:example.com" + description: | + The identifier for the rule. + responses: + 200: + description: |- + The specific push rule. This will also include keys specific to the + rule itself such as the rule's ``actions`` and ``conditions`` if set. + examples: + application/json: |- + { + "actions": [ + "dont_notify" + ], + "rule_id": "#spam:matrix.org", + "enabled": true + } + schema: + type: object + description: The push rule. + title: PushRule + allOf: [ + "$ref": "definitions/push_rule.json" + ] + delete: + summary: Delete a push rule. + description: |- + This endpoint removes the push rule defined in the path. + security: + - accessToken: [] + parameters: + - in: path + type: string + name: scope + required: true + x-example: "global" + description: |- + Either ``global`` or ``device/`` to specify global + rules or device rules for the given ``profile_tag``. + - in: path + type: string + name: kind + required: true + x-example: room + enum: ["override", "underride", "sender", "room", "content"] + description: | + The kind of rule + - in: path + type: string + name: ruleId + required: true + x-example: "#spam:example.com" + description: | + The identifier for the rule. + responses: + 200: + description: The push rule was deleted. + examples: + application/json: |- + {} + schema: + type: object # empty json object + put: + summary: Add or change a push rule. + description: |- + This endpoint allows the creation, modification and deletion of pushers + for this user ID. The behaviour of this endpoint varies depending on the + values in the JSON body. + security: + - accessToken: [] + parameters: + - in: path + type: string + name: scope + required: true + x-example: "global" + description: |- + Either ``global`` or ``device/`` to specify global + rules or device rules for the given ``profile_tag``. + - in: path + type: string + name: kind + required: true + x-example: room + enum: ["override", "underride", "sender", "room", "content"] + description: | + The kind of rule + - in: path + type: string + name: ruleId + required: true + x-example: "#spam:example.com" + description: | + The identifier for the rule. + - in: query + type: string + name: before + required: false + x-example: someRuleId + description: |- + Use 'before' with a ``rule_id`` as its value to make the new rule the + next-most important rule with respect to the given rule. + - in: query + type: string + name: after + required: false + x-example: anotherRuleId + description: |- + This makes the new rule the next-less important rule relative to the + given rule. + - in: body + name: pushrule + description: |- + The push rule data. Additional top-level keys may be present depending + on the parameters for the rule ``kind``. + required: true + schema: + type: object + example: |- + { + "pattern": "cake*lie", + "actions": ["notify"] + } + properties: + actions: + type: array + description: |- + The action(s) to perform when the conditions for this rule are met. + items: + type: string + enum: ["notify", "dont_notify", "coalesce", "set_tweak"] + # TODO: type: object e.g. {"set_sound":"beeroclock.wav"} :/ + conditions: + type: array + description: |- + The conditions that must hold true for an event in order for a + rule to be applied to an event. A rule with no conditions + always matches. + items: + type: object + title: conditions + allOf: [ "$ref": "definitions/push_condition.json" ] + required: ["actions"] + responses: + 200: + description: The pusher was set. + examples: + application/json: |- + {} + schema: + type: object # empty json object + 400: + description: There was a problem configuring this push rule. + examples: + application/json: |- + { + "error": "before/after rule not found: someRuleId", + "errcode": "M_UNKNOWN" + } + schema: + type: object + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/error.yaml" + "/pushrules/{scope}/{kind}/{ruleId}/enabled": + put: + summary: "Enable or disable a push rule." + description: |- + This endpoint allows clients to enable or disable the specified push rule. + security: + - accessToken: [] + parameters: + - in: path + type: string + name: scope + required: true + x-example: "global" + description: |- + Either ``global`` or ``device/`` to specify global + rules or device rules for the given ``profile_tag``. + - in: path + type: string + name: kind + required: true + x-example: room + enum: ["override", "underride", "sender", "room", "content"] + description: | + The kind of rule + - in: path + type: string + name: ruleId + required: true + x-example: "#spam:example.com" + description: | + The identifier for the rule. + - in: body + name: + description: | + Whether the push rule is enabled or not. + required: true + schema: + type: boolean + example: |- + true + responses: + 200: + description: The push rule was enabled or disabled. + examples: + application/json: |- + {} + schema: + type: object # empty json object diff --git a/specification/modules/push.rst b/specification/modules/push.rst new file mode 100644 index 00000000..6e9c8536 --- /dev/null +++ b/specification/modules/push.rst @@ -0,0 +1,438 @@ +Push Notifications +================== + +.. _module:push: + +:: + + +--------------------+ +-------------------+ + Matrix HTTP | | | | + Notification Protocol | App Developer | | Device Vendor | + | | | | + +-------------------+ | +----------------+ | | +---------------+ | + | | | | | | | | | | + | Matrix Home Server+-----> Push Gateway +------> Push Provider | | + | | | | | | | | | | + +-^-----------------+ | +----------------+ | | +----+----------+ | + | | | | | | + Matrix | | | | | | + Client/Server API + | | | | | + | | +--------------------+ +-------------------+ + | +--+-+ | + | | <-------------------------------------------+ + +---+ | + | | Provider Push Protocol + +----+ + + Mobile Device or Client + + +This module adds support for push notifications. Homeservers send notifications +of events to user-configured HTTP endpoints. Users may also configure a +number of rules that determine which events generate notifications. These are +all stored and managed by the user's homeserver. This allows user-specific push +settings to be reused between client applications. + +The above diagram shows the flow of push notifications being sent to a handset +where push notifications are submitted via the handset vendor, such as Apple's +APNS or Google's GCM. This happens as follows: + + 1. The client app signs in to a homeserver. + 2. The client app registers with its vendor's Push Provider and + obtains a routing token of some kind. + 3. The mobile app uses the Client/Server API to add a 'pusher', providing the + URL of a specific Push Gateway which is configured for that + application. It also provides the routing token it has acquired from the + Push Provider. + 4. The homeserver starts sending HTTP requests to the Push Gateway using the + supplied URL. The Push Gateway relays this notification to + the Push Provider, passing the routing token along with any + necessary private credentials the provider requires to send push + notifications. + 5. The Push Provider sends the notification to the device. + +Definitions for terms used in this section are below: + +Push Provider + A push provider is a service managed by the device vendor which can send + notifications directly to the device. Google Cloud Messaging (GCM) and Apple + Push Notification Service (APNS) are two examples of push providers. + +Push Gateway + A push gateway is a server that receives HTTP event notifications from + homeservers and passes them on to a different protocol such as APNS for iOS + devices or GCM for Android devices. Clients inform the homeserver which + Push Gateway to send notifications to when it sets up a Pusher. + +.. _def:pushers: + +Pusher + A pusher is a worker on the homeserver that manages the sending + of HTTP notifications for a user. A user can have multiple pushers: one per + device. + +Push Rule + A push rule is a single rule that states under what *conditions* an event should + be passed onto a push gateway and *how* the notification should be presented. + These rules are stored on the user's homeserver. They are manually configured + by the user, who can create and view them via the Client/Server API. + +Push Ruleset + A push ruleset *scopes a set of rules according to some criteria*. For example, + some rules may only be applied for messages from a particular sender, + a particular room, or by default. The push ruleset contains the entire set + of scopes and rules. + +Client behaviour +---------------- + +Clients MUST configure a Pusher before they will receive push notifications. +There is a single API endpoint for this, as described below. + +{{pusher_http_api}} + +.. _pushers: `def:pushers`_ + +Push Rules +~~~~~~~~~~ +A push rule is a single rule that states under what *conditions* an event should +be passed onto a push gateway and *how* the notification should be presented. +There are different "kinds" of push rules and each rule has an associated +priority. Every push rule MUST have a ``kind`` and ``rule_id``. The ``rule_id`` +is a unique string within the kind of rule and its' scope: ``rule_ids`` do not +need to be unique between rules of the same kind on different devices. Rules may +have extra keys depending on the value of ``kind``.The different kinds of rule +in descending order of priority are: + +Override Rules ``override`` + The highest priority rules are user-configured overrides. +Content-specific Rules ``content`` + These configure behaviour for (unencrypted) messages that match certain + patterns. Content rules take one parameter: ``pattern``, that gives the glob + pattern to match against. This is treated in the same way as ``pattern`` for + ``event_match``. +Room-specific Rules ``room`` + These rules change the behaviour of all messages for a given room. The + ``rule_id`` of a room rule is always the ID of the room that it affects. +Sender-specific rules ``sender`` + These rules configure notification behaviour for messages from a specific + Matrix user ID. The ``rule_id`` of Sender rules is always the Matrix user + ID of the user whose messages they'd apply to. +Underride rules ``underride`` + These are identical to ``override`` rules, but have a lower priority than + ``content``, ``room`` and ``sender`` rules. + +Push rules may be either global or device-specific. Device specific rules only +affect delivery of notifications via pushers with a matching ``profile_tag``. +All device-specific rules have a higher priority than global rules. This means +that the full list of rule kinds, in descending priority order, is as follows: + + * Device-specific Override + * Device-specific Content + * Device-specific Room + * Device-specific Sender + * Device-specific Underride + * Global Override + * Global Content + * Global Room + * Global Sender + * Global Underride + +Rules with the same ``kind`` can specify an ordering priority. This determines +which rule is selected in the event of multiple matches. For example, a rule +matching "tea" and a separate rule matching "time" would both match the sentence +"It's time for tea". The ordering of the rules would then resolve the tiebreak +to determine which rule is executed. Only ``actions`` for highest priority rule +will be sent to the Push Gateway. + +Each rule can be enabled or disabled. Disabled rules never match. If no rules +match an event, the homeserver MUST NOT notify the Push Gateway for that event. +Homeservers MUST NOT notify the Push Gateway for events that the user has sent +themselves. + +Actions ++++++++ +All rules have an associated list of ``actions``. An action affects if and how a +notification is delivered for a matching event. The following actions are defined: + +``notify`` + This causes each matching event to generate a notification. +``dont_notify`` + This prevents each matching event from generating a notification +``coalesce`` + This enables notifications for matching events but activates homeserver + specific behaviour to intelligently coalesce multiple events into a single + notification. Not all homeservers may support this. Those that do not support + it should treat it as the ``notify`` action. +``set_tweak`` + Sets an entry in the ``tweaks`` dictionary key that is sent in the notification + request to the Push Gateway. This takes the form of a dictionary with a + ``set_tweak`` key whose value is the name of the tweak to set. It may also + have a ``value`` key which is the value to which it should be set. + +Actions that have no parameters are represented as a string. Otherwise, they are +represented as a dictionary with a key equal to their name and other keys as +their parameters, e.g. ``{ "set_tweak": "sound", "value": "default" }`` + +Tweaks +^^^^^^ +The ``set_tweak`` action is used to add an entry to the 'tweaks' dictionary +that is sent in the notification request to the Push Gateway. The following +tweaks are defined: + +``sound`` + A string representing the sound to be played when this notification arrives. + A value of ``default`` means to play a default sound. +``highlight`` + A boolean representing whether or not this message should be highlighted in + the UI. This will normally take the form of presenting the message in a + different colour and/or style. The UI might also be adjusted to draw + particular attention to the room in which the event occurred. The ``value`` + may be omitted from the highlight tweak, in which case it should default to + ``true``. + +Tweaks are passed transparently through the homeserver so client applications +and Push Gateways may agree on additional tweaks. For example, a tweak may be +added to specify how to flash the notification light on a mobile device. + +Predefined Rules +++++++++++++++++ +Homeservers can specify "server-default rules" which operate at a lower priority +than "user-defined rules". The ``rule_id`` for all server-default rules MUST +start with a dot (".") to identify them as "server-default". The following +server-default rules are specified: + +``.m.rule.contains_user_name`` + Matches any message whose content is unencrypted and contains the local part + of the user's Matrix ID, separated by word boundaries. + + Definition (as a ``content`` rule):: + + { + "rule_id": ".m.rule.contains_user_name" + "pattern": "[the local part of the user's Matrix ID]", + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + } + ], + } + +``.m.rule.contains_display_name`` + Matches any message whose content is unencrypted and contains the user's + current display name in the room in which it was sent. + + Definition (this rule can only be an ``override`` or ``underride`` rule):: + + { + "rule_id": ".m.rule.contains_display_name" + "conditions": [ + { + "kind": "contains_display_name" + } + ], + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + } + ], + } + +``.m.rule.room_one_to_one`` + Matches any message sent in a room with exactly two members. + + Definition (this rule can only be an ``override`` or ``underride`` rule):: + + { + "rule_id": ".m.rule.room_two_members" + "conditions": [ + { + "is": "2", + "kind": "room_member_count" + } + ], + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + } + ], + } + +``.m.rule.suppress_notices`` + Matches messages with a ``msgtype`` of ``notice``. This should be an + ``override`` rule so that it takes priority over ``content`` / ``sender`` / + ``room`` rules. + + Definition:: + + { + 'rule_id': '.m.rule.suppress_notices', + 'conditions': [ + { + 'kind': 'event_match', + 'key': 'content.msgtype', + 'pattern': 'm.notice', + } + ], + 'actions': [ + 'dont-notify', + ] + } + +``.m.rule.fallback`` + Matches any message. Used to define the behaviour of messages that match no + other rules. If homeservers define this it should be the lowest priority + ``underride`` rule. + + Definition:: + + { + "rule_id": ".m.rule.fallback" + "conditions": [], + "actions": [ + "notify" + ], + } + + + +Conditions +++++++++++ + +Override, Underride and Default Rules MAY have a list of 'conditions'. +All conditions must hold true for an event in order to apply the ``action`` for +the event. A rule with no conditions always matches. Room, Sender, User and +Content rules do not have conditions in the same way, but instead have +predefined conditions. These conditions can be configured using the parameters +outlined below. In the cases of room and sender rules, the ``rule_id`` of the +rule determines its behaviour. The following conditions are defined: + +``event_match`` + This is a glob pattern match on a field of the event. Parameters: + * ``key``: The dot-separated field of the event to match, e.g. ``content.body`` + * ``pattern``: The glob-style pattern to match against. Patterns with no + special glob characters should be treated as having asterisks + prepended and appended when testing the condition. + +``profile_tag`` + Matches the ``profile_tag`` of the device that the notification would be + delivered to. Parameters: + + * ``profile_tag``: The profile_tag to match with. + +``contains_display_name`` + This matches unencrypted messages where ``content.body`` contains the owner's + display name in that room. This is a separate rule because display names may + change and as such it would be hard to maintain a rule that matched the user's + display name. This condition has no parameters. + +``room_member_count`` + This matches the current number of members in the room. Parameters: + * ``is``: A decimal integer optionally prefixed by one of, ``==``, ``<``, + ``>``, ``>=`` or ``<=``. A prefix of ``<`` matches rooms where the member + count is strictly less than the given number and so forth. If no prefix is + present, this parameter defaults to ``==``. + +Push Rules: API +~~~~~~~~~~~~~~~ + +Clients can retrieve, add, modify and remove push rules globally or per-device +using the APIs below. + +{{pushrules_http_api}} + +Examples +++++++++ + +To create a rule that suppresses notifications for the room with ID +``!dj234r78wl45Gh4D:matrix.org``:: + + curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456" -d \ + '{ + "actions" : ["dont_notify"] + }' + +To suppress notifications for the user ``@spambot:matrix.org``:: + + curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456" -d \ + '{ + "actions" : ["dont_notify"] + }' + +To always notify for messages that contain the work 'cake' and set a specific +sound (with a rule_id of ``SSByZWFsbHkgbGlrZSBjYWtl``):: + + curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456" -d \ + '{ + "pattern": "cake", + "actions" : ["notify", {"set_sound":"cakealarm.wav"}] + }' + +To add a rule suppressing notifications for messages starting with 'cake' but +ending with 'lie', superseding the previous rule:: + + curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl" -d \ + '{ + "pattern": "cake*lie", + "actions" : ["notify"] + }' + +To add a custom sound for notifications messages containing the word 'beer' in +any rooms with 10 members or fewer (with greater importance than the room, +sender and content rules):: + + curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456" -d \ + '{ + "conditions": [ + {"kind": "event_match", "key": "content.body", "pattern": "beer" }, + {"kind": "room_member_count", "is": "<=10"} + ], + "actions" : [ + "notify", + {"set_sound":"beeroclock.wav"} + ] + }' + +Server behaviour +---------------- + +This describes the format used by "HTTP" pushers to send notifications of +events to Push Gateways. If the endpoint returns an HTTP error code, the +homeserver SHOULD retry for a reasonable amount of time using exponential-backoff. + +{{push_notifier_http_api}} + +Push Gateway behaviour +---------------------- + +Recommendations for APNS +~~~~~~~~~~~~~~~~~~~~~~~~ +The exact format for sending APNS notifications is flexible and up to the +client app and its' push gateway to agree on. As APNS requires that the sender +has a private key owned by the app developer, each app must have its own push +gateway. It is recommended that: + + * The APNS token be base64 encoded and used as the pushkey. + * A different app_id be used for apps on the production and sandbox + APS environments. + * APNS push gateways do not attempt to wait for errors from the APNS + gateway before returning and instead to store failures and return + 'rejected' responses next time that pushkey is used. + +Security considerations +----------------------- + +Clients specify the Push Gateway URL to use to send event notifications to. This +URL should be over HTTPS and *never* over HTTP. + +As push notifications will pass through a Push Provider, message content +shouldn't be sent in the push itself where possible. Instead, Push Gateways +should send a "sync" command to instruct the client to get new events from the +homeserver directly. + diff --git a/specification/modules/push_cs_api.rst b/specification/modules/push_cs_api.rst deleted file mode 100644 index c6301926..00000000 --- a/specification/modules/push_cs_api.rst +++ /dev/null @@ -1,420 +0,0 @@ -Pushers HTTP API ----------------- - -To receive any notification pokes at all, it is necessary to configure a -'pusher' on the Home Server that you wish to receive notifications from. There -is a single API endpoint for this:: - - POST $PREFIX/pushers/set - -This takes a JSON object with the following keys: - -pushkey - This is a unique identifier for this pusher. The value you should use for this - is the routing or destination address information for the notification, for - example, the APNS token for APNS or the Registration ID for GCM. If your - notification client has no such concept, use any unique identifier. Max length, - 512 bytes. -kind - The kind of pusher to configure. 'http' makes a pusher that sends HTTP pokes. - null deletes the pusher. -profile_tag - This is a string that determines what set of device rules will be matched when - evaluating push rules for this pusher. It is an arbitrary string. Multiple - devices maybe use the same profile_tag. It is advised that when an app's - data is copied or restored to a different device, this value remain the same. - Client apps should offer ways to change the profile_tag, optionally copying - rules from the old profile tag. Max length, 32 bytes. -app_id - appId is a reverse-DNS style identifier for the application. It is recommended - that this end with the platform, such that different platform versions get - different app identifiers. Max length, 64 chars. -app_display_name - A string that will allow the user to identify what application owns this - pusher. -device_display_name - A string that will allow the user to identify what device owns this pusher. -lang - The preferred language for receiving notifications (eg, 'en' or 'en-US') -data - A dictionary of information for the pusher implementation itself. For HTTP - pushers, this must contain a 'url' key which is a string of the URL that - should be used to send notifications. -append - If this is set to boolean true, the Home Server should add another pusher - with the given pushkey and App ID in addition to any others with different - user IDs. Otherwise, the Home Server must remove any other pushers with the - same App ID and pushkey for different users. The default is false. - -If the pusher was created successfully, a JSON dictionary is returned (which may -be empty). - - -Push Rules ----------- -Home Servers have an interface to configure what events trigger notifications. -This behaviour is configured through 'Push Rules'. Push Rules come in a variety -of different kinds and each kind of rule has an associated priority. The -different kinds of rule, in descending order of priority, are: - -Override Rules - The highest priority rules are user-configured overrides. -Content Rules - These configure behaviour for (unencrypted) messages that match certain - patterns. Content rules take one parameter, 'pattern', that gives the pattern - to match against. This is treated in the same way as pattern for event_match - conditions, below. -Room Rules - These change the behaviour of all messages to a given room. The rule_id of a - room rule is always the ID of the room that it affects. -Sender - These rules configure notification behaviour for messages from a specific, - named Matrix user ID. The rule_id of Sender rules is always the Matrix user - ID of the user whose messages they'd apply to. -Underride - These are identical to override rules, but have a lower priority than content, - room and sender rules. - -In addition, each kind of rule may be either global or device-specific. Device -specific rules only affect delivery of notifications via pushers with a matching -profile_tag. All device-specific rules are higher priority than all global -rules. Thusly, the full list of rule kinds, in descending priority order, is as -follows: - - * Device-specific Override - * Device-specific Content - * Device-specific Room - * Device-specific Sender - * Device-specific Underride - * Global Override - * Global Content - * Global Room - * Global Sender - * Global Underride - -For some kinds of rule, rules of the same kind also have an ordering with -respect to one another. The kinds that do not are room and sender rules where -the rules are mutually exclusive by definition and therefore an ordering would -be redundant. Actions for the highest priority rule and only that rule apply -(for example, a set_tweak action in a lower priority rule will not apply if a -higher priority rule matches, even if that rule does not specify any tweaks). - -Rules also have an identifier, ``rule_id``, which is a string. The ``rule_id`` -is unique within the kind of rule and scope: ``rule_ids`` need not be unique -between rules of the same kind on different devices. A home server may also have -server default rules of each kind and in each scope. Server default rules are -lower priority than user-defined rules in each scope. Server default rules (and -only server default rules) begin with a dot ('.') character. In addition, all -rules may be enabled or disabled. Disabled rules never match. - -If no rules match an event, the Home Server should not notify for the message -(that is to say, the default action is "dont-notify"). Events that the user sent -themselves are never alerted for. - -Predefined Rules ----------------- -Matrix specifies the following rule IDs for server default rules. Home Servers -may define rules as follows with the given IDs. If Home Servers provide rules -with these IDs, their semantics should match those given below: - -.m.rule.contains_user_name - Matches any message whose content is unencrypted and contains the local part - of the user's Matrix ID, separated by word boundaries. - - Definition (as a content rule):: - - { - "rule_id": ".m.rule.contains_user_name" - "pattern": "[the local part of the user's Matrix ID]", - "actions": [ - "notify", - { - "set_tweak": "sound", - "value": "default" - } - ], - } - -.m.rule.contains_display_name - Matches any message whose content is unencrypted and contains the user's - current display name in the room in which it was sent. - - Definition (this rule can only be an override or underride rule):: - - { - "rule_id": ".m.rule.contains_display_name" - "conditions": [ - { - "kind": "contains_display_name" - } - ], - "actions": [ - "notify", - { - "set_tweak": "sound", - "value": "default" - } - ], - } - -.m.rule.room_one_to_one - Matches any message sent in a room with exactly two members. - - Definition (this rule can only be an override or underride rule):: - - { - "rule_id": ".m.rule.room_two_members" - "conditions": [ - { - "is": "2", - "kind": "room_member_count" - } - ], - "actions": [ - "notify", - { - "set_tweak": "sound", - "value": "default" - } - ], - } - -.m.rule.suppress_notices - Matches messages with 'msgtype' of 'notice'. This should be an override rule - such that, when enabled, it takes priority over content / sender / room rules. - - Definition:: - - { - 'rule_id': '.m.rule.suppress_notices', - 'conditions': [ - { - 'kind': 'event_match', - 'key': 'content.msgtype', - 'pattern': 'm.notice', - } - ], - 'actions': [ - 'dont-notify', - ] - } - -.m.rule.fallback - Matches any message. Used to define the behaviour of messages that match no - other rules. Therefore, if Home Servers define this, it should be the lowest - priority underride rule. - - Definition:: - - { - "rule_id": ".m.rule.fallback" - "conditions": [], - "actions": [ - "notify" - ], - } - -Push Rules: Actions: --------------------- -All rules have an associated list of 'actions'. An action affects if and how a -notification is delivered for a matching event. This standard defines the -following actions, although if Home servers wish to support more, they are free -to do so: - -notify - This causes each matching event to generate a notification. -dont_notify - Prevents this event from generating a notification -coalesce - This enables notifications for matching events but activates Home Server - specific behaviour to intelligently coalesce multiple events into a single - notification. Not all Home Servers may support this. Those that do not should - treat it as the 'notify' action. -set_tweak - Sets an entry in the 'tweaks' dictionary key that is sent in the notification - poke. This takes the form of a dictionary with a 'set_tweak' key whose value - is the name of the tweak to set. It may also have a 'value' key which is - the value to which it should be set. - -Actions that have no parameters are represented as a string. Otherwise, they are -represented as a dictionary with a key equal to their name and other keys as -their parameters, e.g. ``{ "set_tweak": "sound", "value": "default" }`` - -Push Rules: Actions: Tweaks ---------------------------- -The ``set_tweak`` key action is used to add an entry to the 'tweaks' dictionary -that is sent in the notification poke. The following tweaks are defined: - -sound - A sound to be played when this notification arrives. 'default' means to - play a default sound. -highlight - Whether or not this message should be highlighted in the UI. This will - normally take the form of presenting the message in a different colour and/or - weight. The UI might also be adjusted to draw particular attention to the room - in which the event occurred. The value may be omitted from the highlight - tweak, in which case it should be read as if it had a value of true. - -Tweaks are passed transparently through the Home Server so client applications -and push gateways may agree on additional tweaks, for example, how to flash the -notification light on a mobile device. - -If a kind of tweak that a client understands is not specified in an action, the -client may choose a sensible behaviour for the tweak. - -Push Rules: Conditions ----------------------- -Override, Underride and Default rules have a list of 'conditions'. All -conditions must hold true for an event in order for a rule to be applied to an -event. A rule with no conditions always matches. Matrix specifies the following -conditions, although if Home Servers wish to support others, they are free to -do so: - -event_match - This is a glob pattern match on a field of the event. Parameters: - * 'key': The dot-separated field of the event to match, e.g. content.body - * 'pattern': The glob-style pattern to match against. Patterns with no - special glob characters should be treated as having asterisks - prepended and appended when testing the condition. -profile_tag - Matches the profile_tag of the device that the notification would be - delivered to. Parameters: - - * 'profile_tag': The profile_tag to match with. -contains_display_name - This matches unencrypted messages where content.body contains the owner's - display name in that room. This is a separate rule because display names may - change and as such it would be hard to maintain a rule that matched the user's - display name. This condition has no parameters. -room_member_count - This matches the current number of members in the room. - * 'is': A decimal integer optionally prefixed by one of, '==', '<', '>', - '>=' or '<='. A prefix of '<' matches rooms where the member count is - strictly less than the given number and so forth. If no prefix is present, - this matches rooms where the member count is exactly equal to the given - number (i.e. the same as '=='). - -Room, Sender, User and Content rules do not have conditions in the same way, -but instead have predefined conditions, the behaviour of which can be configured -using parameters named as described above. In the cases of room and sender -rules, the rule_id of the rule determines its behaviour. - -Push Rules: API ---------------- -Rules live under a hierarchy in the REST API that resembles:: - - $PREFIX/pushrules/// - -The component parts are as follows: - -scope - Either 'global' or 'device/' to specify global rules or - device rules for the given profile_tag. -kind - The kind of rule, i.e. 'override', 'underride', 'sender', 'room', 'content'. -rule_id - The identifier for the rule. - -To add or change a rule, a client performs a PUT request to the appropriate URL. -When adding rules of a type that has an ordering, the client can add parameters -that define the priority of the rule: - -before - Use 'before' with a rule_id as its value to make the new rule the next-more - important rule with respect to the given rule. -after - This makes the new rule the next-less important rule relative to the given - rule. - -All requests to the push rules API also require an access_token as a query -parameter. - -The content of the PUT request is a JSON object with a list of actions under the -'actions' key and either conditions (under the 'conditions' key) or the -appropriate parameters for the rule (under the appropriate key name). - -Examples: - -To create a rule that suppresses notifications for the room with ID '!dj234r78wl45Gh4D:matrix.org':: - - curl -X PUT -H "Content-Type: application/json" -d '{ "actions" : ["dont_notify"] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456" - -To suppress notifications for the user '@spambot:matrix.org':: - - curl -X PUT -H "Content-Type: application/json" -d '{ "actions" : ["dont_notify"] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456" - -To always notify for messages that contain the work 'cake' and set a specific sound (with a rule_id of 'SSByZWFsbHkgbGlrZSBjYWtl'):: - - curl -X PUT -H "Content-Type: application/json" -d '{ "pattern": "cake", "actions" : ["notify", {"set_sound":"cakealarm.wav"}] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456" - -To add a rule suppressing notifications for messages starting with 'cake' but ending with 'lie', superseeding the previous rule:: - - curl -X PUT -H "Content-Type: application/json" -d '{ "pattern": "cake*lie", "actions" : ["notify"] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl" - -To add a custom sound for notifications messages containing the word 'beer' in any rooms with 10 members or fewer (with greater importance than the room, sender and content rules):: - - curl -X PUT -H "Content-Type: application/json" -d '{ "conditions": [{"kind": "event_match", "key": "content.body", "pattern": "beer" }, {"kind": "room_member_count", "is": "<=10"}], "actions" : ["notify", {"set_sound":"beeroclock.wav"}] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456 - - -To delete rules, a client would just make a DELETE request to the same URL:: - - curl -X DELETE "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%23spam%3Amatrix.org?access_token=123456" - - -Retrieving the current ruleset can be done either by fetching individual rules -using the scheme as specified above. This returns the rule in the same format as -would be given in the PUT API with the addition of a rule_id:: - - curl "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%23spam%3Amatrix.org?access_token=123456" - -Returns:: - - { - "actions": [ - "dont_notify" - ], - "rule_id": "#spam:matrix.org", - "enabled": true - } - -Clients can also fetch broader sets of rules by removing path components. -Requesting the root level returns a structure as follows:: - - { - "device": { - "exampledevice": { - "content": [], - "override": [], - "room": [ - { - "actions": [ - "dont_notify" - ], - "rule_id": "#spam:matrix.org", - "enabled", true - } - ], - "sender": [], - "underride": [] - } - }, - "global": { - "content": [], - "override": [], - "room": [], - "sender": [], - "underride": [] - } - } - -Adding patch components to the request drills down into this structure to filter -to only the requested set of rules. - -Enabling and Disabling Rules ----------------------------- -Rules can be enabled or disabled with a PUT operation to the 'enabled' component -beneath the rule's URI with a content of 'true' or 'false':: - - curl -X PUT -H "Content-Type: application/json" -d 'false' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/sender/%40spambot%3Amatrix.org/enabled?access_token=123456" - - diff --git a/specification/modules/push_overview.rst b/specification/modules/push_overview.rst deleted file mode 100644 index 46028283..00000000 --- a/specification/modules/push_overview.rst +++ /dev/null @@ -1,80 +0,0 @@ -Push Notifications -================== - -.. _module:push: - -Overview --------- - -:: - - +--------------------+ +-------------------+ - Matrix HTTP | | | | - Notification Protocol | App Developer | | Device Vendor | - | | | | - +-------------------+ | +----------------+ | | +---------------+ | - | | | | | | | | | | - | Matrix Home Server+-----> Push Gateway | +---> Push Provider | | - | | | | | | | | | | - +-^-----------------+ | +----------------+ | | +----+----------+ | - | | | | | | - Matrix | | | | | | - Client/Server API + | | | | | - | | +--------------------+ +-------------------+ - | +--+-+ | - | | <------------------------------------------+ - +---+ | - | | Provider Push Protocol - +----+ - - Mobile Device or Client - - -Matrix supports push notifications as a first class citizen. Home Servers send -notifications of user events to user-configured HTTP endpoints. User may also -configure a number of rules that determine what events generate notifications. -These are all stored and managed by the users home server such that settings can -be reused between client apps as appropriate. - -The above diagram shows the flow of push notifications being sent to a handset -where push notifications are submitted via the handset vendor, such as Apple's -APNS or Google's GCM. This happens as follows: - - 1. The client app signs in to a Matrix Home Server - 2. The client app registers with its vendor's Push Notification provider and - obtains a routing token of some kind. - 3. The mobile app, uses the Matrix client/server API to add a 'pusher', - providing the URL of a specific Push Gateway which is configured for that - application. It also provides the routing token it has acquired from the - Push Notification Provider. - 4. The Home Server starts sending notification HTTP requests to the Push - Gateway using the supplied URL. The Push Gateway relays this notification to - the Push Notification Provider, passing the routing token along with any - necessary private credentials the provider requires to send push - notifications. - 5. The Push Notification provider sends the notification to the device. - -Nomenclature ------------- - -Pusher - A 'pusher' is an activity in the Home Server that manages the sending - of HTTP notifications for a single device of a single user. - -Push Rules - A push rule is a single rule, configured by a matrix user, that gives - instructions to the Home Server about whether an event should be notified - about and how given a set of conditions. Matrix clients allow the user to - configure these. They create and view them via the Client to Server REST API. - -Push Gateway - A push gateway is a server that receives HTTP event notifications from Home - Servers and passes them on to a different protocol such as APNS for iOS - devices or GCM for Android devices. Matrix.org provides a reference push - gateway, 'sygnal'. A client app tells a Home Server what push gateway - to send notifications to when it sets up a pusher. - -For information on the client-server API for setting pushers and push rules, see -the Client Server API section. For more information on the format of HTTP -notifications, see the HTTP Notification Protocol section. - diff --git a/specification/modules/push_push_gw_api.rst b/specification/modules/push_push_gw_api.rst deleted file mode 100644 index c970e93e..00000000 --- a/specification/modules/push_push_gw_api.rst +++ /dev/null @@ -1,144 +0,0 @@ -HTTP Notification Protocol --------------------------- - -This describes the format used by "HTTP" pushers to send notifications of -events. - -Notifications are sent as HTTP POST requests to the URL configured when the -pusher is created, but Matrix strongly recommends that the path should be:: - - /_matrix/push/v1/notify - -The body of the POST request is a JSON dictionary. The format -is as follows:: - - { - "notification": { - "id": "$3957tyerfgewrf384", - "room_id": "!slw48wfj34rtnrf:example.com", - "type": "m.room.message", - "sender": "@exampleuser:matrix.org", - "sender_display_name": "Major Tom", - "room_name": "Mission Control", - "room_alias": "#exampleroom:matrix.org", - "prio": "high", - "content": { - "msgtype": "m.text", - "body": "I'm floating in a most peculiar way." - } - }, - "counts": { - "unread" : 2, - "missed_calls": 1 - } - "devices": [ - { - "app_id": "org.matrix.matrixConsole.ios", - "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", - "pushkey_ts": 12345678, - "data" : { - }, - "tweaks": { - "sound": "bing" - } - } - ] - } - } - -The contents of this dictionary are defined as follows: - -id - An identifier for this notification that may be used to detect duplicate - notification requests. This is not necessarily the ID of the event that - triggered the notification. -room_id - The ID of the room in which this event occurred. -type - The type of the event as in the event's 'type' field. -sender - The sender of the event as in the corresponding event field. -sender_display_name - The current display name of the sender in the room in which the event - occurred. -room_name - The name of the room in which the event occurred. -room_alias - An alias to display for the room in which the event occurred. -prio - The priority of the notification. Acceptable values are 'high' or 'low. If - omitted, 'high' is assumed. This may be used by push gateways to deliver less - time-sensitive notifications in a way that will preserve battery power on - mobile devices. -content - The 'content' field from the event, if present. If the event had no content - field, this field is omitted. -counts - This is a dictionary of the current number of unacknowledged communications - for the recipient user. Counts whose value is zero are omitted. -unread - The number of unread messages a user has across all of the rooms they are a - member of. -missed_calls - The number of unacknowledged missed calls a user has across all rooms of - which they are a member. -device - This is an array of devices that the notification should be sent to. -app_id - The app_id given when the pusher was created. -pushkey - The pushkey given when the pusher was created. -pushkey_ts - The unix timestamp (in seconds) when the pushkey was last updated. -data - A dictionary of additional pusher-specific data. For 'http' pushers, this is - the data dictionary passed in at pusher creation minus the 'url' key. -tweaks - A dictionary of customisations made to the way this notification is to be - presented. These are added by push rules. -sound - Sets the sound file that should be played. 'default' means that a default - sound should be played. - -And additional key is defined but only present on member events: - -user_is_target - This is true if the user receiving the notification is the subject of a member - event (i.e. the state_key of the member event is equal to the user's Matrix - ID). - -The recipient of an HTTP notification should respond with an HTTP 2xx response -when the notification has been processed. If the endpoint returns an HTTP error -code, the Home Server should retry for a reasonable amount of time with a -reasonable back-off scheme. - -The endpoint should return a JSON dictionary as follows:: - - { - "rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ] - } - -Whose keys are: - -rejected - A list of all pushkeys given in the notification request that are not valid. - These could have been rejected by an upstream gateway because they have - expired or have never been valid. Home Servers must cease sending notification - requests for these pushkeys and remove the associated pushers. It may not - necessarily be the notification in the request that failed: it could be that - a previous notification to the same pushkey failed. - -Push: Recommendations for APNS ------------------------------- -For sending APNS notifications, the exact format is flexible and up to the -client app and its push gateway to agree on (since APNS requires that the sender -have a private key owned by the app developer, each app must have its own push -gateway). However, Matrix strongly recommends: - - * That the APNS token be base64 encoded and used as the pushkey. - * That a different app_id be used for apps on the production and sandbox - APS environments. - * That APNS push gateways do not attempt to wait for errors from the APNS - gateway before returning and instead to store failures and return - 'rejected' responses next time that pushkey is used. - diff --git a/specification/targets.yaml b/specification/targets.yaml index d77bf8b5..5786ea6b 100644 --- a/specification/targets.yaml +++ b/specification/targets.yaml @@ -22,11 +22,9 @@ groups: # reusable blobs of files when prefixed with 'group:' - modules/content_repo.rst - modules/end_to_end_encryption.rst - modules/history_visibility.rst - - modules/push_overview.rst - # relative depth - - { 1: [modules/push_cs_api.rst , modules/push_push_gw_api.rst] } + - modules/push.rst -title_styles: ["=", "-", "~", "+", "^"] +title_styles: ["=", "-", "~", "+", "^", "`"] # The templating system doesn't know the right title style to use when generating # RST. These symbols are 'relative' to say "make a sub-title" (-1), "make a title diff --git a/templating/matrix_templates/templates/http-api.tmpl b/templating/matrix_templates/templates/http-api.tmpl index 472c9d7a..d1633513 100644 --- a/templating/matrix_templates/templates/http-api.tmpl +++ b/templating/matrix_templates/templates/http-api.tmpl @@ -13,17 +13,17 @@ Request format: -================== ================= =========================================== - Parameter Value Description -================== ================= =========================================== +=================================== ================= =========================================== + Parameter Value Description +=================================== ================= =========================================== {% for loc in endpoint.req_param_by_loc -%} *{{loc}} parameters* --------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------- {% for param in endpoint.req_param_by_loc[loc] -%} -{{param.key}}{{param.type|indent(19-param.key|length)}}{{param.desc|indent(18-param.type|length)|wrap(43)|indent_block(37)}} +{{param.key}}{{param.type|indent(36-param.key|length)}}{{param.desc|indent(18-param.type|length)|wrap(43)|indent_block(54)}} {% endfor -%} {% endfor -%} -================== ================= =========================================== +=================================== ================= =========================================== {% if endpoint.res_tables|length > 0 -%} Response format: @@ -31,18 +31,18 @@ Response format: {% for table in endpoint.res_tables -%} {{"``"+table.title+"``" if table.title else "" }} -=================== ================= ========================================== - Param Type Description -=================== ================= ========================================== +======================= ========================= ========================================== + Param Type Description +======================= ========================= ========================================== {% for row in table.rows -%} {# -#} {# Row type needs to prepend spaces to line up with the type column (20 ch) -#} {# Desc needs to prepend the required text (maybe) and prepend spaces too -#} {# It also needs to then wrap inside the desc col (42 ch width) -#} {# -#} -{{row.key}}{{row.type|indent(20-row.key|length)}}{{row.desc|wrap(42,row.req_str | indent(18 - (row.type|length))) |indent_block(38)}} +{{row.key}}{{row.type|indent(24-row.key|length)}}{{row.desc|wrap(42,row.req_str | indent(26 - (row.type|length))) |indent_block(50)}} {% endfor -%} -=================== ================= ========================================== +======================= ========================= ========================================== {% endfor %} {% endif -%} diff --git a/templating/matrix_templates/units.py b/templating/matrix_templates/units.py index eebfba84..bbfa8423 100644 --- a/templating/matrix_templates/units.py +++ b/templating/matrix_templates/units.py @@ -61,6 +61,13 @@ def get_json_schema_object_fields(obj, enforce_title=False): if pretty_key: props[pretty_key] = props[key_name] del props[key_name] + if not props and not parents: + # Sometimes you just want to specify that a thing is an object without + # doing all the keys. Allow people to do that if they set a 'title'. + if obj.get("title"): + parents = [{ + "$ref": obj.get("title") + }] if not props and not parents: raise Exception( "Object %s has no properties or parents." % obj @@ -86,7 +93,10 @@ def get_json_schema_object_fields(obj, enforce_title=False): props[key_name]["additionalProperties"], enforce_title=True ) - value_type = "{string: %s}" % nested_object[0]["title"] + key = props[key_name]["additionalProperties"].get( + "x-pattern", "string" + ) + value_type = "{%s: %s}" % (key, nested_object[0]["title"]) if not nested_object[0].get("no-table"): tables += nested_object else: @@ -181,6 +191,11 @@ class MatrixUnits(Units): val_type = refType # TODO: Resolve to human-readable. if not val_type and schemaFmt: val_type = schemaFmt + # handle top-level strings/bools + if not val_type and Units.prop(param, "schema/type") == "string": + val_type = "string" + if not val_type and Units.prop(param, "schema/type") == "boolean": + val_type = "boolean" if val_type: endpoint["req_params"].append({ "key": param["name"], @@ -207,13 +222,48 @@ class MatrixUnits(Units): ) # loop top-level json keys json_body = Units.prop(param, "schema/properties") + required_params = [] + if Units.prop(param, "schema/required"): + required_params = Units.prop(param, "schema/required") for key in json_body: + req_obj = json_body[key] + pdesc = req_obj["description"] + if key in required_params: + pdesc = "**Required.** " + pdesc + + is_array = req_obj["type"] == "array" + is_array_of_objects = ( + is_array and req_obj["items"]["type"] == "object" + ) endpoint["req_params"].append({ "key": key, "loc": "JSON body", - "type": json_body[key]["type"], - "desc": json_body[key]["description"] + "type": ( + req_obj["type"] if not is_array else + "array[%s]" % req_obj["items"]["type"] + ), + "desc": pdesc }) + if not is_array_of_objects and req_obj["type"] == "array": + continue + # Put in request.dot.notation for nested keys + if req_obj["type"] in ["object", "array"]: + if is_array_of_objects: + req_obj = req_obj["items"] + + req_tables = get_json_schema_object_fields(req_obj) + key_sep = "[0]." if is_array else "." + for table in req_tables: + if table.get("no-table"): + continue + for row in table["rows"]: + endpoint["req_params"].append({ + "key": key + key_sep + row["key"], + "loc": "JSON body", + "type": row["type"], + "desc": row["req_str"] + row["desc"] + }) + # endfor[param] for row in endpoint["req_params"]: self.log("Request parameter: %s" % row)