CommunicationScriptsSubclass - skilchen/bots GitHub Wiki

Subclassing communication

It is possible to overwrite bots communication methods completely.
This is done using python subclassing.
Again, as with all communication scripting there should be a file in usersys/communicationscripts with the same name as the channel (and extension '.py')

Example 1

In this case communication-type of the channel is 'file'. Bots will check the communication-script file if there is a class called 'file' and use that. The class 'file' subclasses the standard 'file' method of bots.

import bots.communication as communication

class file(communication.file):
    def connect(self,*args,**kwargs):
        #do the preparing work
        print 'in connect method'

Example 2

In this case communication-type of the channel is 'ftp'. The class 'ftp' subclasses the standard 'ftp' method of bots. The 'outcommunicate' method of the ftp class is taken over with this implementation.

import bots.communication as communication
import bots.botslib as botslib
from bots.botsconfig import *

class ftp(communication.ftp):
    @botslib.log_session
    def outcommunicate(self,*args,**kwargs):
        #get right filename_mask & determine if fixed name (append) or files with unique names
        filename_mask = self.channeldict['filename'] if self.channeldict['filename'] else '*'
        if '{overwrite}' in filename_mask:
            filename_mask = filename_mask.replace('{overwrite}','')
            mode = 'STOR '
        else:
            mode = 'APPE '
        for row in botslib.query('''SELECT idta,filename,numberofresends
                                    FROM ta
                                    WHERE idta>%(rootidta)s
                                      AND status=%(status)s
                                      AND statust=%(statust)s
                                      AND tochannel=%(tochannel)s
                                        ''',
                                    {'tochannel':self.channeldict['idchannel'],'rootidta':self.rootidta,
                                    'status':FILEOUT,'statust':OK}):
            try:
                ta_from = botslib.OldTransaction(row['idta'])
                ta_to = ta_from.copyta(status=EXTERNOUT)
                tofilename = self.filename_formatter(filename_mask,ta_from)
                if self.channeldict['ftpbinary']:
                    fromfile = botslib.opendata(row['filename'], 'rb')
                    self.session.storbinary(mode + tofilename, fromfile)
                else:
                    fromfile = botslib.opendata(row['filename'], 'r')
                    self.session.storlines(mode + tofilename, fromfile)
                fromfile.close()
            except:
                txt = botslib.txtexc()
                ta_to.update(statust=ERROR,errortext=txt,filename='ftp:/'+posixpath.join(self.dirpath,tofilename),numberofresends=row['numberofresends']+1)
            else:
                ta_to.update(statust=DONE,filename='ftp:/'+posixpath.join(self.dirpath,tofilename),numberofresends=row['numberofresends']+1)
            finally:
                ta_from.update(statust=DONE)

Example 3

In this case communication-type of the channel is 'ftp' or 'sftp'.
The class 'ftp' subclasses the standard 'ftp' method of bots. The 'disconnect' method of the ftp class is taken over with this implementation. The bots channel should be configured to upload either to a 'tmp' sub-directory, or with a '.tmp' extension. This function renames the files once uploads are complete, this preventing the recipient from processing partial files.

'''
For safety when uploading to ftp servers, it is a good idea to rename/move
files once complete. This prevents the receiver processing partial files.
When all files have been sent and before the session is disconnected, the
files are renamed so the receiver can process them.

Two methods are available:
 1. Append extension ".tmp" to the channel filename
    This method is simpler, but the receiver may still process the
    .tmp files if it does not look for specific extensions to process.
 2. Append subdirectory "/tmp" to the channel path
    This requires an extra directory created on the server, you may not
    be authorised to do this.

Subclassing of ftp.disconnect. Import this to your communicationscript (ftp or sftp as required):
    from _ftp_rename import ftp
    from _ftp_rename import sftp

Mike Griffin  4/09/2013

'''

import bots.communication as communication
import bots.botslib as botslib
import bots.botsglobal as botsglobal

class ftp(communication.ftp):
    def disconnect(self,*args,**kwargs):

        # rename files to remove .tmp extensions
        if self.channeldict['filename'].endswith('.tmp'):
            for f in self.session.nlst():
                if f.endswith('.tmp'):
                    try:
                        self.session.rename(f,f[:-4])
                    except:
                        pass

        # rename files from tmp subdirectory to parent directory
        if self.channeldict['path'].endswith('/tmp'):
            for f in self.session.nlst():
                try:
                    self.session.rename(f,'../%s' %f)
                except:
                    pass

        try:
            self.session.quit()
        except:
            self.session.close()
        botslib.settimeout(botsglobal.ini.getint('settings','globaltimeout',10))

class sftp(communication.sftp):
    def disconnect(self,*args,**kwargs):

        # rename files to remove .tmp extensions
        if self.channeldict['filename'].endswith('.tmp'):
            for f in self.session.listdir('.'):
                if f.endswith('.tmp'):
                    try:
                        self.session.rename(f,f[:-4])
                    except:
                        pass

        # rename files from tmp subdirectory to parent directory
        if self.channeldict['path'].endswith('/tmp'):
            for f in self.session.listdir('.'):
                try:
                    self.session.rename(f,'../%s' %f)
                except:
                    pass

        self.session.close()
        self.transport.close()

Example 4

In this case communication-type of the channel is 'ftp'.
The class 'ftp' subclasses the standard 'ftp' method of bots. The 'disconnect' method of the ftp class is taken over with this implementation. This provides a way to submit a remote command to the ftp server, for example to run a program on that server. The bots channel is configured with the command in the 'parameters' field.

'''
Before disconnecting, send a remote command
Channel "parameters" holds the command to send

Subclassing of ftp.disconnect. Import this to your communicationscript:
    from _ftp_remote_command import ftp

Mike Griffin  13/09/2013
'''

import bots.communication as communication
import bots.botsglobal as botsglobal

class ftp(communication.ftp):
    def disconnect(self,*args,**kwargs):

        # send remote command to ftp server
        botsglobal.logger.info('Send remote command: %s',self.channeldict['parameters'])
        self.session.sendcmd('RCMD %s' %self.channeldict['parameters'])

        try:
            self.session.quit()
        except:
            self.session.close()
        botslib.settimeout(botsglobal.ini.getint('settings','globaltimeout',10))