sauron
what is it?
sauron is an emacs mode for keeping track of events happening in the
(emacs) world around you. Events are things like âappointment in 5 minutesâ,
âbob pinged you on IRCâ, âtorrent download is completeâ etc. Sauron shows
those events like a list â basically like a log. You can âactivateâ an event
by either pressing RET
when point is on it, or clicking it with the middle
mouse button (<mouse-2>
).
When activated, it can execute some arbitrary function â for example in the case of IRC (ERC), it will switch you to the buffer (channel) it originated from. Itâs a bit of a generalization of what tracking mode does in ERC (the emacs IRC client), and that is in fact how it started.
Thereâs an increasing number of hooks and tunables in sauron, which allows you to fine-tune the behavior. However, I strive for it to be useful with minimal configuration.
getting started
After youâve put the various sauron files in a directory, you can enable it
with something like the following in your .emacs
:
;; set load path, obviously replace â<path-to-sauron-dir>â with the actual pathâŚ
(add-to-list 'load-path "<path-to-sauron-dir>") (require 'sauron)
Now, you can start sauron with⌠M-x sauron-start
, and stop it with M-x
sauron-stop
.
sauron-start
will pop-up a new frame (window) which will show events coming
from any of its sources (i.e., ERC, org-mode appointments, D-Bus). You can
âactivateâ a source by pressing âEnterâ with the cursor on the event, which
will then take some backend-specific action.
For example, for the ERC-backend, it will transfer you to the buffer
(IRC-channel) where said event happened. You can clear all events with M-x
sauron-clear
(default keybinding: c
).
You can toggle between showing and hiding of the Sauron frame or window using
M-x sauron-toggle-hide-show
.
Sauron (by default) loads the sauron-erc
, sauron-org
and sauron-dbus
modules; if you donât have ERC, org-mode or d-bus support, these will simply
be ignored. If so desired, you can customize sauron-modules
. See below for
some specifics about the backends.
customization
Iâve tried hard to come up with reasonable defaults, such that users can get started with sauron without reading too much documentation or having to write elisp etc.; still, Iâve also tried to make sauron very configurable - different people have different needs, so it should be possible to coerce the software in whatever direction.
Below are some customization points.
sauron look-and-feel
Sauron can be shown either as a separate frame (the default), or embedded in
your current frame. For the latter, set sauron-separate-frame
to nil
:
(setq sauron-separate-frame nil)
Note, this latter option (embedded sauron) is experimental. Emacs does not
make it easy to do this reliable. Note, you can use: M-x
sauron-toggle-hide-show
to hide/show the sauron frame or window.
You can customize the columns shown in the sauron buffer by setting
sauron-column-alist
- see its documentation.
You can remove the mode-line in the sauron-buffer by setting
sauron-hide-mode-line
to t
, e.g.:
(setq sauron-hide-mode-line t)
You can make the Sauron window appear on every (virtual) desktop by setting
sauron-sticky-add
to t, i.e..
(setq sauron-sticky-frame t)
in your configuration. Depending on your window manager, this may also set the frame to be always-on-top. Obviously, this is only effective if you use sauron in a separate frame.
sauron-min-priority
priorities â Each event in sauron has a certain priority. Sauron ignores all events
which have a priority that is lower that sauron-min-priority
(default
value: 3).
For example, all messages written on IRC (i.e., coming from the ERC-backend) which are not directed towards you have priority 2 â you will not see them. And that is probably a good idea.
sauron-watch-patterns
watching patterns â You can specify a list of patterns (regular expressions) which sauron should check. An event matching any of the patterns in the list will have its priority raised by 1 point. If that one point raises it to `sauron-min-priorityâ or higher level, it will now show up in the Sauron buffer.
sauron-watch-patterns
is useful if you want to check if, for example, your
name, or your hobby project is mentioned in some IRC channel.
So, for example, as part of your settings:
;; watch for some animals (setq sauron-watch-patterns '("\\bgnu\\b" "yak" "capybara" "wombat"))
sauron-watch-nicks
watching nicks â You can also specify a list of nicks to watch for; nicks are matched using a string-match (not a regular expression). A nick matching any of the nicks in the list will have its priority raised by 1 point. If that one point raises it to `sauron-min-priorityâ or higher level, it will now show up in the Sauron buffer.
donât get swamped by a certain nick
Since you may not want to get too many events from one nick â and, who knows, accompanying sound effects, pop-ups and what have you, you can set some insensitivity time; events from the same nick during this time will be lowered in priority by one point.
You can set the time period (in seconds) with `sauron-nick-insensitivityâ, which defaults to 60 seconds.
sauron-event-block-functions
blocking events from showing up â We can customize things even more precisely using the
sauron-event-block-functions
hook function. Any event with a priority >=
sauron-min-priority
will be passed to the hook function(s); if any of
those functions returns non-nil, the event will be blocked. See the emacs
documentation for a general introduction to hook functions, hereâs an
example:
(add-hook 'sauron-event-block-functions (lambda (origin prio msg &optional props) (or (string-match "foo" msg) ;; ignore events that match 'foo' ;; other matchers )))
Note that the props
parameter is a backend specific property-list, which
allows you e.g. (for the ERC-backend) to get the sender of some ERC message,
and block based on that.
sauron-event-added-functions
doing stuff based on events â After events have been added, another hook is called:
sauron-event-added-functions
.
This is place to add sound effects, notifications and so on. After all, if you get an event for e.g. the org-mode backend that you have a meeting to attend in 5 minutes, simply adding a line in the Sauron-buffer may not be enough.
Instead, you can define a hook function for this.
For doing very sound effects, pop-ups etc., a few convenience functions are provided:
sauron-fx-sox
(play a sound using âsoxâ)sauron-fx-aplay
(play a sound using âaplayâ)sauron-fx-gnome-osd
(show some letters on your screen)sauron-fx-zenity
(pop up a zenity window)sauron-fx-notify
(trigger a notification using the D-Bus notification daemon)
(see the doc-strings for the functions for the details about their parameters).
Now, our hook function could look something like:
(add-hook 'sauron-event-added-functions (lambda (origin prio msg &optional props) (if (string-match "ping" msg) (sauron-fx-sox "/usr/share/sounds/ping.wav") (sauron-fx-sox "/usr/share/sounds/something-happened.wav")) (when (>= prio 4) (sauron-fx-sox "/usr/share/sounds/uhoh.wav") (sauron-fx-gnome-osd msg 10))))
Seeing all events
Sometimes, you may want to see all events instead of filtering them, for
example for debugging purposes. For this, there is the variable
sauron-log-events
. If you set it to t
, all events will be shown in a
buffer names *Sauron Log*
. This buffer shows up to
sauron-log-buffer-max-lines
(default: 1000) lines of the last events.
connecting to alert.el
John Wiegleyâs alert.el has a bit of overlap with sauron; however, Iâve added some wrapper function to make it trivial to feed sauron events into alert. Simply adding:
(add-hook 'sauron-event-added-functions 'sauron-alert-el-adapter)
in your setup should do the trick (of course, alert.el
must be loaded).
the backend modules
Currently, 8 backend modules have been implemented:
- erc - for ERC, the emacs IRC client
- org-mode - for tracking
org-mode
(appt
) notifications - notifications - for the emacs24+
notifications
module - d-bus - for
dbus
events - identica - for
identica-mode
, the social-network site - twittering - for
twittering-mode
, the emacs twitter client - jabber - for
jabber
, the IM protocol (XMPP) - elfeed - for
elfeed
, an emacs Atom/RSS feed readerBy default,
sauron
tries to load all of them; this should work, even if you donât have some of these packages (they simply wonât be activated).If you do not want to load some module, see the variable
sauron-modules
.
erc
The ERC module check all IRC PRIVMSG messages, and JOIN/LEAVE/QUIT messages. PRIVMSG includes the messages sent to any channel by anyone. These message are given (by default) priority 2, so (by default) they do not show up in your sauron buffer.
However, messages that match one of your sauron-watch-patterns
or
sauron-watch-nicks
are getting a higher priority, or messages that are
private messages directed at you. However, after sending a message, you
wonât get notified from the same nick for another 60 seconds (by default â
see sauron-nick-insensitivity
), so you wonât get e.g. sound effects for
each message in a private conversation.
org-mode / appt
For org-mode, sauron adds functionality to appt-disp-window-function
(but
leaves it intact), so that whenever some event is near, you get a
notification with the following priorities:
- 15 minutes left: priority 3
- 10 minutes left: priority 3
- 5 minutes left: priority 4
- 2 minutes left: priority 5
For all other minutes, youâll get events with priority 2.
Note that you can influence the number of warnings and the time they start
by setting the variables appt-display-interval
and
appt-message-warning-time
, as documented in emacs manual.
You should load org before starting sauron, in particular before you set
appt-disp-window-function
, as sauron-org uses that same function (it will
preserve the existing functionality though).
d-bus
The dbus backend allows you to get events from outside emacs; it listens for
two messages, AddUrlEvent
and AddMsgEvent
. You can call them like this:
dbus-send --session --dest="org.gnu.Emacs" \
--type=method_call \
"/org/gnu/Emacs/Sauron" \
"org.gnu.Emacs.Sauron.AddUrlEvent" \
string:shell uint32:3 string:"Link: Emacs-Fu" \
string:"http://emacs-fu.blogspot.com"
The four parameters are resp. the originator (âshellâ), the priority (â3â in the example), a description and a URL. This will show up in the sauron buffer (if the priority is high enough), and if you activate the event (press RET), your browser will visit the link.
dbus-send --session \
--dest="org.gnu.Emacs" \
--type=method_call \
"/org/gnu/Emacs/Sauron" \
"org.gnu.Emacs.Sauron.AddMsgEvent" \
string:shell uint32:3 string:"Hello, world!"
The three parameters are resp. the sender (âshellâ), the priority (â3â in the example), and message. This will show up in the sauron buffer (if the priority is high enough).
As an example, you can get a notification when torrent has been completed in âTransmissionâ. In the torrent-completion script (see Preferences/ Call-script-when-torrent-is-completed), add something like:
dbus-send --session \
--dest="org.gnu.Emacs" \
--type=method_call \
"/org/gnu/Emacs/Sauron" \
"org.gnu.Emacs.Sauron.AddMsgEvent" \
string:Transmission uint32:3 string:"Torrent completed: $TR_TORRENT_NAME"
You also need to enable the web client support in Transmission - itâs in the âWebâ tab of the preferences dialog.
Note, if you start transmission before you start your session, see `Using D-Bus outside your sessionâ.
Using D-Bus outside your session
Note, you normally only use D-Bus (i.e.., the d-bus session bus) when you are in
the same session â say, your desktop environment. Thus, it is generally not
possible to send yourself D-Bus messages from programs outside your session, for
example something running from crontab
.
For this, if you set sauron-dbus-cookie
to non-nil (before starting sauron),
it will drop a file ~/.sauron-dbus
which contains the D-Bus session bus
address (DBUS_SESSION_BUS_ADDRESS
). Using this address you can, in fact, send
messages to sauron from outside your session, by doing something like in the
previous examples, but first setting DBUS_SESSION_BUS_ADDRESS
:
DBUS_SESSION_BUS_ADDRESS="`cat ~/.sauron-dbus`" dbus-send ....
We donât write ~/.sauron-dbus
as there may be security downsides to this -
even though normally other users are not allowed to send to âyourâ session bus,
even with the cookie, itâs always good to be a bit paranoid.
notifications
sauron-notifications tracks notifications sent using `notifications-notifyâ, which was added in emacs 24. You can use `sauron-notifications-urgency-to-priority-plistâ for the mapping of the âurgencyâ field of notification to the sauronâs priority field.
Note, one should be careful when calling `notifications-notifyâ from functions listed in the `sauron-event-added-functionsâ hook, as to not create some infinite recursion.
identi.ca
sauron-identica
shows the number of new dents found by identica-mode
whenever
there is at least one new dent.
twittering
sauron-twittering
shows the number of new tweets found by
twittering-mode
whenever there is at least one new tweet.
If twittering-username
is set, it will also show @-mentions when new
tweets arrive.
jabber
sauron-jabber
shows events from jabber.el
, this includes new messages, info
messages, presence alerts and lost connections.
The info, presence and connection events get priority 2, so by default you wonât get to see these. The others get priority 3, so those should be visible by default.
elfeed
sauron-elfeed
show events from elfeed.el, this include new entries in each feed.
By default, all events get priority 2 therefore you wonât get to see these. However, it is possible to configure the priority using the following instruction
(puthash url priority sauron-elfeed-prio-hash)
adding new modules
It may be interesting to track other modules as well; this shouldnât be too hard. Suppose we have a module âfooâ:
- create âsauron-foo.elâ, and make sure itâs in the load-path
- sauron-foo should implement at least:
sauron-foo-start
to start the module; this function should returnt
if startup is successful,nil
otherwisesauron-foo-stop
to stop the module / cleanup etc.
- add sauron-foo.el with
(provide 'sauron-foo)
- now, add
sauron-foo
to yoursauron-modules
Now, to actually make your module useful, youâd want to add some event is
something happens. This is done using sauron-add-event
(see itâs
documentation).
sauron
in other elisp
Using If you want to create simple sauron-events from other elisp code, writing a
backend modules might be unnecessary; you can simply call the
sauron-add-event
function directly. See its docstring for the details. Example:
(sauron-add-event
'kitchen ;; origin
3 ;; priority
"Coffee is ready!"
'(lambda () ;; function called when activated
(message "Coffee's ready, get it while it's hot!"))
'(:temperature 80)) ;; arbitrary props passed to
;; hook functions
A typical pattern may also be to switch to the buffer of origin when the
event is activated. The sauron-switch-to-marker-or-buffer
function may be
useful there, as it tries to ensure that the buffer is shown in the other
frame (not the one with Sauron).
sample configuration
(require 'sauron)
;; note, you add (setq sauron-debug t) to get errors which can debug if
;; there's something wrong; normally, we catch such errors, since e.g an error
;; in one of the hooks may cause ERC to fail (i.e., the message won't come
;; trough).
(global-set-key (kbd "C-c s") 'sauron-toggle-hide-show)
(global-set-key (kbd "C-c t") 'sauron-clear)
(setq
sauron-max-line-length 120
;; uncomment to show sauron in the current frame
;; sauron-separate-frame nil
;; you probably want to add your own nickname to the these patterns
sauron-watch-patterns
'("emacs-fu" "emacsfu" "wombat" "capybara" "yak" "gnu" "\\bmu\\b")
;; you probably want to add you own nick here as well
sauron-watch-nicks
'("Tom" "Dick" "Harry"))
;; some sound/light effects for certain events
(add-hook 'sauron-event-added-functions
(lambda (origin prio msg &optional props)
(if (string-match "ping" msg)
(sauron-fx-sox "/usr/share/sounds/ping.wav"))
(cond
((= prio 3) (sauron-fx-sox "/usr/share/sounds/pling.wav"))
((= prio 4) (sauron-fx-sox "/usr/share/sounds/plong.wav"))
((= prio 5)
(sauron-fx-sox "/usr/share/sounds/alarm.wav")
(sauron-fx-gnome-osd(format "%S: %s" origin msg) 5)))))
;; events to ignore
(add-hook 'sauron-event-block-functions
(lambda (origin prio msg &optional props)
(or
(string-match "^*** Users" msg)))) ;; filter out IRC spam