Fixup and add tests for separating config

This commit is contained in:
Shauna 2015-03-15 10:20:17 -04:00
commit a6bd89512f
5 changed files with 107 additions and 164 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ lib
local
bin
error.log
nicks.csv

73
bot.py
View File

@ -3,17 +3,10 @@
# Import some necessary libraries.
import socket, sys, time, csv, Queue, random, re, pdb, select, os.path
from threading import Thread
import ConfigParser
# Some basic variables used to configure the bot.
server = "irc.freenode.net"
channel = "#openhatch" # Please use #openhatch-bots rather than #openhatch for testing
botnick = "WelcomeBot"
channel_greeters = ['shauna', 'paulproteus', 'marktraceur']
wait_time = 60
hello_list = [r'hello', r'hi', r'hey', r'yo', r'sup']
help_list = [r'help', r'info', r'faq', r'explain yourself']
registered = False # If users don't want to identify, change the value to false.
# To configure bot, please make changes in bot_settings.py
import bot_settings as settings
#########################
### Class Definitions ###
@ -22,7 +15,11 @@ registered = False # If users don't want to identify, change the value to false.
# Defines a bot
class Bot(object):
def __init__(self, nick_source='nicks.csv', wait_time=wait_time):
def __init__(self, botnick=settings.botnick, welcome_message=settings.welcome_message,
nick_source=settings.nick_source, wait_time=settings.wait_time,
hello_list=settings.hello_list, help_list=settings.help_list):
self.botnick = botnick
self.welcome_message = welcome_message
self.nick_source = nick_source
self.wait_time = wait_time
self.known_nicks = []
@ -32,8 +29,8 @@ class Bot(object):
row = clean_nick(row[0]) # Sends nicks to remove unnecessary decorators. Hacked to deal with list-of-string format. :(
self.known_nicks.append([row])
self.newcomers = []
self.hello_regex = re.compile(get_regex(hello_list), re.I) # Regexed version of hello list
self.help_regex = re.compile(get_regex(help_list), re.I) # Regexed version of help list
self.hello_regex = re.compile(get_regex(hello_list, botnick), re.I) # Regexed version of hello list
self.help_regex = re.compile(get_regex(help_list, botnick), re.I) # Regexed version of help list
# Adds the current newcomer's nick to nicks.csv and known_nicks.
def add_known_nick(self,new_known_nick):
@ -63,12 +60,12 @@ class NewComer(object):
# Creates a socket that will be used to send and receive messages,
# then connects the socket to an IRC server and joins the channel.
def irc_start(): # pragma: no cover (this excludes this function from testing)
def irc_start(server): # pragma: no cover (this excludes this function from testing)
ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ircsock.connect((server, 6667)) # Here we connect to server using port 6667.
return ircsock
def join_irc(ircsock):
def join_irc(ircsock, botnick, channel):
ircsock.send("USER {0} {0} {0} :This is http://openhatch.org/'s greeter bot"
".\n".format(botnick)) # bot authentication
ircsock.send("NICK {}\n".format(botnick)) # Assign the nick to the bot.
@ -88,7 +85,7 @@ def msg_handler(ircsock): # pragma: no cover (this excludes this function from
return new_msg
# Called by bot on startup. Builds a regex that matches one of the options + (space) botnick.
def get_regex(options):
def get_regex(options, botnick):
pattern = "("
for s in options:
pattern += s
@ -103,7 +100,7 @@ def get_regex(options):
#########################
# Welcomes the "person" passed to it.
def welcome_nick(newcomer, ircsock):
def welcome_nick(newcomer, ircsock, channel, channel_greeters):
ircsock.send("PRIVMSG {0} :Welcome {1}! The channel is pretty quiet "
"right now, so I thought I'd say hello, and ping some people "
"(like {2}) that you're here. If no one responds for a "
@ -113,11 +110,11 @@ def welcome_nick(newcomer, ircsock):
"\n".format(channel, newcomer, greeter_string(channel_greeters)))
# Checks and manages the status of newcomers.
def process_newcomers(bot, newcomerlist, ircsock, welcome=1):
def process_newcomers(bot, newcomerlist, ircsock, channel, greeters, welcome=1):
for person in newcomerlist:
if welcome == 1:
welcome_nick(person.nick, ircsock)
bot.add_known_nick(person.clean_nick)
welcome_nick(person.nick, ircsock, channel, greeters)
bot.add_known_nick(person.nick)
bot.newcomers.remove(person)
# Checks for messages.
@ -141,19 +138,19 @@ def clean_nick(actor):
return actor
# Parses messages and responds to them appropriately.
def message_response(bot, ircmsg, actor, ircsock):
def message_response(bot, ircmsg, actor, ircsock, channel, greeters):
# if someone other than a newcomer speaks into the channel
if ircmsg.find("PRIVMSG " + channel) != -1 and clean_nick(actor) not in [i.clean_nick for i in bot.newcomers]:
process_newcomers(bot,bot.newcomers, ircsock, welcome=0) # Process/check newcomers without welcoming them
if ircmsg.find("PRIVMSG " + channel) != -1 and actor not in [i.nick for i in bot.newcomers]:
process_newcomers(bot,bot.newcomers, ircsock, channel, greeters, welcome=0) # Process/check newcomers without welcoming them
# if someone (other than the bot) joins the channel
if ircmsg.find("JOIN " + channel) != -1 and actor != botnick:
if [clean_nick(actor)] not in bot.known_nicks + [i.clean_nick for i in bot.newcomers]: # And they're new
if ircmsg.find("JOIN " + channel) != -1 and actor != bot.botnick:
if [actor.replace("_", "")] not in bot.known_nicks + [i.nick for i in bot.newcomers]: # And they're new
NewComer(actor, bot)
# if someone changes their nick while still in newcomers update that nick
if ircmsg.find("NICK :") != -1 and actor != botnick:
if ircmsg.find("NICK :") != -1 and actor != bot.botnick:
for i in bot.newcomers: # if that person was in the newlist
if i.nick == actor:
i.nick = ircmsg.split(":")[2] # update to new nick (and clean up the nick)
@ -166,15 +163,15 @@ def message_response(bot, ircmsg, actor, ircsock):
bot.newcomers.remove(i) # remove them from the list
# If someone talks to (or refers to) the bot.
if botnick.lower() and "PRIVMSG".lower() in ircmsg.lower():
if bot.botnick.lower() and "PRIVMSG".lower() in ircmsg.lower():
if bot.hello_regex.search(ircmsg):
bot_hello(random.choice(hello_list), actor, ircsock)
bot_hello(random.choice(settings.hello_list), actor, ircsock, channel)
if bot.help_regex.search(ircmsg):
bot_help(ircsock)
bot_help(ircsock, channel)
# If someone tries to change the wait time...
if ircmsg.find(botnick + " --wait-time ") != -1:
bot.wait_time = wait_time_change(actor, ircmsg, ircsock) # call this to check and change it
if ircmsg.find(bot.botnick + " --wait-time ") != -1:
bot.wait_time = wait_time_change(actor, ircmsg, ircsock, channel, greeters) # call this to check and change it
# If the server pings us then we've got to respond!
if ircmsg.find("PING :") != -1:
@ -186,11 +183,11 @@ def message_response(bot, ircmsg, actor, ircsock):
#############################################################
# Responds to a user that inputs "Hello Mybot".
def bot_hello(greeting, actor, ircsock):
def bot_hello(greeting, actor, ircsock, channel):
ircsock.send("PRIVMSG {0} :{1} {2}\n".format(channel, greeting, actor))
# Explains what the bot is when queried.
def bot_help(ircsock):
def bot_help(ircsock, channel):
ircsock.send("PRIVMSG {} :I'm a bot! I'm from here <https://github"
".com/shaunagm/oh-irc-bot>. You can change my behavior by "
"submitting a pull request or by talking to shauna"
@ -210,7 +207,7 @@ def greeter_string(greeters):
return greeterstring
# Changes the wait time from the channel.
def wait_time_change(actor, ircmsg, ircsock):
def wait_time_change(actor, ircmsg, ircsock, channel, channel_greeters):
for admin in channel_greeters:
if actor == admin:
finder = re.search(r'\d\d*', re.search(r'--wait-time \d\d*', ircmsg)
@ -232,17 +229,17 @@ def pong(ircsock):
##########################
def main():
ircsock = irc_start()
join_irc(ircsock)
ircsock = irc_start(settings.server)
join_irc(ircsock, settings.botnick, settings.channel)
WelcomeBot = Bot()
while 1: # Loop forever
ready_to_read, b, c = select.select([ircsock],[],[], 1) # b&c are ignored here
process_newcomers(WelcomeBot, [i for i in WelcomeBot.newcomers if i.around_for() > WelcomeBot.wait_time],ircsock)
process_newcomers(WelcomeBot, [i for i in WelcomeBot.newcomers if i.around_for() > WelcomeBot.wait_time], ircsock,settings.channel, settings.channel_greeters)
if ready_to_read:
ircmsg = msg_handler(ircsock) # gets message from ircsock
ircmsg, actor = parse_messages(ircmsg) # parses it or returns None
if ircmsg is not None: # If we were able to parse it
message_response(WelcomeBot, ircmsg, actor, ircsock) # Respond to the parsed message
message_response(WelcomeBot, ircmsg, actor, ircsock, settings.channel, settings.channel_greeters) # Respond to the parsed message
if __name__ == "__main__": # This line tells the interpreter to only execute main() if the program is being run, not imported.
sys.exit(main())

17
bot_settings.py Normal file
View File

@ -0,0 +1,17 @@
# Replace these default settings with your own personal settings
# IRC configuration
channel = "#openhatch-unlogged"
botnick = "parsewelcomebot"
server = "irc.freenode.net"
registered = False
# Bot behavior
wait_time = 60
channel_greeters = ["shauna", "paulproteus", "marktraceur"]
nick_source = "nicks.csv"
# Bot text
hello_list = ["hello", "hi", "hey", "yo", "sup"]
help_list = ["help", "info", "faq", "explain_yourself"]
welcome_message = "Welcome! THe channel is pretty quiet right now, so I thought I'd say hello, and ping some people that you're here. If no one responds for a whle, try emailing us at hello@openhatch.org or just try coming back later. FYI, you're now on my list of known nicknames, so I won't bother you again."

View File

@ -1,75 +0,0 @@
paulproteus
britta
aldeka
ArcTanSusan
aeva
daveeloo2
ehashman
exor674
glyph
GorillaWarfare
gregglind
IShadowed
JasonWoof
jdunck
jeremyb
jesstess
johnmorr
judytuna
keturn
klange
kxra
Kyzz[Mobile]
lfaraone
mandarg
marktraceur
mchua
mindspillage
mlinksva
openhatch_hudson
paroneayea
paulproteus|mobi
paultag
ptone
ptressel
pyrak
pythonian4000
rmo
skay
sunu
ThomasWaldmann
Triskelios
wolftune
Rsouthee
rihnapstor
fijal
kstar
freedeb
aaron
reutest
willingc
romzy
ArcTanSusan
paroneayea
wolftune
reutest
IShadowed
FHaag
Eldanen
exor674
shauna
Aaron1011
FHaag1
carbon
tricksy
zorkian
thunderbolt
daleharvey
tajys
TDJACR
gitcommits
sassyapril
MaraJade
achock
aaparella
abcqfiojqw
1 paulproteus
2 britta
3 aldeka
4 ArcTanSusan
5 aeva
6 daveeloo2
7 ehashman
8 exor674
9 glyph
10 GorillaWarfare
11 gregglind
12 IShadowed
13 JasonWoof
14 jdunck
15 jeremyb
16 jesstess
17 johnmorr
18 judytuna
19 keturn
20 klange
21 kxra
22 Kyzz[Mobile]
23 lfaraone
24 mandarg
25 marktraceur
26 mchua
27 mindspillage
28 mlinksva
29 openhatch_hudson
30 paroneayea
31 paulproteus|mobi
32 paultag
33 ptone
34 ptressel
35 pyrak
36 pythonian4000
37 rmo
38 skay
39 sunu
40 ThomasWaldmann
41 Triskelios
42 wolftune
43 Rsouthee
44 rihnapstor
45 fijal
46 kstar
47 freedeb
48 aaron
49 reutest
50 willingc
51 romzy
52 ArcTanSusan
53 paroneayea
54 wolftune
55 reutest
56 IShadowed
57 FHaag
58 Eldanen
59 exor674
60 shauna
61 Aaron1011
62 FHaag1
63 carbon
64 tricksy
65 zorkian
66 thunderbolt
67 daleharvey
68 tajys
69 TDJACR
70 gitcommits
71 sassyapril
72 MaraJade
73 achock
74 aaparella
75 abcqfiojqw

View File

@ -6,6 +6,9 @@ import bot as botcode
import time
import pdb
# To configure bot, please make changes in bot_settings.py
import bot_settings as settings
#########################
### FAKE IRCSOCK ###
#########################
@ -41,11 +44,11 @@ class TestBotClass(unittest.TestCase):
self.assertEqual(self.bot.nick_source, 'nicks.csv')
def test_known_nicks_setup(self):
bot = botcode.Bot('test_nicks.csv')
bot = botcode.Bot(nick_source='test_nicks.csv')
self.assertEqual(bot.known_nicks, [['Alice'], ['Bob']])
def test_wait_time(self):
self.assertEqual(self.bot.wait_time, botcode.wait_time)
self.assertEqual(self.bot.wait_time, settings.wait_time)
def test_custom_wait_time(self):
bot = botcode.Bot(wait_time=30)
@ -65,7 +68,7 @@ class TestBotClass(unittest.TestCase):
self.assertEqual(self.bot.known_nicks,[['Fluffy'], ['Spot'], ['Roger']])
def test_add_nick_to_csv(self):
bot = botcode.Bot('test_nicks.csv')
bot = botcode.Bot(nick_source='test_nicks.csv')
bot.add_known_nick('Roger__')
with open('test_nicks.csv', 'rb') as csv_file:
known_nicks = []
@ -104,14 +107,14 @@ class TestJoinIRC(unittest.TestCase):
self.bot = botcode.Bot()
def test_sent_messages(self):
botcode.join_irc(self.ircsock)
expected = ["USER {} {} {} :This is http://openhatch.org/'s greeter bot.\n".format(botcode.botnick, botcode.botnick, botcode.botnick), 'NICK {}\n'.format(botcode.botnick), 'JOIN {} \n'.format(botcode.channel)]
botcode.join_irc(self.ircsock, settings.botnick, settings.channel)
expected = ["USER {} {} {} :This is http://openhatch.org/'s greeter bot.\n".format(self.bot.botnick, self.bot.botnick, self.bot.botnick), 'NICK {}\n'.format(self.bot.botnick), 'JOIN {} \n'.format(settings.channel)]
self.assertEqual(self.ircsock.sent_messages,expected)
class TestProcessNewcomers(unittest.TestCase):
def setUp(self):
self.bot = botcode.Bot('test_nicks.csv', wait_time=.1)
self.bot = botcode.Bot(nick_source='test_nicks.csv', wait_time=.1)
botcode.NewComer('Harry', self.bot)
botcode.NewComer('Hermione', self.bot)
time.sleep(.15)
@ -119,16 +122,16 @@ class TestProcessNewcomers(unittest.TestCase):
self.ircsock = fake_irc_start()
def test_check_new_newcomers(self):
botcode.process_newcomers(self.bot, [i for i in self.bot.newcomers if i.around_for() > self.bot.wait_time], ircsock=self.ircsock, welcome=0)
botcode.process_newcomers(self.bot, [i for i in self.bot.newcomers if i.around_for() > self.bot.wait_time], ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters, welcome=0)
self.assertEqual(len(self.bot.newcomers), 1)
def test_check_new_known_nicks(self):
botcode.process_newcomers(self.bot, [i for i in self.bot.newcomers if i.around_for() > self.bot.wait_time], ircsock=self.ircsock, welcome=0)
botcode.process_newcomers(self.bot, [i for i in self.bot.newcomers if i.around_for() > self.bot.wait_time], ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters, welcome=0)
self.assertEqual(self.bot.known_nicks,[['Alice'],['Bob'],['Harry'],['Hermione']])
def test_welcome_nick(self):
botcode.process_newcomers(bot=self.bot, newcomerlist=[i for i in self.bot.newcomers if i.around_for() > self.bot.wait_time], ircsock=self.ircsock, welcome=1)
self.assertEqual(self.ircsock.sent_message(), "PRIVMSG {0} :Welcome Hermione! The channel is pretty quiet right now, so I thought I'd say hello, and ping some people (like {1}) that you're here. If no one responds for a while, try emailing us at hello@openhatch.org or just try coming back later. FYI, you're now on my list of known nicknames, so I won't bother you again.\n".format(botcode.channel,botcode.greeter_string(botcode.channel_greeters)))
botcode.process_newcomers(bot=self.bot, newcomerlist=[i for i in self.bot.newcomers if i.around_for() > self.bot.wait_time], ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters, welcome=1)
self.assertEqual(self.ircsock.sent_message(), "PRIVMSG {0} :Welcome Hermione! The channel is pretty quiet right now, so I thought I'd say hello, and ping some people (like {1}) that you're here. If no one responds for a while, try emailing us at hello@openhatch.org or just try coming back later. FYI, you're now on my list of known nicknames, so I won't bother you again.\n".format(settings.channel,botcode.greeter_string(settings.channel_greeters)))
def tearDown(self):
with open('test_nicks.csv', 'w') as csv_file:
@ -152,49 +155,49 @@ class TestMessageResponse(unittest.TestCase):
self.ircsock = fake_irc_start()
def test_newcomer_speaking(self):
botcode.message_response(self.bot,"~q@r.m.us PRIVMSG {} :hah".format(botcode.channel),"Chappe", ircsock=self.ircsock) # Standard message by newcomer
botcode.message_response(self.bot,"~q@r.m.us PRIVMSG {} :hah".format(settings.channel),"Chappe", ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters) # Standard message by newcomer
nicklist = [i.nick for i in self.bot.newcomers] # Makes a list of newcomers nicks for easy asserting
self.assertEqual(nicklist, ['Chappe'])
def test_oldtimer_speaking(self):
botcode.message_response(self.bot,"~q@r.m.us PRIVMSG {} :hah".format(botcode.channel),"Alice", ircsock=self.ircsock) # Standard message by oldtimer
botcode.message_response(self.bot,"~q@r.m.us PRIVMSG {} :hah".format(settings.channel),"Alice", ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters) # Standard message by oldtimer
nicklist = [i.nick for i in self.bot.newcomers] # Makes a list of newcomers nicks for easy asserting
self.assertEqual(nicklist, [])
def test_join(self):
botcode.message_response(self.bot,"JOIN {} right now!".format(botcode.channel),"Shauna", ircsock=self.ircsock) # Replace with actual ping message
botcode.message_response(self.bot,"JOIN {} right now!".format(settings.channel),"Shauna", ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters) # Replace with actual ping message
self.assertEqual(self.bot.newcomers[1].nick,'Shauna')
def test_part(self):
botcode.message_response(self.bot,"JOIN {} right now!".format(botcode.channel),"Shauna", ircsock=self.ircsock) # Replace with actual ping message
botcode.message_response(self.bot,"JOIN {} right now!".format(settings.channel),"Shauna", ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters) # Replace with actual ping message
self.assertEqual(len(self.bot.newcomers), 2)
botcode.message_response(self.bot,"PART {}".format(botcode.channel),"Shauna", ircsock=self.ircsock) # Replace with actual ping message
botcode.message_response(self.bot,"PART {}".format(settings.channel),"Shauna", ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters) # Replace with actual ping message
self.assertEqual(len(self.bot.newcomers), 1)
def test_hello(self):
botcode.message_response(self.bot,"PRIVMSG sup {}".format(botcode.botnick),"Shauna", ircsock=self.ircsock)
botcode.message_response(self.bot,"PRIVMSG sup {}".format(self.bot.botnick),"Shauna", ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters)
self.assertTrue(self.ircsock.has_sent_message())
self.assertIn(self.ircsock.sent_message(), ["PRIVMSG {} :hello Shauna\n".format(botcode.channel), "PRIVMSG {} :hi Shauna\n".format(botcode.channel), "PRIVMSG {} :hey Shauna\n".format(botcode.channel), "PRIVMSG {} :yo Shauna\n".format(botcode.channel), "PRIVMSG {} :sup Shauna\n".format(botcode.channel)])
self.assertIn(self.ircsock.sent_message(), ["PRIVMSG {} :hello Shauna\n".format(settings.channel), "PRIVMSG {} :hi Shauna\n".format(settings.channel), "PRIVMSG {} :hey Shauna\n".format(settings.channel), "PRIVMSG {} :yo Shauna\n".format(settings.channel), "PRIVMSG {} :sup Shauna\n".format(settings.channel)])
def test_help(self):
botcode.message_response(self.bot,"PRIVMSG info {}".format(botcode.botnick),"Shauna", ircsock=self.ircsock)
botcode.message_response(self.bot,"PRIVMSG info {}".format(self.bot.botnick),"Shauna", ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters)
self.assertTrue(self.ircsock.has_sent_message())
self.assertEqual(self.ircsock.sent_message(), "PRIVMSG {} :I'm a bot! I'm from here <https://github.com/shaunagm/oh-irc-bot>. You can change my behavior by submitting a pull request or by talking to shauna.\n".format(botcode.channel))
self.assertEqual(self.ircsock.sent_message(), "PRIVMSG {} :I'm a bot! I'm from here <https://github.com/shaunagm/oh-irc-bot>. You can change my behavior by submitting a pull request or by talking to shauna.\n".format(settings.channel))
def test_wait_time_from_admin(self):
botcode.message_response(self.bot,"{} --wait-time 40".format(botcode.botnick),"shauna",ircsock=self.ircsock) # Channel-greeters may also be changed. :(
self.assertEqual(self.ircsock.sent_message(), "PRIVMSG {} :shauna the wait time is changing to 40 seconds.\n".format(botcode.channel))
botcode.message_response(self.bot,"{} --wait-time 40".format(self.bot.botnick),"shauna",ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters) # Channel-greeters may also be changed. :(
self.assertEqual(self.ircsock.sent_message(), "PRIVMSG {} :shauna the wait time is changing to 40 seconds.\n".format(settings.channel))
def test_wait_time_from_non_admin(self):
botcode.message_response(self.bot,"{} --wait-time 40".format(botcode.botnick),"Impostor",ircsock=self.ircsock) # Channel-greeters may also be changed. :(
self.assertEqual(self.ircsock.sent_message(), "PRIVMSG {0} :Impostor you are not authorized to make that change. Please contact one of the channel greeters, like {1}, for assistance.\n".format(botcode.channel,botcode.greeter_string(botcode.channel_greeters)))
botcode.message_response(self.bot,"{} --wait-time 40".format(self.bot.botnick),"Impostor",ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters) # Channel-greeters may also be changed. :(
self.assertEqual(self.ircsock.sent_message(), "PRIVMSG {0} :Impostor you are not authorized to make that change. Please contact one of the channel greeters, like {1}, for assistance.\n".format(settings.channel,botcode.greeter_string(settings.channel_greeters)))
def test_pong(self):
botcode.message_response(self.bot,"PING :","Shauna",ircsock=self.ircsock) # Replace this with actual ping message
botcode.message_response(self.bot,"PING :","Shauna",ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters) # Replace this with actual ping message
self.assertEqual(self.ircsock.sent_message(),"PONG :pingis\n")
def test_bad_pong(self):
botcode.message_response(self.bot,"PING!!! :","Shauna",ircsock=self.ircsock) # Replace this with actual ping message
botcode.message_response(self.bot,"PING!!! :","Shauna",ircsock=self.ircsock, channel=settings.channel, greeters=settings.channel_greeters) # Replace this with actual ping message
self.assertFalse(self.ircsock.has_sent_message())
def tearDown(self):