#!/usr/bin/python
import cookielib
import re
import mechanize
import time
from pprint import pprint as pp
from bs4 import BeautifulSoup
from bs4.element import NavigableString
from imagesearch import imageSearch
import cleverbot
import os
import random
import textanalyzer
from configobj import ConfigObj
import socket
import urllib
import datetime
loginurl = "http://w...content-available-to-author-only...d.com/login"
accounturl = 'https://a...content-available-to-author-only...d.com/login'
searchurl = 'http://p...content-available-to-author-only...d.com/search.php'
#showposturl = 'http://p...content-available-to-author-only...d.com/showpost.php?p='
showposturl = "http://a...content-available-to-author-only...s.net/pw/forumfetch.php?link=showpost.php%3Fp%3D"
showthreadurl = 'http://p...content-available-to-author-only...d.com/showthread.php?t='
showthreadurlforpost = 'http://p...content-available-to-author-only...d.com/showthread.php?p='
showarchivethreadurl = "http://a...content-available-to-author-only...s.net/pw/forumfetch.php?link=archive%2Findex.php%2Ft-"
chartApiHour = "http://c...content-available-to-author-only...s.com/chart?chf=bg,s,1E1713&chxr=1,0,23&chxs=0,EFEFEF,12,0,lt,EFEFEF&chxt=x&chbh=a&chs=440x130&cht=bvg&chco=76A4FB&chtt=Posts+by+Hour:&chts=EFEFEF,13,l&chds=a&chd=t:"
chartApiWeekday = "http://c...content-available-to-author-only...s.com/chart?chf=bg,s,1E1713&chxr=1,0,7&chxs=0,EFEFEF,12,0,lt,EFEFEF&chxt=x&chbh=a&chs=440x130&cht=bvg&chco=76A4FB&chtt=Posts+by+Day:&chts=EFEFEF,13,l&chds=a&chxl=0:|Mon|Tue|Wed|Thu|Fri|Sat|Sun&chd=t:"
chartApiThread = "http://c...content-available-to-author-only...s.com/chart?chf=bg,s,1E1713&chxs=0,EFEFEF,12,0,lt,EFEFEF&chbh=a&chs=440x110&cht=bvg&chco=76A4FB&chts=EFEFEF,13,l&chds=a&chd=t:"
baseurl = 'http://p...content-available-to-author-only...d.com/'
LOGIN = 'XXXXXXX'
PASS = 'XXXXXXX'
BOTNAME = "SweetieBot"
MAX_SLEEP_INTERVAL = 480
br=mechanize.Browser()
config = ConfigObj('sweetiebot.ini')
settings = {
"cleverbotSessions": {}
}
def getTextContentForTag(tag):
if tag.name == "a" and tag.text.startswith("http"):
return ""
text = ""
for content in tag.contents:
if type(content) == NavigableString:
text += content
elif content.name != "div":
text += getTextContentForTag(content)
return text
class Post:
def __init__(self, postId, html=""):
self.postId = postId
retry_count = 0
print "Reading post#", postId
if html=="":
html = br.open(showposturl+ str(postId)).read()
soup = BeautifulSoup(html, "html5lib")
tag = soup.find("div",{"class": "bigusername"})
if type(tag) == type(None):
print "HTML IS", html
raise Exception("Can't find username")
self.name = tag.text
self.time = soup.find("div",{"class": "time"}).text
self.admin = False
if type(tag.contents[0].contents[0]) != NavigableString or self.name == "Asterelle - Sanctuary":
self.admin = True
tag = soup.find("div",{"id": "post_message_"+str(postId)})
#tag = soup.find("div",id=re.compile("post_message_"))
self.text = getTextContentForTag(tag) + "\n"
#print "POST", postId, "\n", self.text
self.postCount = re.search("<div class=\"smallfont\">Posts: (.*?)</div>", html).group(1)
self.joinDate = re.search("<div class=\"smallfont\">Join Date: (.*?)</div>", html).group(1)
def getThreadId(self):
html = br.open(showthreadurlforpost+ str(self.postId)).read()
match = re.search("showthread\.php\?t=([0-9]*)", html)
return match.group(1)
class Thread:
def __init__(self, threadId, loadAllPosts = False):
self.threadId = threadId
retry_count = 0
print "scanning thread", threadId
html = br.open(showthreadurl+ str(threadId)).read()
soup = BeautifulSoup(html, "html5lib")
tags = soup.findAll("div",{"class": "PW-postbit"})
self.posts = [Post(re.search("showpost\.php\?p=([0-9]*)", str(tag)).group(1),
str(tag)) for tag in tags]
def doLogin():
response = br.open(loginurl)
br.select_form("frm_login")
for control in br.form.controls:
if control.type == 'text':
br.form[control.name] = LOGIN
if control.type == 'password':
br.form[control.name] = PASS
br.form.action = accounturl
response = br.submit()
def init():
cj = cookielib.MozillaCookieJar('./cookies.txt')
def setup():
br.set_cookiejar(cj)
br.set_handle_equiv(True)
#br.set_handle_gzip(True)
br.set_handle_redirect(True)
br.set_handle_referer(True)
br.set_handle_robots(False)
br.addheaders = [('User-agent', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008071615 Fedora/3.0.1-1.fc9 Firefox/3.0.1')]
# Want debugging messages?
#br.set_debug_http(True)
#br.set_debug_redirects(True)
#br.set_debug_responses(True)
if os.path.isfile('./cookies.txt'):
cj.load('./cookies.txt', ignore_discard=False)
setup()
#doLogin()
else:
setup()
#doLogin()
cj.save(ignore_expires=False, ignore_discard=False)
def doSearch(name):
br.open(searchurl)
br.select_form("vbform")
br.form['searchuser'] = name
br.find_control("exactname").items[0].selected = True
br.form['showposts'] = ['1']
response = br.submit()
return response.read()
def findPostId(name):
html = doSearch(name)
matches = re.findall('#post([0-9]*)"', html)
if len(matches) == 0:
print "Can't Find: ", name
else:
print "Found", name, matches[0]
def getPostIdsFromSearchPage(html):
matches = re.findall('#post([0-9]*)"', html)
return matches
def getThreadIdsFromSearchPage(html):
matches = re.findall('thread_title_([0-9]*)"', html)
return matches
def extractText(postId):
try:
return Post(postId).text
except:
return ""
def getNextPageLinkFromSearchPage(page):
for link in br.links(text_regex='>'):
return link.url
return ""
def getTextForName(name):
page = doSearch(name)
postIds = getPostIdsFromSearchPage(page)
nextPageLink = getNextPageLinkFromSearchPage(page)
MAX_CHAR_COUNT = 35000
MAX_POST_COUNT = 150
result = {}
today = time.strftime("%m-%d-%Y", time.localtime(time.time()-3600*3))
yesterday = time.strftime("%m-%d-%Y", time.localtime(time.time()-3600*(3+24)))
def getPostTime(timeStr):
timeStr = timeStr.replace("Today", today).replace("Yesterday", yesterday)
posttime = time.strptime(timeStr, "%m-%d-%Y, %I:%M %p")
return posttime
result['postByHour'] = [0]*24
result['postByWeekday'] = [0]*7
result['postCount'] = 0
result['textSample'] = ""
while len(result['textSample']) < MAX_CHAR_COUNT:
for postId in postIds:
result['post'] = Post(postId)
result['textSample'] += result['post'].text
result['postCount'] += 1
posttime = getPostTime(result['post'].time)
result['postByHour'][posttime.tm_hour] += 1
result['postByWeekday'][posttime.tm_wday] += 1
if len(result['textSample']) > MAX_CHAR_COUNT or result['postCount'] >= MAX_POST_COUNT:
break
if len(result['textSample']) < MAX_CHAR_COUNT and nextPageLink != "" and result['postCount'] < MAX_POST_COUNT:
print " Going to " + nextPageLink
page = br.open(baseurl + nextPageLink).read()
postIds = getPostIdsFromSearchPage(page)
nextPageLink = getNextPageLinkFromSearchPage(page)
else:
break
return result
def doReply(postId, text):
url = baseurl + "newreply.php?do=newreply&p=" + str(postId)
print "REPLYING WITH: ", text
if config["quietmode"] == 'True':
print "QUIET MODE DETECTED"
return
try:
br.open(url)
br.select_form(name="vbform")
except:
doLogin()
br.open(url)
br.select_form(name="vbform")
br.form['message'] = text
if __name__=="__main__":
br.submit()
print "Reply sent"
time.sleep(46)
def quotePost(post, text):
return "[QUOTE=" + post.name + ";" + str(post.postId) +"]" + text + "[/QUOTE]\n"
def getThreadStats(threadid):
print "thread id is", threadid
stats = {"threadid":threadid, "users": {}, "posts": 0}
page = 1
maxPage = 1
posttext = ""
dates = []
while page <= maxPage:
print "Loading thread", threadid, "page", page
html = br.open(showarchivethreadurl + threadid + "-p-" + str(page) +".html")
soup = BeautifulSoup(html, "html5lib")
users = soup.findAll("div", {"class" : "username"})
if len(users) == 0:
return stats
for user in users:
stats["users"][user.text] = stats["users"].get(user.text,0) + 1
stats["posts"] += 1
tags = soup.findAll("div", {"class":"posttext"})
posttext += "\n".join([getTextContentForTag(tag) for tag in tags])
dates.extend([datetime.datetime.strptime(date.text, "%m-%d-%Y, %I:%M %p") for date in soup.findAll("div", {"class" : "date"})])
if page == 1:
stats['title'] = soup.find('p', {"class":"largefont"}).find('a').text
tag = soup.find("div", {"id":"pagenumbers"})
if type(tag) != type(None):
maxPage = int(tag.findAll("a")[-1].text)
page += 1
stats['startdate'] = dates[0]
stats['enddate'] = dates[-1]
def toSeconds(delta):
return float(delta.days*86400+delta.seconds)
timedifference = toSeconds(dates[-1] - dates[0])
normalizedDates = [toSeconds(date - dates[0])/timedifference for date in dates]
buckets = max(min(100, stats['posts']/10), min(10, stats['posts']))
activity = [0]*buckets
for value in normalizedDates:
activity[int(value*(len(activity) - 1))] += 1
stats['activity'] = activity
textanalyzer.calcStats(stats, posttext)
return stats
def getMessageForThreadStats(stats):
startdate = stats['startdate']
enddate = stats['enddate']
age = (enddate - startdate).days
message = "[b]"+BOTNAME + "[/b] has finished reading the thread: [b][url="+showthreadurl+stats['threadid']+"]"+stats['title']+"[/url][/b]!\n\n"
message += "[b]General Stats:[/b]\n"
message += "First Post: [color=white]%s[/color]\n" % (datetime.datetime.strftime(startdate, "%m-%d-%Y"))
message += "Last Post: [color=white]%s[/color]\n" % (datetime.datetime.strftime(enddate, "%m-%d-%Y"))
message += "Thread Lifetime: [color=white]%d days[/color]\n" % (age + 1)
message += "Total Posts: [color=white]%d[/color]\n" % (stats['posts'])
message += "Average Posts Per Day: [color=white]%.1f[/color]\n\n" % (float(stats['posts'])/float(age + 1))
message += "[b]Activity[/b]:\n"
message += "[img]%s[/img]\n\n" % (chartApiThread + ",".join([str(a) for a in stats['activity']]))
users = stats['users'].items()
users.sort(key=lambda x: -x[1])
message += "[b]Top "+str(min(20,len(users)))+" Frequent Posters (count):[/b]\n"
message += "\n".join(["%s ([color=white]%d[/color])" % item for item in users[:20]]) + "\n\n"
results = stats['textanalysis']
message += "[b]Writing Stats:[/b]\n"
message += "Total Words: [color=white]%d[/color] \n" % (results['words'])
message += "Total Sentences: [color=white]%d[/color] \n" % (results['sentences'])
message += "Average Words per Post: [color=white]%5.1f[/color]\n" % (float(results['words'])/float(stats['posts']))
message += "Average Words per Sentence: [color=white]%5.1f[/color]\n" % (float(results['words'])/float(results['sentences']))
message += "Average Sentences per Post: [color=white]%5.1f[/color]\n" % (float(results['sentences'])/float(stats['posts']))
message += "Average Syllables per Word: [color=white]%5.1f[/color]\n" % (float(results['syllables'])/float(results['words']))
message += "Average Letters per Word: [color=white]%5.1f[/color]\n" % (float(results['characters'])/float(results['words']))
message += "\n"
message += "[b]Most Used Words (count): [color=gray](common words excluded)[/color][/b]\n"
message += "\n".join(["%s ([color=white]%d[/color])" % (word[0].replace("#", "'"), word[1]) for word in results['favoritewords0']]) + "\n"
for i in range(3):
message += "\n[b]Most Used "+str(i+3)+"-Word Phrases (count):[/b]\n"
message += "\n".join(['"%s ([color=white]%d[/color])"' % (word[0].replace("#", "'"), word[1]) for word in results['favoritewords'+str(i+2)][:3]])
return message
def doAnalyzeThreadErrorReply(post, command, title, threadid):
message = BOTNAME + " couldn't find the thread you're talking about :(\n"
if len(threadid):
message += "Is the thread's id really: '"+ threadid +"'?\n"
if len(title):
message += "Is the thread's title really: '"+ title +"'?\n"
message += "You should try typing either: \n'" +BOTNAME+ " analyze this thread' or \n'" \
+BOTNAME+ " analyze thread <Thread Title>' or\n'" \
+BOTNAME+ " analyze thread <Thread ID>'"
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
return
def analyzeThreadCommand(post, command, args):
print "ANALYZE THREAD from " , post.name, post.postId, command, ":", args
starttime = time.time()
threadid = ""
title = ""
if args.startswith("this thread"):
threadid = post.getThreadId()
else:
match = re.search("thread +([0-9]+)", args)
if match:
threadid = match.group(1)
if not match or int(threadid < 300):
match = re.search("thread (.*)", args)
if match:
title = match.group(1).strip()
print "Searching thread title", title
threadids = searchForThreadByTitle('"'+title+'"')
if len(threadids) > 0:
threadid = threadids[0]
if len(threadid) == 0:
doAnalyzeThreadErrorReply(post, command, title, threadid)
return
stats = getThreadStats(threadid)
if len(stats['users']) == 0:
try:
refpost = Post(threadid)
threadid = refpost.getThreadId()
stats = getThreadStats(threadid)
except:
doAnalyzeThreadErrorReply(post, command, title, threadid)
return
message = getMessageForThreadStats(stats)
message += "\n\n This took me [color=white]%5.1f[/color] s to finish" % (time.time() - starttime)
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
def analyzeCommand(post, command, args):
print "GOT COMMAND TO ANALYZE from " , post.name, post.postId, command, ":", args
starttime = time.time()
possessive = "your"
if args.startswith("me"):
target = post.name
elif args.startswith("this thread") or args.startswith("thread"):
analyzeThreadCommand(post, command, args)
return
else:
target = args.strip()
target = re.sub("[,.?;!]+.*$", "", target)
possessive = target + "'s"
if len(target) == 0:
message = "Umm, exactly who did you want " + BOTNAME + " to analyze?"
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
return
# samplePost, postCount, postByHour, text = getTextForName(target)
result = getTextForName(target)
if result['postCount'] == 0:
message = BOTNAME + " couldn't find any posts for '"+target+"' :(\nCan you try again with their exact name please?"
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
return
postingHourUrl = chartApiHour + ",".join([str(x) for x in result['postByHour']])
postingWeekdayUrl = chartApiWeekday + ",".join([str(x) for x in result['postByWeekday']])
print "EXTRACTED TEXT FOR", target
if target not in config['users']:
config['users'][target] = {}
stats = {}
textanalyzer.calcStats(stats, result['textSample'])
results = stats['textanalysis']
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
startDate = result['post'].joinDate.split()
totalDays = (datetime.date.today() - datetime.date(int(startDate[1]), months.index(startDate[0]) + 1, 1)).days + 1
config['users'][target]['samplePost'] = result['post'].postId
config['users'][target]['analyzed posts'] = result['postCount']
config['users'][target]['favorite word'] = results['favoritewords0'][0][0]
config['users'][target]['analyzed words'] = results['words']
config['users'][target]['postCount'] = result['post'].postCount
config['users'][target]['joinDate'] = result['post'].joinDate
config['users'][target]['totalDays'] = totalDays
message = "[b]"+BOTNAME + " has read "+possessive+" last "+str(result['postCount'])+" posts![/b]\nHere's some neat stuff "+BOTNAME+" has learned.\n\n"
message += "[b]General Stats:[/b]\n"
message += "Forum name: [color=white]%s[/color]\n" % (target)
message += "Days since joining forums: [color=white]%d[/color]\n" % (totalDays)
message += "Total Posts: [color=white]%s[/color]\n" % (result['post'].postCount)
message += "Average Posts per Day: [color=white]%.1f[/color]\n\n" % (float(result['post'].postCount.replace(',',''))/float(totalDays))
message += "[b]Activity:[/b]\n"
message += "[img]%s[/img]\n" % (postingHourUrl)
message += "[img]%s[/img]\n\n" % (postingWeekdayUrl)
message += "[b]Writing Stats (what "+BOTNAME+" read):[/b]\n"
message += "Total Words: [color=white]%d[/color] \n" % (results['words'])
message += "Total Sentences: [color=white]%d[/color] \n" % (results['sentences'])
message += "Average Words per Post: [color=white]%5.1f[/color]\n" % (float(results['words'])/float(result['postCount']))
message += "Average Words per Sentence: [color=white]%5.1f[/color]\n" % (float(results['words'])/float(results['sentences']))
message += "Average Sentences per Post: [color=white]%5.1f[/color]\n" % (float(results['sentences'])/float(result['postCount']))
message += "Average Syllables per Word: [color=white]%5.1f[/color]\n" % (float(results['syllables'])/float(results['words']))
message += "Average Letters per Word: [color=white]%5.1f[/color]\n" % (float(results['characters'])/float(results['words']))
message += "\n"
message += "[b]Most Used Words (count): [color=gray](common words excluded)[/color][/b]\n"
message += "\n".join(["%s ([color=white]%d[/color])" % (word[0].replace("#", "'"), word[1]) for word in results['favoritewords0']]) + "\n"
for i in range(3):
message += "\n[b]Most Used "+str(i+3)+"-Word Phrases (count):[/b]\n"
message += "\n".join(['"%s ([color=white]%d[/color])"' % (word[0].replace("#", "'"), word[1]) for word in results['favoritewords'+str(i+2)][:3]])
message += "\n\n This took me [color=white]%5.1f[/color] s to finish" % (time.time() - starttime)
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
def getCleverBotSessionForName(name):
if name in settings['cleverbotSessions']:
return settings['cleverbotSessions'][name]
else:
keys = settings['cleverbotSessions'].keys()
if len(keys) > 7:
del settings['cleverbotSessions'][keys[0]]
del settings['cleverbotSessions'][keys[1]]
session = cleverbot.Session()
print "MAKING NEW SESSION for", name
settings['cleverbotSessions'][name] = session
return session
def askBotCommand(post, command):
session = getCleverBotSessionForName(post.name)
print "matching", command
text = re.search("^\s*" + BOTNAME + ",? (.*)$", command, re.IGNORECASE | re.MULTILINE)
print "ASKING CLEV ", text.group(1)
reply = session.Ask(text.group(1))
reply = reply.replace("Cleverbot", BOTNAME).replace("cleverbot", BOTNAME)
if len(reply) < 5:
reply += "[color=#1e1713](5char)[/color]"
reply = quotePost(post, command) + decorate(reply)
doReply(post.postId, reply)
def checkServerStatusCommand(post, command, args):
servers, message = getServerStatusMessage()
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
def getServerStatusMessage():
#Simply change the host and port values
servers = [("Sanctuary (PVE)", "pwigc2.perfectworld.com"),
("Lost City (PvP)", "pwigc3.perfectworld.com"),
("Heaven's Tear (PvE)", "pwigc4.perfectworld.com"),
("Archosaur (PvE)", "pwiwest4.perfectworld.com"),
("Harshlands (PvP)", "pwieast1.perfectworld.com"),
("Dreamweaver (PvE)", "pwieast2.perfectworld.com"),
("Raging Tide (PvE)", "pwieast3.perfectworld.com"),
("Lothranis (PvE)", "pwieu1.fr.perfectworld.eu"),
("Momaganon (PvE)", "pwieu2.de.perfectworld.eu")
]
port = 29000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
number_servers_up = 0
server_message = []
for server in servers:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1.5)
milli = int(round(time.time() * 1000))
s.connect((server[1], 29000))
milli = int(round(time.time() * 1000)) - milli
s.shutdown(2)
number_servers_up += 1
server_message.append("[B]"+server[0] +"[/B] [color=gray](" + server[1] +")[/color]: [color=Lime]ONLINE[/color] (Ping: " + str(milli) +"ms)")
except:
server_message.append("[B]"+server[0] +"[/B] [color=gray](" + server[1] +")[/color]: [color=RED]OFFLINE[/color]")
message = "Here's the server status:\n\n[B][color=white]West Coast Servers:[/color][/B]\n" + "\n".join(server_message[:4]) + "\n\n"
message += "[B][color=white]East Coast Servers:[/color][/B]\n" + "\n".join(server_message[4:7]) + "\n\n"
message += "[B][color=white]European Servers:[/color][/B]\n" + "\n".join(server_message[7:]) + "\n"
return number_servers_up, message
def checkForMorePatches(url):
patchurl = "http://p...content-available-to-author-only...d.com/patches/manual/"
match = re.search("ec_patch_([0-9]+)-([0-9]+)", url)
if match:
currentPatch = match.group(2)
for i in range(1,15):
testurl = patchurl + "ec_patch_" + currentPatch + "-" + str(int(currentPatch)+i) + ".cup"
print "checking##", testurl
try:
response = br.open(testurl)
return response.info().getheader("Content-length"), testurl
except:
continue
return ""
def findManualPatchCommand(post, command, args):
message = getFindManualPatchMessage()
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
def getFindManualPatchMessage():
techsupportUrl = 'http://t...content-available-to-author-only...d.com/showthread.php?t=1717'
html = br.open(techsupportUrl).read()
soup = BeautifulSoup(html, "html5lib")
currentPatchLink = soup.find("a", href=re.compile('perfectworld\.com/patches/manual'))
print 'found patch url', currentPatchLink['href']
currentpatch = currentPatchLink['href']
patchText = currentPatchLink.text
nextpatch = checkForMorePatches(currentpatch)
message = ''
if nextpatch == "":
message = 'The latest manual patch is here: [URL="'+currentpatch+'"]'+patchText+'[/URL]'
else:
patchText = "Patch " + re.search("([0-9]+-[0-9]+)", nextpatch[1]).group(0)
size = nextpatch[0]
message = 'The latest manual patch appears to be: [URL="'+nextpatch[1]+'"]'+patchText+'[/URL] (' + size + " bytes)\n"
if int(size) < 5000000:
message += "It is weirdly small for some reason. \n\n"
elif int(size) < 19000000:
message += "That's average-sized. \n\n"
elif int(size) < 35000000:
message += "It is somewhat bigger than average. \n\n"
else:
message += "Wow this thing is big! \n\n"
message += "This patch hasn't been [URL="+techsupportUrl+"]officially listed[/URL] yet."
return message
def decorate(chatText):
return '[SIZE="2"][FONT="Tahoma"][COLOR="Silver"]' + chatText + '[/COLOR][/FONT][/SIZE]'
def showMeCommand(post, command, args):
print "Doing image search for", args
image = imageSearch(args)
reply = quotePost(post, command)
if image == '':
reply += decorate(BOTNAME + " has no idea what you're talking about.")
else:
reply += decorate("Sure, no problem.\n\n" )
reply += "[IMG]" + image + "[/IMG]"
doReply(post.postId, reply)
def requestDeniedReply(post, command, args):
message = "Hey that's an admin-only request. "+BOTNAME+" doesn't have to listen to you!"
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
def addToIgnoreCommand(post, command, args):
if not post.admin:
requestDeniedReply(post, command, args)
return
print "Gonna ignore", args
ignorename = args
users = args.split(",")
message = ""
for user in users:
if user.strip() in config['ignorelist']:
message += "You know I'd do anything for you but I already have "+user.strip()+" in my ignore list.\n"
else:
message += "Sure thing! I'm adding " + user.strip() + " to my ignore list.\n"
config['ignorelist'].append(user.strip())
config.write()
reply = quotePost(post, command) + decorate(message)
print reply
doReply(post.postId, reply)
def removeFromIgnoreCommand(post, command, args):
if not post.admin:
requestDeniedReply(post, command, args)
return
print "Gonna unignore", args
users = args.split(",")
message = ""
for user in users:
if user.strip() in config['ignorelist']:
message += "Anything you say! I'm removing " + user.strip() + " from my ignore list.\n"
config['ignorelist'].remove(user.strip())
config.write()
else:
message += "Hmm, are you sure I was ignoring " + user.strip() + "? I don't think I was.\n"
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
def listIgnoreCommand(post, command, args):
if not post.admin:
requestDeniedReply(post, command, args)
return
print "Gonna list ignores"
message = "I am ignoring the following trolls:\n\n"
message += "\n".join(config['ignorelist'])
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
def startTalkingCommand(post, command, args):
if not post.admin:
requestDeniedReply(post, command, args)
return
message = "Yay! Sweetiebot is so happy now! Being quiet is no fun at all :("
config['quietmode'] = 'False'
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
def beQuietCommand(post, command, args):
if not post.admin:
requestDeniedReply(post, command, args)
return
print "got bequiet", post.admin
message = "Aww ok, you're the boss! If you find it in yourself to forgive me just tell me 'you can start talking again'"
reply = quotePost(post, command) + decorate(message)
doReply(post.postId, reply)
config['quietmode'] = 'True'
def scanPostForCommand(postId):
print "scanning Post", postId
try:
post = Post(postId)
if post.name.startswith(BOTNAME) or (post.name in config['ignorelist'] and not post.admin):
return
except:
#post was deleted
return
text = post.text
print text
commands = {
'analyze': analyzeCommand,
'analyse': analyzeCommand,
'show me': showMeCommand,
'find the manual patch': findManualPatchCommand,
'check server status': checkServerStatusCommand,
'what is the server status': checkServerStatusCommand,
'report server status': checkServerStatusCommand,
'ignore' : addToIgnoreCommand,
'unignore' : removeFromIgnoreCommand,
'stop ignoring' : removeFromIgnoreCommand,
'be quiet' : beQuietCommand,
'stop talking' : beQuietCommand,
'who are you ignoring' : listIgnoreCommand,
'you can start talking again' : startTalkingCommand
}
match = re.search("^\s*" + BOTNAME + ",? (?:can you |will you |please |could you |would you |won't you )*("+"|".join(commands.keys())+"|\S*)\W? ?(\w+.*)?", text, re.IGNORECASE | re.MULTILINE)
print "match", match
if match:
if match.group(1).lower() in commands:
commands[match.group(1).lower()](post, match.group(0), match.group(2))
else:
askBotCommand(post, match.group(0))
return True
else:
return False
def doMaintenanceReply(post):
message = "First non-human reply!\n\n"
message += getFindManualPatchMessage()
print post.text
questionMatch = re.search("^(.*\?)", post.text, re.MULTILINE)
if questionMatch:
clever = getCleverBotSessionForName(post.name)
question = questionMatch.group(1)
message += quotePost(post, question)
message += (clever.Ask(question)).replace("Cleverbot", BOTNAME).replace("cleverbot", BOTNAME)
reply = decorate(message)
#print reply
doReply(post.postId, reply)
def dayCount():
return (datetime.date.today() - datetime.date.min).days
def searchForThreadByTitle(title):
br.open(searchurl)
br.select_form("vbform")
#The added random numbers force a cache miss!
br.form['query'] = title + " " + str(random.randint(0,999)) + " " + str(random.randint(0,999))
br.form['titleonly'] = ['1']
response = br.submit()
threadIds = getThreadIdsFromSearchPage(response.read())
return threadIds
def searchForMaintenanceThread():
threadIds = searchForThreadByTitle("Maintenance Discussion")
print "Scanning maintenance thread minid is", config['maintenance']['threadId']
for threadId in threadIds:
if int(threadId) > int(config['maintenance']['threadId']):
print "Found new maintenance thread mention!"
maintThread = Thread(threadId)
if maintThread.posts[0].admin:
config['maintenance']['threadId'] = int(threadId)
config['maintenance']['postId'] = int(maintThread.posts[0].postId)
config['maintenance']['status'] = 'pending'
config['maintenance']['date'] = dayCount()
config.write()
doMaintenanceReply(maintThread.posts[0])
def checkMaintenanceServerStatus():
servers, message = getServerStatusMessage()
print "Maint thread up! Checking server status", config['maintenance']['status'], servers
reply = ""
if config['maintenance']['status'] == "pending" and servers == 0:
reply = "The servers are now down! Everyone start panicing! Ohh wait, this happens every maintenance.\n\n" + message
config['maintenance']['status'] = "down"
config.write()
elif config['maintenance']['status'] == "down" and servers == 9:
reply = BOTNAME + " can confirm the servers are back up! Yay!\n\n" + message
config['maintenance']['status'] = "complete"
config.write()
if reply != "":
print "Making reply!"
maintThread = Thread(config['maintenance']['threadId'])
doReply(maintThread.posts[0].postId, decorate(reply))
def searchForCommand():
br.open(searchurl)
br.select_form("vbform")
#The added random numbers force a cache miss!
br.form['query'] = BOTNAME + " " + str(random.randint(0,999)) + " " + str(random.randint(0,999))
br.form['showposts'] = ['1']
response = br.submit()
postIds = getPostIdsFromSearchPage(response.read())[::-1]
print "Scanning posts minID is", config['minPostId']
updated = False
for postId in postIds:
# print "PostID is", postId, config['minPostId']
if int(postId) > int(config['minPostId']):
print "Found New Post", postId, config['minPostId']
updated = True
scanPostForCommand(postId)
setMinPostId(postId)
return updated
def setMinPostId(postId):
config['minPostId'] = int(postId)
config.write()
init()
if __name__=="__main__":
if config['maintenance']['status'] == "down":
if getServerStatusMessage()[0] > 0:
config['maintenance']['status'] == "complete"
sleeptime = 60
while True:
if dayCount() - int(config['maintenance']['date']) > 5:
searchForMaintenanceThread()
if datetime.date.today().weekday() == 1 and time.localtime().tm_hour >= 14:
sleeptime = min(sleeptime, 120)
if dayCount() - int(config['maintenance']['date']) <= 1 and config['maintenance']['status'] in ["pending", "down"]:
checkMaintenanceServerStatus()
else:
config['maintenance']['status'] == "complete"
time.sleep(10)
if searchForCommand():
sleeptime = 0
print "Sleeping for ", sleeptime
time.sleep(sleeptime)
sleeptime = min(sleeptime + 30, MAX_SLEEP_INTERVAL)