# -*- encoding: iso-8859-1 -*- # # Copyright (c) 2006 by EgS # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ####################################################################### # # # This script enables the execution of events triggered commands # # where the events are attach or detach of the screen weechat # # is running in. # # # # Name: OnAttach # # Licence: GPL v2 # # Author: Marcus Eggenberger # # # # Usage: # # /onattach or /ondetach to add new events # # /screenevents to manage events # # # # use /help command for detailed information # # # # Changelog: # # 0.5: added screen guessing to backup screen-detection # # 0.4: fixed TypeError in weechat 0.1.9 # # 0.3: now checking on startup if weechat runs in a screen # # 0.2: added usage of get/set_plugin_config to store eventlist # # 0.1: first version released # # # ####################################################################### # ==================== # Imports # ==================== import os import sys # ==================== # Constants # ==================== NAME = 'OnAttach' VERSION = '0.5' DESCRIPTION = "Executing commands on screen Attach/Detach" OFF = False ON = True # ==================== # Exceptions # ==================== class OnAttachError(Exception): pass # ==================== # Helpers # ==================== class WeePrint(object): def write(self, text): text = text.rstrip(' \0\n') # strip the null byte appended by pythons print if text: weechat.prnt(text,'') def getScreenByPpid(): # the screen we are in should be our parents parent # aka something like: # SCREEN # \_ -/bin/bash # \_ weechat-curses ppid = os.getppid() # get SCREEN pid pipe = os.popen("ps o ppid= -p %d" % ppid) screenpid = pipe.read().strip() pipe.close() # check if screen pid really belongs to a screen pipe = os.popen("ps o cmd= -p %s" % screenpid) cmd = pipe.read().strip() pipe.close() if 'screen' not in cmd.lower(): raise OnAttachError else: return screenpid def getScreenByList(): # checks if there is only one attached screen # if so use: it! ;) pipe = os.popen("screen -list | grep -i attached") screens = pipe.read().strip() pipe.close() screens = screens.splitlines() if len(screens) > 1: print "There are more then one screen currently attached." print "Detach all other running screens and reload OnAttach" print "to ensure correct screen detection." raise OnAttachError try: socket, status = screens[0].split() except: # thats no common screen list... print "failed!" raise OnAttachError else: print "Using screen %s" % socket return socket def getScreenPid(): try: return getScreenByPpid() except OnAttachError: print "!!! Unable to determine screen by parentid!" print "Trying to guess screen..." return getScreenByList() def registerFunction(function): # Register a python function as a commandhandler # Function needs to be named like weeFunction and # is bound to /function # docstring is used for weechat help functionname = function.__name__ # guess what :) weecommand = functionname[3:].lower() # strip the wee doc = function.__doc__.splitlines() arguments = doc[0] # First docstring line is the arguments string description = doc[1][4:] args_description = '\n'.join([line[4:] for line in doc[2:-1]]) # join rest and strip indentation if not function.func_defaults: # use args default value as template template = '' elif len(function.func_defaults) == 1: template = function.func_defaults[0] elif len(function.func_defaults) == 2: template = func.func_defaults[1] weechat.add_command_handler(weecommand, functionname, description, arguments, args_description, template) def registerFunctions(): functions = [function for name, function in globals().iteritems() if name.startswith('wee') and callable(function)] for func in functions: registerFunction(func) # ==================== # Classes # ==================== class Event(object): ESCAPECHAR = '\\' SEPARATOR = ';' def __init__(self, delay, step, channel, server, command, activehigh=True): self.delay = int(delay) self.step = step self.channel = channel self.server = server self.command = command self.activestate = activehigh self.reset() #@classmethod def unserialize(cls, step, serial): try: # let's try the easy way :) delay, channel, server, command, activestate = serial.split() except ValueError: # ok we got an escaped separator... :/ data = serial.split(cls.SEPARATOR) for i in range(len(data)): if data[i].endswith(cls.ESCAPECHAR): data[i+1] = data[i] + cls.SEPARATOR + data[i+1] data = [item.replace(cls.ESCAPECHAR + cls.SEPARATOR, cls.SEPARATOR) for item in data if not item.endswith(cls.ESCAPECHAR)] delay, channel, server, command, activestate = data delay = int(delay) activestate = activestate == 'True' return cls(delay, step, channel, server, command, activestate) # lets go for 2.3 compatiblity unserialize = classmethod(unserialize) #@property def serialized(self): data = [self.delay, self.channel, self.server, self.command, self.activestate] data = [str(item).replace(self.SEPARATOR, self.ESCAPECHAR + self.SEPARATOR) for item in data] return self.SEPARATOR.join(data) # lets go for 2.3 compatiblity serialized = property(serialized) def reset(self): self.currentdelay = self.delay self.fired = False def fire(self): weechat.command(self.command, self.channel, self.server) self.fired = True def stimulus(self, state): if state != self.activestate: self.reset() else: self.currentdelay -= self.step if self.currentdelay <= 0 and not self.fired: self.fire() class CheckStatus(object): ESCAPECHAR = '\\' SEPARATOR = '|' eventlist = [] step = 5 def __init__(self): try: self.screenpid = getScreenPid() except OnAttachError: raise # try to read config data serializedEvents = weechat.get_plugin_config("events") if not serializedEvents: return events = serializedEvents.split(self.SEPARATOR) for i in range(len(events)): if events[i].endswith(self.ESCAPECHAR): events[i+1] = events[i] + self.SEPARATOR + ss[i+1] events = [event.replace(self.ESCAPECHAR + self.SEPARATOR, self.SEPARATOR) for event in events if not event.endswith(self.ESCAPECHAR)] for event in events: try: self.eventlist.append(Event.unserialize(self.step, event)) except: print "Unable to unserialize event!" print "Try to add the event manualy and save config again." def __call__(self, server="", args=""): # check if the screen is attached or detatched pipe = os.popen("screen -list | grep %s" % self.screenpid) screenlist = pipe.read().strip() pipe.close() if 'attached' in screenlist.lower(): state = ON elif 'detached' in screenlist.lower(): state = OFF else: print "Unable to determine screen status" return weechat.PLUGIN_RC_KO for event in self.eventlist: event.stimulus(state) return weechat.PLUGIN_RC_OK def addEvent(self, delay, channel, server, command, activehigh=True): event = Event(delay, self.step, channel, server, command, activehigh) self.eventlist.append(event) def showEvents(self): format = "%2s | %-10s | %-10s | %-15s | %-7s | %-2s %s" separator = "---+------------+------------+-----------------+---------+----" print separator print format % ("ID", "Server", "Channel", "Command", "Delay", "on", "") print separator for i in range(len(self.eventlist)): event = self.eventlist[i] if event.activestate: atde = 'AT' else: atde = 'DE' if event.fired: fired = '*' else: fired = '' print format % (i, event.server[:10], event.channel[:10], event.command[:15], "%3d sec" % event.delay, atde, fired) print separator def save(self): weechat.set_plugin_config("events",self.serialized) #@property def serialized(self): events = [str(event.serialized).replace(self.SEPARATOR, self.ESCAPECHAR + self.SEPARATOR) for event in self.eventlist] return self.SEPARATOR.join(events) # lets go for 2.3 compatiblity serialized = property(serialized) def dropEvent(self, eventid): del self.eventlist[eventid] # ==================== # Functions # ==================== def addEvent(server, args, activehigh=True): delay, channel, command = args.split(' ', 2) delay = int(delay) channel = channel.strip("'\"") checkStatus.addEvent(delay, channel, server, command, activehigh) if activehigh: atde = "at" else: atde = "de" print "Added %stach event '%s' for channel %s on network %s with %d seconds delay" % (atde, command, channel, server, delay) def weeOnAttach(server, args): """delay channel command Adds a command which is executed after attaching the screen The command is executed seconds after the attach. Use "" or '' as channel if the command should be executed in the server buffer. Example: /onattach 180 &bitlbee account on 0 """ try: addEvent(server, args) except: weechat.command("/help onattach", "", server) return 0 else: return 1 def weeOnDetach(server, args): """delay channel command Adds a command which is executed after detaching the screen The command is executed seconds after the detach. Use "" or '' as channel if the command should be executed in the server buffer. Example: /ondetach 180 &bitlbee account on 0 """ try: addEvent(server, args, activehigh=False) except: weechat.command("/help onattach", "", server) return 0 else: return 1 def weeScreenevents(server, args="show|save|drop"): """show | save | drop eventid [eventid [...]] shows, saves or drops events show: Shows all current active events save: Saves current events permanently drop: Delete event from onAttach/onDetach list. Use /showevents to get eventid """ try: action, args = args.split(' ',1) except ValueError: action = args.strip() args = '' actions = {'show':showEvents, 'drop':dropEvent, 'save':saveEvents} try: action = actions[action] except KeyError: weechat.command("/help screenevents", "", server) return 0 else: return action(server, args) def showEvents(server, args): checkStatus.showEvents() return 1 def dropEvent(server, args): try: eventids = [int(id) for id in args.split()] except: print "eventid musst be a number!" return 0 eventids.sort() eventids.reverse() for eventid in eventids: try: checkStatus.dropEvent(eventid) except: print "Unable to drop Event %d" % eventid return 0 print "dropped %d events!" % len(eventids) return 1 def saveEvents(server, args): checkStatus.save() return 1 # ==================== # Let's Register! # ==================== if __name__ == '__main__': try: import weechat except ImportError: print "This script is to be loaded as PythonScript in WeeChat" print "Get WeeChat now at: http://weechat.flashtux.org/" import sys sys.exit(10) # kick pythons print to weechat.prnt(msg, '') sys.stdout = WeePrint() weechat.register(NAME, VERSION, "", DESCRIPTION) try: checkStatus = CheckStatus() except OnAttachError: print "!!! Requirements for %s not met:" % NAME print "!!! - WeeChat is not running in a screen or not able to get screen PID" print "!!! --> Run WeeChat in a screen to fix the problem!" else: weechat.add_timer_handler(checkStatus.step, "checkStatus") registerFunctions()