Adds #34 and #38
9.7 KiB
Scalar API (Riot)
Scalar and Riot communicate using cross-origin messages in a defined format (described in this document). The full source for the messaging layer in Riot can be seen here. With this API, the integrations manager is able to invite users, get some basic state information, and interact with the room in a limited capacity. The API is intentionally restricted to ensure that misbehaving domains don't have full control over Riot.
Setting up communications
Riot will automatically open a channel for receiving messages. The integrations manager needs to do the same so it can speak to Riot. Here's some sample JavaScript that will do this for us:
window.addEventListener("message", function(event) {
if (!event.data || !event.data.response) return; // invalid
if (event.data.error) throw new Error(event.data.error); // TODO: Better error handling
// TODO: Process response
});
function sendMessage(action, roomId, userId, otherFields) {
if (!otherFields) otherFields = {};
var request = otherFields;
request["user_id"] = userId;
request["room_id"] = roomId;
request["action"] = action;
window.opener.postMessage(request, "*"); // "*" posts to all origins
}
Response format
For all requests, the request is returned with the response. As an example, if we made a request like this:
{
"action": "invite",
"room_id": "!curbf:matrix.org",
"user_id": "@voyager:t2bot.io"
}
then we can expect a response object that looks like this:
{
"action": "invite",
"room_id": "!curbf:matrix.org",
"user_id": "@voyager:t2bot.io",
"response": {
"success": true
}
}
Errors
An error response will always have the following structure under response
:
{
"error": {
"message": "Something went wrong",
"_error": <original Error object>
}
}
Actions
The examples in this section assume the helper function in "Setting up communication" is present.
Inviting users
Action: "invite"
Required params:
room_id
- where to invite the user touser_id
- the user to invite
Sample call:
sendMessage("invite", "!curbf:matrix.org", "@voyager:t2bot.io");
Success Response:
{
"action": "invite",
"room_id": "!curbf:matrix.org",
"user_id": "@voyager:t2bot.io",
"response": {
"success": true
}
}
Get membership state for a user
Action: "membership_state"
Required params:
room_id
- where to try and find the useruser_id
- the user to lookup
Sample call:
sendMessage("membership_state", "!curbf:matrix.org", "@voyager:t2bot.io");
Success Response:
{
"action": "membership_state",
"room_id": "!curbf:matrix.org",
"user_id": "@voyager:t2bot.io",
"response": {
"membership": "join",
"avatar_url": "mxc://t2bot.io/hcSELkhLCNMRxLLTXKffPPSn",
"displayname": "Matrix Traveler"
}
}
Note: The response
will be the content of the m.room.member
state event. An error may be returned if the user is not in the room.
Get defined options for a bot
Action: "bot_options"
Required params:
room_id
- where to try and find the optionsuser_id
- the bot (user) to get options for
Sample call:
sendMessage("bot_options", "!curbf:matrix.org", "@voyager:t2bot.io");
Success Response:
{
"action": "bot_options",
"room_id": "!curbf:matrix.org",
"user_id": "@voyager:t2bot.io",
"response": {
"botOption": "hello world"
}
}
Note: The response
will be the content of the m.room.bot.options
state event, which is defined using set_bot_options
.
Setting options for a bot
Action: "set_bot_options"
Required params:
room_id
- where to try and set the optionsuser_id
- the bot (user) to set options forcontent
- the options to specify (extra data)
Sample call:
sendMessage("set_bot_options", "!curbf:matrix.org", "@voyager:t2bot.io", {content: {botOption: 'hello world'}});
Success Response:
{
"action": "set_bot_options",
"room_id": "!curbf:matrix.org",
"user_id": "@voyager:t2bot.io",
"content": {
"botOption": "hello world"
},
"response": {
"success": true
}
}
Note: The options will be set to the state event m.room.bot.options
, keyed to the bot/user.
Setting powerlevel for a bot
Action: "set_bot_power"
Required params:
room_id
- where to try and set the optionsuser_id
- the bot (user) to set options forlevel
- the powerlevel for the bot (integer)
Sample call:
sendMessage("set_bot_power", "!curbf:matrix.org", "@voyager:t2bot.io", {level: 50});
Success Response:
{
"action": "set_bot_power",
"room_id": "!curbf:matrix.org",
"user_id": "@voyager:t2bot.io",
"level": 50,
"response": {
"success": true
}
}
Getting join rules for the room
Action: "join_rules_state"
Required params:
room_id
- where to try and set the options
Sample call:
sendMessage("join_rules_state", "!curbf:matrix.org");
Success Response:
{
"action": "join_rules_state",
"room_id": "!curbf:matrix.org",
"response": {
"join_rule": "public"
}
}
Note: This returns the content of the state event m.room.join_rules
in the room.
Setting the plumbing state for a room
Action: "set_plumbing_state"
Required params:
room_id
- where to try and set the optionsstatus
- the plumbing state (string)
Sample call:
sendMessage("set_plumbing_state", "!curbf:matrix.org", null, {status:"whatever"});
Success Response:
{
"action": "set_plumbing_state",
"room_id": "!curbf:matrix.org",
"status": "whatever",
"response": {
"success": true
}
}
Note: This sets the m.room.plumbing
state event for the room.
Getting the number of people in a room
Action: "get_membership_count"
Required params:
room_id
- where to query the membership count
Sample call:
sendMessage("get_membership_count", "!curbf:matrix.org");
Success Response:
{
"action": "get_membership_count",
"room_id": "!curbf:matrix.org",
"response": 78
}
Determining if the user can send a particular event
Action: "can_send_event"
Required params:
room_id
- where to query the membership countevent_type
- the event type wishing to be sentis_state
- true if the event being sent will be a state event
Sample call:
sendMessage("can_send_event", "!curbf:matrix.org", null, {event_type: "m.room.message", is_state: false});
Success Response:
{
"action": "get_membership_count",
"room_id": "!curbf:matrix.org",
"event_type": "m.room.message",
"is_state": false,
"response": true
}
Get widgets in a room
Action: "get_widgets"
Required params:
room_id
- where to find widgets
Sample call:
sendMessage("get_widgets", "!curbf:matrix.org");
Success Response:
{
"action": "get_widgets",
"room_id": "!curbf:matrix.org",
"response: [{
"type": "im.vector.modular.widgets",
"state_key": "widget1",
"content": {
"type": "grafana",
"url": "https://somewhere.com",
"name": "Dashboard",
"data": {"key": "val"}
},
"room_id": "!curbf:matrix.org",
"sender": "@travis:t2l.io"
}]
}
Adding, updating, or deleting a widget in a room
Action: "set_widget"
Required params:
"room_id"
- the room to affect"widget_id"
- an arbitrary utf8 unique identifier for the widget (used to distinguish update from add)"url"
- the url to put in the iframe (null to delete the widget). Some values are replaced:$matrix_user_id
- current user ID (not a way to authorize a user for the widget)$matrix_room_id
- current room ID$matrix_display_name
- current user's display name$matrix_avatar_url
- current user's avatar url (http, not mxc)$key
- the value ofkey
in thedata
object
"type"
- the widget type. Widgets supported in Scalar are:grafana
youtube
jitsi
googledocs
etherpad
customwidget
"name"
- optional human-readable string for the widget (eg: "Dashboard")"data"
- optional key-value pairs for the widget. Appended to the iframe. Some values are magic:
Sample call:
// Add
sendMessage("set_widget", "!curbf:matrix.org", null, {
widget_id: "my_cool_widget",
type: "grafana",
url: "https://somewhere.com",
name: "Dashboard",
data: {
"userId": "$mxUserId"
}
});
// Update
// All fields must be supplied. If the field is optional, it must be present or undefined
sendMessage("set_widget", "!curbf:matrix.org", null, {
widget_id: "my_cool_widget",
type: "grafana",
url: "https://somewhere.else.com",
name: "New Dashboard",
data: {
"userId": "$mxUserId"
}
});
// Delete
sendMessage("set_widget", "!curbf:matrix.org", null, {
widget_id: "my_cool_widget",
type: "grafana",
url: null
// other fields not required if deleting
});
Success Response:
{
"action": "set_widget",
"room_id": "!curbf:matrix.org",
"url": "https://somewhere.com",
"type": "example",
"response": {
"success": true
}
}
Note: Widgets are documented by the matrix.org team on this Google Doc. That document is the source of truth for the event structure and usage.
Note: scalar_token
will be appended to the query string if the widget's url matches the API URL of the integration manager (in Riot)
Closing the integrations manager (scalar)
Action: "close_scalar"
Required params:
- None
Sample call:
sendMessage("close_scalar");
Success Response:
{
"action": "close_scalar",
"response": null
}