ferris 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ require_relative 'ferris/metadata'
2
+ require_relative 'ferris/api'
3
+ require_relative 'ferris/rtm'
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: