Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Port to javascript #652

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/maintainers_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ All you need to work with this project is a supported version of [Node.js](https

This package has unit tests for most modules (files) in the `test` directory. The structure mirrors the `src` directory closely. You can run the entire test suite using the npm script `npm test`. This command is also executed by Travis, the continuous integration service, for every Pull Request and branch. The coverage is computed with the `istanbul` package. The tests themselves are run using the `mocha` test runner.

Test code should be written in syntax that runs on the oldest supported Node.js version in the oldest supported
coffeescript syntax. This ensures that backwards compatibility is tested and the APIs look reasonable in versions of Node.js that do not support the most modern syntax.
Test code should be written in syntax that runs on the oldest supported Node.js version. This ensures that backwards compatibility is tested and the APIs look reasonable in versions of Node.js that do not support the most modern syntax.

### Generating Documentation

Expand Down
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ignore:
- slack.coffee
- slack.js
- test/*
coverage:
status:
Expand Down
4 changes: 2 additions & 2 deletions docs/_pages/advanced_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ and using it to initialize a new `WebClient` object, as described in
example, if you put the new token in an environment variable called `SLACK_OAUTH_TOKEN`, you'd simply change the
initialization of the `WebClient` object to the following:

```coffeescript
web = new WebClient process.env.SLACK_OAUTH_TOKEN
```javascript
const web = new WebClient(process.env.SLACK_OAUTH_TOKEN)
```

## Running Hubot behind an HTTP Proxy
Expand Down
241 changes: 121 additions & 120 deletions docs/_pages/basic_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@ Nevertheless, we'll cover the basics, as well as some interesting Slack-specific
You can listen for messages in any channel that your Hubot has been invited into very simply, by using `robot.hear` with
a RegExp to match against. Any message that matches the RegExp will trigger the function.

```coffeescript
module.exports = (robot) ->

# Any message that contains "badger" will trigger the following function
robot.hear /badger/i, (res) ->

# res.message is a SlackTextMessage instance that represents the incoming message Hubot just heard
robot.logger.debug "Received message #{res.message.text}"
```javascript
module.exports = robot => // Any message that contains "badger" will trigger the following function
robot.hear(
/badger/i,
res => // res.message is a SlackTextMessage instance that represents the incoming message Hubot just heard
robot.logger.debug(`Received message ${res.message.text}`)
);
```

Hubot will only hear messages in converastions where it is a member. A human must invite Hubot into conversations
Expand All @@ -48,13 +47,13 @@ Hubot will only hear messages in converastions where it is a member. A human mus
If you want to specifically listen for messages that mention your Hubot, use the `robot.respond` method. You can also
be more specific using a RegExp (or don't be more specific, `/.*/` will match all messages).

```coffeescript
module.exports = (robot) ->

# Any message that contains "badger" and is directed at Hubot (in a DM or starting with its name)
# will trigger the following function
robot.respond /badger/i, (res) ->
robot.logger.debug "Received message #{res.message.text}"
```javascript
module.exports = robot => // Any message that contains "badger" and is directed at Hubot (in a DM or starting with its name)
// will trigger the following function
robot.respond(
/badger/i,
res => robot.logger.debug(`Received message ${res.message.text}`)
);
```

--------
Expand All @@ -64,12 +63,9 @@ module.exports = (robot) ->
Responding to a message is straightforward, regardless of whether the message was sent to your Hubot specifically or to
anyone in general.

```coffeescript
module.exports = (robot) ->

robot.hear /badger/i, (res) ->
# Hubot sends a response to the same channel it heard the incoming message
res.send "Yes, more badgers please!"
```javascript
module.exports = robot => robot.hear(/badger/i, res => // Hubot sends a response to the same channel it heard the incoming message
res.send("Yes, more badgers please!"));
```

--------
Expand All @@ -85,28 +81,32 @@ write mentions that have the user's preferred display name.
Each incoming message has a `mentions` array that contains the ID and any other information known about the user or
conversation that was mentioned.

```coffeescript
module.exports = (robot) ->
# A map of user IDs to scores
thank_scores = {}

robot.hear /thanks/i, (res) ->
# filter mentions to just user mentions
user_mentions = (mention for mention in res.message.mentions when mention.type is "user")

# when there are user mentions...
if user_mentions.length > 0
response_text = ""

# process each mention
for { id } in user_mentions
# increment the thank score
thank_scores[id] = if thank_scores[id]? then (thank_scores[id] + 1) else 1
# show the total score in the message with a properly formatted mention (uses display name)
response_text += "<@#{id}> has been thanked #{thank_scores[id]} times!\n"

# send the response
res.send response_text
```javascript
mmodule.exports = function(robot) {
// A map of user IDs to scores
const thank_scores = {};

return robot.hear(/thanks/i, function(res) {
// filter mentions to just user mentions
const user_mentions = (Array.from(res.message.mentions).filter((mention) => mention.type === "user"));

// when there are user mentions...
if (user_mentions.length > 0) {
let response_text = "";

// process each mention
for (let { id } of Array.from(user_mentions)) {
// increment the thank score
thank_scores[id] = (thank_scores[id] != null) ? (thank_scores[id] + 1) : 1;
// show the total score in the message with a properly formatted mention (uses display name)
response_text += `<@${id}> has been thanked ${thank_scores[id]} times!\n`;
}

// send the response
return res.send(response_text);
}
});
};
```

--------
Expand All @@ -122,29 +122,29 @@ npm install --save @slack/client

Next, modify your script to instatiate a `WebClient` object using the same token your Hubot used to connect.

```coffeescript
# Import the Slack Developer Kit
{WebClient} = require "@slack/client"

module.exports = (robot) ->
web = new WebClient robot.adapter.options.token
```javascript
// Import the Slack Developer Kit
const {WebClient} = require("@slack/client");

# remainder of your script...
module.exports = function(robot) {
let web;
return web = new WebClient(robot.adapter.options.token);
};
```

Finally, anytime you'd like to call a Web API method, call it like a method on `web`.

```coffeescript
# Import the Slack Developer Kit
{WebClient} = require "@slack/client"
```javascript
// Import the Slack Developer Kit
const {WebClient} = require("@slack/client");

module.exports = (robot) ->
web = new WebClient robot.adapter.options.token
module.exports = function(robot) {
const web = new WebClient(robot.adapter.options.token);

robot.hear /test/i, (res) ->
web.api.test()
.then () -> res.send "Your connection to the Slack API is working!"
.catch (error) -> res.send "Your connection to the Slack API failed :("
return robot.hear(/test/i, res => web.api.test()
.then(() => res.send("Your connection to the Slack API is working!"))
.catch(error => res.send("Your connection to the Slack API failed :(")));
};
```

You only have access to the Web API methods that your bot token is authorized to use. Depending on
Expand All @@ -164,17 +164,17 @@ directly designed to support. However, with a little bit of knowledge about the
new threads and send messages into a thread using Hubot.


```coffeescript
module.exports = (robot) ->

robot.hear /badger/i, (res) ->
if res.message.thread_ts?
# The incoming message was inside a thread, responding normally will continue the thread
res.send "Did someone say BADGER?"
else
# The incoming message was not inside a thread, so lets respond by creating a new thread
res.message.thread_ts = res.message.rawMessage.ts
res.send "Slight digression, we need to talk about these BADGERS"
```javascript
module.exports = robot => robot.hear(/badger/i, function(res) {
if (res.message.thread_ts != null) {
// The incoming message was inside a thread, responding normally will continue the thread
return res.send("Did someone say BADGER?");
} else {
// The incoming message was not inside a thread, so lets respond by creating a new thread
res.message.thread_ts = res.message.rawMessage.ts;
return res.send("Slight digression, we need to talk about these BADGERS");
}
});
```

If you want to use the `reply_broadcast` feature of threads, you'll have to
Expand All @@ -189,22 +189,26 @@ Of course, Slack is more than just text messages. Users can send
can both listen for these from other users, and send reactions of its own. Here is a recipe to listen for
emoji reactions and add the same reaction back to the same message.

```coffeescript
{WebClient} = require "@slack/client"
```javascript
const {WebClient} = require("@slack/client");

module.exports = (robot) ->
web = new WebClient robot.adapter.options.token
module.exports = function(robot) {
const web = new WebClient(robot.adapter.options.token);

robot.react (res) ->
return robot.react(function(res) {

# res.message is a ReactionMessage instance that represents the reaction Hubot just heard
if res.message.type == "added" and res.message.item.type == "message"
// res.message is a ReactionMessage instance that represents the reaction Hubot just heard
if ((res.message.type === "added") && (res.message.item.type === "message")) {

# res.messsage.reaction is the emoji alias for the reaction Hubot just heard
web.reactions.add
// res.messsage.reaction is the emoji alias for the reaction Hubot just heard
return web.reactions.add({
name: res.message.reaction,
channel: res.message.item.channel,
timestamp: res.message.item.ts
});
}
});
};
```

When using `robot.react` as shown above, the `res.message` value is of type `ReactionMessage`. In addition to the normal
Expand All @@ -225,16 +229,15 @@ message properties, this type has a few really helpful properties you might want

Each time a user changes from away to active, or vice-versa, Hubot can listen that event.

```coffeescript
module.exports = (robot) ->

robot.presenceChange (res) ->
```javascript
module.exports = robot => robot.presenceChange(function(res) {

# res.message is a PresenceMessage instance that represents the presence change Hubot just heard
names = (user.name for user in res.message.users).join ", "
// res.message is a PresenceMessage instance that represents the presence change Hubot just heard
const names = (Array.from(res.message.users).map((user) => user.name)).join(", ");

message = if res.message.presence is "away" then "Bye bye #{names}" else "Glad you are back #{names}"
robot.logger.debug message
const message = res.message.presence === "away" ? `Bye bye ${names}` : `Glad you are back ${names}`;
return robot.logger.debug(message);
});
```

--------
Expand All @@ -249,34 +252,33 @@ we use the Web API to translate to default named channel to an ID and then store
set up that can update that variable based on an incoming message. Then a new listener is used to send data into
the current channel in the variable, no matter where the incoming message is received.

```coffeescript
{WebClient} = require "@slack/client"
```javascript
const {WebClient} = require("@slack/client");

module.exports = (robot) ->
web = new WebClient robot.adapter.options.token
module.exports = function(robot) {
const web = new WebClient(robot.adapter.options.token);

# When the script starts up, there is no notification room
notification_room = undefined
// When the script starts up, there is no notification room
let notification_room = undefined;

# Immediately, a request is made to the Slack Web API to translate a default channel name into an ID
default_channel_name = "general"
// Immediately, a request is made to the Slack Web API to translate a default channel name into an ID
const default_channel_name = "general";
web.channels.list()
.then (api_response) ->
# List is searched for the channel with the right name, and the notification_room is updated
room = api_response.channels.find (channel) -> channel.name is default_channel_name
notification_room = room.id if room?

# NOTE: for workspaces with a large number of channels, this result in a timeout error. Use pagination.
.catch (error) -> robot.logger.error error.message

# Any message that says "send updates here" will change the notification room
robot.hear /send updates here/i, (res) ->
notification_room = res.message.rawMessage.channel.id

# Any message that says "my update" will cause Hubot to echo that message to the notification room
robot.hear /my update/i, (res) ->
if notification_room?
robot.messageRoom(notification_room, "An update from: <@#{res.message.user.id}>: '#{res.message.text}'")
.then(function(api_response) {
// List is searched for the channel with the right name, and the notification_room is updated
const room = api_response.channels.find(channel => channel.name === default_channel_name);
if (room != null) { return notification_room = room.id; }}).catch(error => robot.logger.error(error.message));

// Any message that says "send updates here" will change the notification room
robot.hear(/send updates here/i, res => notification_room = res.message.rawMessage.channel.id);

// Any message that says "my update" will cause Hubot to echo that message to the notification room
return robot.hear(/my update/i, function(res) {
if (notification_room != null) {
return robot.messageRoom(notification_room, `An update from: <@${res.message.user.id}>: '${res.message.text}'`);
}
});
};
```

--------
Expand All @@ -288,14 +290,13 @@ and mentions. This formatting sometimes removes meaningful information from the
unaltered text in the incoming message, you can use the `rawText` property. Similarly, if you need to access any other
property of the incoming Slack message, use the `rawMessage` property.

```coffeescript
module.exports = (robot) ->

# listen to all incoming messages
robot.hear /.*/, (res) ->
# find URLs in the rawText
urls = res.message.rawText.match /https?:\/\/[^\|>\s]+/gi
```javascript
module.exports = robot => // listen to all incoming messages
robot.hear(/.*/, function(res) {
// find URLs in the rawText
const urls = res.message.rawText.match(/https?:\/\/[^\|>\s]+/gi);

# log each link found
robot.logger.debug ("link shared: #{url}" for url in urls).join("\n") if urls
// log each link found
if (urls) { return robot.logger.debug((Array.from(urls).map((url) => `link shared: ${url}`)).join("\n")); }
});
```
23 changes: 11 additions & 12 deletions docs/_pages/upgrading.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@ shown in
2. Version 3 of {{ site.product_name }} supported attachments by emitting a `slack.attachment` event. In version 4, you
use `msg.send`, passing an object with an `attachments` array:

```coffeescript
robot.respond /send attachments/i, (msg) ->
msg.send(
attachments: [
{
text: '*error*: something bad happened'
fallback: 'error: something bad happened'
color: 'danger'
mrkdwn_in: ['text']
}
]
)
```javascript
robot.respond(/send attachments/i, msg => msg.send({
attachments: [
{
text: '*error*: something bad happened',
fallback: 'error: something bad happened',
color: 'danger',
mrkdwn_in: ['text']
}
]
}));
```

## Upgrading from version 2 or earlier
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ _Windows users_: The above command sets an environment variable correctly for Li
Hubot has many pre-written scripts that you can easy add by naming them in `external-scripts.json`. Take a look at all
the possibilites with the [hubot-scripts keyword in npm](https://www.npmjs.com/search?q=keywords:hubot-scripts).

Feeling inspired? You can jump into writing your own scripts by modifying `scripts/example.coffee`. It had tons of
Feeling inspired? You can jump into writing your own scripts by modifying `scripts/example.js`. It had tons of
helpful comments to guide you. You might also find the [Hubot scripting docs](https://hubot.github.com/docs/scripting/)
useful.

Expand Down
Loading