ruby_gntp 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +12 -0
- data/lib/ruby_gntp.rb +149 -37
- data/test/ruby_gntp_spec.rb +74 -0
- data/test/ruby_gntp_spec_helper.rb +34 -0
- metadata +10 -5
data/ChangeLog
CHANGED
@@ -16,3 +16,15 @@
|
|
16
16
|
== Version 0.1.1 - 2009/4/17
|
17
17
|
* Add NOTE to example directory.
|
18
18
|
|
19
|
+
== Version 0.1.2 - 2009/08/15
|
20
|
+
* Enabled password authentication mainly for sending notifications to a network machine.
|
21
|
+
|
22
|
+
== Version 0.1.3 - 2009/08/19
|
23
|
+
* Added notification icon sending.
|
24
|
+
* Now sends out Origin-X headers.
|
25
|
+
* Use \r\n instead of \n in the header lines.
|
26
|
+
|
27
|
+
== Version 0.2.0 - 2009/11/03
|
28
|
+
* Merge spidahman's commits. Lot of thanks, spidahman!
|
29
|
+
* Add some test(spec).
|
30
|
+
|
data/lib/ruby_gntp.rb
CHANGED
@@ -29,6 +29,7 @@
|
|
29
29
|
#
|
30
30
|
#}}}
|
31
31
|
require 'socket'
|
32
|
+
require 'digest/md5'
|
32
33
|
|
33
34
|
#$DEBUG = true
|
34
35
|
|
@@ -36,13 +37,17 @@ class TooFewParametersError < Exception
|
|
36
37
|
end
|
37
38
|
|
38
39
|
class GNTP
|
39
|
-
attr_reader :app_name, :target_host, :target_port
|
40
|
+
attr_reader :app_name, :target_host, :target_port, :password
|
40
41
|
attr_reader :message if $DEBUG
|
41
42
|
|
42
|
-
|
43
|
+
RUBY_GNTP_NAME = 'ruby_gntp'
|
44
|
+
RUBY_GNTP_VERSION = '0.1.3.1'
|
45
|
+
|
46
|
+
def initialize(app_name = 'Ruby/GNTP', host = 'localhost', password = '', port = 23053)
|
43
47
|
@app_name = app_name
|
44
48
|
@target_host = host
|
45
49
|
@target_port = port
|
50
|
+
@password = password
|
46
51
|
end
|
47
52
|
|
48
53
|
#
|
@@ -53,36 +58,37 @@ class GNTP
|
|
53
58
|
raise TooFewParametersError, "Need least one 'notification' for register" unless @notifications
|
54
59
|
|
55
60
|
@app_icon = params[:app_icon]
|
61
|
+
@binaries = []
|
62
|
+
|
63
|
+
@message = register_header(@app_name, @app_icon)
|
64
|
+
@message << output_origin_headers
|
56
65
|
|
57
|
-
@message
|
58
|
-
|
59
|
-
Application-Name: #{@app_name}
|
60
|
-
Notifications-Count: #{@notifications.size}
|
61
|
-
EOF
|
62
|
-
@message << "Application-Icon: #{@app_icon}\n" if @app_icon
|
63
|
-
@message << "\n"
|
66
|
+
@message << "Notifications-Count: #{@notifications.size}\r\n"
|
67
|
+
@message << "\r\n"
|
64
68
|
|
65
69
|
@notifications.each do |notification|
|
66
70
|
name = notification[:name]
|
67
|
-
disp_name = notification[:disp_name]
|
71
|
+
disp_name = notification[:disp_name] || name
|
68
72
|
enabled = notification[:enabled] || true
|
69
73
|
icon = notification[:icon]
|
70
74
|
|
71
|
-
@message
|
72
|
-
Notification-
|
73
|
-
|
74
|
-
@message << "
|
75
|
-
@message << "Notification-Enabled: #{enabled}\n" if enabled
|
76
|
-
@message << "Notification-Icon: #{icon}\n" if icon
|
77
|
-
@message << "\n"
|
75
|
+
@message << "Notification-Name: #{name}\r\n"
|
76
|
+
@message << "Notification-Enabled: #{enabled ? 'True' : 'False'}\r\n"
|
77
|
+
@message << "Notification-Display-Name: #{disp_name}\r\n"
|
78
|
+
@message << "#{handle_icon(icon, 'Notification')}\r\n" if icon
|
78
79
|
end
|
79
80
|
|
81
|
+
@binaries.each {|binary|
|
82
|
+
@message << output_binary(binary)
|
83
|
+
}
|
84
|
+
|
85
|
+
@message << "\r\n"
|
86
|
+
|
80
87
|
unless (ret = send_and_recieve(@message))
|
81
88
|
raise "Register failed"
|
82
89
|
end
|
83
90
|
end
|
84
91
|
|
85
|
-
|
86
92
|
#
|
87
93
|
# notify
|
88
94
|
#
|
@@ -95,16 +101,16 @@ EOF
|
|
95
101
|
icon = params[:icon] || get_notification_icon(name)
|
96
102
|
sticky = params[:sticky]
|
97
103
|
|
98
|
-
@
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
@message << "\n"
|
104
|
+
@binaries = []
|
105
|
+
|
106
|
+
@message = notify_header(app_name, name, title, text, sticky, icon)
|
107
|
+
@message << output_origin_headers
|
108
|
+
|
109
|
+
@binaries.each {|binary|
|
110
|
+
@message << output_binary(binary)
|
111
|
+
}
|
112
|
+
|
113
|
+
@message << "\r\n"
|
108
114
|
|
109
115
|
unless (ret = send_and_recieve(@message))
|
110
116
|
raise "Notify failed"
|
@@ -116,7 +122,11 @@ EOF
|
|
116
122
|
# instant notification
|
117
123
|
#
|
118
124
|
def self.notify(params)
|
119
|
-
|
125
|
+
host = params[:host]
|
126
|
+
passwd = params[:passwd]
|
127
|
+
|
128
|
+
growl = GNTP.new(params[:app_name], host, passwd)
|
129
|
+
|
120
130
|
notification = params
|
121
131
|
notification[:name] = params[:app_name] || "Ruby/GNTP notification"
|
122
132
|
growl.register(:notifications => [
|
@@ -131,7 +141,6 @@ EOF
|
|
131
141
|
# send and recieve
|
132
142
|
#
|
133
143
|
def send_and_recieve msg
|
134
|
-
msg.gsub!(/\n/, "\r\n")
|
135
144
|
print msg if $DEBUG
|
136
145
|
|
137
146
|
sock = TCPSocket.open(@target_host, @target_port)
|
@@ -156,29 +165,132 @@ EOF
|
|
156
165
|
return nil unless notification
|
157
166
|
return notification[:icon]
|
158
167
|
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# outputs the registration header
|
171
|
+
#
|
172
|
+
def register_header(app_name, app_icon)
|
173
|
+
message = "#{get_gntp_header_start('REGISTER')}\r\n"
|
174
|
+
message << "Application-Name: #{app_name}\r\n"
|
175
|
+
message << "#{handle_icon(@app_icon, 'Application')}\r\n" if app_icon
|
176
|
+
message
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# outputs the notification header
|
181
|
+
#
|
182
|
+
def notify_header(app_name, name, title, text, sticky, icon)
|
183
|
+
message = "#{get_gntp_header_start('NOTIFY')}\r\n"
|
184
|
+
message << "Application-Name: #{@app_name}\r\n"
|
185
|
+
message << "Notification-Name: #{name}\r\n"
|
186
|
+
message << "Notification-Title: #{title}\r\n"
|
187
|
+
message << "Notification-Text: #{text}\r\n" if text
|
188
|
+
message << "Notification-Sticky: #{sticky}\r\n" if sticky
|
189
|
+
message << "#{handle_icon(icon, 'Notification')}\r\n" if icon
|
190
|
+
message
|
191
|
+
end
|
192
|
+
|
193
|
+
def output_origin_headers
|
194
|
+
message = "Origin-Machine-Name: #{Socket.gethostname}\r\n"
|
195
|
+
message << "Origin-Software-Name: #{RUBY_GNTP_NAME}\r\n"
|
196
|
+
message << "Origin-Software-Version: #{RUBY_GNTP_VERSION}\r\n"
|
197
|
+
|
198
|
+
platformname, platformversion = '', ''
|
199
|
+
|
200
|
+
if ENV['OS']
|
201
|
+
ver = `ver`
|
202
|
+
if ver.index('[')
|
203
|
+
matches = ver.scan(/(.*)\[+(.*)\]+/)[0]
|
204
|
+
platformname, platformversion = matches[0], matches[1]
|
205
|
+
else
|
206
|
+
platformname, platformversion = 'Microsoft Windows', ver
|
207
|
+
end
|
208
|
+
else
|
209
|
+
platformname, platformversion = `uname -s`, `uname -r`
|
210
|
+
end
|
211
|
+
|
212
|
+
message << "Origin-Platform-Name: #{platformname.strip}\r\n"
|
213
|
+
message << "Origin-Platform-Version: #{platformversion.strip}\r\n"
|
214
|
+
end
|
215
|
+
|
216
|
+
#
|
217
|
+
# get start of the GNTP header
|
218
|
+
#
|
219
|
+
def get_gntp_header_start(type)
|
220
|
+
if @password.empty?
|
221
|
+
"GNTP/1.0 #{type} NONE"
|
222
|
+
else
|
223
|
+
saltvar = Time.now.to_s
|
224
|
+
salt = Digest::MD5.digest(saltvar)
|
225
|
+
salthash = Digest::MD5.hexdigest(saltvar)
|
226
|
+
key = Digest::MD5.digest("#{@password}#{salt}")
|
227
|
+
keyhash = Digest::MD5.hexdigest(key)
|
228
|
+
"GNTP/1.0 #{type} NONE MD5:#{keyhash}.#{salthash}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
#
|
233
|
+
# figure out how to handle the icon
|
234
|
+
# a URL icon just gets put into the header
|
235
|
+
# a file icon gets read and stored, ready to be appended to the end of the request
|
236
|
+
#
|
237
|
+
def handle_icon(icon, type)
|
238
|
+
if File.exists?(icon) && @target_host != 'localhost'
|
239
|
+
file = File.new(icon)
|
240
|
+
data = file.read
|
241
|
+
size = data.length
|
242
|
+
if size > 0
|
243
|
+
binary = {
|
244
|
+
:size => size,
|
245
|
+
:data => data,
|
246
|
+
:uniqueid => Digest::MD5.hexdigest(data)
|
247
|
+
}
|
248
|
+
@binaries << binary
|
249
|
+
"#{type}-Icon: x-growl-resource://#{binary[:uniqueid]}"
|
250
|
+
end
|
251
|
+
else
|
252
|
+
"#{type}-Icon: #{icon}"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
#
|
257
|
+
# outputs any binary data to be sent
|
258
|
+
#
|
259
|
+
def output_binary(binary)
|
260
|
+
message = "\r\n"
|
261
|
+
message << "Identifier: #{binary[:uniqueid]}\r\n"
|
262
|
+
message << "Length: #{binary[:size]}\r\n"
|
263
|
+
message << "\r\n"
|
264
|
+
message << "#{binary[:data]}\r\n"
|
265
|
+
end
|
159
266
|
end
|
160
267
|
|
161
268
|
#----------------------------
|
162
269
|
# self test code
|
163
270
|
if __FILE__ == $0
|
271
|
+
host = ARGV[0] || 'loaclhost'
|
272
|
+
passwd = host && ARGV[1]
|
273
|
+
|
164
274
|
#--- Use standard notification method ('register' first then 'notify')
|
165
|
-
growl = GNTP.new("Ruby/GNTP self test")
|
166
|
-
growl.register(
|
275
|
+
growl = GNTP.new("Ruby/GNTP self test", host, passwd)
|
276
|
+
growl.register(:notifications => [{
|
167
277
|
:name => "notify",
|
168
|
-
:enabled => true
|
169
|
-
}]
|
278
|
+
:enabled => true
|
279
|
+
}])
|
170
280
|
|
171
|
-
growl.notify(
|
281
|
+
growl.notify(
|
172
282
|
:name => "notify",
|
173
283
|
:title => "Congraturation",
|
174
284
|
:text => "Congraturation! You are successful install ruby_gntp.",
|
175
285
|
:icon => "http://www.hatena.ne.jp/users/sn/snaka72/profile.gif",
|
176
|
-
:sticky=> true
|
177
|
-
|
286
|
+
:sticky=> true
|
287
|
+
)
|
178
288
|
|
179
289
|
#--- Use instant notification method (just 'notify')
|
180
290
|
GNTP.notify({
|
181
291
|
:app_name => "Instant notify",
|
292
|
+
:host => host,
|
293
|
+
:passwd => passwd,
|
182
294
|
:title => "Instant notification",
|
183
295
|
:text => "Instant notification available now.",
|
184
296
|
:icon => "http://www.hatena.ne.jp/users/sn/snaka72/profile.gif",
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require '../lib/ruby_gntp'
|
2
|
+
require 'ruby_gntp_spec_helper'
|
3
|
+
|
4
|
+
# use Double Ruby for mock/stub framework.
|
5
|
+
Spec::Runner.configure do |conf|
|
6
|
+
conf.mock_with :rr
|
7
|
+
end
|
8
|
+
|
9
|
+
# describe GNTP behavior
|
10
|
+
describe GNTP do
|
11
|
+
include GNTPExampleHelperMethods
|
12
|
+
|
13
|
+
DEFAULT_APP_NAME = "Ruby/GNTP"
|
14
|
+
NOTIFICATION_NAME = "TestApp"
|
15
|
+
|
16
|
+
before do
|
17
|
+
@sended_messages = []
|
18
|
+
@ok_response = StringIO.new(["GNTP/1.0 -OK NONE\r\n", "\r\n"].join)
|
19
|
+
@opened_socket = create_stub_socket(@ok_response, @sended_messages)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can register notifications with minimum params" do
|
23
|
+
@gntp = GNTP.new
|
24
|
+
@gntp.register :notifications => [{:name => NOTIFICATION_NAME}]
|
25
|
+
|
26
|
+
[
|
27
|
+
"GNTP/1.0 REGISTER NONE\r\n",
|
28
|
+
"Application-Name: #{DEFAULT_APP_NAME}\r\n",
|
29
|
+
"Notifications-Count: 1\r\n",
|
30
|
+
"\r\n",
|
31
|
+
"Notification-Name: #{NOTIFICATION_NAME}\r\n",
|
32
|
+
"Notification-Display-Name: #{NOTIFICATION_NAME}\r\n",
|
33
|
+
"Notification-Enabled: True\r\n"
|
34
|
+
].each {|expected_text|
|
35
|
+
@sended_messages.last.should include(expected_text)
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
it "can register notifications to remote host" do
|
40
|
+
@gntp = GNTP.new "TestApp", "1.2.3.4", "password", 12345
|
41
|
+
@gntp.register :notifications => [{:name => NOTIFICATION_NAME}]
|
42
|
+
|
43
|
+
@opened_socket[:host].should == "1.2.3.4"
|
44
|
+
@opened_socket[:port].should == 12345
|
45
|
+
|
46
|
+
@sended_messages.last.first.should match(/GNTP\/1\.0 REGISTER NONE MD5:\S+\r\n/)
|
47
|
+
[
|
48
|
+
"Application-Name: TestApp\r\n",
|
49
|
+
"Notifications-Count: 1\r\n",
|
50
|
+
"\r\n",
|
51
|
+
"Notification-Name: #{NOTIFICATION_NAME}\r\n",
|
52
|
+
"Notification-Display-Name: #{NOTIFICATION_NAME}\r\n",
|
53
|
+
"Notification-Enabled: True\r\n"
|
54
|
+
].each {|expected_text|
|
55
|
+
@sended_messages.last.should include(expected_text)
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can notify with minimum params" do
|
60
|
+
@gntp = GNTP.new
|
61
|
+
@gntp.register :notifications => [{:name => NOTIFICATION_NAME}]
|
62
|
+
@gntp.notify :name => NOTIFICATION_NAME
|
63
|
+
|
64
|
+
[
|
65
|
+
"GNTP/1.0 NOTIFY NONE\r\n",
|
66
|
+
"Application-Name: #{DEFAULT_APP_NAME}\r\n",
|
67
|
+
"Notification-Name: #{NOTIFICATION_NAME}\r\n",
|
68
|
+
"Notification-Title: \r\n"
|
69
|
+
].each {|expected_text|
|
70
|
+
@sended_messages.last.should include(expected_text)
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module GNTPExampleHelperMethods
|
2
|
+
|
3
|
+
def create_stub_socket(ok_response, sended_messages)
|
4
|
+
|
5
|
+
sock = {}
|
6
|
+
|
7
|
+
stub(sock).write do |string|
|
8
|
+
lines = []
|
9
|
+
buf = StringIO.new(string)
|
10
|
+
|
11
|
+
while line = buf.gets
|
12
|
+
lines << line
|
13
|
+
end
|
14
|
+
|
15
|
+
sended_messages << lines
|
16
|
+
ok_response.rewind
|
17
|
+
end
|
18
|
+
|
19
|
+
stub(sock).gets do
|
20
|
+
ok_response.gets
|
21
|
+
end
|
22
|
+
|
23
|
+
stub(sock).close
|
24
|
+
|
25
|
+
stub(TCPSocket).open do |host, port|
|
26
|
+
sock[:host] = host
|
27
|
+
sock[:port] = port
|
28
|
+
sock
|
29
|
+
end
|
30
|
+
|
31
|
+
sock
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
metadata
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_gntp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- snaka
|
8
|
+
- David Hayward (spidah)
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date: 2009-04
|
13
|
+
date: 2009-11-04 00:00:00 +09:00
|
13
14
|
default_executable:
|
14
15
|
dependencies: []
|
15
16
|
|
16
17
|
description:
|
17
|
-
email:
|
18
|
+
email:
|
19
|
+
- snaka.gml@gmail.com
|
20
|
+
- spidahman@gmail.com
|
18
21
|
executables: []
|
19
22
|
|
20
23
|
extensions: []
|
@@ -25,10 +28,12 @@ files:
|
|
25
28
|
- lib/ruby_gntp.rb
|
26
29
|
- example/twitter_notifier.rb
|
27
30
|
- example/gntp-notify
|
31
|
+
- test/ruby_gntp_spec.rb
|
32
|
+
- test/ruby_gntp_spec_helper.rb
|
28
33
|
- README
|
29
34
|
- TODO
|
30
35
|
- ChangeLog
|
31
|
-
has_rdoc:
|
36
|
+
has_rdoc: true
|
32
37
|
homepage: http://snaka.github.com/ruby_gntp/
|
33
38
|
licenses: []
|
34
39
|
|
@@ -52,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
57
|
requirements: []
|
53
58
|
|
54
59
|
rubyforge_project:
|
55
|
-
rubygems_version: 1.3.
|
60
|
+
rubygems_version: 1.3.4
|
56
61
|
signing_key:
|
57
62
|
specification_version: 3
|
58
63
|
summary: Ruby library for GNTP(Growl Notification Transport Protocol) client
|