Skip to content

Instantly share code, notes, and snippets.

@jeluard
Created April 3, 2018 17:34
Show Gist options
  • Save jeluard/c826251b83cd4ba44b175cc90568a835 to your computer and use it in GitHub Desktop.
Save jeluard/c826251b83cd4ba44b175cc90568a835 to your computer and use it in GitHub Desktop.

Status API

Status allows to register chat commands via it's API. Due to a number of recent low-level changes and low visibility of this feature part of it have been broken.

This document provides a high-level view of the current state of the API and associated limitations. Features that used to work are also detailed.

TLDR

Status bots features:

  • small JS programs that can be triggered by users in a chat
  • interact via messages, can have complex UI (suggestions and messages)
  • isolated (so secure)
  • optional webview (eventually fullscreen) for complex JS/HTML UI
  • hability to suggest command execution to chatting partner
  • react and manipulate chat input

Current limitations:

  • can only be added when status account is in debug mode (enable dev mode, then start debug mode via console)
  • must be manually added from dev env, no external distribution model (like a catalog or store)
  • chatbot create a new contact with associated local command
  • global commands can be added, and associated contact hidden
  • chatbot JS must be hosted on an HTTP server, JS file loaded remotely every time the account is opened
  • very manual UI model, limited by options offered
  • no inline interactions (purely message based)

Deployment tool: status-dev-cli

This tool is a nodejs based utility allowing to register DApps or bots on a running device identified by its IP. It mostly serves 3 purposes:

  • discovery of devices and associated running bots / DApps
  • registration of bots / DApps
  • light dev facility to refresh those apps

What this tool offer:

  • scan network to detect devices running in debug mode
  • add / remove contact (either DApps or bots) to an identified device
  • switch geth network used for Dapps of an identified device
  • list contacts currently installed in an identified device
  • watch a directory and refresh (eventually manually) a Dapp
  • access log messages for a contact

When debug mode is enabled in the console chat, an embedded HTTP server (using react-native-http-bridge) is started and listens to incoming HTTP POST request to manipulate status internal states. This embedded server also handles the mDNS advertisment logic.

Limitations

This tool suffer from endemic installation issues. It's pretty hard to setup notably due to it's usage of mDNS protocol (used to discover devices) and difficulty to install watchman.

Since its inception status significantly evolved, most specifically around DApps which are now first class citizen. The dev facility (watch, reload, switch-node) might be a little week compared to regular web tools. One might wonder what is status added value here compared to a regular web dev env. (Note that status offer DApps remote debugging capacities allowing dev to easily debug their DApps running on status).

Local installation of bots is a subset of the complete extensions publication and hosting story. Current solution is not practical for end users.

switch-node is definitively a feature we need to keep. This allows DApps / extensions developer to target a local chain that can be more easily manipulated (e.g. using ganache). This must modify newtork for both DApps and bots. This feature could be provided by allowing users to provide their own node URL in Profile / Advanced settings. Note It has to be clarified how this plays with current network switching capacities.

