Merge pull request #15 from noobur/develop
Fix for Issue #14 and PEP8 changes.
This commit is contained in:
commit
f4671db191
@ -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.
|
||||
|
||||
|
257
bot.py
257
bot.py
@ -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
|
||||
# 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
|
||||
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()
|
||||
|
Loading…
Reference in New Issue
Block a user