Merge branch 'master' into daniel/threepidinvites-2

Conflicts:
	specification/targets.yaml
pull/977/head
Daniel Wagner-Hall 9 years ago
commit 2502ca7ac6

@ -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)

@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": ["event_match", "profile_tag", "contains_display_name", "room_member_count"]
}
}
}

@ -0,0 +1,20 @@
{
"type": "object",
"properties": {
"default": {
"type": "boolean"
},
"enabled": {
"type": "boolean"
},
"rule_id": {
"type": "string"
},
"actions": {
"items": {
"type": ["object", "string"]
},
"type": "array"
}
}
}

@ -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"
}
}
}

@ -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

@ -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"

@ -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/<profile_tag>`` 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/<profile_tag>`` 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/<profile_tag>`` 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/<profile_tag>`` 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: <body>
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

@ -39,6 +39,16 @@ paths:
description: The maximum time in milliseconds to wait for an event.
required: false
x-example: "35000"
- in: query
type: string
name: archived
description: |-
Whether to include rooms that the user has left. If absent then
only rooms that the user has been invited to or has joined are
included. If set to "true" then rooms that the user has left are
included as well.
required: false
x-example: "true"
responses:
200:
description: "The events received, which may be none."

@ -86,7 +86,7 @@ func gitFetchAndMerge(path string) error {
if err := runGitCommand(path, []string{"fetch"}); err != nil {
return err
}
return runGitCommand(path, []string{"merge"})
return runGitCommand(path, []string{"merge", "origin/master"})
}
func runGitCommand(path string, args []string) error {

@ -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.

@ -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/<scope>/<kind>/<rule_id>
The component parts are as follows:
scope
Either 'global' or 'device/<profile_tag>' 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"

@ -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.

@ -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.

@ -22,12 +22,10 @@ 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.rst
- modules/third_party_invites.rst
- modules/push_overview.rst
# relative depth
- { 1: [modules/push_cs_api.rst , modules/push_push_gw_api.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

@ -1,16 +0,0 @@
# Site settings
title: Matrix
email: webmaster@matrix.org
description: > # this means to ignore newlines until "baseurl:"
Matrix.org documentation
baseurl: "/docs/guides" # the subpath of your site, e.g. /blog/
url: "http://matrix.org" # the base hostname & protocol for your site
twitter_username: matrixdotorg
github_username: matrix-org
# Build settings
markdown: kramdown
#defaults:
#permalink: /:categories/:title.html # can use this when/if we use jekyll for all docs
permalink: :title.html

@ -1,25 +0,0 @@
<div id="footer"><div id="footerContent">&copy 2014-2015 Matrix.org</div></div>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-54779209-1', 'auto');
ga('send', 'pageview');
</script>
<script type='text/javascript'>
/* <![CDATA[ */
var tocplus = {"smooth_scroll":"1"};
/* ]]> */
</script>
<script type='text/javascript' src='https://matrix.org/blog/wp-content/plugins/table-of-contents-plus/front.min.js?ver=1509'></script>
<script type='text/javascript' src='https://matrix.org/blog/wp-content/themes/Divi-child/js/jquery.fitvids.js?ver=2.2'></script>
<script type='text/javascript' src='https://matrix.org/blog/wp-content/themes/Divi-child/js/waypoints.min.js?ver=2.2'></script>
<script type='text/javascript' src='https://matrix.org/blog/wp-content/themes/Divi-child/js/jquery.magnific-popup.js?ver=2.2'></script>
<script type='text/javascript'>
/* <![CDATA[ */
var et_custom = {"ajaxurl":"https:\/\/matrix.org\/blog\/wp-admin\/admin-ajax.php","images_uri":"https:\/\/matrix.org\/blog\/wp-content\/themes\/Divi-child\/images","et_load_nonce":"dc61fa2f94","subscription_failed":"Please, check the fields below to make sure you entered the correct information.","fill":"Fill","field":"field","invalid":"Invalid email","captcha":"Captcha","prev":"Prev","next":"Next"};
/* ]]> */
</script>
<script type='text/javascript' src='https://matrix.org/blog/wp-content/themes/Divi-child/js/custom.js?ver=2.2'></script>

@ -1,31 +0,0 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}</title>
<meta name="description" content="{% if page.excerpt %}{{ page.excerpt | strip_html | strip_newlines | truncate: 160 }}{% else %}{{ site.description }}{% endif %}">
<link rel="stylesheet" href="http://matrix.org/site.css">
<link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
<link rel="stylesheet" href="{{ "/css/site_overrides.css" | prepend: site.baseurl }}">
<link rel="stylesheet" href="{{ "/css/basic.css" | prepend: site.baseurl }}">
<link rel="stylesheet" href="{{ "/css/nature.css" | prepend: site.baseurl }}">
<link rel='stylesheet' id='toc-screen-css' href='https://matrix.org/blog/wp-content/plugins/table-of-contents-plus/screen.min.css?ver=1509' type='text/css' media='all' />
<link rel='stylesheet' id='divi-fonts-css' href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,700italic,800italic,400,300,700,800&#038;subset=latin,latin-ext' type='text/css' media='all' />
<link rel='stylesheet' id='divi-style-css' href='https://matrix.org/blog/wp-content/themes/Divi-child/style.css?ver=2.2' type='text/css' media='all' />
<link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
<link rel="alternate" type="application/rss+xml" title="{{ site.title }}" href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}" />
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-54779209-1', 'auto');
ga('send', 'pageview');
</script>

@ -1,33 +0,0 @@
<header id="main-header" class="et_nav_text_color_dark et-fixed-header">
<div class="container clearfix" >
<a href="https://matrix.org/blog/">
<img src="https://matrix.org/blog/wp-content/uploads/2015/01/logo1.png" alt="Matrix.org" id="logo" />
</a>
<div id="et-top-navigation">
<nav id="top-menu-nav">
<ul id="top-menu" class="nav"><li id="menu-item-17" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-17"><a href="http://matrix.org/">Home</a></li>
<li id="menu-item-794" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-794"><a href="https://matrix.org/blog/try-matrix-now">Try Matrix Now!</a></li>
<li id="menu-item-348" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-348"><a href="/docs/howtos">HOWTOs</a></li>
<li id="menu-item-349" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-349"><a href="/docs/spec">Spec</a></li>
<li id="menu-item-350" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-350"><a href="/docs/api">APIs</a></li>
<li id="menu-item-351" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-351"><a href="/code">Code</a></li>
<li id="menu-item-352" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-352"><a href="/jira">JIRA</a></li>
<li id="menu-item-347" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-347"><a href="https://matrix.org/blog/faq/">FAQ</a></li>
<li id="menu-item-353" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-353"><a href="/blog">Blog</a></li>
</ul>
</nav>
<div id="et_top_search">
<span id="et_search_icon"></span>
<form role="search" method="get" class="et-search-form et-hidden" action="https://matrix.org/blog/">
<input type="search" class="et-search-field" placeholder="Search &hellip;" value="" name="s" title="Search for:" />
</form>
</div>
<div id="et_mobile_nav_menu">
<a href="#" class="mobile_nav closed">
<span class="select_page">Select Page</span>
<span class="mobile_menu_bar"></span>
</a>
</div>
</div> <!-- #et-top-navigation -->
</div> <!-- .container -->
</header> <!-- #main-header -->

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html>
<head>
{% include head.html %}
</head>
<body class="blog et_fixed_nav et_cover_background linux et_right_sidebar chrome">
<div class="page-content" id="page-container" style="padding-top: 78px;">
{% include nav.html %}
<div id="main-content">
<div class="wrapper" id="wrapper">
<div class="document_foo" id="document">
{{ content }}
</div>
</div>
<div class="push"></div>
</div>
{% include footer.html %}
</body>
</html>

@ -1,14 +0,0 @@
---
layout: default
---
<div class="post">
<header class="post-header">
<h1 class="post-title">{{ page.title }}</h1>
</header>
<article class="post-content">
{{ content }}
</article>
</div>

@ -1,6 +0,0 @@
---
layout: default
---
{{ content }}

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2011 Greg Thornton, http://xdissent.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -1,97 +0,0 @@
Overview
========
This plugin adds `ReStructuredText`_ support to `Jekyll`_ and `Octopress`_.
It renders ReST in posts and pages, and provides a custom directive to
support Octopress-compatible syntax highlighting.
Requirements
============
* Jekyll *or* Octopress >= 2.0
* Docutils
* Pygments
* `RbST`_
Installation
============
1. Install Docutils and Pygments.
The most convenient way is to use virtualenv_burrito:
::
$ curl -s https://raw.github.com/brainsik/virtualenv-burrito/master/virtualenv-burrito.sh | bash
$ source /Users/xdissent/.venvburrito/startup.sh
$ mkvirtualenv jekyll-rst
$ pip install docutils pygments
2. Install RbST.
If you use `bundler`_ with Octopress, add ``gem 'RbST'`` to
your ``Gemfile`` in the ``development`` group, then run
``bundle install``. Otherwise, ``gem install RbST``.
3. Install the plugin.
For Jekyll:
::
$ cd <jekyll-project-path>
$ git submodule add https://github.com/xdissent/jekyll-rst.git _plugins/jekyll-rst
For Octopress:
::
$ cd <octopress-project-path>
$ git submodule add https://github.com/xdissent/jekyll-rst.git plugins/jekyll-rst
4. Start blogging in ReStructuredText. Any file with the ``.rst`` extension
will be parsed as ReST and rendered into HTML.
.. note:: Be sure to activate the ``jekyll-rst`` virtualenv before generating
the site by issuing a ``workon jekyll-rst``. I suggest you follow `Harry
Marr's advice`_ and create a ``.venv`` file that will automatically
activate the ``jekyll-rst`` virtualenv when you ``cd`` into your project.
Source Code Highlighting
========================
A ``code-block`` ReST directive is registered and aliased as ``sourcecode``.
It adds syntax highlighting to code blocks in your documents::
.. code-block:: ruby
# Output "I love ReST"
say = "I love ReST"
puts say
Optional arguments exist to supply a caption, link, and link title::
.. code-block:: console
:caption: Read Hacker News on a budget
:url: http://news.ycombinator.com
:title: Hacker News
$ curl http://news.ycombinator.com | less
Octopress already includes style sheets for syntax highlighting, but you'll
need to generate one yourself if using Jekyll::
$ pygmentize -S default -f html > css/pygments.css
Octopress Tips
==============
* Use ``.. more`` in your ReST documents to indicate where Octopress's
``excerpt`` tag should split your content for summary views.
.. _ReStructuredText: http://docutils.sourceforge.net/rst.html
.. _Jekyll: http://jekyllrb.com/
.. _Octopress: http://octopress.com/
.. _RbST: http://rubygems.org/gems/RbST
.. _bundler: http://gembundler.com/
.. _Harry Marr's advice: http://hmarr.com/2010/jan/19/making-virtualenv-play-nice-with-git/

@ -1,30 +0,0 @@
require 'rbst'
module Jekyll
class RestConverter < Converter
safe true
priority :low
def matches(ext)
ext =~ /rst/i
end
def output_ext(ext)
".html"
end
def convert(content)
RbST.executables = {:html => "#{File.expand_path(File.dirname(__FILE__))}/rst2html.py"}
RbST.new(content).to_html(:part => :fragment, :initial_header_level => 2)
end
end
module Filters
def restify(input)
site = @context.registers[:site]
converter = site.getConverterImpl(Jekyll::RestConverter)
converter.convert(input)
end
end
end

@ -1,97 +0,0 @@
# Define a new directive `code-block` (aliased as `sourcecode`) that uses the
# `pygments` source highlighter to render code in color.
#
# Incorporates code from the `Pygments`_ documentation for `Using Pygments in
# ReST documents`_ and `Octopress`_.
#
# .. _Pygments: http://pygments.org/
# .. _Using Pygments in ReST documents: http://pygments.org/docs/rstdirective/
# .. _Octopress: http://octopress.org/
import re
import os
import md5
import __main__
# Absolute path to pygments cache dir
PYGMENTS_CACHE_DIR = os.path.abspath(os.path.join(os.path.dirname(__main__.__file__), '../../.pygments-cache'))
# Ensure cache dir exists
if not os.path.exists(PYGMENTS_CACHE_DIR):
os.makedirs(PYGMENTS_CACHE_DIR)
from pygments.formatters import HtmlFormatter
from docutils import nodes
from docutils.parsers.rst import directives, Directive
from pygments import highlight
from pygments.lexers import get_lexer_by_name, TextLexer
class Pygments(Directive):
""" Source code syntax hightlighting.
"""
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
string_opts = ['title', 'url', 'caption']
option_spec = dict([(key, directives.unchanged) for key in string_opts])
has_content = True
def run(self):
self.assert_has_content()
try:
lexer_name = self.arguments[0]
lexer = get_lexer_by_name(lexer_name)
except ValueError:
# no lexer found - use the text one instead of an exception
lexer_name = 'text'
lexer = TextLexer()
formatter = HtmlFormatter()
# Construct cache filename
cache_file = None
content_text = u'\n'.join(self.content)
cache_file_name = '%s-%s.html' % (lexer_name, md5.new(content_text).hexdigest())
cached_path = os.path.join(PYGMENTS_CACHE_DIR, cache_file_name)
# Look for cached version, otherwise parse
if os.path.exists(cached_path):
cache_file = open(cached_path, 'r')
parsed = cache_file.read()
else:
parsed = highlight(content_text, lexer, formatter)
# Strip pre tag and everything outside it
pres = re.compile("<pre>(.+)<\/pre>", re.S)
stripped = pres.search(parsed).group(1)
# Create tabular code with line numbers
table = '<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers">'
lined = ''
for idx, line in enumerate(stripped.splitlines(True)):
table += '<span class="line-number">%d</span>\n' % (idx + 1)
lined += '<span class="line">%s</span>' % line
table += '</pre></td><td class="code"><pre><code class="%s">%s</code></pre></td></tr></table></div>' % (lexer_name, lined)
# Add wrapper with optional caption and link
code = '<figure class="code">'
if self.options:
caption = ('<span>%s</span>' % self.options['caption']) if 'caption' in self.options else ''
title = self.options['title'] if 'title' in self.options else 'link'
link = ('<a href="%s">%s</a>' % (self.options['url'], title)) if 'url' in self.options else ''
if caption or link:
code += '<figcaption>%s %s</figcaption>' % (caption, link)
code += '%s</figure>' % table
# Write cache
if cache_file is None:
cache_file = open(cached_path, 'w')
cache_file.write(parsed)
cache_file.close()
return [nodes.raw('', code, format='html')]
directives.register_directive('code-block', Pygments)
directives.register_directive('sourcecode', Pygments)

@ -1,39 +0,0 @@
#!/usr/bin/env python
# :Author: David Goodger, the Pygments team, Guenter Milde
# :Date: $Date: $
# :Copyright: This module has been placed in the public domain.
# This is a merge of the `Docutils`_ `rst2html` front end with an extension
# suggestion taken from the `Pygments`_ documentation, reworked specifically
# for `Octopress`_.
#
# .. _Pygments: http://pygments.org/
# .. _Docutils: http://docutils.sourceforge.net/
# .. _Octopress: http://octopress.org/
"""
A front end to docutils, producing HTML with syntax colouring using pygments
"""
try:
import locale
locale.setlocale(locale.LC_ALL, '')
except:
pass
from transform import transform
from docutils.writers.html4css1 import Writer
from docutils.core import default_description
from directives import Pygments
description = ('Generates (X)HTML documents from standalone reStructuredText '
'sources. Uses `pygments` to colorize the content of'
'"code-block" directives. Needs an adapted stylesheet'
+ default_description)
def main():
return transform(writer=Writer(), part='html_body')
if __name__ == '__main__':
print(main())

@ -1,42 +0,0 @@
import sys
from docutils.core import publish_parts
from optparse import OptionParser
from docutils.frontend import OptionParser as DocutilsOptionParser
from docutils.parsers.rst import Parser
def transform(writer=None, part=None):
p = OptionParser(add_help_option=False)
# Collect all the command line options
docutils_parser = DocutilsOptionParser(components=(writer, Parser()))
for group in docutils_parser.option_groups:
p.add_option_group(group.title, None).add_options(group.option_list)
p.add_option('--part', default=part)
opts, args = p.parse_args()
settings = dict({
'file_insertion_enabled': False,
'raw_enabled': False,
'doctitle_xform': False,
})
if len(args) == 1:
try:
content = open(args[0], 'r').read()
except IOError:
content = args[0]
else:
content = sys.stdin.read()
parts = publish_parts(
source=content,
writer=writer,
settings_overrides=settings
)
if 'html_body' in parts:
return parts['html_body']
if opts.part in parts:
return parts[opts.part]
return ''

@ -1,51 +0,0 @@
module Jekyll
class ProjectVersionTag < Liquid::Tag
NO_GIT_MESSAGE = 'Oops, are you sure this is a git project?'
UNABLE_TO_PARSE_MESSAGE = 'Sorry, could not read project version at the moment'
def render(context)
if git_repo?
current_version.chomp
else
NO_GIT_MESSAGE
end
end
private
def current_version
@_current_version ||= begin
# attempt to find the latest tag, falling back to last commit
version = git_describe || parse_head
version || UNABLE_TO_PARSE_MESSAGE
end
end
def git_describe
tagged_version = %x{ git describe --tags --always }
if command_succeeded?
tagged_version
end
end
def parse_head
head_commitish = %x{ git rev-parse --short HEAD }
if command_succeeded?
head_commitish
end
end
def command_succeeded?
!$?.nil? && $?.success?
end
def git_repo?
system('git rev-parse')
end
end
end
Liquid::Template.register_tag('project_version', Jekyll::ProjectVersionTag)

@ -1,18 +0,0 @@
---
layout: default
---
{% include basic.css %}
{% include nature.css %}
<div class="post">
<header class="post-header">
<div class="post-title">{{ page.title }}</div>
<p class="post-meta">{{ page.date | date: "%b %-d, %Y" }}{% if page.author %} • {{ page.author }}{% endif %}{% if page.meta %} • {{ page.meta }}{% endif %}</p>
</header>
<article class="post-content">
{{ content }}
</article>
</div>

@ -1,204 +0,0 @@
/**
* Reset some basic elements
*/
body, h1, h2, h3, h4, h5, h6,
p, blockquote, pre, hr,
dl, dd, ol, ul, figure {
margin: 0;
padding: 0;
}
/**
* Basic styling
*/
body {
font-family: $base-font-family;
font-size: $base-font-size;
line-height: $base-line-height;
font-weight: 300;
color: $text-color;
background-color: $background-color;
-webkit-text-size-adjust: 100%;
}
/**
* Set `margin-bottom` to maintain vertical rhythm
*/
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure,
%vertical-rhythm {
margin-bottom: $spacing-unit / 2;
}
/**
* Images
*/
img {
max-width: 100%;
vertical-align: middle;
}
/**
* Figures
*/
figure > img {
display: block;
}
figcaption {
font-size: $small-font-size;
}
/**
* Lists
*/
ul, ol {
margin-left: $spacing-unit;
}
li {
> ul,
> ol {
margin-bottom: 0;
}
}
/**
* Headings
*/
h1, h2, h3, h4, h5, h6 {
font-weight: 300;
}
/**
* Links
*/
a {
color: $brand-color;
text-decoration: none;
&:visited {
color: darken($brand-color, 15%);
}
&:hover {
color: $text-color;
text-decoration: underline;
}
}
/**
* Blockquotes
*/
blockquote {
color: $grey-color;
border-left: 4px solid $grey-color-light;
padding-left: $spacing-unit / 2;
font-size: 18px;
letter-spacing: -1px;
font-style: italic;
> :last-child {
margin-bottom: 0;
}
}
/**
* Code formatting
*/
pre,
code {
font-size: 15px;
border: 1px solid $grey-color-light;
border-radius: 3px;
background-color: #eef;
}
code {
padding: 1px 5px;
}
pre {
padding: 8px 12px;
overflow-x: scroll;
> code {
border: 0;
padding-right: 0;
padding-left: 0;
}
}
/**
* Wrapper
*/
.wrapper {
max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit} * 2));
max-width: calc(#{$content-width} - (#{$spacing-unit} * 2));
margin-right: auto;
margin-left: auto;
padding-right: $spacing-unit;
padding-left: $spacing-unit;
@extend %clearfix;
@include media-query($on-laptop) {
max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit}));
max-width: calc(#{$content-width} - (#{$spacing-unit}));
padding-right: $spacing-unit / 2;
padding-left: $spacing-unit / 2;
}
}
/**
* Clearfix
*/
%clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
/**
* Icons
*/
.icon {
> svg {
display: inline-block;
width: 16px;
height: 16px;
vertical-align: middle;
path {
fill: $grey-color;
}
}
}

