slackr 0.0.2 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/risk-io/slackr.svg?branch=master)](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
|