addxml.lua

Posted by Isthiriel on Thu 27 Dec 2007 03:20 PM — 2 posts, 16,312 views.

#0
It took longer than I expected, but a Python addxml.lua equivalent. :)

Some use cases:
The close-to-lua syntax:
x = XMLTimer('GetEntity', { 'second': 0.1, 'enabled': True, 'one_shot': 'y', }).add()

More OO:
x = XMLTimer('GetEntity')
x.set_flags(eEnabled | eOneShot | eActiveWhenClosed | eTemporary)
x.second = 0.1
x.script = 'OnEmptyTimer'
x.add()

Or rather than using a bitvector, you can set flags individually:
x.enabled = True
x.one_shot = 'y'


You retrieve current xml by the get() methods on the XMLTrigger, XMLAlias and XMLTimer classes:
x = XMLTimer.get('GetEntity')

Which you can modify as above:
x.one_shot = True
x.second = 20
x.add() # send the changes back to MUSHclient


... Hmm. I could also make it so you can use |= and &= to mask the current flags, but that would be something for another day.

First the magic part:
# ---------------------------------------------------------
# Python version of addxml.lua
# -- See: http://www.gammon.com.au/forum/?id=7123
# ---------------------------------------------------------

import xml.etree.ElementTree as et

eXmlTrigger             = 0
eXmlAlias               = 1
eXmlTimer               = 2
eXmlMacro               = 3

eXmlSendMUD             = 0  # send to MUD
eXmlSendCommand         = 1  # put in command window        
eXmlSendOutput          = 2  # display in output window     
eXmlSendStatus          = 3  # put in status line
eXmlSendNotepadNew      = 4  # new notepad 
eXmlSendNotepadAppend   = 5  # append to notepad  
eXmlSendLog             = 6  # put in log file     
eXmlSendNotepadReplace  = 7  # replace notepad 
eXmlSendQueue           = 8  # queue it      
eXmlSendVariable        = 9  # set a variable    
eXmlSendReparseCommand  = 10 # re-parse as command 
eXmlSendSpeedwalk       = 11 # send to MUD as speedwalk 
eXmlSendScript          = 12 # send to script engine
eXmlSendImmediate       = 13 # send without queuing
eXmlSendScriptOmit      = 14 # send to script engine - after omitting from output (triggers)  

class XMLObject(object):
    magic_data = None
    def __init__(self, name, data=None, send=None):
        assert self.magic_data != None
        assert name
        self.__dict__['data'] = { 'name': name, 'send': '', }
        self.data = data
        if send:
            self.data['send'] = send

    @classmethod
    def get(klass, name):
        assert klass.magic_data != None
        xml = world.ExportXML(klass.magic_data[0], name)
        assert xml != ''
        root = et.XML(xml)
        body = root.find(".//%s" % klass.magic_data[2])
        data = body.attrib
        send = root.find(".//send")
        if send:
            data['send'] = send.text
        r = klass(name, data)
        return r

    def set_flags(self, flags):
        for k, v in self.valid_flags.iteritems():
            if flags & k:
                self.data[v] = 'y'
                continue
            self.data[v] = 'n'

    def get_flags(self):
        flags = 0
        for k, v in self.valid_flags.iteritems():
            if self.data.has_key(v) and self.data[v] == 'y':
                flags |= k
        return flags

    def add(self, note_xml=False):
        assert self.magic_data != None
        data = self.data
        root = et.Element(self.magic_data[1])

        text = data['send']
        del data['send']
        body = et.SubElement(root, self.magic_data[2], data)
        data['send'] = text

        send = et.SubElement(body, 'send')
        send.text = data['send']
        xml = et.tostring(root)
        if note_xml:
            world.Note(xml)
        assert 1 == world.ImportXML(xml)
        # Force script entry-point resolution
        if self.magic_data[0] < 3 and \
                data.has_key('name') and data['name'] and \
                data.has_key('script') and data['script']:
            s = "world.Set%sOption(data['name'], 'script', data['script'])"
            exec s % self.magic_data[2].capitalize()

    def __getattr__(self, attr):
        if attr in self.valid_attribs:
            return self.data[attr]
        raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, attr))
    def __setattr__(self, attr, val):
        if attr == 'name':
            raise AttributeError("Cannot modify name of '%s'" % self.__class__.__name__)
        if attr == 'data':
            if not isinstance(val, dict):
                raise AttributeError("'%s' expected dict to be assigned to 'data'" % self.__class__.__name__)
            for k, v in val.iteritems():
                if k == 'name':
                    continue
                self.__setattr__(k, v)
            return
        if attr in self.valid_attribs:
            if isinstance(val, bool):
                if val:
                    val = 'y'
                else:
                    val = 'n'
            self.data[attr] = str(val)
            return
        raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, attr))
    def __delattr__(self, attr):
        if attr == 'name':
            raise AttributeError("Cannot modify name of '%s'" % self.__class__.__name__)
        if attr in self.valid_attribs:
            del self.data[attr]
            return
        raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, attr))
