slacks 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +68 -10
- data/lib/slacks/connection.rb +17 -20
- data/lib/slacks/observer.rb +29 -0
- data/lib/slacks/version.rb +1 -1
- data/lib/slacks.rb +1 -1
- data/slacks.gemspec +1 -2
- metadata +4 -23
- data/lib/slacks/event.rb +0 -38
- data/lib/slacks/listener.rb +0 -17
- data/lib/slacks/listener_collection.rb +0 -22
- data/lib/slacks/message.rb +0 -51
- data/lib/slacks/rtm_event.rb +0 -30
- data/lib/slacks/session.rb +0 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e10c085c812db7ebb4c4882d2d29963c9b29941
|
4
|
+
data.tar.gz: 8d4fcc5ce2622251a901d97b35d895819e874fbd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3eb4f58e4f8cd344d133539ccf3413ff6fce1622fc3228a61ca77cff35c9ff8cbe25c288d4a4cf7425e99d62802efe4db58f191a01b8c85b32aa20416df63640
|
7
|
+
data.tar.gz: f5094c0ede839359afed74c5726df6a2df7731b19317210e73b4f6d4d558653916f7a061a2a77168e377b7d80a98e8126483eca01e5c9d36818739222988613b
|
data/README.md
CHANGED
@@ -2,34 +2,92 @@
|
|
2
2
|
|
3
3
|
A library for communicating via Slack
|
4
4
|
|
5
|
-
## Installation
|
6
5
|
|
7
|
-
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
##### Speaking
|
10
|
+
|
11
|
+
Slacks uses [Slack's Web API](https://api.slack.com/web) to allow your bot to communicate on Slack:
|
8
12
|
|
9
13
|
```ruby
|
10
|
-
|
14
|
+
require "slacks"
|
15
|
+
slack = Slacks::Connection.new("xoxb-0123456789-abcdefghijklmnopqrstuvwx")
|
16
|
+
slack.send_message "Hi everyone!", channel: "#general"
|
11
17
|
```
|
12
18
|
|
13
|
-
|
19
|
+
You can post [attachment](https://api.slack.com/docs/attachments):
|
14
20
|
|
15
|
-
|
21
|
+
```ruby
|
22
|
+
slack.send_message "", channel: "#general", attachments: [{
|
23
|
+
color: "#36a64f",
|
24
|
+
fallback: "Tests passed!",
|
25
|
+
text: "Tests passed!",
|
26
|
+
fields: [{
|
27
|
+
title: "Tests",
|
28
|
+
value: "143",
|
29
|
+
short: true
|
30
|
+
}, {
|
31
|
+
title: "Assertions",
|
32
|
+
value: "298",
|
33
|
+
short: true
|
34
|
+
}]
|
35
|
+
}]
|
36
|
+
```
|
16
37
|
|
17
|
-
|
38
|
+
(For more information about sending messages, see [chat.postMessage](https://api.slack.com/methods/chat.postMessage).)
|
18
39
|
|
19
|
-
|
40
|
+
You can indicate that your bot is typing:
|
20
41
|
|
42
|
+
```ruby
|
43
|
+
slack.typing_on "#general"
|
44
|
+
```
|
45
|
+
|
46
|
+
You can react to messages:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
slack.add_reaction "+1", message.ts
|
50
|
+
```
|
21
51
|
|
22
52
|
|
23
|
-
|
53
|
+
|
54
|
+
#### Listening
|
55
|
+
|
56
|
+
Slacks uses [Slack's RTM API](https://api.slack.com/rtm) to allow your bot to listen on Slack. Slacks connects to a websocket provided by Slack and allows you to set up listeners for events by type:
|
24
57
|
|
25
58
|
```ruby
|
26
59
|
require "slacks"
|
27
60
|
slack = Slacks::Connection.new("xoxb-0123456789-abcdefghijklmnopqrstuvwx")
|
28
|
-
slack.
|
29
|
-
|
61
|
+
slack.on "message" do |event|
|
62
|
+
puts "I heard #{event["user"]} say #{event["text"].inspect}"
|
63
|
+
end
|
64
|
+
slack.on "reaction_added" do |event|
|
65
|
+
next unless event["item"]["type"] == "message"
|
66
|
+
message = slack.get_message data["item"]["channel"], data["item"]["ts"]
|
67
|
+
puts "#{event["user"]} added #{event["reaction"]} to #{message["text"].inspect}"
|
30
68
|
end
|
69
|
+
slack.listen!
|
70
|
+
```
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
## Installation
|
75
|
+
|
76
|
+
Add this line to your application's Gemfile:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
gem "slacks"
|
31
80
|
```
|
32
81
|
|
82
|
+
And then execute:
|
83
|
+
|
84
|
+
$ bundle
|
85
|
+
|
86
|
+
Or install it yourself as:
|
87
|
+
|
88
|
+
$ gem install slacks
|
89
|
+
|
90
|
+
|
33
91
|
|
34
92
|
|
35
93
|
## Development
|
data/lib/slacks/connection.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require "slacks/bot_user"
|
2
2
|
require "slacks/channel"
|
3
3
|
require "slacks/driver"
|
4
|
+
require "slacks/observer"
|
4
5
|
require "slacks/errors"
|
5
6
|
require "slacks/guest_channel"
|
6
|
-
require "slacks/rtm_event"
|
7
7
|
require "slacks/team"
|
8
8
|
require "slacks/user"
|
9
9
|
require "faraday"
|
@@ -11,15 +11,11 @@ require "faraday/raise_errors"
|
|
11
11
|
|
12
12
|
module Slacks
|
13
13
|
class Connection
|
14
|
+
include ::Slacks::Observer
|
15
|
+
|
14
16
|
attr_reader :team, :bot, :token
|
15
17
|
attr_accessor :typing_speed
|
16
18
|
|
17
|
-
EVENT_MESSAGE = "message".freeze
|
18
|
-
EVENT_GROUP_JOINED = "group_joined".freeze
|
19
|
-
EVENT_USER_JOINED = "team_join".freeze
|
20
|
-
EVENT_REACTION_ADDED = "reaction_added".freeze
|
21
|
-
EVENT_REACTION_REMOVED = "reaction_removed".freeze
|
22
|
-
|
23
19
|
def initialize(token, options={})
|
24
20
|
@token = token
|
25
21
|
@typing_speed = options.fetch(:typing_speed, 100.0)
|
@@ -87,7 +83,7 @@ module Slacks
|
|
87
83
|
|
88
84
|
|
89
85
|
|
90
|
-
def listen!
|
86
|
+
def listen!
|
91
87
|
response = api("rtm.start")
|
92
88
|
unless response["ok"]
|
93
89
|
raise MigrationInProgress if response["error"] == "migration_in_progress"
|
@@ -97,7 +93,7 @@ module Slacks
|
|
97
93
|
|
98
94
|
@websocket = Slacks::Driver.new
|
99
95
|
websocket.connect_to websocket_url
|
100
|
-
|
96
|
+
trigger "connected"
|
101
97
|
|
102
98
|
websocket.on(:error) do |event|
|
103
99
|
raise ConnectionError.new(event)
|
@@ -115,33 +111,34 @@ module Slacks
|
|
115
111
|
@users_by_id[user["id"]] = user
|
116
112
|
@user_id_by_name[user["name"]] = user["id"]
|
117
113
|
|
118
|
-
when
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
when EVENT_REACTION_REMOVED
|
124
|
-
# Only care if someone reacted to something the bot said
|
125
|
-
next unless data["item_user"] == bot.id
|
126
|
-
callbacks.reaction_removed(data)
|
114
|
+
when EVENT_CHANNEL_CREATED
|
115
|
+
channel = data["channel"]
|
116
|
+
@channels_by_id[channel["id"]] = channel
|
117
|
+
@channel_id_by_name[channel["name"]] = channel["id"]
|
127
118
|
|
128
119
|
when EVENT_MESSAGE
|
129
120
|
# Don't respond to things that this bot said
|
130
121
|
next if data["user"] == bot.id
|
131
122
|
# ...or to messages with no text
|
132
123
|
next if data["text"].nil? || data["text"].empty?
|
133
|
-
callbacks.message(data)
|
134
124
|
end
|
125
|
+
|
126
|
+
trigger data["type"], data
|
135
127
|
end
|
136
128
|
|
137
129
|
websocket.main_loop
|
138
130
|
|
139
131
|
rescue EOFError
|
140
132
|
# Slack hung up on us, we'll ask for a new WebSocket URL and reconnect.
|
141
|
-
|
133
|
+
trigger "error", "Websocket Driver received EOF; reconnecting"
|
142
134
|
retry
|
143
135
|
end
|
144
136
|
|
137
|
+
EVENT_CHANNEL_CREATED = "channel_created".freeze
|
138
|
+
EVENT_GROUP_JOINED = "group_joined".freeze
|
139
|
+
EVENT_MESSAGE = "message".freeze
|
140
|
+
EVENT_USER_JOINED = "team_join".freeze
|
141
|
+
|
145
142
|
|
146
143
|
|
147
144
|
def channels
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "concurrent/array"
|
2
|
+
require "concurrent/hash"
|
3
|
+
|
4
|
+
module Slacks
|
5
|
+
module Observer
|
6
|
+
|
7
|
+
def on(event, &block)
|
8
|
+
observers_of(event).push(block)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def trigger(event, *args)
|
14
|
+
observers_of(event).each do |block|
|
15
|
+
block.call(*args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def observers_of(event)
|
20
|
+
observers[event.to_sym]
|
21
|
+
end
|
22
|
+
|
23
|
+
def observers
|
24
|
+
return @observers if defined?(@observers)
|
25
|
+
@observers = Concurrent::Hash.new { |hash, key| hash[key] = Concurrent::Array.new }
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/slacks/version.rb
CHANGED
data/lib/slacks.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
require "slacks/version"
|
2
|
-
require "slacks/
|
2
|
+
require "slacks/connection"
|
data/slacks.gemspec
CHANGED
@@ -23,8 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_dependency "multi_json"
|
24
24
|
spec.add_dependency "faraday"
|
25
25
|
spec.add_dependency "faraday-raise-errors"
|
26
|
-
spec.add_dependency "
|
27
|
-
spec.add_dependency "attentive"
|
26
|
+
spec.add_dependency "concurrent-ruby"
|
28
27
|
|
29
28
|
spec.add_development_dependency "bundler", "~> 1.10"
|
30
29
|
spec.add_development_dependency "rake", "~> 10.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slacks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Lail
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: websocket-driver
|
@@ -67,21 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: attentive
|
70
|
+
name: concurrent-ruby
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
73
|
- - ">="
|
@@ -201,13 +187,8 @@ files:
|
|
201
187
|
- lib/slacks/core_ext/exception.rb
|
202
188
|
- lib/slacks/driver.rb
|
203
189
|
- lib/slacks/errors.rb
|
204
|
-
- lib/slacks/event.rb
|
205
190
|
- lib/slacks/guest_channel.rb
|
206
|
-
- lib/slacks/
|
207
|
-
- lib/slacks/listener_collection.rb
|
208
|
-
- lib/slacks/message.rb
|
209
|
-
- lib/slacks/rtm_event.rb
|
210
|
-
- lib/slacks/session.rb
|
191
|
+
- lib/slacks/observer.rb
|
211
192
|
- lib/slacks/team.rb
|
212
193
|
- lib/slacks/user.rb
|
213
194
|
- lib/slacks/version.rb
|
data/lib/slacks/event.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require "slacks/conversation"
|
2
|
-
|
3
|
-
module Slacks
|
4
|
-
class Event
|
5
|
-
attr_reader :message, :channel, :sender
|
6
|
-
|
7
|
-
def initialize(session: nil, message: nil, channel: nil, sender: nil)
|
8
|
-
@session = session
|
9
|
-
@message = message
|
10
|
-
@channel = channel
|
11
|
-
@sender = sender
|
12
|
-
end
|
13
|
-
|
14
|
-
def user
|
15
|
-
return @user if defined?(@user)
|
16
|
-
@user = sender && ::User.find_by_slack_username(sender.username)
|
17
|
-
end
|
18
|
-
|
19
|
-
def reply(*args)
|
20
|
-
channel.reply(*args)
|
21
|
-
end
|
22
|
-
|
23
|
-
def random_reply(*args)
|
24
|
-
channel.random_reply(*args)
|
25
|
-
end
|
26
|
-
|
27
|
-
def typing
|
28
|
-
channel.typing
|
29
|
-
end
|
30
|
-
|
31
|
-
def start_conversation!
|
32
|
-
Conversation.new(session, channel, sender)
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
attr_reader :session
|
37
|
-
end
|
38
|
-
end
|
data/lib/slacks/listener.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require "attentive/listener"
|
2
|
-
|
3
|
-
module Slacks
|
4
|
-
class Listener < Attentive::Listener
|
5
|
-
attr_accessor :conversation
|
6
|
-
|
7
|
-
def matches_context?(message)
|
8
|
-
contexts = message.contexts.dup
|
9
|
-
contexts << :conversation if conversation && conversation.includes?(message)
|
10
|
-
|
11
|
-
return false unless contexts.superset? @required_contexts
|
12
|
-
return false unless contexts.disjoint? @prohibited_contexts
|
13
|
-
true
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require "attentive/listener_collection"
|
2
|
-
require "slacks/listener"
|
3
|
-
|
4
|
-
module Slacks
|
5
|
-
class ListenerCollection < Attentive::ListenerCollection
|
6
|
-
|
7
|
-
def overhear(*args, &block)
|
8
|
-
options = args.last.is_a?(::Hash) ? args.pop : {}
|
9
|
-
options[:context] = { in: :any }
|
10
|
-
listen_for(*args, options, &block)
|
11
|
-
end
|
12
|
-
|
13
|
-
def listen_for(*args, &block)
|
14
|
-
options = args.last.is_a?(::Hash) ? args.pop : {}
|
15
|
-
|
16
|
-
Slacks::Listener.new(self, args, options, block).tap do |listener|
|
17
|
-
push listener
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
data/lib/slacks/message.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
require "attentive/message"
|
2
|
-
|
3
|
-
module Slacks
|
4
|
-
class Message < ::Attentive::Message
|
5
|
-
|
6
|
-
def initialize(session, data, params={})
|
7
|
-
@session = session
|
8
|
-
@data = data
|
9
|
-
super data["text"], params
|
10
|
-
contexts << :conversation if channel.direct_message?
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
def channel
|
15
|
-
return @channel if defined?(@channel)
|
16
|
-
@channel = session.slack.find_channel data["channel"]
|
17
|
-
end
|
18
|
-
|
19
|
-
def sender
|
20
|
-
return @sender if defined?(@sender)
|
21
|
-
@sender = session.slack.find_user data["user"]
|
22
|
-
end
|
23
|
-
|
24
|
-
def timestamp
|
25
|
-
data["ts"]
|
26
|
-
end
|
27
|
-
|
28
|
-
def type
|
29
|
-
data.fetch("subtype", "message")
|
30
|
-
end
|
31
|
-
|
32
|
-
|
33
|
-
def add_reaction(emoji)
|
34
|
-
session.slack.add_reaction(emoji, self)
|
35
|
-
end
|
36
|
-
|
37
|
-
|
38
|
-
def respond_to_missing?(method, include_all)
|
39
|
-
return true if text.respond_to?(method)
|
40
|
-
super
|
41
|
-
end
|
42
|
-
|
43
|
-
def method_missing(method, *args, &block)
|
44
|
-
return text.public_send(method, *args, &block) if text.respond_to?(method)
|
45
|
-
super
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
attr_reader :session, :data
|
50
|
-
end
|
51
|
-
end
|
data/lib/slacks/rtm_event.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
require "slacks/event"
|
2
|
-
|
3
|
-
module Slacks
|
4
|
-
class RtmEvent < Event
|
5
|
-
attr_reader :match
|
6
|
-
|
7
|
-
def initialize(session, match)
|
8
|
-
@match = match
|
9
|
-
@listener = match.listener
|
10
|
-
message = match.message
|
11
|
-
super(session: session, message: message, channel: message.channel, sender: message.sender)
|
12
|
-
end
|
13
|
-
|
14
|
-
def matched?(key)
|
15
|
-
match.matched?(key)
|
16
|
-
end
|
17
|
-
|
18
|
-
def stop_listening!
|
19
|
-
listener.stop_listening!
|
20
|
-
end
|
21
|
-
|
22
|
-
def react(emoji)
|
23
|
-
message.add_reaction(emoji)
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
attr_reader :listener
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
data/lib/slacks/session.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
require "slacks/connection"
|
2
|
-
require "slacks/listener_collection"
|
3
|
-
require "slacks/message"
|
4
|
-
require "attentive"
|
5
|
-
|
6
|
-
module Slacks
|
7
|
-
class Session
|
8
|
-
include Attentive
|
9
|
-
|
10
|
-
attr_reader :slack
|
11
|
-
|
12
|
-
def initialize(token, &block)
|
13
|
-
@slack = Slacks::Connection.new(token)
|
14
|
-
|
15
|
-
if block_given?
|
16
|
-
listeners.instance_eval(&block)
|
17
|
-
start!
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def listeners
|
22
|
-
@listeners ||= Slacks::ListenerCollection.new
|
23
|
-
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def start!
|
28
|
-
slack.listen!(self)
|
29
|
-
end
|
30
|
-
|
31
|
-
def connected
|
32
|
-
Attentive.invocations = [slack.bot.name, slack.bot.to_s]
|
33
|
-
end
|
34
|
-
|
35
|
-
def error(error_message)
|
36
|
-
end
|
37
|
-
|
38
|
-
def message(data)
|
39
|
-
|
40
|
-
# Don't respond to things that another bot said
|
41
|
-
return if data.fetch("subtype", "message") == "bot_message"
|
42
|
-
|
43
|
-
# Normalize mentions of users
|
44
|
-
data["text"].gsub!(/<@U[^|]+\|([^>]*)>/, %q{@\1})
|
45
|
-
|
46
|
-
# Normalize mentions of channels
|
47
|
-
data["text"].gsub!(/<[@#]?([UC][^>]+)>/) do |match|
|
48
|
-
begin
|
49
|
-
slack.find_channel($1)
|
50
|
-
rescue ArgumentError
|
51
|
-
match
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
message = Slacks::Message.new(self, data)
|
56
|
-
hear(message).each do |match|
|
57
|
-
|
58
|
-
event = Slacks::RtmEvent.new(self, match)
|
59
|
-
invoke! match.listener, event
|
60
|
-
|
61
|
-
# Invoke only one listener per message
|
62
|
-
return
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def reaction_added(data)
|
67
|
-
end
|
68
|
-
|
69
|
-
def reaction_removed(data)
|
70
|
-
end
|
71
|
-
|
72
|
-
protected
|
73
|
-
|
74
|
-
def invoke!(listener, event)
|
75
|
-
listener.call(event)
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|