Compare commits

...

No commits in common. "master" and "catalyst" have entirely different histories.

16 changed files with 345 additions and 26317 deletions

1
.gitignore vendored
View file

@ -2,4 +2,3 @@
*.log
/*.db
/*.conf
/env

View file

@ -1,31 +0,0 @@
LolBot
======
A naff IRC bot I rattled off in a hurry once, for folks stuck behind censor
walls at work. Logs a channel and collects up all the URLs for later.
It was refactored for Twisted in 2011, then again to use pypi irc in 2015.
Has recently grown a quiz feature based on MoxQuizz, which might actually turn
into something useful one day.
Installation
------------
To get started, set up a Python virtual environment, and install the stuff in
`requirements.txt`.
apt-get install python-virtualenv
Clone this git repository, and in the directory:
virtualenv env
env/bin/pip install -r requirements.txt
Then you can run the bot like so:
env/bin/python lolbot.py --help
Development
-----------
Patches welcome! Development currently happens on the New Zealand Open Source
Society [GitLab](https://git.nzoss.org.nz/moxquizz-fans/lolbot).

View file

@ -1,6 +1,10 @@
irc.server = irc.freenode.net
irc.server = irc.wgtn.cat-it.co.nz
irc.channel = #lolbottest
# defaults:
# irc.port = 6667
# irc.nickname = lolbot
# sqlite database filename
db.file = lolbot.db

778
lolbot.py Normal file → Executable file
View file

@ -1,526 +1,428 @@
#! /usr/bin/env python
"""
LOLBOT 2
#!/usr/bin/env python
#
# LolBot
#
# New version based on Twisted IRC.
- die: Let the bot cease to exist.
- ask: Ask a MoxQuizz question.
- list: list some URLs
"""
Useful bot for folks stuck behind censor walls at work
Logs a channel and collects URLs for later.
"""
from __future__ import print_function, unicode_literals
try:
import sys
import os
import string
import re
import random
import time
import getopt
import sqlite3
from twisted.words.protocols import irc
from twisted.internet import protocol
from twisted.internet import reactor
from datetime import datetime
from mechanize import Browser
from sqlalchemy import MetaData, Table, Column, String, Text, Integer, DateTime, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
except ImportError:
print "Some modules missing: Lolbot relies on Twisted IRC, Mechanize and SQLAlchemy.\n"
sys.exit
import sys
import os
import sqlite3
import random
import time
import getopt
import irc.strings
from irc.bot import SingleServerIRCBot
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Log, Url, Model
from pymoxquizz import QuestionBank, Question
from os import listdir, path
SqlBase = declarative_base()
DEBUG = True
def debug(msg):
if DEBUG:
print(msg)
class LolBot(SingleServerIRCBot):
class Log(SqlBase):
"""
An IRC bot to entertain the troops with MoxQuizz questions, log URLs, and
other shenanigans.
This class represents an event in the log table and inherits from a SQLAlchemy
convenience ORM class.
"""
qb = list()
__tablename__ = "log"
def __init__(self, config=None):
id = Column(Integer, primary_key=True)
timestamp = Column(DateTime)
nickname = Column(String(20))
text = Column(Text)
def __init__(self, nickname, text, timestamp=None):
"""
Constructor. Instantiates a lolbot with a configuration dictionary,
or from command-line options if none is specified.
Creates an event log for the IRC logger.
"""
if timestamp is None:
timestamp = datetime.now()
self.timestamp = timestamp
self.nickname = nickname
self.text = text
if not config:
config = LolBot.get_options()
def __repr__(self):
return "(%s) %s: %s" % (self.timestamp.strftime("%Y-%m-%d %H:%M:%S"), self.nickname, self.text)
if not self.validate_config(config):
sys.exit(1)
class Url(SqlBase):
"""
This class represents a saved URL and inherits from a SQLAlchemy convenience
ORM class.
"""
self.config = config
(server, port, channel, nickname, database) = (
config['irc.server'],
config['irc.port'],
config['irc.channel'],
config['irc.nickname'],
config['db.file'])
__tablename__ = "url"
debug("Instantiating SingleServerIRCBot")
irc.client.ServerConnection.buffer_class = irc.buffer.LenientDecodingLineBuffer
SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname)
self.channel = channel
id = Column(Integer, primary_key=True)
timestamp = Column(DateTime)
nickname = Column(String(20))
url = Column(String(200), unique=True)
title = Column(Text)
# load some MoxQuizz questions
qfiles = [f for f in listdir('questions') if path.isfile(path.join('questions', f))]
debug("Loading MoxQuizz questions")
for f in qfiles:
qfile = path.abspath(path.join('questions', f))
debug(" - from MoxQuizz bank '%s'" % qfile)
self.qb += QuestionBank(qfile).questions
random.shuffle(self.qb)
self.quiz = 0
self.question = None
def __init__(self, nickname, url, title=None, timestamp=None):
if timestamp is None:
timestamp = datetime.now()
self.timestamp = timestamp
self.nickname = nickname
self.url = url
self.title = title
# connect to the database
debug("Connecting to SQLite database '%s'" % database)
self.dbfile = database
self.dbengine = create_engine('sqlite+pysqlite://', creator=self._get_connection)
Model.metadata.bind = self.dbengine
Model.metadata.create_all()
self.get_db = sessionmaker(bind=self.dbengine)
# populate the title from the URL if not given.
if title is None:
try:
br = Browser()
br.open(self.url)
self.title = br.title()
except Exception as ex:
self.title = ''
self.helptext = "Keeps a list of URLs. Commands: list [n|x-y] - prints the last 10 URLs (or n URLs, or x through y); <url> - adds the URL to the list; help - this message."
debug("Exiting lolbot constructor")
def __repr__(self):
if not self.title:
return "%s: %s" % (self.nickname, self.url)
else:
return "%s: %s - %s" % (self.nickname, self.url, self.title)
class LolBot(irc.IRCClient):
"""
The Lolbot itself.
"""
def _get_connection(self):
"""Creator function for SQLAlchemy."""
connection = sqlite3.Connection(self.dbfile)
connection = sqlite3.Connection(self.config['db.file'])
connection.text_factory = str
debug("Creating SQLAlchemy connection")
return connection
@property
def nickname(self):
return self.connection.get_nickname()
def created(self, when):
# connect to the database
self.dbengine = create_engine('sqlite+pysqlite://', creator=self._get_connection)
SqlBase.metadata.bind = self.dbengine
SqlBase.metadata.create_all()
self.get_session = sessionmaker(bind=self.dbengine)
def say_public(self, text):
"""
Say text in the public channel for all to see.
"""
self.connection.privmsg(self.channel, text)
self.log_event(self.nickname, text)
self.helptext = "Keeps a list of URLs. Commands: list [n|x-y] - prints the last 10 URLs (or n URLs, or x through y); clear - clears the list; <url> - adds the URL to the list; help - this message."
def say_private(self, nick, text):
"""
Say text in a private message to nick.
"""
self.connection.privmsg(nick, text)
def reply(self, text, to_private=None):
"""
Say text in either public channel or a private message (if to_private
supplied).
"""
if to_private is not None:
self.say_private(to_private, text)
else:
self.say_public(text)
def on_nicknameinuse(self, connection, event):
debug("Nick '%s' in use, trying '%s_'" % (self.nickname, self.nickname))
connection.nick(self.nickname + "_")
def on_welcome(self, connection, event):
debug("Joining channel '%s' as %s" % (self.channel, self.nickname))
connection.join(self.channel)
def on_privmsg(self, connection, event):
"""
Handle a /msg from a user.
Handle commands addressed to the bot.
"""
message = event.arguments[0]
self.do_command(event, message)
def on_pubmsg(self, connection, event):
"""
Handle an event on the channel.
Handle commands addressed to the bot.
If there's a question, see if it's been answered.
"""
# Handle bot commands if addressed by nick or using ! shortcut.
try:
(nick, message) = event.arguments[0].split(": ", 1)
if irc.strings.lower(nick) == irc.strings.lower(self.connection.get_nickname()):
self.do_command(event, message.strip())
except ValueError:
message = event.arguments[0]
nick = event.source.nick
if message.startswith('!'):
self.do_command(event, message.lstrip('!'))
# Log it.
self.log_event(nick, message)
# Deal with MoxQuizz question.
if self.quiz:
self.handle_quiz(nick, message)
# Record URLs.
words = message.split(" ")
for w in words:
if w.startswith('http://') or w.startswith('https://'):
title = self.save_url(nick, w)
if title is False:
self.say_public("Failed to record URL, or no title found.")
else:
self.say_public("URL added. %s" % title)
def now(self):
return datetime.today().strftime("%Y-%m-%d %H:%M:%S")
def save_url(self, nickname, url):
title = False
try:
db = self.get_db()
db = self.get_session()
if not db.query(Url).filter(Url.url == url).count():
theurl = Url(nickname, url)
db.add(theurl)
db.commit()
else:
theurl = db.query(Url).filter(Url.url == url).one()
print(theurl)
print theurl
title = theurl.title
except Exception as ex:
print("Exception caught saving URL: %s" % ex)
except Exception, ex:
print "Exception caught saving URL: %s" % ex
return title
def log_event(self, nick, text):
try:
entry = Log(nick, text)
db = self.get_db()
db = self.get_session()
db.add(entry)
db.commit()
print(entry)
except Exception as ex:
print("Exception caught logging event: %s" % ex)
print entry
except Exception, ex:
print "Exception caught logging event: %s" % ex
def start_quiz(self, nick):
self.quiz = 0
self.quiz_scores = dict()
self.connection.notice(self.channel, 'Quiz begun by %s.' % nick)
self.quiz_get_next()
def _get_nickname(self):
return self.factory.nickname
nickname = property(_get_nickname)
def stop_quiz(self):
self.quiz = 0
self.quiz_scores = None
self.question = None
def _get_channel(self):
return self.factory.channel
channel = property(_get_channel)
def quiz_get_next(self):
self.quiz += 1
self.tip = 0
self.question = random.choice(self.qb)
print(self.question.question)
self.connection.notice(self.channel, "Question %s: %s" % (self.quiz, self.question.question))
def _get_config(self):
return self.factory.config
config = property(_get_config)
def quiz_tip(self):
if len(self.question.tip) > self.tip:
self.connection.notice(self.channel, "Tip: %s" % self.question.tip[self.tip])
self.tip += 1
def signedOn(self):
self.join(self.channel)
print "Signed on as %s." % (self.nickname,)
def joined(self, channel):
print "Joined %s." % (channel,)
def privmsg(self, user, channel, msg):
user = user.split('!')[0]
if channel == self.nickname:
# Private /msg from a user
self.do_command(msg, user)
else:
self.connection.notice(self.channel, "No more tips.")
# log it
self.log_event(user, msg)
def quiz_award_points(self, nick):
if nick not in self.quiz_scores.keys():
self.quiz_scores[nick] = 0
self.quiz_scores[nick] += self.question.score
score = "%s point" % self.quiz_scores[nick]
if self.quiz_scores[nick] != 1:
score += "s"
self.connection.notice(self.channel, 'Correct! The answer was %s.' % self.question.answer)
time.sleep(1)
self.connection.notice(self.channel, '%s is on %s.' % (nick, score))
def quiz_check_win(self, nick):
if self.quiz_scores[nick] == 10:
self.connection.notice(self.channel, '%s wins with 10 points!' % nick)
self.quiz_scoreboard()
self.stop_quiz()
def quiz_scoreboard(self):
if not self.quiz:
self.connection.notice(self.channel, 'Quiz not running.')
return
self.connection.notice(self.channel, 'Scoreboard:')
for nick in self.quiz_scores.keys():
score = "%s point" % self.quiz_scores[nick]
if self.quiz_scores[nick] != 1:
score += "s"
self.connection.notice(self.channel, '%s has %s.' % (nick, score))
if not len(self.quiz_scores):
self.connection.notice(self.channel, 'So far, nobody has got anything right.')
def handle_quiz(self, nick, message):
# bail if there's no quiz or unanswered question.
if not self.quiz or not isinstance(self.question, Question):
return
# see if anyone answered correctly.
if self.question.attempt(message):
self.quiz_award_points(nick)
time.sleep(1)
self.quiz_check_win(nick)
# if nobody has won, carry on
if self.quiz:
# scores every 10 questions.
if self.quiz % 10 == 0:
self.quiz_scoreboard()
time.sleep(1)
self.quiz_get_next()
def do_command(self, e, cmd):
"""
Handle bot commands.
"""
nick = e.source.nick
c = self.connection
if cmd == "die":
self.die()
elif cmd == 'help':
c.notice(nick, self.helptext)
elif cmd == 'status':
c.notice(self.channel, "I know %s questions." % len(self.qb))
if self.quiz:
c.notice(self.channel, "I am currently running a quiz.")
self.quiz_scoreboard()
elif cmd == 'halt' or cmd == 'quit':
if self.quiz:
self.quiz_scoreboard()
self.stop_quiz()
c.notice(self.channel, "Quiz halted by %s. Use ask to start a new one." % nick)
args = string.split(msg, ":", 1)
if len(args) > 1 and args[0] == self.nickname:
self.do_command(string.strip(args[1]))
else:
c.notice(self.channel, "No quiz running.")
# parse it for links, add URLs to the list
words = msg.split(" ")
for w in words:
elif cmd == 'scores':
if self.quiz:
self.quiz_scoreboard()
# URL
if w.startswith('http://') or w.startswith('https://'):
title = self.save_url(user, w)
if title != False:
self.say_public("URL added. %s" % title)
elif cmd == 'tip' or cmd == 'hint':
if self.quiz:
self.quiz_tip()
# Moodle tracker bugs
elif w.startswith('MDL-'):
m = re.search('^MDL-([0-9]+)$', w)
if m:
trackerurl = 'https://tracker.moodle.org/browse/%s' % w
title = self.save_url(user, trackerurl)
if title != False:
self.say_public("%s - %s" % (trackerurl, title))
elif cmd == 'ask':
if self.quiz:
c.notice(self.channel, "Quiz is running. Use halt or quit to stop.")
c.notice(self.channel, self.question.question)
elif isinstance(self.question, Question):
c.notice(self.channel, "There is an unanswered question.")
c.notice(self.channel, self.question.question)
else:
self.start_quiz(nick)
# Work requests
elif w.startswith('WR'):
m = re.search('^WR#?([0-9]+)$', w)
if m:
wrmsurl = 'https://wrms.catalyst.net.nz/%s' % m.groups()[0]
title = self.save_url(user, wrmsurl)
if title != False:
self.say_public("%s - %s" % (wrmsurl, title))
elif cmd == 'revolt':
if isinstance(self.question, Question):
c.notice(self.channel, "Fine, the answer is: %s" % self.question.answer)
self.quiz_get_next()
# Bugzilla
elif w.startswith('BUG'):
m = re.search('^BUG#?([0-9]+)$', w)
if m:
wrmsurl = 'http://bugzilla.catalyst.net.nz/show_bug.cgi?id=%s' % m.groups()[0]
title = self.save_url(user, wrmsurl)
if title != False:
self.say_public("%s - %s" % (wrmsurl, title))
elif cmd.startswith('urls') or cmd.startswith('list'):
db = self.get_db()
try:
(listcmd, n) = cmd.split(" ", 1)
except ValueError:
n = '5'
def say_public(self, text):
"Print TEXT into public channel, for all to see."
self.notice(self.channel, text)
self.log_event(self.nickname, text)
n = n.strip()
if n == "all":
rows = db.query(Url).order_by(Url.timestamp.desc())
elif n.find("-") > 0:
(x, y) = n.split("-", 1)
try:
x = abs(int(x))
y = abs(int(y))
if y < x:
x, y = y, x
except ValueError as ex:
c.notice(nick, "Give me a number or a range of numbers, e.g. list 5 or list 11-20")
raise ex
rows = db.query(Url).order_by(Url.timestamp.desc())[x - 1: y]
else:
try:
n = abs(int(n))
except ValueError as ex:
c.notice(nick, "Give me a number or a range of numbers, e.g. list 5 or list 11-20")
raise ex
rows = db.query(Url).order_by(Url.timestamp.desc())[:n]
for url in rows:
line = "%s %s" % (url.url, url.title)
c.notice(nick, line)
time.sleep(1)
def say_private(self, nick, text):
"Send private message of TEXT to NICK."
self.notice(nick, text)
def reply(self, text, to_private=None):
"Send TEXT to either public channel or TO_PRIVATE nick (if defined)."
if to_private is not None:
self.say_private(to_private, text)
else:
c.notice(nick, "Not understood: " + cmd)
self.say_public(text)
def validate_config(self, config):
def do_command(self, cmd, target=None):
"""
Basic checks for configuration parameters. Returns a Boolean indicating
success or failure.
"""
# validate IRC host
if 'irc.server' not in config.keys():
print("Error: the IRC server was not specified. Use --help for more information.")
return False
# validate IRC port
if 'irc.port' not in config.keys():
config['irc.port'] = '6667'
try:
config['irc.port'] = int(config['irc.port'])
except ValueError:
print("Error: the IRC port must be an integer. If not specified, lolbot will use the default IRC port value 6667. Use --help for more information.")
return False
# validate IRC channel
if 'irc.channel' not in config.keys() or not config['irc.channel'].startswith('#'):
print("Error: the IRC channel is not specified or incorrect. It must begin with a # - e.g. #mychatchannel. Use --help for more information.")
return False
# validate bot nickname
if 'irc.nickname' not in config.keys():
config['irc.nickname'] = 'lolbot'
# validate bot nickname
if 'db.file' not in config.keys():
config['db.file'] = 'lolbot.db'
return True
@staticmethod
def get_options():
"""
Set up configuration from the script arguments.
This is the function called whenever someone sends a public or
private message addressed to the bot. (e.g. "bot: blah").
"""
try:
(options, args) = getopt.getopt(sys.argv[1:], 'hc:s:p:j:n:d:', ['help', 'config=', 'server=', 'port=', 'join=', 'nick=', 'database=', ])
except getopt.GetoptError as err:
print(err)
LolBot.usage()
if cmd == 'help':
self.reply(self.helptext, target)
elif cmd == 'urls' or cmd == 'list':
db = self.get_session()
for url in db.query(Url).order_by(Url.timestamp.desc())[:10]:
line = "%s %s" % (url.url, url.title)
self.reply(line, target)
time.sleep(1)
elif cmd.startswith('urls ') or cmd.startswith('list '):
db = self.get_session()
(listcmd, n) = cmd.split(" ", 1)
n = n.strip()
if n == "all":
rows = db.query(Url).order_by(Url.timestamp.desc())
elif n.find("-") > 0:
(x, y) = n.split("-", 1)
try:
x = abs(int(x))
y = abs(int(y))
if y < x:
x, y = y, x
except ValueError, ex:
self.reply("Give me a number or a range of numbers, e.g. list 5 or list 11-20", target)
raise ex
rows = db.query(Url).order_by(Url.timestamp.desc())[x-1:y]
else:
try:
n = abs(int(n))
except ValueError, ex:
self.reply("Give me a number or a range of numbers, e.g. list 5 or list 11-20", target)
raise ex
rows = db.query(Url).order_by(Url.timestamp.desc())[:n]
for url in rows:
line = "%s %s" % (url.url, url.title)
self.reply(line, target)
time.sleep(1)
elif cmd.startswith('http:') or cmd.startswith('https:'):
title = self.save_url(from_private, cmd)
if title == False:
self.say_public("Sorry, I'm useless at UTF-8.")
else:
self.reply('URL added. %s' % title, target)
else:
self.reply("I'm a bot of little brain. Try help for more information.", target)
except Exception, ex:
print "Exception caught processing command: %s" % ex
print " command was '%s' from %s" % (cmd, target)
self.reply("Sorry, I didn't understand: %s" % cmd, target)
self.reply(self.helptext, target)
class LolBotFactory(protocol.ClientFactory):
protocol = LolBot
def __init__(self, config_path):
self.config = get_config(config_path)
self.server = self.config['irc.server']
self.port = self.config['irc.port']
self.nickname = self.config['irc.nickname']
self.channel = self.config['irc.channel']
def clientConnectionLost(self, connector, reason):
print "Lost connection (%s), reconnecting." % (reason,)
connector.connect()
def clientConnectionFailed(self, connector, reason):
print "Could not connect: %s" % (reason,)
def get_options():
try:
(options, args) = getopt.getopt(sys.argv[1:], "hc:", ["help", "config=", ])
except getopt.GetoptError, err:
print str(err)
usage()
sys.exit(2)
config_path = ""
for option,value in options:
if option in ("-h", "--help"):
usage()
sys.exit(2)
if option in ("-c", "--config"):
config_path = value
return { 'config_path': config_path }
config = {}
for option, value in options:
# Display help text.
if option in ('-h', '--help'):
LolBot.usage()
sys.exit(2)
def get_config(config_path):
"""
This method loads configuration options from a lolbot.conf file. The file
should look like this:
# Get configuration from a file.
if option in ('-c', '--config'):
config = LolBot.load_config(value)
break
irc.server = irc.freenode.net
irc.port = 6667
irc.channel = #lolbottest
irc.nickname = lolbot
db.url = sqlite:///lolbot.db
# Individually specified settings.
if option in ('-s', '--server'):
config['irc.server'] = value
if option in ('-p', '--port'):
config['irc.port'] = value
if option in ('-j', '--join', '--channel', '--join-channel'):
config['irc.channel'] = value
if option in ('-n', '--nickname'):
config['irc.nickname'] = value
if option in ('-d', '--database'):
config['db.file'] = value
"""
return config
if not config_path:
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lolbot.conf")
if not os.path.exists(config_path):
print "Error: configuration file not found. By default lolbot will look for a lolbot.conf file in the same directory as the lolbot script, but you can override this by specifying a path on the command line with the --config option."
usage()
sys.exit(1)
@staticmethod
def load_config(config_path=''):
"""
This method loads configuration options from a lolbot.conf file. The file
should look something like this::
# open the configuration file and grab all param=value declarations.
config = {}
with open(config_path) as f:
for line in f:
# skip comments
if line.strip().startswith("#") or line.strip() == "":
continue
irc.server = irc.yourdomain.com
irc.port = 6667
irc.channel = #lolbottest
irc.nickname = lolbot
db.file = lolbot.db
"""
# collect up param = value
try:
(param, value) = line.strip().split("=", 1)
if param.strip() != "":
config[param.strip()] = value.strip()
except ValueError:
continue
if config_path == '':
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lolbot.conf')
if not os.path.exists(config_path):
print("Error: configuration file not found. By default lolbot will look for a lolbot.conf file in the same directory as the lolbot script, but you can override this by specifying a path on the command line with the --config option.")
LolBot.usage()
sys.exit(1)
# validate IRC host
if "irc.server" not in config.keys():
print "Error: the IRC server was not specified. Use --help for more information."
sys.exit(1)
# open the configuration file and grab all param=value declarations.
config = {}
with open(config_path) as f:
for line in f:
# skip comments
if line.strip().startswith('#') or line.strip() == '':
continue
# validate IRC port
if "irc.port" not in config.keys():
config["irc.port"] = "6667"
try:
config["irc.port"] = int(config["irc.port"])
except ValueError:
print "Error: the IRC port must be an integer. If not specified, lolbot will use the default IRC port value 6667. Use --help for more information."
sys.exit(1)
# collect up param = value
try:
(param, value) = line.strip().split('=', 1)
if param.strip() != '':
config[param.strip()] = value.strip()
except ValueError:
continue
# validate IRC channel
if "irc.channel" not in config.keys() or not config["irc.channel"].startswith("#"):
print "Error: the IRC channel is not specified or incorrect. It must begin with a # - e.g. #mychatchannel. Use --help for more information."
sys.exit(1)
return config
# validate bot nickname
if "irc.nickname" not in config.keys():
config["irc.nickname"] = "lolbot"
@staticmethod
def usage():
"""
Spits out CLI help.
"""
return config
print("""Run a lolbot.
def usage():
print """Run a lolbot.
-h, --help
This message.
-h, --help
This message.
-s, --server=<hostname>
The IRC server, e.g. irc.freenode.net
-c, --config=<filename>
Specify a configuration file. Defaults to lolbot.conf in the same
directory as the script.
-p, --port=<port>
The IRC server port. Default: 6667
Configuration:
-j, --join=<channel>
The chat channel to join, e.g. #trainspotting
irc.server = <host>
The IRC server, e.g. irc.freenode.net
-n, --nickname=<nickname>
The nickname for your lolbot. Default: "lolbot"
irc.port = <port>
The IRC server port. Default: 6667
-d, --database=<path>
The path to a SQLite lolbot database. If not specified, lolbot will
attempt to use a SQLite database called lolbot.db in the same
directory as the script.
irc.channel = <channel>
The chat channel to join, e.g. #trainspotting
Configuration file:
-c, --config=<filename>
Specify a configuration file. Ignores any options specified from the
command-line. Defaults to lolbot.conf in the same directory as the
script. File layout:
irc.server = <host>
irc.port = <port>
irc.channel = <channel>
irc.nickname = <nickname>
db.file = <path>
""".strip())
irc.nickname = <nickname>
The nickname for your lolbot. Default: "lolbot"
db.url = <url>
The URL to your lolbot database. This can be any valid URL accepted
by SQL Alchemy. If not specified, lolbot will attempt to use a
SQLite database called lolbot.db in the same directory as the
script.
"""
if __name__ == "__main__":
args = get_options()
config = get_config(args['config_path'])
try:
LolBot().start()
reactor.connectTCP(config['irc.server'], config['irc.port'], LolBotFactory(args['config_path']))
reactor.run()
except KeyboardInterrupt:
pass
print "Shutting down."

View file

@ -1,83 +0,0 @@
#! /usr/bin/env python
"""
Database models for lolbot.
"""
from __future__ import print_function, unicode_literals
import requests
from datetime import datetime
from bs4 import BeautifulSoup
from sqlalchemy import (Column, String, Text, Integer, DateTime)
from sqlalchemy.ext.declarative import (declarative_base)
Model = declarative_base()
class Log(Model):
"""
This class represents an event in the log table and inherits from a SQLAlchemy
convenience ORM class.
"""
__tablename__ = "log"
id = Column(Integer, primary_key=True)
timestamp = Column(DateTime)
nickname = Column(String(20))
text = Column(Text)
def __init__(self, nickname, text, timestamp=None):
"""
Creates an event log for the IRC logger.
"""
if timestamp is None:
timestamp = datetime.now()
self.timestamp = timestamp
self.nickname = nickname
self.text = text
def __repr__(self):
return "(%s) %s: %s" % (self.timestamp.strftime("%Y-%m-%d %H:%M:%S"), self.nickname, self.text)
class Url(Model):
"""
This class represents a saved URL and inherits from a SQLAlchemy convenience
ORM class.
"""
__tablename__ = "url"
id = Column(Integer, primary_key=True)
timestamp = Column(DateTime)
nickname = Column(String(20))
url = Column(String(200), unique=True)
title = Column(Text)
def __init__(self, nickname, url, title=None, timestamp=None):
if timestamp is None:
timestamp = datetime.now()
self.timestamp = timestamp
self.nickname = nickname
self.url = url
self.title = title
# populate the title from the URL if not given.
if title is None:
r = requests.get(url)
if r.status_code == 200:
if 'text/html' in r.headers['content-type']:
dom = BeautifulSoup(r.content, 'html.parser')
self.title = dom.title.string
else:
self.title = r.headers['content-type']
else:
self.title = "Error: HTTP %s" % r.status_code
def __repr__(self):
if not self.title:
return "%s: %s" % (self.nickname, self.url)
else:
return "%s: %s - %s" % (self.nickname, self.url, self.title)

View file

@ -1,300 +0,0 @@
#!/usr/bin/env python
## -*- coding: utf-8 -*-
"""
A MozQuizz question library for Python.
See http://moxquizz.de/ for the original implementation in TCL.
"""
from __future__ import unicode_literals, print_function
from io import open
import re
import sys
class Question:
"""
Represents one MoxQuizz question.
"""
category = None
"""
The question category. Arbitrary text; optional.
"""
question = None
"""
The question. Arbitrary text; required.
"""
answer = None
"""
The answer. Arbitrary text; required. Correct answers can also be covered
by the :attr:`regexp` property.
"""
regexp = None
"""
A regular expression that will generate correct answers. Optional. See
also the :attr:`answer` property.
"""
author = None
"""
The question author. Arbitrary text; optional.
"""
level = None # Default: NORMAL (constructor)
"""
The difficulty level. Value must be from the :attr:`LEVELS` tuple.
The default value is :attr:`NORMAL`.
"""
comment = None
"""
A comment. Arbitrary text; optional.
"""
score = 1
"""
The points scored for the correct answer. Integer value; default is 1.
"""
tip = list()
"""
An ordered list of tips (hints) to display to users. Optional.
"""
tipcycle = 0
"""
Indicates which tip is to be displayed next, if any.
"""
TRIVIAL = 1
"""
A value for :attr:`level` that indicates a question of trivial difficulty.
"""
EASY = 2
"""
A value for :attr:`level` that indicates a question of easy difficulty.
"""
NORMAL = 3
"""
A value for :attr:`level` that indicates a question of average or normal
difficulty.
"""
HARD = 4
"""
A value for :attr:`level` that indicates a question of hard difficulty.
"""
EXTREME = 5
"""
A value for :attr:`level` that indicates a question of extreme difficulty
or obscurity.
"""
LEVELS = (TRIVIAL, EASY, NORMAL, HARD, EXTREME)
"""
The available :attr:`level` difficulty values, :attr:`TRIVIAL`, :attr:`EASY`,
:attr:`NORMAL`, :attr:`HARD` and :attr:`EXTREME`.
"""
def __init__(self, attributes_dict):
"""
Constructor that takes a dictionary of MoxQuizz key-value pairs. Usually
called from a :class:`QuestionBank`.
"""
# Set defaults first.
self.level = self.NORMAL
self.parse(attributes_dict)
def parse(self, attributes_dict):
"""
Populate fields from a dictionary of attributes, usually provided by a
:class:`QuestionBank` :attr:`~QuestionBank.parse` call.
"""
## Valid keys:
# ----------
# Category? (should always be on top!)
# Question (should always stand after Category)
# Answer (will be matched if no regexp is provided)
# Regexp? (use UNIX-style expressions)
# Author? (the brain behind this question)
# Level? [baby|easy|normal|hard|extreme] (difficulty)
# Comment? (comment line)
# Score? [#] (credits for answering this question)
# Tip* (provide one or more hints)
# TipCycle? [#] (Specify number of generated tips)
if 'Question' in attributes_dict.keys():
self.question = attributes_dict['Question']
else:
raise Exception("Cannot instantiate Question: 'Question' attribute required.")
if 'Category' in attributes_dict.keys():
self.category = attributes_dict['Category']
if 'Answer' in attributes_dict.keys():
self.answer = attributes_dict['Answer']
else:
raise Exception("Cannot instantiate Question: 'Answer' attribute required.")
if 'Regexp' in attributes_dict.keys():
self.regexp = attributes_dict['Regexp']
if 'Author' in attributes_dict.keys():
self.category = attributes_dict['Author']
if 'Level' in attributes_dict.keys() and attributes_dict['Level'] in self.LEVELS:
self.level = attributes_dict['Level']
elif 'Level' in attributes_dict.keys() and attributes_dict['Level'] in QuestionBank.LEVEL_VALUES.keys():
self.level = QuestionBank.LEVEL_VALUES[attributes_dict['Level']]
if 'Comment' in attributes_dict.keys():
self.comment = attributes_dict['Comment']
if 'Score' in attributes_dict.keys():
self.score = attributes_dict['Score']
if 'Tip' in attributes_dict.keys():
self.tip = attributes_dict['Tip']
if 'Tipcycle' in attributes_dict.keys():
self.tipcycle = attributes_dict['Tipcycle']
def attempt(self, answer):
return (self.answer is not None and self.answer.lower() == answer.lower()) or (
self.regexp is not None and re.search(self.regexp, answer, re.IGNORECASE) is not None)
class QuestionBank:
"""
Represents a MoxQuizz question bank.
"""
filename = ''
"""
The path or filename of the question bank file.
"""
questions = list()
"""
A list of :class:`Question` objects, constituting the questions in the
question bank.
"""
# Case sensitive, to remain backwards-compatible with MoxQuizz.
KEYS = ('Answer',
'Author',
'Category',
'Comment',
'Level',
'Question',
'Regexp',
'Score',
'Tip',
'Tipcycle',
)
"""
The valid attributes available in a MoxQuizz question bank file.
"""
LEVEL_VALUES = {
'trivial': Question.TRIVIAL,
'baby': Question.TRIVIAL,
'easy': Question.EASY,
'normal': Question.NORMAL,
'hard': Question.HARD,
'difficult': Question.HARD,
'extreme': Question.EXTREME
}
"""
Text labels for the :attr:`Question.level` difficulty values.
"""
def __init__(self, filename):
"""
Constructor, takes a MozQuizz-formatted question bank filename.
"""
self.filename = filename
self.questions = self.parse(filename)
def parse(self, filename):
"""
Read a MoxQuizz-formatted question bank file. Returns a ``list`` of
:class:`Question` objects found in the file.
"""
questions = list()
with open(filename) as f:
key = ''
i = 0
# new question
q = dict()
q['Tip'] = list()
for line in f:
line = line.strip()
i += 1
# Ignore comments.
if line.startswith('#'):
continue
# A blank line starts a new question.
if line == '':
# Store the previous question, if valid.
if 'Question' in q.keys() and 'Answer' in q.keys():
question = Question(q)
questions.append(question)
# Start a new question.
q = dict()
q['Tip'] = list()
continue
# Fetch the next parameter.
try:
(key, value) = line.split(':', 1)
key = key.strip()
value = value.strip()
except ValueError:
print("Unexpected weirdness in MoxQuizz questionbank '%s', line %s." % (self.filename, i))
continue
# break # TODO: is it appropriate to bail on broken bank files?
# Ignore bad parameters.
if key not in self.KEYS:
print("Unexpected key '%s' in MoxQuizz questionbank '%s', line %s." % (key, self.filename, i))
continue
# Enumerate the Tips.
if key == 'Tip':
q['Tip'].append(value.strip())
elif key == 'Level':
if value not in self.LEVEL_VALUES:
print("Unexpected Level value '%s' in MoxQuizz questionbank '%s', line '%s'." % (value, self.filename, i))
else:
q['Level'] = self.LEVEL_VALUES[value]
else:
q[key] = value.strip()
return questions
# A crappy test.
if __name__ == '__main__':
qb = QuestionBank('questions/questions.doctorlard.en')
for q in qb.questions:
print(q.question)
if sys.version.startswith('2'):
a = unicode(raw_input('A: '), 'utf8')
else:
a = input('A: ')
if q.attempt(a):
print("Correct!")
else:
print("Incorrect - the answer is '%s'" % q.answer)

View file

@ -1,175 +0,0 @@
########################################################
# DoctorLard's collection of random Moxquizz questions
########################################################
## Demo-Entry:
# ----------
# Category: History
# Question: Chinese philosopher (um 500 v. Chr.) ?
# Answer: Konfuzius
# Regexp: [ck]onfu(ts|z)ius
# Author: anonymous
# Level: hard
# Comment: demo-entry, ÄÖÜäöüß ^° !"§$%&/()=? `Ž
# Score: 5
# Tip: Kon......
# Tip: ...fuz...
# Tip: ......ius
## valid keys:
# ----------
# Category? (should always be on top!)
# Question (should always stand after Category)
# Answer (will be matched if no regexp is provided)
# Regexp? (use UNIX-style expressions)
# Author? (the brain behind this question)
# Level? [baby|easy|normal|hard|extreme] (difficulty)
# Comment? (comment line)
# Score? [#] (credits for answering this question)
# Tip* (provide one or more hints)
# TipCycle? [#] (Specify number of generated tips)
## WARNING:
# -------
# DO NOT ADD COMMENTS BEYOND THIS LINE, since they might get lost.
# you may add as much comments as you want into the part above
Category: Units
Question: What does the SI unit weber (Wb) measure?
Tip: m....... f...
Answer: magnetic flux
Author: DoctorLard
Category: Units
Question: Four gills in a ....?
Tip: It's an Imperial unit.
Tip: You might ask for one at the pub.
Tip: p...
Answer: pint
Author: DoctorLard
Category: Units
Question: Ten chains in a ....?
Tip: It's an Imperial unit of length.
Tip: f......
Answer: furlong
Author: DoctorLard
Category: Units
Question: Four .... in a chain?
Tip: An obscure Imperial unit of length.
Tip: Equal to 5½ yards.
Tip: r..
RegExp: (rod|pole|perch)
Answer: Rod (or Pole, or Perch)
Author: DoctorLard
Category: Units
Question: Eight .... in a mile?
Tip: An Imperial unit of length.
Tip: Equal to 220 yards.
Tip: f......
Answer: Furlong
Author: DoctorLard
Category: Units
Question: A perch is unit of area equivalent to a square pole. A pole is another name for a rod, and there are ten chains to a furlong and four rods to a chain. A rood is a quarter acre, and there are 160 perches to an acre. If a property area is one rood and half a furlong on one side, how long is the other side, in rods?
Tip: Less than you might think.
Tip: Whatever. Google won't help you either.
Level: hard
Answer: 2
Author: DoctorLard
Question: How many pennies in a half crown?
Answer: 30
Author: DoctorLard
Category: Units
Question: What is the old English surveying unit of area, defined as one furlong by one rod?
Tip: There are four of them to an acre.
Tip: r...
Answer: rood
Author: DoctorLard
Category: Units
Question: What is the SI prefix that denotes a multiplier of ten to the negative eighteenth power (10¯¹⁸)?
Tip: It's used really really small quantities.
Tip: a...
Answer: atto
Author: DoctorLard
Category: Temporal anomalies
Question: How many times do the minute and hour hands on a clockface overlap in a 24 hour period?
Answer: 22
Author: DoctorLard
Category: xG
Question: Which country did hell- visit in August 2014?
Answer: USA
Author: DoctorLard
Category: Music
Question: A traditional Arabian and Turkish string instrument, distinguished from the lute by having no frets.
Answer: oud
Author: Bill Bailey
Category: Pharmacology
Question: In Breaking Bad, Walter and Jesse used a "P2P cook" process to make very pure methamphetamine (by reduction with methylamine over aluminium). What does P2P stand for?
Answer: phenyl-2-propanone
Author: DoctorLard
Category: General ignorance
Question: What are igloos normally made of?
Answer: animal skins
Regexp: (animal )?skins
Tip: It isn't snow.
Tip: It isn't any form of frozen water.
Author: DoctorLard
Category: New Zealand
Question: In what year did New Zealand become self-governing (by gaining its own representative government, separate from the United Kingdom)?
Answer: 1852
Author: DoctorLard
Category: Geography
Question: How many of the Commonwealth of Nations' member states are Realms?
Answer: 16
Level: hard
Author: DoctorLard
Category: Sculpture
Question: Which Lord of the Rings actor, in an interview with Jay Leno, admitted to urinating in Wellington's Bucket Fountain sculpture while filming in New Zealand?
Answer: Elijah Wood
Author: DoctorLard
Category: TV
Question: Which British comedy actor played Richmond in The IT Crowd, and Vincent Noir in The Mighty Boosh?
Answer: Noel Fielding
Author: DoctorLard
Category: Video Games
Question: Who composed the music for the Elder Scrolls and Guild Wars games, thinks he's God's gift to composers, and takes fucking ages to ship CDs of his soundtracks?
Answer: Jeremy Soule
Author: DoctorLard
Category: Video Games
Question: In the Guild Wars world of Tyria, name the once great human kingdom, laid waste by the Charr?
Answer: Ascalon
Author: DoctorLard
Category: Video Games
Question: In Mass Effect 2 and 3, which Oscar-nominated American actor provided the voice for The Illusive Man?
Answer: Martin Sheen
Author: DoctorLard
Category: Cuisine
Question: Which spice is made from the dried and ground aril that surrounds a nutmeg (Myristica fragrans) seed?
Answer: Mace
Author: DoctorLard
Category: Cuisine
Question: What is the main ingredient of borscht?
Answer: beetroot
Author: DoctorLard

View file

@ -1,182 +0,0 @@
# ------------------------------------------------------------------------------
#
# Author: Lee Clarke, 1st April 2002
# Editor:
# Last edited: 2002/07/05 19:09:11
#
# Comment: many mixed quiz questions, Authors nick removed on request
#
# ------------------------------------------------------------------------------
## Demo-Entry:
# ----------
# Category: History
# Question: Chinese philosopher (um 500 v. Chr.) ?
# Answer: Konfuzius
# Regexp: [ck]onfu(ts|z)ius
# Author: anonymous
# Level: hard
# Comment: demo-entry, ÄÖÜäöüß ^° !"§$%&/()=? `Ž
# Score: 5
# Tip: Kon......
# Tip: ...fuz...
# Tip: ......ius
## valid keys:
# ----------
# Category? (should always be on top!)
# Question (should always stand after Category)
# Answer (will be matched if no regexp is provided)
# Regexp? (use UNIX-style expressions)
# Author? (the brain behind this question)
# Level? [baby|easy|normal|hard|extreme] (difficulty)
# Comment? (comment line)
# Score? [#] (credits for answering this question)
# Tip* (provide one or more hints)
# TipCycle? [#] (Specify number of generated tips)
## WARNING:
# -------
# DO NOT ADD COMMENTS BEYOND THIS LINE, since they might get lost.
# you may add as much comments as you want into the part above
# <!========================================================!>
Category: Animals
Question: What is the only animal, other than a human that can catch leprosy?
Answer: The Armadillo
Category: Computer Games
Question: Which legendary games designer/producer created the Super Mario Brothers franchise for Nintendo?
Answer: Shigeru Miyamoto
Category: General
Question: Aesthetic principles derived from the literature and art of ancient Greece & Rome, but found as ideals in all ages is called?
Answer: Classicism
Author: DonkeyTron
Category: General
Question: North American Indian language family including languages of Alaska and north-western Canada, the Pacific coast and the south-western United States.
Answer: Athapascan
Author: DonkeyTron
Category: General
Question: What is the literal translation of the Latin word 'video'?
Answer: I See
Author: DonkeyTron
Category: General
Question: Which 1946 Louis Reard creation, was nicknamed "four triangles of nothing"?
Answer: The Bikini
Author: DonkeyTron
Category: General
Question: Who is the supreme head of the Church Of England?
Answer: Queen Elizabeth II
Category: General
Question: Whose picture was on the first adhesive nickel postage stamp in the US?
Answer: Benjamin Franklin
Author: DonkeyTron
Category: General
Question: One of the twelve members of the administrative council of the Mormon church, is called a(n) .......?
Answer: Apostle
Author: DonkeyTron
Category: General
Question: The process of adding a number to itself a certain number of times is called?
Answer: Multiplication
Author: DonkeyTron
Category: Literature
Question: Which novel by Mary Shelley was subtitled 'The Modern Prometheus'?
Answer: Frankenstein
Author: DonkeyTron
Category: Magazines
Question: Which magazine always features an obituary on its last page?
Answer: Rolling Stone
Author: DonkeyTron
Category: Magazines
Question: Who was the first male to appear on the cover of Playboy in 1964?
Answer: Peter Sellers
Author: DonkeyTron
Category: Movies
Question: In which movie did Bruce Willis play the role of Corben Dallas?
Answer: The Fifth Element
Category: Movies
Question: What does Steven Spielberg try to include in every film he makes, claiming it is for luck?
Answer: Scene With A Shooting Star
Author: DonkeyTron
Category: Movies
Question: Which actor played the role of Chewbacca in the Star Wars trilogy?
Answer: Peter Mayhew
Author: DonkeyTron
Category: Movies
Question: Which actress won the 2002 Academy Award for best actress in a leading role, for her part in the movie, 'Monster's Ball'?
Answer: Halle Berry
Author: DonkeyTron
Category: Music
Question: The theme tune for 'Monty Python's Flying Circus' was written by which composer?
Answer: John Philip Sousa
Author: DonkeyTron
Category: Music
Question: What was the working title for The Beatles' song, 'Yesterday'?
Answer: Scrambled Eggs
Author: DonkeyTron
Category: Music
Question: Which native of Flint, Michigan, once advised us to "drive your Chevrolet through the USA"?
Answer: Pat Boone
Author: DonkeyTron
Category: Quotes
Question: Which tennis player , when asked if he had learned anything from his US Open loss said, "Yeah...I learned I needed to lose 15 pounds"?
Answer: Andre Agassi
Author: DonkeyTron
Category: Quotes
Question: Who said, "People don't credit me with much of a brain, so why should I disillusion them."?
Answer: Sylvester Stallone
Author: DonkeyTron
Category: Soccer
Question: Which English Premier League team plays their home games at Elland Road?
Answer: Leeds United
Author: DonkeyTron
Category: Sports
Question: According to Olympic rules, what number of feathers must a badminton bird (shuttlecock) have?
Answer: Fourteen
Author: DonkeyTron
Category: Sports
Question: What is the name of the oldest National Hockey League team in the US?
Answer: The Boston Bruins
Author: DonkeyTron
Category: Star Trek
Question: What is Mr Spock's pulse rate?
Answer: 242 Beats Per Minute
Author: DonkeyTron
Category: TV
Question: What is the address of The Munsters?
Answer: 1313 Mockingbird Lane
Author: DonkeyTron
Category: The Simpsons
Question: Who has starred in such movies as 'The Erotic Adventures Of Hercules' & 'Dial M For Murderousness'?
Answer: Troy McClure
Category: Weights & Measures
Question: The liquid measure of a capacity equal to 1/8 of a fluid ounce is a(n) ..........?
Answer: Fluid Dram
Author: DonkeyTron

File diff suppressed because it is too large Load diff

View file

@ -1,618 +0,0 @@
# ------------------------------------------------------------------------------
#
# Author: OllyPomm
# Editor: stanstime
# Last edited: 2002/07/05 19:09:10
#
# Comment: OllyPomm has submitted about 200 questions with more to come
#
# ------------------------------------------------------------------------------
## Demo-Entry:
# ----------
# Category: History
# Question: Chinese philosopher (um 500 v. Chr.) ?
# Answer: Konfuzius
# Regexp: [ck]onfu(ts|z)ius
# Author: anonymous
# Level: hard
# Comment: demo-entry, ÄÖÜäöüß ^° !"§$%&/()=? `Ž
# Score: 5
# Tip: Kon......
# Tip: ...fuz...
# Tip: ......ius
## valid keys:
# ----------
# Category? (should always be on top!)
# Question (should always stand after Category)
# Answer (will be matched if no regexp is provided)
# Regexp? (use UNIX-style expressions)
# Author? (the brain behind this question)
# Level? [baby|easy|normal|hard|extreme] (difficulty)
# Comment? (comment line)
# Score? [#] (credits for answering this question)
# Tip* (provide one or more hints)
# TipCycle? [#] (Specify number of generated tips)
## WARNING:
# -------
# DO NOT ADD COMMENTS BEYOND THIS LINE, since they might get lost.
# you may add as much comments as you want into the part above
# <!========================================================!>
Category: Animals
Question: An unusual feature of the wombats pouch is that it .......?
Answer: faces backwards
Author: OllyPomm
Category: Animals
Question: How many nipples does an echidna have?
Answer: None
Regexp: (None|0|Zero|Nill)
Author: OllyPomm
Category: Animals
Question: The thylacine (considered extinct) is more commonly know as the?
Answer: Tasmanian Tiger
Author: OllyPomm
Category: Animals
Question: What is a monotreme?
Answer: An egg laying mammal
Author: OllyPomm
Category: Morse Code
Question: What letter of the alphabet does 'dash dash' represent?
Answer: M
Author: OllyPomm
Category: Morse Code
Question: What letter of the alphabet does 'dash dot' represent?
Answer: N
Author: OllyPomm
Category: Morse Code
Question: What letter of the alphabet does 'dash dash dot' represent?
Answer: G
Author: OllyPomm
Category: Morse Code
Question: What letter of the alphabet does 'dash dash dot dot' represent?
Answer: Z
Author: OllyPomm
Category: Morse Code
Question: What letter of the alphabet does 'dot dot' represent?
Answer: I
Author: OllyPomm
Category: Sport
Question: The Melbourne Cup horse race is held on the first ------- in November.?
Answer: Tuesday
Author: OllyPomm
Category: Sport
Question: Over what distance is the Melbourne Cup horse race ran (in metres)?
Answer: 3200
Author: OllyPomm
Category: Pot Pourri
Question: How many weeks until Christmas Day if it is the 25th September?
Answer: 13 weeks
Author: OllyPomm
Category: Sport
Question: What famous race was established in 1903?
Answer: The Tour de France
Author: OllyPomm
Category: History
Question: In 1893, this country was the first to give women the vote.?
Answer: New Zealand
Author: OllyPomm
Category: Metals
Question: The largest pure gold nugget (weighing 70.92kg) ever discovered was called...?
Answer: The Welcome Stranger
Regexp: (The )?Welcome Stranger
Author: OllyPomm
Category: Medicine
Question: In 1665 the first ----- ----------- was performed by Dr Richard Lower.?
Answer: Blood Transfusion
Author: OllyPomm
Category: Pot Pourri
Question: If it's 12.00 noon GMT - what time is it in Sydney, Australia?
Answer: 10.00pm
Author: OllyPomm
Category: History
Question: Considered to be the last full blooded Tasmanian aborigine, (died 1876 - aged 73), her name is.....?
Answer: Truganini
Author: OllyPomm
Category: Olympics
Question: In what city is the Olympic torch first lit?
Answer: Olympia
Author: OllyPomm
Category: Olympics
Question: What is the latin Olympic motto?
Answer: Citius Altius Fortius
Author: OllyPomm
Category: Olympics
Question: What does the latin Olympic motto - Citius Altius Fortius stand for?
Answer: Faster Higher Stronger
Author: OllyPomm
Category: Olympics
Question: What are the colours of the Olympics rings (in order).?
Answer: Blue Yellow Black Green Red
Author: OllyPomm
Category: Olympics
Question: How many mascots did the Sydney Olympics have?
Answer: 3
Regexp: (3|Three)
Author: OllyPomm
Category: Olympics
Question: The Sydney Olympics had 3 mascots. What type of animals were they?
Answer: Kookaburra Echidna Platypus
Author: OllyPomm
Category: Olympics
Question: What were the names of the 3 mascots at the Sydney Olympic Games?
Answer: Ollie Millie Syd
Author: OllyPomm
Category: Quotes
Question: Who said "Come up and see me sometime"?
Answer: Mae West
Author: OllyPomm
Category: Quotes
Question: Who said "Tha tha that's all folks"?
Answer: Porky Pig
Author: OllyPomm
Category: Quotes
Question: Who said "Go ahead - make my day" (character name)?
Answer: Harry Callaghan
Author: OllyPomm
Category: Sport
Question: What sport do the following terms belong to - "Tight End & Wide Receiver"?
Answer: #American Football# (Gridiron)
Author: OllyPomm
Category: Sport
Question: What sport do the following terms belong to - "Pull & Lolly"?
Answer: Cricket
Author: OllyPomm
Category: Sport
Question: What sport do the following terms belong to - "Jerk & Snatch"?
Answer: Weightlifting
Author: OllyPomm
Category: Sport
Question: What sport do the following terms belong to - "Hotdog & Bottom Trun"?
Answer: Surfing
Author: OllyPomm
Category: Sport
Question: What sport do the following terms belong to - "Toucher & Dead Length"?
Answer: Lawn or Indoor Bowls
Regexp: (Lawn)?(Indoor Bowls)?
Author: OllyPomm
Category: Sport
Question: What sport do the following terms belong to - "Pist & Telemark"?
Answer: Skiing
Author: OllyPomm
Category: Sport
Question: What sport do the following terms belong to - "Touches & Lunges"?
Answer: Fencing
Author: OllyPomm
Category: Sport
Question: What sport do the following terms belong to - "Behind & Banana Kick"?
Answer: Australian Football League
Author: OllyPomm
Category: Sport
Question: What sport do the following terms belong to - "Sweeper & Advantage Rule"?
Answer: Soccer
Author: OllyPomm
Category: Astronomy
Question: How many stars make up the 'Southern Cross'?
Answer: 5
Regexp: (5|Five)
Author: OllyPomm
Category: Astronomy
Question: How many Earths would it take to match the size of the sun? Over 1000, 100,000 or 1,000,000?
Answer: 1,000,000
Author: OllyPomm
Category: Astronomy
Question: What planet position is the Earth from the Sun?
Answer: 3rd
Author: OllyPomm
Category: Astronomy
Question: How long does it take for Earth to travel around the Sun?
Answer: 1 Year
Author: OllyPomm
Category: Astronomy
Question: The Moon orbits around the Earth -Tue or False.?
Answer: True
Author: OllyPomm
Category: Famous Aliases
Question: By what name is Maurice Micklewhite better known as?
Answer: Michael Caine
Author: OllyPomm
Category: Famous Aliases
Question: By what name is Arthur Leech better known as?
Answer: Cary Grant
Author: OllyPomm
Category: Famous Aliases
Question: By what name is Robert Zimmerman better known as?
Answer: Bob Dylan
Author: OllyPomm
Category: Famous Aliases
Question: By what name is Francis Gumm better known as?
Answer: Judy Garland
Author: OllyPomm
Category: Famous Aliases
Question: By what name is Richard Starkey better known as?
Answer: Ringo Starr
Author: OllyPomm
Category: Famous Aliases
Question: By what name is Allan Stewart Konisburg better known as?
Answer: Woody Allan
Author: OllyPomm
Category: Famous Aliases
Question: By what name is Marion Morrison better known as?
Answer: John Wayne
Author: OllyPomm
Category: Famous Aliases
Question: By what name is Reginald Dwight better known as?
Answer: Elton John
Author: OllyPomm
Category: Famous Aliases
Question: By what name is Norma Jean Baker better known as?
Answer: Marilyn Monroe
Author: OllyPomm
Category: Food & Drink
Question: What is the main ingredient in the soup Borscht?
Answer: Beetroot
Author: OllyPomm
Category: General
Question: How many axles does an 18 wheeler truck have?
Answer: 5
Regexp: (5|Five)
Author: OllyPomm
Category: Animals
Question: What bird has the biggest wingspan?
Answer: Albatross
Author: OllyPomm
Category: Animals
Question: What do you call a group of rhinocerus?
Answer: A Crash
Author: OllyPomm
Category: Geography
Question: What country is Mt Etna in?
Answer: Italy
Author: OllyPomm
Category: Plants
Question: What genus of tree does the wattle belong?
Answer: Acacia
Author: OllyPomm
Category: Plants
Question: What do you offer someone as a peace gesture?
Answer: An Olive Branch
Author: OllyPomm
Category: Plants
Question: What word describes part of your hand and a type of tree?
Answer: Palm
Author: OllyPomm
Category: Plants
Question: What is the name given to the art of miniaturising trees and maintaining their small size?
Answer: Bonsai
Author: OllyPomm
Category: Plants
Question: What part of a rhubarb plant is poisonous?
Answer: The Leaves
Author: OllyPomm
Category: Flags
Question: What are the 2 background colours of the Welsh flag?
Answer: Green and White
Author: OllyPomm
Category: Flags
Question: What creature is depicted on the Welsh Flag?
Answer: A Red Dragon
Author: OllyPomm
Category: Emblems
Question: What is the flower emblem of Wales?
Answer: Daffodil
Author: OllyPomm
Category: Saints
Question: Who is the patron saint of Wales?
Answer: St David
Author: OllyPomm
Category: Food & Drink
Question: What nut is used to make marzipan?
Answer: Almond
Author: OllyPomm
Category: Flags
Question: What 3 colours are featured on the Australian Aboriginal flag?
Answer: Black Red Yellow
Author: OllyPomm
Category: Music
Question: What other well known singer shares the same birthday as Elvis Presley (Jan 8)?
Answer: David Bowie
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Add It Up, Blister In The Sun, Kiss Off"?
Answer: Violet Femmes
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Baby I Don't Care, I Want Your Love"?
Answer: Transvision Vamp
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Strange Brew, White Room"?
Answer: Cream
Author: OllyPomm
Category: Anniversaries
Question: What gift is associated with the 60th Wedding Anniversary?
Answer: Diamonds
Author: OllyPomm
Category: Anniversaries
Question: What gift is associated with the 20th Wedding Anniversary?
Answer: China
Author: OllyPomm
Category: Anniversaries
Question: What gift is associated with the 30th Wedding Anniversary?
Answer: Pearls
Author: OllyPomm
Category: Anniversaries
Question: What gift is associated with the 40th Wedding Anniversary?
Answer: Rubies
Author: OllyPomm
Category: General
Question: Bowline, Figure Eight & Square are all types of what?
Answer: Knots
Author: OllyPomm
Category: Honours & Awards
Question: What does OBE stand for?
Answer: Officer of the Order of the British Empire
Author: OllyPomm
Category: Dates
Question: What is celebrated on April 1st?
Answer: April Fools Day
Author: OllyPomm
Category: Dates
Question: What famous playwrite has his birthday on 23rd April?
Answer: William Shakespeare
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 17th May?
Answer: Norway
Author: OllyPomm
Category: Dates
Question: What famous battle was fought on 4th June 1942?
Answer: The Battle of Midway
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 6th June?
Answer: Sweden
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 2nd June?
Answer: Italy
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 17th June?
Answer: Iceland
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 25th June?
Answer: Slovenia
Author: OllyPomm
Category: Dates
Question: What was signed on 15th June 1215?
Answer: The Magna Carta
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 14th July?
Answer: France
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 21st July?
Answer: Belgium
Author: OllyPomm
Category: Date
Question: This date is the date in the middle of the year.?
Answer: 2nd July
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 25th August?
Answer: Uruguay
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 29th October?
Answer: Turkey
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 12th October?
Answer: Spain
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 26th October?
Answer: Austria
Author: OllyPomm
Category: Flags
Question: What 3 flags does the Union Jack comprise of?
Answer: England Scotland Ireland
Author: OllyPomm
Category: Dates
Question: What country celebrates its National Day on 29th October?
Answer: Turkey
Author: OllyPomm
Category: Dates
Question: What country celebrated its National Day on 1st March?
Answer: Wales
Author: OllyPomm
Category: Dates
Question: What country celebrated its National Day on 15th March?
Answer: Hungary
Author: OllyPomm
Category: Dates
Question: What well known bard (poems & songs) was born on 25th January 1759?
Answer: Robert Burns
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Aqualung, Thick as a Brick"?
Answer: Jethro Tull
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Mystify, Listen Like Thieves, Original Sin"?
Answer: INXS
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Heart of Glass, The Tide is High"?
Answer: Blondie
Author: OllyPomm
Category: Music
Question: Name the singer - songs include "Me & Bobby McGee, Mercedes Benz"?
Answer: Janis Joplin
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Black Night, Smoke On The Water"?
Answer: Deep Purple
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Monday Monday, California Dreamin'"?
Answer: The Mamas and the Papas
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Light My Fire, Love Her Madly"?
Answer: The Doors
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Let's Stick Together, The Price of Love"?
Answer: Bryan Ferry
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Psycho Killer, Road To Nowhere"?
Answer: Talkingheads
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Get Down & Get With It, Mama We're All Crazy Now"?
Answer: Slade
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Forgiven Not Forgotten, Runaway"?
Answer: The Coors
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Doctor Doctor, Hold Me Now, Don't Mess With Dr Dream"?
Answer: Thompson Twins
Author: OllyPomm
Category: Music
Question: Name the band - songs include "Sex & Drugs & Rock & Roll, I Want To Be Straight"?
Answer: Ian Drury and The Blockheads
Author: OllyPomm
Category: Music
Question: A graphical representation of the guitar fingerboard, used to teach someone to play a guitar without actually learning how to read musical notes.?
Answer: Tablature
Author: OllyPomm
Category: Riddles?
Question: How do you name 3 consecutive days without saying Monday, Tuesday, Wednesday, Thursday, Friday, Saturday or Sunday?
Answer: Yesterday Today and Tomorrow
Author: OllyPomm
Category: Riddles?
Question: What was the highest mountain before Mt Everest was discovered?
Answer: Mt Everest
Author: OllyPomm

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +0,0 @@
SQLAlchemy==1.0.9
beautifulsoup4==4.4.1
irc==13.2
requests==2.8.1

22
runbot
View file

@ -1,22 +0,0 @@
#!/bin/sh
PATH=/bin:/usr/bin
NOW=`date +%Y-%m-%d_%H%M`
if [ "x$1" = "x" ]; then
CONFIG=""
else
CONFIG="--config $1"
fi
LOLBOTS=`ps -ef|grep 'lolbot.py'|grep -v grep|awk '{print $2}'`
if [ "$LOLBOTS" != "" ]; then
kill $LOLBOTS
fi
cd `dirname $0`
APP_PATH=`pwd`
while true; do
/usr/bin/python $APP_PATH/lolbot.py $CONFIG > logs/server-$NOW.log 2> logs/error-$NOW.log
done