Browse Source

massive cleanup and moving a lot of functions to their own files; creating two new classes to store irc msg data and irc actions

tags/v0.1
Ben Kurtovic 13 years ago
parent
commit
0ae73a4aef
9 changed files with 248 additions and 142 deletions
  1. +0
    -92
      actions.py
  2. +39
    -50
      bot.py
  3. +29
    -0
      irc/Actions.py
  4. +12
    -0
      irc/Data.py
  5. +0
    -0
     
  6. +0
    -0
     
  7. +127
    -0
      irc/commands/git.py
  8. +20
    -0
      irc/commands/test.py
  9. +21
    -0
      irc/triggers.py

+ 0
- 92
actions.py View File

@@ -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]))

+ 39
- 50
bot.py View File

@@ -2,73 +2,62 @@

## Imports
import socket, string, re
from actions import *
from config.irc_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():
readbuffer = str()
data = [s, send, say, notice, join]
read_buffer = str()

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:
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:
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

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__":
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()

+ 29
- 0
irc/Actions.py View File

@@ -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)

+ 12
- 0
irc/Data.py View File

@@ -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
View File


+ 0
- 0
View File


+ 127
- 0
irc/commands/git.py View File

@@ -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]))

+ 20
- 0
irc/commands/test.py View File

@@ -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)

+ 21
- 0
irc/triggers.py View File

@@ -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)

Loading…
Cancel
Save