Class: Slackbotsy::Bot

Inherits:
Object
  • Object
show all
Defined in:
lib/slackbotsy/bot.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options, &block) ⇒ Bot

Returns a new instance of Bot.



12
13
14
15
16
17
18
19
# File 'lib/slackbotsy/bot.rb', line 12

def initialize(options, &block)
  @options = options
  @listeners = []
  @options['outgoing_token'] = parse_outgoing_tokens(@options['outgoing_token'])
  setup_incoming_webhook                # http connection for async replies
  setup_web_api                         # setup slack Web API
  instance_eval(&block) if block_given? # run any hear statements in block
end

Instance Attribute Details

#apiObject

Returns the value of attribute api.



10
11
12
# File 'lib/slackbotsy/bot.rb', line 10

def api
  @api
end

#last_descriptionObject

Returns the value of attribute last_description.



10
11
12
# File 'lib/slackbotsy/bot.rb', line 10

def last_description
  @last_description
end

#listenersObject

Returns the value of attribute listeners.



10
11
12
# File 'lib/slackbotsy/bot.rb', line 10

def listeners
  @listeners
end

Instance Method Details

#attach(ary, options = {}) ⇒ Object

simple wrapper on post to send attachment(s)



69
70
71
72
# File 'lib/slackbotsy/bot.rb', line 69

def attach(ary, options = {})
  attachments = ary.is_a?(Array) ? ary : [ ary ] #force first arg to array
  post({ attachments: attachments }.merge(options))
end

#desc(command, description = nil) ⇒ Object

record a description of the next hear block, for use in help



110
111
112
# File 'lib/slackbotsy/bot.rb', line 110

def desc(command, description = nil)
  @last_desc = [ command, description ]
end

#eval_scripts(*files) ⇒ Object

pass list of files containing hear statements, to be opened and evaled



121
122
123
124
125
# File 'lib/slackbotsy/bot.rb', line 121

def eval_scripts(*files)
  files.flatten.each do |file|
    self.instance_eval(File.open(file).read)
  end
end

#get_responses(msg, text) ⇒ Object

run on msg all hear blocks matching text



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/slackbotsy/bot.rb', line 156

def get_responses(msg, text)
  message = Slackbotsy::Message.new(self, msg)
  @listeners.map do |listener|
    text.match(listener.regex) do |mdata|
     begin
        message.instance_exec(mdata, *mdata[1..-1], &listener.proc)
      rescue => err # keep running even with a broken script, but report the error
        err
      end
    end
  end
end

#handle_item(msg) ⇒ Object

alias for backward compatibility



141
142
143
# File 'lib/slackbotsy/bot.rb', line 141

def handle_item(msg)
  handle_outgoing_webhook(msg)
end

#handle_outgoing_webhook(msg) ⇒ Object

check message and run blocks for any matches



128
129
130
131
132
133
134
135
136
137
138
# File 'lib/slackbotsy/bot.rb', line 128

def handle_outgoing_webhook(msg)
  return nil unless @options['outgoing_token'].include?(msg[:token]) # ensure messages are for us from slack
  return nil if msg[:user_name] == 'slackbot'  # do not reply to self
  return nil unless msg[:text].is_a?(String) # skip empty messages

  responses = get_responses(msg, msg[:text].strip)

  if responses
    { text: responses.compact.join("\n") }.to_json # webhook responses are json
  end
end

#handle_slash_command(msg) ⇒ Object



145
146
147
148
149
150
151
152
153
# File 'lib/slackbotsy/bot.rb', line 145

def handle_slash_command(msg)
  return nil unless @options['slash_token'].include?(msg[:token])

  responses = get_responses(msg, "#{msg[:command]} #{msg[:text]}".strip)

  if responses
    responses.join("\n") # plain text responses
  end
end

#hear(regex, &block) ⇒ Object

add regex to things to hear



115
116
117
118
# File 'lib/slackbotsy/bot.rb', line 115

def hear(regex, &block)
  @listeners << OpenStruct.new(regex: regex, desc: @last_desc, proc: block)
  @last_desc = nil
end

#parse_outgoing_tokens(tokens) ⇒ Object

use set of tokens for (more or less) O(1) lookup on multiple channels



22
23
24
25
26
27
28
29
30
31
# File 'lib/slackbotsy/bot.rb', line 22

def parse_outgoing_tokens(tokens)
  case tokens
  when NilClass
    []
  when String
    tokens.split(/[,\s]+/)
  when Array
    tokens
  end.to_set
end

#post(options) ⇒ Object

raw post of hash to slack webhook



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/slackbotsy/bot.rb', line 51

def post(options)
  payload = {
    username: @options['name'],
    channel:  @options['channel']
  }.merge(options)
  payload[:channel] = payload[:channel].gsub(/^#?/, '#') #slack api needs leading # on channel
  request = Net::HTTP::Post.new(@uri.request_uri)
  request.set_form_data(payload: payload.to_json)
  @http.request(request)
  return nil # so as not to trigger text in outgoing webhook reply
end

#post_message(text, options = {}) ⇒ Object

simple wrapper on api.post_message (which calls chat.postMessage)



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/slackbotsy/bot.rb', line 75

def post_message(text, options = {})
  payload = {
    username: @options['name'],
    channel:  @options['channel'],
    text:     text,
    as_user:  true
  }.merge(options)

  unless direct_message?(payload[:channel])
    payload[:channel] = enforce_leading_hash(payload[:channel])
    @api.join(payload[:channel])
  end

  @api.post_message(payload)
  return nil # be quiet in webhook reply
end

#say(text, options = {}) ⇒ Object

simple wrapper on post to send text



64
65
66
# File 'lib/slackbotsy/bot.rb', line 64

def say(text, options = {})
  post({ text: text }.merge(options))
end

#setup_incoming_webhookObject

setup http connection for sending async incoming webhook messages to slack



34
35
36
37
38
39
40
41
# File 'lib/slackbotsy/bot.rb', line 34

def setup_incoming_webhook
  ## incoming_webhook will be used if provided, otherwise fallback to old-style url with team and token
  url = @options.fetch('incoming_webhook', false) || "https://#{@options['team']}.slack.com/services/hooks/incoming-webhook?token=#{@options['incoming_token']}"
  @uri  = URI.parse(url)
  @http = Net::HTTP.new(@uri.host, @uri.port)
  @http.use_ssl = true
  @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end

#setup_web_apiObject

use api_token to setup Web API authentication



44
45
46
47
48
# File 'lib/slackbotsy/bot.rb', line 44

def setup_web_api
  if @options['api_token']
    @api = Api.new(@options['api_token'])
  end
end

#upload(options) ⇒ Object

simple wrapper on api.upload (which calls files.upload) pass ‘channel’ as a csv list of channel names, otherwise same args as files.upload



94
95
96
97
98
99
100
101
102
# File 'lib/slackbotsy/bot.rb', line 94

def upload(options)
  payload = options
  channels = @api.channels # list of channel objects
  payload[:channels] ||= (options.fetch(:channel, @options['channel'])).split(/[\s,]+/).map do |name|
    channels.find { |c| name.match(/^#?#{c['name']}$/) }.fetch('id') # convert channel id to name
  end.join(',')
  @api.upload(payload)
  return nil # be quiet in webhook reply
end

#usersObject

simple wrapper on api.users (which calls users.list)



105
106
107
# File 'lib/slackbotsy/bot.rb', line 105

def users
  @api.users
end