hubot 5.0.5 → 5.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/.editorconfig +0 -14
- package/.github/stale.yml +0 -23
- package/.github/workflows/nodejs-macos.yml +0 -26
- package/.github/workflows/nodejs-ubuntu.yml +0 -28
- package/.github/workflows/nodejs-windows.yml +0 -26
- package/.github/workflows/release.yml +0 -37
- package/bin/e2e-test.sh +0 -47
- package/docs/adapters/campfire.md +0 -79
- package/docs/adapters/development.md +0 -125
- package/docs/adapters/shell.md +0 -24
- package/docs/adapters.md +0 -27
- package/docs/deploying/azure.md +0 -97
- package/docs/deploying/bluemix.md +0 -111
- package/docs/deploying/heroku.md +0 -66
- package/docs/deploying/unix.md +0 -72
- package/docs/deploying/windows.md +0 -66
- package/docs/deploying.md +0 -11
- package/docs/implementation.md +0 -55
- package/docs/index.md +0 -125
- package/docs/patterns.md +0 -265
- package/docs/scripting.md +0 -1051
- package/examples/hubot-start.ps1 +0 -12
- package/examples/hubot.service +0 -27
- package/script/bootstrap +0 -3
- package/script/release +0 -44
- package/script/server +0 -3
- package/script/smoke-test +0 -3
- package/script/test +0 -3
- package/test/adapter_test.js +0 -97
- package/test/brain_test.js +0 -336
- package/test/datastore_test.js +0 -154
- package/test/es2015_test.js +0 -199
- package/test/fixtures/MockAdapter.coffee +0 -10
- package/test/fixtures/MockAdapter.mjs +0 -43
- package/test/fixtures/TestScript.coffee +0 -9
- package/test/fixtures/TestScript.js +0 -13
- package/test/fixtures/mock-adapter.js +0 -35
- package/test/listener_test.js +0 -379
- package/test/message_test.js +0 -46
- package/test/middleware_test.js +0 -507
- package/test/robot_test.js +0 -1153
- package/test/shell_test.js +0 -73
- package/test/user_test.js +0 -29
package/docs/deploying/heroku.md
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
permalink: /docs/deploying/heroku/
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Deploying to Heroku
|
|
6
|
-
|
|
7
|
-
If you've been following along with [Getting Started](../index.md), it's time to deploy so you can use it beyond just your local machine.
|
|
8
|
-
[Heroku](http://www.heroku.com/) is an easy and supported way to deploy hubot.
|
|
9
|
-
|
|
10
|
-
Install the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) to start, then follow their '[Getting Started](https://devcenter.heroku.com/articles/heroku-cli#getting-started)' instructions, including logging in the first time:
|
|
11
|
-
|
|
12
|
-
% heroku login
|
|
13
|
-
Enter your Heroku credentials.
|
|
14
|
-
Email: youremail@example.com
|
|
15
|
-
Password:
|
|
16
|
-
Could not find an existing public key.
|
|
17
|
-
Would you like to generate one? [Yn]
|
|
18
|
-
Generating new SSH public key.
|
|
19
|
-
Uploading ssh public key /Users/you/.ssh/id_rsa.pub
|
|
20
|
-
|
|
21
|
-
Inside your new hubot directory, make sure you've created a git repository, and that your work is committed:
|
|
22
|
-
|
|
23
|
-
% git init
|
|
24
|
-
% git add .
|
|
25
|
-
% git commit -m "Initial commit"
|
|
26
|
-
|
|
27
|
-
Then create a Heroku application:
|
|
28
|
-
|
|
29
|
-
% heroku create
|
|
30
|
-
Creating rosemary-britches-123... done, stack is cedar
|
|
31
|
-
http://rosemary-britches-123.herokuapp.com/ | git@heroku.com:rosemary-britches-123.git
|
|
32
|
-
Git remote heroku added
|
|
33
|
-
|
|
34
|
-
Before you deploy the application, you'll need to configure some environment
|
|
35
|
-
variables for hubot to use. The specific variables you'll need depends on which
|
|
36
|
-
[adapter](../adapters.md) and scripts you are using. For Campfire, with no other
|
|
37
|
-
scripts, you'd need to set the following environment variables:
|
|
38
|
-
|
|
39
|
-
% heroku config:set HUBOT_CAMPFIRE_ACCOUNT=yourcampfireaccount
|
|
40
|
-
% heroku config:set HUBOT_CAMPFIRE_TOKEN=yourcampfiretoken
|
|
41
|
-
% heroku config:set HUBOT_CAMPFIRE_ROOMS=comma,separated,list,of,rooms,to,join
|
|
42
|
-
|
|
43
|
-
At this point, you are ready to deploy and start chatting. With Heroku, that's a
|
|
44
|
-
git push away:
|
|
45
|
-
|
|
46
|
-
% git push heroku main
|
|
47
|
-
|
|
48
|
-
You'll see some text flying, and eventually some success. You should be able to
|
|
49
|
-
see your bot in your configured chat rooms at this point. If not, you can peek
|
|
50
|
-
at the logs to try to debug:
|
|
51
|
-
|
|
52
|
-
% heroku logs
|
|
53
|
-
|
|
54
|
-
If you make any changes to your hubot, just commit and push them as
|
|
55
|
-
before:
|
|
56
|
-
|
|
57
|
-
% git commit -am "Awesome scripts OMG"
|
|
58
|
-
% git push heroku main
|
|
59
|
-
|
|
60
|
-
Some scripts needs Redis to work, Heroku offers an addon called [Redis Cloud](https://addons.heroku.com/rediscloud), which has a free plan. To use it:
|
|
61
|
-
|
|
62
|
-
% heroku addons:create rediscloud
|
|
63
|
-
|
|
64
|
-
Note: the free redis plans don't offer any persistence so your hubot will lose all the information when it goes to sleep.
|
|
65
|
-
|
|
66
|
-
Free dynos on Heroku will [sleep after 30 minutes of inactivity](https://devcenter.heroku.com/articles/dyno-sleeping). That means your hubot would leave the chat room and only rejoin when it does get traffic. This is extremely inconvenient since most interaction is done through chat, and hubot has to be online and in the room to respond to messages. To get around this, you can use the [hubot-heroku-keepalive](https://github.com/hubot-scripts/hubot-heroku-keepalive) script, which will keep your free dyno alive for up to 18 hours/day. If you never want Hubot to sleep, you will need to [upgrade to Heroku's hobby plan](https://www.heroku.com/pricing).
|
package/docs/deploying/unix.md
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
permalink: /docs/deploying/unix/
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Deploying to Unix
|
|
6
|
-
|
|
7
|
-
Because there are so many variations of Linux, and more generally UNIX, it's
|
|
8
|
-
difficult for the hubot team to have canonical documentation for installing and
|
|
9
|
-
deploying it to every version out there. So, this is an attempt to give an
|
|
10
|
-
overview of what's needed to get deploying.
|
|
11
|
-
|
|
12
|
-
There are 3 primary things to deploying and running hubot:
|
|
13
|
-
|
|
14
|
-
* node and npm
|
|
15
|
-
* a way to get source code updated on the server
|
|
16
|
-
* a way to start hubot, start it up if it crashes, and restart it when code
|
|
17
|
-
updates
|
|
18
|
-
|
|
19
|
-
## node and npm
|
|
20
|
-
|
|
21
|
-
To start, your UNIX server will need node and npm. Check out the node.js wiki
|
|
22
|
-
for [installing Node.js via package manager](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager), [Building on GNU/Linux and other UNIX](https://github.com/joyent/node/wiki/Installation#building-on-gnulinux-and-other-unix).
|
|
23
|
-
|
|
24
|
-
## Updating code on the server
|
|
25
|
-
|
|
26
|
-
The simplest way to update your hubot's code is going to be to have a git
|
|
27
|
-
checkout of your hubot's source code (that you've created during [Getting Started](../index.md), not the [github/hubot repository](http://github.com/github/hubot), and just git pull to get change. This may
|
|
28
|
-
feel a dirty hack, but it works when you are starting out.
|
|
29
|
-
|
|
30
|
-
If you have a Ruby background, you might be more comfortable using
|
|
31
|
-
[capistrano](https://github.com/capistrano/capistrano).
|
|
32
|
-
|
|
33
|
-
If you have a [Chef](http://www.chef.io/chef/) background, there's a
|
|
34
|
-
[deploy](https://docs.chef.io/resource_deploy.html) resource for managing
|
|
35
|
-
deployments.
|
|
36
|
-
|
|
37
|
-
## Starting, stopping, and restarting hubot
|
|
38
|
-
|
|
39
|
-
Every hubot install has a `bin/hubot` script to handle starting up the hubot.
|
|
40
|
-
You can run this command from your git checkout on the server, but there are some problems you can encounter:
|
|
41
|
-
|
|
42
|
-
* you disconnect, and hubot dies
|
|
43
|
-
* hubot dies, for any reason, and doesn't start again
|
|
44
|
-
* it doesn't start up at boot automatically
|
|
45
|
-
|
|
46
|
-
For handling you disconnecting, you can start with running `bin/hubot` in
|
|
47
|
-
[screen session](http://www.gnu.org/software/screen/) or with
|
|
48
|
-
[nohup](http://linux.die.net/man/1/nohup).
|
|
49
|
-
|
|
50
|
-
For handling hubot dying, and restarting it automatically, you can imagine
|
|
51
|
-
running `bin/hubot` in a
|
|
52
|
-
[bash while loop](http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-7.html#ss7.3). But
|
|
53
|
-
really, you probably want some process monitoring using tools like
|
|
54
|
-
[monit](http://mmonit.com/monit/),
|
|
55
|
-
[god](http://godrb.com/),
|
|
56
|
-
[bluepill](https://github.com/arya/bluepill),
|
|
57
|
-
[upstart](http://upstart.ubuntu.com/),
|
|
58
|
-
[runit](http://smarden.org/runit/),
|
|
59
|
-
[systemd](http://freedesktop.org/wiki/Software/systemd/).
|
|
60
|
-
|
|
61
|
-
For starting at boot, you can create an init script appropriate for your UNIX
|
|
62
|
-
distribution. If you are using one of the process monitoring tools above, make
|
|
63
|
-
sure it boots at startup. See the [examples](https://github.com/github/hubot/tree/main/examples)
|
|
64
|
-
for configuration examples.
|
|
65
|
-
|
|
66
|
-
## Recommendations
|
|
67
|
-
|
|
68
|
-
This document has been deliberately light on strong recommendations. At a high
|
|
69
|
-
level though, it's strongly recommended to avoid anything that is overly manual
|
|
70
|
-
and non-repeatable. That would mean using your OS's packages and tools whenever
|
|
71
|
-
possible, and having a proper deploy tool to update hubot, and process
|
|
72
|
-
management to keep hubot running.
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
permalink: /docs/deploying/windows/
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Deploying to Windows
|
|
6
|
-
|
|
7
|
-
Hasn't been fully tested - YMMV
|
|
8
|
-
|
|
9
|
-
There are 4 primary steps to deploying and running hubot on a Windows machine:
|
|
10
|
-
|
|
11
|
-
* node and npm
|
|
12
|
-
* a way to get source code updated on the server
|
|
13
|
-
* setting up environment variables for hubot
|
|
14
|
-
* a way to start hubot, start it up if it crashes, and restart it when code updates
|
|
15
|
-
|
|
16
|
-
## node and npm
|
|
17
|
-
|
|
18
|
-
To start, your windows server will need node and npm.
|
|
19
|
-
The best way to do this is with [chocolatey](http://chocolatey.org) using the [nodejs.install](http://chocolatey.org/packages/nodejs.install) package.
|
|
20
|
-
I've found that sometimes the system path variable is not correctly set; ensure you can run node/npm from the command line. If needed set the PATH variable with `set PATH=%PATH%;\"C:\Program Files\nodejs\"`
|
|
21
|
-
|
|
22
|
-
Your other option is to install directly from [NodeJS](https://nodejs.org/) and run the current download (v0.12.4 as of this documentation). This should set your PATH variables for you.
|
|
23
|
-
|
|
24
|
-
## Updating code on the server
|
|
25
|
-
|
|
26
|
-
To get the code on your server, you can follow the instructions at [Getting Started](../index.md) on your local development machine or directly on the server. If you are building locally, push your hubot to GitHub and clone the repo onto your server. Don't clone the normal [github/hubot repository](http://github.com/github/hubot), make sure you're using the Yo Generator to build your own hubot.
|
|
27
|
-
|
|
28
|
-
## Setting up environment vars
|
|
29
|
-
|
|
30
|
-
You will want to set up your hubot environment variables on the server where it will run. You can do this by opening an administrative PowerShell and typing the following:
|
|
31
|
-
|
|
32
|
-
[Environment]::SetEnvironmentVariable("HUBOT_ADAPTER", "Campfire", "Machine")
|
|
33
|
-
|
|
34
|
-
This is equivalent to going into the system menu -> selecting advanced system settings -> environment vars and adding a new system variable called HUBOT_ADAPTER with the value of Campfire.
|
|
35
|
-
|
|
36
|
-
## Starting, stopping, and restarting hubot
|
|
37
|
-
|
|
38
|
-
Every hubot install has a `bin/hubot` script to handle starting up the hubot.
|
|
39
|
-
You can run this command directly from your hubot folder by typing the following:
|
|
40
|
-
|
|
41
|
-
.\bin\hubot –adapter campfire
|
|
42
|
-
|
|
43
|
-
There are a few issues if you call it manually, though.
|
|
44
|
-
|
|
45
|
-
* you disconnect, and hubot dies
|
|
46
|
-
* hubot dies, for any reason, and doesn't start again
|
|
47
|
-
* it doesn't start up at boot automatically
|
|
48
|
-
|
|
49
|
-
To fix this, you will want to create a .ps1 file with whatever name makes you happy that you will call from your hubot directory. There is a copy of this file in the `examples` directory [here](../../examples/hubot-start.ps1). It should contain the following:
|
|
50
|
-
|
|
51
|
-
Write-Host "Starting Hubot Watcher"
|
|
52
|
-
While (1)
|
|
53
|
-
{
|
|
54
|
-
Write-Host "Starting Hubot"
|
|
55
|
-
Start-Process powershell -ArgumentList ".\bin\hubot –adapter slack" -wait
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
Remember to allow local unsigned PowerShell scripts if you are using the .ps1 file to run hubot. Run this command in an Administrator PowerShell window.
|
|
59
|
-
|
|
60
|
-
Set-ExecutionPolicy RemoteSigned
|
|
61
|
-
|
|
62
|
-
You can set this .ps1 as scheduled task on boot if you like or some other way to start your process.
|
|
63
|
-
|
|
64
|
-
## Expanding the documentation
|
|
65
|
-
|
|
66
|
-
Not yet fleshed out. [Help contribute by submitting a pull request, please?](https://github.com/github/hubot/pull/new/main)
|
package/docs/deploying.md
DELETED
package/docs/implementation.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Implementation Notes
|
|
3
|
-
permalink: /docs/implementation/
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Implementation
|
|
7
|
-
|
|
8
|
-
For the purpose of maintainability, several internal flows are documented here.
|
|
9
|
-
|
|
10
|
-
## Message Processing
|
|
11
|
-
|
|
12
|
-
When a new message is received by an adapter, a new Message object is constructed and passed to `robot.receive` (async). `robot.receive` then attempts to execute each Listener in order of registration by calling `listener.call` (async), passing in the Listener Middleware stack. `listener.call` first checks to see if the listener matches (`match` method, sync), and if so, calls `middleware.execute` (async) on the provided middleware.
|
|
13
|
-
|
|
14
|
-
`middleware.execute` calls each middleware in order of registration. Middleware can either continue forward (call `next`) or abort (call `done`). If all middleware continues, `middleware.execute` calls `next` (the `listener.call` callback). If any middleware aborts, `middleware.execute` calls `done` (which eventually returns to the `robot.receive` callback).
|
|
15
|
-
|
|
16
|
-
`middleware.execute` `next` returns to `listener.call`, which executes the matched Listener's callback and then calls the `robot.receive` callback.
|
|
17
|
-
|
|
18
|
-
Inside the `robot.receive` processing loop, `message.done` is checked after each `listener.call`. If the message has been marked as done, `robot.receive` returns. This correctly handles asynchronous middleware, but will not catch an asynchronous set of `message.done` inside the listener callback (which is expected to be synchronous).
|
|
19
|
-
|
|
20
|
-
If no listener matches the message (distinct from setting `message.done`), a CatchAllMessage is created which wraps the original message. This new message is run through all listeners again testing for a match. `robot.catchAll` creates a special listener that only matches CatchAllMessages.
|
|
21
|
-
|
|
22
|
-
## Listeners
|
|
23
|
-
|
|
24
|
-
Listeners are registered using several functions on the `robot` object: `hear`, `respond`, `enter`, `leave`, `topic`, and `catchAll`.
|
|
25
|
-
|
|
26
|
-
A listener is used via its `call` method, which is responsible for testing to see if a message matches (`match` is an abstract method) and if so, executes the listener's callback.
|
|
27
|
-
|
|
28
|
-
Listener callbacks are assumed to be synchronous.
|
|
29
|
-
|
|
30
|
-
## Middleware
|
|
31
|
-
|
|
32
|
-
There are two primary entry points for middleware:
|
|
33
|
-
|
|
34
|
-
1. `robot.listenerMiddleware` - registers a new piece of middleware in a global array
|
|
35
|
-
2. `middleware.execute` - executes all registered middleware in order
|
|
36
|
-
|
|
37
|
-
## Persistence
|
|
38
|
-
|
|
39
|
-
### Brain
|
|
40
|
-
|
|
41
|
-
Hubot has a memory exposed as the `robot.brain` object that can be used to store and retrieve data.
|
|
42
|
-
Furthermore, Hubot scripts exist to enable persistence across Hubot restarts.
|
|
43
|
-
`hubot-redis-brain` is such a script and uses a backend Redis server.
|
|
44
|
-
|
|
45
|
-
By default, the brain contains a list of all users seen by Hubot.
|
|
46
|
-
Therefore, without persistence across restarts, the brain will contain the list of users encountered so far, during the current run of Hubot. On the other hand, with persistence across restarts, the brain will contain all users encountered by Hubot during all of its runs. This list of users can be accessed through `hubot.brain.users()` and other utility methods.
|
|
47
|
-
|
|
48
|
-
### Datastore
|
|
49
|
-
|
|
50
|
-
Hubot's optional datastore, exposed as the `robot.datastore` object, provides a more robust persistence model. Compared to the brain, the datastore:
|
|
51
|
-
|
|
52
|
-
1. Is always (instead of optionally) backed by a database
|
|
53
|
-
2. Fetches data from the database and stores data in the database on every request, instead of periodically persisting the entire in-memory brain.
|
|
54
|
-
|
|
55
|
-
The datastore is useful in cases where there's a need for greater reassurances of data integrity or in cases where multiple Hubot instances need to access the same database.
|
package/docs/index.md
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
permalink: /docs/
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
## Getting Started With Hubot
|
|
6
|
-
|
|
7
|
-
You will need [node.js and npm](https://docs.npmjs.com/getting-started/installing-node). Once those are installed, we can install the hubot generator:
|
|
8
|
-
|
|
9
|
-
% npm install -g yo generator-hubot
|
|
10
|
-
|
|
11
|
-
This will give us the `hubot` [yeoman](http://yeoman.io/) generator. Now we
|
|
12
|
-
can make a new directory, and generate a new instance of hubot in it. For example, if
|
|
13
|
-
we wanted to make a bot called myhubot:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
% mkdir myhubot
|
|
17
|
-
% cd myhubot
|
|
18
|
-
% yo hubot
|
|
19
|
-
|
|
20
|
-
At this point, you'll be asked a few questions about who is creating the bot,
|
|
21
|
-
and which [adapter](adapters.md) you'll be using. Adapters are hubot's
|
|
22
|
-
way of integrating with different chat providers.
|
|
23
|
-
|
|
24
|
-
If you are using git, the generated directory includes a .gitignore, so you can
|
|
25
|
-
initialize and add everything:
|
|
26
|
-
|
|
27
|
-
% git init
|
|
28
|
-
% git add .
|
|
29
|
-
% git commit -m "Initial commit"
|
|
30
|
-
|
|
31
|
-
If you'd prefer to automate your hubot build without being interactively
|
|
32
|
-
prompted for its configuration, you can add the following options
|
|
33
|
-
to the `yo hubot` command to do so:
|
|
34
|
-
|
|
35
|
-
| Option | Description |
|
|
36
|
-
|:--------------------------------------------|:-------------------------------------------------------|
|
|
37
|
-
| `--owner="Bot Wrangler <bw@example.com>"` | Bot owner, e.g. "Bot Wrangler <bw@example.com>" |
|
|
38
|
-
| `--name="Hubot"` | Bot name, e.g. "Hubot" |
|
|
39
|
-
| `--description="Delightfully aware robutt"` | Bot description, e.g. "Delightfully aware robutt" |
|
|
40
|
-
| `--adapter=campfire` | Bot adapter, e.g. "campfire" |
|
|
41
|
-
| `--defaults` | Declare all defaults are set and no prompting required |
|
|
42
|
-
|
|
43
|
-
You now have your own functional hubot! There's a `bin/hubot`
|
|
44
|
-
command for convenience, to handle installing npm dependencies, loading scripts,
|
|
45
|
-
and then launching your hubot.
|
|
46
|
-
|
|
47
|
-
Note: Hubot can use Redis to persist data, so before you can start hubot on your own computer, if you want to persist data, then you should have Redis running on your machine accessible via `localhost`. Then, ensure that `hubot-redis-brain` is listed in `external-scripts.json` as an `Array` of module names (e.g. `['hubot-redis-brain']`) or an `object` where the key is the name of the module (e.g. `{'hubot-redis-brain': 'some arbitrary value'}`) where the value of the property in the object is passed to the module function as the second argument. The first argument being the hubot Robot instance.
|
|
48
|
-
|
|
49
|
-
% bin/hubot
|
|
50
|
-
Hubot>
|
|
51
|
-
|
|
52
|
-
This starts hubot using the [shell adapter](./adapters/shell.md), which is mostly useful for development. Make note of the name in the `hubot>` prompt; this is the name your hubot will respond to with commands. If the prompt reads `myhubot>` then your commands must start with `myhubot <command>`.
|
|
53
|
-
|
|
54
|
-
For example, to list available commands:
|
|
55
|
-
|
|
56
|
-
% bin/hubot
|
|
57
|
-
myhubot> myhubot help
|
|
58
|
-
myhubot> Shell: myhubot adapter - Reply with the adapter
|
|
59
|
-
myhubot animate me <query> - The same thing as `image me`, except adds a few parameters to try to return an animated GIF instead.
|
|
60
|
-
myhubot echo <text> - Reply back with <text>
|
|
61
|
-
myhubot help - Displays all of the help commands that Hubot knows about.
|
|
62
|
-
myhubot help <query> - Displays all help commands that match <query>.
|
|
63
|
-
myhubot image me <query> - The Original. Queries Google Images for <query> and returns a random top result.
|
|
64
|
-
myhubot map me <query> - Returns a map view of the area returned by `query`.
|
|
65
|
-
myhubot mustache me <url|query> - Adds a mustache to the specified URL or query result.
|
|
66
|
-
myhubot ping - Reply with pong
|
|
67
|
-
myhubot pug bomb N - get N pugs
|
|
68
|
-
myhubot pug me - Receive a pug
|
|
69
|
-
myhubot the rules - Make sure hubot still knows the rules.
|
|
70
|
-
myhubot time - Reply with current time
|
|
71
|
-
myhubot translate me <phrase> - Searches for a translation for the <phrase> and then prints that bad boy out.
|
|
72
|
-
myhubot translate me from <source> into <target> <phrase> - Translates <phrase> from <source> into <target>. Both <source> and <target> are optional
|
|
73
|
-
ship it - Display a motivation squirrel
|
|
74
|
-
|
|
75
|
-
You almost definitely will want to change your hubot's name to add character. bin/hubot takes a `--name`:
|
|
76
|
-
|
|
77
|
-
% bin/hubot --name sam
|
|
78
|
-
sam>
|
|
79
|
-
|
|
80
|
-
Your hubot will now respond as `sam`. This is
|
|
81
|
-
case-insensitive, and can be prefixed with `@` or suffixed with `:`. These are equivalent:
|
|
82
|
-
|
|
83
|
-
SAM help
|
|
84
|
-
sam help
|
|
85
|
-
@sam help
|
|
86
|
-
sam: help
|
|
87
|
-
|
|
88
|
-
## Scripts
|
|
89
|
-
|
|
90
|
-
Hubot's power comes through scripts. There are hundreds of scripts written and maintained by the community. Find them by searching the [NPM registry](https://www.npmjs.com/browse/keyword/hubot-scripts) for `hubot-scripts <your-search-term>`. For example:
|
|
91
|
-
|
|
92
|
-
```
|
|
93
|
-
$ npm search hubot-scripts github
|
|
94
|
-
NAME DESCRIPTION
|
|
95
|
-
hubot-deployer Giving Hubot the ability to deploy GitHub repos to PaaS providers hubot hubot-scripts hubot-gith
|
|
96
|
-
hubot-gh-release-pr A hubot script to create GitHub's PR for release
|
|
97
|
-
hubot-github Giving Hubot the ability to be a vital member of your github organization
|
|
98
|
-
…
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
To use a script from an NPM package:
|
|
102
|
-
|
|
103
|
-
1. Run `npm install --save <package-name>` to add the package as a dependency and install it.
|
|
104
|
-
2. Add the package to `external-scripts.json`.
|
|
105
|
-
3. Run `npm home <package-name>` to open a browser window for the homepage of the script, where you can find more information about configuring and installing the script.
|
|
106
|
-
|
|
107
|
-
You can also put your own scripts under the `scripts/` directory. All scripts (files ending with either `.js` or `.mjs`) placed there are automatically loaded and ready to use with your hubot. Read more about customizing hubot by [writing your own scripts](scripting.md).
|
|
108
|
-
|
|
109
|
-
## Adapters
|
|
110
|
-
|
|
111
|
-
Hubot uses the adapter pattern to support multiple chat-backends. Here is a [list of available adapters](adapters.md), along with details on how to configure them.
|
|
112
|
-
|
|
113
|
-
## Deploying
|
|
114
|
-
|
|
115
|
-
You can deploy hubot to Heroku, which is the officially supported method. Additionally you are able to deploy hubot to a UNIX-like system or Windows. Please note the support for deploying to Windows isn't officially supported.
|
|
116
|
-
|
|
117
|
-
* [Deploying Hubot onto Azure](./deploying/azure.md)
|
|
118
|
-
* [Deploying Hubot onto Bluemix](./deploying/bluemix.md)
|
|
119
|
-
* [Deploying Hubot onto Heroku](./deploying/heroku.md)
|
|
120
|
-
* [Deploying Hubot onto Unix](./deploying/unix.md)
|
|
121
|
-
* [Deploying Hubot onto Windows](./deploying/windows.md)
|
|
122
|
-
|
|
123
|
-
## Patterns
|
|
124
|
-
|
|
125
|
-
Using custom scripts, you can quickly customize Hubot to be the most life embettering robot he or she can be. Read [docs/patterns.md](patterns.md) for some nifty tricks that may come in handy as you teach your hubot new skills.
|
package/docs/patterns.md
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
permalink: /docs/patterns/
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Patterns
|
|
6
|
-
|
|
7
|
-
Shared patterns for dealing with common Hubot scenarios.
|
|
8
|
-
|
|
9
|
-
## Renaming the Hubot instance
|
|
10
|
-
|
|
11
|
-
When you rename Hubot, he will no longer respond to his former name. In order to train your users on the new name, you may choose to add a deprecation notice when they try to say the old name. The pattern logic is:
|
|
12
|
-
|
|
13
|
-
* listen to all messages that start with the old name
|
|
14
|
-
* reply to the user letting them know about the new name
|
|
15
|
-
|
|
16
|
-
Setting this up is very easy:
|
|
17
|
-
|
|
18
|
-
1. Create a [bundled script](scripting.md) in the `scripts/` directory of your Hubot instance called `rename-hubot.js`
|
|
19
|
-
2. Add the following code, modified for your needs:
|
|
20
|
-
|
|
21
|
-
```javascript
|
|
22
|
-
// Description:
|
|
23
|
-
// Tell people hubot's new name if they use the old one
|
|
24
|
-
|
|
25
|
-
// Commands:
|
|
26
|
-
// None
|
|
27
|
-
|
|
28
|
-
module.exports = (robot) => {
|
|
29
|
-
robot.hear(/^hubot:? (.+)/i, (res) => {
|
|
30
|
-
let response = `Sorry, I'm a diva and only respond to ${robot.name}`
|
|
31
|
-
response += robot.alias ? ` or ${robot.alias}` : ''
|
|
32
|
-
return res.reply(response)
|
|
33
|
-
})
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
In the above pattern, modify both the hubot listener and the response message to suit your needs.
|
|
38
|
-
|
|
39
|
-
Also, it's important to note that the listener should be based on what hubot actually hears, instead of what is typed into the chat program before the Hubot Adapter has processed it. For example, the [HipChat Adapter](https://github.com/hipchat/hubot-hipchat) converts `@hubot` into `hubot:` before passing it to Hubot.
|
|
40
|
-
|
|
41
|
-
## Deprecating or Renaming Listeners
|
|
42
|
-
|
|
43
|
-
If you remove a script or change the commands for a script, it can be useful to let your users know about the change. One way is to just tell them in chat or let them discover the change by attempting to use a command that no longer exists. Another way is to have Hubot let people know when they've used a command that no longer works.
|
|
44
|
-
|
|
45
|
-
This pattern is similar to the Renaming the Hubot Instance pattern above:
|
|
46
|
-
|
|
47
|
-
* listen to all messages that match the old command
|
|
48
|
-
* reply to the user letting them know that it's been deprecated
|
|
49
|
-
|
|
50
|
-
Here is the setup:
|
|
51
|
-
|
|
52
|
-
1. Create a [bundled script](scripting.md) in the `scripts/` directory of your Hubot instance called `deprecations.js`
|
|
53
|
-
2. Copy any old command listeners and add them to that file. For example, if you were to rename the help command for some silly reason:
|
|
54
|
-
|
|
55
|
-
```javascript
|
|
56
|
-
// Description:
|
|
57
|
-
// Tell users when they have used commands that are deprecated or renamed
|
|
58
|
-
//
|
|
59
|
-
// Commands:
|
|
60
|
-
// None
|
|
61
|
-
//
|
|
62
|
-
module.exports = (robot) => {
|
|
63
|
-
robot.respond(/help\s*(.*)?$/i, (res) => {
|
|
64
|
-
return res.reply('That means nothing to me anymore. Perhaps you meant "docs" instead?')
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Preventing Hubot from Running Scripts Concurrently
|
|
71
|
-
|
|
72
|
-
Sometimes you have scripts that take several minutes to execute. If these scripts are doing something that could be interfered with by running subsequent commands, you may wish to code your scripts to prevent concurrent access.
|
|
73
|
-
|
|
74
|
-
To do this, you can set up a lock in the Hubot [brain](scripting.md#persistence) object. The lock is set up here so that different scripts can share the same lock if necessary.
|
|
75
|
-
|
|
76
|
-
Setting up the lock looks something like this:
|
|
77
|
-
|
|
78
|
-
```javascript
|
|
79
|
-
module.exports = (robot) => {
|
|
80
|
-
robot.brain.on('loaded', ()=>{
|
|
81
|
-
// Clear the lock on startup in case Hubot has restarted and Hubot's brain has persistence (e.g. redis).
|
|
82
|
-
// We don't want any orphaned locks preventing us from running commands.
|
|
83
|
-
robot.brain.remove('yourLockName')
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
robot.respond(/longrunningthing/i, (msg) => {
|
|
87
|
-
const lock = robot.brain.get('yourLockName')
|
|
88
|
-
if (lock) {
|
|
89
|
-
return msg.send(`I'm sorry, ${msg.message.user.name}, I'm afraid I can't do that. I'm busy doing something for ${lock.user.name}.`)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
robot.brain.set('yourLockName', msg.message) // includes user, room, etc about who locked
|
|
93
|
-
|
|
94
|
-
yourLongClobberingAsyncThing(err, res).then(
|
|
95
|
-
// Clear the lock
|
|
96
|
-
robot.brain.remove('yourLockName')
|
|
97
|
-
msg.reply('Finally Done')
|
|
98
|
-
)).catch(e => console.error(e))
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Forwarding all HTTP requests through a proxy
|
|
103
|
-
|
|
104
|
-
In many corporate environments, a web proxy is required to access the Internet and/or protected resources. For one-off control, use can specify an [Agent](https://nodejs.org/api/http.html) to use with `robot.http`. However, this would require modifying every script your robot uses to point at the proxy. Instead, you can specify the agent at the global level and have all HTTP requests use the agent by default.
|
|
105
|
-
|
|
106
|
-
Due to the way Node.js handles HTTP and HTTPS requests, you need to specify a different Agent for each protocol. ScopedHTTPClient will then automatically choose the right ProxyAgent for each request.
|
|
107
|
-
|
|
108
|
-
1. Install ProxyAgent. `npm install proxy-agent`
|
|
109
|
-
2. Create a [bundled script](scripting.md) in the `scripts/` directory of your Hubot instance called `proxy.js`
|
|
110
|
-
3. Add the following code, modified for your needs:
|
|
111
|
-
|
|
112
|
-
```javascript
|
|
113
|
-
const proxy = require('proxy-agent')
|
|
114
|
-
module.exports = (robot) => {
|
|
115
|
-
robot.globalHttpOptions.httpAgent = proxy('http://my-proxy-server.internal', false)
|
|
116
|
-
robot.globalHttpOptions.httpsAgent = proxy('http://my-proxy-server.internal', true)
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Dynamic matching of messages
|
|
121
|
-
|
|
122
|
-
In some situations, you want to dynamically match different messages (e.g. factoids, JIRA projects). Rather than defining an overly broad regular expression that always matches, you can tell Hubot to only match when certain conditions are met.
|
|
123
|
-
|
|
124
|
-
In a simple robot, this isn't much different from just putting the conditions in the Listener callback, but it makes a big difference when you are dealing with middleware: with the basic model, middleware will be executed for every match of the generic regex. With the dynamic matching model, middleware will only be executed when the dynamic conditions are matched.
|
|
125
|
-
|
|
126
|
-
For example, the [factoid lookup command](https://github.com/github/hubot-scripts/blob/bd810f99f9394818a9dcc2ea3729427e4101b96d/src/scripts/factoid.coffee#L95-L99) could be reimplemented as:
|
|
127
|
-
|
|
128
|
-
```javascript
|
|
129
|
-
// use case: Hubot>fact1
|
|
130
|
-
// This listener doesn't require you to type the bot's name first
|
|
131
|
-
|
|
132
|
-
const {TextMessage} = require('../src/message')
|
|
133
|
-
module.exports = (robot) => {
|
|
134
|
-
// Dynamically populated list of factoids
|
|
135
|
-
const facts = {
|
|
136
|
-
fact1: 'stuff',
|
|
137
|
-
fact2: 'other stuff'
|
|
138
|
-
}
|
|
139
|
-
robot.listen(
|
|
140
|
-
// Matcher
|
|
141
|
-
(message) => {
|
|
142
|
-
// Check that message is a TextMessage type because
|
|
143
|
-
// if there is no match, this matcher function will
|
|
144
|
-
// be called again but the message type will be CatchAllMessage
|
|
145
|
-
// which doesn't have a `match` method.
|
|
146
|
-
if(!(message instanceof TextMessage)) return false
|
|
147
|
-
const match = message.match(/^(.*)$/)
|
|
148
|
-
// Only match if there is a matching factoid
|
|
149
|
-
if (match && match[1] in facts) {
|
|
150
|
-
return match[1]
|
|
151
|
-
} else {
|
|
152
|
-
return false
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
// Callback
|
|
156
|
-
(res) => {
|
|
157
|
-
const fact = res.match
|
|
158
|
-
res.reply(`${fact} is ${facts[fact]}`)
|
|
159
|
-
}
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## Restricting access to commands
|
|
165
|
-
|
|
166
|
-
One of the awesome features of Hubot is its ability to make changes to a production environment with a single chat message. However, not everyone with access to your chat service should be able to trigger production changes.
|
|
167
|
-
|
|
168
|
-
There are a variety of different patterns for restricting access that you can follow depending on your specific needs:
|
|
169
|
-
|
|
170
|
-
* Two buckets of access: full and restricted with include/exclude list
|
|
171
|
-
* Specific access rules for every command (Role-based Access Control)
|
|
172
|
-
* Include/exclude listing commands in specific rooms
|
|
173
|
-
|
|
174
|
-
### Simple per-listener access
|
|
175
|
-
|
|
176
|
-
In some organizations, almost all employees are given the same level of access and only a select few need to be restricted (e.g. new hires, contractors, etc.). In this model, you partition the set of all listeners to separate the "power commands" from the "normal commands".
|
|
177
|
-
|
|
178
|
-
Once you have segregated the listeners, you need to make some tradeoff decisions around include/exclude users and listeners.
|
|
179
|
-
|
|
180
|
-
The key deciding factors for inclusion vs exclusion of users are the number of users in each category, the frequency of change in either category, and the level of security risk your organization is willing to accept.
|
|
181
|
-
|
|
182
|
-
* Including users (users X, Y, Z have access to power commands; all other users only get access to normal commands) is a more secure method of access (new users have no default access to power commands), but has higher maintenance overhead (you need to add each new user to the "include" list).
|
|
183
|
-
* Excluding users (all users get access to power commands, except for users X, Y, Z, who only get access to normal commands) is a less secure method (new users have default access to power commands until they are added to the exclusion list), but has a much lower maintenance overhead if the exclusion list is small/rarely updated.
|
|
184
|
-
|
|
185
|
-
The key deciding factors for selectively allowing vs restricting listeners are the number of listeners in each category, the ratio of internal to external scripts, and the level of security risk your organization is willing to accept.
|
|
186
|
-
|
|
187
|
-
* Selectively allowing listeners (all listeners are power commands, except for listeners A, B, C, which are considered normal commands) is a more secure method (new listeners are restricted by default), but has a much higher maintenance overhead (every silly/fun listener needs to be explicity downgraded to "normal" status).
|
|
188
|
-
* Selectively restricting listeners (listeners A, B, C are power commands, everything else is a normal command) is a less secure method (new listeners are put into the normal category by default, which could give unexpected access; external scripts are particularly risky here), but has a lower maintenance overhead (no need to modify/enumerate all the fun/culture scripts in your access policy).
|
|
189
|
-
|
|
190
|
-
As an additional consideration, most scripts do not currently have listener IDs, so you will likely need to open PRs (or fork) any external scripts you use to add listener IDs. The actual modification is easy, but coordinating with lots of maintainers can be time consuming.
|
|
191
|
-
|
|
192
|
-
Once you have decided which of the four possible models to follow, you need to build the appropriate lists of users and listeners to plug into your authorization middleware.
|
|
193
|
-
|
|
194
|
-
Example: inclusion list of users given access to selectively restricted power commands
|
|
195
|
-
|
|
196
|
-
```javascript
|
|
197
|
-
const POWER_COMMANDS = [
|
|
198
|
-
'deploy.web' // String that matches the listener ID
|
|
199
|
-
]
|
|
200
|
-
|
|
201
|
-
// Change name to something else to see it reject the command.
|
|
202
|
-
const POWER_USERS = [
|
|
203
|
-
'Shell' // String that matches the user ID set by the adapter
|
|
204
|
-
]
|
|
205
|
-
|
|
206
|
-
module.exports = (robot) => {
|
|
207
|
-
robot.listenerMiddleware((context, next, done) => {
|
|
208
|
-
if (POWER_COMMANDS.indexOf(context.listener.options.id) > -1) {
|
|
209
|
-
if (POWER_USERS.indexOf(context.response.message.user.name) > -1){
|
|
210
|
-
// User is allowed access to this command
|
|
211
|
-
next()
|
|
212
|
-
} else {
|
|
213
|
-
// Restricted command, but user isn't in whitelist
|
|
214
|
-
context.response.reply(`I'm sorry, @${context.response.message.user.name}, but you don't have access to do that.`)
|
|
215
|
-
done()
|
|
216
|
-
}
|
|
217
|
-
} else {
|
|
218
|
-
// This is not a restricted command; allow everyone
|
|
219
|
-
next()
|
|
220
|
-
}
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
robot.listen(message => {
|
|
224
|
-
return true
|
|
225
|
-
}, {id: 'deploy.web'},
|
|
226
|
-
res => {
|
|
227
|
-
res.reply('Deploying web...')
|
|
228
|
-
})
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
Remember that middleware executes for ALL listeners that match a given message (including `robot.hear(/.+/)`), so make sure you include them when categorizing your listeners.
|
|
233
|
-
|
|
234
|
-
### Specific access rules per listener
|
|
235
|
-
|
|
236
|
-
For larger organizations, a binary categorization of access is usually insufficient and more complex access rules are required.
|
|
237
|
-
|
|
238
|
-
Example access policy:
|
|
239
|
-
* Each development team has access to cut releases and deploy their service
|
|
240
|
-
* The Operations group has access to deploy all services (but not cut releases)
|
|
241
|
-
* The front desk cannot cut releases nor deploy services
|
|
242
|
-
|
|
243
|
-
Complex policies like this are currently best implemented in code directly.
|
|
244
|
-
|
|
245
|
-
### Specific access rules per room
|
|
246
|
-
|
|
247
|
-
Organizations that have a number of chat rooms that serve different purposes often want to be able to use the same instance of hubot but have a different set of commands allowed in each room.
|
|
248
|
-
|
|
249
|
-
Work on generalized exlusion list solution is [ongoing](https://github.com/kristenmills/hubot-command-blacklist). An inclusive list soultion could take a similar approach.
|
|
250
|
-
|
|
251
|
-
## Use scoped npm packages as adapter
|
|
252
|
-
|
|
253
|
-
It is possible to [install](https://docs.npmjs.com/cli/v7/commands/npm-install) package under a custom alias:
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
npm install <alias>@npm:<name>
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
So for example to use `@foo/hubot-adapter` package as the adapter, you can:
|
|
260
|
-
|
|
261
|
-
```bash
|
|
262
|
-
npm install hubot-foo@npm:@foo/hubot-adapter
|
|
263
|
-
|
|
264
|
-
bin/hubot --adapter foo
|
|
265
|
-
```
|