Skip to content

Commit

Permalink
feat: update libs, merge mods from PR RocketChat#289, Improve Dockerf…
Browse files Browse the repository at this point in the history
…ile for multistage builds, improve hubot executable, cleanups
  • Loading branch information
amirhmoradi committed Jan 4, 2022
1 parent c45faf6 commit 0db14e2
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 1,419 deletions.
53 changes: 24 additions & 29 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
FROM node:4.8.3
MAINTAINER Rocket.Chat Team <[email protected]>
FROM node:14-alpine AS builder
LABEL maintainer="Amir Moradi - https://linkedin/in/amirhmoradi"

RUN npm install -g coffee-script yo generator-hubot && \
useradd hubot -m

USER hubot

WORKDIR /home/hubot

ENV BOT_NAME "rocketbot"
ENV npm_config_loglevel=verbose
ENV BOT_OWNER "No owner specified"
ENV BOT_DESC "Hubot with rocketbot adapter"
ENV HUBOT_LOG_LEVEL "error"
ENV BOT_DESC "Hubot with the Rocket.Chat adapter"

ENV EXTERNAL_SCRIPTS=hubot-diagnostics,hubot-help,hubot-google-images,hubot-google-translate,hubot-pugme,hubot-maps,hubot-rules,hubot-shipit
USER root
RUN apk add --update \
git && \
adduser -S hubot && \
addgroup -S hubot && \
touch ~/.bashrc && \
npm install --global npm@latest && \
npm install -g coffeescript && \
mkdir /home/hubot/scripts/

RUN yo hubot --owner="$BOT_OWNER" --name="$BOT_NAME" --description="$BOT_DESC" --defaults && \
sed -i /heroku/d ./external-scripts.json && \
sed -i /redis-brain/d ./external-scripts.json && \
npm install hubot-scripts
FROM builder AS final

ADD . /home/hubot/node_modules/hubot-rocketchat
WORKDIR /home/hubot/

# hack added to get around owner issue: https://github.com/docker/docker/issues/6119
USER root
RUN chown hubot:hubot -R /home/hubot/node_modules/hubot-rocketchat
USER hubot
COPY package.json /home/hubot/
COPY bin/hubot /home/hubot/bin/
RUN chown -R hubot:hubot /home/hubot/

RUN cd /home/hubot/node_modules/hubot-rocketchat && \
npm install && \
#coffee -c /home/hubot/node_modules/hubot-rocketchat/src/*.coffee && \
cd /home/hubot
USER hubot
# EXTERNAL_SCRIPTS is managed in bin/hubot script.
#ENV EXTERNAL_SCRIPTS=hubot-diagnostics,hubot-google-images,hubot-google-translate,hubot-pugme,hubot-maps,hubot-rules,hubot-shipit
RUN npm install --no-audit

