-
Notifications
You must be signed in to change notification settings - Fork 2
/
bangbang.coffee
189 lines (158 loc) · 6.66 KB
/
bangbang.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# Description
# Execute changeable pre-defined shell commands via hubot in a secure way !!
#
# Configuration:
#
# Commands:
# show bangbang commands -- show currently available commands
# reload bangbang commands -- reload command definition
#
# Notes:
#
# Author:
#
# Todos:
# 0.1.0
# * Demo Docker Image
# 0.2.0
# * Events
# * Receive
# * reload commands
# * execute command
# * Send
# * reload failed|success
# * execute failed|success
Log = require 'log'
utils = require './utils'
module_name = "hubot-bangbang"
config =
commands_file: process.env.HUBOT_BANGBANG_COMMANDS_FILE
default_timeout: if process.env.HUBOT_BANGBANG_TIMEOUT then parseInt process.env.HUBOT_BANGBANG_TIMEOUT else 10000
log_level: process.env.HUBOT_BANGBANG_LOG_LEVEL or "info"
role: process.env.HUBOT_BANGBANG_ROLE or ""
slack: process.env.HUBOT_BANGBANG_SLACK is "yes"
logger = new Log config.log_level
logger.notice "#{module_name}: Started."
load_commands = () ->
result = utils.load_commands_from_file config.commands_file
if result instanceof Error
logger.error "#{module_name}: Failed to load commands, because #{result}"
{}
else
logger.info "#{module_name}: Loaded #{result.length} command#{if result.length > 1 then 's' else ''}."
result
commands = load_commands()
module.exports = (robot) ->
robot.respond /show bangbang commands/i, (res) ->
unless is_authorized robot, res.envelope.user
warn_unauthorized res
else
logger.info "#{module_name}: show bangbang commands requested by #{res.envelope.user.name}."
msg = if commands.length > 0
("!! #{c.regex} - #{c.description}" for c in commands).join('\n')
else
"Uh oh, I'm sorry. There no commands availabe right now. Try to reload the commands file."
res.reply msg
robot.respond /reload bangbang commands/i, (res) ->
unless is_authorized robot, res.envelope.user
warn_unauthorized res
else
logger.info "#{module_name}: reload bangbang commands requested by #{res.envelope.user.name}."
commands = load_commands()
res.reply "Reloaded. Now I recognize #{commands.length} command#{if commands.length > 1 then 's' else ''}."
robot.respond /!! (.*)/i, (res) ->
unless is_authorized robot, res.envelope.user
warn_unauthorized res
else
user_name = res.envelope.user.name
command_req = res.match[1]
logger.info "#{module_name}: '#{command_req}' requested by #{user_name}."
command = null
for c in commands
if match = ///#{c.regex}///i.exec command_req
command = c
command.matches = match[1..]
command.time = utils.now()
command.timeout = config.default_timeout unless command.timeout
break
unless command? and command.matches
logger.info "#{module_name}: Did not recognize any command in '#{command_req}'."
res.reply "Oh oh! Did not recognize any command in '#{command_req}'."
else
logger.info "#{module_name}: Recognized command '#{command.name}' in '#{command_req}'."
if command.role and not is_authorized robot, res.envelope.user, command
warn_unauthorized res, command
else
res.reply "Alright, trying to #{c.description} with parameters '#{command.matches}'."
command.line = utils.bind_command_parameters command
logger.debug "#{module_name}: Going to execute '#{command.line}'."
command.ticket = utils.exec_command command, (error, stdout, stderr) ->
result_msg = if error
"command with ticket '#{utils.shorten_ticket command.ticket}' finished with error code #{error.code}, because of #{error.signal}."
else
"command with ticket '#{utils.shorten_ticket command.ticket}' finished successfully."
logger.info "#{module_name}: #{result_msg}"
unless config.slack
res.reply "Your " + result_msg
unless command.output_type is 'ignore'
res.reply "Command output for '#{command.line}':"
res.reply stdout if stdout
res.reply stderr if stderr
else
color = if error then 'danger' else 'good'
[has_mrkdwn, pretty_out, pretty_err] = switch command.output_type
when 'markdown' then [
["text"]
stdout or null
stderr or null
]
when 'pre' then [
["text"]
if stdout then "```\n#{stdout}\n```" else null
if stderr then "```\n#{stderr}\n```" else null
]
else [ # Also applies for 'plain'
[]
stdout or null
stderr or null
]
attachments = []
attachments.push {
color: color
title: "stdout"
text: pretty_out
mrkdwn_in: has_mrkdwn
} if pretty_out? and command.output_type != 'ignore'
attachments.push {
color: color
title: "stderr"
text: pretty_err
mrkdwn_in: has_mrkdwn
} if pretty_err? and command.output_type != 'ignore'
robot.adapter.customMessage {
channel: res.message.room
text: "Your " + result_msg
attachments: attachments
}
logger.info "#{module_name}: Ticket for '#{command.line}' is '#{command.ticket}'."
res.reply "Your ticket is '#{utils.shorten_ticket command.ticket}'."
robot.error (err, res) ->
robot.logger.error "#{module_name}: DOES NOT COMPUTE"
if res?
res.reply "DOES NOT COMPUTE: #{err}"
is_authorized = (robot, user, command) ->
general_auth = config.role is "" or robot.auth.hasRole(user, config.role)
command_auth = not command? or not command.role? or robot.auth.hasRole(user, command.role)
result = general_auth and command_auth
logger.debug "Checking authorization for user '#{user.name}', role '#{config.role}', and command '#{if command? then command.name else "<not queried>"}': general authorization is '#{general_auth}', command authorization is '#{command_auth}', and result is '#{result}'."
result
warn_unauthorized = (res, command) ->
user = res.envelope.user.name
message = res.message.text
logger.warning "hubot-#{module_name}: #{user} tried to run '#{message}' but was not authorized."
role_msg = if command?
"'#{config.role}' and '#{command.role}' roles"
else
"'#{config.role}' role"
res.reply "Sorry, you're not allowed to do that. You need the #{role_msg}."