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: <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. 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. 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 some necessary libraries.
import socket import socket
import time import time
import csv import csv
import string
import Queue import Queue
import random import random
from threading import Thread from threading import Thread
from re import search from re import search
# Some basic variables used to configure the bot # Some basic variables used to configure the bot.
server = "irc.freenode.net" server = "irc.freenode.net"
channel = "#openhatch" channel = "#openhatch"
botnick = "WelcomeBot" botnick = 'WelcomeBot'
waitTime = 60 # Amount of time after joining before bot replies to someone 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): def __init__(self, nick):
self.nick = nick self.nick = nick
self.born = time.time() self.born = time.time()
self.status = 0 self.status = 0
def updateStatus(self): def update_status(self):
self.status = 1 self.status = 1
def aroundFor(self): def around_for(self):
return int(time.time() - self.born) 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: while True:
ircmsg = ircsock.recv(2048) # receive data from the server new_msg = ircsock.recv(2048) # receive data from the server
ircmsg = ircmsg.strip('\n\r') # removing any unnecessary linebreaks. new_msg = new_msg.strip('\n\r') # removing any unnecessary linebreaks
q.put(ircmsg) # Put in queue for main loop to read q.put(new_msg) # put in queue for main loop to read
print(ircmsg) print(new_msg)
def ping(): # Responds to server Pings.
# Responds to server Pings.
def pong():
ircsock.send("PONG :pingis\n") 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. # This function responds to a user that inputs "Hello Mybot".
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") 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. # This function explains what the bot is when queried.
nickArray = [] def bot_help():
with open('nicks.csv', 'rb') as csvfile: ircsock.send("PRIVMSG {} :I'm a bot! I'm from here <https://github"
nicksData = csv.reader(csvfile, delimiter=',', quotechar='|') ".com/shaunagm/oh-irc-bot>. You can change my behavior by "
for row in nicksData: "submitting a pull request or by talking to shauna"
nickArray.append(row) ".\n".format(channel))
return nickArray
def addPerson(person): # Adds newcomer to list of known nicks
person = person.replace("_","") # Returns a grammatically correct string of the channel_greeters.
nickArray.append([person]) 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: with open('nicks.csv', 'a') as csvfile:
nickwriter = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) nickwriter = csv.writer(csvfile, delimiter=',', quotechar='|',
nickwriter.writerow([person]) 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 #make regex case-insenstive
pattern = r'(?i)' pattern = r'(?i)'
for s in stringArray: for s in string_array:
pattern += r'(?:[ :]'+s+r'(?:[ \.!\?,\)]|$))|' pattern += r'(?:[ :]'+s+r'(?:[ \.!\?,\)]|$))|'
#delete trailing '|' #delete trailing '|'
pattern = pattern[:-1] pattern = pattern[:-1]
return pattern 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 # This function is used to change the wait time from the channel.
q = Queue.LifoQueue() # It confirms that the attempt is allowed and then returns the requested value.
t = Thread(target=getIRC) # calls getIRC() (defined above) in a separate thread # 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.daemon = True
t.start() 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. hello_list = [r'hello', r'hi', r'hey', r'yo', r'sup']
helloArray = [r'hello',r'hi',r'hey',r'yo',r'sup'] help_list = [r'help', r'info', r'faq', r'explain yourself']
helpArray = [r'help',r'info',r'faq',r'explain yourself'] hello_pattern = get_welcome_regex(hello_list)
helloPattern = getWelcomeRegEx(helloArray) help_pattern = get_welcome_regex(help_list)
helpPattern = getWelcomeRegEx(helpArray)
while 1: # Loop forever
for i in newList: #################### The Workhorse ####################
if i.status == 0 and i.aroundFor() > waitTime: # 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) welcome(i.nick)
i.updateStatus() i.update_status()
addPerson(i.nick) add_known_nick(i.nick)
newList.remove(i) newcomers.remove(i)
# If the queue is not empty...
if q.empty() == 0: if q.empty() == 0:
# get the next msg in the queue
ircmsg = q.get() ircmsg = q.get()
# and get the nick of the msg sender
actor = ircmsg.split(":")[1].split("!")[0] actor = ircmsg.split(":")[1].split("!")[0]
# Welcome functions ##### Welcome functions #####
if ircmsg.find("PRIVMSG " + channel) != -1: # If someone has spoken into the channel # If someone has spoken into the channel...
for i in newList: if ircmsg.find("PRIVMSG " + channel) != -1:
if actor != i.nick: # Don't turn off response if the person speaking is the person who joined. for i in newcomers:
i.updateStatus() # Sets status to 1 if actor != i.nick: # and is not the new NewComer
addPerson(i.nick) i.update_status() # set the status to 1
newList.remove(i) add_known_nick(i.nick)
## Else: Do we want to do something extra if the person who joined the chat says something with no response? 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 someone joins #channel...
if actor != botnick: # Remove the case where the bot gets a message that the bot has joined. if ircmsg.find("JOIN " + channel) != -1:
if [actor.replace("_","")] not in nickArray: if actor != botnick: # and it is not the bot
if actor not in (i.nick for i in newList): if [actor.replace("_", "")] not in known_nicks:
newList.append(newcomer(actor)) 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 # 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 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: if actor == i.nick:
newList.remove(i) # Remove them from the list newcomers.remove(i) # remove them from the list
# Unwelcome functions ##### Unwelcome functions #####
if ircmsg.find(botnick) != -1 and ircmsg.find("PRIVMSG") != -1: # If someone talks to (or refers to) the bot # If someone talks to (or refers to) the bot.
chan = channel if ircmsg.find(botnick) != -1 and ircmsg.find("PRIVMSG") != -1:
matchHello = search(helloPattern,ircmsg) matchHello = search(hello_pattern, ircmsg)
matchHelp = search(helpPattern,ircmsg) matchHelp = search(help_pattern, ircmsg)
if ircmsg.find("PRIVMSG " + botnick) != -1:
chan = actor
if matchHello: if matchHello:
hello(actor,random.choice(helloArray), chan) bot_hello(random.choice(hello_list))
if matchHelp: if matchHelp:
help(actor, chan) bot_help()
if ircmsg.find("PING :") != -1: # if the server pings us then we've got to respond! # If someone tries to change the wait time...
ping() 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()