Merge pull request #15 from noobur/develop

Fix for Issue #14 and PEP8 changes.
This commit is contained in:
shaunagm 2014-05-06 14:37:29 -04:00
commit f4671db191
2 changed files with 175 additions and 88 deletions

View File

@ -12,7 +12,7 @@ staying in touch
<code>bot.py</code> is the project's main file. It uses the socket module to communicate and gathers a list of known IRC nicknames, stored in <code>nicks.csv</code>. Its basic functions include:
1. If someone enters the channel, the bot checks to see if they are a known nick. If not, it adds them to a list of people to greet. If no one else has spoken into the channel after 60 seconds, it greets them. If someone else (not the new nick) speaks into the channel within 60 seconds, or if the nick is known, the bot remains silent. The bots response text includes the nicks of channel maintainers/frequent contributors so that they are pinged.
1. If someone enters the channel, the bot checks to see if they are a known nick. If not, it adds them to a list of people to greet. If no one else has spoken into the channel after a period of time, 60 seconds by default, it greets them. If someone else (not the new nick) speaks into the channel within the set wait time, or if the nick is known, the bot remains silent. The bots response text includes the nicks of channel maintainers/frequent contributors so that they are pinged. Channel maintainers can change the wait time of the bot by using the following command in the irc channel: *Botname* --wait-time *new wait time in seconds*.
2. If someone says hello to the bot, the bot says hello back.
3. If someone asks the bot for information (via key phrases like "help", "faq", etc) the bot explains what it is and links to this repository.

261
bot.py
View File

