ferris 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/Readme.md +45 -0
- data/VERSION +1 -0
- data/lib/ferris.rb +3 -0
- data/lib/ferris/api.rb +30 -0
- data/lib/ferris/metadata.rb +48 -0
- data/lib/ferris/rtm.rb +177 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 44f7b3f20a88bfe85335fca5585c9defa5d0f49f
|
4
|
+
data.tar.gz: ef9e9b221ab4401946e385b6e9ec6325fcad3c87
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e66d90192fe63c8c05216225a5dc15f9597895a1d4506a45cd2d4dceb8701bc7a7ae3c4559bc2fff95b593b02ed7b27a443e99e0ffb605c7a205791b40e74f03
|
7
|
+
data.tar.gz: 0fca2026e06abb0f329b565b57d84e658a979f347dd1a1624954b31764d16d57e87f0e4a4702d62a85d93becbf7a651138ae1432b556baed4d3186258601790f
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2015 Sean Clemmer and Blue Jeans Network
|
2
|
+
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
purpose with or without fee is hereby granted, provided that the above
|
5
|
+
copyright notice and this permission notice appear in all copies.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
8
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
9
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
10
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
11
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
12
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
13
|
+
PERFORMANCE OF THIS SOFTWARE.
|
data/Readme.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Ferris
|
2
|
+
|
3
|
+
A simple Slack client for Ruby
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
#!/usr/bin/env ruby
|
7
|
+
require 'ferris'
|
8
|
+
|
9
|
+
TOKEN, CHANNEL = '...', '...'
|
10
|
+
|
11
|
+
# Interact with the Real-Time Messaging API
|
12
|
+
rtm = Ferris::RTM.new token: TOKEN
|
13
|
+
|
14
|
+
# Interact with the regular API methods
|
15
|
+
api = Ferris::API.new token: TOKEN
|
16
|
+
|
17
|
+
# Callback for messages from the RTM API
|
18
|
+
rtm.on :message do |m|
|
19
|
+
puts m
|
20
|
+
end
|
21
|
+
|
22
|
+
# Add as many callbacks as you like, they'll
|
23
|
+
# each get a copy of incoming messages
|
24
|
+
rtm.on :message do |m|
|
25
|
+
$stderr.puts message: m
|
26
|
+
end
|
27
|
+
|
28
|
+
rtm.go # Start the RTM API worker in the background
|
29
|
+
|
30
|
+
# The RTM API allows you to send basic messages
|
31
|
+
# See https://api.slack.com/rtm for docs
|
32
|
+
rtm.send 'message', \
|
33
|
+
channel: CHANNEL, text: 'Hello from the RTM API'
|
34
|
+
|
35
|
+
rtm.send 'ping' # Send "ping" and get back "pong"
|
36
|
+
|
37
|
+
# Use the regular API for advanced scenarios (e.g. attachments)
|
38
|
+
# See https://api.slack.com/methods for docs
|
39
|
+
api.send 'chat.postMessage', \
|
40
|
+
channel: CHANNEL, text: 'Hello from the regular API'
|
41
|
+
|
42
|
+
rtm.join # Bring the RTM API worker into the foreground
|
43
|
+
|
44
|
+
rtm.kill # We'll never reach here...
|
45
|
+
```
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/lib/ferris.rb
ADDED
data/lib/ferris/api.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
require 'slog'
|
5
|
+
|
6
|
+
Thread.abort_on_exception = true
|
7
|
+
|
8
|
+
|
9
|
+
module Ferris
|
10
|
+
class API
|
11
|
+
def initialize options={}
|
12
|
+
@token = options.fetch :token
|
13
|
+
@logger = options.fetch :logger, Slog.new
|
14
|
+
@api_url = options.fetch :api_url, 'https://slack.com/api'
|
15
|
+
log.trace event: 'API client initialized'
|
16
|
+
end
|
17
|
+
|
18
|
+
def send method, options={}
|
19
|
+
uri = URI File.join(@api_url, method)
|
20
|
+
options = { token: @token }.merge(options)
|
21
|
+
log.trace event: 'sending api request', method: method, options: options
|
22
|
+
res = Net::HTTP.post_form uri, options
|
23
|
+
log.debug event: 'sent api request', method: method, options: options, response: res
|
24
|
+
JSON.parse res.body
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def log ; @logger end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Ferris
|
2
|
+
|
3
|
+
# We use a VERSION file to tie into our build pipeline
|
4
|
+
VERSION = File.read(File.join(File.dirname(__FILE__), '..', '..', 'VERSION')).strip
|
5
|
+
|
6
|
+
# We don't really do all that much, be humble
|
7
|
+
SUMMARY = 'A simple Slack client for Ruby'
|
8
|
+
|
9
|
+
# Like the MIT license, but even simpler
|
10
|
+
LICENSE = 'ISC'
|
11
|
+
|
12
|
+
# Where you should look first
|
13
|
+
HOMEPAGE = 'https://github.com/sczizzo/ferris'
|
14
|
+
|
15
|
+
# Your benevolent dictator for life
|
16
|
+
AUTHOR = 'Sean Clemmer'
|
17
|
+
|
18
|
+
# Turn here to strangle your dictator
|
19
|
+
EMAIL = 'sclemmer@bluejeans.com'
|
20
|
+
|
21
|
+
# Bundled extensions
|
22
|
+
TRAVELING_RUBY_BUCKET = 'http://d6r77u77i8pq3.cloudfront.net'
|
23
|
+
TRAVELING_RUBY_VERSION = '20150715-2.2.2'
|
24
|
+
EM_VERSION = '1.0.4'
|
25
|
+
|
26
|
+
# Every project deserves its own ASCII art
|
27
|
+
ART = <<-'EOART' % VERSION
|
28
|
+
|
29
|
+
|
30
|
+
***
|
31
|
+
** *** *
|
32
|
+
** *** ***
|
33
|
+
** *
|
34
|
+
** *** **** *** **** ****
|
35
|
+
****** *** **** **** * **** **** * *** * **** *
|
36
|
+
***** * *** ** **** ** **** *** ** ****
|
37
|
+
** * *** ** ** ** ****
|
38
|
+
** ** *** ** ** ** ***
|
39
|
+
** ******** ** ** ** ***
|
40
|
+
** ******* ** ** ** ***
|
41
|
+
** ** ** ** ** **** **
|
42
|
+
** **** * *** *** ** * **** *
|
43
|
+
** ******* *** *** *** * ****
|
44
|
+
** ***** ***
|
45
|
+
v%s
|
46
|
+
|
47
|
+
EOART
|
48
|
+
end
|
data/lib/ferris/rtm.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'socket'
|
3
|
+
require 'thread'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
require 'slog'
|
7
|
+
require 'websocket/driver'
|
8
|
+
|
9
|
+
Thread.abort_on_exception = true
|
10
|
+
|
11
|
+
|
12
|
+
module Ferris
|
13
|
+
class RTM
|
14
|
+
HTTPS_PORT = 443
|
15
|
+
SOCKET_READ_SIZE = 1024
|
16
|
+
|
17
|
+
def initialize options={}
|
18
|
+
@id = 0
|
19
|
+
@jobs = []
|
20
|
+
@stopped = false
|
21
|
+
@callbacks = Hash.new { |h,k| h[k] = [] }
|
22
|
+
@q = Queue.new
|
23
|
+
@qlock = Mutex.new
|
24
|
+
@token = options.fetch :token
|
25
|
+
@api_url = options.fetch :api_url, 'https://slack.com/api'
|
26
|
+
@logger = options.fetch :logger, Slog.new
|
27
|
+
@wss_uri = construct_wss_uri @token, @api_url
|
28
|
+
log.trace event: 'RTM client initialized', wss_uri: @wss_uri.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def on event, &block
|
33
|
+
unless [ :open, :close, :error, :message ].include? event
|
34
|
+
log.fatal event: 'invalid event', on_event: event
|
35
|
+
raise ArgumentError
|
36
|
+
end
|
37
|
+
|
38
|
+
log.debug event: 'adding callback', on_event: event
|
39
|
+
@callbacks[event] << block
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def send type, message={}
|
44
|
+
log.debug event: 'sending message', message: message
|
45
|
+
message[:id] ||= next_id
|
46
|
+
message[:type] = type
|
47
|
+
enqueue message
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def go
|
53
|
+
log.trace event: 'connecting'
|
54
|
+
socket, driver = connect
|
55
|
+
log.debug event: 'connected'
|
56
|
+
@jobs << go_read_input(socket, driver)
|
57
|
+
@jobs << go_write_output(socket, driver)
|
58
|
+
@jobs
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def kill ; @jobs.map(&:kill) end
|
63
|
+
|
64
|
+
def join ; @jobs.map(&:join) end
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def log ; @logger end
|
71
|
+
|
72
|
+
|
73
|
+
def construct_wss_uri token, api_url
|
74
|
+
uri = URI File.join(api_url, 'rtm.start')
|
75
|
+
req = Net::HTTP.post_form uri, token: token
|
76
|
+
bod = JSON.parse req.body, symbolize_names: true
|
77
|
+
URI bod[:url]
|
78
|
+
|
79
|
+
rescue
|
80
|
+
x = { event: 'could not construct WSS URI' }
|
81
|
+
x[:error] = bod[:error] if bod && bod[:error]
|
82
|
+
log.fatal x
|
83
|
+
raise RuntimeError
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def next_id
|
88
|
+
@qlock.synchronize { @id += 1 }
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def enqueue message
|
93
|
+
@qlock.synchronize { @q << message }
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def full_dequeue q=[]
|
98
|
+
nq = @qlock.synchronize { q << @q.shift until @q.empty? ; q }
|
99
|
+
nq
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def wrapper socket
|
104
|
+
s = Struct.new :url, :socket do
|
105
|
+
def write *args
|
106
|
+
self.socket.write *args
|
107
|
+
end
|
108
|
+
end
|
109
|
+
s.new @wss_uri.to_s, socket
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def connect
|
114
|
+
tcp_socket = TCPSocket.new @wss_uri.host, HTTPS_PORT
|
115
|
+
ssl_socket = OpenSSL::SSL::SSLSocket.new tcp_socket
|
116
|
+
ssl_socket.connect
|
117
|
+
|
118
|
+
driver = WebSocket::Driver.client wrapper(ssl_socket)
|
119
|
+
|
120
|
+
driver.on :open do |_|
|
121
|
+
log.trace event: 'driver open'
|
122
|
+
@callbacks[:open].map(&:call)
|
123
|
+
end
|
124
|
+
|
125
|
+
driver.on :close do |_|
|
126
|
+
log.trace event: 'driver close'
|
127
|
+
@callbacks[:close].map(&:call)
|
128
|
+
end
|
129
|
+
|
130
|
+
driver.on :error do |_|
|
131
|
+
log.trace event: 'driver error'
|
132
|
+
@callbacks[:error].map(&:call)
|
133
|
+
end
|
134
|
+
|
135
|
+
driver.on :message do |event|
|
136
|
+
log.trace event: 'driver message', message: event
|
137
|
+
data = JSON.parse event.data
|
138
|
+
@callbacks[:message].map { |c| c.call data }
|
139
|
+
end
|
140
|
+
|
141
|
+
driver.start
|
142
|
+
[ ssl_socket, driver ]
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def go_read_input socket, driver
|
147
|
+
Thread.new do
|
148
|
+
loop do
|
149
|
+
log.trace event: 'reading socket'
|
150
|
+
data = socket.readpartial SOCKET_READ_SIZE
|
151
|
+
log.debug event: 'read socket', data_size: data.size
|
152
|
+
next if data.nil? || data.empty?
|
153
|
+
driver.parse data
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
def go_write_output socket, driver, delay=0.1
|
160
|
+
Thread.new do
|
161
|
+
loop do
|
162
|
+
q = full_dequeue
|
163
|
+
|
164
|
+
q.each do |m|
|
165
|
+
sent = driver.text JSON.generate(m)
|
166
|
+
unless sent
|
167
|
+
log.fatal event: 'could not send', message: m
|
168
|
+
raise RuntimeError
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
sleep delay if q.empty?
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ferris
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sean Clemmer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: slog
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.19'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.19'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: websocket-driver
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.6'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: eventmachine
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.0.4
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.0.4
|
69
|
+
description: A simple Slack client for Ruby.
|
70
|
+
email: sclemmer@bluejeans.com
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- LICENSE
|
76
|
+
- Readme.md
|
77
|
+
- VERSION
|
78
|
+
- lib/ferris.rb
|
79
|
+
- lib/ferris/api.rb
|
80
|
+
- lib/ferris/metadata.rb
|
81
|
+
- lib/ferris/rtm.rb
|
82
|
+
homepage: https://github.com/sczizzo/ferris
|
83
|
+
licenses:
|
84
|
+
- ISC
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 2.4.5.1
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: A simple Slack client for Ruby
|
106
|
+
test_files: []
|
107
|
+
has_rdoc:
|