Mercurial > prosody-modules
view mod_rest/README.markdown @ 3856:8bdb1729529b
mod_rest: Make XHTML-IM mapping more convenient
Avoid burdening web developers with XML namespace attribute
Might be a bit fragile
Note that the <body> element must be included
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sat, 25 Jan 2020 00:46:09 +0100 |
parents | a8aa11cc351d |
children | 8752e5b5dd08 |
line wrap: on
line source
--- labels: - 'Stage-Alpha' summary: RESTful XMPP API --- # Introduction This is yet another RESTful API for sending and receiving stanzas via Prosody. It can be used to build bots and components implemented as HTTP services. # Usage ## Enabling ``` {.lua} Component "rest.example.net" "rest" rest_credentials = "Bearer dmVyeSBzZWNyZXQgdG9rZW4K" ``` ## Sending stanzas The API endpoint becomes available at the path `/rest`, so the full URL will be something like `https://your-prosody.example:5281/rest`. To try it, simply `curl` an XML stanza payload: ``` {.sh} curl https://prosody.example:5281/rest \ --oauth2-bearer dmVyeSBzZWNyZXQgdG9rZW4K \ -H 'Content-Type: application/xmpp+xml' \ --data-binary '<message type="chat" to="user@example.org"> <body>Hello!</body> </body>' ``` or a JSON payload: ``` {.sh} curl https://prosody.example:5281/rest \ --oauth2-bearer dmVyeSBzZWNyZXQgdG9rZW4K \ -H 'Content-Type: application/json' \ --data-binary '{ "body" : "Hello!", "kind" : "message", "to" : "user@example.org", "type" : "chat" }' ``` The `Content-Type` header is important! ### Replies A POST containing an `<iq>` stanza automatically wait for the reply, long-polling style. ``` {.sh} curl https://prosody.example:5281/rest \ --oauth2-bearer dmVyeSBzZWNyZXQgdG9rZW4K \ -H 'Content-Type: application/xmpp+xml' \ --data-binary '<iq type="get" to="example.net"> <ping xmlns="urn:xmpp:ping"/> </iq>' ``` Replies to other kinds of stanzas that are generated by the same Prosody instance *MAY* be returned in the HTTP response. Replies from other entities (connected clients or remote servers) will not be returned, but can be forwarded via the callback API described in the next section. ## Receiving stanzas TL;DR: Set this webhook callback URL, get XML `POST`-ed there. ``` {.lua} Component "rest.example.net" "rest" rest_credentials = "Bearer dmVyeSBzZWNyZXQgdG9rZW4K" rest_callback_url = "http://my-api.example:9999/stanzas" ``` To enable JSON payloads set ``` {.lua} rest_callback_content_type = "application/json" ``` Example callback looks like: ``` {.xml} POST /stanzas HTTP/1.1 Content-Type: application/xmpp+xml Content-Length: 102 <message to="bot@rest.example.net" from="user@example.com" type="chat"> <body>Hello</body> </message> ``` or as JSON: ``` {.json} POST /stanzas HTTP/1.1 Content-Type: application/json Content-Length: 133 { "body" : "Hello", "from" : "user@example.com", "kind" : "message", "to" : "bot@rest.example.net", "type" : "chat" } ``` ### Replying To accept the stanza without returning a reply, respond with HTTP status code `202` or `204`. HTTP status codes in the `4xx` and `5xx` range are mapped to an appropriate stanza error. For full control over the response, set the `Content-Type` header to `application/xmpp+xml` and return an XMPP stanza as an XML snippet. ``` {.xml} HTTP/1.1 200 Ok Content-Type: application/xmpp+xml <message type="chat"> <body>Yes, this is bot</body> </message> ``` ## Payload format ### JSON ``` {.json} { "body" : "Hello!", "kind" : "message", "type" : "chat" } ``` Further JSON object keys as follows: #### Messages `kind` : `"message"` `type` : Commonly `"chat"` for 1-to-1 messages and `"groupchat"` for group chat messages. Others include `"normal"`, `"headline"` and `"error"`. `body` : Human-readable message text. `subject` : Message subject or MUC topic. `html` : HTML. `oob_url` : URL of an out-of-band resource, often used for images. #### Presence `kind` : `"presence"` `type` : Empty for online or `"unavailable"` for offline. `show` : [Online status](https://xmpp.org/rfcs/rfc6121.html#presence-syntax-children-show), `away`, `dnd` etc. `status` : Human-readable status message. `join` : Boolean. Join a group chat. #### Info-Queries Only one type of payload can be included in an `iq`. `kind` : `"iq"` `type` : `"get"` or `"set"` for queries, `"response"` or `"error"` for replies. `ping` : Send a ping. Get a pong. Maybe. `disco` : Retrieve service discovery information about an entity. `items` : Discover list of items (other services, groupchats etc). ### XML ``` {.xml} <message type="" id="" to="" from="" xml:lang=""> ... </message> ``` An XML declaration (`<?xml?>`) **MUST NOT** be included. The payload MUST contain one (1) `message`, `presence` or `iq` stanza. The stanzas MUST NOT have an `xmlns` attribute, and the default/empty namespace is treated as `jabber:client`. # Examples ## Python / Flask Simple echo bot that responds to messages as XML: ``` {.python} from flask import Flask, Response, request import xml.etree.ElementTree as ET app = Flask("echobot") @app.before_request def parse(): request.stanza = ET.fromstring(request.data) @app.route("/", methods=["POST"]) def hello(): if request.stanza.tag == "message": return Response( "<message><body>Yes this is bot</body></message>", content_type="application/xmpp+xml", ) return Response(status=501) if __name__ == "__main__": app.run() ``` And a JSON variant: ``` {.python} from flask import Flask, Response, request, jsonify app = Flask("echobot") @app.route("/", methods=["POST"]) def hello(): print(request.data) if request.is_json: data = request.get_json() if data["kind"] == "message": return jsonify({"body": "hello"}) return Response(status=501) if __name__ == "__main__": app.run() ``` Remember to set `rest_callback_content_type = "application/json"` for this to work. # Compatibility Requires Prosody trunk / 0.12