@ -1,146 +1,233 @@
#########################################################################
################# Abundant notes are appreciated here! ##################
#########################################################################
# Import some necessary libraries.
import socket
import time
import csv
import string
import Queue
import random
from threading import Thread
from re import search
# Some basic variables used to configure the bot
server = "irc.freenode.net"
channel = "#openhatch"
botnick = "WelcomeBot"
waitTime = 60 # Amount of time after joining before bot replies to someone
# Some basic variables used to configure the bot.
server = "irc.freenode.net"
channel = "#openhatch"
botnick = 'WelcomeBot'
channel_greeters = ['shauna', 'paulproteus', 'marktraceur']
wait_time = 60 # amount of time after joining before bot replies to someone
change_wait = botnick + " --wait-time "
# Classes
class newcomer(object): # Newcomer class created when someone joins the room
#################### Classes ####################
# Newcomer class that is created when someone joins the room.
class NewComer(object):
def __init__(self, nick):
self.nick = nick
self.born = time.time()
self.status = 0
def updateStatus(self):
def update_status(self):
self.status = 1
def aroundFor(self):
def around_for(self):
return int(time.time() - self.born)
# Functions!
def joinchan(chan): # Joins channels
ircsock.send("JOIN " + chan + "\n")
def getIRC(): # Creates separate thread for reading messages from the server
#################### Functions! ####################
# Joins specified channel.
def join_channel(chan):
ircsock.send("JOIN {} \n".format(chan))
# Reads the messages from the server and adds them to the Queue and prints
# them to the console. This function will be run in a thread, see below.
def msg_handler():
while True:
ircmsg = ircsock.recv(2048) # receive data from the server
ircmsg = ircmsg.strip('\n\r') # removing any unnecessary linebreaks.
q.put(ircmsg) # Put in queue for main loop to read
print(ircmsg)
new_msg = ircsock.recv(2048) # receive data from the server
new_msg = new_msg.strip('\n\r') # removing any unnecessary linebreaks
q.put(new_msg) # put in queue for main loop to read
print(new_msg)
def ping(): # Responds to server Pings.
# Responds to server Pings.
def pong():
ircsock.send("PONG :pingis\n")
def hello(actor, greeting, chan=channel): # This function responds to a user that inputs "Hello Mybot"
ircsock.send("PRIVMSG " + chan + " :" + greeting + " " + actor + "\n")
def help(actor, chan=channel): # This function explains what the bot is when queried.
ircsock.send("PRIVMSG " + chan + " :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")
# This function responds to a user that inputs "Hello Mybot".
def bot_hello(greeting):
ircsock.send("PRIVMSG {0} :{1} {2}\n".format(channel, greeting, actor))
def welcome(newcomer): # This welcomes a specific person.
ircsock.send("PRIVMSG " + channel + " :Welcome " + newcomer + "! The channel's pretty quiet right now, so I thought I'd say hello, and ping some people (like shauna, paulproteus, marktraceur) that you're here. If no one responds for a while, try emailing us at hello@openhatch.org or just coming back later. FYI, you're now on my list of known nicknames, so I won't bother you again.\n")
def makeNickArray(): # On startup, makes array of nicks from Nicks.txt. New info will be written to both array and txt file.
nickArray = []
with open('nicks.csv', 'rb') as csvfile:
nicksData = csv.reader(csvfile, delimiter=',', quotechar='|')
for row in nicksData:
nickArray.append(row)
return nickArray
# This function explains what the bot is when queried.
def bot_help():
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"
".\n".format(channel))
def addPerson(person): # Adds newcomer to list of known nicks
person = person.replace("_","")
nickArray.append([person])
# Returns a grammatically correct string of the channel_greeters.
def greeter_string(conjunction):
greeters = ""
if len(channel_greeters) > 2:
for name in channel_greeters[:-1]:
greeters += "{}, ".format(name)
greeters += "{0} {1}".format(conjunction, channel_greeters[-1])
elif len(channel_greeters) == 2:
greeters = "{0} {1} {2}".format(channel_greeters[0], conjunction,
channel_greeters[1])
else:
greeters = channel_greeters[0]
return greeters
# This welcomes the "person" passed to it.
def welcome(newcomer):
ircsock.send("PRIVMSG {0} :Welcome {1}! The channel is pretty quiet "
"right now, so I though I'd say hello, and ping some people "
",like {2}, 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(channel, newcomer, greeter_string("and")))
# Adds the current NewComer's nick to nicks.csv and known_nicks.
def add_known_nick(new_known_nick):
new_known_nick = new_known_nick.replace("_", "")
known_nicks.append([new_known_nick])
with open('nicks.csv', 'a') as csvfile:
nickwriter = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL)
nickwriter.writerow([person])
nickwriter = csv.writer(csvfile, delimiter=',', quotechar='|',
quoting=csv.QUOTE_MINIMAL)
nickwriter.writerow([new_known_nick])
def getWelcomeRegEx(stringArray):
# "I NEED NOTES!!!!", said the function.
def get_welcome_regex(string_array):
#make regex case-insenstive
pattern = r'(?i)'
for s in stringArray:
for s in string_array:
pattern += r'(?:[ :]'+s+r'(?:[ \.!\?,\)]|$))|'
#delete trailing '|'
pattern = pattern[:-1]
return pattern
# Startup
ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ircsock.connect((server, 6667)) # Here we connect to the server using the port 6667
ircsock.send("USER " + botnick + " " + botnick + " " + botnick + " :This is http://openhatch.org/'s greeter bot.\n") # user authentication
ircsock.send("NICK " + botnick + "\n") # here we actually assign the nick to the bot
joinchan(channel)
# Starts a separate thread to get messages from server
q = Queue.LifoQueue()
t = Thread(target=getIRC) # calls getIRC() (defined above) in a separate thread
# This function is used to change the wait time from the channel.
# It confirms that the attempt is allowed and then returns the requested value.
# If the attempt is not allowed, a message is sent to help
def wait_time_change():
for admin in channel_greeters:
if actor == admin:
finder = search(r'\d\d*', search(r'--wait-time \d\d*', ircmsg)
.group())
ircsock.send("PRIVMSG {0} :{1} the wait time is changing to {2} "
"seconds.\n".format(channel, actor, finder.group()))
return int(finder.group())
ircsock.send("PRIVMSG {0} :{1} you are not authorized to make that "
"change. Please contact one of the channel greeters, like {2}, for "
"assistance.\n".format(channel, actor, greeter_string("or")))
#################### Startup ####################
# Creates a socket that will be used to send and receive messages,
# then connects the socket to an IRC server and joins the channel.
ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ircsock.connect((server, 6667)) # Here we connect to server using port 6667.
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.
join_channel(channel)
# Creates a list of known nicks from nicks.csv.
# The list is checked to ensure that people are only greeted once.
# NewComers will be added to the list and the csv.
known_nicks = []
with open('nicks.csv', 'rb') as csv_file:
csv_file_data = csv.reader(csv_file, delimiter=',', quotechar='|')
for row in csv_file_data:
known_nicks.append(row)
# Creates a Queue that will hold the incoming messages.
q = Queue.Queue()
# Creates a separate thread that will listen on the server and add any
# messages to the Queue. The thread is created to run target which equals
# the msg_handler function from above.
t = Thread(target=msg_handler)
t.daemon = True
t.start()
nickArray = makeNickArray()
# This is the array of NewComer objects that people who join are added to.
newcomers = []
newList = [] # This is the array of newcomer objects that people who join the room are added to.
helloArray = [r'hello',r'hi',r'hey',r'yo',r'sup']
helpArray = [r'help',r'info',r'faq',r'explain yourself']
helloPattern = getWelcomeRegEx(helloArray)
helpPattern = getWelcomeRegEx(helpArray)
hello_list = [r'hello', r'hi', r'hey', r'yo', r'sup']
help_list = [r'help', r'info', r'faq', r'explain yourself']
hello_pattern = get_welcome_regex(hello_list)
help_pattern = get_welcome_regex(help_list)
while 1: # Loop forever
for i in newList:
if i.status == 0 and i.aroundFor() > waitTime:
#################### The Workhorse ####################
# This is the main loop that monitors the channel and sends and receives the
# messages, either in the below code or by calling a function. This is the
# brain of this code.
while 1: # loop forever
for i in newcomers:
if i.status == 0 and i.around_for() > wait_time:
welcome(i.nick)
i.updateStatus()
addPerson(i.nick)
newList.remove(i)
i.update_status()
add_known_nick(i.nick)
newcomers.remove(i)
# If the queue is not empty...
if q.empty() == 0:
# get the next msg in the queue
ircmsg = q.get()
# and get the nick of the msg sender
actor = ircmsg.split(":")[1].split("!")[0]
# Welcome functions
if ircmsg.find("PRIVMSG " + channel) != -1: # If someone has spoken into the channel
for i in newList:
if actor != i.nick: # Don't turn off response if the person speaking is the person who joined.
i.updateStatus() # Sets status to 1
addPerson(i.nick)
newList.remove(i)
## Else: Do we want to do something extra if the person who joined the chat says something with no response?
##### Welcome functions #####
# If someone has spoken into the channel...
if ircmsg.find("PRIVMSG " + channel) != -1:
for i in newcomers:
if actor != i.nick: # and is not the new NewComer
i.update_status() # set the status to 1
add_known_nick(i.nick)
newcomers.remove(i)
## Else: Do we want to do something extra if the person who
# joined the chat says something with no response?
if ircmsg.find("JOIN " + channel) != -1: # If someone joins #channel
if actor != botnick: # Remove the case where the bot gets a message that the bot has joined.
if [actor.replace("_","")] not in nickArray:
if actor not in (i.nick for i in newList):
newList.append(newcomer(actor))
# If someone joins #channel...
if ircmsg.find("JOIN " + channel) != -1:
if actor != botnick: # and it is not the bot
if [actor.replace("_", "")] not in known_nicks:
if actor not in (i.nick for i in newcomers):
newcomers.append(NewComer(actor))
if ircmsg.find("PART " + channel) != -1 or ircmsg.find("QUIT") != -1: # If someone parts or quits the #channel
for i in newList: # And that person is on the newlist (has entered channel within last 60 seconds)
# If someone parts or quits the #channel...
if ircmsg.find("PART " + channel) != -1 or ircmsg.find("QUIT") != -1:
for i in newcomers: # and that person is on the newlist
if actor == i.nick:
newList.remove(i) # Remove them from the list
newcomers.remove(i) # remove them from the list
# Unwelcome functions
if ircmsg.find(botnick) != -1 and ircmsg.find("PRIVMSG") != -1: # If someone talks to (or refers to) the bot
chan = channel
matchHello = search(helloPattern,ircmsg)
matchHelp = search(helpPattern,ircmsg)
if ircmsg.find("PRIVMSG " + botnick) != -1:
chan = actor
##### Unwelcome functions #####
# If someone talks to (or refers to) the bot.
if ircmsg.find(botnick) != -1 and ircmsg.find("PRIVMSG") != -1:
matchHello = search(hello_pattern, ircmsg)
matchHelp = search(help_pattern, ircmsg)
if matchHello:
hello(actor,random.choice(helloArray), chan)
bot_hello(random.choice(hello_list))
if matchHelp:
help(actor, chan)
bot_help()
if ircmsg.find("PING :") != -1: # if the server pings us then we've got to respond!
ping()
# If someone tries to change the wait time...
if ircmsg.find(change_wait) != -1:
wait_time = wait_time_change() # call this to check and change it
# If the server pings us then we've got to respond!
if ircmsg.find("PING :") != -1:
pong()