Skip to content

Pumble SDK

Overview

In this guide you will install pumble-cli and use it to generate a project for a Pumble App.

Quick Start

Start by running this command to install the Pumble CLI:

sh
npm i -g pumble-cli

After successfully installing pumble-cli use the command below to log in to your workspace:

sh
pumble-cli login

You will be asked to input your email address, and you will receive a code in your inbox. After logging in you can proceed to create your first Pumble App.

sh
pumble-cli create

This command will ask you to type a name and description and will generate the project.
After this command has completed you can then cd into the generated directory and start the addon.

sh
cd my_app
npm run dev

If you login into Pumble you will see your App is installed and ready to use.

TIP

You can also skip installing pumble-cli and use npx instead

sh
npx pumble-cli create

Project structure

sh
.
├── manifest.json
├── .pumbleapprc
├── package.json
├── src
   └── main.ts
└── tsconfig.json

manifest.json

In this file you can change your App's name, Bot title, and scopes:

json
{
    "name": "my-app",
    "displayName": "My App",
    "botTitle": "My App Bot",
    "bot": true,
    "scopes": {
        "botScopes": [
            "messages:read",
            "messages:write"
        ],
        "userScopes": [
            "messages:read"
        ]
    }
}

INFO

manifest.json in the generated project it's not the full manifest that is registered in Pumble.
The full manifest is generated by the cli as a combination of this file, your application code in src/main.ts and the hostname in which the cli runs.

.pumbleapprc

After running your app for the first time with npm run dev, this file will contain your app ID and secrets.

WARNING

Make sure not to share app secrets anywhere.

src/main.ts

This is the main file for your project to run.

It's recommended to keep this file name and location unchanged.

typescript
//main.ts
import { App, JsonFileTokenStore, start } from "pumble-sdk";

const addon: App = {
    globalShortcuts: [
        {
            name: "Global shortcut",
            handler: async (ctx) => {
                await ctx.ack();
                console.log("Received global shortcut!");
                await ctx.say("Received global shortcut!");
            },
        },
    ],
    messageShortcuts: [
        {
            name: "Message shortcut",
            handler: async (ctx) => {
                await ctx.ack();
                console.log("Received message shortcut!");
                await ctx.say("Ok", "in_channel", true);
            },
        },
    ],
    slashCommands: [
        {
            command: "/slash_first",
            handler: async (ctx) => {
                await ctx.ack();
                console.log("Received slash command!");
                await ctx.say("Received slash command!");
            },
        },
    ],
    events: [
        {
            name: "NEW_MESSAGE",
            handler: (ctx) => {
                console.log("Received new message!", ctx.payload.body);
            },
        },
    ],
    blockInteraction: {
        interactions: [
            {
                sourceType: "MESSAGE",
                handlers: {
                    "on_action_1": async (ctx: BlockInteractionContext<'MESSAGE'>) => {
                        await ctx.ack();
                        console.log("Action triggered!");
                    }
                }
            }
        ]    
    },
    viewAction: {
        onSubmit: {
            view_callback_1: async (ctx) => {
                await ctx.ack();
                console.log("View submitted.");
            }
        },
        onClose: {
            view_callback_1: async (ctx) => {
                await ctx.ack();
                console.log("View closed.");
            }
        }
    },
    eventsPath: "/hook",
    redirect: { enable: true, path: "/redirect" },
    tokenStore: new JsonFileTokenStore("tokens.json"),
    welcomeMessage: 'Welcome!',
    offlineMessage: 'App cannot respond at this moment. Please try again later.',
    onServerConfiguring: (e, addon) => { 
        // ... 
    }
};

start(addon);

Create an app without Pumble CLI

It is also possible to create and run an app without using Pumble CLI, if you want to incorporate it into an existing Node.js project.

In order to do so, you first need to manually create an app by calling a Pumble endpoint, as described here. In the response, you will receive the app info, including the full Manifest and the app's secrets:

json
{
	//...your manifest
	"id": "...",
	"clientSecret": "...",
	"appKey": "...",
	"signingSecret": "..."
}
namedescription
idYour app's id. This will be used as the clientId in OAuth2 request. See Authorization.
clientSecretYour app's clientSecret. This will be used to generate access tokens. See Authorization.
appKeyYour app's secret key. This should be sent on every Pumble request along with the access token. See API.
signingSecretYour app's signing key. Pumble will sign every request that will be sent to your endpoints with this key. See Verifying Signature.

Make sure to save them, as they will be needed later.

Now you have a couple of ways to instantiate and run the addon in your Node.js project:

Creating an App instance

Create a JSON file where you will define your app's name, display name, bot title, scopes, and whether it should include a bot user or not, as it is shown here.

Then create an App instance, and provide the necessary handlers, token store, welcome message, help message, as it is shown in this example. Here you can also include your app's secrets, that you received when creating it. If you don't include them here, you will need to specify them as environment variables when running the app.

Utilizing the setup function

In your Node.js project, you can create an addon instance by calling the setup function, and providing the app manifest and http server options (server port and OAuth2 config).

typescript
const addon = setup(
    {
        id: "<<Your app's ID>>",
        clientSecret: "<<You app's client secret>>",
        appKey: "<<Your app's app key>>",
        signingSecret: "<<Your app's signing secret>>",
        name: 'my-first-app',
        displayName: 'My first app',
        bot: true,
        botTitle: 'My first app',
        scopes: {
            botScopes: ['messages:read', 'messages:write'],
            userScopes: ['messages:read', 'messages:write'],
        },
        shortcuts: [
            // ...
        ],
        slashCommands: [
            // ...
        ],
        redirectUrls: ["..."],
        eventSubscriptions: {
            url: "...",
            events: ['APP_UNINSTALLED'],
        },
        dynamicMenus: [],
        // ...
    }, 
    {
        serverPort: 9898,
        oauth2Config: {
            redirect: {
                enable: true,
                onSuccess: async (result, req, res) => { 
                    res.send("Success!"); 
                },
                onError: async (err, req, res) => { 
                    res.send("Something went wrong!"); 
                },
            },
        }
    }
);

Finally, to run the created addon simply call addon.start() method.