Class: Growl
- Inherits:
-
Object
- Object
- Growl
- Defined in:
- lib/ruby-growl.rb
Overview
ruby-growl allows you to perform Growl notification via UDP from machines without growl installed (for example, non-OSX machines).
What’s Growl? Growl is a really cool “global notification system for Mac OS X”. See growl.info/
You’ll need a Mac to recieve Growl notifications, but you can send Growl notifications from any UDP-capable machine that runs Ruby.
See also the Ruby Growl bindings in Growl’s subversion repository: growl.info/documentation/growl-source-install.php
ruby-growl also contains a command-line notification tool named ‘growl’. It is almost completely option-compatible with growlnotify. (All except for -p is supported, use –priority instead.)
Synopsis
g = Growl.new "127.0.0.1", "ruby-growl",
["ruby-growl Notification"]
g.notify "ruby-growl Notification", "It Came From Ruby-Growl",
"Greetings!"
Constant Summary collapse
- BROKEN_PACK =
The Ruby that ships with Tiger has a broken #pack, so ‘v’ means network byte order instead of ‘n’.
[1].pack("n") != "\000\001"
- LITTLE_ENDIAN =
Endianness of this machine
little_endian
- VERSION =
ruby-growl Version
'2.1'
- GNR_FORMAT =
Growl Network Registration Packet
pack
Format – Format:struct GrowlNetworkRegistration { struct GrowlNetworkPacket { unsigned char version; unsigned char type; } __attribute__((packed)); unsigned short appNameLen; unsigned char numAllNotifications; unsigned char numDefaultNotifications; /* * Variable sized. Format: * <application name><all notifications><default notifications><checksum> * where <all notifications> is of the form (<length><name>){num} and * <default notifications> is an array of indices into the all notifications * array, each index being 8 bits. */ unsigned char data[]; } __attribute__((packed));
"CCnCCa*"
- GNN_FORMAT =
Growl Network Notification Packet
pack
Format – Format:struct GrowlNetworkNotification { struct GrowlNetworkPacket { unsigned char version; unsigned char type; } __attribute__((packed)); struct GrowlNetworkNotificationFlags { unsigned reserved: 12; signed priority: 3; unsigned sticky: 1; } __attribute__((packed)) flags; //size = 16 (12 + 3 + 1) unsigned short nameLen; unsigned short titleLen; unsigned short descriptionLen; unsigned short appNameLen; /* * Variable sized. Format: * <notification name><title><description><application name><checksum> */ unsigned char data[]; } __attribute__((packed));
"CCnnnnna*"
- GROWL_UDP_PORT =
Growl UDP Port
9887
- GROWL_PROTOCOL_VERSION =
Growl Protocol Version
1
- GROWL_TYPE_REGISTRATION =
Growl Registration Packet Id
0
- GROWL_TYPE_NOTIFICATION =
Growl Notification Packet Id
1
- STRING_BYTESIZE_METHOD =
String bytesize method – HACK for 1.8.6 support
("".respond_to? :bytesize) ? :bytesize : :length
Class Method Summary collapse
-
.list ⇒ Object
List of hosts accessible via dnssd.
-
.notify(options) ⇒ Object
Sends a notification using
options
. -
.process_args(argv) ⇒ Object
Parses argv-style options from
ARGV
into an options hash. -
.run(argv = ARGV) ⇒ Object
Command-line interface.
Instance Method Summary collapse
-
#initialize(host, app_name, all_notifies, default_notifies = nil, password = nil) ⇒ Growl
constructor
Creates a new Growl notifier and automatically registers any notifications with the remote machine.
-
#notification_packet(name, title, description, priority, sticky) ⇒ Object
Builds a Growl notification packet.
-
#notify(notify_type, title, message, priority = 0, sticky = false) ⇒ Object
Sends a notification.
-
#register ⇒ Object
Registers the notification types with
host
. -
#registration_packet ⇒ Object
Builds a Growl registration packet.
-
#send(packet) ⇒ Object
Sends a Growl packet.
-
#set_sndbuf(length) ⇒ Object
Set the size of the send buffer – Is this truly necessary?.
Constructor Details
#initialize(host, app_name, all_notifies, default_notifies = nil, password = nil) ⇒ Growl
Creates a new Growl notifier and automatically registers any notifications with the remote machine.
host
is the host to contact.
app_name
is the name of the application sending the notifications.
all_notifies
is a list of notification types your application sends.
default_notifies
is a list of notification types that are turned on by default.
I’m not sure about what default_notifies
is supposed to be set to, since there is a comment that says “not a subset of all_notifies” in the code.
password
is the password needed to send notifications to host
.
317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/ruby-growl.rb', line 317 def initialize(host, app_name, all_notifies, default_notifies = nil, password = nil) @socket = UDPSocket.open # FIXME This goes somewhere else @socket.connect host, GROWL_UDP_PORT @app_name = app_name @all_notifies = all_notifies @default_notifies = default_notifies.nil? ? all_notifies : default_notifies @password = password register end |
Class Method Details
.list ⇒ Object
List of hosts accessible via dnssd
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/ruby-growl.rb', line 143 def self.list require 'dnssd' growls = [] DNSSD.browse! '_growl._tcp' do |reply| next unless reply.flags.add? growls << reply break unless reply.flags.more_coming? end hosts = [] growls.each do |growl| DNSSD.resolve! growl do |reply| hosts << reply.target break end end hosts.uniq rescue LoadError raise 'you must gem install dnssd' end |
.notify(options) ⇒ Object
Sends a notification using options
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ruby-growl.rb', line 173 def self.notify = [:message] unless then puts "Type your message and hit ^D" if $stdin.tty? = $stdin.read end notify_type = [:notify_type] notify_types = [notify_type] g = new([:host], [:name], notify_types, notify_types, [:password]) g.notify(notify_type, [:title], , [:priority], [:sticky]) end |
.process_args(argv) ⇒ Object
Parses argv-style options from ARGV
into an options hash
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/ruby-growl.rb', line 194 def self.process_args argv require 'optparse' = { :host => nil, :message => nil, :name => "ruby-growl", :notify_type => "ruby-growl Notification", :password => nil, :priority => 0, :sticky => false, :title => "", :list => false, } opts = OptionParser.new do |o| o.program_name = File.basename $0 o.version = Growl::VERSION o.release = nil o. = "Usage: \#{o.program_name} -H HOSTNAME [options]\n\nWhere possible, growl is compatible with growlnotify's arguments.\n(Except for -p, use --priority)\n\nSynopsis:\necho \\\"message\\\" | growl -H localhost\n\ngrowl -H localhost -m message\n\n" o.separator "Options:" o.on("-H", "--host HOSTNAME", "Send notifications to HOSTNAME") do |val| [:host] = val end o.on("-n", "--name [NAME]", "Sending application name", "(Defaults to \"ruby-growl\")") do |val| [:name] = val end o.on("-y", "--type [TYPE]", "Notification type", "(Defauts to \"Ruby Growl Notification\")") do |val| [:notify_type] = val end o.on("-t", "--title [TITLE]", "Notification title") do |val| [:title] = val end o.on("-m", "--message [MESSAGE]", "Send this message instead of reading STDIN") do |val| [:message] = val end # HACK -p -1 raises o.on("--priority [PRIORITY]", Integer, "Notification priority", "Priority can be between -2 and 2") do |val| [:priority] = val end o.on("-s", "--[no-]sticky", "Make the notification sticky") do |val| [:sticky] = val end o.on("-P", "--password [PASSWORD]", "Growl UDP Password") do |val| [:password] = val end o.on("--list", "List growl hosts using dnssd") do |val| [:list] = true end end opts.parse! argv abort opts.to_s unless [:host] or [:list] end |
.run(argv = ARGV) ⇒ Object
Command-line interface
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/ruby-growl.rb', line 282 def self.run argv = ARGV = process_args argv if [:list] then begin puts list rescue => e raise unless e. =~ /gem install dnssd/ abort "#{e.message} to use --list" end return end notify end |
Instance Method Details
#notification_packet(name, title, description, priority, sticky) ⇒ Object
Builds a Growl notification packet
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/ruby-growl.rb', line 416 def notification_packet(name, title, description, priority, sticky) flags = 0 data = [] packet = [ GROWL_PROTOCOL_VERSION, GROWL_TYPE_NOTIFICATION, ] flags = 0 flags |= ((0x7 & priority) << 1) # 3 bits for priority flags |= 1 if sticky # 1 bit for sticky packet << flags packet << name.send(STRING_BYTESIZE_METHOD) packet << title.length packet << description.send(STRING_BYTESIZE_METHOD) packet << @app_name.send(STRING_BYTESIZE_METHOD) data << name data << title data << description data << @app_name packet << data.join packet = packet.pack GNN_FORMAT checksum = Digest::MD5.new << packet checksum.update @password unless @password.nil? packet << checksum.digest return packet end |
#notify(notify_type, title, message, priority = 0, sticky = false) ⇒ Object
Sends a notification.
notify_type
is the type of notification to send.
title
is a title for the notification.
message
is the body of the notification.
priority
is the priorty of message to send.
sticky
makes the notification stick until clicked.
343 344 345 346 347 348 |
# File 'lib/ruby-growl.rb', line 343 def notify(notify_type, title, , priority = 0, sticky = false) raise "Unknown Notification" unless @all_notifies.include? notify_type raise "Invalid Priority" unless priority >= -2 and priority <= 2 send notification_packet(notify_type, title, , priority, sticky) end |
#register ⇒ Object
Registers the notification types with host
.
353 354 355 |
# File 'lib/ruby-growl.rb', line 353 def register send registration_packet end |
#registration_packet ⇒ Object
Builds a Growl registration packet
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
# File 'lib/ruby-growl.rb', line 369 def registration_packet length = 0 data = [] data_format = "" packet = [ GROWL_PROTOCOL_VERSION, GROWL_TYPE_REGISTRATION ] packet << @app_name.send(STRING_BYTESIZE_METHOD) packet << @all_notifies.length packet << @default_notifies.length data << @app_name data_format = "a#{@app_name.send(STRING_BYTESIZE_METHOD)}" @all_notifies.each do |notify| data << notify.length data << notify data_format << "na#{notify.length}" end @default_notifies.each do |notify| data << @all_notifies.index(notify) if @all_notifies.include? notify data_format << "C" end data_format.gsub!(/n/, 'v') if BROKEN_PACK data = data.pack data_format packet << data packet = packet.pack GNR_FORMAT checksum = Digest::MD5.new << packet checksum.update @password unless @password.nil? packet << checksum.digest return packet end |
#send(packet) ⇒ Object
Sends a Growl packet
360 361 362 363 364 |
# File 'lib/ruby-growl.rb', line 360 def send(packet) set_sndbuf packet.length @socket.send packet, 0 @socket.flush end |
#set_sndbuf(length) ⇒ Object
Set the size of the send buffer – Is this truly necessary?
456 457 458 |
# File 'lib/ruby-growl.rb', line 456 def set_sndbuf(length) @socket.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDBUF, length end |