Switch file to spaces

This commit is contained in:
dpedu 2015-08-08 15:08:31 -07:00
parent 5a511944bd
commit 7543cb604e
1 changed files with 350 additions and 350 deletions

View File

@ -14,353 +14,353 @@ from pyircbot.irccore import IRCCore
import os.path import os.path
class PyIRCBot: class PyIRCBot:
""":param botconfig: The configuration of this instance of the bot. Passed by main.py. """:param botconfig: The configuration of this instance of the bot. Passed by main.py.
:type botconfig: dict :type botconfig: dict
""" """
def __init__(self, botconfig): def __init__(self, botconfig):
self.log = logging.getLogger('PyIRCBot') self.log = logging.getLogger('PyIRCBot')
"""Reference to logger object""" """Reference to logger object"""
self.botconfig = botconfig self.botconfig = botconfig
"""saved copy of the instance config""" """saved copy of the instance config"""
"""storage of imported modules""" """storage of imported modules"""
self.modules = {} self.modules = {}
"""instances of modules""" """instances of modules"""
self.moduleInstances = {} self.moduleInstances = {}
self.rpc = BotRPC(self) self.rpc = BotRPC(self)
"""Reference to BotRPC thread""" """Reference to BotRPC thread"""
self.irc = IRCCore() self.irc = IRCCore()
"""IRC protocol class""" """IRC protocol class"""
self.irc.server = self.botconfig["connection"]["server"] self.irc.server = self.botconfig["connection"]["server"]
self.irc.port = self.botconfig["connection"]["port"] self.irc.port = self.botconfig["connection"]["port"]
self.irc.ipv6 = True if self.botconfig["connection"]["ipv6"]=="on" else False self.irc.ipv6 = True if self.botconfig["connection"]["ipv6"]=="on" else False
self.irc.addHook("_DISCONNECT", self.connection_closed) self.irc.addHook("_DISCONNECT", self.connection_closed)
# legacy support # legacy support
self.act_PONG = self.irc.act_PONG self.act_PONG = self.irc.act_PONG
self.act_USER = self.irc.act_USER self.act_USER = self.irc.act_USER
self.act_NICK = self.irc.act_NICK self.act_NICK = self.irc.act_NICK
self.act_JOIN = self.irc.act_JOIN self.act_JOIN = self.irc.act_JOIN
self.act_PRIVMSG = self.irc.act_PRIVMSG self.act_PRIVMSG = self.irc.act_PRIVMSG
self.act_MODE = self.irc.act_MODE self.act_MODE = self.irc.act_MODE
self.act_ACTION = self.irc.act_ACTION self.act_ACTION = self.irc.act_ACTION
self.act_KICK = self.irc.act_KICK self.act_KICK = self.irc.act_KICK
self.act_QUIT = self.irc.act_QUIT self.act_QUIT = self.irc.act_QUIT
self.get_nick = self.irc.get_nick self.get_nick = self.irc.get_nick
self.decodePrefix = IRCCore.decodePrefix self.decodePrefix = IRCCore.decodePrefix
# Load modules # Load modules
self.initModules() self.initModules()
# Connect to IRC # Connect to IRC
self.connect() self.connect()
def connect(self): def connect(self):
self.irc._connect() self.irc._connect()
def loop(self): def loop(self):
self.irc.loop() self.irc.loop()
def kill(self): def kill(self):
"""Shut down the bot violently""" """Shut down the bot violently"""
#Close all modules #Close all modules
self.closeAllModules() self.closeAllModules()
self.irc.kill() self.irc.kill()
sys.exit(0) sys.exit(0)
def connection_closed(self): def connection_closed(self):
"""Called when the socket is disconnected. We will want to reconnect. """ """Called when the socket is disconnected. We will want to reconnect. """
if self.alive: if self.alive:
self.log.warning("Connection was lost. Reconnecting in 5 seconds.") self.log.warning("Connection was lost. Reconnecting in 5 seconds.")
time.sleep(5) time.sleep(5)
self._connect() self._connect()
def initModules(self): def initModules(self):
"""load modules specified in instance config""" """load modules specified in instance config"""
" append module location to path " " append module location to path "
sys.path.append(os.path.dirname(__file__)+"/modules/") sys.path.append(os.path.dirname(__file__)+"/modules/")
for modulename in self.botconfig["modules"]: for modulename in self.botconfig["modules"]:
self.loadmodule(modulename) self.loadmodule(modulename)
def importmodule(self, name): def importmodule(self, name):
"""Import a module """Import a module
:param moduleName: Name of the module to import :param moduleName: Name of the module to import
:type moduleName: str""" :type moduleName: str"""
" check if already exists " " check if already exists "
if not name in self.modules: if not name in self.modules:
" attempt to load " " attempt to load "
try: try:
moduleref = __import__(name) moduleref = __import__(name)
self.modules[name]=moduleref self.modules[name]=moduleref
return (True, None) return (True, None)
except Exception as e: except Exception as e:
" on failure (usually syntax error in Module code) print an error " " on failure (usually syntax error in Module code) print an error "
self.log.error("Module %s failed to load: " % name) self.log.error("Module %s failed to load: " % name)
self.log.error("Module load failure reason: " + str(e)) self.log.error("Module load failure reason: " + str(e))
return (False, str(e)) return (False, str(e))
else: else:
self.log.warning("Module %s already imported" % name) self.log.warning("Module %s already imported" % name)
return (False, "Module already imported") return (False, "Module already imported")
def deportmodule(self, name): def deportmodule(self, name):
"""Remove a module's code from memory. If the module is loaded it will be unloaded silently. """Remove a module's code from memory. If the module is loaded it will be unloaded silently.
:param moduleName: Name of the module to import :param moduleName: Name of the module to import
:type moduleName: str""" :type moduleName: str"""
" unload if necessary " " unload if necessary "
if name in self.moduleInstances: if name in self.moduleInstances:
self.unloadmodule(name) self.unloadmodule(name)
" delete all references to the module" " delete all references to the module"
if name in self.modules: if name in self.modules:
item = self.modules[name] item = self.modules[name]
del self.modules[name] del self.modules[name]
del item del item
" delete copy that python stores in sys.modules " " delete copy that python stores in sys.modules "
if name in sys.modules: if name in sys.modules:
del sys.modules[name] del sys.modules[name]
def loadmodule(self, name): def loadmodule(self, name):
"""Activate a module. """Activate a module.
:param moduleName: Name of the module to activate :param moduleName: Name of the module to activate
:type moduleName: str""" :type moduleName: str"""
" check if already loaded " " check if already loaded "
if name in self.moduleInstances: if name in self.moduleInstances:
self.log.warning( "Module %s already loaded" % name ) self.log.warning( "Module %s already loaded" % name )
return False return False
" check if needs to be imported, and verify it was " " check if needs to be imported, and verify it was "
if not name in self.modules: if not name in self.modules:
importResult = self.importmodule(name) importResult = self.importmodule(name)
if not importResult[0]: if not importResult[0]:
return importResult return importResult
" init the module " " init the module "
self.moduleInstances[name] = getattr(self.modules[name], name)(self, name) self.moduleInstances[name] = getattr(self.modules[name], name)(self, name)
" load hooks " " load hooks "
self.loadModuleHooks(self.moduleInstances[name]) self.loadModuleHooks(self.moduleInstances[name])
def unloadmodule(self, name): def unloadmodule(self, name):
"""Deactivate a module. """Deactivate a module.
:param moduleName: Name of the module to deactivate :param moduleName: Name of the module to deactivate
:type moduleName: str""" :type moduleName: str"""
if name in self.moduleInstances: if name in self.moduleInstances:
" notify the module of disabling " " notify the module of disabling "
self.moduleInstances[name].ondisable() self.moduleInstances[name].ondisable()
" unload all hooks " " unload all hooks "
self.unloadModuleHooks(self.moduleInstances[name]) self.unloadModuleHooks(self.moduleInstances[name])
" remove the instance " " remove the instance "
item = self.moduleInstances.pop(name) item = self.moduleInstances.pop(name)
" delete the instance" " delete the instance"
del item del item
self.log.info( "Module %s unloaded" % name ) self.log.info( "Module %s unloaded" % name )
return (True, None) return (True, None)
else: else:
self.log.info("Module %s not loaded" % name) self.log.info("Module %s not loaded" % name)
return (False, "Module not loaded") return (False, "Module not loaded")
def reloadmodule(self, name): def reloadmodule(self, name):
"""Deactivate and activate a module. """Deactivate and activate a module.
:param moduleName: Name of the target module :param moduleName: Name of the target module
:type moduleName: str""" :type moduleName: str"""
" make sure it's imporeted" " make sure it's imporeted"
if name in self.modules: if name in self.modules:
" remember if it was loaded before" " remember if it was loaded before"
loadedbefore = name in self.moduleInstances loadedbefore = name in self.moduleInstances
self.log.info("Reloading %s" % self.modules[name]) self.log.info("Reloading %s" % self.modules[name])
" unload " " unload "
self.unloadmodule(name) self.unloadmodule(name)
" load " " load "
if loadedbefore: if loadedbefore:
self.loadmodule(name) self.loadmodule(name)
return (True, None) return (True, None)
return (False, "Module is not loaded") return (False, "Module is not loaded")
def redomodule(self, name): def redomodule(self, name):
"""Reload a running module from disk """Reload a running module from disk
:param moduleName: Name of the target module :param moduleName: Name of the target module
:type moduleName: str""" :type moduleName: str"""
" remember if it was loaded before " " remember if it was loaded before "
loadedbefore = name in self.moduleInstances loadedbefore = name in self.moduleInstances
" unload/deport " " unload/deport "
self.deportmodule(name) self.deportmodule(name)
" import " " import "
importResult = self.importmodule(name) importResult = self.importmodule(name)
if not importResult[0]: if not importResult[0]:
return importResult return importResult
" load " " load "
if loadedbefore: if loadedbefore:
self.loadmodule(name) self.loadmodule(name)
return (True, None) return (True, None)
def loadModuleHooks(self, module): def loadModuleHooks(self, module):
"""**Internal.** Enable (connect) hooks of a module """**Internal.** Enable (connect) hooks of a module
:param module: module object to hook in :param module: module object to hook in
:type module: object""" :type module: object"""
" activate a module's hooks " " activate a module's hooks "
for hook in module.hooks: for hook in module.hooks:
if type(hook.hook) == list: if type(hook.hook) == list:
for hookcmd in hook.hook: for hookcmd in hook.hook:
self.irc.addHook(hookcmd, hook.method) self.irc.addHook(hookcmd, hook.method)
else: else:
self.irc.addHook(hook.hook, hook.method) self.irc.addHook(hook.hook, hook.method)
def unloadModuleHooks(self, module): def unloadModuleHooks(self, module):
"""**Internal.** Disable (disconnect) hooks of a module """**Internal.** Disable (disconnect) hooks of a module
:param module: module object to unhook :param module: module object to unhook
:type module: object""" :type module: object"""
" remove a modules hooks " " remove a modules hooks "
for hook in module.hooks: for hook in module.hooks:
if type(hook.hook) == list: if type(hook.hook) == list:
for hookcmd in hook.hook: for hookcmd in hook.hook:
self.irc.removeHook(hookcmd, hook.method) self.irc.removeHook(hookcmd, hook.method)
else: else:
self.irc.removeHook(hook.hook, hook.method) self.irc.removeHook(hook.hook, hook.method)
def getmodulebyname(self, name): def getmodulebyname(self, name):
"""Get a module object by name """Get a module object by name
:param name: name of the module to return :param name: name of the module to return
:type name: str :type name: str
:returns: object -- the module object""" :returns: object -- the module object"""
if not name in self.moduleInstances: if not name in self.moduleInstances:
return None return None
return self.moduleInstances[name] return self.moduleInstances[name]
def getmodulesbyservice(self, service): def getmodulesbyservice(self, service):
"""Get a list of modules that provide the specified service """Get a list of modules that provide the specified service
:param service: name of the service searched for :param service: name of the service searched for
:type service: str :type service: str
:returns: list -- a list of module objects""" :returns: list -- a list of module objects"""
validModules = [] validModules = []
for module in self.moduleInstances: for module in self.moduleInstances:
if service in self.moduleInstances[module].services: if service in self.moduleInstances[module].services:
validModules.append(self.moduleInstances[module]) validModules.append(self.moduleInstances[module])
return validModules return validModules
def getBestModuleForService(self, service): def getBestModuleForService(self, service):
"""Get the first module that provides the specified service """Get the first module that provides the specified service
:param service: name of the service searched for :param service: name of the service searched for
:type service: str :type service: str
:returns: object -- the module object, if found. None if not found.""" :returns: object -- the module object, if found. None if not found."""
m = self.getmodulesbyservice(service) m = self.getmodulesbyservice(service)
if len(m)>0: if len(m)>0:
return m[0] return m[0]
return None return None
def closeAllModules(self): def closeAllModules(self):
""" Deport all modules (for shutdown). Modules are unloaded in the opposite order listed in the config. """ """ Deport all modules (for shutdown). Modules are unloaded in the opposite order listed in the config. """
loaded = list(self.moduleInstances.keys()) loaded = list(self.moduleInstances.keys())
loadOrder = self.botconfig["modules"] loadOrder = self.botconfig["modules"]
loadOrder.reverse() loadOrder.reverse()
for key in loadOrder: for key in loadOrder:
if key in loaded: if key in loaded:
loaded.remove(key) loaded.remove(key)
self.deportmodule(key) self.deportmodule(key)
for key in loaded: for key in loaded:
self.deportmodule(key) self.deportmodule(key)
" Filesystem Methods " " Filesystem Methods "
def getDataPath(self, moduleName): def getDataPath(self, moduleName):
"""Return the absolute path for a module's data dir """Return the absolute path for a module's data dir
:param moduleName: the module who's data dir we want :param moduleName: the module who's data dir we want
:type moduleName: str""" :type moduleName: str"""
if not os.path.exists("%s/data/%s" % (self.botconfig["bot"]["datadir"], moduleName)): if not os.path.exists("%s/data/%s" % (self.botconfig["bot"]["datadir"], moduleName)):
os.mkdir("%s/data/%s/" % (self.botconfig["bot"]["datadir"], moduleName)) os.mkdir("%s/data/%s/" % (self.botconfig["bot"]["datadir"], moduleName))
return "%s/data/%s/" % (self.botconfig["bot"]["datadir"], moduleName) return "%s/data/%s/" % (self.botconfig["bot"]["datadir"], moduleName)
def getConfigPath(self, moduleName): def getConfigPath(self, moduleName):
"""Return the absolute path for a module's config file """Return the absolute path for a module's config file
:param moduleName: the module who's config file we want :param moduleName: the module who's config file we want
:type moduleName: str""" :type moduleName: str"""
basepath = "%s/config/%s" % (self.botconfig["bot"]["datadir"], moduleName) basepath = "%s/config/%s" % (self.botconfig["bot"]["datadir"], moduleName)
if os.path.exists("%s.yml"%basepath): if os.path.exists("%s.yml"%basepath):
return "%s.yml"%basepath return "%s.yml"%basepath
elif os.path.exists("%s.json"%basepath): elif os.path.exists("%s.json"%basepath):
return "%s.json"%basepath return "%s.json"%basepath
return None return None
" Utility methods " " Utility methods "
@staticmethod @staticmethod
def messageHasCommand(command, message, requireArgs=False): def messageHasCommand(command, message, requireArgs=False):
"""Check if a message has a command with or without args in it """Check if a message has a command with or without args in it
:param command: the command string to look for, like !ban. If a list is passed, the first match is returned. :param command: the command string to look for, like !ban. If a list is passed, the first match is returned.
:type command: str or list :type command: str or list
:param message: the message string to look in, like "!ban Lil_Mac" :param message: the message string to look in, like "!ban Lil_Mac"
:type message: str :type message: str
:param requireArgs: if true, only validate if the command use has any amount of trailing text :param requireArgs: if true, only validate if the command use has any amount of trailing text
:type requireArgs: bool""" :type requireArgs: bool"""
if not type(command)==list: if not type(command)==list:
command = [command] command = [command]
for item in command: for item in command:
cmd = PyIRCBot.messageHasCommandSingle(item, message, requireArgs) cmd = PyIRCBot.messageHasCommandSingle(item, message, requireArgs)
if cmd: if cmd:
return cmd return cmd
return False return False
@staticmethod @staticmethod
def messageHasCommandSingle(command, message, requireArgs=False): def messageHasCommandSingle(command, message, requireArgs=False):
# Check if the message at least starts with the command # Check if the message at least starts with the command
messageBeginning = message[0:len(command)] messageBeginning = message[0:len(command)]
if messageBeginning!=command: if messageBeginning!=command:
return False return False
# Make sure it's not a subset of a longer command (ie .meme being set off by .memes) # Make sure it's not a subset of a longer command (ie .meme being set off by .memes)
subsetCheck = message[len(command):len(command)+1] subsetCheck = message[len(command):len(command)+1]
if subsetCheck!=" " and subsetCheck!="": if subsetCheck!=" " and subsetCheck!="":
return False return False
# We've got the command! Do we need args? # We've got the command! Do we need args?
argsStart = len(command) argsStart = len(command)
args = "" args = ""
if argsStart > 0: if argsStart > 0:
args = message[argsStart+1:] args = message[argsStart+1:]
if requireArgs and args.strip() == '': if requireArgs and args.strip() == '':
return False return False
# Verified! Return the set. # Verified! Return the set.
ob = type('ParsedCommand', (object,), {}) ob = type('ParsedCommand', (object,), {})
ob.command = command ob.command = command
ob.args = [] if args=="" else args.split(" ") ob.args = [] if args=="" else args.split(" ")
ob.args_str = args ob.args_str = args
ob.message = message ob.message = message
return ob return ob
# return (True, command, args, message) # return (True, command, args, message)
@staticmethod @staticmethod
def load(filepath): def load(filepath):
"""Return an object from the passed filepath """Return an object from the passed filepath
:param filepath: path to a file of json or yaml format. filename must end with .json or .yml :param filepath: path to a file of json or yaml format. filename must end with .json or .yml
:type filepath: str :type filepath: str
:Returns: | dict :Returns: | dict
""" """
if filepath.endswith(".yml"): if filepath.endswith(".yml"):
from yaml import load from yaml import load
return load(open(filepath, 'r')) return load(open(filepath, 'r'))
elif filepath.endswith(".json"): elif filepath.endswith(".json"):
from json import load from json import load
return load(open(filepath, 'r')) return load(open(filepath, 'r'))
else: else:
raise Exception("Unknown config format") raise Exception("Unknown config format")