Logo Search packages:      
Sourcecode: weechat-scripts version File versions  Download package

onattach.py

# -*- encoding: iso-8859-1 -*-
#
# Copyright (c) 2006 by EgS <i@egs.name>
#
# 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 <i@egs.name>                            #
#                                                                     #
#  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 <delay> 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 <delay> 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()

                      

Generated by  Doxygen 1.6.0   Back to index