| @@ -1,92 +0,0 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| import string, re, subprocess | |||||
| from config.irc_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, "Hey \x02%s\x0F!" % nick) | |||||
| def cmd_git(nick, host, chan, msg): # commands to interface with the bot's git repository | |||||
| if host not in ADMINS: | |||||
| say(chan, "\x02%s\x0F: you must be a bot admin to use this command." % nick) | |||||
| return | |||||
| args = get_args(msg) | |||||
| if not args: | |||||
| say(chan, "\x02%s\x0F: no arguments provided." % nick) | |||||
| return | |||||
| if args[0] == "help": # display all commands | |||||
| cmds = ["\x0303branch\x0301 (show current branch)", "\x0303branches\x0301 (show all branches)", | |||||
| "\x0303checkout\x0301 (switch branches)", "\x0303pull\x0301 (update current branch)"] | |||||
| cmds = ', '.join(cmds) | |||||
| say(chan, "\x02%s\x0F: 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, "\x02%s\x0F: currently on branch \x0302%s\x0301." % (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, "\x02%s\x0F: branches: \x0302%s\x0301." % (nick, branches)) | |||||
| elif args[0] == "checkout": # switch branches | |||||
| try: | |||||
| branch = args[1] | |||||
| except IndexError: # no branch name provided | |||||
| say(chan, "\x02%s\x0F: switch to which branch?" % nick) | |||||
| return | |||||
| try: | |||||
| result = subprocess.check_output(['git', 'checkout', branch], stderr=subprocess.STDOUT) # git checkout our_branch | |||||
| if "Already on" in result: | |||||
| say(chan, "\x02%s\x0F: already on \x0302%s\x0301!" % (nick, branch)) | |||||
| else: | |||||
| say(chan, "\x02%s\x0F: switched to branch \x0302%s\x0301." % (nick, branch)) | |||||
| except subprocess.CalledProcessError: # git couldn't switch branches | |||||
| say(chan, "\x02%s\x0F: branch \x0302%s\x0301 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, "\x02%s\x0F: pulling from remote (currently on \x0302%s\x0301)..." % (nick, branch)) | |||||
| result = subprocess.check_output(['git', 'pull']) # pull from remote | |||||
| if "Already up-to-date." in result: | |||||
| say(chan, "\x02%s\x0F: done; no new changes." % nick) | |||||
| else: | |||||
| say(chan, "\x02%s\x0F: done; new changes merged." % nick) | |||||
| else: | |||||
| say(chan, "\x02%s\x0F: unknown argument: \x0303%s\x0301." % (nick, args[0])) | |||||
| @@ -2,73 +2,62 @@ | |||||
| ## Imports | ## Imports | ||||
| import socket, string, re | import socket, string, re | ||||
| from actions import * | |||||
| from config.irc_config import * | from config.irc_config import * | ||||
| from config.secure_config import * | from config.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 | |||||
| from irc import triggers | |||||
| from irc.actions import * | |||||
| from irc.data import * | |||||
| def main(): | def main(): | ||||
| readbuffer = str() | |||||
| data = [s, send, say, notice, join] | |||||
| read_buffer = str() | |||||
| while 1: | while 1: | ||||
| readbuffer = readbuffer + s.recv(1024) | |||||
| temp = string.split(readbuffer, "\n") | |||||
| readbuffer = temp.pop() | |||||
| read_buffer = read_buffer + actions.sock.recv(1024) | |||||
| temp = string.split(read_buffer, "\n") | |||||
| read_buffer = temp.pop() | |||||
| for line in temp: | for line in temp: | ||||
| line2 = string.split(string.rstrip(line)) | |||||
| line = string.split(string.rstrip(line)) | |||||
| data = Data() | |||||
| if line2[1] == "JOIN": | |||||
| nick, ident, host = re.findall(":(.*?)!(.*?)@(.*?)\Z", line2[0])[0] | |||||
| chan = line2[2][1:] | |||||
| if line[1] == "JOIN": | |||||
| data.nick, data.ident, data.host = re.findall(":(.*?)!(.*?)@(.*?)\Z", line[0])[0] | |||||
| data.chan = line[2][1:] | |||||
| check_triggers(cmds, "join", nick, ident, host, chan) # check if there's anything we can respond to, and if so, respond | |||||
| triggers.check(actions, data, "join") # 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 line[1] == "PRIVMSG": | |||||
| data.nick, data.ident, data.host = re.findall(":(.*?)!(.*?)@(.*?)\Z", line[0])[0] | |||||
| data.msg = ' '.join(line[3:])[1:] | |||||
| data.chan = line[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 | |||||
| if data.chan == NICK: # this is a privmsg to us, so set 'chan' as the nick of the sender | |||||
| data.chan = data.nick | |||||
| triggers.check(actions, data, "msg_private") # only respond if it's a private message | |||||
| else: | else: | ||||
| check_triggers(cmds, "msg_public", nick, ident, host, chan, msg) # only respond if it's a public (channel) message | |||||
| triggers.check(actions, data, "msg_public") # only respond if it's a public (channel) message | |||||
| check_triggers(cmds, "msg", nick, ident, host, chan, msg) # check for general messages | |||||
| triggers.check(actions, data, "msg") # check for general messages | |||||
| if msg == "!restart": # hardcode the !restart command (we can't return from within actions.py) | |||||
| if host in ADMINS: | |||||
| if data.msg == "!restart": # hardcode the !restart command (we can't return from within actions.py) | |||||
| if data.host in ADMINS: | |||||
| return True | return True | ||||
| if line2[0] == "PING": # If we are pinged, pong back to the server | |||||
| send("PONG %s" % line2[1]) | |||||
| if line[0] == "PING": # If we are pinged, pong back to the server | |||||
| actions.send("PONG %s" % line[1]) | |||||
| if line2[1] == "376": | |||||
| if NS_AUTH: | |||||
| say("NickServ", "IDENTIFY %s %s" % (NS_USER, NS_PASS)) | |||||
| for this_chan in CHANS: # join all of our startup channels | |||||
| join(this_chan) | |||||
| if line[1] == "376": | |||||
| if NS_AUTH: # if we're supposed to auth to nickserv, do that | |||||
| actions.say("NickServ", "IDENTIFY %s %s" % (NS_USER, NS_PASS)) | |||||
| for chan in CHANS: # join all of our startup channels | |||||
| actions.join(chan) | |||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||
| s = socket.socket() | |||||
| s.connect((HOST, PORT)) | |||||
| send("NICK %s" % NICK) | |||||
| send("USER %s %s bla :%s" % (IDENT, HOST, REALNAME)) | |||||
| sock = socket.socket() | |||||
| sock.connect((HOST, PORT)) | |||||
| actions = Actions(sock) | |||||
| actions.send("NICK %s" % NICK) | |||||
| actions.send("USER %s %s bla :%s" % (IDENT, HOST, REALNAME)) | |||||
| main() | main() | ||||
| @@ -0,0 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # Actions/commands to interface with IRC. | |||||
| class Actions: | |||||
| def __init__(self, sock): | |||||
| """actions/commands to interface with IRC""" | |||||
| self.sock = sock | |||||
| def send(self, msg): | |||||
| """send data to the server""" | |||||
| self.sock.send(msg + "\r\n") | |||||
| print " %s" % msg | |||||
| def say(self, target, msg): | |||||
| """send a message""" | |||||
| self.send("PRIVMSG %s :%s" % (target, msg)) | |||||
| def action(self, target, msg): | |||||
| """send a message as an action""" | |||||
| self.say(target,"%sACTION %s%s" % (chr(1), msg, chr(1))) | |||||
| def notice(self, target, msg): | |||||
| """send a notice""" | |||||
| self.send("NOTICE %s :%s" % (target, msg)) | |||||
| def join(self, chan): | |||||
| """join a channel""" | |||||
| self.send("JOIN %s" % chan) | |||||
| @@ -0,0 +1,12 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # A class to store data from an individual line received on IRC. | |||||
| class Data: | |||||
| def __init__(self): | |||||
| """store data from an individual line received on IRC""" | |||||
| self.chan = None | |||||
| self.nick = None | |||||
| self.ident = None | |||||
| self.host = None | |||||
| self.msg = None | |||||
| @@ -0,0 +1,127 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # Commands to interface with the bot's git repository. | |||||
| import shlex, subprocess | |||||
| from config.irc_config import * | |||||
| actions, data = None, None | |||||
| args = None | |||||
| def call(a, d): | |||||
| global actions, data | |||||
| actions, data = a, d | |||||
| if not check_user_is_admin(): | |||||
| return | |||||
| args = get_args() | |||||
| if not check_has_args(): | |||||
| return | |||||
| if args[0] == "help": | |||||
| do_help() | |||||
| elif args[0] == "branch": | |||||
| do_branch() | |||||
| elif args[0] == "branches": | |||||
| do_branches() | |||||
| elif args[0] == "checkout": | |||||
| do_checkout() | |||||
| elif args[0] == "pull": | |||||
| do_pull() | |||||
| else: | |||||
| unknown_arg() # they asked us to do something we don't know | |||||
| def get_args(): | |||||
| """get command arguments""" | |||||
| args = data.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 check_user_is_admin(): | |||||
| """check if the user is a bot admin (and can use this command, as a result)""" | |||||
| if data.host not in ADMINS: | |||||
| actions.say(data.chan, "\x02%s\x0F: you must be a bot admin to use this command." % data.nick) | |||||
| return False | |||||
| return True | |||||
| def check_has_args(): | |||||
| """check if they provided arguments along with the !git command""" | |||||
| if not args: | |||||
| actions.say(data.chan, "\x02%s\x0F: no arguments provided." % data.nick) | |||||
| return False | |||||
| return True | |||||
| def exec_shell(command): | |||||
| """execute a shell command and get the output""" | |||||
| command = shlex.split(command) | |||||
| result = subprocess.check_output(command, stderr=subprocess.STDOUT) | |||||
| return result | |||||
| def do_help(): | |||||
| """display all commands""" | |||||
| help = ["\x0303branch\x0301 (show current branch)", "\x0303branches\x0301 (show all branches)", | |||||
| "\x0303checkout\x0301 (switch branches)", "\x0303pull\x0301 (update current branch)"] | |||||
| help = ', '.join(help) | |||||
| actions.say(data.chan, "\x02%s\x0F: sub-commands are: %s" % (data.nick, help)) | |||||
| def do_branch(): | |||||
| """get our current branch""" | |||||
| branch = exec_shell("git name-rev --name-only HEAD") | |||||
| branch = branch[:-1] # strip newline | |||||
| actions.say(data.chan, "\x02%s\x0F: currently on branch \x0302%s\x0301." % (data.nick, branch)) | |||||
| def do_branches(): | |||||
| """get list of branches""" | |||||
| branches = exec_shell("git branch") | |||||
| branches = branches[:-1] # strip newline | |||||
| branches = branches.replace('\n* ', ', ') # cleanup extraneous characters | |||||
| branches = branches.replace('* ', ' ') | |||||
| branches = branches.replace('\n ', ', ') | |||||
| branches = branches.strip() | |||||
| actions.say(data.chan, "\x02%s\x0F: branches: \x0302%s\x0301." % (data.nick, branches)) | |||||
| def do_checkout(): | |||||
| """switch branches""" | |||||
| try: | |||||
| branch = args[1] | |||||
| except IndexError: # no branch name provided | |||||
| actions.say(chan, "\x02%s\x0F: switch to which branch?" % data.nick) | |||||
| return | |||||
| try: | |||||
| result = exec_shell("git checkout %s" % branch) | |||||
| if "Already on" in result: | |||||
| actions.say(data.chan, "\x02%s\x0F: already on \x0302%s\x0301!" % (data.nick, branch)) | |||||
| else: | |||||
| actions.say(data.chan, "\x02%s\x0F: switched to branch \x0302%s\x0301." % (data.nick, branch)) | |||||
| except subprocess.CalledProcessError: # git couldn't switch branches | |||||
| actions.say(data.chan, "\x02%s\x0F: branch \x0302%s\x0301 does not exist!" % (data.nick, branch)) | |||||
| def do_pull(): | |||||
| """pull from remote repository""" | |||||
| branch = exec_shell("git name-rev --name-only HEAD") | |||||
| branch = branch[:-1] # strip newline | |||||
| actions.say(data.chan, "\x02%s\x0F: pulling from remote (currently on \x0302%s\x0301)..." % (data.nick, branch)) | |||||
| result = exec_shell("git pull") | |||||
| if "Already up-to-date." in result: | |||||
| actions.say(data.chan, "\x02%s\x0F: done; no new changes." % data.nick) | |||||
| else: | |||||
| actions.say(data.chan, "\x02%s\x0F: done; new changes merged." % data.nick) | |||||
| def unknown_arg(): | |||||
| actions.say(data.chan, "\x02%s\x0F: unknown argument: \x0303%s\x0301." % (data.nick, args[0])) | |||||
| @@ -0,0 +1,20 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # A simple command to test the bot. | |||||
| import random | |||||
| actions, data = None, None | |||||
| def call(a, d): | |||||
| global actions, data | |||||
| actions, data = a, d | |||||
| choices = ("say_hi()", "say_sup()") | |||||
| exec random.choice(choices) | |||||
| def say_hi(): | |||||
| actions.say(data.chan, "Hey \x02%s\x0F!" % data.nick) | |||||
| def say_sup(): | |||||
| actions.say(data.chan, "'sup \x02%s\x0F?" % data.nick) | |||||
| @@ -0,0 +1,21 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # Check what events on IRC we can respond to. | |||||
| from irc.commands import test, git | |||||
| def check(actions, data, hook): | |||||
| if hook == "join": | |||||
| pass | |||||
| if hook == "msg_private": | |||||
| pass | |||||
| if hook == "msg_public": | |||||
| pass | |||||
| if hook == "msg": | |||||
| if data.msg == "!test": | |||||
| test.call(actions, data) | |||||
| if data.msg.startswith("!git"): | |||||
| git.call(actions, data) | |||||