diff --git a/.gitignore b/.gitignore index 0d20b64..3174e35 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +secure_config.py diff --git a/actions.py b/actions.py new file mode 100644 index 0000000..acf4f12 --- /dev/null +++ b/actions.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +import string, re, subprocess +from config import * + +s, send, say, action, notice, join = None, None, None, None, None, None + +def check_triggers(cmds, act, nick, ident, host, chan, msg = None): + global s, send, say, action, notice, join # set commands as globals so we can use them in other functions + s, send, say, action, notice, join = cmds # unpack commands + + if act == "join": + pass + + if act == "msg_private": + pass + + if act == "msg_public": + pass + + if act == "msg": + if msg == "!test": + cmd_test(nick, chan) + if msg.startswith("!git"): + cmd_git(nick, host, chan, msg) + +def get_args(msg): # get command arguments + args = msg.strip().split(' ') # strip out extra whitespace and split the message into a list + while '' in args: # remove any empty arguments + args.remove('') + return args[1:] # remove the command itself + +def cmd_test(nick, chan): # bot test + say(chan, "'sup %s?" % nick) + +def cmd_git(nick, host, chan, msg): # commands to interface with the bot's git repository + if host not in ADMINS: + say(chan, "%s: you must be a bot admin to use this command." % nick) + return + + args = get_args(msg) + if not args: + say(chan, "%s: no arguments provided." % nick) + return + + if args[0] == "help": # display all commands + cmds = ["branch (show current branch)", "branches (show all branches)", "checkout (switch branches)", "pull (update current branch)"] + cmds = ', '.join(cmds) + say(chan, "%s: sub-commands are: %s" % (nick, cmds)) + + elif args[0] == "branch": # get our current branch + branch = subprocess.check_output(['git', 'name-rev', '--name-only', 'HEAD'], stderr=subprocess.STDOUT) # git name-rev --name-only HEAD + branch = branch[:-1] # strip newline + say(chan, "%s: currently on branch '%s'." % (nick, branch)) + + elif args[0] == "branches": # get list of branches + branches = subprocess.check_output(['git', 'branch'], stderr=subprocess.STDOUT) # git branch + branches = branches[:-1] # strip newline + branches = branches.replace('\n* ', ', ') # cleanup extraneous characters + branches = branches.replace('* ', ' ') + branches = branches.replace('\n ', ', ') + branches = branches.strip() + say(chan, "%s: branches: %s." % (nick, branches)) + + elif args[0] == "checkout": # switch branches + try: + branch = args[1] + except IndexError: # no branch name provided + say(chan, "%s: switch to which branch?" % nick) + return + try: + result = subprocess.check_output(['git', 'checkout', branch], stderr=subprocess.STDOUT) # git checkout our_branch + result = result[:-1] # strip newline + result = string.lower(result[0] + result[1:]) # lowercase first word + say(chan, "%s: %s." % (nick, result)) + except subprocess.CalledProcessError: # git couldn't switch branches + say(chan, "%s: branch '%s' does not exist!" % (nick, branch)) + + elif args[0] == "pull": # pull from remote repository + branch = subprocess.check_output(['git', 'name-rev', '--name-only', 'HEAD'], stderr=subprocess.STDOUT) # git name-rev --name-only HEAD + branch = branch[:-1] # strip newline + say(chan, "%s: pulling branch '%s' from remote..." % (nick, branch)) + result = subprocess.check_output(['git', 'pull', 'origin', branch]) # pull from remote + if "Already up-to-date." in result: + say(chan, "%s: done; no new changes." % nick) + else: + say(chan, "%s: done; new changes merged." % nick) + + else: + say(chan, "%s: unknown argument: '%s'." % (nick, arg[0])) diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..c8f3f19 --- /dev/null +++ b/bot.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +## Imports +import socket, string, re +from actions import * +from config import * +from secure_config import * + +def send(msg): # send a message 'msg' to the server + s.send(msg + "\r\n") + print " %s" % msg + +def say(target, msg): # send a private message 'msg' to 'target' + send("PRIVMSG %s :%s" % (target, msg)) + +def action(target, msg): # send a message as an action + say(target,"%sACTION %s%s" % (chr(1), msg, chr(1))) + +def notice(target, msg): # send a notice 'msg' to 'target' + send("NOTICE %s :%s" % (target, msg)) + +def join(chan): # join channel 'chan' + send("JOIN %s" % chan) + +cmds = (s, send, say, action, notice, join) # pack up commands + +def main(): + readbuffer = str() + data = [s, send, say, notice, join] + while 1: + readbuffer = readbuffer + s.recv(1024) + temp = string.split(readbuffer, "\n") + readbuffer = temp.pop() + for line in temp: + line2 = string.split(string.rstrip(line)) + + if line2[1] == "JOIN": + nick, ident, host = re.findall(":(.*?)!(.*?)@(.*?)\Z", line2[0])[0] + chan = line2[2][1:] + + check_triggers(cmds, "join", nick, ident, host, chan) # check if there's anything we can respond to, and if so, respond + + if line2[1] == "PRIVMSG": + nick, ident, host = re.findall(":(.*?)!(.*?)@(.*?)\Z", line2[0])[0] + msg = ' '.join(line2[3:])[1:] + chan = line2[2] + + if chan == NICK: # if line2[2] is us, this is a privmsg to us, so set 'chan' as the nick of the sender + chan = nick + check_triggers(cmds, "msg_private", nick, ident, host, chan, msg) # only respond if it's a private message + else: + check_triggers(cmds, "msg_public", nick, ident, host, chan, msg) # only respond if it's a public (channel) message + + check_triggers(cmds, "msg", nick, ident, host, chan, msg) # check for general messages + + if msg == "!restart": # hardcode the !restart command (we can't return from within actions.py) + if host in ADMINS: + return True + + if line2[0] == "PING": # If we are pinged, pong back to the server + send("PONG %s" % line2[1]) + + if line2[1] == "376": + if NS_AUTH: + say("NickServ", "IDENTIFY %s %s" % (NS_USER, NS_PASS)) + join(CHAN) + +if __name__ == "__main__": + s = socket.socket() + s.connect((HOST, PORT)) + send("NICK %s" % NICK) + send("USER %s %s bla :%s" % (IDENT, HOST, REALNAME)) + main() diff --git a/config.py b/config.py new file mode 100644 index 0000000..e06f4ce --- /dev/null +++ b/config.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +# our server's hostname +HOST = "irc.freenode.net" + +# our server's port +PORT = 6667 + +# our nick +NICK = "EarwigBot|2" + +# our ident +IDENT = "earwigbot" + +# our real name +REALNAME = "[[w:en:User:EarwigBot]]" + +# channel to join on startup +CHAN = "##earwigbot" + +# sleep this number of seconds in between API calls and messages sent to IRC +THROTTLE = 1 + +# hostnames of users who can update/restart the bot with !update +ADMINS = ["wikipedia/The-Earwig"] \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..f9605c4 --- /dev/null +++ b/main.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +from subprocess import * + +while 1: + cmd = ['python', 'bot.py'] + call(cmd) \ No newline at end of file