| @@ -11,9 +11,9 @@ class KwargParseException(Exception): | |||||
| pass | pass | ||||
| class Data(object): | class Data(object): | ||||
| def __init__(self): | |||||
| """store data from an individual line received on IRC""" | |||||
| self.line = str() | |||||
| def __init__(self, line): | |||||
| """Store data from an individual line received on IRC.""" | |||||
| self.line = line | |||||
| self.chan = str() | self.chan = str() | ||||
| self.nick = str() | self.nick = str() | ||||
| self.ident = str() | self.ident = str() | ||||
| @@ -5,29 +5,11 @@ | |||||
| import os | import os | ||||
| import traceback | import traceback | ||||
| commands = [] | |||||
| __all__ = ["load", "get_all", "check"] | |||||
| def load_commands(connection): | |||||
| """load all valid command classes from irc/commmands/ into the commands variable""" | |||||
| files = os.listdir(os.path.join("irc", "commands")) # get all files in irc/commands/ | |||||
| files.sort() # alphabetically sort list of files | |||||
| _commands = [] | |||||
| for f in files: | |||||
| if f.startswith("_") or not f.endswith(".py"): # ignore non-python files or files beginning with "_" | |||||
| continue | |||||
| module = f[:-3] # strip .py from end | |||||
| try: | |||||
| exec "from irc.commands import %s" % module | |||||
| except: # importing the file failed for some reason... | |||||
| print "Couldn't load file %s:" % f | |||||
| traceback.print_exc() | |||||
| continue | |||||
| process_module(connection, eval(module)) # 'module' is a string, so get the actual object for processing by eval-ing it | |||||
| pretty_cmnds = map(lambda c: c.__class__.__name__, commands) | |||||
| print "Found %s command classes: %s." % (len(commands), ', '.join(pretty_cmnds)) | |||||
| def process_module(connection, module): | |||||
| def _process_module(connection, module): | |||||
| """go through all objects in a module and add valid command classes to the commands variable""" | """go through all objects in a module and add valid command classes to the commands variable""" | ||||
| global commands | global commands | ||||
| objects = dir(module) | objects = dir(module) | ||||
| @@ -43,19 +25,41 @@ def process_module(connection, module): | |||||
| for base in bases: | for base in bases: | ||||
| if base.__name__ == "BaseCommand": # this inherits BaseCommand, so it must be a command class | if base.__name__ == "BaseCommand": # this inherits BaseCommand, so it must be a command class | ||||
| command = obj(connection) # initialize a new command object | command = obj(connection) # initialize a new command object | ||||
| commands.append(command) | |||||
| _commands.append(command) | |||||
| print "Added command class %s from %s..." % (this_obj, module.__name__) | print "Added command class %s from %s..." % (this_obj, module.__name__) | ||||
| continue | continue | ||||
| def get_commands(): | |||||
| """get our commands""" | |||||
| return commands | |||||
| def load(connection): | |||||
| """load all valid command classes from irc/commmands/ into the commands variable""" | |||||
| files = os.listdir(os.path.join("irc", "commands")) # get all files in irc/commands/ | |||||
| files.sort() # alphabetically sort list of files | |||||
| for f in files: | |||||
| if f.startswith("_") or not f.endswith(".py"): # ignore non-python files or files beginning with "_" | |||||
| continue | |||||
| module = f[:-3] # strip .py from end | |||||
| try: | |||||
| exec "from irc.commands import %s" % module | |||||
| except: # importing the file failed for some reason... | |||||
| print "Couldn't load file %s:" % f | |||||
| traceback.print_exc() | |||||
| continue | |||||
| process_module(connection, eval(module)) # 'module' is a string, so get the actual object for processing by eval-ing it | |||||
| pretty_cmnds = map(lambda c: c.__class__.__name__, commands) | |||||
| print "Found %s command classes: %s." % (len(commands), ', '.join(pretty_cmnds)) | |||||
| def get_all(): | |||||
| """Return our list of all commands.""" | |||||
| return _commands | |||||
| def check(hook, data): | def check(hook, data): | ||||
| """given an event on IRC, check if there's anything we can respond to by calling each command class""" | |||||
| data.parse_args() # parse command arguments into data.command and data.args | |||||
| """Given an event on IRC, check if there's anything we can respond to by | |||||
| calling each command class""" | |||||
| # parse command arguments into data.command and data.args | |||||
| data.parse_args() | |||||
| for command in commands: | |||||
| for command in _commands: | |||||
| if hook in command.get_hooks(): | if hook in command.get_hooks(): | ||||
| if command.check(data): | if command.check(data): | ||||
| try: | try: | ||||
| @@ -18,9 +18,9 @@ from within config's three global variables and one function: | |||||
| """ | """ | ||||
| import json | import json | ||||
| from os import makedirs, path | |||||
| from os import path | |||||
| from lib import blowfish | |||||
| import blowfish | |||||
| script_dir = path.dirname(path.abspath(__file__)) | script_dir = path.dirname(path.abspath(__file__)) | ||||
| root_dir = path.split(script_dir)[0] | root_dir = path.split(script_dir)[0] | ||||
| @@ -149,7 +149,6 @@ def schedule(minute, hour, month_day, month, week_day): | |||||
| def make_new_config(): | def make_new_config(): | ||||
| """Make a new config file based on the user's input.""" | """Make a new config file based on the user's input.""" | ||||
| makedirs(config_dir) | |||||
| encrypt = raw_input("Would you like to encrypt passwords stored in " + | encrypt = raw_input("Would you like to encrypt passwords stored in " + | ||||
| "config.json? [y/n] ") | "config.json? [y/n] ") | ||||
| @@ -1,7 +1,7 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| """ | """ | ||||
| EarwigBot's IRC Front-end Component | |||||
| EarwigBot's IRC Frontend Component | |||||
| The IRC frontend runs on a normal IRC server and expects users to interact with | The IRC frontend runs on a normal IRC server and expects users to interact with | ||||
| it and give it commands. Commands are stored as "command classes", subclasses | it and give it commands. Commands are stored as "command classes", subclasses | ||||
| @@ -9,13 +9,16 @@ of BaseCommand in irc/base_command.py. All command classes are automatically | |||||
| imported by irc/command_handler.py if they are in irc/commands. | imported by irc/command_handler.py if they are in irc/commands. | ||||
| """ | """ | ||||
| from re import findall | |||||
| import re | |||||
| from core import config | |||||
| from irc import command_handler | |||||
| from irc.classes import Connection, Data, BrokenSocketException | |||||
| import config | |||||
| import commands | |||||
| from classes import Connection, Data, BrokenSocketException | |||||
| __all__ = ["get_connection", "startup", "main"] | |||||
| connection = None | connection = None | ||||
| sender_regex = re.compile(":(.*?)!(.*?)@(.*?)\Z") | |||||
| def get_connection(): | def get_connection(): | ||||
| """Return a new Connection() instance with information about our server | """Return a new Connection() instance with information about our server | ||||
| @@ -31,16 +34,18 @@ def startup(conn): | |||||
| command_handler, and then establish a connection with the IRC server.""" | command_handler, and then establish a connection with the IRC server.""" | ||||
| global connection | global connection | ||||
| connection = conn | connection = conn | ||||
| command_handler.load_commands(connection) | |||||
| commands.load(connection) | |||||
| connection.connect() | connection.connect() | ||||
| def main(): | def main(): | ||||
| """Main loop for the Frontend IRC Bot component. get_connection() and | |||||
| startup() should have already been called.""" | |||||
| """Main loop for the frontend component. | |||||
| get_connection() and startup() should have already been called before this. | |||||
| """ | |||||
| read_buffer = str() | read_buffer = str() | ||||
| while 1: | while 1: | ||||
| try: | |||||
| try: | |||||
| read_buffer = read_buffer + connection.get() | read_buffer = read_buffer + connection.get() | ||||
| except BrokenSocketException: | except BrokenSocketException: | ||||
| print "Socket has broken on front-end; restarting bot..." | print "Socket has broken on front-end; restarting bot..." | ||||
| @@ -48,57 +53,60 @@ def main(): | |||||
| lines = read_buffer.split("\n") | lines = read_buffer.split("\n") | ||||
| read_buffer = lines.pop() | read_buffer = lines.pop() | ||||
| for line in lines: # handle a single message from IRC | |||||
| line = line.strip().split() | |||||
| data = Data() # new Data() instance to store info about this line | |||||
| data.line = line | |||||
| if line[1] == "JOIN": | |||||
| data.nick, data.ident, data.host = findall( | |||||
| ":(.*?)!(.*?)@(.*?)\Z", line[0])[0] | |||||
| data.chan = line[2][1:] | |||||
| command_handler.check("join", data) # check for 'join' hooks in | |||||
| # our commands | |||||
| if line[1] == "PRIVMSG": | |||||
| data.nick, data.ident, data.host = findall( | |||||
| ":(.*?)!(.*?)@(.*?)\Z", line[0])[0] | |||||
| data.msg = ' '.join(line[3:])[1:] | |||||
| data.chan = line[2] | |||||
| if data.chan == config.irc["frontend"]["nick"]: | |||||
| # this is a privmsg to us, so set 'chan' as the nick of the | |||||
| # sender, then check for private-only command hooks | |||||
| data.chan = data.nick | |||||
| command_handler.check("msg_private", data) | |||||
| else: | |||||
| # check for public-only command hooks | |||||
| command_handler.check("msg_public", data) | |||||
| # check for command hooks that apply to all messages | |||||
| command_handler.check("msg", data) | |||||
| # hardcode the !restart command (we can't restart from within | |||||
| # an ordinary command) | |||||
| if data.msg in ["!restart", ".restart"]: | |||||
| if data.host in config.irc["permissions"]["owners"]: | |||||
| print "Restarting bot per owner request..." | |||||
| return | |||||
| if line[0] == "PING": # if we are pinged, pong back to the server | |||||
| connection.send("PONG %s" % line[1]) | |||||
| if line[1] == "376": # we've successfully connected to the network | |||||
| try: # if we're supposed to auth to nickserv, do that | |||||
| ns_username = config.irc["frontend"]["nickservUsername"] | |||||
| ns_password = config.irc["frontend"]["nickservPassword"] | |||||
| except KeyError: | |||||
| pass | |||||
| else: | |||||
| connection.say("NickServ", "IDENTIFY {0} {1}".format( | |||||
| ns_username, ns_password)) | |||||
| # join all of our startup channels | |||||
| for chan in config.irc["frontend"]["channels"]: | |||||
| connection.join(chan) | |||||
| for line in lines: | |||||
| _process_message(line) | |||||
| def _process_message(line): | |||||
| """Process a single message from IRC.""" | |||||
| line = line.strip().split() | |||||
| data = Data(line) # new Data instance to store info about this line | |||||
| if line[1] == "JOIN": | |||||
| data.nick, data.ident, data.host = sender_regex.findall(line[0])[0] | |||||
| data.chan = line[2][1:] | |||||
| # Check for 'join' hooks in our commands: | |||||
| commands.check("join", data) | |||||
| elif line[1] == "PRIVMSG": | |||||
| data.nick, data.ident, data.host = sender_regex.findall(line[0])[0] | |||||
| data.msg = ' '.join(line[3:])[1:] | |||||
| data.chan = line[2] | |||||
| if data.chan == config.irc["frontend"]["nick"]: | |||||
| # This is a privmsg to us, so set 'chan' as the nick of the, sender | |||||
| # then check for private-only command hooks: | |||||
| data.chan = data.nick | |||||
| commands.check("msg_private", data) | |||||
| else: | |||||
| # Check for public-only command hooks: | |||||
| commands.check("msg_public", data) | |||||
| # Check for command hooks that apply to all messages: | |||||
| commands.check("msg", data) | |||||
| # Hardcode the !restart command (we can't restart from within an | |||||
| # ordinary command): | |||||
| if data.msg in ["!restart", ".restart"]: | |||||
| if data.host in config.irc["permissions"]["owners"]: | |||||
| print "Restarting bot per owner request..." | |||||
| return | |||||
| # If we are pinged, pong back to the server: | |||||
| if line[0] == "PING": | |||||
| connection.send("PONG %s" % line[1]) | |||||
| # On successful connection to the server: | |||||
| if line[1] == "376": | |||||
| # If we're supposed to auth to NickServ, do that: | |||||
| try: | |||||
| username = config.irc["frontend"]["nickservUsername"] | |||||
| password = config.irc["frontend"]["nickservPassword"] | |||||
| except KeyError: | |||||
| pass | |||||
| else: | |||||
| msg = " ".join(("IDENTIFY", username, password)) | |||||
| connection.say("NickServ", msg) | |||||
| # Join all of our startup channels: | |||||
| for chan in config.irc["frontend"]["channels"]: | |||||
| connection.join(chan) | |||||
| @@ -33,17 +33,11 @@ Else, the bot will stop, as no components are enabled. | |||||
| import threading | import threading | ||||
| import time | import time | ||||
| import traceback | import traceback | ||||
| import sys | |||||
| import os | |||||
| script_dir = os.path.dirname(os.path.abspath(__file__)) | |||||
| root_dir = os.path.split(script_dir)[0] # the bot's "root" directory relative | |||||
| # to its different components | |||||
| sys.path.append(root_dir) # make sure we look in the root dir for modules | |||||
| from core import config | |||||
| from irc import frontend, watcher | |||||
| from wiki import task_manager | |||||
| import config | |||||
| import frontend | |||||
| import tasks | |||||
| import watcher | |||||
| f_conn = None | f_conn = None | ||||
| w_conn = None | w_conn = None | ||||
| @@ -70,7 +64,7 @@ def wiki_scheduler(): | |||||
| time_start = time.time() | time_start = time.time() | ||||
| now = time.gmtime(time_start) | now = time.gmtime(time_start) | ||||
| task_manager.start_tasks(now) | |||||
| tasks.schedule(now) | |||||
| time_end = time.time() | time_end = time.time() | ||||
| time_diff = time_start - time_end | time_diff = time_start - time_end | ||||
| @@ -89,7 +83,7 @@ def irc_frontend(): | |||||
| if "wiki_schedule" in config.components: | if "wiki_schedule" in config.components: | ||||
| print "\nStarting wiki scheduler..." | print "\nStarting wiki scheduler..." | ||||
| task_manager.load_tasks() | |||||
| tasks.load() | |||||
| t_scheduler = threading.Thread(target=wiki_scheduler) | t_scheduler = threading.Thread(target=wiki_scheduler) | ||||
| t_scheduler.name = "wiki-scheduler" | t_scheduler.name = "wiki-scheduler" | ||||
| t_scheduler.daemon = True | t_scheduler.daemon = True | ||||
| @@ -123,7 +117,7 @@ def run(): | |||||
| elif "wiki_schedule" in enabled: # run the scheduler on the main | elif "wiki_schedule" in enabled: # run the scheduler on the main | ||||
| print "Starting wiki scheduler..." # thread, but also run the IRC | print "Starting wiki scheduler..." # thread, but also run the IRC | ||||
| task_manager.load_tasks() # watcher on another thread iff it | |||||
| tasks.load() # watcher on another thread iff it | |||||
| if "irc_watcher" in enabled: # is enabled | if "irc_watcher" in enabled: # is enabled | ||||
| print "\nStarting IRC watcher..." | print "\nStarting IRC watcher..." | ||||
| t_watcher = threading.Thread(target=irc_watcher, args=()) | t_watcher = threading.Thread(target=irc_watcher, args=()) | ||||
| @@ -1,9 +1,10 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| """ | """ | ||||
| EarwigBot's Wiki Bot Task Manager | |||||
| EarwigBot's Wiki Task Manager | |||||
| This module provides some functions to run and load bot tasks from wiki/tasks/. | |||||
| This package provides the wiki bot "tasks" EarwigBot runs. Here in __init__, | |||||
| you can find some functions used to load and run these tasks. | |||||
| """ | """ | ||||
| import time | import time | ||||
| @@ -11,28 +12,17 @@ import traceback | |||||
| import threading | import threading | ||||
| import os | import os | ||||
| from core import config | |||||
| import config | |||||
| __all__ = ["load", "schedule", "start"] | |||||
| # store loaded tasks as a dict where the key is the task name and the value is | # store loaded tasks as a dict where the key is the task name and the value is | ||||
| # an instance of the task class (wiki.tasks.task_file.Task()) | # an instance of the task class (wiki.tasks.task_file.Task()) | ||||
| task_list = dict() | |||||
| _tasks = dict() | |||||
| def load_tasks(): | |||||
| """Load all valid task classes from wiki/tasks/, and add them to the | |||||
| task_list variable.""" | |||||
| files = os.listdir(os.path.join("wiki", "tasks")) | |||||
| files.sort() # alphabetically sort all files in wiki/tasks/ | |||||
| for f in files: | |||||
| if not os.path.isfile(os.path.join("wiki", "tasks", f)): | |||||
| continue # ignore non-files | |||||
| if f.startswith("_") or not f.endswith(".py"): | |||||
| continue # ignore non-python files or files beginning with an _ | |||||
| load_class_from_file(f) | |||||
| print "Found %s tasks: %s." % (len(task_list), ', '.join(task_list.keys())) | |||||
| def load_class_from_file(f): | |||||
| def _load_class_from_file(f): | |||||
| """Look in a given file for the task class.""" | """Look in a given file for the task class.""" | ||||
| global task_list | |||||
| global _tasks | |||||
| module = f[:-3] # strip .py from end | module = f[:-3] # strip .py from end | ||||
| try: | try: | ||||
| @@ -48,45 +38,58 @@ def load_class_from_file(f): | |||||
| traceback.print_exc() | traceback.print_exc() | ||||
| return | return | ||||
| task_name = task_class.task_name | task_name = task_class.task_name | ||||
| task_list[task_name] = task_class() | |||||
| print "Added task %s from wiki/tasks/%s..." % (task_name, f) | |||||
| _tasks[task_name] = task_class() | |||||
| print "Added task %s from bot/tasks/%s..." % (task_name, f) | |||||
| def _wrapper(task, **kwargs): | |||||
| """Wrapper for task classes: run the task and catch any errors.""" | |||||
| try: | |||||
| task.run(**kwargs) | |||||
| except: | |||||
| print "Task '{0}' raised an exception and had to stop:".format(task.task_name) | |||||
| traceback.print_exc() | |||||
| else: | |||||
| print "Task '{0}' finished without error.".format(task.task_name) | |||||
| def start_tasks(now=time.gmtime()): | |||||
| def load(): | |||||
| """Load all valid task classes from bot/tasks/, and add them to the | |||||
| _tasks variable.""" | |||||
| files = os.listdir(os.path.join("bot", "tasks")) | |||||
| files.sort() # alphabetically sort all files in wiki/tasks/ | |||||
| for f in files: | |||||
| if not os.path.isfile(os.path.join("bot", "tasks", f)): | |||||
| continue # ignore non-files | |||||
| if f.startswith("_") or not f.endswith(".py"): | |||||
| continue # ignore non-python files or files beginning with an _ | |||||
| load_class_from_file(f) | |||||
| print "Found %s tasks: %s." % (len(_tasks), ', '.join(_tasks.keys())) | |||||
| def schedule(now=time.gmtime()): | |||||
| """Start all tasks that are supposed to be run at a given time.""" | """Start all tasks that are supposed to be run at a given time.""" | ||||
| tasks = config.schedule(now.tm_min, now.tm_hour, now.tm_mday, now.tm_mon, | tasks = config.schedule(now.tm_min, now.tm_hour, now.tm_mday, now.tm_mon, | ||||
| now.tm_wday) # get list of tasks to run this turn | now.tm_wday) # get list of tasks to run this turn | ||||
| for task in tasks: | for task in tasks: | ||||
| if isinstance(task, list): # they've specified kwargs | |||||
| start_task(task[0], **task[1]) # so pass those to start_task | |||||
| if isinstance(task, list): # they've specified kwargs | |||||
| start(task[0], **task[1]) # so pass those to start_task | |||||
| else: # otherwise, just pass task_name | else: # otherwise, just pass task_name | ||||
| start_task(task) | |||||
| start(task) | |||||
| def start_task(task_name, **kwargs): | |||||
| def start(task_name, **kwargs): | |||||
| """Start a given task in a new thread. Pass args to the task's run() | """Start a given task in a new thread. Pass args to the task's run() | ||||
| function.""" | function.""" | ||||
| print "Starting task '{0}' in a new thread...".format(task_name) | print "Starting task '{0}' in a new thread...".format(task_name) | ||||
| try: | try: | ||||
| task = task_list[task_name] | |||||
| task = _tasks[task_name] | |||||
| except KeyError: | except KeyError: | ||||
| print ("Couldn't find task '{0}': wiki/tasks/{0}.py does not exist.").format(task_name) | |||||
| print ("Couldn't find task '{0}': bot/tasks/{0}.py does not exist.").format(task_name) | |||||
| return | return | ||||
| task_thread = threading.Thread(target=lambda: task_wrapper(task, **kwargs)) | |||||
| task_thread = threading.Thread(target=lambda: _wrapper(task, **kwargs)) | |||||
| task_thread.name = "{0} ({1})".format(task_name, time.strftime("%b %d %H:%M:%S")) | task_thread.name = "{0} ({1})".format(task_name, time.strftime("%b %d %H:%M:%S")) | ||||
| # stop bot task threads automagically if the main bot stops | # stop bot task threads automagically if the main bot stops | ||||
| task_thread.daemon = True | task_thread.daemon = True | ||||
| task_thread.start() | task_thread.start() | ||||
| def task_wrapper(task, **kwargs): | |||||
| """Wrapper for task classes: run the task and catch any errors.""" | |||||
| try: | |||||
| task.run(**kwargs) | |||||
| except: | |||||
| print "Task '{0}' raised an exception and had to stop:".format(task.task_name) | |||||
| traceback.print_exc() | |||||
| else: | |||||
| print "Task '{0}' finished without error.".format(task.task_name) | |||||
| @@ -10,25 +10,30 @@ wiki bot tasks being started (listed in wiki/tasks/) or messages being sent to | |||||
| channels in the IRC frontend. | channels in the IRC frontend. | ||||
| """ | """ | ||||
| from core import config | |||||
| from irc.classes import Connection, RC, BrokenSocketException | |||||
| from irc import watcher_logic | |||||
| import config | |||||
| from classes import Connection, RC, BrokenSocketException | |||||
| import watcher_logic as logic | |||||
| frontend_conn = None | frontend_conn = None | ||||
| def get_connection(): | def get_connection(): | ||||
| """Return a new Connection() instance with information about our server | |||||
| connection, but don't actually connect yet.""" | |||||
| """Return a new Connection() instance with connection information. | |||||
| Don't actually connect yet. | |||||
| """ | |||||
| cf = config.irc["watcher"] | cf = config.irc["watcher"] | ||||
| connection = Connection(cf["host"], cf["port"], cf["nick"], cf["ident"], | connection = Connection(cf["host"], cf["port"], cf["nick"], cf["ident"], | ||||
| cf["realname"]) | cf["realname"]) | ||||
| return connection | return connection | ||||
| def main(connection, f_conn=None): | def main(connection, f_conn=None): | ||||
| """Main loop for the Watcher IRC Bot component. get_connection() should | |||||
| have already been called and the connection should have been started with | |||||
| connection.connect(). Accept the frontend connection as well as an optional | |||||
| parameter in order to send messages directly to frontend IRC channels.""" | |||||
| """Main loop for the Watcher IRC Bot component. | |||||
| get_connection() should have already been called and the connection should | |||||
| have been started with connection.connect(). Accept the frontend connection | |||||
| as well as an optional parameter in order to send messages directly to | |||||
| frontend IRC channels. | |||||
| """ | |||||
| global frontend_conn | global frontend_conn | ||||
| frontend_conn = f_conn | frontend_conn = f_conn | ||||
| read_buffer = str() | read_buffer = str() | ||||
| @@ -43,34 +48,42 @@ def main(connection, f_conn=None): | |||||
| read_buffer = lines.pop() | read_buffer = lines.pop() | ||||
| for line in lines: | for line in lines: | ||||
| line = line.strip().split() | |||||
| if line[1] == "PRIVMSG": | |||||
| chan = line[2] | |||||
| # ignore messages originating from channels not in our list, to | |||||
| # prevent someone PMing us false data | |||||
| if chan not in config.irc["watcher"]["channels"]: | |||||
| continue | |||||
| msg = ' '.join(line[3:])[1:] | |||||
| rc = RC(msg) # new RC object to store this event's data | |||||
| rc.parse() # parse a message into pagenames, usernames, etc. | |||||
| process(rc) # report to frontend channels or start tasks | |||||
| if line[0] == "PING": # if we are pinged, pong back to the server | |||||
| connection.send("PONG %s" % line[1]) | |||||
| # when we've finished starting up, join all watcher channels | |||||
| if line[1] == "376": | |||||
| for chan in config.irc["watcher"]["channels"]: | |||||
| connection.join(chan) | |||||
| def process(rc): | |||||
| """Process a message from IRC (technically, an RC object). The actual | |||||
| processing is configurable, so we don't have that hard-coded here. We | |||||
| simply call irc/watcher_logic.py's process() function and expect a list of | |||||
| channels back, which we report the event data to.""" | |||||
| _process_message(line) | |||||
| def _process_message(line): | |||||
| """Process a single message from IRC.""" | |||||
| line = line.strip().split() | |||||
| if line[1] == "PRIVMSG": | |||||
| chan = line[2] | |||||
| # Ignore messages originating from channels not in our list, to prevent | |||||
| # someone PMing us false data: | |||||
| if chan not in config.irc["watcher"]["channels"]: | |||||
| continue | |||||
| msg = ' '.join(line[3:])[1:] | |||||
| rc = RC(msg) # new RC object to store this event's data | |||||
| rc.parse() # parse a message into pagenames, usernames, etc. | |||||
| process_rc(rc) # report to frontend channels or start tasks | |||||
| # If we are pinged, pong back to the server: | |||||
| elif line[0] == "PING": | |||||
| msg = " ".join(("PONG", line[1])) | |||||
| connection.send(msg) | |||||
| # When we've finished starting up, join all watcher channels: | |||||
| elif line[1] == "376": | |||||
| for chan in config.irc["watcher"]["channels"]: | |||||
| connection.join(chan) | |||||
| def process_rc(rc): | |||||
| """Process a recent change event from IRC (or, an RC object). | |||||
| The actual processing is configurable, so we don't have that hard-coded | |||||
| here. We simply call watcher_logic's process() function and expect a list | |||||
| of channels back, which we report the event data to. | |||||
| """ | |||||
| chans = watcher_logic.process(rc) | chans = watcher_logic.process(rc) | ||||
| if chans and frontend_conn: | if chans and frontend_conn: | ||||
| pretty = rc.get_pretty() | pretty = rc.get_pretty() | ||||
| @@ -12,7 +12,7 @@ sense for this sort of thing... so... | |||||
| import re | import re | ||||
| from wiki import task_manager as tasks | |||||
| import tasks | |||||
| afc_prefix = "wikipedia( talk)?:(wikiproject )?articles for creation" | afc_prefix = "wikipedia( talk)?:(wikiproject )?articles for creation" | ||||
| @@ -39,8 +39,8 @@ def process(rc): | |||||
| chans.update(("##earwigbot", "#wikipedia-en-afc")) | chans.update(("##earwigbot", "#wikipedia-en-afc")) | ||||
| if r_page.search(page_name): | if r_page.search(page_name): | ||||
| tasks.start_task("afc_statistics", action="process_edit", page=rc.page) | |||||
| tasks.start_task("afc_copyvios", action="process_edit", page=rc.page) | |||||
| tasks.start("afc_statistics", action="process_edit", page=rc.page) | |||||
| tasks.start("afc_copyvios", action="process_edit", page=rc.page) | |||||
| chans.add("#wikipedia-en-afc") | chans.add("#wikipedia-en-afc") | ||||
| elif r_ffu.match(page_name): | elif r_ffu.match(page_name): | ||||
| @@ -50,22 +50,22 @@ def process(rc): | |||||
| chans.add("#wikipedia-en-afc") | chans.add("#wikipedia-en-afc") | ||||
| elif rc.flags == "move" and (r_move1.match(comment) or | elif rc.flags == "move" and (r_move1.match(comment) or | ||||
| r_move2.match(comment)): | |||||
| r_move2.match(comment)): | |||||
| p = r_moved_pages.findall(rc.comment)[0] | p = r_moved_pages.findall(rc.comment)[0] | ||||
| tasks.start_task("afc_statistics", action="process_move", pages=p) | |||||
| tasks.start_task("afc_copyvios", action="process_move", pages=p) | |||||
| tasks.start("afc_statistics", action="process_move", pages=p) | |||||
| tasks.start("afc_copyvios", action="process_move", pages=p) | |||||
| chans.add("#wikipedia-en-afc") | chans.add("#wikipedia-en-afc") | ||||
| elif rc.flags == "delete" and r_delete.match(comment): | elif rc.flags == "delete" and r_delete.match(comment): | ||||
| p = r_deleted_page.findall(rc.comment)[0] | p = r_deleted_page.findall(rc.comment)[0] | ||||
| tasks.start_task("afc_statistics", action="process_delete", page=p) | |||||
| tasks.start_task("afc_copyvios", action="process_delete", page=p) | |||||
| tasks.start("afc_statistics", action="process_delete", page=p) | |||||
| tasks.start("afc_copyvios", action="process_delete", page=p) | |||||
| chans.add("#wikipedia-en-afc") | chans.add("#wikipedia-en-afc") | ||||
| elif rc.flags == "restore" and r_restore.match(comment): | elif rc.flags == "restore" and r_restore.match(comment): | ||||
| p = r_restored_page.findall(rc.comment)[0] | p = r_restored_page.findall(rc.comment)[0] | ||||
| tasks.start_task("afc_statistics", action="process_restore", page=p) | |||||
| tasks.start_task("afc_copyvios", action="process_restore", page=p) | |||||
| tasks.start("afc_statistics", action="process_restore", page=p) | |||||
| tasks.start("afc_copyvios", action="process_restore", page=p) | |||||
| chans.add("#wikipedia-en-afc") | chans.add("#wikipedia-en-afc") | ||||
| elif rc.flags == "protect" and r_protect.match(comment): | elif rc.flags == "protect" and r_protect.match(comment): | ||||
| @@ -20,7 +20,7 @@ from os import path | |||||
| from sys import executable | from sys import executable | ||||
| from time import sleep | from time import sleep | ||||
| from core.config import verify_config | |||||
| from bot import config | |||||
| __author__ = "Ben Kurtovic" | __author__ = "Ben Kurtovic" | ||||
| __copyright__ = "Copyright (C) 2009, 2010, 2011 by Ben Kurtovic" | __copyright__ = "Copyright (C) 2009, 2010, 2011 by Ben Kurtovic" | ||||
| @@ -28,12 +28,12 @@ __license__ = "MIT License" | |||||
| __version__ = "0.1-dev" | __version__ = "0.1-dev" | ||||
| __email__ = "ben.kurtovic@verizon.net" | __email__ = "ben.kurtovic@verizon.net" | ||||
| bot_script = path.join(path.dirname(path.abspath(__file__)), "core", "main.py") | |||||
| bot_script = path.join(path.dirname(path.abspath(__file__)), "bot", "main.py") | |||||
| def main(): | def main(): | ||||
| print "EarwigBot v{0}\n".format(__version__) | print "EarwigBot v{0}\n".format(__version__) | ||||
| is_encrypted = verify_config() | |||||
| is_encrypted = config.verify_config() | |||||
| if is_encrypted: # passwords in the config file are encrypted | if is_encrypted: # passwords in the config file are encrypted | ||||
| key = getpass("Enter key to unencrypt bot passwords: ") | key = getpass("Enter key to unencrypt bot passwords: ") | ||||
| else: | else: | ||||