slackr 0.0.2 → 0.0.5
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.
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +22 -15
- data/lib/slackr.rb +37 -1
- data/lib/slackr/channel.rb +61 -0
- data/lib/slackr/connection.rb +51 -0
- data/lib/slackr/version.rb +1 -1
- data/lib/slackr/webhooks/incoming.rb +42 -0
- data/slackr.gemspec +6 -6
- data/spec/slackr/channel_spec.rb +127 -0
- data/spec/slackr/connection_spec.rb +114 -0
- data/spec/slackr/webhooks/incoming_spec.rb +71 -0
- data/spec/slackr_spec.rb +29 -0
- data/spec/spec_helper.rb +2 -2
- metadata +56 -33
- data/lib/slackr/webhook.rb +0 -62
- data/spec/webhook_spec.rb +0 -57
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
# Slackr
|
2
2
|
|
3
|
-
A simple
|
3
|
+
A simple wrapper for the http://slack.com API.
|
4
|
+
|
5
|
+
## Build Status
|
6
|
+
|
7
|
+
[](https://travis-ci.org/risk-io/slackr)
|
4
8
|
|
5
9
|
## Installation
|
6
10
|
|
11
|
+
NOTE: the most recent version of the gem has not been published to rubygems.org, so specify the path as shown.
|
12
|
+
|
7
13
|
Add this line to your application's Gemfile:
|
8
14
|
|
9
|
-
gem 'slackr'
|
15
|
+
gem 'slackr', :git => 'git://github.com/risk-io/slackr.git'
|
10
16
|
|
11
17
|
And then execute:
|
12
18
|
|
@@ -18,23 +24,18 @@ Or install it yourself as:
|
|
18
24
|
|
19
25
|
## Usage
|
20
26
|
|
21
|
-
|
22
|
-
|
23
27
|
Send a message to slack:
|
24
28
|
|
25
29
|
```
|
26
30
|
require 'slackr'
|
27
|
-
slack = Slackr
|
28
|
-
slack.say "
|
31
|
+
slack = Slackr.connect("teamX", "token124", {"channel" => "#myroom", "username" => "systems_bot"})
|
32
|
+
slack.say "hello world" # posts 'hello world' to the myroom channel as the systems_bot user
|
33
|
+
slack.say "hello", {"channel" => "#room2", "username" => "joke_bot"} # posts 'hello' to the room2 channel as the joke_but user
|
29
34
|
```
|
30
35
|
|
31
|
-
|
32
|
-
|
36
|
+
Retrieve the channel list:
|
33
37
|
```
|
34
|
-
|
35
|
-
slack = Slackr::Webhook.new("my_team_name","my_api_key",{:icon_emoji => ":ghost:"})
|
36
|
-
slack.say("this is a test as a ghost")
|
37
|
-
slack.say("this is a test as a ghost with a custom name",{:username => "casper"}
|
38
|
+
slack.channels.list # returns a hash of channel objects
|
38
39
|
```
|
39
40
|
|
40
41
|
Available customizations include:
|
@@ -49,11 +50,17 @@ Available customizations include:
|
|
49
50
|
# }
|
50
51
|
```
|
51
52
|
|
53
|
+
## General Notes
|
54
|
+
- Slackr::Connection stores the connection information
|
55
|
+
- Slackr::Errors stores the various custom errors thrown
|
56
|
+
- Slackr::Version stores the gem version
|
57
|
+
- Slackr::IncomingWebhook stores the logic for the various incoming webhook API endpoints
|
58
|
+
|
52
59
|
## TODO
|
53
60
|
|
54
|
-
|
55
|
-
|
56
|
-
|
61
|
+
- [ ] Support formats for incoming webhook messages
|
62
|
+
- [ ] Link parsing and attachments
|
63
|
+
- [ ] CLI
|
57
64
|
|
58
65
|
## Contributing
|
59
66
|
|
data/lib/slackr.rb
CHANGED
@@ -1,3 +1,39 @@
|
|
1
1
|
require "slackr/version"
|
2
2
|
require "slackr/errors"
|
3
|
-
require "slackr/
|
3
|
+
require "slackr/connection"
|
4
|
+
require "slackr/webhooks/incoming"
|
5
|
+
require "slackr/channel"
|
6
|
+
|
7
|
+
# slack = Slackr.connect("teamX", "token124", {"channel" => "#myroom", "username" => "systems_bot"})
|
8
|
+
# slack.say "hello world" => posts 'hello world' to the myroom channel as the systems_bot user
|
9
|
+
# slack.say "hello", {"channel" => "#room2", "username" => "joke_bot"} => posts 'hello' to the room2 channel as the joke_but user
|
10
|
+
# slack.channels.list => returns a hash of channel objects
|
11
|
+
module Slackr
|
12
|
+
extend self
|
13
|
+
|
14
|
+
attr_accessor :connection
|
15
|
+
|
16
|
+
def connect(team, token, options = {})
|
17
|
+
raise Slackr::ArgumentError, "Team required" if team.nil?
|
18
|
+
raise Slackr::ArgumentError, "Token required" if token.nil?
|
19
|
+
#TODO: raise error if options doesn't contain channel and username values
|
20
|
+
@connection = Slackr::Connection.new(team, token, options).init
|
21
|
+
return self
|
22
|
+
end
|
23
|
+
|
24
|
+
def say(text, options = {})
|
25
|
+
Slackr::IncomingWebhook.say(connection, text, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def channels
|
29
|
+
Slackr::Channel.init(connection)
|
30
|
+
end
|
31
|
+
|
32
|
+
# support for backwards compatibility
|
33
|
+
class Webhook
|
34
|
+
def initialize(team, token, options = {})
|
35
|
+
warn "[DEPRECATION] `Slackr::Webhook.new` is deprecated. Please use `Slackr.connect` instead."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Slackr
|
4
|
+
module Channel
|
5
|
+
extend self
|
6
|
+
|
7
|
+
attr_accessor :connection
|
8
|
+
|
9
|
+
def init(connection)
|
10
|
+
@connection = connection
|
11
|
+
return self
|
12
|
+
end
|
13
|
+
|
14
|
+
def list
|
15
|
+
request = request_for(url_for('list'))
|
16
|
+
make_request(request)
|
17
|
+
end
|
18
|
+
|
19
|
+
def history(channel)
|
20
|
+
request = request_for(history_url(channel))
|
21
|
+
response = make_request(request)
|
22
|
+
messages = response["messages"]
|
23
|
+
while response["has_more"] do
|
24
|
+
oldest_timestamp = oldest_message(response["messages"])["ts"]
|
25
|
+
request = request_for(history_url(channel, oldest_timestamp))
|
26
|
+
response = make_request(request)
|
27
|
+
messages += response["messages"]
|
28
|
+
end
|
29
|
+
response.merge("messages" => messages)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def url_for(action)
|
35
|
+
"#{connection.base_url}/api/channels.#{action}?token=#{connection.token}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def request_for(url)
|
39
|
+
Net::HTTP::Get.new(url)
|
40
|
+
end
|
41
|
+
|
42
|
+
def history_url(channel, timestamp=nil)
|
43
|
+
base = "#{url_for('history')}&channel=#{channel}&count=1000"
|
44
|
+
return base unless timestamp
|
45
|
+
"#{base}&latest=#{timestamp}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def make_request(request)
|
49
|
+
response = connection.http_request(request)
|
50
|
+
if response.code != "200"
|
51
|
+
raise Slackr::ServiceError, "Slack.com - #{response.code} - #{response.body}"
|
52
|
+
end
|
53
|
+
JSON.parse(response.body)
|
54
|
+
end
|
55
|
+
|
56
|
+
def oldest_message(messages)
|
57
|
+
messages.min_by { |message| message["ts"]}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "net/https" # Obsolete as of what ruby version?
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
# slack_options
|
6
|
+
# {
|
7
|
+
# "channel" => "#myroom",
|
8
|
+
# "username" => "my_bot_name",
|
9
|
+
# "icon_url" => "https://slack.com/img/icons/app-57.png",
|
10
|
+
# "icon_emoji" => ":ghost:"
|
11
|
+
# }
|
12
|
+
module Slackr
|
13
|
+
class Connection
|
14
|
+
attr_accessor :http, :uri, :options, :team, :token
|
15
|
+
|
16
|
+
def initialize(team, token, options={})
|
17
|
+
@team, @token, @options = team, token, options
|
18
|
+
end
|
19
|
+
|
20
|
+
def init
|
21
|
+
validate_options
|
22
|
+
setup_connection
|
23
|
+
return self
|
24
|
+
end
|
25
|
+
|
26
|
+
def base_url
|
27
|
+
"https://#{@team}.slack.com"
|
28
|
+
end
|
29
|
+
|
30
|
+
def uri_request_uri
|
31
|
+
uri.request_uri
|
32
|
+
end
|
33
|
+
|
34
|
+
def http_request(request)
|
35
|
+
http.request(request)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def validate_options
|
41
|
+
(options.has_key?("channel") && options.has_key?("username")) && !options["channel"].match(/^#/).nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup_connection
|
45
|
+
@uri = URI.parse(base_url)
|
46
|
+
@http = Net::HTTP.new(@uri.host, @uri.port)
|
47
|
+
@http.use_ssl = true
|
48
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/slackr/version.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "net/https" # Obsolete as of what ruby version?
|
3
|
+
require "uri"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Slackr
|
7
|
+
module IncomingWebhook
|
8
|
+
extend self
|
9
|
+
|
10
|
+
attr_accessor :connection
|
11
|
+
|
12
|
+
# {
|
13
|
+
# :formatter => [BasicFormatter, LinkFormatter, AttachmentFormatter]
|
14
|
+
# }
|
15
|
+
def say(connection, text="", options={})
|
16
|
+
@connection = connection
|
17
|
+
#formatter = options[:formatter] || BasicFormatter
|
18
|
+
#text = format_text(formatter, text)
|
19
|
+
#TODO: fix law of demeter violations
|
20
|
+
request = Net::HTTP::Post.new(service_url)
|
21
|
+
request.body = encode_message(text, options)
|
22
|
+
response = connection.http_request(request)
|
23
|
+
if response.code != "200"
|
24
|
+
raise Slackr::ServiceError, "Slack.com - #{response.code} - #{response.body}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def service_url
|
31
|
+
"#{connection.base_url}/services/hooks/incoming-webhook?token=#{connection.token}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def encode_message(text, options)
|
35
|
+
#TODO: extract OptionValidator
|
36
|
+
#TODO: add guard against invalid options
|
37
|
+
#TODO: add guard against nil text
|
38
|
+
connection.options.merge(options).merge({"text" => text}).to_json.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
data/slackr.gemspec
CHANGED
@@ -6,11 +6,11 @@ require 'slackr/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "slackr"
|
8
8
|
spec.version = Slackr::VERSION
|
9
|
-
spec.authors = ["Jason Rohwedder"]
|
10
|
-
spec.email = ["jro@risk.io"]
|
9
|
+
spec.authors = ["Jason Rohwedder", "Mike Krisher"]
|
10
|
+
spec.email = ["jro@risk.io", "mike@mikekrisher.com"]
|
11
11
|
spec.description = %q{Talk to slack.com chat platform from ruby}
|
12
12
|
spec.summary = %q{Send data into Slack in real-time, via the Incoming Webhooks API}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/risk-io/slackr"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
-
spec.add_development_dependency "rake"
|
23
|
-
spec.add_development_dependency "rspec"
|
24
|
-
spec.add_development_dependency "webmock"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.1.1", ">= 10.1.1"
|
23
|
+
spec.add_development_dependency "rspec", "~> 2.14.1", ">= 2.14.1"
|
24
|
+
spec.add_development_dependency "webmock", "~> 1.17.4", ">= 1.17.4"
|
25
25
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Slackr::Channel do
|
4
|
+
let(:connection) { Slackr::Connection.new('team', 'fakeToken').init }
|
5
|
+
subject { Slackr::Channel.init(connection) }
|
6
|
+
|
7
|
+
|
8
|
+
describe "#list" do
|
9
|
+
let(:list_body) do
|
10
|
+
{
|
11
|
+
:ok => true,
|
12
|
+
:channels => [
|
13
|
+
{
|
14
|
+
:id => "C024BE91L",
|
15
|
+
:name => "fun",
|
16
|
+
:created => "1360782804",
|
17
|
+
:creator => "U024BE7LH",
|
18
|
+
:is_archived => false,
|
19
|
+
:is_member => false,
|
20
|
+
:num_members => 6,
|
21
|
+
:topic => {
|
22
|
+
:value => "Fun times",
|
23
|
+
:creator => "U024BE7LV",
|
24
|
+
:last_set => "1369677212"
|
25
|
+
},
|
26
|
+
:purpose => {
|
27
|
+
:value => "This channel is for fun",
|
28
|
+
:creator => "U024BE7LH",
|
29
|
+
:last_set => "1360782804"
|
30
|
+
}
|
31
|
+
}
|
32
|
+
]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
before do
|
36
|
+
stub_request(:get, "https://team.slack.com/api/channels.list?token=fakeToken").
|
37
|
+
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
|
38
|
+
to_return(:status => 200, :body => list_body.to_json, :headers => {})
|
39
|
+
|
40
|
+
stub_request(:get, "https://team.slack.com/api/channels.foo?token=fakeToken").
|
41
|
+
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
|
42
|
+
to_return(:status => 404, :body => "", :headers => {})
|
43
|
+
|
44
|
+
stub_request(:get, "https://team.slack.com/api/channels.list?token=fakeToken").
|
45
|
+
with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
|
46
|
+
to_return(:status => 200, :body => list_body.to_json, :headers => {})
|
47
|
+
end
|
48
|
+
it "requests the channel list" do
|
49
|
+
expect(subject.list).to eq(JSON.parse(list_body.to_json))
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with a bad request" do
|
53
|
+
it "raises an error" do
|
54
|
+
bad_url = "#{subject.connection.base_url}/api/channel.foo?token=#{subject.connection.token}"
|
55
|
+
expect(subject).to receive(:url_for).and_return(bad_url)
|
56
|
+
expect {
|
57
|
+
subject.list
|
58
|
+
}.to raise_error
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#history" do
|
64
|
+
let(:timestamp_1) { "1407368222.000037" }
|
65
|
+
def message(timestamp=timestamp_1)
|
66
|
+
{
|
67
|
+
"user" => "U02FBRY5Z",
|
68
|
+
"type" => "message",
|
69
|
+
"subtype" => "channel_join",
|
70
|
+
"text" => "<@U02FBRY5Z|USER> has joined the channel",
|
71
|
+
"ts" => timestamp
|
72
|
+
}
|
73
|
+
end
|
74
|
+
let(:message_1) { message }
|
75
|
+
let(:message_2) { message("1307368222.000037")}
|
76
|
+
|
77
|
+
def history_body(options={})
|
78
|
+
options = {messages:[message_1], has_more: false}.merge(options)
|
79
|
+
{
|
80
|
+
"ok" => true,
|
81
|
+
"messages" => options[:messages],
|
82
|
+
"has_more" => options[:has_more]
|
83
|
+
}
|
84
|
+
end
|
85
|
+
let(:long_history_body_1) { history_body(has_more: true) }
|
86
|
+
let(:long_history_body_2) { history_body(messages: [message_2]) }
|
87
|
+
let(:headers) { {:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}} }
|
88
|
+
|
89
|
+
let(:good_channel) { "goodChannel" }
|
90
|
+
let(:bad_channel) { "badChannel" }
|
91
|
+
let(:long_channel) { "longChannel" }
|
92
|
+
let(:base_history_url) {"https://team.slack.com/api/channels.history?token=fakeToken&count=1000&channel="}
|
93
|
+
let(:bad_history_url) { "#{base_history_url}#{bad_channel}" }
|
94
|
+
let(:good_history_url) { "#{base_history_url}#{good_channel}" }
|
95
|
+
let(:long_history_url_1) { "#{base_history_url}#{long_channel}" }
|
96
|
+
let(:long_history_url_2) { "#{long_history_url_1}&latest=#{timestamp_1}" }
|
97
|
+
|
98
|
+
before do
|
99
|
+
stub_request(:get, bad_history_url).with(headers).
|
100
|
+
to_return(:status => 404, :body => "", :headers => {})
|
101
|
+
|
102
|
+
stub_request(:get, good_history_url).with(headers).
|
103
|
+
to_return(:status => 200, :body => history_body.to_json, :headers => {})
|
104
|
+
|
105
|
+
stub_request(:get, long_history_url_1).with(headers).
|
106
|
+
to_return(:status => 200, :body => long_history_body_1.to_json, :headers => {})
|
107
|
+
stub_request(:get, long_history_url_2).with(headers).
|
108
|
+
to_return(:status => 200, :body => long_history_body_2.to_json, :headers => {})
|
109
|
+
end
|
110
|
+
it "requests the history of a channel" do
|
111
|
+
expect(subject.history(good_channel)).to eq(history_body) #TODO: Should this be JSON.parse(history_body.to_json)? why?
|
112
|
+
end
|
113
|
+
context "with lots of history" do
|
114
|
+
it "makes enough requests to get everything" do
|
115
|
+
expect(subject.history(long_channel)).to eql(history_body(messages: [message_1, message_2]))
|
116
|
+
expect(WebMock).to have_requested(:get, long_history_url_1).once
|
117
|
+
expect(WebMock).to have_requested(:get, long_history_url_2)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
context "with a bad request" do
|
121
|
+
it "raises an error" do
|
122
|
+
expect{subject.history(bad_channel)}.to raise_error
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Slackr::Connection do
|
4
|
+
describe "#init" do
|
5
|
+
before do
|
6
|
+
@subject = Slackr::Connection.new('team', 'fakeToken')
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should validate the options" do
|
10
|
+
expect(@subject).to receive(:validate_options)
|
11
|
+
@subject.init
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should setup the connection" do
|
15
|
+
expect(@subject).to receive(:setup_connection)
|
16
|
+
@subject.init
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#base_url" do
|
21
|
+
it "should return the complete url with subdomain" do
|
22
|
+
subject = Slackr::Connection.new("foo", "bar")
|
23
|
+
expect(subject.send(:base_url)).to eq("https://foo.slack.com")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#uri_request_uri" do
|
28
|
+
it "should return the request_url of the uri" do
|
29
|
+
subject = Slackr::Connection.new("team", "token", {"channel" => "#foo", "username" => "baz"})
|
30
|
+
subject.send(:setup_connection)
|
31
|
+
expect(subject.uri_request_uri).to eq("/")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#http_request" do
|
36
|
+
before do
|
37
|
+
stub_request(:post, "https://team.slack.com/").
|
38
|
+
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
|
39
|
+
to_return(:status => 200, :body => "", :headers => {})
|
40
|
+
|
41
|
+
stub_request(:post, "https://team.slack.com/").
|
42
|
+
with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
|
43
|
+
to_return(:status => 200, :body => "", :headers => {})
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return the request_url of the http" do
|
47
|
+
subject = Slackr::Connection.new("team", "token", {"channel" => "#foo", "username" => "baz"})
|
48
|
+
subject.send(:setup_connection)
|
49
|
+
request = Net::HTTP::Post.new(subject.uri_request_uri)
|
50
|
+
response = subject.http_request(request)
|
51
|
+
expect(response.code).to eq("200")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
describe "#validate_options" do
|
57
|
+
before do
|
58
|
+
@subject = Slackr::Connection.new('team', 'fakeToken')
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with valid options' do
|
62
|
+
it "should return true" do
|
63
|
+
options = {
|
64
|
+
"channel" => "#myroom",
|
65
|
+
"username" => "my_bot_name"
|
66
|
+
}
|
67
|
+
@subject.instance_variable_set(:@options, options)
|
68
|
+
expect(@subject.send(:validate_options)).to eq(true)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with invalid options' do
|
73
|
+
context 'with improper channel name' do
|
74
|
+
it "should return false" do
|
75
|
+
options = {
|
76
|
+
"channel" => "foo",
|
77
|
+
"username" => "my_bot_name"
|
78
|
+
}
|
79
|
+
@subject.instance_variable_set(:@options, options)
|
80
|
+
expect(@subject.send(:validate_options)).to eq(false)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'with missing username' do
|
85
|
+
it "should return false" do
|
86
|
+
options = {
|
87
|
+
"channel" => "foo"
|
88
|
+
}
|
89
|
+
@subject.instance_variable_set(:@options, options)
|
90
|
+
expect(@subject.send(:validate_options)).to eq(false)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#setup_connection" do
|
97
|
+
before do
|
98
|
+
@subject = Slackr::Connection.new("team", "token", {"channel" => "#foo", "username" => "bot"})
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should define the uri instance variable" do
|
102
|
+
expect(@subject.uri).to be_nil
|
103
|
+
@subject.send(:setup_connection)
|
104
|
+
expect(@subject.uri).to_not be_nil
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should define the http isntance variable" do
|
108
|
+
expect(@subject.http).to be_nil
|
109
|
+
@subject.send(:setup_connection)
|
110
|
+
expect(@subject.http).to_not be_nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Slackr::IncomingWebhook do
|
4
|
+
before do
|
5
|
+
@connection = Slackr::Connection.new("team", "token", {})
|
6
|
+
@connection.send(:setup_connection)
|
7
|
+
|
8
|
+
stub_request(:post, "https://team.slack.com/services/hooks/incoming-webhook?token=token").
|
9
|
+
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
|
10
|
+
to_return(:status => 200, :body => "", :headers => {})
|
11
|
+
|
12
|
+
stub_request(:post, "https://team.slack.com/services/hooks/incoming-webhook?token=token").
|
13
|
+
with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
|
14
|
+
to_return(:status => 200, :body => "", :headers => {})
|
15
|
+
end
|
16
|
+
|
17
|
+
context "Public API" do
|
18
|
+
describe "#say" do
|
19
|
+
it "should send the request, posting the message in the channel" do
|
20
|
+
text = "This is a test message"
|
21
|
+
options = {"channel" => "#somewhere"}
|
22
|
+
expect(Slackr::IncomingWebhook).to receive(:encode_message)
|
23
|
+
Slackr::IncomingWebhook.say(@connection, text, options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "Private API" do
|
29
|
+
describe "#service_url" do
|
30
|
+
it "should generate the right url" do
|
31
|
+
subject.instance_variable_set(:@connection, @connection)
|
32
|
+
result = subject.send(:service_url)
|
33
|
+
expect(result).to eq "https://team.slack.com/services/hooks/incoming-webhook?token=token"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#encode_message" do
|
38
|
+
it "should encode a basic message" do
|
39
|
+
text = "this is my awesome message"
|
40
|
+
|
41
|
+
result = subject.send(:encode_message, text, {})
|
42
|
+
expect(result).to eq "{\"text\":\"#{text}\"}"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should encode a message with options" do
|
46
|
+
text = "this is my awesome message"
|
47
|
+
options = {"channel" => "#awesometown"}
|
48
|
+
|
49
|
+
result = subject.send(:encode_message, text, options)
|
50
|
+
expect(result).to eq "{\"channel\":\"#awesometown\",\"text\":\"#{text}\"}"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should encode a basic message when there are default options" do
|
54
|
+
text = "this is my awesome message"
|
55
|
+
|
56
|
+
subject.connection.stub(:options).and_return({"icon_emoji" => "ghost"})
|
57
|
+
result = subject.send(:encode_message, text, {})
|
58
|
+
expect(result).to eq("{\"icon_emoji\":\"ghost\",\"text\":\"#{text}\"}")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should encode a message with option when there are default options present" do
|
62
|
+
text = "this is my awesome message"
|
63
|
+
options = {"channel" => "#awesometown"}
|
64
|
+
|
65
|
+
subject.connection.stub(:options).and_return({"icon_emoji" => "ghost"})
|
66
|
+
result = subject.send(:encode_message, text, options)
|
67
|
+
expect(result).to eq("{\"icon_emoji\":\"ghost\",\"channel\":\"#awesometown\",\"text\":\"#{text}\"}")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/spec/slackr_spec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Slackr do
|
4
|
+
describe "#say" do
|
5
|
+
it "should delegate to the IncomingWebhook" do
|
6
|
+
subject = Slackr.connect("foo", "bar", {"channel" => "#general", "username" => "baz"})
|
7
|
+
expect(Slackr::IncomingWebhook).to receive(:say)
|
8
|
+
subject.say("hello world", {})
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "Webook.new" do
|
13
|
+
before do
|
14
|
+
@orig_stderr = $stderr
|
15
|
+
$stderr = StringIO.new
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should raise a deprecation warning" do
|
19
|
+
Slackr::Webhook.new("team", "token")
|
20
|
+
$stderr.rewind
|
21
|
+
$stderr.string.chomp.should match(/^\[DEPRECATION\]/)
|
22
|
+
end
|
23
|
+
|
24
|
+
after do
|
25
|
+
$stderr = @orig_stderr
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
2
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
1
|
+
Dir.glob('../lib/**/*.rb', &method(:require))
|
3
2
|
|
4
3
|
require 'slackr'
|
5
4
|
require 'webmock/rspec'
|
5
|
+
require 'pry'
|
6
6
|
|
7
7
|
RSpec.configure do |config|
|
8
8
|
# Use color in STDOUT
|
metadata
CHANGED
@@ -2,99 +2,125 @@
|
|
2
2
|
name: slackr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jason Rohwedder
|
9
|
+
- Mike Krisher
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2014-
|
13
|
+
date: 2014-09-13 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
16
|
+
type: :development
|
15
17
|
prerelease: false
|
16
18
|
name: bundler
|
17
|
-
type: :development
|
18
19
|
requirement: !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
19
21
|
requirements:
|
20
22
|
- - ~>
|
21
23
|
- !ruby/object:Gem::Version
|
22
24
|
version: '1.3'
|
23
|
-
none: false
|
24
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
25
27
|
requirements:
|
26
28
|
- - ~>
|
27
29
|
- !ruby/object:Gem::Version
|
28
30
|
version: '1.3'
|
29
|
-
none: false
|
30
31
|
- !ruby/object:Gem::Dependency
|
32
|
+
type: :development
|
31
33
|
prerelease: false
|
32
34
|
name: rake
|
33
|
-
type: :development
|
34
35
|
requirement: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
35
37
|
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 10.1.1
|
36
41
|
- - ! '>='
|
37
42
|
- !ruby/object:Gem::Version
|
38
|
-
version:
|
39
|
-
none: false
|
43
|
+
version: 10.1.1
|
40
44
|
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
41
46
|
requirements:
|
47
|
+
- - ~>
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 10.1.1
|
42
50
|
- - ! '>='
|
43
51
|
- !ruby/object:Gem::Version
|
44
|
-
version:
|
45
|
-
none: false
|
52
|
+
version: 10.1.1
|
46
53
|
- !ruby/object:Gem::Dependency
|
54
|
+
type: :development
|
47
55
|
prerelease: false
|
48
56
|
name: rspec
|
49
|
-
type: :development
|
50
57
|
requirement: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
51
59
|
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 2.14.1
|
52
63
|
- - ! '>='
|
53
64
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
55
|
-
none: false
|
65
|
+
version: 2.14.1
|
56
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
57
68
|
requirements:
|
69
|
+
- - ~>
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: 2.14.1
|
58
72
|
- - ! '>='
|
59
73
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
61
|
-
none: false
|
74
|
+
version: 2.14.1
|
62
75
|
- !ruby/object:Gem::Dependency
|
76
|
+
type: :development
|
63
77
|
prerelease: false
|
64
78
|
name: webmock
|
65
|
-
type: :development
|
66
79
|
requirement: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
67
81
|
requirements:
|
82
|
+
- - ~>
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 1.17.4
|
68
85
|
- - ! '>='
|
69
86
|
- !ruby/object:Gem::Version
|
70
|
-
version:
|
71
|
-
none: false
|
87
|
+
version: 1.17.4
|
72
88
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
73
90
|
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.17.4
|
74
94
|
- - ! '>='
|
75
95
|
- !ruby/object:Gem::Version
|
76
|
-
version:
|
77
|
-
none: false
|
96
|
+
version: 1.17.4
|
78
97
|
description: Talk to slack.com chat platform from ruby
|
79
98
|
email:
|
80
99
|
- jro@risk.io
|
100
|
+
- mike@mikekrisher.com
|
81
101
|
executables: []
|
82
102
|
extensions: []
|
83
103
|
extra_rdoc_files: []
|
84
104
|
files:
|
85
105
|
- .gitignore
|
106
|
+
- .travis.yml
|
86
107
|
- Gemfile
|
87
108
|
- LICENSE.txt
|
88
109
|
- README.md
|
89
110
|
- Rakefile
|
90
111
|
- lib/slackr.rb
|
112
|
+
- lib/slackr/channel.rb
|
113
|
+
- lib/slackr/connection.rb
|
91
114
|
- lib/slackr/errors.rb
|
92
115
|
- lib/slackr/version.rb
|
93
|
-
- lib/slackr/
|
116
|
+
- lib/slackr/webhooks/incoming.rb
|
94
117
|
- slackr.gemspec
|
118
|
+
- spec/slackr/channel_spec.rb
|
119
|
+
- spec/slackr/connection_spec.rb
|
120
|
+
- spec/slackr/webhooks/incoming_spec.rb
|
121
|
+
- spec/slackr_spec.rb
|
95
122
|
- spec/spec_helper.rb
|
96
|
-
|
97
|
-
homepage: ''
|
123
|
+
homepage: https://github.com/risk-io/slackr
|
98
124
|
licenses:
|
99
125
|
- MIT
|
100
126
|
post_install_message:
|
@@ -102,29 +128,26 @@ rdoc_options: []
|
|
102
128
|
require_paths:
|
103
129
|
- lib
|
104
130
|
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
none: false
|
105
132
|
requirements:
|
106
133
|
- - ! '>='
|
107
134
|
- !ruby/object:Gem::Version
|
108
|
-
segments:
|
109
|
-
- 0
|
110
|
-
hash: 4548143177958109987
|
111
135
|
version: '0'
|
112
|
-
none: false
|
113
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
114
138
|
requirements:
|
115
139
|
- - ! '>='
|
116
140
|
- !ruby/object:Gem::Version
|
117
|
-
segments:
|
118
|
-
- 0
|
119
|
-
hash: 4548143177958109987
|
120
141
|
version: '0'
|
121
|
-
none: false
|
122
142
|
requirements: []
|
123
143
|
rubyforge_project:
|
124
|
-
rubygems_version: 1.8.
|
144
|
+
rubygems_version: 1.8.23.2
|
125
145
|
signing_key:
|
126
146
|
specification_version: 3
|
127
147
|
summary: Send data into Slack in real-time, via the Incoming Webhooks API
|
128
148
|
test_files:
|
149
|
+
- spec/slackr/channel_spec.rb
|
150
|
+
- spec/slackr/connection_spec.rb
|
151
|
+
- spec/slackr/webhooks/incoming_spec.rb
|
152
|
+
- spec/slackr_spec.rb
|
129
153
|
- spec/spec_helper.rb
|
130
|
-
- spec/webhook_spec.rb
|
data/lib/slackr/webhook.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
require "net/http"
|
2
|
-
require "net/https" # Obsolete as of what ruby version?
|
3
|
-
require "uri"
|
4
|
-
require "json"
|
5
|
-
|
6
|
-
# slack_options
|
7
|
-
# {
|
8
|
-
# "channel" => "#myroom",
|
9
|
-
# "username" => "my_bot_name",
|
10
|
-
# "icon_url" => "https://slack.com/img/icons/app-57.png",
|
11
|
-
# "icon_emoji" => ":ghost:"
|
12
|
-
# }
|
13
|
-
|
14
|
-
module Slackr
|
15
|
-
|
16
|
-
class Webhook
|
17
|
-
attr_reader :http, :uri, :default_options
|
18
|
-
|
19
|
-
def initialize(team,token,options={})
|
20
|
-
raise Slackr::ArgumentError, "Team required" if team.nil?
|
21
|
-
raise Slackr::ArgumentError, "Token required" if token.nil?
|
22
|
-
|
23
|
-
@team = team
|
24
|
-
@token = token
|
25
|
-
@default_options = options
|
26
|
-
|
27
|
-
setup_connection
|
28
|
-
end
|
29
|
-
|
30
|
-
def say(text="",options={})
|
31
|
-
# reformat links, etc here
|
32
|
-
send_request(text,options)
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def setup_connection
|
38
|
-
@uri = URI.parse(service_url)
|
39
|
-
@http = Net::HTTP.new(@uri.host, @uri.port)
|
40
|
-
@http.use_ssl = true
|
41
|
-
@http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
42
|
-
end
|
43
|
-
|
44
|
-
def service_url
|
45
|
-
"https://#{@team}.slack.com/services/hooks/incoming-webhook?token=#{@token}"
|
46
|
-
end
|
47
|
-
|
48
|
-
def encode_message(text,options)
|
49
|
-
"payload=#{default_options.merge(options).merge({"text" => text}).to_json.to_s}"
|
50
|
-
end
|
51
|
-
|
52
|
-
def send_request(text,options)
|
53
|
-
request = Net::HTTP::Post.new(uri.request_uri)
|
54
|
-
request.body = encode_message(text,options)
|
55
|
-
response = http.request(request)
|
56
|
-
unless response.code == "200"
|
57
|
-
raise Slackr::ServiceError, "Slack.com - #{response.code} - #{response.body}"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
62
|
-
end
|
data/spec/webhook_spec.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Slackr::Webhook do
|
4
|
-
let(:client) { Slackr::Webhook.new("team","token") }
|
5
|
-
subject { client }
|
6
|
-
|
7
|
-
describe "#say" do
|
8
|
-
it "should call Webhook#send_request with proper options" do
|
9
|
-
msg = "This is a test message"
|
10
|
-
opts = {"channel" => "#somewhere"}
|
11
|
-
|
12
|
-
subject.should_receive(:send_request).with(msg,opts)
|
13
|
-
subject.say msg,opts
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe "#service_url" do
|
18
|
-
it "should generate the right url" do
|
19
|
-
team = "my-team"
|
20
|
-
token = "my-token"
|
21
|
-
subject = Slackr::Webhook.new(team,token)
|
22
|
-
|
23
|
-
subject.send(:service_url).should eq "https://#{team}.slack.com/services/hooks/incoming-webhook?token=#{token}"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
describe "#encode_message" do
|
28
|
-
it "should encode a basic message" do
|
29
|
-
msg = "this is my awesome message"
|
30
|
-
|
31
|
-
subject.send(:encode_message,msg,{}).should eq "payload={\"text\":\"#{msg}\"}"
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should encode a message with options" do
|
35
|
-
msg = "this is my awesome message"
|
36
|
-
opts = {"channel" => "#awesometown"}
|
37
|
-
|
38
|
-
subject.send(:encode_message,msg,opts).should eq "payload={\"channel\":\"#awesometown\",\"text\":\"#{msg}\"}"
|
39
|
-
end
|
40
|
-
|
41
|
-
it "should encode a basic message when there are default options" do
|
42
|
-
msg = "this is my awesome message"
|
43
|
-
|
44
|
-
subject.stub(:default_options).and_return({"icon_emoji" => "ghost"})
|
45
|
-
subject.send(:encode_message,msg,{}).should eq "payload={\"icon_emoji\":\"ghost\",\"text\":\"#{msg}\"}"
|
46
|
-
end
|
47
|
-
|
48
|
-
it "should encode a message with option when there are default options present" do
|
49
|
-
msg = "this is my awesome message"
|
50
|
-
opts = {"channel" => "#awesometown"}
|
51
|
-
|
52
|
-
subject.stub(:default_options).and_return({"icon_emoji" => "ghost"})
|
53
|
-
subject.send(:encode_message,msg,opts).should eq "payload={\"icon_emoji\":\"ghost\",\"channel\":\"#awesometown\",\"text\":\"#{msg}\"}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|