1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 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 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 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 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
|
require 'digest/md5'
require 'socket'
begin
require 'dnssd'
rescue LoadError
end
##
# ruby-growl allows you to perform Growl notifications from machines without
# growl installed (for example, non-OSX machines).
#
# In version 4, the Growl class is a wrapper for Growl::UDP and Growl::GNTP.
# The GNTP protocol allows setting icons for notifications and callbacks. To
# upgrade from version 3 replace the notification names passed to initialize
# with a call to #add_notification.
#
# Basic usage:
#
# require 'ruby-growl'
#
# g = Growl.new "localhost", "ruby-growl"
# g.add_notification "ruby-growl Notification"
# g.notify "ruby-growl Notification", "It came from ruby-growl!",
# "Greetings!"
#
# For GNTP users, ruby-growl ships with the Ruby icon from the {Ruby Visual
# Identity Team}[http://rubyidentity.org/]:
#
# require 'ruby-growl'
# require 'ruby-growl/ruby_logo'
#
# g = Growl.new "localhost", "ruby-growl"
# g.add_notification("notification", "ruby-growl Notification",
# Growl::RUBY_LOGO_PNG)
# g.notify "notification", "It came from ruby-growl", "Greetings!"
#
# See Growl::UDP and Growl::GNTP for protocol-specific API.
class Growl
##
# ruby-growl version
VERSION = '4.1'
##
# Growl error base class
class Error < RuntimeError
end
##
# Password for authenticating and encrypting requests.
attr_accessor :password
##
# List of hosts accessible via dnssd
def self.list type
raise 'you must gem install dnssd' unless Object.const_defined? :DNSSD
require 'timeout'
growls = []
begin
Timeout.timeout 10 do
DNSSD.browse! type do |reply|
next unless reply.flags.add?
growls << reply
break unless reply.flags.more_coming?
end
end
rescue Timeout::Error
end
hosts = []
growls.each do |growl|
DNSSD.resolve! growl do |reply|
hosts << reply.target
break
end
end
hosts.uniq
end
##
# Sends a notification using +options+
def self.notify options
message = options[:message]
unless message then
puts "Type your message and hit ^D" if $stdin.tty?
message = $stdin.read
end
notify_type = options[:notify_type]
g = new options[:host], options[:name]
g.add_notification notify_type, options[:name], options[:icon]
g.password = options[:password]
g.notify(notify_type, options[:title], message, options[:priority],
options[:sticky])
end
##
# Parses argv-style options from +ARGV+ into an options hash
def self.process_args argv
require 'optparse'
options = {
host: nil,
icon: nil,
list: false,
message: nil,
name: "ruby-growl",
notify_type: "ruby-growl Notification",
password: nil,
priority: 0,
sticky: false,
title: "",
}
opts = OptionParser.new do |o|
o.program_name = File.basename $0
o.version = Growl::VERSION
o.release = nil
o.banner = <<-BANNER
Usage: #{o.program_name} -H HOSTNAME [options]
Where possible, growl is compatible with growlnotify's arguments.
(Except for -p, use --priority)
Synopsis:
echo \"message\" | growl -H localhost
growl -H localhost -m message
BANNER
o.separator "Options:"
o.on("-H", "--host HOSTNAME", "Send notifications to HOSTNAME") do |val|
options[:host] = val
end
o.on("-i", "--icon [ICON]", "Icon url") do |val|
options[:icon] = URI(val)
end
o.on("-n", "--name [NAME]", "Sending application name",
"(Defaults to \"ruby-growl\")") do |val|
options[:name] = val
end
o.on("-y", "--type [TYPE]", "Notification type",
"(Defauts to \"Ruby Growl Notification\")") do |val|
options[:notify_type] = val
end
o.on("-t", "--title [TITLE]", "Notification title") do |val|
options[:title] = val
end
o.on("-m", "--message [MESSAGE]",
"Send this message instead of reading STDIN") do |val|
options[:message] = val
end
# HACK -p -1 raises
o.on("--priority [PRIORITY]", Integer,
"Notification priority",
"Priority can be between -2 and 2") do |val|
options[:priority] = val
end
o.on("-s", "--[no-]sticky", "Make the notification sticky") do |val|
options[:sticky] = val
end
o.on("-P", "--password [PASSWORD]", "Growl UDP Password") do |val|
options[:password] = val
end
o.on("--list", "List growl hosts using dnssd") do |val|
options[:list] = true
end
end
opts.parse! argv
abort opts.to_s unless options[:host] or options[:list]
options
end
##
# Command-line interface
def self.run argv = ARGV
options = process_args argv
if options[:list] then
begin
puts 'Growl GNTP hosts:'
puts list '_gntp._tcp'
puts
puts 'Growl UDP hosts:'
puts list '_growl._tcp'
rescue => e
raise unless e.message =~ /gem install dnssd/
abort "#{e.message} to use --list"
end
return
end
notify options
end
##
# Creates a new growl basic notifier for +host+ and +application_name+.
#
# +growl_type+ is used to specify the type of growl server to connect to.
# The following values are allowed:
#
# nil::
# Automatically determine the growl type. If a GNTP server is not found
# then ruby-growl chooses UDP.
# 'GNTP'::
# Use GNTP connections. GNTP is supported by Growl 1.3 and newer and by
# Growl for Windows.
# 'UDP'::
# Uses the UDP growl protocol. UDP growl is supported by Growl 1.2 and
# older.
#
# You can use <tt>growl --list</tt> to see growl servers on your local
# network.
def initialize host, application_name, growl_type = nil
@host = host
@application_name = application_name
@notifications = {}
@password = nil
@growl_type = choose_implementation growl_type
end
##
# Adds a notification named +name+ to the basic notifier. For GNTP servers
# you may specify a +display_name+ and +icon+ and set the default +enabled+
# status.
def add_notification name, display_name = nil, icon = nil, enabled = true
@notifications[name] = display_name, icon, enabled
end
def choose_implementation type # :nodoc:
raise ArgumentError,
"type must be \"GNTP\", \"UDP\" or nil; was #{type.inspect}" unless
['GNTP', 'UDP', nil].include? type
return type if type
TCPSocket.open @host, Growl::GNTP::PORT do end
'GNTP'
rescue SystemCallError
'UDP'
end
##
# Sends a notification of type +name+ with the given +title+, +message+,
# +priority+ and +sticky+ settings.
def notify name, title, message, priority = 0, sticky = false, icon = nil
case @growl_type
when 'GNTP' then
notify_gntp name, title, message, priority, sticky, icon
when 'UDP' then
notify_udp name, title, message, priority, sticky
else
raise Growl::Error, "bug, unknown growl type #{@growl_type.inspect}"
end
self
end
def notify_gntp name, title, message, priority, sticky, icon = nil # :nodoc:
growl = Growl::GNTP.new @host, @application_name
growl.password = @password
@notifications.each do |notification_name, details|
growl.add_notification notification_name, *details
end
growl.register
growl.notify name, title, message, priority, sticky
end
def notify_udp name, title, message, priority, sticky # :nodoc:
all_notifications = @notifications.keys
default_notifications =
@notifications.select do |notification_name, (_, _, enabled)|
enabled
end.map do |notification_name,|
notification_name
end
growl = Growl::UDP.new(@host, @application_name, all_notifications,
default_notifications, @password)
growl.notify name, title, message, priority, sticky
end
end
require 'ruby-growl/gntp'
require 'ruby-growl/udp'
|