Amended on Thu 27 Dec 2007 03:47 PM by Isthiriel
#1
And then the actual information about triggers/aliases/timers:

class XMLTrigger(XMLObject):
    magic_data = (0, 'triggers', 'trigger')
    valid_attribs = [
        'back_colour', 'bold', 'clipboard_arg', 'colour_change_type',
        'custom_colour', 'enabled', 'expand_variables', 'group',
        'ignore_case', 'inverse', 'italic', 'keep_evaluating',
        'lines_to_match', 'lowercase_wildcard', 'make_bold', 'make_italic',
        'make_underline', 'match', 'match_back_colour', 'match_bold',
        'match_inverse', 'match_italic', 'match_text_colour',
        'match_underline', 'multi_line', 'name', 'omit_from_log',
        'omit_from_output', 'one_shot', 'other_back_colour',
        'other_text_colour', 'regexp', 'repeat', 'script', 'send',
        'send_to', 'sequence', 'sound', 'sound_if_inactive',
        'temporary', 'text_colour', 'user', 'variable',
        ]
    valid_flags = {
        eEnabled: 'enabled',
        eOmitFromLog: 'omit_from_log',
        eOmitFromOutput: 'omit_from_output',
        eKeepEvaluating: 'keep_evaluating',
        eIgnoreCase: 'ignore_case',
        eTriggerRegularExpression: 'regexp',
        eExpandVariables: 'expand_variables',
        eLowercaseWildcard: 'lowercase_wildcard',
        eTemporary: 'temporary',
        }   

class XMLAlias(XMLObject):
    magic_data = (1, 'aliases', 'alias')
    valid_attribs = [
        'echo_alias', 'enabled', 'expand_variables', 'group', 'ignore_case',
        'keep_evaluating', 'match', 'menu', 'name', 'omit_from_command_history',
        'omit_from_log', 'omit_from_output', 'one_shot', 'regexp', 'script',
        'send', 'send_to', 'sequence', 'temporary', 'user', 'variable',
        ]
    valid_flags = {
        eEnabled: 'enabled',
        eIgnoreAliasCase: 'ignore_case',
        eOmitFromLogFile: 'omit_from_log',
        eAliasRegularExpression: 'regexp',
        eExpandVariables: 'expand_variables',
        eAliasMenu: 'menu',
        eTemporary: 'temporary',
        }   

class XMLTimer(XMLObject):
    magic_data = (2, 'timers', 'timer')
    valid_attribs = [
        'active_closed', 'at_time', 'enabled', 'group', 'hour', 'minute', 'name',
        'offset_hour', 'offset_minute', 'offset_second', 'omit_from_log',
        'omit_from_output', 'one_shot', 'script', 'second', 'send', 'send_to',
        'temporary', 'variable',
        ]
    valid_flags = {
        eEnabled: 'enabled',
        eAtTime: 'at_time',
        eOneShot: 'one_shot',
        eActiveWhenClosed: 'active_closed',
        eTemporary: 'temporary',
        }   

class XMLMacro(XMLObject):
    magic_data = (3, 'macros', 'macro')
    # crippled due to lack of information