ftpbot/twisted/plugins/monitorbot_plugin.py

131 lines
3.6 KiB
Python

# -*- coding: utf-8 -*-
from ConfigParser import ConfigParser
from twisted.application.service import IServiceMaker, Service
from twisted.internet.endpoints import clientFromString
from twisted.plugin import IPlugin
from twisted.python import usage, log
from zope.interface import implementer
from twisted.internet import inotify
from twisted.python import filepath
from monitor.bot import MonitorBotFactory
class MonitorBotService(Service):
_bot = None
def __init__(self, endpoint, channel, nickname, realname, path):
self._endpoint = endpoint
self._channel = channel
self._nickname = nickname
self._realname = realname
self._watch_path = filepath.FilePath(path)
self._messages = []
self._callid = None
def startService(self):
"""Construct a client & connect to server."""
from twisted.internet import reactor
"""Define callbacks."""
def connected(bot):
self._bot = bot
def failure(err):
log.err(err, _why='Could not connect to specified server.')
reactor.stop()
client = clientFromString(reactor, self._endpoint)
factory = MonitorBotFactory(
self._channel,
self._nickname,
self._realname
)
def humanReadableMask(mask):
flags_to_human = [
(inotify.IN_MODIFY, 'geändert'),
(inotify.IN_CREATE, 'erstellt'),
(inotify.IN_DELETE, 'gelöscht'),
(inotify.IN_MOVED_FROM, 'umbenannt von'),
(inotify.IN_MOVED_TO, 'umbenannt nach')
]
s = []
for k, v in flags_to_human:
if k & mask:
s.append(v)
return s
def fsnotify(ignored, filepath, mask):
if self._callid != None and self._callid.active():
self._callid.cancel()
path_segments = filepath.segmentsFrom(self._watch_path)
new_path = '/'.join(path_segments)
msg = "ftp> /%s (%s)" % (new_path, ', '.join(humanReadableMask(mask)))
if msg not in self._messages:
self._messages.append(msg)
self._callid = reactor.callLater(10.0, sendQueuedMessages)
def sendQueuedMessages():
if len(self._messages) > 3:
self._bot.msg(self._channel, "ftp> %i Events übersprungen. Letzter Event:" % (len(self._messages)-1))
self._bot.msg(self._channel, self._messages[len(self._messages)-1])
else:
for msg in self._messages:
self._bot.msg(self._channel, msg)
self._messages = []
watchMask = ( inotify.IN_MODIFY
| inotify.IN_CREATE
| inotify.IN_DELETE
| inotify.IN_MOVED_FROM
| inotify.IN_MOVED_TO
)
notifier = inotify.INotify()
notifier.startReading()
notifier.watch(self._watch_path, autoAdd=True, recursive=True, callbacks=[fsnotify], mask=watchMask)
"""Attach defined callbacks."""
return client.connect(factory).addCallbacks(connected, failure)
def stopService(self):
"""Disconnect."""
if self._bot and self._bot.transport.connected:
self._bot.transport.loseConnection()
class Options(usage.Options):
optParameters = [
['config', 'c', 'settings.ini', 'Configuration file.'],
]
@implementer(IServiceMaker, IPlugin)
class BotServiceMaker(object):
tapname = "monitorbot"
description = "IRC bot that provides verbose monitoring of an fs path."
options = Options
def makeService(self, options):
"""Read the config and construct the monitorbot service."""
config = ConfigParser()
config.read([options['config']])
return MonitorBotService(
endpoint=config.get('irc', 'endpoint'),
channel=config.get('irc', 'channel'),
nickname=config.get('irc', 'nickname'),
realname=config.get('irc', 'realname'),
path=config.get('fsmonitor', 'path'),
)
# Now construct an object which *provides* the relevant interfaces
# The name of this variable is irrelevant, as long as there is *some*
# name bound to a provider of IPlugin and IServiceMaker.
serviceMaker = BotServiceMaker()