#!/user/bin/env python import twitchBot, os, re, sys, imp, traceback from configparser import RawConfigParser from parseModulesFromFile import parseMods class VespaBot(twitchBot.TwitchBot): """Defines an instance of VeSPAbot, an IRC bot designed for the VeSPA Twitch.tv channel chat that subclasses twitchBot.TwitchBot. The class requires a cfg file with the template and information described below and a list of module files to load at runtime: [settings] nick=bot's twitch account name password=bot twitch account password stream=the twitch channel name owner= a list of twitch accounts the bot will recognize as 'owners' of the bot [streamers] none=message to display if no streamer is set streamer name 1: information to display for this streamer . . . streamer name N: information to display for this streamer Keyword arguments: verbosemode: a boolean value controlling print statements for debugging and possible logging purposes. Defaults True. testmode: a boolean value that controls whether output should made to sys.stdout or to the twtich channel chat. Defaults False.""" def __init__(self, configure_file, mods, verbosemode=True, testmode=False): """Initialize instance of VesapBot, parameter summary in class documentation.""" self.modules=[] self.config_file=configure_file self.settings=self.__set_up_config(self.config_file) twitchBot.TwitchBot.__init__(self, self.settings['nick'], self.settings['password'], self.settings['stream'], verbose=verbosemode, test=testmode) self.shush=False #shush prevents the bot from responding, but still allows it to continue executing commands self.currentGame='' self.streamer=("No one", "") self.commands={'owners':{}, 'admins':{}, 'regular':{}} self.help={} self.command_list=[] self.say_funcs=[] #list of functions that modifiy the say gateway function self.terminate_funcs=[] #list of function to be called to safely terminate the bot self.__load_mods(mods) #Given a config file, parse it to get the nickname, password, stream name, and owners for the bot to use to login def __set_up_config(self, file): """Given a config file, file, parse it to get the nickname, password, stream name, and owners for the bot to use to login to the bot's twitch account""" parser=RawConfigParser() parser.read(file) settings={} settings['nick']=parser.get('settings', 'nick') settings['password']=parser.get('settings', 'password') settings['stream']=parser.get('settings', 'stream') settings['owners']=parser.get('settings', 'owner') return settings #given a list of modules, attempt to load each module and register all of the functions within the modules def __load_mods(self, mods): """attempt to load each module from a list of modules and call __update_commands to register the functions in the modules with the instance of VespaBot""" if self.verbose_m: print("mods to load:\n"+str(mods)) print("Total mods to be loaded: "+str(len(mods))) for file in mods: if self.verbose_m: print("Attempting to load module "+file) filename=file #all the follwing [:X] are unideal, and system specific to windows, should be more robust name=os.path.basename(filename)[:-4] try: mod=imp.load_source(name, filename) self.modules.append(name) self.__update_commands(vars(mod)) if self.verbose_m: print("Successfully loaded module "+filename) except Exception as e: print('Error Loading %s: '%(name)) traceback.print_exc(file=sys.stdout) #given a dictionary of functions from a module, register the function with its chat command, add the fucntion to the say_funcs list or terminate_funcs list, or run a setup function def __update_commands(self, commands): """Parses the functions of a module and registers then to the VespaBot instance according to any special attributes the function has""" for name, object in commands.items(): if hasattr(object, 'commands'): privilege ='' if not hasattr(object, 'privilege'): privilege='regular' else: privilege=object.privilege for command in object.commands: self.commands[privilege][command]=object if self.verbose_m: print("registering "+name+" to the "+privilege+" commands with the signature"+command) #add all regular command names to a list for possible use in module functions if hasattr(object, 'usage'): #add a usage description for the command if privilege=='regular': self.help[object.usage[0]]=object.usage[1] self.command_list.append(object.usage[0]) else: self.command_list.append(name) else: self.command_list.append(name) if hasattr(object, 'register_say'): self.say_funcs.append(object) if self.verbose_m: print("fucntion "+name+" register to say functions") if hasattr(object, 'register_terminate'): self.terminate_funcs.append(object) if self.verbose_m: print("fucntion "+name+" register to terminate functions") if hasattr(object, 'setup'): try: if self.verbose_m: print("Attempting to run setup function "+name+". . .") object(self) except Exception as e: if self.verbose_m: print("Setup function "+name+" failed") print(e) def do_command(self, nick, msg): """Given a user nickname and message, check to see if the message is an acceptable command and, if so, execute that command""" if nick in self.settings['owners']: for x in self.commands["owners"]: if re.search(x, msg, re.I): self.commands["owners"].get(x)(self, nick, msg) return #TODO: Implement check for admin status to run admin commands #if nick in self.settings['owners'] OR check for nick to have admin sign (may require changing TwitchBot found_terminator function for x in self.commands["regular"]: if re.search(x, msg, re.I): self.commands["regular"].get(x)(self, nick, msg) def say(self, msg): """Gateway function to TwitchBot.say function, applies and special functions registered through __do_commands and imposes the shush mode behavior to prohibit or allow bot messages""" for func in self.say_funcs: try: msg=func(self, msg) except Exception as e: if self.verbose_m: print('failed to execute function '+func) print(e) if not self.shush: twitchBot.TwitchBot.say(self, msg) def terminate(self): """Attempt to safely terminates the VespaBot instance by running any special functions registered through __do_commands and calling the TwitchBot.terminate function""" for func in self.terminate_funcs: try: func(self) except Exception as e: if self.verbose_m: print('failed to execute function: '+func) print(e) twitchBot.TwitchBot.terminate(self) if __name__=="__main__": usage="usage: -f filename or -l module1.py module2.py . . . moduleN.py" if len(sys.argv)>2: if sys.argv[1] in ("-f", "--file"): try: mods=parseMods(sys.argv[2]) vbot=VespaBot('VespaBot.cfg', mods) vbot.run() except Exception as e: print("error starting vespabot:") print(e) traceback.print_exc(file=sys.stdout) elif sys.argv[1] in ("-l", "--list"): try: mods=sys.argv[2:] vbot=VespaBot('VespaBot.cfg', mods) vbot.run() except Exception as e: print("error starting vespabot: "+e) elif len(sys.argv)>1: if sys.argv[1] in ("-h", "--help"): print(usage) raise SystemExit(0) else: print(usage) raise SystemExit(0)