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

Include options to mirror messages from other Bots & Basic Docker setup #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
slackrelay.json
.DS_Store
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM python:3

WORKDIR /opt/slack-relay

COPY entrypoint.sh requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [ "bash", "/opt/slack-relay/entrypoint.sh" ]
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ optional arguments:
configuration
-f CONFIG_FILE, --config-file CONFIG_FILE
Configuration file
-mb, --mirror-bots True|False Option to relay messages from other Bots (such as your Build server Notifications)
-e EMOJI_TO_CONFIRM, --emoji-to-confirm EMOJI_TO_CONFIRM
Emoji that relayed messages will be updated with
(reacted to) to show confirmation to humans, e.g.
Expand Down Expand Up @@ -66,6 +67,11 @@ Test message2
Test message3
```

Docker Usage / Running in Production
------------------------------------

A Dockerfile and docker-compose file has been included for ease of deployment... Simply `vi docker-compose.yml` and then run `docker-compose up -d` to start the container on a server.

Contribute
----------

Expand Down
25 changes: 25 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
version: '3'
services:

slack-relay:
container_name: slack-relay
build: .

restart: always
privileged: true

volumes:
- ./conf/:/opt/slack-relay/conf

environment:
- CONFIG_FILE=/opt/slack-relay/conf/slackrelay.json

## You MUST set these variables at a minimum to run the application.
#- BOT_TOKEN=
#- BOT_NAME=name_of_your_bot

## Uncomment/set the following variables as required:
#- LOG_LEVEL=debug
#- MIRROR_BOTS=True
#- EMOJI_TO_CONFIRM=
#- SLEEP_MS=
14 changes: 14 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash

OPTARGS=""

[[ ! -z "$LOG_LEVEL" ]] && OPTARGS="$OPTARGS -l $LOG_LEVEL"
[[ ! -z "$BOT_NAME" ]] && OPTARGS="$OPTARGS --bot $BOT_NAME"
[[ ! -z "$CONFIG_FILE" ]] && OPTARGS="$OPTARGS --config-file $CONFIG_FILE"
[[ ! -z "$EMOJI_TO_CONFIRM" ]] && OPTARGS="$OPTARGS --emoji-to-confirm $EMOJI_TO_CONFIRM"
[[ ! -z "$SLEEP_MS" ]] && OPTARGS="$OPTARGS --sleep-ms $SLEEP_MS"
[[ ! -z "$MIRROR_BOTS" ]] && OPTARGS="$OPTARGS --mirror-bots $MIRROR_BOTS"

[[ ! -z "$BOT_TOKEN" ]] && OPTARGS="$OPTARGS $BOT_TOKEN"

python ./slackrelay.py $OPTARGS
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
slackclient==1.3.2
jason==0.1.7
simplejson==3.16.0
Django==2.2.4
94 changes: 71 additions & 23 deletions slackrelay.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#!/usr/bin/env python

# Module: slackrelay
# Date: 5th January 2017
# Author: Cristiano Giuffrida, giuffrida at cs dot vu dot nl

"""slackrelay

Slack Relay Bot.
"""

__desc__ = "Slack Relay Bot"
__version__ = "0.1"
__author__ = "Cristiano Giuffrida"
Expand Down Expand Up @@ -132,6 +132,16 @@ def lookup(sc, team, name):
return Bot(user.id, user.name, user.image)
err_exit(3, "Unable to find bot identity")

def lookupBot(sc, botId):
bots = sc.api_call(
"bots.info",
bot=botId
)

logging.debug("BOT INFO: %s" % bots)
return bots


class Rule:
def __init__(self, name, fTeam, fChannel, backend='echo', bURL=None):
self.name=name
Expand Down Expand Up @@ -212,7 +222,8 @@ def handleCommand(self, team, channel, cmd, prefix):
syntax = "Syntax: %s [rule-add json] [rule-del name] [rule-del-all] [rule-list] [help]" % prefix
try:
if cmd.startswith(' rule-add '):
args = cmd[10:].strip()
args = cmd[10:].strip().replace(u"“", "\"").replace(u"”", "\"")
logging.debug(args)
ruleDict = json.loads(args)
ruleDict['frontend-team'] = team.name
ruleDict['frontend-channel'] = channel.name
Expand Down Expand Up @@ -246,7 +257,7 @@ def load(self):
if not os.path.isfile(self.file):
self.store()
with open(self.file, mode='rb') as f:
ruleSet = json.load(f)
ruleSet = json.load(f)
for d in ruleSet:
r = Rule.fromDict(d)
if not self.addRule(r):
Expand All @@ -255,7 +266,7 @@ def load(self):

def store(self):
ruleSet = self.getRuleSet()
with open(self.file, mode='wb') as f:
with open(self.file, mode='w') as f:
json.dump(ruleSet, f, indent=4, sort_keys=True)

def err_exit(status, message):
Expand All @@ -264,12 +275,12 @@ def err_exit(status, message):

def parse_args():
"""parse_args() -> args

Parse any command-line arguments..
"""

parser = argparse.ArgumentParser(description=__desc__)

parser.add_argument("-l", "--log",
default="warning", choices=['debug', 'info', 'warning', 'error'],
help="Log level")
Expand All @@ -293,11 +304,15 @@ def parse_args():
default=100,
help="Polling interval (ms)")

parser.add_argument("-mb", "--mirror-bots",
default=False,
help="Mirrors messages from bots, as well as users when relaying messages with the 'slack-iwh' rule.")

parser.add_argument("-v", "--version",
action='version', version=__version__)

parser.add_argument("bot_user_token")

args = parser.parse_args()

return args
Expand Down Expand Up @@ -353,29 +368,44 @@ def main():
logging.warning("Reconnecting to bot..")
(bot,team,sc) = connect_to_bot(args.bot_user_token, args.bot)
for part in response:
logging.debug(part)
# Skip nonmessages and bot messages
if len(part) == 0:
logging.debug("part length is 0!")
continue
if 'type' not in part:
logging.warning("Type not in part: %s" % str(part))
if 'type' in part and part['type'] != 'message':
logging.debug("'type' in part and part['type'] != 'message'")
continue
if 'bot_id' in part:
continue
if args.mirror_bots == False:
if 'bot_id' in part:
logging.debug("'bot_id' in part")
continue
if 'previous_message' in part and 'bot_id' in part['previous_message']:
logging.debug("'previous_message' in part and 'bot_id' in part['previous_message']")
continue

logging.debug(response)

# Lookup event channel
logging.debug("New event: %s" % part)
channel = Channel.lookup(sc, team, part['channel'])

# Handle @slackrelay commands
if not 'subtype' in part and part['text'].startswith(bot.commandPrefix):
if args.slave:
logging.warning('Skipping command "%s" (slave mode)' % part['text'])
else:
ret = config.handleCommand(team, channel, part['text'], bot.commandPrefix)
sc.api_call("chat.postMessage", channel=part['channel'], text=ret, username=bot.name, icon_url=bot.image)
try:
if not 'subtype' in part and part['text'].startswith(bot.commandPrefix):
if args.slave:
logging.warning('Skipping command "%s" (slave mode)' % part['text'])
else:
ret = config.handleCommand(team, channel, part['text'], bot.commandPrefix)
sc.api_call("chat.postMessage", channel=part['channel'], text=ret, username=bot.name, icon_url=bot.image)
continue
except:
continue

ret = config.handleCommand(team, channel, part['text'], bot.commandPrefix)
sc.api_call("chat.postMessage", channel=part['channel'], text=ret, username=bot.name, icon_url=bot.image)
continue

# See if we have any matching rules
Expand All @@ -391,8 +421,12 @@ def main():
# Determine user and text
user = None
text = None
isOtherBot = False
if 'subtype' in part:
mtype = part['subtype']

logging.debug("TYPE: %s" % mtype)

if mtype == 'message_deleted':
user = User.lookup(sc, team, part['previous_message']['user'])
text = '[DELETED] %s' % part['previous_message']['text']
Expand All @@ -401,12 +435,26 @@ def main():
text = '[EDITED] %s -> %s' % (part['previous_message']['text'], part['message']['text'])
elif mtype == 'me_message':
part['text'] = "/me %s" % part['text']
elif args.mirror_bots != False and mtype == 'bot_message':
part['text'] = part['text']
isOtherBot = True
else:
logging.warning("Unhandled message, skipping")
continue
if not user:
user = User.lookup(sc, team, part['user'])
text = part['text']
if isOtherBot == False:
user = User.lookup(sc, team, part['user'])
text = part['text']
if isOtherBot == True:
CurrentBot = Bot.lookupBot(sc, part['bot_id'])
class botUser:
fullName = CurrentBot['bot']['name'] + "@" + team.name
image = CurrentBot['bot']['icons']['image_72']
user = botUser
try:
text = part['attachments'][0]['fallback']
except:
continue

for p in usernamePattern.findall(text):
src = p
Expand All @@ -428,9 +476,9 @@ def main():
"icon_url": user.image
}
req = requests.post(r.bURL, json.dumps(payload), headers={'content-type': 'application/json'})
req = req.ok
req = req.ok
else:
req = sc.api_call("chat.postMessage", channel=part['channel'], text=text, username=user.fullName, icon_url=user.image)
req = sc.api_call("chat.postMessage", channel=part['channel'], text=text, username=user.fullName, icon_url=user.image)
req = req['ok']
except Exception:
print(traceback.format_exc())
Expand Down