CMD node -e "console.log(JSON.stringify('$EXTERNAL_SCRIPTS'.split(',')))" > external-scripts.json && \
npm install $(node -e "console.log('$EXTERNAL_SCRIPTS'.split(',').join(' '))") && \
bin/hubot -n $BOT_NAME -a rocketchat
VOLUME ["/home/hubot/scripts"]
CMD ["/bin/ash", "/home/hubot/bin/hubot"]
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![Rocket.Chat logo](https://rocket.chat/images/logo/logo-dark.svg?v3)
![Rocket.Chat logo](https://open.rocket.chat/images/logo/logo.svg)

[![Rocket.Chat](https://open.rocket.chat/api/v1/shield.svg?type=channel&name=Rocket.Chat&channel=hubot)](https://open.rocket.chat/channel/hubot)
[![Test Coverage](https://codeclimate.com/github/RocketChat/hubot-rocketchat/badges/coverage.svg)](https://codeclimate.com/github/RocketChat/hubot-rocketchat/coverage)
Expand Down Expand Up @@ -135,10 +135,9 @@ relevant to Hubot. It has some additional configs, [documented here][rcsdk-env].
| Env variable | Description |
| ---------------------- | ----------------------------------------------------- |
| **Hubot** | A subset of relevant [Hubot env vars][hubot-env] |
| `HUBOT_ADAPTER` | Set to `rocketchat` (or pass as launch argument) |
| `HUBOT_NAME` | The programmatic name for listeners |
| `HUBOT_ALIAS` | An alternate name for the bot to respond to |
| `HUBOT_LOG_LEVEL` | The minimum level of logs to output (error) |
| `BOT_NAME` / `HUBOT_NAME` | The programmatic name for listeners |
| `BOT_ALIAS` / `HUBOT_ALIAS` | An alternate name for the bot to respond to |
| `HUBOT_LOG_LEVEL` | Verbosity of the bots console logs. Can be **info, warn or error** |
| `HUBOT_HTTPD` | If the bot needs to listen to or make HTTP requests |
| **Rocket.Chat SDK** | A subset of relevant [SDK env vars][rcsdk-env] |
| `ROCKETCHAT_URL`* | Local Rocketchat address (start before the bot) |
Expand All @@ -158,7 +157,7 @@ relevant to Hubot. It has some additional configs, [documented here][rcsdk-env].


Be aware you *must* add the bot's user as a member of the new private group(s)
before it will respond.
before it will respond. If you attempt to start the bot with a room listed in the ROCKETCHAT_ROOM variable that it's not already a member of, the bot will error on startup.

## Connecting to Rocket.Chat

Expand Down
179 changes: 179 additions & 0 deletions bin/hubot
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#!/bin/bash

#Stop NPM from complaining about useless stuff.
export npm_config_loglevel=${npm_config_loglevel:-error}

#This function is used to listen for SIGINT/SIGTERM so the container can be killed with CTRL+C
#Also prevents the docker reboot bug where the container freezes for 30-60 seconds on reboot.
asyncRun() {
"$@" &
pid="$!"
trap "echo 'Stopping PID $pid'; kill -SIGTERM $pid" SIGINT SIGTERM

# A signal emitted while waiting will make the wait command return code > 128
# Let's wrap it in a loop that doesn't end before the process is indeed stopped
while kill -0 $pid >/dev/null 2>&1; do
wait
done
}

cat <<EOF
██████╗ ██████╗ ██████╗██╗ ██╗███████╗████████╗ ██████╗██╗ ██╗ █████╗ ████████╗
██╔══██╗██╔═══██╗██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝ ██╔════╝██║ ██║██╔══██╗╚══██╔══╝
██████╔╝██║ ██║██║ █████╔╝ █████╗ ██║ ██║ ███████║███████║ ██║
██╔══██╗██║ ██║██║ ██╔═██╗ ██╔══╝ ██║ ██║ ██╔══██║██╔══██║ ██║
██║ ██║╚██████╔╝╚██████╗██║ ██╗███████╗ ██║ ██╗╚██████╗██║ ██║██║ ██║ ██║
╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝
██╗ ██╗██╗ ██╗██████╗ ██████╗ ████████╗
██║ ██║██║ ██║██╔══██╗██╔═══██╗╚══██╔══╝
███████║██║ ██║██████╔╝██║ ██║ ██║
██╔══██║██║ ██║██╔══██╗██║ ██║ ██║
██║ ██║╚██████╔╝██████╔╝╚██████╔╝ ██║
╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝
EOF

echo "Your Rocket.Chat Hubot Docker Container is now starting. Please wait...."

#Set log level to info by default if it has not been set
if [[ -z "${HUBOT_LOG_LEVEL}" ]]; then
echo "INFO: HUBOT_LOG_LEVEL not set. Using HUBOT_LOG_LEVEL=info as the default"
export HUBOT_LOG_LEVEL=info
fi
#Check if the Rocket.Chat URL has been set.
if [[ -z "${ROCKETCHAT_URL}" ]]; then
echo "-------------"
echo "The ROCKETCHAT_URL Environment Variable has not been set. Set this to your Rocket.Chat Server URL"
echo "Example: ROCKETCHAT_URL=https://open.rocket.chat"
echo "Exiting...."
echo "-------------"
exit 1
fi

#Check if the Rocket.Chat User has been set.
if [[ -z "${ROCKETCHAT_USER}" ]]; then
echo "-------------"
echo "ROCKETCHAT_USER Environment Variable has not been set. Set this to the username on your Rocket.Chat server that the bot will log in with."
echo "Example: ROCKETCHAT_USER=rocketbot"
echo "Exiting...."
echo "-------------"
exit 1
fi

#Check if the Rocket.Chat Password has been set for the user above
if [[ -z "${ROCKETCHAT_PASSWORD}" ]]; then
echo "-------------"
echo "----ERROR----"
echo "-------------"
echo "The ROCKETCHAT_PASSWORD Environment Variable has not been set. Set this to the password for the user specified in the ROCKETCHAT_USER environment variable."
echo "Example: ROCKETCHAT_PASSWORD=supersecret"
echo "Exiting...."
echo "-------------"
exit 1

fi

#Check for BOT_NAME or HUBOT_NAME.
if [[ -z "${BOT_NAME}" ]]; then
if [[ -z "${HUBOT_NAME}" ]]; then
echo "-------------"
echo "ERROR: The BOT_NAME Environment Variable has not been set. Set this to the name your bot will respond too."
echo "Exiting...."
echo "-------------"
exit 1
fi
else
export HUBOT_NAME=${BOT_NAME}
fi

#Install any required deps.
cd /home/hubot/

if [[ -z "${NPM_REGISTRY}" ]]; then
echo "INFO: The NPM_REGISTRY environment variable has not been set. Using npmjs as the default."
else
echo "INFO: The NPM_REGISTRY environment variable is $NPM_REGISTRY. NPM will use this registry to pull packages from."
npm set registry $NPM_REGISTRY
fi

#This happens here as well as during the container build process. There seems to be a bug where sometimes hubot misses 1 or 2 deps. This is insurance for that. Some people also mount node_modules externally and this will ensure that the base deps are there in those cases.
echo "INFO: Attempting to install this containers dependancies"
npm install --no-audit

#Check for BOT_ALIAS or HUBOT_ALIAS.
if [[ -z "${BOT_ALIAS}" ]]; then
if [[ -z "${HUBOT_ALIAS}" ]]; then
echo "-------------"
echo "WARN: BOT_ALIAS has not been set. This is used to call your bot by a 'short name'."
echo "WARN: For example if the following are set: BOT_NAME=rocketbot and BOT_ALIAS='!' - Your bot will respond to both '@rocketbot help' and '!help'"
echo "-------------"
fi
else
export HUBOT_ALIAS=${BOT_ALIAS}
fi

if [[ "${ROCKETCHAT_BOT_DIAGNOSTICS}" == 'true' ]]; then
echo "INFO: ROCKETCHAT_BOT_DIAGNOSTICS is enabled. A set of diagnostics/test scripts will be loaded."
if [[ -z "${EXTERNAL_SCRIPTS}" ]]; then
#If no EXTERNAL_SCRIPTS are set, but this is set to true, only load the diagnostics package
EXTERNAL_SCRIPTS=hubot-rocketchat-diagnostics
else
#If we have specified some EXTERNAL_SCRIPTS then append the diagnostics package to the end of them
EXTERNAL_SCRIPTS+=,hubot-rocketchat-diagnostics
fi
fi

if [[ -z "${EXTERNAL_SCRIPTS}" ]]; then
echo "-------------"
echo "WARN: The EXTERNAL_SCRIPTS environment variable has not been set."
echo "WARN: This means no additional hubot scripts will be loaded except for whatever is bundled by default with this container"
echo "WARN: This should be set with a array of hubot NPM script repos like the example below."
echo "Example: EXTERNAL_SCRIPTS=hubot-help,hubot-security,hubot-auth"
echo "-------------"
else
echo "INFO: Installing hubot scripts we passed in the EXTERNAL_SCRIPTS environment variable."
echo "INFO: Could be git+https or git+http protocol urls (see [here](https://docs.npmjs.com/cli/install) for details) if your script is not on the NPM public registry."
npm i -S $(node -e "console.log('$EXTERNAL_SCRIPTS'.split(',').join(' '))") --no-audit
node -e "console.log(JSON.stringify('$EXTERNAL_SCRIPTS'.split(',').map(elem => elem.replace(/git\+https?\:\/\/.*\/(.*).git/, '\$1'))))" >/home/hubot/external-scripts.json
fi

if [[ -z "${ROCKETCHAT_AUTH}" ]]; then
echo "-------------"
echo "WARN: The ROCKETCHAT_AUTH environment variable has not been set."
echo "WARN: If the bot is using a LDAP account to log into your Rocket.Chat instance and are seeing issues, try setting this to ROCKETCHAT_AUTH=ldap"
echo "Default: ROCKETCHAT_AUTH=password"
echo "-------------"
export ROCKETCHAT_AUTH=password
fi

if [[ -z "${RESPOND_TO_DM}" ]]; then
echo "-------------"
echo "INFO: The RESPOND_TO_DM environment variable has not been set. This bot will not respond to Direct/Private messages"
echo "INFO: Set RESPOND_TO_DM=true if you want your bot to respond to direct/private messages."
echo "Default: RESPOND_TO_DM=false"
echo "-------------"
fi

if [[ -z "${RESPOND_TO_EDITED}" ]]; then
echo "-------------"
echo "INFO: The RESPOND_TO_EDITED environment varialbe is not set."
echo "INFO: Set RESPOND_TO_EDITED=true if you want this bot to respond to messages after they have been edited"
echo "Default: RESPOND_TO_EDITED=false"
echo "-------------"
fi

if [[ -z "${RESPOND_TO_LIVECHAT}" ]]; then
echo "-------------"
echo "INFO: The RESPOND_TO_LIVECHAT environment varialbe is not set. The default is RESPOND_TO_LIVECHAT=false"
echo "INFO: This means the bot will not respond in a LiveChat conversation"
echo "Default: RESPOND_TO_LIVECHAT=false"
echo "-------------"
fi
set -e

export PATH="node_modules/.bin:node_modules/hubot/node_modules/.bin:$PATH"

#Hack to add coffeescript requirement as the fix was never merged in the official Hubot repo
sed -i '2irequire("coffeescript")' /home/hubot/node_modules/hubot/src/robot.js

#Start Hubot using the asyncRun function
asyncRun node node_modules/hubot/bin/hubot.js -a rocketchat "$@"
22 changes: 22 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: "3.7"
services:
rocketbot:
image: princecloud/hubot-rocketchat
environment:
- ROCKETCHAT_URL=https://rocket.example.com
- ROCKETCHAT_AUTH=password
- ROCKETCHAT_USER=hubot
- ROCKETCHAT_PASSWORD=supersecret
- BOT_NAME=hubot
- ROCKETCHAT_ROOM=GENERAL
- BOT_ALIAS=hubot
- LISTEN_ON_ALL_PUBLIC=false
- HUBOT_LOG_LEVEL=verbose
- RESPOND_TO_DM=true
- RESPOND_TO_EDITED=false
- RESPOND_TO_LIVECHAT=false
- INTEGRATION_ID=hubot
- EXTERNAL_SCRIPTS=hubot-help,hubot-rocketchat-diagnostics,git+https://github.com/amirhmoradi/hubot-rocketchat-standup.git
- npm_config_loglevel=info
volumes:
- ./scripts:/home/hubot/scripts
28 changes: 26 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AttachmentMessage extends TextMessage {
this.id = id
}
toString () {
return this.attachment
return this.text
}
}

Expand Down Expand Up @@ -82,7 +82,7 @@ class RocketChatBotAdapter extends Adapter {
}

/** Process every incoming message in subscription */
process (err, message, meta) {
async process (err, message, meta) {
if (err) throw err
// Prepare message type for Hubot to receive...
this.robot.logger.info('Filters passed, will receive message')
Expand All @@ -104,12 +104,30 @@ class RocketChatBotAdapter extends Adapter {
return this.robot.receive(new EnterMessage(user, null, message._id))
}

if ('au' === message.t) {
this.robot.logger.debug('Message type EnterMessage', message)
const joiningUser = await this.getUserByUsername(message.msg);
joiningUser.roomID = message.rid
joiningUser.roomType = meta.roomType
joiningUser.room = meta.roomName || message.rid
return this.robot.receive(new EnterMessage(joiningUser, null, message._id))
}

// Room exit, receive without further detail
if (message.t === 'ul') {
this.robot.logger.debug('Message type LeaveMessage')
return this.robot.receive(new LeaveMessage(user, null, message._id))
}

if ('ru' === message.t) {
this.robot.logger.debug('Message type LeaveMessage', message)
const joiningUser = await this.getUserByUsername(message.msg);
joiningUser.roomID = message.rid
joiningUser.roomType = meta.roomType
joiningUser.room = meta.roomName || message.rid
return this.robot.receive(new LeaveMessage(joiningUser, null, message._id))
}

// Direct messages prepend bot's name so Hubot can `.respond`
const startOfText = (message.msg.indexOf('@') === 0) ? 1 : 0
const robotIsNamed = message.msg.indexOf(this.robot.name) === startOfText || message.msg.indexOf(this.robot.alias) === startOfText
Expand Down Expand Up @@ -181,6 +199,12 @@ class RocketChatBotAdapter extends Adapter {
callMethod (method, ...args) {
return driver.callMethod(method, args)
}

async getUserByUsername(username) {
const response = await this.api.get('users.info', { username })
if (!response.success) throw new Error('user info response unsucessful.')
return response.user;
}
}

exports.use = (robot) => new RocketChatBotAdapter(robot)
Loading

0 comments on commit 0db14e2

Please sign in to comment.