API (as documented by https://docs.status.im/)

Documentation first details how to use status-dev-cli and related issues, including how to workaround genymotion configuration. Then creation of a DApps is detailed using Embark, Truffle and manually.

It would make sense to separate our docs between extensions, DApps and smart-contract creation.

Bots are added as a separate contact. Comands are then available when chatting with this contact.

Bots API

3rd party developers can leverage a set of JavaScript APIs available via the status global object.

Lifecycle

A bot can perform logic when installed. A common pattern is to send messages to great users.

status.addListener("init", function (params, context) {
  status.sendMessage("Hello user!");
});

React on messages from other party

status.addListener("on-message-send", function (params, context) {
    var result = {
        err: null,
        data: null,
        messages: []
    };
    result["text-message"] = "You're amazing, master!";
    return result;
});

Hook on every message received. Can answer back a string message.

Register command

Register a command that will be accessible in the command bar. How the command is displayed (as a message) can be customized via the preview property (using an hiccup based syntax).

status.command({
     name: "hello",
     title: "HelloBot",
     description: "Helps you say hello",
     color: "#CCCCCC",
     preview: function (params) {
        var text = status.components.text(
            {
                style: {
                    marginTop: 5,
                    marginHorizontal: 0,
                    fontSize: 14,
                    fontFamily: "font",
                    color: "black"
                }
            }, "Hello from the other side!");

        return {markup: status.components.view({}, [text])};
    }
 });

Custom logic can be triggered when executing (sending) the command.

status.command({
     name: "hello",
     title: "HelloBot",
     description: "Helps you say hello",
     color: "#CCCCCC",
     handler: function (params) {
        return {
            event: "request",
            params: [params.amount],
            request: {
                command: "send",
                params: {
                    amount: params.amount
                }
            }
        };
    }
 });

A command can accept typed parameters with an optional suggestions. When filling a command, each parameter with a suggestion will trigger the opening of a panel that usually contains predefined values. A common pattern is to fill this parameter when the suggestion is pressed.

status.command({
    name: "greet",
    title: "Greeter",
    description: "Helps you choose greetings",
    color: "#0000ff",
    params: [{
        name: "greet",
        type: status.types.TEXT,
        suggestions: helloSuggestions
        }]
})

function helloSuggestions() {
    var suggestions = ["Hello", "Goodbye"].map(function(entry) {
        return status.components.touchable(
            {onPress: status.components.dispatch([status.events.SET_VALUE, entry])},
            status.components.view(
                suggestionsContainerStyle,
                [status.components.view(
                    suggestionSubContainerStyle,
                    [
                        status.components.text(
                            {style: valueStyle},
                            entry
                        )
                    ]
                )]
            )
        );
    });

    // Let's wrap those two touchable buttons in a scrollView
    var view = status.components.scrollView(
        suggestions
    );

    // Give back the whole thing inside an object.
    return {markup: view};
}

For more complex scenario data can be stored / retrieved from an internal reactive database.

function doubledValueLabel(params) {
    return "sliderValue = " + params.value +
        "; (2 * sliderValue) = " + (2 * value);
}

status.defineSubscription(
    // the name of subscription and the name of the value in bot-db
    // associated with this subscription
    "doubledValue",
    // the map of values on which subscription depends: keys are arbitrary names
    // and values are db paths to another value
    {value: ["sliderValue"]},
    // the function which will be called as reaction on changes of values above,
    // should be pure. Returned result will be associated with subscription in bot-db
    doubledValueLabel
);

status.setDefaultDb({
    doubledValue: doubledValueLabel({value: defaultSliderValue})
});


// Some view that depends on 'doubledValue' and modifies it
// Note 'subscribe' and 'dispatch' calls
var view = ["view", {},
    ["text", {}, "Balance " + balance + " ETH"],
    ["text", {}, ["subscribe", ["doubledValue"]]],
    ["slider", {
        maximumValue: ["subscribe", ["balance"]],
        value: defaultSliderValue,
        minimumValue: 0,
        onSlidingComplete: ["dispatch", ["set", "sliderValue"]],
        step: 0.05
    }],
    ['touchable',
        {onPress: ['dispatch', ["set-value-from-db", "roundedValue"]]},
        ["view", {}, ["text", {}, "Set value"]]
    ],
    ["text", {style: {color: "red"}}, ["subscribe", ["validationText"]]]
];

Suggest user action

Allows to send a message that encapsulates a command call. Useful when you want to suggest an action to your chatting partner.

status.response({
    name: "confirmation-code",
    color: "#7099e6",
    description: I18n.t('confirm_description'),
    sequentialParams: true,
    params: [{
        name: "code",
        type: status.types.NUMBER
    }],
    validator: function (params) {
        if (!/^[\d]{4}$/.test(params.code)) {
            var error = status.components.validationMessage(
                I18n.t('confirm_validation_title'),
                I18n.t('confirm_validation_description')
            );

            return {markup: error};
        }
    }
});

Text input manipulation

Change the text input:

status.components.dispatch([status.events.SET_VALUE, "@browse url"])

React on text input changes:

status.addListener("on-message-input-change", function (params, context) {
});

JS integration

Some global Javascript objects are accessible to bots: web3 and localStorage.

Existing bots

Status relies on the bots API for its internal commands. Note that those commands also rely on features not available to external bots.

Console

Used to provide debug and faucet commands.

Use the following features:

  • command (params, preview, suggestions, shortPreview, validator)
  • status.response
  • "on-message-input-change"
  • "on-message-change"

Transactor

Used to provide send and request commands.

Use the following features:

  • command (scope, parmas, validator, async handler, preview, shortPreview)
  • status.defineSubscription
  • status.response

Links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment