#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
#
# Copyright (C) 2007 Adam Wolk "Mulander" <netprobe@gmail.com>
# Copyright (C) 2007, 2008 Mikael Berthe "McKael" <mikael@lilotux.net>
#
# This script is provided under the terms of the GNU General Public License,
# see the file COPYING in this directory.
#
"""
This script reads mcabber event lines from the standard input
and creates events accordingly. Examples:
STATUS O user2@domain.org
MSG IN user@domain.org
UNREAD 1
"""
import sys, getopt
import os
import pynotify
CONFFILE = "mcevent.cfg"
# Default option values
OPT = {
'use_notify': 0,
'use_voice': 0,
'use_sound': 1,
'short_nick': 1,
'unread_file': '',
'snd_cmd_msg_in': '/usr/bin/play -V0 -v3 sound.wav',
}
NOTIFY_TIMEOUT = 4000
CONTACT_MAP = { }
# Nickname used for the notification
CONTACT_CUSTOM_MSG = { }
# Message used for the notification
ONLINE_ALERTS = { }
# 0: disabled 1: normal notification 2: permanent notify box
VOICEMAP = { }
# Specify the name pronounced by espeak
BLACKLIST = { }
# No notification for these JIDs
CMD_ESPEAK = "/usr/bin/espeak"
ENCODING = ''
def init_notify():
"""
Initialize the pynotify subsystem.
"""
import locale
global ENCODING, NOTIFY_LOADED
pynotify.init('mcnotify')
ENCODING = (locale.getdefaultlocale())[1]
NOTIFY_LOADED = True
def read_conf_from_file():
"""
Read the configuration file.
"""
import ConfigParser
config = ConfigParser.ConfigParser()
config.read(CONFFILE)
CONTACT_MAP.clear()
CONTACT_CUSTOM_MSG.clear()
ONLINE_ALERTS.clear()
VOICEMAP.clear()
BLACKLIST.clear()
if config.has_option("Notifications", "notify"):
OPT['use_notify'] = int(config.get("Notifications", "notify"))
if config.has_option("Notifications", "voice"):
OPT['use_voice'] = int(config.get("Notifications", "voice"))
if config.has_option("Notifications", "sound"):
OPT['use_sound'] = int(config.get("Notifications", "sound"))
if config.has_option("Notifications", "short_nick"):
OPT['short_nick'] = int(config.get("Notifications", "short_nick"))
if config.has_option("Notifications", "unread_file"):
OPT['unread_file'] = config.get("Notifications", "unread_file")
if config.has_option("Notifications", "snd_cmd_msg_in"):
OPT['snd_cmd_msg_in'] = config.get("Notifications", "snd_cmd_msg_in")
if config.has_section("Contacts"):
for cid in config.options("Contacts"):
CONTACT_MAP[cid] = config.get("Contacts", cid)
if config.has_section("Contact_Customized_Messages"):
for cid in config.options("Contact_Customized_Messages"):
CONTACT_CUSTOM_MSG[cid] = config.get("Contact_Customized_Messages",
cid)
if config.has_section("Alerts"):
for cid in config.options("Alerts"):
ONLINE_ALERTS[cid] = int(config.get("Alerts", cid))
if config.has_section("Voicemap"):
for cid in config.options("Voicemap"):
VOICEMAP[cid] = config.get("Voicemap", cid)
if config.has_section("Blacklist"):
for cid in config.options("Blacklist"):
BLACKLIST[cid] = int(config.get("Blacklist", cid))
if OPT['use_notify'] and not NOTIFY_LOADED:
init_notify()
def say(buddy, text):
"""
Create a subprocess and run a speech synthesizer.
"""
import subprocess
p = subprocess.Popen(CMD_ESPEAK, stdin=subprocess.PIPE, close_fds=True)
child_stdin = p.stdin
child_stdin.write(buddy + " " + text)
child_stdin.close()
def notify(buddy, msg, timeout):
"""
Create a pynotify popup.
"""
msgbox = pynotify.Notification(unicode(buddy, ENCODING),
unicode(msg, ENCODING))
msgbox.set_timeout(timeout)
msgbox.set_urgency(pynotify.URGENCY_LOW)
msgbox.show()
def get_nick(jid):
"""
Return the nick of the given contact (JID).
"""
if jid in CONTACT_MAP:
buddy = CONTACT_MAP[jid]
else:
buddy = jid
if OPT['short_nick'] and '@' in buddy:
buddy = buddy[0:buddy.index('@')]
return buddy
def is_blacklisted(jid):
"""
Return True if the given contcat (JID) is blacklisted.
"""
if jid in BLACKLIST and BLACKLIST[jid]:
return True
return False
def process_line(args):
"""
Parse the provided line and run events.
"""
argn = len(args)
if argn < 2:
print "Ignoring invalid event line."
return
elif argn == 2:
event, arg1 = args[0:2]
arg2 = None
filename = None
elif argn == 3:
event, arg1, arg2 = args[0:3]
filename = None
else:
# For now we simply ignore the file name
event, arg1, arg2 = args[0:3]
filename = None
if event == 'MSG' and arg1 == 'IN':
jid = arg2
if not jid:
print "Ignoring invalid MSG event line."
return
buddy = get_nick(jid)
if jid in CONTACT_CUSTOM_MSG:
msg = CONTACT_CUSTOM_MSG[jid]
elif 'default' in CONTACT_CUSTOM_MSG:
msg = CONTACT_CUSTOM_MSG['default']
else:
msg = 'sent you a message.'
if not is_blacklisted(jid):
textmsg = None
if filename and os.path.exists(filename):
fileh = file(filename)
textmsg = fileh.read()
if OPT['use_notify']:
if not textmsg:
textmsg = msg
notify(buddy, textmsg, NOTIFY_TIMEOUT)
if OPT['use_sound']:
os.system(OPT['snd_cmd_msg_in'] + '> /dev/null 2>&1')
if OPT['use_voice'] and not is_blacklisted(jid):
if jid in VOICEMAP:
buddy = VOICEMAP[jid]
say(buddy, msg)
if filename and os.path.exists(filename):
os.remove(filename)
elif event == 'STATUS':
jid = arg2
if arg1 == 'O' and jid in ONLINE_ALERTS:
alert_type = ONLINE_ALERTS[jid]
if alert_type > 0:
if OPT['use_sound']:
os.system(OPT['snd_cmd_msg_in'] + '> /dev/null 2>&1')
buddy = get_nick(jid)
if OPT['use_notify']:
if alert_type == 1:
timeout = NOTIFY_TIMEOUT
else:
timeout = pynotify.EXPIRES_NEVER
notify(buddy, "is online", timeout)
if filename and os.path.exists(filename):
os.remove(filename)
if OPT['use_voice']:
if jid in VOICEMAP:
buddy = VOICEMAP[jid]
say(buddy, "is online now.")
elif event == 'UNREAD':
# arg1 is the number of unread buffers
if OPT['unread_file'] != '':
fileh = open(OPT['unread_file'], 'w')
fileh.write(arg1)
fileh.close()
##### MAIN #####
try:
opts, cargs = getopt.getopt(sys.argv[1:], "c:", ["help", "output="])
except getopt.GetoptError, err:
print str(err)
sys.exit(2)
for o, a in opts:
if o == "-c":
CONFFILE = a
try:
last_conf_read = os.stat(CONFFILE).st_ctime
except OSError:
sys.stderr.write("Cannot read config file!\n")
sys.exit(3)
NOTIFY_LOADED = False
read_conf_from_file()
# Read stdin line by line, and process the commands
while 1:
try:
line = sys.stdin.readline()
except KeyboardInterrupt:
print "\nInterrupted!"
sys.exit(0)
if not line:
break
last_conf_change = os.stat(CONFFILE).st_ctime
if last_conf_change > last_conf_read:
read_conf_from_file()
last_conf_read = last_conf_change
lineargs = line.split()
process_line(lineargs)
if NOTIFY_LOADED:
pynotify.uninit()
sys.exit(0)
# vim:set si et sts=4 sw=4: