tweet 0.6
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/bin/ttime +36 -0
- data/bin/tweet +30 -0
- data/bin/tweetd +126 -0
- data/doc/classes/SimplePop.html +125 -0
- data/doc/classes/SimplePop.src/M000009.html +59 -0
- data/doc/classes/SimplePop.src/M000010.html +43 -0
- data/doc/classes/SimplePopWindow.html +126 -0
- data/doc/classes/SimplePopWindow.src/M000005.html +77 -0
- data/doc/classes/SimplePopWindow.src/M000006.html +36 -0
- data/doc/classes/SyslogNotifier.html +113 -0
- data/doc/classes/SyslogNotifier.src/M000003.html +34 -0
- data/doc/classes/SyslogNotifier.src/M000004.html +39 -0
- data/doc/classes/Tweet/Monitor.html +236 -0
- data/doc/classes/Tweet/Monitor.src/M000028.html +53 -0
- data/doc/classes/Tweet/Monitor.src/M000029.html +38 -0
- data/doc/classes/Tweet/Monitor.src/M000030.html +40 -0
- data/doc/classes/Tweet/Monitor.src/M000031.html +31 -0
- data/doc/classes/Tweet/Monitor.src/M000032.html +32 -0
- data/doc/classes/Tweet/Monitor.src/M000033.html +65 -0
- data/doc/classes/Tweet/Monitor.src/M000034.html +38 -0
- data/doc/classes/Tweet/Note.html +272 -0
- data/doc/classes/Tweet/Note.src/M000020.html +32 -0
- data/doc/classes/Tweet/Note.src/M000021.html +30 -0
- data/doc/classes/Tweet/Note.src/M000022.html +30 -0
- data/doc/classes/Tweet/Note.src/M000023.html +30 -0
- data/doc/classes/Tweet/Note.src/M000024.html +30 -0
- data/doc/classes/Tweet/Note.src/M000025.html +30 -0
- data/doc/classes/Tweet/Note.src/M000026.html +30 -0
- data/doc/classes/Tweet/Note.src/M000027.html +30 -0
- data/doc/classes/Tweet/Notifier.html +147 -0
- data/doc/classes/Tweet/Notifier.src/M000018.html +34 -0
- data/doc/classes/Tweet/Notifier.src/M000019.html +30 -0
- data/doc/classes/Tweet.html +336 -0
- data/doc/classes/Tweet.src/M000011.html +42 -0
- data/doc/classes/Tweet.src/M000012.html +30 -0
- data/doc/classes/Tweet.src/M000013.html +30 -0
- data/doc/classes/Tweet.src/M000014.html +30 -0
- data/doc/classes/Tweet.src/M000015.html +30 -0
- data/doc/classes/Tweet.src/M000016.html +30 -0
- data/doc/classes/Tweet.src/M000017.html +30 -0
- data/doc/classes/XOSDNotifier.html +113 -0
- data/doc/classes/XOSDNotifier.src/M000007.html +36 -0
- data/doc/classes/XOSDNotifier.src/M000008.html +38 -0
- data/doc/created.rid +1 -0
- data/doc/files/README.html +228 -0
- data/doc/files/bin/ttime.html +112 -0
- data/doc/files/bin/tweet.html +111 -0
- data/doc/files/bin/tweetd.html +163 -0
- data/doc/files/bin/tweetd.src/M000001.html +51 -0
- data/doc/files/bin/tweetd.src/M000002.html +30 -0
- data/doc/files/lib/tweet_rb.html +178 -0
- data/doc/files/plugins/simplepop_rb.html +97 -0
- data/doc/files/plugins/syslog_rb.html +101 -0
- data/doc/files/plugins/xosd_rb.html +96 -0
- data/doc/fr_class_index.html +53 -0
- data/doc/fr_file_index.html +53 -0
- data/doc/fr_method_index.html +79 -0
- data/doc/index.html +26 -0
- data/doc/rdoc-style.css +175 -0
- data/images/comments.png +0 -0
- data/lib/tweet.rb +437 -0
- data/plugins/simplepop.rb +128 -0
- data/plugins/syslog.rb +24 -0
- data/plugins/xosd.rb +23 -0
- metadata +142 -0
data/lib/tweet.rb
ADDED
@@ -0,0 +1,437 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# = Tweet
|
4
|
+
#
|
5
|
+
# == Synopsis
|
6
|
+
#
|
7
|
+
# A generalized notification daemon. Can be petitioned by an arbitrary
|
8
|
+
# application to activate a notification. Notifications can be customized by
|
9
|
+
# the user via a plugin architecture. Examples include a GTK2 popup dialog, a
|
10
|
+
# syslog entry, or an email alert.
|
11
|
+
#
|
12
|
+
# Can also be required in a ruby script to access a running Tweet daemon (see
|
13
|
+
# Tweet.notify, Tweet.message, Tweet.running?, Tweet.stop).
|
14
|
+
#
|
15
|
+
# The website is here: http://tweet.rubyforge.org/
|
16
|
+
#
|
17
|
+
# == Installation
|
18
|
+
#
|
19
|
+
# gem install tweet
|
20
|
+
#
|
21
|
+
# or maybe:
|
22
|
+
#
|
23
|
+
# sudo gem install tweet
|
24
|
+
#
|
25
|
+
# == Usage
|
26
|
+
#
|
27
|
+
# tweetd [options]
|
28
|
+
#
|
29
|
+
# == Requirements
|
30
|
+
#
|
31
|
+
# - Ruby 1.8
|
32
|
+
# - daemons library
|
33
|
+
# - pidify library
|
34
|
+
#
|
35
|
+
# == Examples
|
36
|
+
#
|
37
|
+
# To start the daemon:
|
38
|
+
# tweetd -d
|
39
|
+
#
|
40
|
+
# To contact the daemon from the shell:
|
41
|
+
# tweet "hello world"
|
42
|
+
#
|
43
|
+
# To contact the daemon from ruby:
|
44
|
+
# require 'tweet'
|
45
|
+
# Tweet.message("hello world")
|
46
|
+
#
|
47
|
+
# == Author
|
48
|
+
#
|
49
|
+
# Payton Swick, 2006
|
50
|
+
#
|
51
|
+
# == License Creative Commons GPL license.
|
52
|
+
# http://creativecommons.org/licenses/GPL/2.0/
|
53
|
+
#
|
54
|
+
|
55
|
+
module Tweet
|
56
|
+
|
57
|
+
require 'rubygems'
|
58
|
+
require 'gserver'
|
59
|
+
require 'yaml'
|
60
|
+
require 'pidify'
|
61
|
+
|
62
|
+
VERSION = '0.6'
|
63
|
+
|
64
|
+
# Priority levels.
|
65
|
+
#
|
66
|
+
# They can be referenced from other classes like this:
|
67
|
+
#
|
68
|
+
# Tweet::PRI['WARNING']
|
69
|
+
PRI = {
|
70
|
+
'CRITICAL' => 1,
|
71
|
+
'WARNING' => 2,
|
72
|
+
'INFO' => 3,
|
73
|
+
'LOW' => 4,
|
74
|
+
'DEBUG' => 5
|
75
|
+
}
|
76
|
+
|
77
|
+
# The TCP port to listen on.
|
78
|
+
PORT = 9128
|
79
|
+
|
80
|
+
# Enable using syslog for tweetd events (this is different from the syslog
|
81
|
+
# notifier plugin, as it does not log notifications. This logs only tweetd
|
82
|
+
# activity, eg: stopping and starting.)
|
83
|
+
USE_SYSLOG = true
|
84
|
+
|
85
|
+
# Experimental GTK Tray icon.
|
86
|
+
USE_GTK_TRAY_ICON = true
|
87
|
+
|
88
|
+
class << self
|
89
|
+
|
90
|
+
# Given an instance of Note, this will try to contact a running tweet
|
91
|
+
# instance and send a notification. See Tweet::message for a simpler
|
92
|
+
# version.
|
93
|
+
#
|
94
|
+
# Example:
|
95
|
+
# require 'tweet'
|
96
|
+
# Tweet.notify(Tweet::Note.info("Hello World", "Hello World!", "5", "test application"))
|
97
|
+
def notify(note)
|
98
|
+
require 'net/telnet'
|
99
|
+
message = 'app "'+note.application.to_s+'";priority "'+note.priority.to_s+'";duration "'+note.duration.to_s+'";notifier "'+note.requested_notifiers.join(' ')+'";title "'+note.title.to_s+'";message "'+note.message.to_s+'"'
|
100
|
+
begin
|
101
|
+
conn = Net::Telnet.new({'Host' => 'localhost', 'Port' => PORT, 'Telnet mode' => false })
|
102
|
+
rescue
|
103
|
+
raise "Connection to tweet failed. Maybe tweetd is not running?"
|
104
|
+
end
|
105
|
+
out = conn.recv(128)
|
106
|
+
raise "While trying to connect to tweetd, server replied: '#{out.chomp}'" if out !~ /^2/
|
107
|
+
conn.write(message+"\n")
|
108
|
+
out = conn.recv(128)
|
109
|
+
raise "While trying to send this: '#{message}', tweetd replied: '#{out.chomp}'" if out !~ /^2/
|
110
|
+
conn.close
|
111
|
+
end
|
112
|
+
|
113
|
+
# Given a String, this will try to contact a running tweet instance and send
|
114
|
+
# the String as a notification from the calling application with a duration
|
115
|
+
# of a number of seconds (the default is a duration of 0 (permanent)). This
|
116
|
+
# is a simple form of Tweet::notify.
|
117
|
+
#
|
118
|
+
# Example:
|
119
|
+
# require 'tweet'
|
120
|
+
# Tweet.message "Hello World!"
|
121
|
+
def message(message, duration=0)
|
122
|
+
notify(Note.info(message, message, duration, $0))
|
123
|
+
end
|
124
|
+
|
125
|
+
# Just like Tweet.message, but allows specifying an array of notifier
|
126
|
+
# plugins to use as requested notifiers on the server. See Monitor for
|
127
|
+
# more information.
|
128
|
+
def message_with_notifiers(message, notifiers=[], duration=0)
|
129
|
+
notify(Note.new(message, message, duration, Tweet::PRI['INFO'], $0, notifiers))
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns the path location to the library file.
|
133
|
+
def where_am_i?
|
134
|
+
File.dirname(__FILE__)+'/..'
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns true if an instance of tweetd is running (or appears to be
|
138
|
+
# running).
|
139
|
+
def running?
|
140
|
+
Pidify.running?
|
141
|
+
end
|
142
|
+
|
143
|
+
# Stops a running instance of tweetd.
|
144
|
+
def stop
|
145
|
+
Pidify.stop
|
146
|
+
end
|
147
|
+
|
148
|
+
def start
|
149
|
+
Pidify.start
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
# Represents a unique notification message. One of these objects is passed
|
155
|
+
# to the show() method of the Notifier.
|
156
|
+
#
|
157
|
+
# The message can be printed by simply printing the Note or by accessing
|
158
|
+
# note.message. The Note also contains a title, a duration, a priority,
|
159
|
+
# the application (program) that activated it, the connection method that
|
160
|
+
# it used, and the timestamp of when the message was requested. All of
|
161
|
+
# these parameters (except for the method and time) rely on the application
|
162
|
+
# being honest, and have no built-in effects. Any interpretation is done
|
163
|
+
# by the Notifier.
|
164
|
+
#
|
165
|
+
# A Note may also include an array of requested notifiers (see Monitor).
|
166
|
+
class Note
|
167
|
+
attr_reader :title, :message, :time, :duration, :priority, :application, :method_received, :requested_notifiers
|
168
|
+
|
169
|
+
# Creates a new Note.
|
170
|
+
def initialize(title, message, duration, priority, application, notifiers=[], method_received=:built_in)
|
171
|
+
@title, @message, @duration, @priority, @application, @method_received = title, message, duration.to_i, priority.to_i, application, method_received
|
172
|
+
@requested_notifiers = notifiers
|
173
|
+
time = Time.now
|
174
|
+
end
|
175
|
+
|
176
|
+
# Shortcut for calling Note.info with a duration of 0.
|
177
|
+
def Note.simple(message)
|
178
|
+
self.info(message, message, 0, $0)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Shortcut for making a new note with the INFO priority.
|
182
|
+
def Note.info(title, message, duration, application, method_received=:built_in)
|
183
|
+
self.new(title, message, duration, PRI['INFO'], application, [], method_received)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Shortcut for making a new note with the WARNING priority.
|
187
|
+
def Note.warning(title, message, duration, application, method_received=:built_in)
|
188
|
+
self.new(title, message, duration, PRI['WARNING'], application, [], method_received)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Shortcut for making a new note with the LOW priority.
|
192
|
+
def Note.low(title, message, duration, application, method_received=:built_in)
|
193
|
+
self.new(title, message, duration, PRI['LOW'], application, [], method_received)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Shortcut for making a new note with the CRITICAL priority.
|
197
|
+
def Note.critical(title, message, duration, application, method_received=:built_in)
|
198
|
+
self.new(title, message, duration, PRI['CRITICAL'], application, [], method_received)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Shortcut for making a new note with the DEBUG priority.
|
202
|
+
def Note.debug(title, message, duration, application, method_received=:built_in)
|
203
|
+
self.new(title, message, duration, PRI['DEBUG'], application, [], method_received)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Printing the Note prints the message.
|
207
|
+
def to_s
|
208
|
+
@message
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
# Listens on a network port for notifications, firing off the appropriate
|
214
|
+
# action (via a Notifier) when one is received.
|
215
|
+
#
|
216
|
+
# A proper request looks like this (fields can be in any order, semicolons
|
217
|
+
# not required):
|
218
|
+
#
|
219
|
+
# app "MyApplication";title "hello world";message "Hello World!";duration "2";priority "3"
|
220
|
+
#
|
221
|
+
# A request may also include a list of requested notifier plugins, space
|
222
|
+
# separated, like this:
|
223
|
+
#
|
224
|
+
# app "MyApplication";title "hello world";message "Hello World!";duration "2";notifier "MyPlugin1 MyPlugin2";priority "3"
|
225
|
+
#
|
226
|
+
# If the server is configured to allow requested notifiers (see
|
227
|
+
# Monitor.allow_requested_notifiers), then it will try to use the plugins
|
228
|
+
# that were requested instead of the configured notifier plugins for that
|
229
|
+
# request.
|
230
|
+
class Monitor < GServer
|
231
|
+
|
232
|
+
# Don't show (silently drop) notifications of priority under this.
|
233
|
+
attr_accessor :allow_priority
|
234
|
+
|
235
|
+
# A list of the Notifier objects to send notifications to.
|
236
|
+
attr_reader :active_notifiers
|
237
|
+
|
238
|
+
# If true, the client can request a notifier plugins in its request string.
|
239
|
+
attr_accessor :allow_requested_notifiers
|
240
|
+
|
241
|
+
# Creates a new Monitor. Defaults to showing notifications of INFO
|
242
|
+
# level and higher. Change allow_priority to modify this setting either
|
243
|
+
# here or after the creation of the object.
|
244
|
+
def initialize(allow_priority=PRI['INFO'])
|
245
|
+
super(PORT)
|
246
|
+
@allow_priority = allow_priority
|
247
|
+
@allow_requested_notifiers = true
|
248
|
+
@active_notifiers = []
|
249
|
+
|
250
|
+
Signal.trap('INT') { puts "Tweet stopped by user interrupt."; stop }
|
251
|
+
|
252
|
+
if USE_SYSLOG
|
253
|
+
begin
|
254
|
+
require 'syslog'
|
255
|
+
@syslog = Syslog.open("tweetd");
|
256
|
+
@syslog.info('tweetd starting.')
|
257
|
+
rescue LoadError
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
if USE_GTK_TRAY_ICON
|
262
|
+
begin
|
263
|
+
set_up_gtk_tray_icon
|
264
|
+
@syslog.info('gtk tray icon loaded.') if @syslog
|
265
|
+
rescue LoadError
|
266
|
+
@syslog.info('gtk tray icon not loaded.') if @syslog
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def set_up_gtk_tray_icon
|
272
|
+
require 'gtk2'
|
273
|
+
require 'gtktrayicon'
|
274
|
+
Gtk::init
|
275
|
+
|
276
|
+
screen = Gdk::Screen.default
|
277
|
+
@tray = Gtk::TrayIcon.new("tweetd", screen)
|
278
|
+
image_file = File.dirname(__FILE__) + '/../images/comments.png'
|
279
|
+
if File.exist?(image_file)
|
280
|
+
image = Gtk::Image.new(image_file)
|
281
|
+
else
|
282
|
+
@syslog.info("Error, tray image does not exist: #{image_file}") if @syslog
|
283
|
+
image = Gtk::Image.new(Gtk::Stock::DIALOG_INFO, Gtk::IconSize::BUTTON)
|
284
|
+
end
|
285
|
+
button = Gtk::EventBox.new.add(image)
|
286
|
+
menu = Gtk::Menu.new
|
287
|
+
|
288
|
+
quit_item = Gtk::MenuItem.new("Quit Tweetd")
|
289
|
+
quit_item.signal_connect('activate') do
|
290
|
+
stop
|
291
|
+
end
|
292
|
+
menu.append(quit_item)
|
293
|
+
|
294
|
+
menu.show_all
|
295
|
+
|
296
|
+
button.signal_connect('button_press_event') do |widget,event|
|
297
|
+
menu.popup(nil, nil, event.button, event.time)
|
298
|
+
end
|
299
|
+
|
300
|
+
@tray.add(button)
|
301
|
+
@tray.show_all
|
302
|
+
@gtk_thread = Thread.new { Gtk.main }
|
303
|
+
end
|
304
|
+
private :set_up_gtk_tray_icon
|
305
|
+
|
306
|
+
# Instantiates a (registered) Notifier and adds it to the list of active
|
307
|
+
# notifiers for this Watcher. Returns false if it fails.
|
308
|
+
def activate_notifier(name)
|
309
|
+
begin
|
310
|
+
return false unless n = get_notifier(name)
|
311
|
+
@syslog.info("activating notifier: #{n.class}") if @syslog
|
312
|
+
@active_notifiers << n
|
313
|
+
return true
|
314
|
+
rescue Exception=>e
|
315
|
+
@syslog.info("problem activating notifier: #{name} (#{e})") if @syslog
|
316
|
+
end
|
317
|
+
return false
|
318
|
+
end
|
319
|
+
|
320
|
+
# Instantiates a (registered) Notifier and returns it, or returns false if
|
321
|
+
# it failed.
|
322
|
+
def get_notifier(name)
|
323
|
+
begin
|
324
|
+
klass = eval(name)
|
325
|
+
if Notifier.registered_notifiers.include? klass
|
326
|
+
return klass.new
|
327
|
+
else
|
328
|
+
@syslog.info("notifier not registered: #{klass}") if @syslog
|
329
|
+
end
|
330
|
+
rescue Exception=>e
|
331
|
+
@syslog.info("notifier not found: #{name} (#{e})") if @syslog
|
332
|
+
end
|
333
|
+
return false
|
334
|
+
end
|
335
|
+
|
336
|
+
alias_method :old_start, :start
|
337
|
+
def start
|
338
|
+
Pidify.start
|
339
|
+
old_start
|
340
|
+
end
|
341
|
+
|
342
|
+
alias_method :old_stop, :stop
|
343
|
+
def stop
|
344
|
+
@syslog.info('tweetd stopping.') if @syslog
|
345
|
+
Pidify.stop
|
346
|
+
old_stop
|
347
|
+
end
|
348
|
+
|
349
|
+
# Handles incoming TCP connections.
|
350
|
+
def serve(client)
|
351
|
+
client.puts "200 Tweet #{VERSION} ready."
|
352
|
+
|
353
|
+
data = client.gets.chomp
|
354
|
+
unless data =~ /app "([^"]*)"/
|
355
|
+
client.puts "500 I don't know who you are."
|
356
|
+
return
|
357
|
+
end
|
358
|
+
app = $1
|
359
|
+
unless data =~ /title "([^"]*)"/
|
360
|
+
client.puts "500 Title required."
|
361
|
+
return
|
362
|
+
end
|
363
|
+
title = $1
|
364
|
+
unless data =~ /message "([^"]*)"/
|
365
|
+
client.puts "500 Message required."
|
366
|
+
return
|
367
|
+
end
|
368
|
+
message = $1.gsub('\n', "\n")
|
369
|
+
unless data =~ /duration "([^"]*)"/
|
370
|
+
client.puts "500 Duration required."
|
371
|
+
return
|
372
|
+
end
|
373
|
+
duration = $1
|
374
|
+
notifier = ''
|
375
|
+
if data =~ /notifier "([^"]*)"/
|
376
|
+
notifier = $1
|
377
|
+
end
|
378
|
+
unless data =~ /priority "([^"]*)"/
|
379
|
+
client.puts "500 Priority required."
|
380
|
+
return
|
381
|
+
end
|
382
|
+
priority = $1.to_i
|
383
|
+
client.puts "200 Request accepted."
|
384
|
+
|
385
|
+
note = Note.new(title, message, duration, priority, app, notifier.split(' '), :tcp)
|
386
|
+
notify(note) unless priority > @allow_priority
|
387
|
+
end
|
388
|
+
|
389
|
+
# Calls the Notifier#show method for each active notifier.
|
390
|
+
def notify(note)
|
391
|
+
notifiers_to_use = []
|
392
|
+
if @allow_requested_notifiers
|
393
|
+
notifiers_to_use = note.requested_notifiers.collect { |n| get_notifier(n) }
|
394
|
+
end
|
395
|
+
notifiers_to_use = @active_notifiers if notifiers_to_use.empty?
|
396
|
+
notifiers_to_use.each do |n|
|
397
|
+
next unless n and n.respond_to? :show
|
398
|
+
Thread.new { n.show(note) }
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
|
404
|
+
# Plugin-based notification mechanism. When a Note is activated, it will
|
405
|
+
# call the Notifier#show method for each subclass of Notifier, which can be
|
406
|
+
# overridden to provide whatever functionality is required.
|
407
|
+
#
|
408
|
+
# Example of a plugin:
|
409
|
+
#
|
410
|
+
# class MyPlugin < Tweet::Notifier
|
411
|
+
# def show(e)
|
412
|
+
# puts "--"
|
413
|
+
# puts "alert!! there's a message for you: #{e}"
|
414
|
+
# puts "--"
|
415
|
+
# end
|
416
|
+
# end
|
417
|
+
class Notifier
|
418
|
+
# A list of classes to be used as notifiers.
|
419
|
+
@registered_notifiers = []
|
420
|
+
|
421
|
+
class << self; attr_reader :registered_notifiers end
|
422
|
+
|
423
|
+
def Notifier.inherited(sub)
|
424
|
+
Notifier.registered_notifiers << sub
|
425
|
+
begin
|
426
|
+
Syslog.info("registered plugin: #{sub}")
|
427
|
+
rescue
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# Override this method in a subclass (child) as a callback
|
432
|
+
# hook to provide a notification plugin.
|
433
|
+
def show(note)
|
434
|
+
puts "[tweet!] #{note}"
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'gtk2'
|
2
|
+
require 'tweet'
|
3
|
+
|
4
|
+
# GTK2 slider-window-based notifier plugin for tweet.
|
5
|
+
class SimplePop < Tweet::Notifier
|
6
|
+
|
7
|
+
XSCREENSAVER_WATCH = false
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@current_messages = []
|
11
|
+
|
12
|
+
if Gtk.main_level == 0
|
13
|
+
Thread.new do
|
14
|
+
Gtk::init
|
15
|
+
Gtk.main
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
@suspend_popups = false
|
20
|
+
if XSCREENSAVER_WATCH
|
21
|
+
begin
|
22
|
+
xscreensaver = `which xscreensaver-command`
|
23
|
+
if xscreensaver
|
24
|
+
Thread.new do
|
25
|
+
File.open("|#{xscreensaver} -watch") do |ss|
|
26
|
+
loop do
|
27
|
+
case ss.gets
|
28
|
+
when /^LOCK|^BLANK/
|
29
|
+
@suspend_popups = true
|
30
|
+
when /^UNBLANK/
|
31
|
+
@suspend_popups = false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
rescue
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def show(note)
|
43
|
+
return if @suspend_popups == true
|
44
|
+
|
45
|
+
# Create a new window.
|
46
|
+
element = SimplePopWindow.new(note, note.duration, @current_messages.last)
|
47
|
+
|
48
|
+
# Add the new window to the list of windows.
|
49
|
+
@current_messages << element
|
50
|
+
|
51
|
+
# Call notify which calls Gtk.main (blocks) until the window is clicked or
|
52
|
+
# times out.
|
53
|
+
element.notify # blocks until hidden.
|
54
|
+
|
55
|
+
# Remove the stored reference to this window.
|
56
|
+
@current_messages.delete element
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
class SimplePopWindow < Gtk::Window
|
62
|
+
|
63
|
+
attr_reader :start_y
|
64
|
+
|
65
|
+
def initialize(note, duration=0, highest=nil)
|
66
|
+
@note = note
|
67
|
+
@duration = duration
|
68
|
+
@width = 260
|
69
|
+
@height = 80
|
70
|
+
|
71
|
+
super(Gtk::Window::POPUP)
|
72
|
+
|
73
|
+
resize(@width,@height)
|
74
|
+
stick
|
75
|
+
title = @note.title if @note.respond_to? :title
|
76
|
+
title = @note.to_s unless title
|
77
|
+
|
78
|
+
@root_win = Gdk::Window.default_root_window
|
79
|
+
@start_x = @root_win.geometry[2]-@width-10
|
80
|
+
@start_y = @root_win.geometry[3]-@height
|
81
|
+
@start_y = (highest.start_y-@height) if highest.respond_to? :start_y
|
82
|
+
screen_top = 0
|
83
|
+
@start_y = @root_win.geometry[3]-@height if @start_y <= screen_top
|
84
|
+
move(@start_x,@start_y)
|
85
|
+
|
86
|
+
@note = @note.to_s.gsub("<", "<")
|
87
|
+
@note = @note.to_s.gsub(">", ">")
|
88
|
+
|
89
|
+
@button = Gtk::Button.new
|
90
|
+
frame = Gtk::Frame.new
|
91
|
+
button_box = Gtk::HBox.new(false, 0)
|
92
|
+
image = Gtk::Image.new(Gtk::Stock::DIALOG_WARNING, Gtk::IconSize::DIALOG)
|
93
|
+
label = Gtk::Label.new(@note)
|
94
|
+
|
95
|
+
label.width_chars = (@width-image.width_request)/10
|
96
|
+
col = (@width - image.width_request)/10
|
97
|
+
col = 40
|
98
|
+
wrapped_text = label.text
|
99
|
+
wrapped_text = wrapped_text.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/, "\\1\\3\n")
|
100
|
+
wrapped_text = wrapped_text.split("\n")[0 .. 4].join("\n")
|
101
|
+
label.set_markup("<small>#{wrapped_text}</small>")
|
102
|
+
|
103
|
+
frame.add(button_box)
|
104
|
+
button_box.add(image)
|
105
|
+
button_box.add(label)
|
106
|
+
@button.add(frame)
|
107
|
+
|
108
|
+
@button.signal_connect('clicked') { @sleep_thread.kill if @sleep_thread }
|
109
|
+
add(@button)
|
110
|
+
|
111
|
+
realize
|
112
|
+
@window = window # The GDK window object this uses.
|
113
|
+
show_all
|
114
|
+
end
|
115
|
+
|
116
|
+
# Activates the notification by displaying the window.
|
117
|
+
def notify
|
118
|
+
if @duration < 1
|
119
|
+
@sleep_thread = Thread.new { loop { sleep(0.1) } }
|
120
|
+
else
|
121
|
+
@sleep_thread = Thread.new { sleep(@duration) }
|
122
|
+
end
|
123
|
+
@sleep_thread.join
|
124
|
+
self.destroy
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# SimplePopWindow.new("tweet rocks and is a mild sedative when used with chocolate!", 4).notify
|
data/plugins/syslog.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Notifier for Tweet that logs messages to syslog.
|
2
|
+
require 'syslog'
|
3
|
+
class SyslogNotifier < Tweet::Notifier
|
4
|
+
def initialize
|
5
|
+
begin
|
6
|
+
@log = Syslog.open("tweetd");
|
7
|
+
rescue
|
8
|
+
@log = Syslog
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def show(n)
|
13
|
+
message = n.application+": "+n.message
|
14
|
+
case n.priority
|
15
|
+
when Tweet::PRI['CRITICAL']: @log.crit(message)
|
16
|
+
when Tweet::PRI['WARNING']: @log.warning(message)
|
17
|
+
when Tweet::PRI['INFO']: @log.info(message)
|
18
|
+
when Tweet::PRI['LOW']: @log.notice(message)
|
19
|
+
when Tweet::PRI['DEBUG']: @log.debug(message)
|
20
|
+
else
|
21
|
+
@log.info(message)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/plugins/xosd.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
class XOSDNotifier < Tweet::Notifier
|
2
|
+
def initialize
|
3
|
+
require 'xosd'
|
4
|
+
@display_secs = 1
|
5
|
+
begin
|
6
|
+
@log = Syslog.open("tweetd");
|
7
|
+
rescue
|
8
|
+
@log = Syslog
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def show(note)
|
13
|
+
osd = XOSD::Xosd.new 3
|
14
|
+
osd.position = XOSD::MIDDLE
|
15
|
+
osd.align = XOSD::CENTER
|
16
|
+
osd.color = "#00f0f0"
|
17
|
+
osd.outline_offset = 2
|
18
|
+
osd.shadow_offset = 6
|
19
|
+
osd.font = "-adobe-helvetica-*-r-*-*-34"
|
20
|
+
osd.display_message(0, note.to_s)
|
21
|
+
osd.wait @display_secs.to_i
|
22
|
+
end
|
23
|
+
end
|