@ -1,236 +0,0 @@
/**
* Site header
*/
.site-header {
border-top: 5px solid $grey-color-dark;
border-bottom: 1px solid $grey-color-light;
min-height: 56px;
// Positioning context for the mobile navigation icon
position: relative;
}
.site-title {
font-size: 26px;
line-height: 56px;
letter-spacing: -1px;
margin-bottom: 0;
float: left;
&,
&:visited {
color: $grey-color-dark;
}
}
.site-nav {
float: right;
line-height: 56px;
.menu-icon {
display: none;
}
.page-link {
color: $text-color;
line-height: $base-line-height;
// Gaps between nav items, but not on the first one
&:not(:first-child) {
margin-left: 20px;
}
}
@include media-query($on-palm) {
position: absolute;
top: 9px;
right: 30px;
background-color: $background-color;
border: 1px solid $grey-color-light;
border-radius: 5px;
text-align: right;
.menu-icon {
display: block;
float: right;
width: 36px;
height: 26px;
line-height: 0;
padding-top: 10px;
text-align: center;
> svg {
width: 18px;
height: 15px;
path {
fill: $grey-color-dark;
}
}
}
.trigger {
clear: both;
display: none;
}
&:hover .trigger {
display: block;
padding-bottom: 5px;
}
.page-link {
display: block;
padding: 5px 10px;
}
}
}
/**
* Site footer
*/
.site-footer {
border-top: 1px solid $grey-color-light;
padding: $spacing-unit 0;
}
.footer-heading {
font-size: 18px;
margin-bottom: $spacing-unit / 2;
}
.contact-list,
.social-media-list {
list-style: none;
margin-left: 0;
}
.footer-col-wrapper {
font-size: 15px;
color: $grey-color;
margin-left: -$spacing-unit / 2;
@extend %clearfix;
}
.footer-col {
float: left;
margin-bottom: $spacing-unit / 2;
padding-left: $spacing-unit / 2;
}
.footer-col-1 {
width: -webkit-calc(35% - (#{$spacing-unit} / 2));
width: calc(35% - (#{$spacing-unit} / 2));
}
.footer-col-2 {
width: -webkit-calc(20% - (#{$spacing-unit} / 2));
width: calc(20% - (#{$spacing-unit} / 2));
}
.footer-col-3 {
width: -webkit-calc(45% - (#{$spacing-unit} / 2));
width: calc(45% - (#{$spacing-unit} / 2));
}
@include media-query($on-laptop) {
.footer-col-1,
.footer-col-2 {
width: -webkit-calc(50% - (#{$spacing-unit} / 2));
width: calc(50% - (#{$spacing-unit} / 2));
}
.footer-col-3 {
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
width: calc(100% - (#{$spacing-unit} / 2));
}
}
@include media-query($on-palm) {
.footer-col {
float: none;
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
width: calc(100% - (#{$spacing-unit} / 2));
}
}
/**
* Page content
*/
.page-content {
padding: $spacing-unit 0;
}
.page-heading {
font-size: 20px;
}
.post-list {
margin-left: 0;
list-style: none;
> li {
margin-bottom: $spacing-unit;
}
}
.post-meta {
font-size: $small-font-size;
color: $grey-color;
}
.post-link {
display: block;
font-size: 24px;
}
/**
* Posts
*/
.post-header {
margin-bottom: $spacing-unit;
}
.post-title {
font-size: 42px;
letter-spacing: -1px;
line-height: 1;
@include media-query($on-laptop) {
font-size: 36px;
}
}
.post-content {
margin-bottom: $spacing-unit;
h2 {
font-size: 32px;
@include media-query($on-laptop) {
font-size: 28px;
}
}
h3 {
font-size: 26px;
@include media-query($on-laptop) {
font-size: 22px;
}
}
h4 {
font-size: 20px;
@include media-query($on-laptop) {
font-size: 18px;
}
}
}

@ -1,67 +0,0 @@
/**
* Syntax highlighting styles
*/
.highlight {
background: #fff;
@extend %vertical-rhythm;
.c { color: #998; font-style: italic } // Comment
.err { color: #a61717; background-color: #e3d2d2 } // Error
.k { font-weight: bold } // Keyword
.o { font-weight: bold } // Operator
.cm { color: #998; font-style: italic } // Comment.Multiline
.cp { color: #999; font-weight: bold } // Comment.Preproc
.c1 { color: #998; font-style: italic } // Comment.Single
.cs { color: #999; font-weight: bold; font-style: italic } // Comment.Special
.gd { color: #000; background-color: #fdd } // Generic.Deleted
.gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific
.ge { font-style: italic } // Generic.Emph
.gr { color: #a00 } // Generic.Error
.gh { color: #999 } // Generic.Heading
.gi { color: #000; background-color: #dfd } // Generic.Inserted
.gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific
.go { color: #888 } // Generic.Output
.gp { color: #555 } // Generic.Prompt
.gs { font-weight: bold } // Generic.Strong
.gu { color: #aaa } // Generic.Subheading
.gt { color: #a00 } // Generic.Traceback
.kc { font-weight: bold } // Keyword.Constant
.kd { font-weight: bold } // Keyword.Declaration
.kp { font-weight: bold } // Keyword.Pseudo
.kr { font-weight: bold } // Keyword.Reserved
.kt { color: #458; font-weight: bold } // Keyword.Type
.m { color: #099 } // Literal.Number
.s { color: #d14 } // Literal.String
.na { color: #008080 } // Name.Attribute
.nb { color: #0086B3 } // Name.Builtin
.nc { color: #458; font-weight: bold } // Name.Class
.no { color: #008080 } // Name.Constant
.ni { color: #800080 } // Name.Entity
.ne { color: #900; font-weight: bold } // Name.Exception
.nf { color: #900; font-weight: bold } // Name.Function
.nn { color: #555 } // Name.Namespace
.nt { color: #000080 } // Name.Tag
.nv { color: #008080 } // Name.Variable
.ow { font-weight: bold } // Operator.Word
.w { color: #bbb } // Text.Whitespace
.mf { color: #099 } // Literal.Number.Float
.mh { color: #099 } // Literal.Number.Hex
.mi { color: #099 } // Literal.Number.Integer
.mo { color: #099 } // Literal.Number.Oct
.sb { color: #d14 } // Literal.String.Backtick
.sc { color: #d14 } // Literal.String.Char
.sd { color: #d14 } // Literal.String.Doc
.s2 { color: #d14 } // Literal.String.Double
.se { color: #d14 } // Literal.String.Escape
.sh { color: #d14 } // Literal.String.Heredoc
.si { color: #d14 } // Literal.String.Interpol
.sx { color: #d14 } // Literal.String.Other
.sr { color: #009926 } // Literal.String.Regex
.s1 { color: #d14 } // Literal.String.Single
.ss { color: #990073 } // Literal.String.Symbol
.bp { color: #999 } // Name.Builtin.Pseudo
.vc { color: #008080 } // Name.Variable.Class
.vg { color: #008080 } // Name.Variable.Global
.vi { color: #008080 } // Name.Variable.Instance
.il { color: #099 } // Literal.Number.Integer.Long
}

@ -1,11 +0,0 @@
---
layout: page
title: About
permalink: /about/
---
This is the base Jekyll theme. You can find out more info about customizing your Jekyll theme, as well as basic Jekyll usage documentation at [jekyllrb.com](http://jekyllrb.com/)
You can find the source code for the Jekyll new theme at: [github.com/jglovier/jekyll-new](https://github.com/jglovier/jekyll-new)
You can find the source code for Jekyll at [github.com/jekyll/jekyll](https://github.com/jekyll/jekyll)

@ -1,512 +0,0 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
img {
border: 0;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li div.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable dl, table.indextable dd {
margin-top: 0;
margin-bottom: 0;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- general body styles --------------------------------------------------- */
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink {
visibility: visible;
}
div.document p.caption {
text-align: inherit;
}
div.document td {
text-align: left;
}
.field-list ul {
padding-left: 1em;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
.align-left {
text-align: left;
}
.align-center {
clear: both;
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px 7px 0 7px;
background-color: #ffe;
width: 40%;
float: right;
}
p.sidebar-title {
font-weight: bold;
}
/* -- topics ---------------------------------------------------------------- */
div.topic {
border: 1px solid #ccc;
padding: 7px 7px 0 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
div.admonition dl {
margin-bottom: 0;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.document p.centered {
text-align: center;
margin-top: 25px;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
border: 0;
border-collapse: collapse;
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
table.field-list td, table.field-list th {
border: 0 !important;
}
table.footnote td, table.footnote th {
border: 0 !important;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
dl {
margin-bottom: 15px;
}
dd p {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dt:target, .highlighted {
background-color: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.refcount {
color: #060;
}
.optional {
font-size: 1.3em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
font-size: 1.1em;
}
td.linenos pre {
padding: 5px 0px;
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
margin-left: 0.5em;
}
table.highlighttable td {
padding: 0 0.5em 0 0.5em;
}
tt.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
tt.descclassname {
background-color: transparent;
}
tt.xref, a tt {
background-color: transparent;
font-weight: bold;
}
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.document div.math p {
text-align: center;
}
span.eqno {
float: right;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

@ -1,40 +0,0 @@
.toc {
background: #f9f9f9;
border: 1px solid #aaaaaa;
display: table;
padding-top: 16px;
padding-right: 16px;
padding-bottom: 16px;
list-style: none;
color: #2EA3F2;
}
ul {
padding-left: 30px;
}
p {
padding-left: 30px;
color: #666;
}
h3 {
padding-top: 48px;
color: #333;
}
h4, h5 {
border-top: 40px solid transparent;
margin-top: -40px;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
font-style: italic;
font-size: 16px;
padding-top: 16px;
color: #333;
}

@ -1,52 +0,0 @@
---
# Only the main Sass file needs front matter (the dashes are enough)
---
@charset "utf-8";
// Our variables
$base-font-family: Helvetica, Arial, sans-serif;
$base-font-size: 16px;
$small-font-size: $base-font-size * 0.875;
$base-line-height: 1.5;
$spacing-unit: 30px;
$text-color: #111;
$background-color: #fdfdfd;
$brand-color: #2a7ae2;
$grey-color: #828282;
$grey-color-light: lighten($grey-color, 40%);
$grey-color-dark: darken($grey-color, 25%);
// Width of the content area
$content-width: 1340px;
$on-palm: 600px;
$on-laptop: 800px;
// Using media queries with like this:
// @include media-query($on-palm) {
// .wrapper {
// padding-right: $spacing-unit / 2;
// padding-left: $spacing-unit / 2;
// }
// }
@mixin media-query($device) {
@media screen and (max-width: $device) {
@content;
}
}
// Import partials from `sass_dir` (defaults to `_sass`)
@import
"base",
"layout",
"syntax-highlighting"
;

@ -1,285 +0,0 @@
/*
* nature.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- nature theme.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- page layout ----------------------------------------------------------- */
body {
font-family: Arial, sans-serif;
font-size: 100%;
/*background-color: #111;*/
color: #555;
margin: 0;
padding: 0;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 230px;
}
hr {
border: 1px solid #B1B4B6;
}
/*
div.document {
background-color: #eee;
}
*/
div.document {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
font-size: 0.9em;
}
div.footer {
color: #555;
width: 100%;
padding: 13px 0;
text-align: center;
font-size: 75%;
}
div.footer a {
color: #444;
text-decoration: underline;
}
div.related {
background-color: #6BA81E;
line-height: 32px;
color: #fff;
text-shadow: 0px 1px 0 #444;
font-size: 0.9em;
}
div.related a {
color: #E2F3CC;
}
div.sphinxsidebar {
font-size: 0.75em;
line-height: 1.5em;
}
div.sphinxsidebarwrapper{
padding: 20px 0;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: Arial, sans-serif;
color: #222;
font-size: 1.2em;
font-weight: normal;
margin: 0;
padding: 5px 10px;
background-color: #ddd;
text-shadow: 1px 1px 0 white
}
div.sphinxsidebar h4{
font-size: 1.1em;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p {
color: #888;
padding: 5px 20px;
}
div.sphinxsidebar p.topless {
}
div.sphinxsidebar ul {
margin: 10px 20px;
padding: 0;
color: #000;
}
div.sphinxsidebar a {
color: #444;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar input[type=text]{
margin-left: 20px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #005B81;
text-decoration: none;
}
a:hover {
color: #E32E00;
text-decoration: underline;
}
div.document h1,
div.document h2,
div.document h3,
div.document h4,
div.document h5,
div.document h6 {
font-family: Arial, sans-serif;
background-color: #BED4EB;
font-weight: normal;
color: #212224;
margin: 30px 0px 10px 0px;
padding: 5px 0 5px 10px;
text-shadow: 0px 1px 0 white
}
div.document h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
div.document h2 { font-size: 150%; background-color: #C8D5E3; }
div.document h3 { font-size: 120%; background-color: #D8DEE3; }
div.document h4 { font-size: 110%; background-color: #D8DEE3; }
div.document h5 { font-size: 100%; background-color: #D8DEE3; }
div.document h6 { font-size: 100%; background-color: #D8DEE3; }
a.headerlink {
color: #c60f0f;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
a.headerlink:hover {
background-color: #c60f0f;
color: white;
}
div.document p, div.document dd, div.document li {
line-height: 1.5em;
}
div.admonition p.admonition-title + p {
display: inline;
}
div.highlight{
background-color: white;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre {
padding: 10px;
background-color: White;
color: #222;
line-height: 1.2em;
border: 1px solid #C6C9CB;
font-size: 1.1em;
margin: 1.5em 0 1.5em 0;
-webkit-box-shadow: 1px 1px 1px #d8d8d8;
-moz-box-shadow: 1px 1px 1px #d8d8d8;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
font-size: 1.1em;
font-family: monospace;
}
.viewcode-back {
font-family: Arial, sans-serif;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}
p {
margin: 0;
}
ul li dd {
margin-top: 0;
}
ul li dl {
margin-bottom: 0;
}
li dl dd {
margin-bottom: 0;
}
dd ul {
padding-left: 0;
}
li dd ul {
margin-bottom: 0;
}
table {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
td[colspan]:not([colspan="1"]) {
background: #eeeeee;
}
thead {
background: #eeeeee;
}

@ -1,62 +0,0 @@
p {
line-height: 1.5;
color: #666;
}
a.anchor {
display: block;
position: relative;
top: -50px;
visibility: hidden;
}
a:link {
color: #2EA3F2;
text-decoration: none;
}
a:visited {
color: #2EA3F2;
text-decoration: none;
}
a:active {
color: #2EA3F2;
text-decoration: none;
}
a:hover {
color: #2EA3F2;
text-decoration: underline;
}
h1,h2,h3,h4,h5 {
color: #333;
}
.page-content {
min-height: 100%;
height: auto !important; /* This line and the next line are not necessary unless you need IE6 support */
height: 100%;
margin: 0 auto -33px; /* the bottom margin is the negative value of the footer's height */
padding-bottom: 0;
padding-top: 0;
}
.push {
height: 33px;
}
#page {
margin-bottom: -33px;
}
#document {
margin-top: 10px; /* We want a little whitespace before the page content starts */
width: 1080px;
}
#wrapper {
max-width: 1080px;
}

@ -1,30 +0,0 @@
---
layout: null
---
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ site.title | xml_escape }}</title>
<description>{{ site.description | xml_escape }}</description>
<link>{{ site.url }}{{ site.baseurl }}/</link>
<atom:link href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}" rel="self" type="application/rss+xml"/>
<pubDate>{{ site.time | date_to_rfc822 }}</pubDate>
<lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate>
<generator>Jekyll v{{ jekyll.version }}</generator>
{% for post in site.posts limit:10 %}
<item>
<title>{{ post.title | xml_escape }}</title>
<description>{{ post.content | xml_escape }}</description>
<pubDate>{{ post.date | date_to_rfc822 }}</pubDate>
<link>{{ post.url | prepend: site.baseurl | prepend: site.url }}</link>
<guid isPermaLink="true">{{ post.url | prepend: site.baseurl | prepend: site.url }}</guid>
{% for tag in post.tags %}
<category>{{ tag | xml_escape }}</category>
{% endfor %}
{% for cat in post.categories %}
<category>{{ cat | xml_escape }}</category>
{% endfor %}
</item>
{% endfor %}
</channel>
</rss>

@ -204,7 +204,7 @@ on:
- Disruptive net-splits
- Non-extensible
[IRCv3](http://ircv3.net) exists and is addressing some of issues;
[IRCv3](http://ircv3.net) exists and is addressing some of these issues;
this is great news and we wish them well. It's almost a contradiction
in terms to get competitive between openly interoperable communication
projects - we look forward to increasing the richness of Matrix\<-\>IRC
@ -519,8 +519,8 @@ Follow the instructions for the homeserver you want to run. If you want to run S
##### Can I run my own identity server?
Yes - the reference implementation is
[sydent](https://github.com/matrix-org/sydent) and you can run your own
ID server cluster that tracks 3rd party to Matrix ID mappings. This won't be very useful right now, though, and we don't recommend it.
[sydent](https://github.com/matrix-org/sydent) and you can run your own ID server cluster that tracks 3rd party to Matrix ID mappings. This won't be very useful right now, though, and we don't recommend it.
If you want your server to participate in the global replicated Matrix ID
service then please get in touch with us. Meanwhile, we are looking at
ways of decentralising the 'official' Matrix identity service so that

@ -17,7 +17,7 @@ One of the main use cases for application services is protocol bridges. Our Matr
### Some of the features of the IRC application service we have since implemented include:
- Specific channel-to-matrix room bridging : This is what the original IRC bot did. You can specify specific channels and specific room IDs, and messages will be bridged.
- Dynamic channel-to-matrix room bridging> : This allows Matrix users to join any channel on an IRC network, rather than being forced to use one of the specific channels configured.
- Dynamic channel-to-matrix room bridging : This allows Matrix users to join any channel on an IRC network, rather than being forced to use one of the specific channels configured.
- Two-way PM support : IRC users can PM the virtual "M-" users and private Matrix rooms will be created. Likewise, Matrix users can invite the virtual "@irc_Nick:domain" user IDs to a room and a PM to the IRC nick will be made.
- IRC nick changing support: Matrix users are no longer forced to use "M-" nicks and can change them by sending "!nick" messages directly to the bridge.
- Ident support: This allows usernames to be authenticated for virtual IRC clients, which means IRC bans can be targeted at the Matrix user rather than the entire application service.

@ -1,14 +0,0 @@
---
layout: default
---
<div class="home">
<h1>Guides</h1>
<p>Here is a collection of guides that might help you get involved with Matrix.</p>
<p>First, there is the <a href="./getting_involved.html" title="Getting Involved">Getting Involved</a> guide, which explains various ways of getting started with Matrix.</p>
<p>The <a href="./client-server.html" title="Client-Server API">Client-Server API</a> guide explains in detail how to use the CS API, which is useful if you want to write a client (or modify an existing one) - or if you're just interested in how it works "under the hood".</p>
<p>The <a href="./application_services.html" title="Application services">Application services</a> guide introduces and explains Application services, and what they can be used for.
<p>Finally there is the <a href="./faq.html" title="FAQ">FAQ</a>, which tries to answer all your questions relating to Matrix!</p>
</div>

@ -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 -%}

@ -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)

Loading…
Cancel
Save