diff --git a/.config/systemd/user/banterbot.service b/.config/systemd/user/banterbot.service
index 112e910..0b43f1a 100755
--- a/.config/systemd/user/banterbot.service
+++ b/.config/systemd/user/banterbot.service
@@ -4,7 +4,7 @@ After=banterbot.service
[Service]
Type=simple
-ExecStart=/home/krowbar/Code/irc/banterbot.py -s 127.0.0.1 -p 6667 -n banterbot -c #tildetown #bots
+ExecStart=/home/krowbar/Code/irc/bot_launcher.py -s 127.0.0.1 -p 6667 -n banterbot -c #tildetown #bots
WorkingDirectory=/home/krowbar/Code/irc/
Restart=always
RestartSec=5
diff --git a/.config/systemd/user/tildebot.service b/.config/systemd/user/tildebot.service
index f16f0da..f537579 100755
--- a/.config/systemd/user/tildebot.service
+++ b/.config/systemd/user/tildebot.service
@@ -4,7 +4,7 @@ After=tildebot.service
[Service]
Type=simple
-ExecStart=/home/krowbar/Code/irc/tildebot.py -s 127.0.0.1:6667 -n tildebot -c #bots
+ExecStart=/home/krowbar/Code/irc/bot_launcher.py -n tildebot -s 127.0.0.1 -p 6667 -c #bots
WorkingDirectory=/home/krowbar/Code/irc/
Restart=always
RestartSec=5
diff --git a/.config/systemd/user/topicbot.service b/.config/systemd/user/topicbot.service
index 99c5784..101cdc6 100755
--- a/.config/systemd/user/topicbot.service
+++ b/.config/systemd/user/topicbot.service
@@ -4,7 +4,7 @@ After=topicbot.service
[Service]
Type=simple
-ExecStart=/home/krowbar/Code/irc/topicbot.py -s 127.0.0.1:6667 -n topicbot -c #tildetown #bots
+ExecStart=/home/krowbar/Code/irc/banterbot.py -s 127.0.0.1 -p 6667 -n topicbot -c #bot_test
WorkingDirectory=/home/krowbar/Code/irc/
Restart=always
RestartSec=5
diff --git a/.tracery/aphorism b/.tracery/aphorism
index 49c8046..9cfd111 100644
--- a/.tracery/aphorism
+++ b/.tracery/aphorism
@@ -7,8 +7,8 @@
"#animal.a# in the #bodypart# is #preposition# #number# in the #plant#",
"#noun.a# is only as #adjective# as its #comparative# #noun#",
"#animal.a# is #person.a#'s #comparative# #relation#",
- "#person.ae# and #pronoun_possessive# #money# are #adverb# #verb#ed",
- "#adjective.a# #noun#is the of #comparative# #noun.s#",
+ "#person.a# and #pronoun_possessive# #money# are #adverb# #verb#ed",
+ "#adjective.a# #noun# is the #noun# of #comparative# #noun.s#",
"#person.a# in #verb# is #person.a# #adverb#",
"#comparative.a# #noun# makes #comparative.a# #noun#",
"#person.a# of #adjective# #noun.s# is #person# of #adjective#",
diff --git a/Code/irc/acronymFinder.py b/Code/irc/acronymFinder.py
deleted file mode 100644
index 1b83c92..0000000
--- a/Code/irc/acronymFinder.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/python3
-import urllib
-from bs4 import BeautifulSoup
-import random
-import string
-
-dict = "/usr/share/dict/american-english"
-(userId, token) = open("/home/krowbar/.secret/s4token").readline().rstrip().split(",")
-
-
-def get_acros(word, silly, short):
- acros = []
- url = "http://www.stands4.com/services/v2/abbr.php?uid={}&tokenid={}&term={}".format(
- userId, token, word
- )
- soup = BeautifulSoup(urllib.request.urlopen(url).read(), "html5lib")
- results = soup.find_all("result")
- # there are lots of cases where the same definition is repeated multiple times under different categories. this is dumb so we should do a little more work
- defs = []
- for r in results:
- rdef = r.find("definition").text
- match = next((x for x in defs if x["definition"].lower() == rdef.lower()), None)
- if match is not None:
- # if we find a match, add the category to the existing categories and increase the score
- match["categories"].append(
- (
- (r.find("parentcategoryname").text + "\\")
- if r.find("parentcategoryname")
- else ""
- )
- + r.find("categoryname").text
- )
- match["score"] = str(float(match["score"]) + float(r.find("score").text))
- else: # add a new item
- defs.append(
- {
- "term": r.find("term").text,
- "definition": r.find("definition").text,
- "categories": [
- (
- (r.find("parentcategoryname").text + "\\")
- if r.find("parentcategoryname")
- else ""
- )
- + r.find("categoryname").text
- ],
- "score": r.find("score").text,
- }
- )
-
- for d in sorted(defs, key=lambda x: float(x["score"]), reverse=True):
- # print d;
- if short is True:
- acros.append('"%s"' % d["definition"])
- else:
- acros.append(
- (
- '{}: "{}" ({}, score: {})'.format(
- d["term"],
- d["definition"],
- ", ".join(d["categories"]),
- d["score"],
- )
- )
- )
- if silly is True:
- newDef = []
- words = open(dict, "r").readlines()
- try:
- for idx, letter in enumerate(word):
- newWord = random.choice(
- list(filter(
- lambda w: (idx is 0 or not w.strip().lower().endswith("'s"))
- and w.lower().startswith(letter.lower()),
- words,
- ))
- ).strip()
- print(str(idx) + " -> " + newWord)
- newDef.append(newWord)
- newWord = string.capwords(" ".join(newDef))
- if short is True:
- acros.append('"%s"' % newWord)
- else:
- acros.append(
- (
- '{}: "{}" ({}, score: {})'.format(
- word.upper(), newWord, "Tilde.town Original", "0"
- )
- )
- )
- except IndexError:
- acros.append("Future hazy, try again later: Tilde.town Error")
- if short is True:
- shortList = acros[0:5]
- if len(acros) > 5:
- shortList.append(acros[-1])
- return [word.upper() + ": " + ", ".join(shortList)]
- else:
- return acros
diff --git a/Code/irc/banterbot.py b/Code/irc/banterbot.py
deleted file mode 100755
index 380ccf0..0000000
--- a/Code/irc/banterbot.py
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/python3
-
-import argparse
-from pinhook.bot import Bot
-
-parser = argparse.ArgumentParser()
-
-parser.add_argument(
- "-s",
- "--server",
- dest="server",
- default="127.0.0.1",
- help="the server to connect to",
- metavar="SERVER",
-)
-parser.add_argument(
- "-p",
- "--port",
- dest="port",
- type=int,
- default=6667,
- help="the port to connect to",
- metavar="PORT",
-)
-parser.add_argument(
- "-c",
- "--channels",
- dest="channels",
- nargs="+",
- default=["#bot_test"],
- help="the channels to join",
- metavar="CHANNELS",
-)
-parser.add_argument(
- "-n",
- "--nick",
- dest="nick",
- default="banterbot",
- help="the nick to use",
- metavar="NICK",
-)
-parser.add_argument(
- "-o",
- "--owner",
- dest="owner",
- default="krowbar",
- help="the owner of this bot",
- metavar="OWNER",
-)
-
-args = parser.parse_args()
-print(args)
-
-bot = Bot(
- channels = args.channels,
- nickname = args.nick,
- ops = [ args.owner ],
- plugin_dir = "{}_plugins".format(args.nick),
- server = args.server,
- port = args.port
- )
-
-if __name__ == "__main__":
- bot.start()
diff --git a/Code/irc/banterbot_legacy.py b/Code/irc/banterbot_legacy.py
deleted file mode 100755
index 441bfd7..0000000
--- a/Code/irc/banterbot_legacy.py
+++ /dev/null
@@ -1,440 +0,0 @@
-#!/usr/bin/python3
-# using python3 because of unicode and crap
-# http://wiki.shellium.org/w/Writing_an_IRC_bot_in_Python
-
-# Import some necessary libraries.
-import argparse
-import socket
-import os
-import sys
-import fileinput
-import random
-import re
-import subprocess
-import textwrap
-import time
-import datetime
-
-import inflect
-from rhymesWith import getRhymes
-from rhymesWith import rhymeZone
-from defineWord import defWord
-import welch
-import evil
-import tumblr
-import xkcdApropos
-import wikiphilosophy
-import acronymFinder
-import util
-from whosaid import whoSaid
-
-parser = argparse.ArgumentParser()
-
-parser.add_argument(
- "-s",
- "--server",
- dest="server",
- default="127.0.0.1:6667",
- help="the server to connect to",
- metavar="SERVER",
-)
-parser.add_argument(
- "-c",
- "--channels",
- dest="channels",
- nargs="+",
- default=["#bot_test"],
- help="the channels to join",
- metavar="CHANNELS",
-)
-parser.add_argument(
- "-n",
- "--nick",
- dest="nick",
- default="banterbot_legacy",
- help="the nick to use",
- metavar="NICK",
-)
-parser.add_argument(
- "-o",
- "--owner",
- dest="owner",
- default="krowbar",
- help="the owner of this bot",
- metavar="OWNER",
-)
-
-args = parser.parse_args()
-
-p = inflect.engine()
-
-def hello():
- util.sendmsg(ircsoc, channel, "Hello!")
-
-
-def score_banter(channel, user, messageText):
- score = 5
- with open("banterscores.txt", "r") as banterfile:
- bantz = banterfile.readlines()
- words = messageText.strip("\n").split(" ")
- for word in words:
- for bant in bantz:
- bword = bant.strip("\n").split("|")
- if re.sub("[^a-z0-9]+", "", word.lower()) == bword[0]:
- score += int(bword[1])
-
- score += messageText.count("!") * 2 # hype is banter
- score -= messageText.count("!!!") * 6 # too much hype is not banter
- score += messageText.count("#") * 3 # hashs are mad bantz
- score -= messageText.count("##") * 6 # but too many is garbage
-
- names = ["mate", "lad", "my best boy"]
- compliment = [
- "top-drawer",
- "top-shelf",
- "bangin'",
- "legendary",
- "smashing",
- "incredible",
- "impeccable",
- "stunning",
- ]
-
- msg = ""
- if score > 100:
- msg = "Truely {}, {}! That was some #banter! You earned a {} for that!".format(
- random.choice(compliment).capitalize(), random.choice(names), score
- )
- elif score > 50:
- msg = "{} #banter! You get a {} from me!".format(
- random.choice(compliment).capitalize(), score
- )
- elif score > 10:
- msg = "{} #banter. You get a {}".format(
- random.choice(["acceptible", "reasonable", "passable"]).capitalize(), score
- )
- else:
- msg = "That {} #banter, {}. I'll give you a {}. Maybe try again?".format(
- random.choice(
- ["was hardly", "was barely", "wasn't", "won't pass for", "was awful"]
- ),
- random.choice(["lad", "lah", "boy", ""]),
- score,
- )
-
- util.sendmsg(ircsock, channel, msg)
-
-
-def get_new_banter(channel, user):
- with open("/usr/share/dict/words", "r") as dict:
- words = list(filter(lambda word: re.search(r"^[^']*$", word), dict.readlines()))
- if random.randint(0, 1): # look for *ant words
- words = list(filter(lambda word: re.search(r"ant", word), words))
- random.shuffle(words)
- word = words[0].strip("\n")
- start = word.find("ant")
- if start == 0:
- word = "b" + word
- else:
- if "aeiou".find(word[start]) > -1: # just append a 'b'
- word = word[:start] + "b" + word[start:]
- else: # replace the letter with 'b'
- word = word[: start - 1] + "b" + word[start:]
- else: # look for ban* words
- words = list(filter(lambda word: re.search(r"ban", word), words))
- random.shuffle(words)
- word = words[0].strip("\n")
- end = word.find("ban") + 3
- if end == len(word):
- word = word + "t"
- else:
- if "aeiou".find(word[end]) > -1: # just append 't'
- word = word[:end] + "t" + word[end:]
- else: # replace the letter with 'b'
- word = word[:end] + "t" + word[end + 1 :]
- util.sendmsg(
- ircsock, channel, "{} : Here, why don't you try '{}'?".format(user, word)
- )
-
-
-def get_rhymes(channel, user, text):
- word = ""
- if len(text.split(" ")) > 1:
- word = text.split(" ")[1]
- else:
- with open("/home/nossidge/poems/words_poetic.txt", "r") as words:
- word = random.choice(words.readlines()).strip("\n")
- rhymes = rhymeZone(word)
- if len(rhymes) == 0:
- util.sendmsg(
- ircsock,
- channel,
- "{}: Couldn't find anything that rhymes with '{}' :(".format(user, word),
- )
- else:
- util.sendmsg(
- ircsock,
- channel,
- "{}: Here, these words rhyme with '{}': {}".format(
- user, word, ", ".join(rhymes)
- ),
- )
-
-
-def define_word(channel, user, text):
- word = ""
- defs = []
- if len(text.split(" ")) > 1:
- word = text.split(" ")[1]
- defs = defWord(word)
- if len(defs) == 0:
- util.sendmsg(
- ircsock,
- channel,
- "{}: Couldn't find the definition of '{}' :(".format(user, word),
- )
- elif isinstance(defs, list):
- for entry in defs:
- util.sendmsg(
- ircsock, channel, "{} : Define '{}' {}".format(user, word, entry[0:400])
- )
- else:
- util.sendmsg(
- ircsock, channel, "{} : Define '{}' {}".format(user, word, defs[0:400])
- )
-
-
-def make_rainbow(channel, user, text):
- rbword = util.makeRainbow(text[9:])
- util.sendmsg(ircsock, channel, rbword)
-
-
-def get_welch(channel):
- util.sendmsg(ircsock, channel, welch.get_thing()[0:400])
-
-
-def get_evil(channel):
- evilThing = evil.get_thing()
- for line in [evilThing[i : i + 400] for i in range(0, len(evilThing), 400)]:
- util.sendmsg(ircsock, channel, line)
-
-
-def get_tumble(url, channel):
- tumble = tumblr.tumble(url)
- for line in [tumble[i : i + 400] for i in range(0, len(tumble), 400)]:
- util.sendmsg(ircsock, channel, line)
-
-
-def get_xkcd(channel, text):
- links = xkcdApropos.xkcd(text[6:])
- joined_links = ", ".join(links)
- for line in [joined_links[i : i + 400] for i in range(0, len(joined_links), 400)]:
- util.sendmsg(ircsock, channel, line)
-
-
-def get_wphilosophy(channel, text):
- util.sendmsg(ircsock, channel, "Ok, give me a minute while I look up '{}'".format(text))
- steps = wikiphilosophy.get_philosophy_lower(text)
- if not steps:
- util.sendmsg(
- ircsock, channel, "Couldn't find a wikipedia entry for {}".format(text)
- )
- else:
- joined_steps = " > ".join(steps)
- if steps[-1] == "Philosophy":
- joined_steps += "!!!"
- for line in [
- joined_steps[i : i + 400] for i in range(0, len(joined_steps), 400)
- ]:
- util.sendmsg(ircsock, channel, line)
-
-
-def figlet(channel, text):
- if not text:
- util.sendmsg(ircsock, channel, "No text given. :(")
- else:
- lines = subprocess.Popen(
- ["figlet", "-w140"] + text.split(" "), shell=False, stdout=subprocess.PIPE
- ).stdout.read().decode("utf-8")
- for line in lines.split("\n"):
- util.sendmsg(ircsock, channel, line)
- time.sleep(0.4) # to avoid channel throttle due to spamming
-
-
-def toilet(channel, text):
- if not text:
- util.sendmsg(ircsock, channel, "No text given. :(")
- else:
- lines = subprocess.Popen(
- ["toilet", "-w140", "-F", "crop", "--irc"] + text.split(" "),
- shell=False,
- stdout=subprocess.PIPE,
- ).stdout.read().decode("utf-8")
- for line in lines.split("\n"):
- util.sendmsg(ircsock, channel, line)
- time.sleep(0.4) # to avoid channel throttle due to spamming
-
-
-def get_acronym(channel, text):
- if not text:
- util.sendmsg(ircsock, channel, "No text given :(")
- else:
- defs = acronymFinder.get_acros(text, True, True)
- for d in defs[0:5]: # only the first five. they are already sorted by 'score'
- util.sendmsg(ircsock, channel, d)
- if len(defs) > 5:
- util.sendmsg(ircsock, channel, defs[-1])
-
-
-def get_whosaid(channel, text):
- if not text:
- util.sendmsg(ircsock, channel, " :No text given :(")
- else:
- result = whoSaid(text)
- date = datetime.date.fromtimestamp(result["timecutoff"])
- dateStr = date.strftime("%B %d")
- if not result["data"]:
- msg = "Nobody said '%s' since %s" % (text, dateStr)
- else:
- msg = "Since %s, %s said '%s' %d times" % (
- dateStr,
- result["data"][0][0],
- text,
- result["data"][0][1],
- )
- if len(result["data"]) > 1:
- msg += " and %s said it %d times" % (
- result["data"][1][0],
- result["data"][1][1],
- )
- util.sendmsg(ircsock, channel, msg)
-
-
-def get_notice(user, channel):
- util.notice(ircsock, user, channel, "Notice me senpai!")
-
-
-def get_water(user, channel, msg, botnick):
- if msg.find(botnick) == 0:
- util.sendmsg(ircsock, channel, "Fight me, {}!".format(user))
-
-
-def mug_off(channel):
- util.sendmsg(ircsock, channel, "u want some of this, m8?")
-
-
-def rollcall(channel):
- text = """
- U wot m8? I score all the top drawer #banter and #bantz on this channel! / Find new top-shelf banter with !newbanter [mungeWord [dictionary]], !rhymes, and !define.
- Look up things with !acronym and !whosaid / Make your chatter #legend with !rainbow, !toilet, and !figlet.
- Find interesting things with !xkcd and !wiki-philosophy / Get jokes with !welch !evil !kjp and !help
- """
- for line in textwrap.dedent(text).split("\n"):
- if line == "":
- continue
- util.sendmsg(ircsock, channel, line)
-
-
-def listen(botnick):
- while 1: # loop forever
-
- ircmsg = ircsock.recv(2048).decode('utf-8')
- for msg in ircmsg.split("\n"):
- msg = msg.strip("\n\r")
-
- if msg[:4] == "PING":
- util.ping(ircsock, msg)
- continue
-
- formatted = util.format_message(msg)
-
- if "" == formatted:
- time.sleep(1)
- continue
-
- # print(formatted)
-
- _time, user, _command, channel, messageText = formatted.split("\t")
-
- if messageText.find("#banter") != -1 or messageText.find("#bantz") != -1:
- score_banter(channel, user, messageText)
-
- if messageText.startswith("!newbanter"):
- get_new_banter(channel, user, messageText)
-
- if messageText.startswith("!rhymes"):
- get_rhymes(channel, user, messageText)
-
- if messageText.startswith("!define"):
- define_word(channel, user, messageText)
-
- if messageText.startswith("!rainbow"):
- make_rainbow(channel, user, messageText)
-
- if messageText.startswith("!welch"):
- get_welch(channel)
-
- if messageText.startswith("!evil"):
- get_evil(channel)
-
- if messageText.startswith("!kjp"):
- get_tumble("http://kingjamesprogramming.tumblr.com", channel)
-
- if messageText.startswith("!help"):
- get_tumble("http://thedoomthatcametopuppet.tumblr.com", channel)
-
- if messageText.startswith("!xkcd"):
- get_xkcd(channel, messageText)
-
- if messageText.startswith("!wiki-philosophy"):
- get_wphilosophy(channel, messageText[17:])
-
- if messageText.startswith("!figlet"):
- figlet(channel, messageText[8:])
-
- if messageText.startswith("!toilet"):
- toilet(channel, messageText[8:])
-
- if messageText.startswith("!acronym"):
- get_acronym(channel, messageText[9:])
-
- if messageText.startswith("!whosaid"):
- get_whosaid(channel, messageText[9:])
-
- if messageText.startswith("!notice"):
- get_notice(user, channel)
-
- if messageText.startswith("!water"):
- get_water(user, channel, messageText[7:], botnick)
-
- if messageText.startswith("!rollcall"):
- rollcall(channel)
-
- if messageText.startswith(botnick + ":"):
- mug_off(channel)
-
- if messageText.startswith("!join") and user == args.owner:
- util.joinchan(ircsock, messageText[6:])
-
- if messageText.startswith("!part") and user == args.owner:
- util.part(ircsock, messageText[6:])
-
- if messageText.startswith("!quit") and user == args.owner:
- util.quit(ircsock, "Later chumps!")
- return
-
- sys.stdout.flush()
- time.sleep(1)
-
-if __name__ == "__main__":
- # ROOT: i commented this out until it stops pegging the CPU.
- # ~krowbar: this has the same logic loop as tildebot but for whatever reason
- # it is banterbot that gets booted from IRC then rage-thrashes the machine
- ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- util.connect(ircsock, args)
- listen(args.nick)
-else:
- # create a fake socket that we'll use to just write to the screen
- import mock
- ircsock = mock.Mock()
- # print("!! ircsock was not initialized! most methods will not work !!")
diff --git a/Code/irc/banterbot_plugins/template.py b/Code/irc/banterbot_plugins/template.py
deleted file mode 100644
index 8f79f8a..0000000
--- a/Code/irc/banterbot_plugins/template.py
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/python3
-
-import pinhook.plugin
-
-@pinhook.plugin.register('!')
-def _plugin(msg):
- return pinhook.plugin.message()
diff --git a/Code/irc/banterbot_plugins/units.py b/Code/irc/banterbot_plugins/units.py
new file mode 100644
index 0000000..af14fb3
--- /dev/null
+++ b/Code/irc/banterbot_plugins/units.py
@@ -0,0 +1,17 @@
+#!/usr/bin/python3
+
+import pinhook.plugin
+import subprocess
+
+UNITS_CMD = "/home/krowbar/Code/units/units-2.11/units"
+UNITS_CFG = "/home/krowbar/Code/units/units-2.11/definitions.units"
+
+@pinhook.plugin.register('!units')
+def units_plugin(msg):
+ if not msg.arg:
+ return pinhook.plugin.message("No text given. :(")
+ else:
+ result = subprocess.Popen(
+ [UNITS_CMD, "-v1f", UNITS_CFG] + msg.arg.split(" "), shell=False, stdout=subprocess.PIPE
+ ).stdout.read().decode("utf-8").strip()
+ return pinhook.plugin.message(result)
diff --git a/Code/irc/banterbot_plugins/xkcd.py b/Code/irc/banterbot_plugins/xkcd.py
index 3f358f9..f081cd7 100644
--- a/Code/irc/banterbot_plugins/xkcd.py
+++ b/Code/irc/banterbot_plugins/xkcd.py
@@ -1,8 +1,8 @@
#!/usr/bin/python3
import pinhook.plugin
-from util import xkcdApropos
+import util.xkcdApropos
@pinhook.plugin.register('!xkcd')
def xkcd_plugin(msg):
- return pinhook.plugin.message(xkcdApropos.xkcd(msg.arg))
+ return pinhook.plugin.message(util.xkcdApropos.xkcd(msg.arg))
diff --git a/Code/irc/badwords.txt b/Code/irc/data/badwords.txt
similarity index 100%
rename from Code/irc/badwords.txt
rename to Code/irc/data/badwords.txt
diff --git a/Code/irc/data/banter_corpus.txt b/Code/irc/data/banter_corpus.txt
new file mode 100644
index 0000000..6b683b1
--- /dev/null
+++ b/Code/irc/data/banter_corpus.txt
@@ -0,0 +1,90 @@
+1418511761 karlen no more banter in the irc now
+1421140177 jumblesale karlen yeah mate me too nice one banter
+1421140187 karlen i bantered you right off there
+1421140203 karlen Archbishop of Banterbury over here
+1421140221 jumblesale "Archbishop of Banterbury" amazing
+1421140286 karlen I can provide the Bantidote, I am a Bantersaurus Rex
+1421140530 jumblesale 2nd wave banterism
+1421239834 jumblesale I still can't get over the bishop of banterbury
+1421239946 jumblesale all their faces covered by "archbishop of banterbury" in a monospaced font
+1421240905 jumblesale archbishop of banterbury is too great for irc
+1421244850 jumblesale fruminous bantersnatch
+1421322755 karlen tbf I dont think he has as much banter as Rowan http://i.dailymail.co.uk/i/pix/2008/04_05/006Archbishop_228x388.jpg
+1421322856 karlen they should just make the archbishop and this point: I am always bantering people off
+1421322914 jumblesale banter
+1421323249 jumblesale a tilde for top drawer banter?
+1421323301 karlen ive spent the last 15mins looking for nowertb with banter but only one came up and its not bantz
+1421323368 jumblesale banter
+1421323895 karlen ok there will be a nowertb with banter in it at somepoint but its not bantz
+1421323963 jumblesale how can you have banter and it not be legendary bantz?
+1421324052 karlen It's comforting to realize that no one will ever read this.I am a quiet person for the most part. I mean, I talk, but most of the time it's preprogrammed banter just to pass time and please the people around me.
+1421324515 karlen banter all over the world
+1421325936 karlen haha there is a podcast they ahve called the Bantams banter I have heard mention of before
+1421326023 karlen heard lots of Bantam banter when they beat Arsenal a season or two back
+1421326312 karlen haha course, they bantered me off but such is life for a top drawer banter merchant such as myself
+1421326892 karlen so that guy is called Carl Jenkison and he plays for Arsenal and hes a right top lad and every other word he says is literally banter
+1421326918 karlen of banterbury
+1421327028 karlen he's just a banter lad
+1421327036 jumblesale karlen nobody's THAT banter
+1421327168 karlen me too, will get something banter for lunch
+1421328693 jumblesale how banter was your lunch? top drawer or legendary?
+1421329045 jumblesale just putting together a little faw for banter
+1421329247 karlen 1) What is banter 2) How to banter 3) How to banter someone off 4) Top bantz 5) Banter phrases
+1421329333 karlen 6) Lad banter 7) Am I banter? 8) Banter resources 9) Examples of banter 10) When to banter
+1421329371 karlen i'll put a banter file in my ~ and try to address some of these issues
+1421330214 jumblesale karlen your banter contribution is legendary, I'll combine the two
+1421330680 karlen ill try and think of some more once my banter juices have recharged
+1421331867 jumblesale my dream is to travel anywhere in the world and immediately be able to banter off the first #lad I see
+1421332132 karlen love your top banter phrases jumblesale
+1421332270 jumblesale I love everything about your banter file karlen
+1421332374 karlen feels wrong typing in less banter
+1421332494 jumblesale banter
+1421332676 jumblesale I go home. it's dark. I find a pen and hastily scrawl a note. "~wife. sorry. I can't be with you any more. I need banter. #legend". charles barks. he does not recognise me. I am a different man to the one who left this morning. I put the note on the table, collect my passport and step out into the night. somewhere out there a lad needs to be bantered off. and I am the only one who can do it.
+1421332760 jumblesale we were just enjoying some banter.
+1421337186 jumblesale nah mate just kidding it was shit #banter
+1421337361 karlen i got bantered right off there
+1421345107 jumblesale now we can finally get some top drawer banter going on
+1421346140 krowbar jumblesale: u wot m80? i swear on me mum that's the worst idea i ever dun hear and you should be banned from new ideas. banter!
+1421351218 karlen saw the bantz, not sure I deserve to be a notable banterer though compared to those ledge's
+1421353890 jumblesale haha harmless banter :)
+1421402562 karlen the banter zone
+1421402670 karlen im sure dan has banter, its probably a commonwealth export
+1421402740 karlen banter has probably spread there like the cane toad
+1421402762 karlen they are a banterous people by nature if Foster's adverts are to be believed
+1421402804 jumblesale banter = comedy
+1421402887 karlen you removed my top banter pun, it was the one bit of bantz in that file worth while
+1421402953 karlen from "turn your banter from piss poor to top drawer"
+1421402982 karlen or something like that, I had to delete the file from my ~. I had exceeded my banter allowance according the du -BANTS
+1421403014 karlen or quota -u -BANTER rather
+1421403230 jumblesale http://tilde.town/~banter/
+1421403809 karlen wow, maybe they are the banter messiah?!
+1421403998 jumblesale I saved your stuff to ~jumblesale/banter
+1421404449 karlen just banter mate come on, didn't expect you to look. You've been bantered right off for caring
+1421404474 jumblesale well I am going to add it back in put that in your banter pipe and bant it
+1421404569 karlen ill be in banter space
+1421404903 jumblesale I'm sorry karlen, the banter is fatal. It's spread to your brain.
+1421424059 jumblesale karlen that's why they call you banter the magnificent
+1421661838 karlen hey its jumblebanter!
+1421661982 jumblesale spent all weekend getting bantered right off
+1421661993 jumblesale now I'm seeing banter everywhere
+1421662006 karlen mate you have taken the red banter pill
+1421664295 jumblesale also where are you hanging around in the uk that has swastikas everywhere? are you that desparate for banter?
+1421665117 karlen the dan and um zone is probably so banter
+1421667558 karlen RogueLad: The banter chronicles
+1421667703 jumblesale maybe when I quit here I'll have some time to put into RogueLad: The #banter Chronicles
+1421667994 jumblesale Kragoth banters the kobold right off for 6 damage
+1421668027 karlen KO: you have been bantered right off there. #DeadLad
+1421678445 karlen haha we have to tone down the banter when we have company
+1421678572 um You two can banter away as far as I'm concerned.
+1421678645 jumblesale haha um you won't find it soothing when karlen banters you right off
+1421678654 jumblesale just with his banter
+1421678807 jumblesale mate I've been mugging off crustaceans since before you were born. think you can banter off a crab? fack off mate
+1421678914 karlen Mate Crustaceans ain't shit. Wait till you banter off a Banatee
+1421679040 jumblesale banatee might be a new low in banter punning
+1421771342 karlen you are ascending the banter charts that is for sure
+1421771528 karlen Mate, only in Ireland is that Banter
+1421771917 karlen look what we did to this other guy! he didn't even get exposed! LabMateBanter http://cdn6.bigcommerce.com/s-vhn7zwb/products/452/images/411/eos_safe_showers_freeze__77418__05399.1405105554.1280.1280.jpg?c=2
+1421773775 krowbar i'm thinking of writing a banterbot for scoring #banter
+1421773807 krowbar but i'll first need a corpus of #banter
+1421773838 karlen mensch -q banter
+1421773857 karlen or mensch -q "#banter"
diff --git a/Code/irc/banterscores.txt b/Code/irc/data/banterscores.txt
similarity index 100%
rename from Code/irc/banterscores.txt
rename to Code/irc/data/banterscores.txt
diff --git a/Code/irc/numberwangscores.txt b/Code/irc/data/numberwangscores.txt
similarity index 100%
rename from Code/irc/numberwangscores.txt
rename to Code/irc/data/numberwangscores.txt
diff --git a/Code/irc/data/quotes.txt b/Code/irc/data/quotes.txt
new file mode 100644
index 0000000..f0c07c2
--- /dev/null
+++ b/Code/irc/data/quotes.txt
@@ -0,0 +1,3 @@
+Bang a gong. Get it on. Bang a gong.
+/T.Rex/
+---
diff --git a/Code/irc/randomtopics.txt b/Code/irc/data/randomtopics.txt
similarity index 100%
rename from Code/irc/randomtopics.txt
rename to Code/irc/data/randomtopics.txt
diff --git a/Code/irc/data/tildejackpot.txt b/Code/irc/data/tildejackpot.txt
new file mode 100644
index 0000000..597975b
--- /dev/null
+++ b/Code/irc/data/tildejackpot.txt
@@ -0,0 +1 @@
+35
\ No newline at end of file
diff --git a/Code/irc/tildescores.txt b/Code/irc/data/tildescores.txt
similarity index 89%
rename from Code/irc/tildescores.txt
rename to Code/irc/data/tildescores.txt
index ca9792f..41cc9a0 100644
--- a/Code/irc/tildescores.txt
+++ b/Code/irc/data/tildescores.txt
@@ -1,4 +1,4 @@
-krowbar&^%2740&^%1548423098
+krowbar&^%2745&^%1548770703.413761
karlen&^%498&^%1527613440
endorphant&^%809&^%1444775660
jumblesale&^%25&^%1426171214
@@ -48,13 +48,13 @@ cinch&^%2&^%1480454755
caffbot&^%969&^%1532662872
evilbot&^%4&^%1480693919
tybaltcat&^%7&^%1481076625
-Minerbot&^%316&^%1548341661
+Minerbot&^%328&^%1548433782
mio&^%347&^%1529720473
tehfraga&^%673&^%1547260565
sushi&^%10&^%1493253212
troido&^%303&^%1548409580
gamebot&^%336&^%1548409599
-nilaky&^%1757&^%1548386011
+nilaky&^%1759&^%1548459568
bucket&^%103&^%1507931139
lolbot&^%1&^%1502568407
m455&^%12&^%1512076715
@@ -67,7 +67,7 @@ pinhook&^%8&^%1509744722
emfor&^%3&^%1509671353
k2l8m11n2&^%11&^%1510932395
sacredpixel&^%3&^%1522082931
-login&^%3249&^%1548423081
+login&^%3276&^%1548698114
kelpiebot&^%3&^%1513101957
unreal&^%2&^%1534387108
kinsey&^%26&^%1520446672
@@ -80,11 +80,11 @@ silver&^%9&^%1519333029
equa&^%53&^%1534373756
audiodude&^%2&^%1519453927
whimsy&^%47&^%1529678733
-wangofett&^%285&^%1547499873
+wangofett&^%286&^%1548446486
saturn&^%3&^%1521429369
lucidiot&^%28&^%1526201925
tracer&^%1&^%1521744878
-jan6&^%1346&^%1548353997
+jan6&^%1359&^%1548771222.440076
eeeeeta&^%55&^%1540361066
cmccabe&^%81&^%1529698164
carbon&^%9&^%1524135505
@@ -96,7 +96,7 @@ ralph&^%4&^%1526980620
von&^%490&^%1548233084
ensis&^%1750&^%1546853217
simon&^%26&^%1527937489
-benharri&^%3241&^%1548427827
+benharri&^%3276&^%1548695613
cpb&^%3&^%1528930564
calmbit&^%160&^%1541625694
wisebot&^%5652&^%1539612163
@@ -106,10 +106,10 @@ x4464&^%1&^%1532028546
pounce&^%19&^%1532133325
livix&^%7&^%1533603142
ben&^%3&^%1533767627
-npa&^%319&^%1548385964
+npa&^%326&^%1548659103
ezo&^%6&^%1533883842
aliasless&^%36&^%1541001821
-kirch&^%487&^%1548427938
+kirch&^%492&^%1548773313.169501
root&^%2&^%1535558514
byte&^%5&^%1536416308
qbe&^%7&^%1537850181
@@ -118,16 +118,16 @@ h00fi&^%1&^%1537050053
fantoro&^%57&^%1542125611
tildethief&^%7421&^%1542132794
benjaminwil&^%581&^%1548377274
-deltawitch&^%3451&^%1548192292
+deltawitch&^%3453&^%1548462217
archangelic&^%484&^%1541101297
diodelass&^%3&^%1539382302
-minerobber&^%420&^%1548341654
+minerobber&^%420&^%1548433750
brendantcc&^%3&^%1539908223
dozens&^%21&^%1541090333
bowlercaptain&^%6&^%1540926135
nicole&^%6&^%1541276844
littlebigly&^%49&^%1541283119
-ahriman&^%624&^%1548397580
+ahriman&^%636&^%1548663037
tunas&^%114&^%1545848648
netscape_navigator&^%5&^%1545846867
roan&^%5&^%1545846612
@@ -136,6 +136,6 @@ ben&^%3&^%1545800958
l0010o0001l&^%3&^%154529473
rgdrake&^%1&^%1546086404
handyc&^%5&^%1546456002
-fosslinux&^%8&^%1548296029
+fosslinux&^%10&^%1548661962
banterbot&^%3&^%1547588758
testgamebot&^%4&^%1547722176
diff --git a/Code/irc/topics_#bot_test.txt b/Code/irc/data/topics_#bot_test.txt
similarity index 100%
rename from Code/irc/topics_#bot_test.txt
rename to Code/irc/data/topics_#bot_test.txt
diff --git a/Code/irc/topics_#bots.txt b/Code/irc/data/topics_#bots.txt
similarity index 100%
rename from Code/irc/topics_#bots.txt
rename to Code/irc/data/topics_#bots.txt
diff --git a/Code/irc/topics_#tildetown.txt b/Code/irc/data/topics_#tildetown.txt
similarity index 100%
rename from Code/irc/topics_#tildetown.txt
rename to Code/irc/data/topics_#tildetown.txt
diff --git a/Code/irc/data/topics_now.txt b/Code/irc/data/topics_now.txt
new file mode 100644
index 0000000..6128d08
--- /dev/null
+++ b/Code/irc/data/topics_now.txt
@@ -0,0 +1,2 @@
+1516797459&^%login&^%
+1516797472&^%login&^%
diff --git a/Code/irc/topicscores.txt b/Code/irc/data/topicscores.txt
similarity index 100%
rename from Code/irc/topicscores.txt
rename to Code/irc/data/topicscores.txt
diff --git a/Code/irc/defineWord.py b/Code/irc/defineWord.py
deleted file mode 100644
index e7d977f..0000000
--- a/Code/irc/defineWord.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/python3
-import urllib
-from bs4 import BeautifulSoup
-import random
-
-
-def define(word):
- defs = []
- url = "http://www.merriam-webster.com/dictionary/{}".format(word)
- soup = BeautifulSoup(urllib.request.urlopen(url).read(), "html.parser")
- head = soup.find("div", id="headword")
- if head:
- for p in head.find_all("p"):
- defs.append(p.text)
- return defs
-
-
-key = open("/home/krowbar/.secret/key").readline().rstrip()
-
-
-def defWord(word, short=True):
- defs = []
- url = "http://www.dictionaryapi.com/api/v1/references/collegiate/xml/{}?key={}".format(
- word, key
- )
- soup = BeautifulSoup(urllib.request.urlopen(url).read(), "html5lib")
- entry = soup.find("entry")
- if entry:
- for d in entry.find_all("dt"):
- defs.append(d.text)
- if short:
- return " ".join(defs)
- else:
- return defs
diff --git a/Code/irc/dict_puzzle.py b/Code/irc/dict_puzzle.py
deleted file mode 100644
index 1bef24d..0000000
--- a/Code/irc/dict_puzzle.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/python
-import random
-import inflect
-
-p = inflect.engine()
-dictionary = "/usr/share/dict/american-english-small"
-BAD_WORDS_FILE = "badwords.txt"
-
-
-def gen_wordlist():
- # I feel weird calling this "get_wordlist" when it's a generator without calling out that I do in fact realise it's weird - ~deltawitch
- # how about gen_wordlist
- with open(BAD_WORDS_FILE, "r") as fp:
- bad_words = set(fp)
-
- for word in open(dictionary).readlines():
- if "'" not in word and word not in bad_words:
- yield word.rstrip()
-
-
-def get_puzzle():
- dict_words = list(gen_wordlist())
- words = random.sample(dict_words, 3)
- key = random.randrange(0, 3) # get values 1-3
- puzzle = "When alphebetized, what is the {} in {}?".format(
- p.ordinal(p.number_to_words(key + 1)), ", ".join(words)
- )
- words.sort()
- answer = words[key]
- return [answer, puzzle]
-
-
-def get_anagram(maxlen=6):
- dict_words = [
- word for word in gen_wordlist() if len(word) > 2 and len(word) < maxlen + 1
- ]
- word = random.choice(dict_words)
- anagram = list(word)
- random.shuffle(anagram)
- anagram = "".join(anagram)
-
- # Anagrams can have multiple answers, so we provide a check function that accepts all possibilities
- def answer_checker(guess):
- # Check for exact match
- if guess == word:
- return True
- # Bail out early if they didn't even use all the same letters
- if sorted(guess) != sorted(word):
- return False
- # Ok, gotta actually check if it's a word now
- return any(guess == item for item in gen_wordlist())
-
- return [answer_checker, "Unscramble the following word: '{}'".format(anagram)]
diff --git a/Code/irc/duckduckgo.py b/Code/irc/duckduckgo.py
deleted file mode 100755
index 767254e..0000000
--- a/Code/irc/duckduckgo.py
+++ /dev/null
@@ -1,198 +0,0 @@
-import requests
-import urllib
-import json as j
-import sys
-
-__version__ = 0.242
-
-
-def query(
- query,
- useragent="python-duckduckgo " + str(__version__),
- safesearch=True,
- html=False,
- meanings=True,
- **kwargs
-):
- """
- Query DuckDuckGo, returning a Results object.
-
- Here's a query that's unlikely to change:
-
- >>> result = query('1 + 1')
- >>> result.type
- 'nothing'
- >>> result.answer.text
- '1 + 1 = 2'
- >>> result.answer.type
- 'calc'
-
- Keword arguments:
- useragent: UserAgent to use while querying. Default: "python-duckduckgo %d" (str)
- safesearch: True for on, False for off. Default: True (bool)
- html: True to allow HTML in output. Default: False (bool)
- meanings: True to include disambiguations in results (bool)
- Any other keyword arguments are passed directly to DuckDuckGo as URL params.
- """ % __version__
-
- safesearch = "1" if safesearch else "-1"
- html = "0" if html else "1"
- meanings = "0" if meanings else "1"
- params = {
- "q": query,
- "o": "json",
- "kp": safesearch,
- "no_redirect": "1",
- "no_html": html,
- "d": meanings,
- }
- params.update(kwargs)
- encparams = urllib.parse.urlencode(params)
- url = "http://api.duckduckgo.com/?" + encparams
-
- request = requests.get(url, headers={"User-Agent": useragent})
-
- return Results(request.json())
-
-
-class Results(object):
- def __init__(self, json):
- self.type = {
- "A": "answer",
- "D": "disambiguation",
- "C": "category",
- "N": "name",
- "E": "exclusive",
- "": "nothing",
- }.get(json.get("Type", ""), "")
-
- self.json = json
- self.api_version = None # compat
-
- self.heading = json.get("Heading", "")
-
- self.results = [Result(elem) for elem in json.get("Results", [])]
- self.related = [Result(elem) for elem in json.get("RelatedTopics", [])]
-
- self.abstract = Abstract(json)
- self.redirect = Redirect(json)
- self.definition = Definition(json)
- self.answer = Answer(json)
-
- self.image = Image({"Result": json.get("Image", "")})
-
-
-class Abstract(object):
- def __init__(self, json):
- self.html = json.get("Abstract", "")
- self.text = json.get("AbstractText", "")
- self.url = json.get("AbstractURL", "")
- self.source = json.get("AbstractSource")
-
-
-class Redirect(object):
- def __init__(self, json):
- self.url = json.get("Redirect", "")
-
-
-class Result(object):
- def __init__(self, json):
- self.topics = json.get("Topics", [])
- if self.topics:
- self.topics = [Result(t) for t in self.topics]
- return
- self.html = json.get("Result")
- self.text = json.get("Text")
- self.url = json.get("FirstURL")
-
- icon_json = json.get("Icon")
- if icon_json is not None:
- self.icon = Image(icon_json)
- else:
- self.icon = None
-
-
-class Image(object):
- def __init__(self, json):
- self.url = json.get("Result")
- self.height = json.get("Height", None)
- self.width = json.get("Width", None)
-
-
-class Answer(object):
- def __init__(self, json):
- self.text = json.get("Answer")
- self.type = json.get("AnswerType", "")
-
-
-class Definition(object):
- def __init__(self, json):
- self.text = json.get("Definition", "")
- self.url = json.get("DefinitionURL")
- self.source = json.get("DefinitionSource")
-
-
-def get_zci(
- q,
- web_fallback=True,
- priority=["answer", "abstract", "related.0", "definition"],
- urls=True,
- **kwargs
-):
- """A helper method to get a single (and hopefully the best) ZCI result.
- priority=list can be used to set the order in which fields will be checked for answers.
- Use web_fallback=True to fall back to grabbing the first web result.
- passed to query. This method will fall back to 'Sorry, no results.'
- if it cannot find anything."""
-
- ddg = query("\\" + q, **kwargs)
- response = ""
-
- for p in priority:
- ps = p.split(".")
- type = ps[0]
- index = int(ps[1]) if len(ps) > 1 else None
-
- result = getattr(ddg, type)
- if index is not None:
- if not hasattr(result, "__getitem__"):
- raise TypeError("%s field is not indexable" % type)
- result = result[index] if len(result) > index else None
- if not result:
- continue
-
- if result.text:
- response = result.text
- if result.text and hasattr(result, "url") and urls:
- if result.url:
- response += " (%s)" % result.url
- if response:
- break
-
- # if there still isn't anything, try to get the first web result
- if not response and web_fallback:
- if ddg.redirect.url:
- response = ddg.redirect.url
-
- # final fallback
- if not response:
- response = "Sorry, no results."
-
- return response
-
-
-def main():
- if len(sys.argv) > 1:
- q = query(" ".join(sys.argv[1:]))
- keys = q.json.keys()
- keys.sort()
- for key in keys:
- sys.stdout.write(key)
- if type(q.json[key]) in [str, unicode]:
- print(":", q.json[key])
- else:
- sys.stdout.write("\n")
- for i in q.json[key]:
- print("\t", i)
- else:
- print("Usage: %s [query]" % sys.argv[0])
diff --git a/Code/irc/evil.py b/Code/irc/evil.py
deleted file mode 100644
index eecb7f6..0000000
--- a/Code/irc/evil.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import random
-
-
-def get_thing():
- file = "/home/krowbar/logs/evildata.txt"
- thing = ""
- return (
- "If I Ever Become an Evil Overlord: " + random.choice(list(open(file))).rstrip()
- )
diff --git a/Code/irc/formatter.pyc b/Code/irc/formatter.pyc
deleted file mode 100644
index 48a929c..0000000
Binary files a/Code/irc/formatter.pyc and /dev/null differ
diff --git a/Code/irc/get_users.pyc b/Code/irc/get_users.pyc
deleted file mode 100644
index a422c78..0000000
Binary files a/Code/irc/get_users.pyc and /dev/null differ
diff --git a/Code/irc/inflect.py b/Code/irc/inflect.py
deleted file mode 100644
index f2db66b..0000000
--- a/Code/irc/inflect.py
+++ /dev/null
@@ -1,3981 +0,0 @@
-"""
- inflect.py: correctly generate plurals, ordinals, indefinite articles;
- convert numbers to words
- Copyright (C) 2010 Paul Dyson
-
- Based upon the Perl module Lingua::EN::Inflect by Damian Conway.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
- The original Perl module Lingua::EN::Inflect by Damian Conway is
- available from http://search.cpan.org/~dconway/
-
- This module can be downloaded at http://pypi.python.org/pypi/inflect
-
-methods:
- classical inflect
- plural plural_noun plural_verb plural_adj singular_noun no num a an
- compare compare_nouns compare_verbs compare_adjs
- present_participle
- ordinal
- number_to_words
- join
- defnoun defverb defadj defa defan
-
- INFLECTIONS: classical inflect
- plural plural_noun plural_verb plural_adj singular_noun compare
- no num a an present_participle
-
- PLURALS: classical inflect
- plural plural_noun plural_verb plural_adj singular_noun no num
- compare compare_nouns compare_verbs compare_adjs
-
- COMPARISONS: classical
- compare compare_nouns compare_verbs compare_adjs
-
- ARTICLES: classical inflect num a an
-
- NUMERICAL: ordinal number_to_words
-
- USER_DEFINED: defnoun defverb defadj defa defan
-
-Exceptions:
- UnknownClassicalModeError
- BadNumValueError
- BadChunkingOptionError
- NumOutOfRangeError
- BadUserDefinedPatternError
- BadRcFileError
- BadGenderError
-
-"""
-
-from re import match, search, subn, IGNORECASE, VERBOSE
-from re import split as splitre
-from re import error as reerror
-from re import sub as resub
-
-
-class UnknownClassicalModeError(Exception):
- pass
-
-
-class BadNumValueError(Exception):
- pass
-
-
-class BadChunkingOptionError(Exception):
- pass
-
-
-class NumOutOfRangeError(Exception):
- pass
-
-
-class BadUserDefinedPatternError(Exception):
- pass
-
-
-class BadRcFileError(Exception):
- pass
-
-
-class BadGenderError(Exception):
- pass
-
-
-__ver_major__ = 0
-__ver_minor__ = 2
-__ver_patch__ = 4
-__ver_sub__ = ""
-__version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__, __ver_patch__, __ver_sub__)
-
-
-STDOUT_ON = False
-
-
-def print3(txt):
- if STDOUT_ON:
- print(txt)
-
-
-def enclose(s):
- return "(?:%s)" % s
-
-
-def joinstem(cutpoint=0, words=""):
- """
- join stem of each word in words into a string for regex
- each word is truncated at cutpoint
- cutpoint is usually negative indicating the number of letters to remove
- from the end of each word
-
- e.g.
- joinstem(-2, ["ephemeris", "iris", ".*itis"]) returns
- (?:ephemer|ir|.*it)
-
- """
- return enclose("|".join(w[:cutpoint] for w in words))
-
-
-def bysize(words):
- """
- take a list of words and return a dict of sets sorted by word length
- e.g.
- ret[3]=set(['ant', 'cat', 'dog', 'pig'])
- ret[4]=set(['frog', 'goat'])
- ret[5]=set(['horse'])
- ret[8]=set(['elephant'])
- """
- ret = {}
- for w in words:
- if len(w) not in ret:
- ret[len(w)] = set()
- ret[len(w)].add(w)
- return ret
-
-
-def make_pl_si_lists(lst, plending, siendingsize, dojoinstem=True):
- """
- given a list of singular words: lst
- an ending to append to make the plural: plending
- the number of characters to remove from the singular before appending plending: siendingsize
- a flag whether to create a joinstem: dojoinstem
-
- return:
- a list of pluralised words: si_list (called si because this is what you need to
- look for to make the singular)
- the pluralised words as a dict of sets sorted by word length: si_bysize
- the singular words as a dict of sets sorted by word length: pl_bysize
- if dojoinstem is True: a regular expression that matches any of the stems: stem
- """
- if siendingsize is not None:
- siendingsize = -siendingsize
- si_list = [w[:siendingsize] + plending for w in lst]
- pl_bysize = bysize(lst)
- si_bysize = bysize(si_list)
- if dojoinstem:
- stem = joinstem(siendingsize, lst)
- return si_list, si_bysize, pl_bysize, stem
- else:
- return si_list, si_bysize, pl_bysize
-
-
-# 1. PLURALS
-
-pl_sb_irregular_s = {
- "corpus": "corpuses|corpora",
- "opus": "opuses|opera",
- "genus": "genera",
- "mythos": "mythoi",
- "penis": "penises|penes",
- "testis": "testes",
- "atlas": "atlases|atlantes",
- "yes": "yeses",
-}
-
-pl_sb_irregular = {
- "child": "children",
- "brother": "brothers|brethren",
- "loaf": "loaves",
- "hoof": "hoofs|hooves",
- "beef": "beefs|beeves",
- "thief": "thiefs|thieves",
- "money": "monies",
- "mongoose": "mongooses",
- "ox": "oxen",
- "cow": "cows|kine",
- "graffito": "graffiti",
- "octopus": "octopuses|octopodes",
- "genie": "genies|genii",
- "ganglion": "ganglions|ganglia",
- "trilby": "trilbys",
- "turf": "turfs|turves",
- "numen": "numina",
- "atman": "atmas",
- "occiput": "occiputs|occipita",
- "sabretooth": "sabretooths",
- "sabertooth": "sabertooths",
- "lowlife": "lowlifes",
- "flatfoot": "flatfoots",
- "tenderfoot": "tenderfoots",
- "romany": "romanies",
- "jerry": "jerries",
- "mary": "maries",
- "talouse": "talouses",
- "blouse": "blouses",
- "rom": "roma",
- "carmen": "carmina",
-}
-
-pl_sb_irregular.update(pl_sb_irregular_s)
-# pl_sb_irregular_keys = enclose('|'.join(pl_sb_irregular.keys()))
-
-pl_sb_irregular_caps = {
- "Romany": "Romanies",
- "Jerry": "Jerrys",
- "Mary": "Marys",
- "Rom": "Roma",
-}
-
-pl_sb_irregular_compound = {"prima donna": "prima donnas|prime donne"}
-
-si_sb_irregular = dict([(v, k) for (k, v) in pl_sb_irregular.items()])
-keys = list(si_sb_irregular.keys())
-for k in keys:
- if "|" in k:
- k1, k2 = k.split("|")
- si_sb_irregular[k1] = si_sb_irregular[k2] = si_sb_irregular[k]
- del si_sb_irregular[k]
-si_sb_irregular_caps = dict([(v, k) for (k, v) in pl_sb_irregular_caps.items()])
-si_sb_irregular_compound = dict([(v, k) for (k, v) in pl_sb_irregular_compound.items()])
-keys = list(si_sb_irregular_compound.keys())
-for k in keys:
- if "|" in k:
- k1, k2 = k.split("|")
- si_sb_irregular_compound[k1] = si_sb_irregular_compound[
- k2
- ] = si_sb_irregular_compound[k]
- del si_sb_irregular_compound[k]
-
-# si_sb_irregular_keys = enclose('|'.join(si_sb_irregular.keys()))
-
-# Z's that don't double
-
-pl_sb_z_zes_list = ("quartz", "topaz")
-pl_sb_z_zes_bysize = bysize(pl_sb_z_zes_list)
-
-pl_sb_ze_zes_list = ("snooze",)
-pl_sb_ze_zes_bysize = bysize(pl_sb_ze_zes_list)
-
-
-# CLASSICAL "..is" -> "..ides"
-
-pl_sb_C_is_ides_complete = [
- # GENERAL WORDS...
- "ephemeris",
- "iris",
- "clitoris",
- "chrysalis",
- "epididymis",
-]
-
-pl_sb_C_is_ides_endings = [
- # INFLAMATIONS...
- "itis"
-]
-
-pl_sb_C_is_ides = joinstem(
- -2, pl_sb_C_is_ides_complete + [".*%s" % w for w in pl_sb_C_is_ides_endings]
-)
-
-pl_sb_C_is_ides_list = pl_sb_C_is_ides_complete + pl_sb_C_is_ides_endings
-
-(
- si_sb_C_is_ides_list,
- si_sb_C_is_ides_bysize,
- pl_sb_C_is_ides_bysize,
-) = make_pl_si_lists(pl_sb_C_is_ides_list, "ides", 2, dojoinstem=False)
-
-
-# CLASSICAL "..a" -> "..ata"
-
-pl_sb_C_a_ata_list = (
- "anathema",
- "bema",
- "carcinoma",
- "charisma",
- "diploma",
- "dogma",
- "drama",
- "edema",
- "enema",
- "enigma",
- "lemma",
- "lymphoma",
- "magma",
- "melisma",
- "miasma",
- "oedema",
- "sarcoma",
- "schema",
- "soma",
- "stigma",
- "stoma",
- "trauma",
- "gumma",
- "pragma",
-)
-
-(
- si_sb_C_a_ata_list,
- si_sb_C_a_ata_bysize,
- pl_sb_C_a_ata_bysize,
- pl_sb_C_a_ata,
-) = make_pl_si_lists(pl_sb_C_a_ata_list, "ata", 1)
-
-# UNCONDITIONAL "..a" -> "..ae"
-
-pl_sb_U_a_ae_list = ("alumna", "alga", "vertebra", "persona")
-(
- si_sb_U_a_ae_list,
- si_sb_U_a_ae_bysize,
- pl_sb_U_a_ae_bysize,
- pl_sb_U_a_ae,
-) = make_pl_si_lists(pl_sb_U_a_ae_list, "e", None)
-
-# CLASSICAL "..a" -> "..ae"
-
-pl_sb_C_a_ae_list = (
- "amoeba",
- "antenna",
- "formula",
- "hyperbola",
- "medusa",
- "nebula",
- "parabola",
- "abscissa",
- "hydra",
- "nova",
- "lacuna",
- "aurora",
- "umbra",
- "flora",
- "fauna",
-)
-(
- si_sb_C_a_ae_list,
- si_sb_C_a_ae_bysize,
- pl_sb_C_a_ae_bysize,
- pl_sb_C_a_ae,
-) = make_pl_si_lists(pl_sb_C_a_ae_list, "e", None)
-
-
-# CLASSICAL "..en" -> "..ina"
-
-pl_sb_C_en_ina_list = ("stamen", "foramen", "lumen")
-
-(
- si_sb_C_en_ina_list,
- si_sb_C_en_ina_bysize,
- pl_sb_C_en_ina_bysize,
- pl_sb_C_en_ina,
-) = make_pl_si_lists(pl_sb_C_en_ina_list, "ina", 2)
-
-
-# UNCONDITIONAL "..um" -> "..a"
-
-pl_sb_U_um_a_list = (
- "bacterium",
- "agendum",
- "desideratum",
- "erratum",
- "stratum",
- "datum",
- "ovum",
- "extremum",
- "candelabrum",
-)
-(
- si_sb_U_um_a_list,
- si_sb_U_um_a_bysize,
- pl_sb_U_um_a_bysize,
- pl_sb_U_um_a,
-) = make_pl_si_lists(pl_sb_U_um_a_list, "a", 2)
-
-# CLASSICAL "..um" -> "..a"
-
-pl_sb_C_um_a_list = (
- "maximum",
- "minimum",
- "momentum",
- "optimum",
- "quantum",
- "cranium",
- "curriculum",
- "dictum",
- "phylum",
- "aquarium",
- "compendium",
- "emporium",
- "enconium",
- "gymnasium",
- "honorarium",
- "interregnum",
- "lustrum",
- "memorandum",
- "millennium",
- "rostrum",
- "spectrum",
- "speculum",
- "stadium",
- "trapezium",
- "ultimatum",
- "medium",
- "vacuum",
- "velum",
- "consortium",
- "arboretum",
-)
-
-(
- si_sb_C_um_a_list,
- si_sb_C_um_a_bysize,
- pl_sb_C_um_a_bysize,
- pl_sb_C_um_a,
-) = make_pl_si_lists(pl_sb_C_um_a_list, "a", 2)
-
-
-# UNCONDITIONAL "..us" -> "i"
-
-pl_sb_U_us_i_list = (
- "alumnus",
- "alveolus",
- "bacillus",
- "bronchus",
- "locus",
- "nucleus",
- "stimulus",
- "meniscus",
- "sarcophagus",
-)
-(
- si_sb_U_us_i_list,
- si_sb_U_us_i_bysize,
- pl_sb_U_us_i_bysize,
- pl_sb_U_us_i,
-) = make_pl_si_lists(pl_sb_U_us_i_list, "i", 2)
-
-# CLASSICAL "..us" -> "..i"
-
-pl_sb_C_us_i_list = (
- "focus",
- "radius",
- "genius",
- "incubus",
- "succubus",
- "nimbus",
- "fungus",
- "nucleolus",
- "stylus",
- "torus",
- "umbilicus",
- "uterus",
- "hippopotamus",
- "cactus",
-)
-
-(
- si_sb_C_us_i_list,
- si_sb_C_us_i_bysize,
- pl_sb_C_us_i_bysize,
- pl_sb_C_us_i,
-) = make_pl_si_lists(pl_sb_C_us_i_list, "i", 2)
-
-
-# CLASSICAL "..us" -> "..us" (ASSIMILATED 4TH DECLENSION LATIN NOUNS)
-
-pl_sb_C_us_us = (
- "status",
- "apparatus",
- "prospectus",
- "sinus",
- "hiatus",
- "impetus",
- "plexus",
-)
-pl_sb_C_us_us_bysize = bysize(pl_sb_C_us_us)
-
-# UNCONDITIONAL "..on" -> "a"
-
-pl_sb_U_on_a_list = (
- "criterion",
- "perihelion",
- "aphelion",
- "phenomenon",
- "prolegomenon",
- "noumenon",
- "organon",
- "asyndeton",
- "hyperbaton",
-)
-(
- si_sb_U_on_a_list,
- si_sb_U_on_a_bysize,
- pl_sb_U_on_a_bysize,
- pl_sb_U_on_a,
-) = make_pl_si_lists(pl_sb_U_on_a_list, "a", 2)
-
-# CLASSICAL "..on" -> "..a"
-
-pl_sb_C_on_a_list = ("oxymoron",)
-
-(
- si_sb_C_on_a_list,
- si_sb_C_on_a_bysize,
- pl_sb_C_on_a_bysize,
- pl_sb_C_on_a,
-) = make_pl_si_lists(pl_sb_C_on_a_list, "a", 2)
-
-
-# CLASSICAL "..o" -> "..i" (BUT NORMALLY -> "..os")
-
-pl_sb_C_o_i = [
- "solo",
- "soprano",
- "basso",
- "alto",
- "contralto",
- "tempo",
- "piano",
- "virtuoso",
-] # list not tuple so can concat for pl_sb_U_o_os
-
-pl_sb_C_o_i_bysize = bysize(pl_sb_C_o_i)
-si_sb_C_o_i_bysize = bysize(["%si" % w[:-1] for w in pl_sb_C_o_i])
-
-pl_sb_C_o_i_stems = joinstem(-1, pl_sb_C_o_i)
-
-# ALWAYS "..o" -> "..os"
-
-pl_sb_U_o_os_complete = set(("ado", "ISO", "NATO", "NCO", "NGO", "oto"))
-si_sb_U_o_os_complete = set("%ss" % w for w in pl_sb_U_o_os_complete)
-
-
-pl_sb_U_o_os_endings = [
- "aficionado",
- "aggro",
- "albino",
- "allegro",
- "ammo",
- "Antananarivo",
- "archipelago",
- "armadillo",
- "auto",
- "avocado",
- "Bamako",
- "Barquisimeto",
- "bimbo",
- "bingo",
- "Biro",
- "bolero",
- "Bolzano",
- "bongo",
- "Boto",
- "burro",
- "Cairo",
- "canto",
- "cappuccino",
- "casino",
- "cello",
- "Chicago",
- "Chimango",
- "cilantro",
- "cochito",
- "coco",
- "Colombo",
- "Colorado",
- "commando",
- "concertino",
- "contango",
- "credo",
- "crescendo",
- "cyano",
- "demo",
- "ditto",
- "Draco",
- "dynamo",
- "embryo",
- "Esperanto",
- "espresso",
- "euro",
- "falsetto",
- "Faro",
- "fiasco",
- "Filipino",
- "flamenco",
- "furioso",
- "generalissimo",
- "Gestapo",
- "ghetto",
- "gigolo",
- "gizmo",
- "Greensboro",
- "gringo",
- "Guaiabero",
- "guano",
- "gumbo",
- "gyro",
- "hairdo",
- "hippo",
- "Idaho",
- "impetigo",
- "inferno",
- "info",
- "intermezzo",
- "intertrigo",
- "Iquico",
- "jumbo",
- "junto",
- "Kakapo",
- "kilo",
- "Kinkimavo",
- "Kokako",
- "Kosovo",
- "Lesotho",
- "libero",
- "libido",
- "libretto",
- "lido",
- "Lilo",
- "limbo",
- "limo",
- "lineno",
- "lingo",
- "lino",
- "livedo",
- "loco",
- "logo",
- "lumbago",
- "macho",
- "macro",
- "mafioso",
- "magneto",
- "magnifico",
- "Majuro",
- "Malabo",
- "manifesto",
- "Maputo",
- "Maracaibo",
- "medico",
- "memo",
- "metro",
- "Mexico",
- "micro",
- "Milano",
- "Monaco",
- "mono",
- "Montenegro",
- "Morocco",
- "Muqdisho",
- "myo",
- "neutrino",
- "Ningbo",
- "octavo",
- "oregano",
- "Orinoco",
- "Orlando",
- "Oslo",
- "panto",
- "Paramaribo",
- "Pardusco",
- "pedalo",
- "photo",
- "pimento",
- "pinto",
- "pleco",
- "Pluto",
- "pogo",
- "polo",
- "poncho",
- "Porto-Novo",
- "Porto",
- "pro",
- "psycho",
- "pueblo",
- "quarto",
- "Quito",
- "rhino",
- "risotto",
- "rococo",
- "rondo",
- "Sacramento",
- "saddo",
- "sago",
- "salvo",
- "Santiago",
- "Sapporo",
- "Sarajevo",
- "scherzando",
- "scherzo",
- "silo",
- "sirocco",
- "sombrero",
- "staccato",
- "sterno",
- "stucco",
- "stylo",
- "sumo",
- "Taiko",
- "techno",
- "terrazzo",
- "testudo",
- "timpano",
- "tiro",
- "tobacco",
- "Togo",
- "Tokyo",
- "torero",
- "Torino",
- "Toronto",
- "torso",
- "tremolo",
- "typo",
- "tyro",
- "ufo",
- "UNESCO",
- "vaquero",
- "vermicello",
- "verso",
- "vibrato",
- "violoncello",
- "Virgo",
- "weirdo",
- "WHO",
- "WTO",
- "Yamoussoukro",
- "yo-yo",
- "zero",
- "Zibo",
-] + pl_sb_C_o_i
-
-pl_sb_U_o_os_bysize = bysize(pl_sb_U_o_os_endings)
-si_sb_U_o_os_bysize = bysize(["%ss" % w for w in pl_sb_U_o_os_endings])
-
-
-# UNCONDITIONAL "..ch" -> "..chs"
-
-pl_sb_U_ch_chs_list = ("czech", "eunuch", "stomach")
-
-(
- si_sb_U_ch_chs_list,
- si_sb_U_ch_chs_bysize,
- pl_sb_U_ch_chs_bysize,
- pl_sb_U_ch_chs,
-) = make_pl_si_lists(pl_sb_U_ch_chs_list, "s", None)
-
-
-# UNCONDITIONAL "..[ei]x" -> "..ices"
-
-pl_sb_U_ex_ices_list = ("codex", "murex", "silex")
-(
- si_sb_U_ex_ices_list,
- si_sb_U_ex_ices_bysize,
- pl_sb_U_ex_ices_bysize,
- pl_sb_U_ex_ices,
-) = make_pl_si_lists(pl_sb_U_ex_ices_list, "ices", 2)
-
-pl_sb_U_ix_ices_list = ("radix", "helix")
-(
- si_sb_U_ix_ices_list,
- si_sb_U_ix_ices_bysize,
- pl_sb_U_ix_ices_bysize,
- pl_sb_U_ix_ices,
-) = make_pl_si_lists(pl_sb_U_ix_ices_list, "ices", 2)
-
-# CLASSICAL "..[ei]x" -> "..ices"
-
-pl_sb_C_ex_ices_list = (
- "vortex",
- "vertex",
- "cortex",
- "latex",
- "pontifex",
- "apex",
- "index",
- "simplex",
-)
-
-(
- si_sb_C_ex_ices_list,
- si_sb_C_ex_ices_bysize,
- pl_sb_C_ex_ices_bysize,
- pl_sb_C_ex_ices,
-) = make_pl_si_lists(pl_sb_C_ex_ices_list, "ices", 2)
-
-
-pl_sb_C_ix_ices_list = ("appendix",)
-
-(
- si_sb_C_ix_ices_list,
- si_sb_C_ix_ices_bysize,
- pl_sb_C_ix_ices_bysize,
- pl_sb_C_ix_ices,
-) = make_pl_si_lists(pl_sb_C_ix_ices_list, "ices", 2)
-
-
-# ARABIC: ".." -> "..i"
-
-pl_sb_C_i_list = ("afrit", "afreet", "efreet")
-
-(si_sb_C_i_list, si_sb_C_i_bysize, pl_sb_C_i_bysize, pl_sb_C_i) = make_pl_si_lists(
- pl_sb_C_i_list, "i", None
-)
-
-
-# HEBREW: ".." -> "..im"
-
-pl_sb_C_im_list = ("goy", "seraph", "cherub")
-
-(si_sb_C_im_list, si_sb_C_im_bysize, pl_sb_C_im_bysize, pl_sb_C_im) = make_pl_si_lists(
- pl_sb_C_im_list, "im", None
-)
-
-
-# UNCONDITIONAL "..man" -> "..mans"
-
-pl_sb_U_man_mans_list = """
- ataman caiman cayman ceriman
- desman dolman farman harman hetman
- human leman ottoman shaman talisman
-""".split()
-pl_sb_U_man_mans_caps_list = """
- Alabaman Bahaman Burman German
- Hiroshiman Liman Nakayaman Norman Oklahoman
- Panaman Roman Selman Sonaman Tacoman Yakiman
- Yokohaman Yuman
-""".split()
-
-(
- si_sb_U_man_mans_list,
- si_sb_U_man_mans_bysize,
- pl_sb_U_man_mans_bysize,
-) = make_pl_si_lists(pl_sb_U_man_mans_list, "s", None, dojoinstem=False)
-(
- si_sb_U_man_mans_caps_list,
- si_sb_U_man_mans_caps_bysize,
- pl_sb_U_man_mans_caps_bysize,
-) = make_pl_si_lists(pl_sb_U_man_mans_caps_list, "s", None, dojoinstem=False)
-
-
-pl_sb_uninflected_s_complete = [
- # PAIRS OR GROUPS SUBSUMED TO A SINGULAR...
- "breeches",
- "britches",
- "pajamas",
- "pyjamas",
- "clippers",
- "gallows",
- "hijinks",
- "headquarters",
- "pliers",
- "scissors",
- "testes",
- "herpes",
- "pincers",
- "shears",
- "proceedings",
- "trousers",
- # UNASSIMILATED LATIN 4th DECLENSION
- "cantus",
- "coitus",
- "nexus",
- # RECENT IMPORTS...
- "contretemps",
- "corps",
- "debris",
- "siemens",
- # DISEASES
- "mumps",
- # MISCELLANEOUS OTHERS...
- "diabetes",
- "jackanapes",
- "series",
- "species",
- "subspecies",
- "rabies",
- "chassis",
- "innings",
- "news",
- "mews",
- "haggis",
-]
-
-pl_sb_uninflected_s_endings = [
- # RECENT IMPORTS...
- "ois",
- # DISEASES
- "measles",
-]
-
-pl_sb_uninflected_s = pl_sb_uninflected_s_complete + [
- ".*%s" % w for w in pl_sb_uninflected_s_endings
-]
-
-pl_sb_uninflected_herd = (
- # DON'T INFLECT IN CLASSICAL MODE, OTHERWISE NORMAL INFLECTION
- "wildebeest",
- "swine",
- "eland",
- "bison",
- "buffalo",
- "elk",
- "rhinoceros",
- "zucchini",
- "caribou",
- "dace",
- "grouse",
- "guinea fowl",
- "guinea-fowl",
- "haddock",
- "hake",
- "halibut",
- "herring",
- "mackerel",
- "pickerel",
- "pike",
- "roe",
- "seed",
- "shad",
- "snipe",
- "teal",
- "turbot",
- "water fowl",
- "water-fowl",
-)
-
-pl_sb_uninflected_complete = [
- # SOME FISH AND HERD ANIMALS
- "tuna",
- "salmon",
- "mackerel",
- "trout",
- "bream",
- "sea-bass",
- "sea bass",
- "carp",
- "cod",
- "flounder",
- "whiting",
- "moose",
- # OTHER ODDITIES
- "graffiti",
- "djinn",
- "samuri",
- "offspring",
- "pence",
- "quid",
- "hertz",
-] + pl_sb_uninflected_s_complete
-# SOME WORDS ENDING IN ...s (OFTEN PAIRS TAKEN AS A WHOLE)
-
-pl_sb_uninflected_caps = [
- # ALL NATIONALS ENDING IN -ese
- "Portuguese",
- "Amoyese",
- "Borghese",
- "Congoese",
- "Faroese",
- "Foochowese",
- "Genevese",
- "Genoese",
- "Gilbertese",
- "Hottentotese",
- "Kiplingese",
- "Kongoese",
- "Lucchese",
- "Maltese",
- "Nankingese",
- "Niasese",
- "Pekingese",
- "Piedmontese",
- "Pistoiese",
- "Sarawakese",
- "Shavese",
- "Vermontese",
- "Wenchowese",
- "Yengeese",
-]
-
-
-pl_sb_uninflected_endings = [
- # SOME FISH AND HERD ANIMALS
- "fish",
- "deer",
- "sheep",
- # ALL NATIONALS ENDING IN -ese
- "nese",
- "rese",
- "lese",
- "mese",
- # DISEASES
- "pox",
- # OTHER ODDITIES
- "craft",
-] + pl_sb_uninflected_s_endings
-# SOME WORDS ENDING IN ...s (OFTEN PAIRS TAKEN AS A WHOLE)
-
-
-pl_sb_uninflected_bysize = bysize(pl_sb_uninflected_endings)
-
-
-# SINGULAR WORDS ENDING IN ...s (ALL INFLECT WITH ...es)
-
-pl_sb_singular_s_complete = [
- "acropolis",
- "aegis",
- "alias",
- "asbestos",
- "bathos",
- "bias",
- "bronchitis",
- "bursitis",
- "caddis",
- "cannabis",
- "canvas",
- "chaos",
- "cosmos",
- "dais",
- "digitalis",
- "epidermis",
- "ethos",
- "eyas",
- "gas",
- "glottis",
- "hubris",
- "ibis",
- "lens",
- "mantis",
- "marquis",
- "metropolis",
- "pathos",
- "pelvis",
- "polis",
- "rhinoceros",
- "sassafras",
- "trellis",
-] + pl_sb_C_is_ides_complete
-
-
-pl_sb_singular_s_endings = ["ss", "us"] + pl_sb_C_is_ides_endings
-
-pl_sb_singular_s_bysize = bysize(pl_sb_singular_s_endings)
-
-si_sb_singular_s_complete = ["%ses" % w for w in pl_sb_singular_s_complete]
-si_sb_singular_s_endings = ["%ses" % w for w in pl_sb_singular_s_endings]
-si_sb_singular_s_bysize = bysize(si_sb_singular_s_endings)
-
-pl_sb_singular_s_es = ["[A-Z].*es"]
-
-pl_sb_singular_s = enclose(
- "|".join(
- pl_sb_singular_s_complete
- + [".*%s" % w for w in pl_sb_singular_s_endings]
- + pl_sb_singular_s_es
- )
-)
-
-
-# PLURALS ENDING IN uses -> use
-
-
-si_sb_ois_oi_case = ("Bolshois", "Hanois")
-
-si_sb_uses_use_case = ("Betelgeuses", "Duses", "Meuses", "Syracuses", "Toulouses")
-
-si_sb_uses_use = (
- "abuses",
- "applauses",
- "blouses",
- "carouses",
- "causes",
- "chartreuses",
- "clauses",
- "contuses",
- "douses",
- "excuses",
- "fuses",
- "grouses",
- "hypotenuses",
- "masseuses",
- "menopauses",
- "misuses",
- "muses",
- "overuses",
- "pauses",
- "peruses",
- "profuses",
- "recluses",
- "reuses",
- "ruses",
- "souses",
- "spouses",
- "suffuses",
- "transfuses",
- "uses",
-)
-
-si_sb_ies_ie_case = (
- "Addies",
- "Aggies",
- "Allies",
- "Amies",
- "Angies",
- "Annies",
- "Annmaries",
- "Archies",
- "Arties",
- "Aussies",
- "Barbies",
- "Barries",
- "Basies",
- "Bennies",
- "Bernies",
- "Berties",
- "Bessies",
- "Betties",
- "Billies",
- "Blondies",
- "Bobbies",
- "Bonnies",
- "Bowies",
- "Brandies",
- "Bries",
- "Brownies",
- "Callies",
- "Carnegies",
- "Carries",
- "Cassies",
- "Charlies",
- "Cheries",
- "Christies",
- "Connies",
- "Curies",
- "Dannies",
- "Debbies",
- "Dixies",
- "Dollies",
- "Donnies",
- "Drambuies",
- "Eddies",
- "Effies",
- "Ellies",
- "Elsies",
- "Eries",
- "Ernies",
- "Essies",
- "Eugenies",
- "Fannies",
- "Flossies",
- "Frankies",
- "Freddies",
- "Gillespies",
- "Goldies",
- "Gracies",
- "Guthries",
- "Hallies",
- "Hatties",
- "Hetties",
- "Hollies",
- "Jackies",
- "Jamies",
- "Janies",
- "Jannies",
- "Jeanies",
- "Jeannies",
- "Jennies",
- "Jessies",
- "Jimmies",
- "Jodies",
- "Johnies",
- "Johnnies",
- "Josies",
- "Julies",
- "Kalgoorlies",
- "Kathies",
- "Katies",
- "Kellies",
- "Kewpies",
- "Kristies",
- "Laramies",
- "Lassies",
- "Lauries",
- "Leslies",
- "Lessies",
- "Lillies",
- "Lizzies",
- "Lonnies",
- "Lories",
- "Lorries",
- "Lotties",
- "Louies",
- "Mackenzies",
- "Maggies",
- "Maisies",
- "Mamies",
- "Marcies",
- "Margies",
- "Maries",
- "Marjories",
- "Matties",
- "McKenzies",
- "Melanies",
- "Mickies",
- "Millies",
- "Minnies",
- "Mollies",
- "Mounties",
- "Nannies",
- "Natalies",
- "Nellies",
- "Netties",
- "Ollies",
- "Ozzies",
- "Pearlies",
- "Pottawatomies",
- "Reggies",
- "Richies",
- "Rickies",
- "Robbies",
- "Ronnies",
- "Rosalies",
- "Rosemaries",
- "Rosies",
- "Roxies",
- "Rushdies",
- "Ruthies",
- "Sadies",
- "Sallies",
- "Sammies",
- "Scotties",
- "Selassies",
- "Sherries",
- "Sophies",
- "Stacies",
- "Stefanies",
- "Stephanies",
- "Stevies",
- "Susies",
- "Sylvies",
- "Tammies",
- "Terries",
- "Tessies",
- "Tommies",
- "Tracies",
- "Trekkies",
- "Valaries",
- "Valeries",
- "Valkyries",
- "Vickies",
- "Virgies",
- "Willies",
- "Winnies",
- "Wylies",
- "Yorkies",
-)
-
-si_sb_ies_ie = (
- "aeries",
- "baggies",
- "belies",
- "biggies",
- "birdies",
- "bogies",
- "bonnies",
- "boogies",
- "bookies",
- "bourgeoisies",
- "brownies",
- "budgies",
- "caddies",
- "calories",
- "camaraderies",
- "cockamamies",
- "collies",
- "cookies",
- "coolies",
- "cooties",
- "coteries",
- "crappies",
- "curies",
- "cutesies",
- "dogies",
- "eyrie",
- "floozies",
- "footsies",
- "freebies",
- "genies",
- "goalies",
- "groupies",
- "hies",
- "jalousies",
- "junkies",
- "kiddies",
- "laddies",
- "lassies",
- "lies",
- "lingeries",
- "magpies",
- "menageries",
- "mommies",
- "movies",
- "neckties",
- "newbies",
- "nighties",
- "oldies",
- "organdies",
- "overlies",
- "pies",
- "pinkies",
- "pixies",
- "potpies",
- "prairies",
- "quickies",
- "reveries",
- "rookies",
- "rotisseries",
- "softies",
- "sorties",
- "species",
- "stymies",
- "sweeties",
- "ties",
- "underlies",
- "unties",
- "veggies",
- "vies",
- "yuppies",
- "zombies",
-)
-
-
-si_sb_oes_oe_case = (
- "Chloes",
- "Crusoes",
- "Defoes",
- "Faeroes",
- "Ivanhoes",
- "Joes",
- "McEnroes",
- "Moes",
- "Monroes",
- "Noes",
- "Poes",
- "Roscoes",
- "Tahoes",
- "Tippecanoes",
- "Zoes",
-)
-
-si_sb_oes_oe = (
- "aloes",
- "backhoes",
- "canoes",
- "does",
- "floes",
- "foes",
- "hoes",
- "mistletoes",
- "oboes",
- "pekoes",
- "roes",
- "sloes",
- "throes",
- "tiptoes",
- "toes",
- "woes",
-)
-
-si_sb_z_zes = ("quartzes", "topazes")
-
-si_sb_zzes_zz = ("buzzes", "fizzes", "frizzes", "razzes")
-
-si_sb_ches_che_case = (
- "Andromaches",
- "Apaches",
- "Blanches",
- "Comanches",
- "Nietzsches",
- "Porsches",
- "Roches",
-)
-
-si_sb_ches_che = (
- "aches",
- "avalanches",
- "backaches",
- "bellyaches",
- "caches",
- "cloches",
- "creches",
- "douches",
- "earaches",
- "fiches",
- "headaches",
- "heartaches",
- "microfiches",
- "niches",
- "pastiches",
- "psyches",
- "quiches",
- "stomachaches",
- "toothaches",
-)
-
-si_sb_xes_xe = ("annexes", "axes", "deluxes", "pickaxes")
-
-si_sb_sses_sse_case = ("Hesses", "Jesses", "Larousses", "Matisses")
-si_sb_sses_sse = (
- "bouillabaisses",
- "crevasses",
- "demitasses",
- "impasses",
- "mousses",
- "posses",
-)
-
-si_sb_ves_ve_case = (
- # *[nwl]ives -> [nwl]live
- "Clives",
- "Palmolives",
-)
-si_sb_ves_ve = (
- # *[^d]eaves -> eave
- "interweaves",
- "weaves",
- # *[nwl]ives -> [nwl]live
- "olives",
- # *[eoa]lves -> [eoa]lve
- "bivalves",
- "dissolves",
- "resolves",
- "salves",
- "twelves",
- "valves",
-)
-
-
-plverb_special_s = enclose(
- "|".join(
- [pl_sb_singular_s]
- + pl_sb_uninflected_s
- + list(pl_sb_irregular_s.keys())
- + ["(.*[csx])is", "(.*)ceps", "[A-Z].*s"]
- )
-)
-
-pl_sb_postfix_adj = {
- "general": ["(?!major|lieutenant|brigadier|adjutant|.*star)\S+"],
- "martial": ["court"],
-}
-
-for k in list(pl_sb_postfix_adj.keys()):
- pl_sb_postfix_adj[k] = enclose(
- enclose("|".join(pl_sb_postfix_adj[k])) + "(?=(?:-|\\s+)%s)" % k
- )
-
-pl_sb_postfix_adj_stems = "(" + "|".join(list(pl_sb_postfix_adj.values())) + ")(.*)"
-
-
-# PLURAL WORDS ENDING IS es GO TO SINGULAR is
-
-si_sb_es_is = (
- "amanuenses",
- "amniocenteses",
- "analyses",
- "antitheses",
- "apotheoses",
- "arterioscleroses",
- "atheroscleroses",
- "axes",
- # 'bases', # bases -> basis
- "catalyses",
- "catharses",
- "chasses",
- "cirrhoses",
- "cocces",
- "crises",
- "diagnoses",
- "dialyses",
- "diereses",
- "electrolyses",
- "emphases",
- "exegeses",
- "geneses",
- "halitoses",
- "hydrolyses",
- "hypnoses",
- "hypotheses",
- "hystereses",
- "metamorphoses",
- "metastases",
- "misdiagnoses",
- "mitoses",
- "mononucleoses",
- "narcoses",
- "necroses",
- "nemeses",
- "neuroses",
- "oases",
- "osmoses",
- "osteoporoses",
- "paralyses",
- "parentheses",
- "parthenogeneses",
- "periphrases",
- "photosyntheses",
- "probosces",
- "prognoses",
- "prophylaxes",
- "prostheses",
- "preces",
- "psoriases",
- "psychoanalyses",
- "psychokineses",
- "psychoses",
- "scleroses",
- "scolioses",
- "sepses",
- "silicoses",
- "symbioses",
- "synopses",
- "syntheses",
- "taxes",
- "telekineses",
- "theses",
- "thromboses",
- "tuberculoses",
- "urinalyses",
-)
-
-pl_prep_list = """
- about above across after among around at athwart before behind
- below beneath beside besides between betwixt beyond but by
- during except for from in into near of off on onto out over
- since till to under until unto upon with""".split()
-
-pl_prep_list_da = pl_prep_list + ["de", "du", "da"]
-
-pl_prep_bysize = bysize(pl_prep_list_da)
-
-pl_prep = enclose("|".join(pl_prep_list_da))
-
-pl_sb_prep_dual_compound = (
- r"(.*?)((?:-|\s+)(?:" + pl_prep + r")(?:-|\s+))a(?:-|\s+)(.*)"
-)
-
-
-singular_pronoun_genders = set(
- [
- "neuter",
- "feminine",
- "masculine",
- "gender-neutral",
- "feminine or masculine",
- "masculine or feminine",
- ]
-)
-
-pl_pron_nom = {
- # NOMINATIVE REFLEXIVE
- "i": "we",
- "myself": "ourselves",
- "you": "you",
- "yourself": "yourselves",
- "she": "they",
- "herself": "themselves",
- "he": "they",
- "himself": "themselves",
- "it": "they",
- "itself": "themselves",
- "they": "they",
- "themself": "themselves",
- # POSSESSIVE
- "mine": "ours",
- "yours": "yours",
- "hers": "theirs",
- "his": "theirs",
- "its": "theirs",
- "theirs": "theirs",
-}
-
-si_pron = {}
-si_pron["nom"] = dict([(v, k) for (k, v) in pl_pron_nom.items()])
-si_pron["nom"]["we"] = "I"
-
-
-pl_pron_acc = {
- # ACCUSATIVE REFLEXIVE
- "me": "us",
- "myself": "ourselves",
- "you": "you",
- "yourself": "yourselves",
- "her": "them",
- "herself": "themselves",
- "him": "them",
- "himself": "themselves",
- "it": "them",
- "itself": "themselves",
- "them": "them",
- "themself": "themselves",
-}
-
-pl_pron_acc_keys = enclose("|".join(list(pl_pron_acc.keys())))
-pl_pron_acc_keys_bysize = bysize(list(pl_pron_acc.keys()))
-
-si_pron["acc"] = dict([(v, k) for (k, v) in pl_pron_acc.items()])
-
-for thecase, plur, gend, sing in (
- ("nom", "they", "neuter", "it"),
- ("nom", "they", "feminine", "she"),
- ("nom", "they", "masculine", "he"),
- ("nom", "they", "gender-neutral", "they"),
- ("nom", "they", "feminine or masculine", "she or he"),
- ("nom", "they", "masculine or feminine", "he or she"),
- ("nom", "themselves", "neuter", "itself"),
- ("nom", "themselves", "feminine", "herself"),
- ("nom", "themselves", "masculine", "himself"),
- ("nom", "themselves", "gender-neutral", "themself"),
- ("nom", "themselves", "feminine or masculine", "herself or himself"),
- ("nom", "themselves", "masculine or feminine", "himself or herself"),
- ("nom", "theirs", "neuter", "its"),
- ("nom", "theirs", "feminine", "hers"),
- ("nom", "theirs", "masculine", "his"),
- ("nom", "theirs", "gender-neutral", "theirs"),
- ("nom", "theirs", "feminine or masculine", "hers or his"),
- ("nom", "theirs", "masculine or feminine", "his or hers"),
- ("acc", "them", "neuter", "it"),
- ("acc", "them", "feminine", "her"),
- ("acc", "them", "masculine", "him"),
- ("acc", "them", "gender-neutral", "them"),
- ("acc", "them", "feminine or masculine", "her or him"),
- ("acc", "them", "masculine or feminine", "him or her"),
- ("acc", "themselves", "neuter", "itself"),
- ("acc", "themselves", "feminine", "herself"),
- ("acc", "themselves", "masculine", "himself"),
- ("acc", "themselves", "gender-neutral", "themself"),
- ("acc", "themselves", "feminine or masculine", "herself or himself"),
- ("acc", "themselves", "masculine or feminine", "himself or herself"),
-):
- try:
- si_pron[thecase][plur][gend] = sing
- except TypeError:
- si_pron[thecase][plur] = {}
- si_pron[thecase][plur][gend] = sing
-
-
-si_pron_acc_keys = enclose("|".join(list(si_pron["acc"].keys())))
-si_pron_acc_keys_bysize = bysize(list(si_pron["acc"].keys()))
-
-
-def get_si_pron(thecase, word, gender):
- try:
- sing = si_pron[thecase][word]
- except KeyError:
- raise # not a pronoun
- try:
- return sing[gender] # has several types due to gender
- except TypeError:
- return sing # answer independent of gender
-
-
-plverb_irregular_pres = {
- # 1st PERS. SING. 2ND PERS. SING. 3RD PERS. SINGULAR
- # 3RD PERS. (INDET.)
- "am": "are",
- "are": "are",
- "is": "are",
- "was": "were",
- "were": "were",
- "was": "were",
- "have": "have",
- "have": "have",
- "has": "have",
- "do": "do",
- "do": "do",
- "does": "do",
-}
-
-plverb_ambiguous_pres = {
- # 1st PERS. SING. 2ND PERS. SING. 3RD PERS. SINGULAR
- # 3RD PERS. (INDET.)
- "act": "act",
- "act": "act",
- "acts": "act",
- "blame": "blame",
- "blame": "blame",
- "blames": "blame",
- "can": "can",
- "can": "can",
- "can": "can",
- "must": "must",
- "must": "must",
- "must": "must",
- "fly": "fly",
- "fly": "fly",
- "flies": "fly",
- "copy": "copy",
- "copy": "copy",
- "copies": "copy",
- "drink": "drink",
- "drink": "drink",
- "drinks": "drink",
- "fight": "fight",
- "fight": "fight",
- "fights": "fight",
- "fire": "fire",
- "fire": "fire",
- "fires": "fire",
- "like": "like",
- "like": "like",
- "likes": "like",
- "look": "look",
- "look": "look",
- "looks": "look",
- "make": "make",
- "make": "make",
- "makes": "make",
- "reach": "reach",
- "reach": "reach",
- "reaches": "reach",
- "run": "run",
- "run": "run",
- "runs": "run",
- "sink": "sink",
- "sink": "sink",
- "sinks": "sink",
- "sleep": "sleep",
- "sleep": "sleep",
- "sleeps": "sleep",
- "view": "view",
- "view": "view",
- "views": "view",
-}
-
-plverb_ambiguous_pres_keys = enclose("|".join(list(plverb_ambiguous_pres.keys())))
-
-
-plverb_irregular_non_pres = (
- "did",
- "had",
- "ate",
- "made",
- "put",
- "spent",
- "fought",
- "sank",
- "gave",
- "sought",
- "shall",
- "could",
- "ought",
- "should",
-)
-
-plverb_ambiguous_non_pres = enclose(
- "|".join(("thought", "saw", "bent", "will", "might", "cut"))
-)
-
-# "..oes" -> "..oe" (the rest are "..oes" -> "o")
-
-pl_v_oes_oe = ("canoes", "floes", "oboes", "roes", "throes", "woes")
-pl_v_oes_oe_endings_size4 = ("hoes", "toes")
-pl_v_oes_oe_endings_size5 = "shoes"
-
-
-pl_count_zero = ("0", "no", "zero", "nil")
-
-
-pl_count_one = ("1", "a", "an", "one", "each", "every", "this", "that")
-
-pl_adj_special = {"a": "some", "an": "some", "this": "these", "that": "those"}
-
-pl_adj_special_keys = enclose("|".join(list(pl_adj_special.keys())))
-
-pl_adj_poss = {
- "my": "our",
- "your": "your",
- "its": "their",
- "her": "their",
- "his": "their",
- "their": "their",
-}
-
-pl_adj_poss_keys = enclose("|".join(list(pl_adj_poss.keys())))
-
-
-# 2. INDEFINITE ARTICLES
-
-# THIS PATTERN MATCHES STRINGS OF CAPITALS STARTING WITH A "VOWEL-SOUND"
-# CONSONANT FOLLOWED BY ANOTHER CONSONANT, AND WHICH ARE NOT LIKELY
-# TO BE REAL WORDS (OH, ALL RIGHT THEN, IT'S JUST MAGIC!)
-
-A_abbrev = r"""
-(?! FJO | [HLMNS]Y. | RY[EO] | SQU
- | ( F[LR]? | [HL] | MN? | N | RH? | S[CHKLMNPTVW]? | X(YL)?) [AEIOU])
-[FHLMNRSX][A-Z]
-"""
-
-# THIS PATTERN CODES THE BEGINNINGS OF ALL ENGLISH WORDS BEGINING WITH A
-# 'y' FOLLOWED BY A CONSONANT. ANY OTHER Y-CONSONANT PREFIX THEREFORE
-# IMPLIES AN ABBREVIATION.
-
-A_y_cons = "y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)"
-
-# EXCEPTIONS TO EXCEPTIONS
-
-A_explicit_a = enclose("|".join(("unabomber", "unanimous", "US")))
-
-A_explicit_an = enclose(
- "|".join(("euler", "hour(?!i)", "heir", "honest", "hono[ur]", "mpeg"))
-)
-
-A_ordinal_an = enclose("|".join(("[aefhilmnorsx]-?th",)))
-
-A_ordinal_a = enclose("|".join(("[bcdgjkpqtuvwyz]-?th",)))
-
-
-# NUMERICAL INFLECTIONS
-
-nth = {
- 0: "th",
- 1: "st",
- 2: "nd",
- 3: "rd",
- 4: "th",
- 5: "th",
- 6: "th",
- 7: "th",
- 8: "th",
- 9: "th",
- 11: "th",
- 12: "th",
- 13: "th",
-}
-
-ordinal = dict(
- ty="tieth",
- one="first",
- two="second",
- three="third",
- five="fifth",
- eight="eighth",
- nine="ninth",
- twelve="twelfth",
-)
-
-ordinal_suff = "|".join(list(ordinal.keys()))
-
-
-# NUMBERS
-
-unit = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
-teen = [
- "ten",
- "eleven",
- "twelve",
- "thirteen",
- "fourteen",
- "fifteen",
- "sixteen",
- "seventeen",
- "eighteen",
- "nineteen",
-]
-ten = [
- "",
- "",
- "twenty",
- "thirty",
- "forty",
- "fifty",
- "sixty",
- "seventy",
- "eighty",
- "ninety",
-]
-mill = [
- " ",
- " thousand",
- " million",
- " billion",
- " trillion",
- " quadrillion",
- " quintillion",
- " sextillion",
- " septillion",
- " octillion",
- " nonillion",
- " decillion",
-]
-
-
-# SUPPORT CLASSICAL PLURALIZATIONS
-
-def_classical = dict(
- all=False, zero=False, herd=False, names=True, persons=False, ancient=False
-)
-
-all_classical = dict((k, True) for k in list(def_classical.keys()))
-no_classical = dict((k, False) for k in list(def_classical.keys()))
-
-
-# TODO: .inflectrc file does not work
-# can't just execute methods from another file like this
-
-# for rcfile in (pathjoin(dirname(__file__), '.inflectrc'),
-# expanduser(pathjoin(('~'), '.inflectrc'))):
-# if isfile(rcfile):
-# try:
-# execfile(rcfile)
-# except:
-# print3("\nBad .inflectrc file (%s):\n" % rcfile)
-# raise BadRcFileError
-
-
-class engine:
- def __init__(self):
-
- self.classical_dict = def_classical.copy()
- self.persistent_count = None
- self.mill_count = 0
- self.pl_sb_user_defined = []
- self.pl_v_user_defined = []
- self.pl_adj_user_defined = []
- self.si_sb_user_defined = []
- self.A_a_user_defined = []
- self.thegender = "neuter"
-
- deprecated_methods = dict(
- pl="plural",
- plnoun="plural_noun",
- plverb="plural_verb",
- pladj="plural_adj",
- sinoun="single_noun",
- prespart="present_participle",
- numwords="number_to_words",
- plequal="compare",
- plnounequal="compare_nouns",
- plverbequal="compare_verbs",
- pladjequal="compare_adjs",
- wordlist="join",
- )
-
- def __getattr__(self, meth):
- if meth in self.deprecated_methods:
- print3("%s() deprecated, use %s()" % (meth, self.deprecated_methods[meth]))
- raise DeprecationWarning
- raise AttributeError
-
- def defnoun(self, singular, plural):
- """
- Set the noun plural of singular to plural.
-
- """
- self.checkpat(singular)
- self.checkpatplural(plural)
- self.pl_sb_user_defined.extend((singular, plural))
- self.si_sb_user_defined.extend((plural, singular))
- return 1
-
- def defverb(self, s1, p1, s2, p2, s3, p3):
- """
- Set the verb plurals for s1, s2 and s3 to p1, p2 and p3 respectively.
-
- Where 1, 2 and 3 represent the 1st, 2nd and 3rd person forms of the verb.
-
- """
- self.checkpat(s1)
- self.checkpat(s2)
- self.checkpat(s3)
- self.checkpatplural(p1)
- self.checkpatplural(p2)
- self.checkpatplural(p3)
- self.pl_v_user_defined.extend((s1, p1, s2, p2, s3, p3))
- return 1
-
- def defadj(self, singular, plural):
- """
- Set the adjective plural of singular to plural.
-
- """
- self.checkpat(singular)
- self.checkpatplural(plural)
- self.pl_adj_user_defined.extend((singular, plural))
- return 1
-
- def defa(self, pattern):
- """
- Define the indefinate article as 'a' for words matching pattern.
-
- """
- self.checkpat(pattern)
- self.A_a_user_defined.extend((pattern, "a"))
- return 1
-
- def defan(self, pattern):
- """
- Define the indefinate article as 'an' for words matching pattern.
-
- """
- self.checkpat(pattern)
- self.A_a_user_defined.extend((pattern, "an"))
- return 1
-
- def checkpat(self, pattern):
- """
- check for errors in a regex pattern
- """
- if pattern is None:
- return
- try:
- match(pattern, "")
- except reerror:
- print3("\nBad user-defined singular pattern:\n\t%s\n" % pattern)
- raise BadUserDefinedPatternError
-
- def checkpatplural(self, pattern):
- """
- check for errors in a regex replace pattern
- """
- return
- # can't find a pattern that doesn't pass the following test:
- # if pattern is None:
- # return
- # try:
- # resub('', pattern, '')
- # except reerror:
- # print3("\nBad user-defined plural pattern:\n\t%s\n" % pattern)
- # raise BadUserDefinedPatternError
-
- def ud_match(self, word, wordlist):
- for i in range(len(wordlist) - 2, -2, -2): # backwards through even elements
- mo = search(r"^%s$" % wordlist[i], word, IGNORECASE)
- if mo:
- if wordlist[i + 1] is None:
- return None
- pl = resub(
- r"\$(\d+)", r"\\1", wordlist[i + 1]
- ) # change $n to \n for expand
- return mo.expand(pl)
- return None
-
- def classical(self, **kwargs):
- """
- turn classical mode on and off for various categories
-
- turn on all classical modes:
- classical()
- classical(all=True)
-
- turn on or off specific claassical modes:
- e.g.
- classical(herd=True)
- classical(names=False)
-
- By default all classical modes are off except names.
-
- unknown value in args or key in kwargs rasies exception: UnknownClasicalModeError
-
- """
- classical_mode = list(def_classical.keys())
- if not kwargs:
- self.classical_dict = all_classical.copy()
- return
- if "all" in kwargs:
- if kwargs["all"]:
- self.classical_dict = all_classical.copy()
- else:
- self.classical_dict = no_classical.copy()
-
- for k, v in list(kwargs.items()):
- if k in classical_mode:
- self.classical_dict[k] = v
- else:
- raise UnknownClassicalModeError
-
- def num(self, count=None, show=None): # (;$count,$show)
- """
- Set the number to be used in other method calls.
-
- Returns count.
-
- Set show to False to return '' instead.
-
- """
- if count is not None:
- try:
- self.persistent_count = int(count)
- except ValueError:
- raise BadNumValueError
- if (show is None) or show:
- return str(count)
- else:
- self.persistent_count = None
- return ""
-
- def gender(self, gender):
- """
- set the gender for the singular of plural pronouns
-
- can be one of:
- 'neuter' ('they' -> 'it')
- 'feminine' ('they' -> 'she')
- 'masculine' ('they' -> 'he')
- 'gender-neutral' ('they' -> 'they')
- 'feminine or masculine' ('they' -> 'she or he')
- 'masculine or feminine' ('they' -> 'he or she')
- """
- if gender in singular_pronoun_genders:
- self.thegender = gender
- else:
- raise BadGenderError
-
- def nummo(self, matchobject):
- """
- num but take a matchobject
- use groups 1 and 2 in matchobject
- """
- return self.num(matchobject.group(1), matchobject.group(2))
-
- def plmo(self, matchobject):
- """
- plural but take a matchobject
- use groups 1 and 3 in matchobject
- """
- return self.plural(matchobject.group(1), matchobject.group(3))
-
- def plnounmo(self, matchobject):
- """
- plural_noun but take a matchobject
- use groups 1 and 3 in matchobject
- """
- return self.plural_noun(matchobject.group(1), matchobject.group(3))
-
- def plverbmo(self, matchobject):
- """
- plural_verb but take a matchobject
- use groups 1 and 3 in matchobject
- """
- return self.plural_verb(matchobject.group(1), matchobject.group(3))
-
- def pladjmo(self, matchobject):
- """
- plural_adj but take a matchobject
- use groups 1 and 3 in matchobject
- """
- return self.plural_adj(matchobject.group(1), matchobject.group(3))
-
- def sinounmo(self, matchobject):
- """
- singular_noun but take a matchobject
- use groups 1 and 3 in matchobject
- """
- return self.singular_noun(matchobject.group(1), matchobject.group(3))
-
- def amo(self, matchobject):
- """
- A but take a matchobject
- use groups 1 and 3 in matchobject
- """
- if matchobject.group(3) is None:
- return self.a(matchobject.group(1))
- return self.a(matchobject.group(1), matchobject.group(3))
-
- def nomo(self, matchobject):
- """
- NO but take a matchobject
- use groups 1 and 3 in matchobject
- """
- return self.no(matchobject.group(1), matchobject.group(3))
-
- def ordinalmo(self, matchobject):
- """
- ordinal but take a matchobject
- use group 1
- """
- return self.ordinal(matchobject.group(1))
-
- def numwordsmo(self, matchobject):
- """
- number_to_words but take a matchobject
- use group 1
- """
- return self.number_to_words(matchobject.group(1))
-
- def prespartmo(self, matchobject):
- """
- prespart but take a matchobject
- use group 1
- """
- return self.present_participle(matchobject.group(1))
-
- # 0. PERFORM GENERAL INFLECTIONS IN A STRING
-
- def inflect(self, text):
- """
- Perform inflections in a string.
-
- e.g. inflect('The plural of cat is plural(cat)') returns
- 'The plural of cat is cats'
-
- can use plural, plural_noun, plural_verb, plural_adj, singular_noun, a, an, no, ordinal,
- number_to_words and prespart
-
- """
- save_persistent_count = self.persistent_count
- sections = splitre(r"(num\([^)]*\))", text)
- inflection = []
-
- for section in sections:
- (section, count) = subn(
- r"num\(\s*?(?:([^),]*)(?:,([^)]*))?)?\)", self.nummo, section
- )
- if not count:
- total = -1
- while total:
- (section, total) = subn(
- r"(?x)\bplural \( ([^),]*) (, ([^)]*) )? \) ",
- self.plmo,
- section,
- )
- (section, count) = subn(
- r"(?x)\bplural_noun \( ([^),]*) (, ([^)]*) )? \) ",
- self.plnounmo,
- section,
- )
- total += count
- (section, count) = subn(
- r"(?x)\bplural_verb \( ([^),]*) (, ([^)]*) )? \) ",
- self.plverbmo,
- section,
- )
- total += count
- (section, count) = subn(
- r"(?x)\bplural_adj \( ([^),]*) (, ([^)]*) )? \) ",
- self.pladjmo,
- section,
- )
- total += count
- (section, count) = subn(
- r"(?x)\bsingular_noun \( ([^),]*) (, ([^)]*) )? \) ",
- self.sinounmo,
- section,
- )
- total += count
- (section, count) = subn(
- r"(?x)\ban? \( ([^),]*) (, ([^)]*) )? \) ",
- self.amo,
- section,
- )
- total += count
- (section, count) = subn(
- r"(?x)\bno \( ([^),]*) (, ([^)]*) )? \) ",
- self.nomo,
- section,
- )
- total += count
- (section, count) = subn(
- r"(?x)\bordinal \( ([^)]*) \) ",
- self.ordinalmo,
- section,
- )
- total += count
- (section, count) = subn(
- r"(?x)\bnumber_to_words \( ([^)]*) \) ",
- self.numwordsmo,
- section,
- )
- total += count
- (section, count) = subn(
- r"(?x)\bpresent_participle \( ([^)]*) \) ",
- self.prespartmo,
- section,
- )
- total += count
-
- inflection.append(section)
-
- self.persistent_count = save_persistent_count
- return "".join(inflection)
-
- # ## PLURAL SUBROUTINES
-
- def postprocess(self, orig, inflected):
- """
- FIX PEDANTRY AND CAPITALIZATION :-)
- """
- if "|" in inflected:
- inflected = inflected.split("|")[self.classical_dict["all"]]
- if orig == "I":
- return inflected
- if orig == orig.upper():
- return inflected.upper()
- if orig[0] == orig[0].upper():
- return "%s%s" % (inflected[0].upper(), inflected[1:])
- return inflected
-
- def partition_word(self, text):
- mo = search(r"\A(\s*)(.+?)(\s*)\Z", text)
- try:
- return mo.group(1), mo.group(2), mo.group(3)
- except AttributeError: # empty string
- return "", "", ""
-
- # def pl(self, *args, **kwds):
- # print 'pl() deprecated, use plural()'
- # raise DeprecationWarning
- # return self.plural(*args, **kwds)
- #
- # def plnoun(self, *args, **kwds):
- # print 'plnoun() deprecated, use plural_noun()'
- # raise DeprecationWarning
- # return self.plural_noun(*args, **kwds)
- #
- # def plverb(self, *args, **kwds):
- # print 'plverb() deprecated, use plural_verb()'
- # raise DeprecationWarning
- # return self.plural_verb(*args, **kwds)
- #
- # def pladj(self, *args, **kwds):
- # print 'pladj() deprecated, use plural_adj()'
- # raise DeprecationWarning
- # return self.plural_adj(*args, **kwds)
- #
- # def sinoun(self, *args, **kwds):
- # print 'sinoun() deprecated, use singular_noun()'
- # raise DeprecationWarning
- # return self.singular_noun(*args, **kwds)
- #
- # def prespart(self, *args, **kwds):
- # print 'prespart() deprecated, use present_participle()'
- # raise DeprecationWarning
- # return self.present_participle(*args, **kwds)
- #
- # def numwords(self, *args, **kwds):
- # print 'numwords() deprecated, use number_to_words()'
- # raise DeprecationWarning
- # return self.number_to_words(*args, **kwds)
-
- def plural(self, text, count=None):
- """
- Return the plural of text.
-
- If count supplied, then return text if count is one of:
- 1, a, an, one, each, every, this, that
- otherwise return the plural.
-
- Whitespace at the start and end is preserved.
-
- """
- pre, word, post = self.partition_word(text)
- if not word:
- return text
- plural = self.postprocess(
- word,
- self._pl_special_adjective(word, count)
- or self._pl_special_verb(word, count)
- or self._plnoun(word, count),
- )
- return "%s%s%s" % (pre, plural, post)
-
- def plural_noun(self, text, count=None):
- """
- Return the plural of text, where text is a noun.
-
- If count supplied, then return text if count is one of:
- 1, a, an, one, each, every, this, that
- otherwise return the plural.
-
- Whitespace at the start and end is preserved.
-
- """
- pre, word, post = self.partition_word(text)
- if not word:
- return text
- plural = self.postprocess(word, self._plnoun(word, count))
- return "%s%s%s" % (pre, plural, post)
-
- def plural_verb(self, text, count=None):
- """
- Return the plural of text, where text is a verb.
-
- If count supplied, then return text if count is one of:
- 1, a, an, one, each, every, this, that
- otherwise return the plural.
-
- Whitespace at the start and end is preserved.
-
- """
- pre, word, post = self.partition_word(text)
- if not word:
- return text
- plural = self.postprocess(
- word,
- self._pl_special_verb(word, count) or self._pl_general_verb(word, count),
- )
- return "%s%s%s" % (pre, plural, post)
-
- def plural_adj(self, text, count=None):
- """
- Return the plural of text, where text is an adjective.
-
- If count supplied, then return text if count is one of:
- 1, a, an, one, each, every, this, that
- otherwise return the plural.
-
- Whitespace at the start and end is preserved.
-
- """
- pre, word, post = self.partition_word(text)
- if not word:
- return text
- plural = self.postprocess(word, self._pl_special_adjective(word, count) or word)
- return "%s%s%s" % (pre, plural, post)
-
- def compare(self, word1, word2):
- """
- compare word1 and word2 for equality regardless of plurality
-
- return values:
- eq - the strings are equal
- p:s - word1 is the plural of word2
- s:p - word2 is the plural of word1
- p:p - word1 and word2 are two different plural forms of the one word
- False - otherwise
-
- """
- return (
- self._plequal(word1, word2, self.plural_noun)
- or self._plequal(word1, word2, self.plural_verb)
- or self._plequal(word1, word2, self.plural_adj)
- )
-
- def compare_nouns(self, word1, word2):
- """
- compare word1 and word2 for equality regardless of plurality
- word1 and word2 are to be treated as nouns
-
- return values:
- eq - the strings are equal
- p:s - word1 is the plural of word2
- s:p - word2 is the plural of word1
- p:p - word1 and word2 are two different plural forms of the one word
- False - otherwise
-
- """
- return self._plequal(word1, word2, self.plural_noun)
-
- def compare_verbs(self, word1, word2):
- """
- compare word1 and word2 for equality regardless of plurality
- word1 and word2 are to be treated as verbs
-
- return values:
- eq - the strings are equal
- p:s - word1 is the plural of word2
- s:p - word2 is the plural of word1
- p:p - word1 and word2 are two different plural forms of the one word
- False - otherwise
-
- """
- return self._plequal(word1, word2, self.plural_verb)
-
- def compare_adjs(self, word1, word2):
- """
- compare word1 and word2 for equality regardless of plurality
- word1 and word2 are to be treated as adjectives
-
- return values:
- eq - the strings are equal
- p:s - word1 is the plural of word2
- s:p - word2 is the plural of word1
- p:p - word1 and word2 are two different plural forms of the one word
- False - otherwise
-
- """
- return self._plequal(word1, word2, self.plural_adj)
-
- def singular_noun(self, text, count=None, gender=None):
- """
- Return the singular of text, where text is a plural noun.
-
- If count supplied, then return the singular if count is one of:
- 1, a, an, one, each, every, this, that or if count is None
- otherwise return text unchanged.
-
- Whitespace at the start and end is preserved.
-
- """
- pre, word, post = self.partition_word(text)
- if not word:
- return text
- sing = self._sinoun(word, count=count, gender=gender)
- if sing is not False:
- plural = self.postprocess(
- word, self._sinoun(word, count=count, gender=gender)
- )
- return "%s%s%s" % (pre, plural, post)
- return False
-
- def _plequal(self, word1, word2, pl):
- classval = self.classical_dict.copy()
- self.classical_dict = all_classical.copy()
- if word1 == word2:
- return "eq"
- if word1 == pl(word2):
- return "p:s"
- if pl(word1) == word2:
- return "s:p"
- self.classical_dict = no_classical.copy()
- if word1 == pl(word2):
- return "p:s"
- if pl(word1) == word2:
- return "s:p"
- self.classical_dict = classval.copy()
-
- if pl == self.plural or pl == self.plural_noun:
- if self._pl_check_plurals_N(word1, word2):
- return "p:p"
- if self._pl_check_plurals_N(word2, word1):
- return "p:p"
- if pl == self.plural or pl == self.plural_adj:
- if self._pl_check_plurals_adj(word1, word2):
- return "p:p"
- return False
-
- def _pl_reg_plurals(self, pair, stems, end1, end2):
- if search(r"(%s)(%s\|\1%s|%s\|\1%s)" % (stems, end1, end2, end2, end1), pair):
- return True
- return False
-
- def _pl_check_plurals_N(self, word1, word2):
- pair = "%s|%s" % (word1, word2)
- if pair in list(pl_sb_irregular_s.values()):
- return True
- if pair in list(pl_sb_irregular.values()):
- return True
- if pair in list(pl_sb_irregular_caps.values()):
- return True
-
- for (stems, end1, end2) in (
- (pl_sb_C_a_ata, "as", "ata"),
- (pl_sb_C_is_ides, "is", "ides"),
- (pl_sb_C_a_ae, "s", "e"),
- (pl_sb_C_en_ina, "ens", "ina"),
- (pl_sb_C_um_a, "ums", "a"),
- (pl_sb_C_us_i, "uses", "i"),
- (pl_sb_C_on_a, "ons", "a"),
- (pl_sb_C_o_i_stems, "os", "i"),
- (pl_sb_C_ex_ices, "exes", "ices"),
- (pl_sb_C_ix_ices, "ixes", "ices"),
- (pl_sb_C_i, "s", "i"),
- (pl_sb_C_im, "s", "im"),
- (".*eau", "s", "x"),
- (".*ieu", "s", "x"),
- (".*tri", "xes", "ces"),
- (".{2,}[yia]n", "xes", "ges"),
- ):
- if self._pl_reg_plurals(pair, stems, end1, end2):
- return True
- return False
-
- def _pl_check_plurals_adj(self, word1, word2):
- # VERSION: tuple in endswith requires python 2.5
- word1a = word1[: word1.rfind("'")] if word1.endswith(("'s", "'")) else ""
- word2a = word2[: word2.rfind("'")] if word2.endswith(("'s", "'")) else ""
- # TODO: BUG? report upstream. I don't think you should chop off the s'
- # word1b = word1[:-2] if word1.endswith("s'") else ''
- # word2b = word2[:-2] if word2.endswith("s'") else ''
-
- # TODO: dresses', dresses's -> dresses, dresses when chop off letters
- # then they return False because they are the same. Need to fix this.
-
- if word1a:
- if word2a and (
- self._pl_check_plurals_N(word1a, word2a)
- or self._pl_check_plurals_N(word2a, word1a)
- ):
- return True
- # if word2b and ( self._pl_check_plurals_N(word1a, word2b)
- # or self._pl_check_plurals_N(word2b, word1a) ):
- # return True
-
- # if word1b:
- # if word2a and ( self._pl_check_plurals_N(word1b, word2a)
- # or self._pl_check_plurals_N(word2a, word1b) ):
- # return True
- # if word2b and ( self._pl_check_plurals_N(word1b, word2b)
- # or self._pl_check_plurals_N(word2b, word1b) ):
- # return True
-
- return False
-
- def get_count(self, count=None):
- if count is None and self.persistent_count is not None:
- count = self.persistent_count
-
- if count is not None:
- count = (
- 1
- if (
- (str(count) in pl_count_one)
- or (
- self.classical_dict["zero"]
- and str(count).lower() in pl_count_zero
- )
- )
- else 2
- )
- else:
- count = ""
- return count
-
- # @profile
- def _plnoun(self, word, count=None):
- count = self.get_count(count)
-
- # DEFAULT TO PLURAL
-
- if count == 1:
- return word
-
- # HANDLE USER-DEFINED NOUNS
-
- value = self.ud_match(word, self.pl_sb_user_defined)
- if value is not None:
- return value
-
- # HANDLE EMPTY WORD, SINGULAR COUNT AND UNINFLECTED PLURALS
-
- if word == "":
- return word
-
- lowerword = word.lower()
-
- if lowerword in pl_sb_uninflected_complete:
- return word
-
- if word in pl_sb_uninflected_caps:
- return word
-
- for k, v in pl_sb_uninflected_bysize.items():
- if lowerword[-k:] in v:
- return word
-
- if self.classical_dict["herd"] and lowerword in pl_sb_uninflected_herd:
- return word
-
- # HANDLE COMPOUNDS ("Governor General", "mother-in-law", "aide-de-camp", ETC.)
-
- mo = search(r"^(?:%s)$" % pl_sb_postfix_adj_stems, word, IGNORECASE)
- if mo and mo.group(2) != "":
- return "%s%s" % (self._plnoun(mo.group(1), 2), mo.group(2))
-
- if " a " in lowerword or "-a-" in lowerword:
- mo = search(r"^(?:%s)$" % pl_sb_prep_dual_compound, word, IGNORECASE)
- if mo and mo.group(2) != "" and mo.group(3) != "":
- return "%s%s%s" % (
- self._plnoun(mo.group(1), 2),
- mo.group(2),
- self._plnoun(mo.group(3)),
- )
-
- lowersplit = lowerword.split(" ")
- if len(lowersplit) >= 3:
- for numword in range(1, len(lowersplit) - 1):
- if lowersplit[numword] in pl_prep_list_da:
- return " ".join(
- lowersplit[: numword - 1]
- + [self._plnoun(lowersplit[numword - 1], 2)]
- + lowersplit[numword:]
- )
-
- lowersplit = lowerword.split("-")
- if len(lowersplit) >= 3:
- for numword in range(1, len(lowersplit) - 1):
- if lowersplit[numword] in pl_prep_list_da:
- return " ".join(
- lowersplit[: numword - 1]
- + [
- self._plnoun(lowersplit[numword - 1], 2)
- + "-"
- + lowersplit[numword]
- + "-"
- ]
- ) + " ".join(lowersplit[(numword + 1) :])
-
- # HANDLE PRONOUNS
-
- for k, v in pl_pron_acc_keys_bysize.items():
- if lowerword[-k:] in v: # ends with accusivate pronoun
- for pk, pv in pl_prep_bysize.items():
- if lowerword[:pk] in pv: # starts with a prep
- if lowerword.split() == [
- lowerword[:pk],
- lowerword[-k:],
- ]: # only whitespace in between
- return lowerword[:-k] + pl_pron_acc[lowerword[-k:]]
-
- try:
- return pl_pron_nom[word.lower()]
- except KeyError:
- pass
-
- try:
- return pl_pron_acc[word.lower()]
- except KeyError:
- pass
-
- # HANDLE ISOLATED IRREGULAR PLURALS
-
- wordsplit = word.split()
- wordlast = wordsplit[-1]
- lowerwordlast = wordlast.lower()
-
- if wordlast in list(pl_sb_irregular_caps.keys()):
- llen = len(wordlast)
- return "%s%s" % (word[:-llen], pl_sb_irregular_caps[wordlast])
-
- if lowerwordlast in list(pl_sb_irregular.keys()):
- llen = len(lowerwordlast)
- return "%s%s" % (word[:-llen], pl_sb_irregular[lowerwordlast])
-
- if (" ".join(wordsplit[-2:])).lower() in list(pl_sb_irregular_compound.keys()):
- llen = len(
- " ".join(wordsplit[-2:])
- ) # TODO: what if 2 spaces between these words?
- return "%s%s" % (
- word[:-llen],
- pl_sb_irregular_compound[(" ".join(wordsplit[-2:])).lower()],
- )
-
- if lowerword[-3:] == "quy":
- return word[:-1] + "ies"
-
- if lowerword[-6:] == "person":
- if self.classical_dict["persons"]:
- return word + "s"
- else:
- return word[:-4] + "ople"
-
- # HANDLE FAMILIES OF IRREGULAR PLURALS
-
- if lowerword[-3:] == "man":
- for k, v in pl_sb_U_man_mans_bysize.items():
- if lowerword[-k:] in v:
- return word + "s"
- for k, v in pl_sb_U_man_mans_caps_bysize.items():
- if word[-k:] in v:
- return word + "s"
- return word[:-3] + "men"
- if lowerword[-5:] == "mouse":
- return word[:-5] + "mice"
- if lowerword[-5:] == "louse":
- return word[:-5] + "lice"
- if lowerword[-5:] == "goose":
- return word[:-5] + "geese"
- if lowerword[-5:] == "tooth":
- return word[:-5] + "teeth"
- if lowerword[-4:] == "foot":
- return word[:-4] + "feet"
-
- if lowerword == "die":
- return "dice"
-
- # HANDLE UNASSIMILATED IMPORTS
-
- if lowerword[-4:] == "ceps":
- return word
- if lowerword[-4:] == "zoon":
- return word[:-2] + "a"
- if lowerword[-3:] in ("cis", "sis", "xis"):
- return word[:-2] + "es"
-
- for lastlet, d, numend, post in (
- ("h", pl_sb_U_ch_chs_bysize, None, "s"),
- ("x", pl_sb_U_ex_ices_bysize, -2, "ices"),
- ("x", pl_sb_U_ix_ices_bysize, -2, "ices"),
- ("m", pl_sb_U_um_a_bysize, -2, "a"),
- ("s", pl_sb_U_us_i_bysize, -2, "i"),
- ("n", pl_sb_U_on_a_bysize, -2, "a"),
- ("a", pl_sb_U_a_ae_bysize, None, "e"),
- ):
- if lowerword[-1] == lastlet: # this test to add speed
- for k, v in d.items():
- if lowerword[-k:] in v:
- return word[:numend] + post
-
- # HANDLE INCOMPLETELY ASSIMILATED IMPORTS
-
- if self.classical_dict["ancient"]:
- if lowerword[-4:] == "trix":
- return word[:-1] + "ces"
- if lowerword[-3:] in ("eau", "ieu"):
- return word + "x"
- if lowerword[-3:] in ("ynx", "inx", "anx") and len(word) > 4:
- return word[:-1] + "ges"
-
- for lastlet, d, numend, post in (
- ("n", pl_sb_C_en_ina_bysize, -2, "ina"),
- ("x", pl_sb_C_ex_ices_bysize, -2, "ices"),
- ("x", pl_sb_C_ix_ices_bysize, -2, "ices"),
- ("m", pl_sb_C_um_a_bysize, -2, "a"),
- ("s", pl_sb_C_us_i_bysize, -2, "i"),
- ("s", pl_sb_C_us_us_bysize, None, ""),
- ("a", pl_sb_C_a_ae_bysize, None, "e"),
- ("a", pl_sb_C_a_ata_bysize, None, "ta"),
- ("s", pl_sb_C_is_ides_bysize, -1, "des"),
- ("o", pl_sb_C_o_i_bysize, -1, "i"),
- ("n", pl_sb_C_on_a_bysize, -2, "a"),
- ):
- if lowerword[-1] == lastlet: # this test to add speed
- for k, v in d.items():
- if lowerword[-k:] in v:
- return word[:numend] + post
-
- for d, numend, post in (
- (pl_sb_C_i_bysize, None, "i"),
- (pl_sb_C_im_bysize, None, "im"),
- ):
- for k, v in d.items():
- if lowerword[-k:] in v:
- return word[:numend] + post
-
- # HANDLE SINGULAR NOUNS ENDING IN ...s OR OTHER SILIBANTS
-
- if lowerword in pl_sb_singular_s_complete:
- return word + "es"
-
- for k, v in pl_sb_singular_s_bysize.items():
- if lowerword[-k:] in v:
- return word + "es"
-
- if lowerword[-2:] == "es" and word[0] == word[0].upper():
- return word + "es"
-
- # Wouldn't special words
- # ending with 's' always have been caught, regardless of them starting
- # with a capital letter (i.e. being names)
- # It makes sense below to do this for words ending in 'y' so that
- # Sally -> Sallys. But not sure it makes sense here. Where is the case
- # of a word ending in s that is caught here and would otherwise have been
- # caught below?
- #
- # removing it as I can't find a case that executes it
- # TODO: check this again
- #
- # if (self.classical_dict['names']):
- # mo = search(r"([A-Z].*s)$", word)
- # if mo:
- # return "%ses" % mo.group(1)
-
- if lowerword[-1] == "z":
- for k, v in pl_sb_z_zes_bysize.items():
- if lowerword[-k:] in v:
- return word + "es"
-
- if lowerword[-2:-1] != "z":
- return word + "zes"
-
- if lowerword[-2:] == "ze":
- for k, v in pl_sb_ze_zes_bysize.items():
- if lowerword[-k:] in v:
- return word + "s"
-
- if lowerword[-2:] in ("ch", "sh", "zz", "ss") or lowerword[-1] == "x":
- return word + "es"
-
- # ## (r"(.*)(us)$", "%s%ses"), TODO: why is this commented?
-
- # HANDLE ...f -> ...ves
-
- if lowerword[-3:] in ("elf", "alf", "olf"):
- return word[:-1] + "ves"
- if lowerword[-3:] == "eaf" and lowerword[-4:-3] != "d":
- return word[:-1] + "ves"
- if lowerword[-4:] in ("nife", "life", "wife"):
- return word[:-2] + "ves"
- if lowerword[-3:] == "arf":
- return word[:-1] + "ves"
-
- # HANDLE ...y
-
- if lowerword[-1] == "y":
- if lowerword[-2:-1] in "aeiou" or len(word) == 1:
- return word + "s"
-
- if self.classical_dict["names"]:
- if lowerword[-1] == "y" and word[0] == word[0].upper():
- return word + "s"
-
- return word[:-1] + "ies"
-
- # HANDLE ...o
-
- if lowerword in pl_sb_U_o_os_complete:
- return word + "s"
-
- for k, v in pl_sb_U_o_os_bysize.items():
- if lowerword[-k:] in v:
- return word + "s"
-
- if lowerword[-2:] in ("ao", "eo", "io", "oo", "uo"):
- return word + "s"
-
- if lowerword[-1] == "o":
- return word + "es"
-
- # OTHERWISE JUST ADD ...s
-
- return "%ss" % word
-
- def _pl_special_verb(self, word, count=None):
- if self.classical_dict["zero"] and str(count).lower() in pl_count_zero:
- return False
- count = self.get_count(count)
-
- if count == 1:
- return word
-
- # HANDLE USER-DEFINED VERBS
-
- value = self.ud_match(word, self.pl_v_user_defined)
- if value is not None:
- return value
-
- # HANDLE IRREGULAR PRESENT TENSE (SIMPLE AND COMPOUND)
-
- lowerword = word.lower()
- try:
- firstword = lowerword.split()[0]
- except IndexError:
- return False # word is ''
-
- if firstword in list(plverb_irregular_pres.keys()):
- return "%s%s" % (plverb_irregular_pres[firstword], word[len(firstword) :])
-
- # HANDLE IRREGULAR FUTURE, PRETERITE AND PERFECT TENSES
-
- if firstword in plverb_irregular_non_pres:
- return word
-
- # HANDLE PRESENT NEGATIONS (SIMPLE AND COMPOUND)
-
- if firstword.endswith("n't") and firstword[:-3] in list(
- plverb_irregular_pres.keys()
- ):
- return "%sn't%s" % (
- plverb_irregular_pres[firstword[:-3]],
- word[len(firstword) :],
- )
-
- if firstword.endswith("n't"):
- return word
-
- # HANDLE SPECIAL CASES
-
- mo = search(r"^(%s)$" % plverb_special_s, word)
- if mo:
- return False
- if search(r"\s", word):
- return False
- if lowerword == "quizzes":
- return "quiz"
-
- # HANDLE STANDARD 3RD PERSON (CHOP THE ...(e)s OFF SINGLE WORDS)
-
- if (
- lowerword[-4:] in ("ches", "shes", "zzes", "sses")
- or lowerword[-3:] == "xes"
- ):
- return word[:-2]
-
- # # mo = search(r"^(.*)([cs]h|[x]|zz|ss)es$",
- # # word, IGNORECASE)
- # # if mo:
- # # return "%s%s" % (mo.group(1), mo.group(2))
-
- if lowerword[-3:] == "ies" and len(word) > 3:
- return lowerword[:-3] + "y"
-
- if (
- lowerword in pl_v_oes_oe
- or lowerword[-4:] in pl_v_oes_oe_endings_size4
- or lowerword[-5:] in pl_v_oes_oe_endings_size5
- ):
- return word[:-1]
-
- if lowerword.endswith("oes") and len(word) > 3:
- return lowerword[:-2]
-
- mo = search(r"^(.*[^s])s$", word, IGNORECASE)
- if mo:
- return mo.group(1)
-
- # OTHERWISE, A REGULAR VERB (HANDLE ELSEWHERE)
-
- return False
-
- def _pl_general_verb(self, word, count=None):
- count = self.get_count(count)
-
- if count == 1:
- return word
-
- # HANDLE AMBIGUOUS PRESENT TENSES (SIMPLE AND COMPOUND)
-
- mo = search(r"^(%s)((\s.*)?)$" % plverb_ambiguous_pres_keys, word, IGNORECASE)
- if mo:
- return "%s%s" % (plverb_ambiguous_pres[mo.group(1).lower()], mo.group(2))
-
- # HANDLE AMBIGUOUS PRETERITE AND PERFECT TENSES
-
- mo = search(r"^(%s)((\s.*)?)$" % plverb_ambiguous_non_pres, word, IGNORECASE)
- if mo:
- return word
-
- # OTHERWISE, 1st OR 2ND PERSON IS UNINFLECTED
-
- return word
-
- def _pl_special_adjective(self, word, count=None):
- count = self.get_count(count)
-
- if count == 1:
- return word
-
- # HANDLE USER-DEFINED ADJECTIVES
-
- value = self.ud_match(word, self.pl_adj_user_defined)
- if value is not None:
- return value
-
- # HANDLE KNOWN CASES
-
- mo = search(r"^(%s)$" % pl_adj_special_keys, word, IGNORECASE)
- if mo:
- return "%s" % (pl_adj_special[mo.group(1).lower()])
-
- # HANDLE POSSESSIVES
-
- mo = search(r"^(%s)$" % pl_adj_poss_keys, word, IGNORECASE)
- if mo:
- return "%s" % (pl_adj_poss[mo.group(1).lower()])
-
- mo = search(r"^(.*)'s?$", word)
- if mo:
- pl = self.plural_noun(mo.group(1))
- trailing_s = "" if pl[-1] == "s" else "s"
- return "%s'%s" % (pl, trailing_s)
-
- # OTHERWISE, NO IDEA
-
- return False
-
- # @profile
- def _sinoun(self, word, count=None, gender=None):
- count = self.get_count(count)
-
- # DEFAULT TO PLURAL
-
- if count == 2:
- return word
-
- # SET THE GENDER
-
- try:
- if gender is None:
- gender = self.thegender
- elif gender not in singular_pronoun_genders:
- raise BadGenderError
- except (TypeError, IndexError):
- raise BadGenderError
-
- # HANDLE USER-DEFINED NOUNS
-
- value = self.ud_match(word, self.si_sb_user_defined)
- if value is not None:
- return value
-
- # HANDLE EMPTY WORD, SINGULAR COUNT AND UNINFLECTED PLURALS
-
- if word == "":
- return word
-
- lowerword = word.lower()
-
- if word in si_sb_ois_oi_case:
- return word[:-1]
-
- if lowerword in pl_sb_uninflected_complete:
- return word
-
- if word in pl_sb_uninflected_caps:
- return word
-
- for k, v in pl_sb_uninflected_bysize.items():
- if lowerword[-k:] in v:
- return word
-
- if self.classical_dict["herd"] and lowerword in pl_sb_uninflected_herd:
- return word
-
- # HANDLE COMPOUNDS ("Governor General", "mother-in-law", "aide-de-camp", ETC.)
-
- mo = search(r"^(?:%s)$" % pl_sb_postfix_adj_stems, word, IGNORECASE)
- if mo and mo.group(2) != "":
- return "%s%s" % (self._sinoun(mo.group(1), 1, gender=gender), mo.group(2))
-
- # how to reverse this one?
- # mo = search(r"^(?:%s)$" % pl_sb_prep_dual_compound, word, IGNORECASE)
- # if mo and mo.group(2) != '' and mo.group(3) != '':
- # return "%s%s%s" % (self._sinoun(mo.group(1), 1),
- # mo.group(2),
- # self._sinoun(mo.group(3), 1))
-
- lowersplit = lowerword.split(" ")
- if len(lowersplit) >= 3:
- for numword in range(1, len(lowersplit) - 1):
- if lowersplit[numword] in pl_prep_list_da:
- return " ".join(
- lowersplit[: numword - 1]
- + [self._sinoun(lowersplit[numword - 1], 1, gender=gender)]
- + lowersplit[numword:]
- )
-
- lowersplit = lowerword.split("-")
- if len(lowersplit) >= 3:
- for numword in range(1, len(lowersplit) - 1):
- if lowersplit[numword] in pl_prep_list_da:
- return " ".join(
- lowersplit[: numword - 1]
- + [
- self._sinoun(lowersplit[numword - 1], 1, gender=gender)
- + "-"
- + lowersplit[numword]
- + "-"
- ]
- ) + " ".join(lowersplit[(numword + 1) :])
-
- # HANDLE PRONOUNS
-
- for k, v in si_pron_acc_keys_bysize.items():
- if lowerword[-k:] in v: # ends with accusivate pronoun
- for pk, pv in pl_prep_bysize.items():
- if lowerword[:pk] in pv: # starts with a prep
- if lowerword.split() == [
- lowerword[:pk],
- lowerword[-k:],
- ]: # only whitespace in between
- return lowerword[:-k] + get_si_pron(
- "acc", lowerword[-k:], gender
- )
-
- try:
- return get_si_pron("nom", word.lower(), gender)
- except KeyError:
- pass
-
- try:
- return get_si_pron("acc", word.lower(), gender)
- except KeyError:
- pass
-
- # HANDLE ISOLATED IRREGULAR PLURALS
-
- wordsplit = word.split()
- wordlast = wordsplit[-1]
- lowerwordlast = wordlast.lower()
-
- if wordlast in list(si_sb_irregular_caps.keys()):
- llen = len(wordlast)
- return "%s%s" % (word[:-llen], si_sb_irregular_caps[wordlast])
-
- if lowerwordlast in list(si_sb_irregular.keys()):
- llen = len(lowerwordlast)
- return "%s%s" % (word[:-llen], si_sb_irregular[lowerwordlast])
-
- if (" ".join(wordsplit[-2:])).lower() in list(si_sb_irregular_compound.keys()):
- llen = len(
- " ".join(wordsplit[-2:])
- ) # TODO: what if 2 spaces between these words?
- return "%s%s" % (
- word[:-llen],
- si_sb_irregular_compound[(" ".join(wordsplit[-2:])).lower()],
- )
-
- if lowerword[-5:] == "quies":
- return word[:-3] + "y"
-
- if lowerword[-7:] == "persons":
- return word[:-1]
- if lowerword[-6:] == "people":
- return word[:-4] + "rson"
-
- # HANDLE FAMILIES OF IRREGULAR PLURALS
-
- if lowerword[-4:] == "mans":
- for k, v in si_sb_U_man_mans_bysize.items():
- if lowerword[-k:] in v:
- return word[:-1]
- for k, v in si_sb_U_man_mans_caps_bysize.items():
- if word[-k:] in v:
- return word[:-1]
- if lowerword[-3:] == "men":
- return word[:-3] + "man"
- if lowerword[-4:] == "mice":
- return word[:-4] + "mouse"
- if lowerword[-4:] == "lice":
- return word[:-4] + "louse"
- if lowerword[-5:] == "geese":
- return word[:-5] + "goose"
- if lowerword[-5:] == "teeth":
- return word[:-5] + "tooth"
- if lowerword[-4:] == "feet":
- return word[:-4] + "foot"
-
- if lowerword == "dice":
- return "die"
-
- # HANDLE UNASSIMILATED IMPORTS
-
- if lowerword[-4:] == "ceps":
- return word
- if lowerword[-3:] == "zoa":
- return word[:-1] + "on"
-
- for lastlet, d, numend, post in (
- ("s", si_sb_U_ch_chs_bysize, -1, ""),
- ("s", si_sb_U_ex_ices_bysize, -4, "ex"),
- ("s", si_sb_U_ix_ices_bysize, -4, "ix"),
- ("a", si_sb_U_um_a_bysize, -1, "um"),
- ("i", si_sb_U_us_i_bysize, -1, "us"),
- ("a", si_sb_U_on_a_bysize, -1, "on"),
- ("e", si_sb_U_a_ae_bysize, -1, ""),
- ):
- if lowerword[-1] == lastlet: # this test to add speed
- for k, v in d.items():
- if lowerword[-k:] in v:
- return word[:numend] + post
-
- # HANDLE INCOMPLETELY ASSIMILATED IMPORTS
-
- if self.classical_dict["ancient"]:
-
- if lowerword[-6:] == "trices":
- return word[:-3] + "x"
- if lowerword[-4:] in ("eaux", "ieux"):
- return word[:-1]
- if lowerword[-5:] in ("ynges", "inges", "anges") and len(word) > 6:
- return word[:-3] + "x"
-
- for lastlet, d, numend, post in (
- ("a", si_sb_C_en_ina_bysize, -3, "en"),
- ("s", si_sb_C_ex_ices_bysize, -4, "ex"),
- ("s", si_sb_C_ix_ices_bysize, -4, "ix"),
- ("a", si_sb_C_um_a_bysize, -1, "um"),
- ("i", si_sb_C_us_i_bysize, -1, "us"),
- ("s", pl_sb_C_us_us_bysize, None, ""),
- ("e", si_sb_C_a_ae_bysize, -1, ""),
- ("a", si_sb_C_a_ata_bysize, -2, ""),
- ("s", si_sb_C_is_ides_bysize, -3, "s"),
- ("i", si_sb_C_o_i_bysize, -1, "o"),
- ("a", si_sb_C_on_a_bysize, -1, "on"),
- ("m", si_sb_C_im_bysize, -2, ""),
- ("i", si_sb_C_i_bysize, -1, ""),
- ):
- if lowerword[-1] == lastlet: # this test to add speed
- for k, v in d.items():
- if lowerword[-k:] in v:
- return word[:numend] + post
-
- # HANDLE PLURLS ENDING IN uses -> use
-
- if (
- lowerword[-6:] == "houses"
- or word in si_sb_uses_use_case
- or lowerword in si_sb_uses_use
- ):
- return word[:-1]
-
- # HANDLE PLURLS ENDING IN ies -> ie
-
- if word in si_sb_ies_ie_case or lowerword in si_sb_ies_ie:
- return word[:-1]
-
- # HANDLE PLURLS ENDING IN oes -> oe
-
- if (
- lowerword[-5:] == "shoes"
- or word in si_sb_oes_oe_case
- or lowerword in si_sb_oes_oe
- ):
- return word[:-1]
-
- # HANDLE SINGULAR NOUNS ENDING IN ...s OR OTHER SILIBANTS
-
- if word in si_sb_sses_sse_case or lowerword in si_sb_sses_sse:
- return word[:-1]
-
- if lowerword in si_sb_singular_s_complete:
- return word[:-2]
-
- for k, v in si_sb_singular_s_bysize.items():
- if lowerword[-k:] in v:
- return word[:-2]
-
- if lowerword[-4:] == "eses" and word[0] == word[0].upper():
- return word[:-2]
-
- # Wouldn't special words
- # ending with 's' always have been caught, regardless of them starting
- # with a capital letter (i.e. being names)
- # It makes sense below to do this for words ending in 'y' so that
- # Sally -> Sallys. But not sure it makes sense here. Where is the case
- # of a word ending in s that is caught here and would otherwise have been
- # caught below?
- #
- # removing it as I can't find a case that executes it
- # TODO: check this again
- #
- # if (self.classical_dict['names']):
- # mo = search(r"([A-Z].*ses)$", word)
- # if mo:
- # return "%s" % mo.group(1)
-
- if lowerword in si_sb_z_zes:
- return word[:-2]
-
- if lowerword in si_sb_zzes_zz:
- return word[:-2]
-
- if lowerword[-4:] == "zzes":
- return word[:-3]
-
- if word in si_sb_ches_che_case or lowerword in si_sb_ches_che:
- return word[:-1]
-
- if lowerword[-4:] in ("ches", "shes"):
- return word[:-2]
-
- if lowerword in si_sb_xes_xe:
- return word[:-1]
-
- if lowerword[-3:] == "xes":
- return word[:-2]
- # (r"(.*)(us)es$", "%s%s"), TODO: why is this commented?
-
- # HANDLE ...f -> ...ves
-
- if word in si_sb_ves_ve_case or lowerword in si_sb_ves_ve:
- return word[:-1]
-
- if lowerword[-3:] == "ves":
- if lowerword[-5:-3] in ("el", "al", "ol"):
- return word[:-3] + "f"
- if lowerword[-5:-3] == "ea" and word[-6:-5] != "d":
- return word[:-3] + "f"
- if lowerword[-5:-3] in ("ni", "li", "wi"):
- return word[:-3] + "fe"
- if lowerword[-5:-3] == "ar":
- return word[:-3] + "f"
-
- # HANDLE ...y
-
- if lowerword[-2:] == "ys":
- if len(lowerword) > 2 and lowerword[-3] in "aeiou":
- return word[:-1]
-
- if self.classical_dict["names"]:
- if lowerword[-2:] == "ys" and word[0] == word[0].upper():
- return word[:-1]
-
- if lowerword[-3:] == "ies":
- return word[:-3] + "y"
-
- # HANDLE ...o
-
- if lowerword[-2:] == "os":
-
- if lowerword in si_sb_U_o_os_complete:
- return word[:-1]
-
- for k, v in si_sb_U_o_os_bysize.items():
- if lowerword[-k:] in v:
- return word[:-1]
-
- if lowerword[-3:] in ("aos", "eos", "ios", "oos", "uos"):
- return word[:-1]
-
- if lowerword[-3:] == "oes":
- return word[:-2]
-
- # UNASSIMILATED IMPORTS FINAL RULE
-
- if word in si_sb_es_is:
- return word[:-2] + "is"
-
- # OTHERWISE JUST REMOVE ...s
-
- if lowerword[-1] == "s":
- return word[:-1]
-
- # COULD NOT FIND SINGULAR
-
- return False
-
- # ADJECTIVES
-
- def a(self, text, count=1):
- """
- Return the appropriate indefinite article followed by text.
-
- The indefinite article is either 'a' or 'an'.
-
- If count is not one, then return count followed by text
- instead of 'a' or 'an'.
-
- Whitespace at the start and end is preserved.
-
- """
- mo = search(r"\A(\s*)(?:an?\s+)?(.+?)(\s*)\Z", text, IGNORECASE)
- if mo:
- word = mo.group(2)
- if not word:
- return text
- pre = mo.group(1)
- post = mo.group(3)
- result = self._indef_article(word, count)
- return "%s%s%s" % (pre, result, post)
- return ""
-
- an = a
-
- def _indef_article(self, word, count):
- mycount = self.get_count(count)
-
- if mycount != 1:
- return "%s %s" % (count, word)
-
- # HANDLE USER-DEFINED VARIANTS
-
- value = self.ud_match(word, self.A_a_user_defined)
- if value is not None:
- return "%s %s" % (value, word)
-
- # HANDLE ORDINAL FORMS
-
- for a in ((r"^(%s)" % A_ordinal_a, "a"), (r"^(%s)" % A_ordinal_an, "an")):
- mo = search(a[0], word, IGNORECASE)
- if mo:
- return "%s %s" % (a[1], word)
-
- # HANDLE SPECIAL CASES
-
- for a in (
- (r"^(%s)" % A_explicit_an, "an"),
- (r"^[aefhilmnorsx]$", "an"),
- (r"^[bcdgjkpqtuvwyz]$", "a"),
- ):
- mo = search(a[0], word, IGNORECASE)
- if mo:
- return "%s %s" % (a[1], word)
-
- # HANDLE ABBREVIATIONS
-
- for a in (
- (r"(%s)" % A_abbrev, "an", VERBOSE),
- (r"^[aefhilmnorsx][.-]", "an", IGNORECASE),
- (r"^[a-z][.-]", "a", IGNORECASE),
- ):
- mo = search(a[0], word, a[2])
- if mo:
- return "%s %s" % (a[1], word)
-
- # HANDLE CONSONANTS
-
- mo = search(r"^[^aeiouy]", word, IGNORECASE)
- if mo:
- return "a %s" % word
-
- # HANDLE SPECIAL VOWEL-FORMS
-
- for a in (
- (r"^e[uw]", "a"),
- (r"^onc?e\b", "a"),
- (r"^onetime\b", "a"),
- (r"^uni([^nmd]|mo)", "a"),
- (r"^u[bcfghjkqrst][aeiou]", "a"),
- (r"^ukr", "a"),
- (r"^(%s)" % A_explicit_a, "a"),
- ):
- mo = search(a[0], word, IGNORECASE)
- if mo:
- return "%s %s" % (a[1], word)
-
- # HANDLE SPECIAL CAPITALS
-
- mo = search(r"^U[NK][AIEO]?", word)
- if mo:
- return "a %s" % word
-
- # HANDLE VOWELS
-
- mo = search(r"^[aeiou]", word, IGNORECASE)
- if mo:
- return "an %s" % word
-
- # HANDLE y... (BEFORE CERTAIN CONSONANTS IMPLIES (UNNATURALIZED) "i.." SOUND)
-
- mo = search(r"^(%s)" % A_y_cons, word, IGNORECASE)
- if mo:
- return "an %s" % word
-
- # OTHERWISE, GUESS "a"
- return "a %s" % word
-
- # 2. TRANSLATE ZERO-QUANTIFIED $word TO "no plural($word)"
-
- def no(self, text, count=None):
- """
- If count is 0, no, zero or nil, return 'no' followed by the plural
- of text.
-
- If count is one of:
- 1, a, an, one, each, every, this, that
- return count followed by text.
-
- Otherwise return count follow by the plural of text.
-
- In the return value count is always followed by a space.
-
- Whitespace at the start and end is preserved.
-
- """
- if count is None and self.persistent_count is not None:
- count = self.persistent_count
-
- if count is None:
- count = 0
- mo = search(r"\A(\s*)(.+?)(\s*)\Z", text)
- pre = mo.group(1)
- word = mo.group(2)
- post = mo.group(3)
-
- if str(count).lower() in pl_count_zero:
- return "%sno %s%s" % (pre, self.plural(word, 0), post)
- else:
- return "%s%s %s%s" % (pre, count, self.plural(word, count), post)
-
- # PARTICIPLES
-
- def present_participle(self, word):
- """
- Return the present participle for word.
-
- word is the 3rd person singular verb.
-
- """
- plv = self.plural_verb(word, 2)
-
- for pat, repl in (
- (r"ie$", r"y"),
- (r"ue$", r"u"), # TODO: isn't ue$ -> u encompassed in the following rule?
- (r"([auy])e$", r"\g<1>"),
- (r"ski$", r"ski"),
- (r"[^b]i$", r""),
- (r"^(are|were)$", r"be"),
- (r"^(had)$", r"hav"),
- (r"^(hoe)$", r"\g<1>"),
- (r"([^e])e$", r"\g<1>"),
- (r"er$", r"er"),
- (r"([^aeiou][aeiouy]([bdgmnprst]))$", "\g<1>\g<2>"),
- ):
- (ans, num) = subn(pat, repl, plv)
- if num:
- return "%sing" % ans
- return "%sing" % ans
-
- # NUMERICAL INFLECTIONS
-
- def ordinal(self, num):
- """
- Return the ordinal of num.
-
- num can be an integer or text
-
- e.g. ordinal(1) returns '1st'
- ordinal('one') returns 'first'
-
- """
- if match(r"\d", str(num)):
- try:
- num % 2
- n = num
- except TypeError:
- if "." in str(num):
- try:
- n = int(
- num[-1]
- ) # numbers after decimal, so only need last one for ordinal
- except ValueError: # ends with '.', so need to use whole string
- n = int(num[:-1])
- else:
- n = int(num)
- try:
- post = nth[n % 100]
- except KeyError:
- post = nth[n % 10]
- return "%s%s" % (num, post)
- else:
- mo = search(r"(%s)\Z" % ordinal_suff, num)
- try:
- post = ordinal[mo.group(1)]
- return resub(r"(%s)\Z" % ordinal_suff, post, num)
- except AttributeError:
- return "%sth" % num
-
- def millfn(self, ind=0):
- if ind > len(mill) - 1:
- print3("number out of range")
- raise NumOutOfRangeError
- return mill[ind]
-
- def unitfn(self, units, mindex=0):
- return "%s%s" % (unit[units], self.millfn(mindex))
-
- def tenfn(self, tens, units, mindex=0):
- if tens != 1:
- return "%s%s%s%s" % (
- ten[tens],
- "-" if tens and units else "",
- unit[units],
- self.millfn(mindex),
- )
- return "%s%s" % (teen[units], mill[mindex])
-
- def hundfn(self, hundreds, tens, units, mindex):
- if hundreds:
- return "%s hundred%s%s%s, " % (
- unit[hundreds], # use unit not unitfn as simpler
- " %s " % self.number_args["andword"] if tens or units else "",
- self.tenfn(tens, units),
- self.millfn(mindex),
- )
- if tens or units:
- return "%s%s, " % (self.tenfn(tens, units), self.millfn(mindex))
- return ""
-
- def group1sub(self, mo):
- units = int(mo.group(1))
- if units == 1:
- return " %s, " % self.number_args["one"]
- elif units:
- # TODO: bug one and zero are padded with a space but other numbers aren't. check this in perl
- return "%s, " % unit[units]
- else:
- return " %s, " % self.number_args["zero"]
-
- def group1bsub(self, mo):
- units = int(mo.group(1))
- if units:
- # TODO: bug one and zero are padded with a space but other numbers aren't. check this in perl
- return "%s, " % unit[units]
- else:
- return " %s, " % self.number_args["zero"]
-
- def group2sub(self, mo):
- tens = int(mo.group(1))
- units = int(mo.group(2))
- if tens:
- return "%s, " % self.tenfn(tens, units)
- if units:
- return " %s %s, " % (self.number_args["zero"], unit[units])
- return " %s %s, " % (self.number_args["zero"], self.number_args["zero"])
-
- def group3sub(self, mo):
- hundreds = int(mo.group(1))
- tens = int(mo.group(2))
- units = int(mo.group(3))
- if hundreds == 1:
- hunword = " %s" % self.number_args["one"]
- elif hundreds:
- hunword = "%s" % unit[hundreds]
- # TODO: bug one and zero are padded with a space but other numbers aren't. check this in perl
- else:
- hunword = " %s" % self.number_args["zero"]
- if tens:
- tenword = self.tenfn(tens, units)
- elif units:
- tenword = " %s %s" % (self.number_args["zero"], unit[units])
- else:
- tenword = " %s %s" % (self.number_args["zero"], self.number_args["zero"])
- return "%s %s, " % (hunword, tenword)
-
- def hundsub(self, mo):
- ret = self.hundfn(
- int(mo.group(1)), int(mo.group(2)), int(mo.group(3)), self.mill_count
- )
- self.mill_count += 1
- return ret
-
- def tensub(self, mo):
- return "%s, " % self.tenfn(int(mo.group(1)), int(mo.group(2)), self.mill_count)
-
- def unitsub(self, mo):
- return "%s, " % self.unitfn(int(mo.group(1)), self.mill_count)
-
- def enword(self, num, group):
- # import pdb
- # pdb.set_trace()
-
- if group == 1:
- num = resub(r"(\d)", self.group1sub, num)
- elif group == 2:
- num = resub(r"(\d)(\d)", self.group2sub, num)
- num = resub(r"(\d)", self.group1bsub, num, 1)
- # group1bsub same as
- # group1sub except it doesn't use the default word for one.
- # Is this required? i.e. is the default word not to beused when
- # grouping in pairs?
- #
- # No. This is a bug. Fixed. TODO: report upstream.
- elif group == 3:
- num = resub(r"(\d)(\d)(\d)", self.group3sub, num)
- num = resub(r"(\d)(\d)", self.group2sub, num, 1)
- num = resub(r"(\d)", self.group1sub, num, 1)
- elif int(num) == 0:
- num = self.number_args["zero"]
- elif int(num) == 1:
- num = self.number_args["one"]
- else:
- num = num.lstrip().lstrip("0")
- self.mill_count = 0
- # surely there's a better way to do the next bit
- mo = search(r"(\d)(\d)(\d)(?=\D*\Z)", num)
- while mo:
- num = resub(r"(\d)(\d)(\d)(?=\D*\Z)", self.hundsub, num, 1)
- mo = search(r"(\d)(\d)(\d)(?=\D*\Z)", num)
- num = resub(r"(\d)(\d)(?=\D*\Z)", self.tensub, num, 1)
- num = resub(r"(\d)(?=\D*\Z)", self.unitsub, num, 1)
- return num
-
- def blankfn(self, mo):
- """ do a global blank replace
- TODO: surely this can be done with an option to resub
- rather than this fn
- """
- return ""
-
- def commafn(self, mo):
- """ do a global ',' replace
- TODO: surely this can be done with an option to resub
- rather than this fn
- """
- return ","
-
- def spacefn(self, mo):
- """ do a global ' ' replace
- TODO: surely this can be done with an option to resub
- rather than this fn
- """
- return " "
-
- def number_to_words(
- self,
- num,
- wantlist=False,
- group=0,
- comma=",",
- andword="and",
- zero="zero",
- one="one",
- decimal="point",
- threshold=None,
- ):
- """
- Return a number in words.
-
- group = 1, 2 or 3 to group numbers before turning into words
- comma: define comma
- andword: word for 'and'. Can be set to ''.
- e.g. "one hundred and one" vs "one hundred one"
- zero: word for '0'
- one: word for '1'
- decimal: word for decimal point
- threshold: numbers above threshold not turned into words
-
- parameters not remembered from last call. Departure from Perl version.
- """
- self.number_args = dict(andword=andword, zero=zero, one=one)
- num = "%s" % num
-
- # Handle "stylistic" conversions (up to a given threshold)...
- if threshold is not None and float(num) > threshold:
- spnum = num.split(".", 1)
- while comma:
- (spnum[0], n) = subn(r"(\d)(\d{3}(?:,|\Z))", r"\1,\2", spnum[0])
- if n == 0:
- break
- try:
- return "%s.%s" % (spnum[0], spnum[1])
- except IndexError:
- return "%s" % spnum[0]
-
- if group < 0 or group > 3:
- raise BadChunkingOptionError
- nowhite = num.lstrip()
- if nowhite[0] == "+":
- sign = "plus"
- elif nowhite[0] == "-":
- sign = "minus"
- else:
- sign = ""
-
- myord = num[-2:] in ("st", "nd", "rd", "th")
- if myord:
- num = num[:-2]
- finalpoint = False
- if decimal:
- if group != 0:
- chunks = num.split(".")
- else:
- chunks = num.split(".", 1)
- if chunks[-1] == "": # remove blank string if nothing after decimal
- chunks = chunks[:-1]
- finalpoint = True # add 'point' to end of output
- else:
- chunks = [num]
-
- first = 1
- loopstart = 0
-
- if chunks[0] == "":
- first = 0
- if len(chunks) > 1:
- loopstart = 1
-
- for i in range(loopstart, len(chunks)):
- chunk = chunks[i]
- # remove all non numeric \D
- chunk = resub(r"\D", self.blankfn, chunk)
- if chunk == "":
- chunk = "0"
-
- if group == 0 and (first == 0 or first == ""):
- chunk = self.enword(chunk, 1)
- else:
- chunk = self.enword(chunk, group)
-
- if chunk[-2:] == ", ":
- chunk = chunk[:-2]
- chunk = resub(r"\s+,", self.commafn, chunk)
-
- if group == 0 and first:
- chunk = resub(r", (\S+)\s+\Z", " %s \\1" % andword, chunk)
- chunk = resub(r"\s+", self.spacefn, chunk)
- # chunk = resub(r"(\A\s|\s\Z)", self.blankfn, chunk)
- chunk = chunk.strip()
- if first:
- first = ""
- chunks[i] = chunk
-
- numchunks = []
- if first != 0:
- numchunks = chunks[0].split("%s " % comma)
-
- if myord and numchunks:
- # TODO: can this be just one re as it is in perl?
- mo = search(r"(%s)\Z" % ordinal_suff, numchunks[-1])
- if mo:
- numchunks[-1] = resub(
- r"(%s)\Z" % ordinal_suff, ordinal[mo.group(1)], numchunks[-1]
- )
- else:
- numchunks[-1] += "th"
-
- for chunk in chunks[1:]:
- numchunks.append(decimal)
- numchunks.extend(chunk.split("%s " % comma))
-
- if finalpoint:
- numchunks.append(decimal)
-
- # wantlist: Perl list context. can explictly specify in Python
- if wantlist:
- if sign:
- numchunks = [sign] + numchunks
- return numchunks
- elif group:
- signout = "%s " % sign if sign else ""
- return "%s%s" % (signout, ", ".join(numchunks))
- else:
- signout = "%s " % sign if sign else ""
- num = "%s%s" % (signout, numchunks.pop(0))
- if decimal is None:
- first = True
- else:
- first = not num.endswith(decimal)
- for nc in numchunks:
- if nc == decimal:
- num += " %s" % nc
- first = 0
- elif first:
- num += "%s %s" % (comma, nc)
- else:
- num += " %s" % nc
- return num
-
- # Join words with commas and a trailing 'and' (when appropriate)...
-
- def join(
- self,
- words,
- sep=None,
- sep_spaced=True,
- final_sep=None,
- conj="and",
- conj_spaced=True,
- ):
- """
- Join words into a list.
-
- e.g. join(['ant', 'bee', 'fly']) returns 'ant, bee, and fly'
-
- options:
- conj: replacement for 'and'
- sep: separator. default ',', unless ',' is in the list then ';'
- final_sep: final separator. default ',', unless ',' is in the list then ';'
- conj_spaced: boolean. Should conj have spaces around it
-
- """
- if not words:
- return ""
- if len(words) == 1:
- return words[0]
-
- if conj_spaced:
- if conj == "":
- conj = " "
- else:
- conj = " %s " % conj
-
- if len(words) == 2:
- return "%s%s%s" % (words[0], conj, words[1])
-
- if sep is None:
- if "," in "".join(words):
- sep = ";"
- else:
- sep = ","
- if final_sep is None:
- final_sep = sep
-
- final_sep = "%s%s" % (final_sep, conj)
-
- if sep_spaced:
- sep += " "
-
- return "%s%s%s" % (sep.join(words[0:-1]), final_sep, words[-1])
diff --git a/Code/irc/inflect.pyc b/Code/irc/inflect.pyc
deleted file mode 100644
index 62e9932..0000000
Binary files a/Code/irc/inflect.pyc and /dev/null differ
diff --git a/Code/irc/mentions.pyc b/Code/irc/mentions.pyc
deleted file mode 100644
index e452a1d..0000000
Binary files a/Code/irc/mentions.pyc and /dev/null differ
diff --git a/Code/irc/newBanter.py b/Code/irc/newBanter.py
deleted file mode 100644
index d8b91c7..0000000
--- a/Code/irc/newBanter.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/python3
-import random
-import re
-
-dictDir = "/usr/share/dict/"
-
-def getBanter(morphWord="bant", dictName="words"):
- with open(dictDir + dictName, "r") as dict:
- # get rid of all the words with apostrophes
- words = list(filter(lambda word: re.search(r"^[^']*$", word), dict.readlines()))
-
- head = getBanterHead(words, morphWord)
- tail = getBanterTail(words, morphWord)
-
- if head == "" and tail == "":
- return "" # dang, we just failed
- else:
- # pick randomly between non-empty strings
- return random.choice([w for w in [head, tail] if w != ""])
-
-def getBanterHead(words, morphWord):
- morphHead = morphWord[0:-1]
- morphLast = morphWord[-1]
-
- filtered = list(filter(lambda word: re.search(morphHead, word), words))
- if len(filtered) == 0:
- return "" # nothing applicable found
-
- word = random.choice(filtered).strip("\n")
- end = word.find(morphHead) + len(morphHead)
- if end == len(word):
- return word + morphLast
- else:
- if "aeiou".find(word[end]) > -1: # just append 't'
- return word[:end] + morphLast + word[end:]
- else: # replace the letter with 'b'
- return word[:end] + morphLast + word[end + 1 :]
-
-def getBanterTail(words, morphWord):
- morphTail = morphWord[1:]
- morphFirst = morphWord[0]
-
- filtered = list(filter(lambda word: re.search(morphTail, word), words))
- if len(filtered) == 0:
- return "" # nothing applicable found
-
- word = random.choice(filtered).strip("\n")
- start = word.find(morphTail)
- if start == 0:
- return morphFirst + word
- else:
- if "aeiou".find(word[start]) > -1: # just append a 'b'
- return word[:start] + morphFirst + word[start:]
- else: # replace the letter with 'b'
- return word[: start - 1] + morphFirst + word[start:]
diff --git a/Code/irc/pretty_date.pyc b/Code/irc/pretty_date.pyc
deleted file mode 100644
index e362852..0000000
Binary files a/Code/irc/pretty_date.pyc and /dev/null differ
diff --git a/Code/irc/puzzle.py b/Code/irc/puzzle.py
deleted file mode 100644
index 9a43c98..0000000
--- a/Code/irc/puzzle.py
+++ /dev/null
@@ -1,185 +0,0 @@
-#!/usr/bin/python
-import random
-import hashlib
-import inflect
-import quote_puzzle
-import dict_puzzle
-import textcaptcha
-
-p = inflect.engine()
-primes = [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
-fuzz_amount = 3
-
-
-def make_puzzle(obfuscate=True, roll=None):
- answer = 0
- bonus = 0
- puzzle = random.choice(
- [
- "Prove you're not a robot: ",
- "Are you a robot?: ",
- "Anti-bot check: ",
- "Counter-cnd0rphant measures: ",
- "Cosn0k countermeasures: ",
- "Anti-tilde7hief precautions: ",
- "Pro-l0gin challenge: ",
- "Riddle me this: ",
- "Would you like to play a game? ",
- "How about this? "
- ]
- )
- puzzle += random.choice(
- [
- "What is",
- "What do you get from",
- "What do you get with",
- "What is the value of",
- "Can you answer",
- "Can you tell me",
- "Ask wiz3bot what is",
- "Does anybody know",
- "Who knows",
- "Guess what",
- "Calculate",
- "Find out"
- ]
- )
- puzzle += " "
- roll = roll or random.randrange(0, 21)
- var1 = random.randrange(1, 10)
- var2 = random.randrange(1, 10)
- var3 = random.randrange(1, 20)
- var4 = random.randrange(1, 20)
- let1_ord = random.randrange(ord("a"), ord("z") + 1)
-
- if roll == 0:
- answer = var1 + var2
- puzzle += "{} {} {}".format(
- p.number_to_words(var1),
- random.choice(["and", "plus", "sum", "add"]),
- p.number_to_words(var2),
- )
-
- elif roll == 1:
- answer = var1 * var2
- puzzle += "{} {} {}".format(
- p.number_to_words(var1),
- random.choice(["times", "multiply", "multiplied by", "product"]),
- p.number_to_words(var2),
- )
-
- elif roll == 2:
- if var2 > var1:
- var1, var2 = var2, var1
- answer = var1 - var2
- puzzle += "{} {} {}".format(
- p.number_to_words(var1),
- random.choice(["minus", "subtract", "take away", "less"]),
- p.number_to_words(var2),
- )
-
- elif roll == 3:
- if var2 > var1:
- var1, var2 = var2, var1
- answer = var1 * 2 / var2
- puzzle += "{} {} {} (no remainder)".format(
- p.number_to_words(var1 * 2),
- random.choice(["divided by", "over"]),
- p.number_to_words(var2),
- )
-
- elif roll == 4:
- answer = var1 ** var2
- puzzle += "{} to the {} power".format(
- p.number_to_words(var1), p.ordinal(p.number_to_words(var2))
- )
-
- elif roll == 5:
- p1 = random.choice(primes)
- p2 = random.choice(primes)
-
- def answer(guess):
- # Check the the numbers entered are correct, regardless of order
- # or surrounding whitespace.
- attempt = sorted(word.strip() for word in guess.split(","))
- correct = sorted([str(p1), str(p2)])
- return attempt == correct
-
- bonus = 1
- puzzle += "{} when factored into its two primes (answer in the form of the two primes with a comma between)".format(
- p.number_to_words(p1 * p2)
- )
-
- elif roll == 6:
- prime = random.choice(primes)
- answer = prime % var1
- puzzle += p.number_to_words(prime) + " modulus " + p.number_to_words(var1)
- elif roll == 7:
- if let1_ord + var1 > ord("z"):
- let1_ord -= var1
- answer = chr(let1_ord + var1)
- puzzle += "letter comes {} letters after '{}'".format(
- p.number_to_words(var1), chr(let1_ord)
- )
-
- obfuscate = False
- elif roll == 8:
- if let1_ord - var1 < ord("a"):
- let1_ord += var1
- answer = chr(let1_ord - var1)
- puzzle += "letter comes {} letters before '{}'".format(
- p.number_to_words(var1), chr(let1_ord)
- )
-
- obfuscate = False
- elif roll == 9:
- answer, puzzle = quote_puzzle.get_quote()
- obfuscate = False
- elif roll == 10:
- answer = str(min(var1, var2, var3, var4))
- puzzle += "the {} of {}, {}, {}, and {}".format(
- random.choice(["smallest", "lowest"]),
- p.number_to_words(var1),
- p.number_to_words(var2),
- p.number_to_words(var3),
- p.number_to_words(var4),
- )
-
- elif roll == 11:
- answer = str(max(var1, var2, var3, var4))
- puzzle += "the {} of {}, {}, {}, and {}".format(
- random.choice(["biggest", "largest"]),
- p.number_to_words(var1),
- p.number_to_words(var2),
- p.number_to_words(var3),
- p.number_to_words(var4),
- )
-
- elif roll <= 14: # 12-14
- answer, puzzle = dict_puzzle.get_puzzle()
- obfuscate = False
- elif roll <= 17: # 15-17
- answer, puzzle = dict_puzzle.get_anagram()
- obfuscate = False
- elif roll == 18:
- answer, puzzle = quote_puzzle.get_chuck()
- obfuscate = False
- elif roll <= 20: #19-20
- captcha = textcaptcha.get_captcha()
- puzzle = captcha['q'] # the question part of the captcha
- print(captcha)
- answer = lambda a : hashlib.md5(a.encode()).hexdigest() in captcha['a'] # check if the answer is correct
- obfuscate = False
- bonus = 1
-
- # Add a question mark on the end of the question
- if puzzle[-1] != "?":
- puzzle += "?"
-
- if obfuscate == True:
- for _ in range(fuzz_amount):
- idx = random.randrange(len(puzzle) - 2) # get between 0 and string length (except the ? mark)
- puzzle = "".join(
- [puzzle[0:idx], chr(random.randint(33, 126)), puzzle[idx + 1 :]]
- )
- return [puzzle, answer, bonus]
diff --git a/Code/irc/quote_puzzle.py b/Code/irc/quote_puzzle.py
deleted file mode 100644
index f755e52..0000000
--- a/Code/irc/quote_puzzle.py
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/python
-
-import json
-import urllib
-import random
-import re
-
-quotefile = "/home/karlen/irc/quotes.txt"
-chuckfile = "chuck.txt"
-chuckApi = "https://api.icndb.com/jokes/random"
-
-def get_quote():
- quotes = open(quotefile, "r").read().split("---")
- quote, attr = random.choice(quotes).strip().splitlines()
- quote = quote[:200] # get only the first 200 chars
- word = random.choice([q for q in quote.split(" ") if len(q) > 1])
- quote = quote.replace(word, re.sub(r"[a-zA-Z]", "_", word))
- return [word, 'Fill in the blank: "' + quote + '" ' + attr]
-
-def get_chuck():
- #chucks = open(chuckfile, "r").readlines()
- #chuck = random.choice(chucks).rstrip()[:200] # get only the first 200 chars
- # ha ha! let's see if we can confus login
- chuck = json.loads(urllib.urlopen(chuckApi).read().decode())['value']['joke'][:200]
- word = random.choice([w for w in chuck.split(" ") if len(w) > 1 and w.lower() != "chuck" and w.lower() != "norris"])
- chuck = chuck.replace(word, re.sub(r"[a-zA-Z]", "_", word)).replace(""", "\"")
- return [word, 'Fill in the blank: "{}"'.format(chuck)]
diff --git a/Code/irc/rhymesWith.py b/Code/irc/rhymesWith.py
deleted file mode 100644
index ce6d10e..0000000
--- a/Code/irc/rhymesWith.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/python3
-import urllib
-
-# from lxml.html import fromstring
-from bs4 import BeautifulSoup
-import random
-
-
-def getRhymes(word):
- words = []
- url = "http://www.rhymer.com/RhymingDictionaryLast/%s.html" % word
- soup = BeautifulSoup(urllib.request.urlopen(url).read(), "html.parser")
-
- for t in soup.find_all("table", "table"):
- words.append(
- random.choice(
- [
- w
- for w in t.text.split("\n")
- if w not in [u"", u"\xa0"] and "-" not in w
- ]
- )
- )
- return words
-
-
-def rhymeZone(word):
- words = []
- url = (
- "http://rhymezone.com/r/rhyme.cgi?Word=%s&typeofrhyme=perfect&org1=syl&org2=l&org3=y"
- % word
- )
- soup = BeautifulSoup(urllib.request.urlopen(url).read(), "html.parser")
-
- for t in soup.find_all("a", "d"):
- w = t.text.rstrip()
- if w not in [u"", u"\xa0"] and "?" not in t:
- words.append(w)
- random.shuffle(words)
- return words[0:5]
diff --git a/Code/irc/run.sh b/Code/irc/run.sh
deleted file mode 100755
index dc98a46..0000000
--- a/Code/irc/run.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-nohup ./tilde_bot.py -s 127.0.0.1 -n tilde_bot -c \#bot_test >> log 2>> log &
diff --git a/Code/irc/run_banter.sh b/Code/irc/run_banter.sh
index 7bd4253..2b52756 100755
--- a/Code/irc/run_banter.sh
+++ b/Code/irc/run_banter.sh
@@ -1,9 +1,9 @@
#!/bin/bash
-
-if [[ ! `pidof -sx banterbot.py` ]]; then
- #nohup ./banterbot.py -s 127.0.0.1:6667 -n banterbot -c \#tildetown \#bots >> banterlog 2>> banterlog &
+# check if the bot is already running
+if [[ ! `pidof -sx bot_launcher.py` || ! `ps -p $(pidof -sx bot_launcher.py) -o args | grep "\-n banterbot"` ]]; then
echo "Starting banterbot"
- nohup ./banterbot.py -s 127.0.0.1:6667 -n banterbot -c \#tildetown \#bots >> banterlog 2>> banterlog &
+ /home/krowbar/Code/irc/bot_launcher.py -n banterbot -s 127.0.0.1 -p 6667 -c \#tildetown \#bots
+ # nohup /home/krowbar/Code/irc/bot_launcher.py -n banterbot -s 127.0.0.1:6667 -c \#bot_test
else
echo "Banterbot has already been started"
fi
diff --git a/Code/irc/run_madlib.sh b/Code/irc/run_madlib.sh
deleted file mode 100755
index 0fd18db..0000000
--- a/Code/irc/run_madlib.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-if [[ ! `pidof -sx madlibbot.py` ]]; then
- nohup ./madlibbot/madlibbot.py -s 127.0.0.1:6667 -n madlibbot -c \#bots \#madlibs >> madliblog 2>> madliblog &
- echo "Starting madlibbot"
-else
- echo "madlibbot has already been started"
-fi
diff --git a/Code/irc/run_tilde.sh b/Code/irc/run_tilde.sh
index da66d45..8dc0a26 100755
--- a/Code/irc/run_tilde.sh
+++ b/Code/irc/run_tilde.sh
@@ -1,8 +1,9 @@
#!/bin/bash
-if [[ ! `pidof -sx tildebot.py` ]]; then
+# check if the bot is already running
+if [[ ! `pidof -sx bot_launcher.py` || ! `ps -p $(pidof -sx bot_launcher.py) -o args | grep "\-n tildebot"` ]]; then
echo "Starting tildebot"
- nohup ./tildebot.py -s 127.0.0.1:6667 -n tildebot -c \#tildetown \#bots >> tildelog 2>> tildelog &
- #nohup ./tildebot.py -s 127.0.0.1:6667 -n tildebot -c \#bots >> tildelog 2>> tildelog &
+ /home/krowbar/Code/irc/bot_launcher.py -n tildebot -s 127.0.0.1 -p 6667 -c \#bots
+ # nohup /home/krowbar/Code/irc/bot_launcher.py -n tildebot -s 127.0.0.1:6667 -c \#bot_test
else
echo "Tildebot has already been started"
fi
diff --git a/Code/irc/run_topic.sh b/Code/irc/run_topic.sh
deleted file mode 100755
index 3a619c5..0000000
--- a/Code/irc/run_topic.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-if [[ ! `pidof -sx topicbot.py` ]]; then
- nohup ./topicbot.py -s 127.0.0.1:6667 -n topicbot -c \#tildetown \#bots >> topiclog 2>> topiclog &
-fi
-#nohup ./topicbot.py -s 127.0.0.1:6667 -n topicbot -c \#bot_test \#bots >> topiclog 2>> topiclog &
-#./topic_bot.py -s 127.0.0.1 -n topic_bot -c \#bot_test
diff --git a/Code/irc/run_wang.sh b/Code/irc/run_wang.sh
deleted file mode 100755
index 6dd82ac..0000000
--- a/Code/irc/run_wang.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-nohup ./wangbot.py -n numberwang_bot -c \#bots >> wanglog 2>> wanglog &
-#nohup ./wangbot.py -s 127.0.0.1 -n numberwang_bot -c \#bot_test >> wanglog 2>> wanglog &
diff --git a/Code/irc/tildebot.py b/Code/irc/tildebot.py
deleted file mode 100755
index 497c1ab..0000000
--- a/Code/irc/tildebot.py
+++ /dev/null
@@ -1,394 +0,0 @@
-#!/usr/bin/python
-# http://wiki.shellium.org/w/Writing_an_IRC_bot_in_Python
-
-# Import some necessary libraries.
-import socket
-import os
-import sys
-import time
-import argparse
-import fileinput
-import random
-
-import inflect
-import puzzle
-import util
-
-parser = argparse.ArgumentParser()
-
-parser.add_argument(
- "-s",
- "--server",
- dest="server",
- default="127.0.0.1:6667",
- help="the server to connect to",
- metavar="SERVER",
-)
-parser.add_argument(
- "-c",
- "--channels",
- dest="channels",
- nargs="+",
- default=["#bot_test"],
- help="the channels to join",
- metavar="CHANNELS",
-)
-parser.add_argument(
- "-n",
- "--nick",
- dest="nick",
- default="tildebot",
- help="the nick to use",
- metavar="NICK",
-)
-
-args = parser.parse_args()
-
-p = inflect.engine()
-challenges = {}
-SCORE_FILE = "tildescores.txt"
-JACKPOT_FILE = "tildejackpot.txt"
-JACKPOT_MIN = 3
-DEBUG = False
-
-
-def too_recent(time1, time2):
- return int(time1) - int(time2) < 60 * 60
-
-
-def get_positive():
- return random.choice(
- [
- "Yes",
- "Yep",
- "Yeppers",
- "Correct",
- "You got it",
- "Yeah",
- "Right on",
- "Uh-huh",
- "Positive",
- "Totally right",
- "Close enough",
- "That's it",
- "Winner, winner",
- "Bingo",
- "Affirmative",
- ]
- )
-
-
-def get_negative():
- return random.choice(
- [
- "No",
- "Nope",
- "Sorry",
- "Wrong",
- "Nuh-uh",
- "Negatory",
- "Incorrect",
- "Not today",
- "Try again",
- "Maybe later",
- "Maybe next time",
- "Probably not",
- "Answer hazy",
- "Not quite",
- "Not even close",
- "Not for you",
- "I think not"
- ]
- )
-
-
-def get_superlative(score):
- if score > 4:
- return random.choice(
- [
- "super cool",
- "totally rad",
- "extraordinary",
- "dynomite",
- "#topdrawer",
- "a #TopLad",
- "the cat's meow",
- "a tilde town hero",
- "my favorite person",
- "incredibly lucky",
- "unbelievable",
- "a tilde town hunk",
- "could bring all the boys to the yard",
- "worth twice their weight in gold",
- "the hero we need",
- "no ordinary townie",
- ]
- )
- elif score > 2:
- return random.choice(
- [
- "really cool",
- "pretty neat",
- "rather nice",
- "a dynamic doggo",
- "radical",
- "intense",
- "pretty lucky",
- "knows the territory",
- "has what it takes",
- "has mad skillz",
- "going the distance",
- "a hard worker",
- "my sunshine",
- "ready to rumble",
- ]
- )
- else:
- return random.choice(
- [
- "cool",
- "nice",
- "acceptible",
- "good enough",
- "a promising pupper",
- "better than a horse",
- "swell",
- "a little lucky",
- "just credible",
- "my friend",
- "probably not a robot",
- "valuable to the team",
- "now trending",
- ]
- )
-
-
-def get_bad_thing():
- return random.choice(
- [
- "is a meanie",
- "mugs me right off",
- "miffed me off",
- "is worse than a horse",
- "smells like a ghost",
- "probably didn't bathe today",
- "probably shakes babies",
- "didn't guess hard enough",
- "isn't lucky",
- "smells of elderberries",
- "should reconsider their life choices",
- "did't believe in the heart of the tilde",
- "came to the wrong side of town",
- "should have stopped while they were ahead",
- "requires annotations from an authoratative source",
- "could have been a contender",
- "spreads vicious rumors",
- "drank my milkshake",
- "is probably cheating",
- "is trying too hard",
- "didn't really try",
- "should try harder",
- "caught me in a bad mood",
- "should have gone with their first choice",
- "did not receive IFR clearance from tower",
- "was tardy for class",
- "is on double secret probation",
- "forgot their keys",
- "forgot to bribe me",
- "forgot to close the door",
- "waited too long",
- "doesn't call me on my cellphone",
- "isn't wearing a seatbelt",
- "didn't courtesy flush",
- "asked a bot for answers",
- "was right but I didn't feel like it",
- "is right on opposite day",
- "actually answered the last question",
- "has their pants on backwards",
- "forgot their own name",
- ]
- )
-
-
-def get_prize(name, isHuman, bonus=0):
- prizes = [1] * 8 + [2] * 4 + [3] * 2 + [5] * isHuman # no 5pt prize for non-humans
- prize = random.choice(prizes) + bonus
- if (
- random.randint(1, 10) > 6 - 4 * isHuman
- ): # 80% of the time it's a normal prize (40% for not humans)
- return [
- prize,
- "{}: {}! You are {} and get {} tildes!".format(
- name,
- (get_positive() if isHuman else get_negative()),
- get_superlative(prize),
- p.number_to_words(prize),
- ),
- ]
- else: # 20% of the time its a jackpot situation
- with open(JACKPOT_FILE, "r+") as jackpotfile:
- jackpot = int(jackpotfile.readline().strip("\n"))
- jackpotfile.seek(0)
- jackpotfile.truncate()
- if (
- random.randint(1, 10) > 1 or not isHuman
- ): # 90% of the time it's a non-prize. non-humans never win jackpot
- new_jackpot = jackpot + max(1, prize)
- jackpotfile.write(
- str(new_jackpot)
- ) # increase the jackpot by the prize size
- return [
- 0,
- "{} {} and gets no tildes! (Jackpot is now {} tildes)".format(
- name, get_bad_thing(), new_jackpot
- ),
- ]
- else: # hit jackpot!
- jackpotfile.write(str(JACKPOT_MIN))
- return [
- jackpot,
- "{} hit the jackpot and won **{}** tildes!".format(
- name, p.number_to_words(jackpot)
- ),
- ]
-
-
-def show_jackpot(channel):
- with open(JACKPOT_FILE, "r") as jackpotfile:
- jackpot = int(jackpotfile.readline().strip("\n"))
- util.sendmsg(
- ircsock,
- channel,
- "The jackpot is currently {} tildes!".format(p.number_to_words(jackpot)),
- )
-
-
-def give_tilde(channel, user, name, time, human, bonus=0):
- found = False
- with open(SCORE_FILE, "r+") as scorefile:
- scores = scorefile.readlines()
- scorefile.seek(0)
- scorefile.truncate()
- for score in scores:
- name, score_on_file, timestamp = score.strip("\n").split("&^%")
- if name == user:
- found = True
- if too_recent(time, timestamp) and not DEBUG:
- util.sendmsg(
- ircsock,
- channel,
- "{} asked for a tilde too recently and {}. Try again later.".format(
- name, get_bad_thing()
- ),
- )
- else:
- prizevalue, prizetext = get_prize(name, human, bonus)
- score = "{}&^%{}&^%{}\n".format(
- name, str(int(score_on_file) + prizevalue), time
- )
- util.sendmsg(ircsock, channel, prizetext)
- scorefile.write(score)
- if not found:
- prizevalue, prizetext = get_prize(name, True, bonus)
- util.sendmsg(
- ircsock,
- channel,
- "Welcome to the tilde game! Here's {} free tildes to start you off.".format(
- p.number_to_words(prizevalue + 1)
- ),
- )
- scorefile.write("{}&^%{}&^%{}\n".format(user, str(prizevalue + 1), time))
-
-
-def show_tildescore(channel, user, name):
- with open(SCORE_FILE, "r") as scorefile:
- for _idx, score in enumerate(scorefile):
- person = score.strip("\n").split("&^%")
- if person[0] == user:
- util.sendmsg(
- ircsock,
- channel,
- "{} has {} tildes!".format(name, p.number_to_words(person[1])),
- )
- return
- # person has not played yet
- util.sendmsg(ircsock, channel, "{} has no tildes yet!".format(name))
-
-
-def challenge(channel, user, name, time):
- if channel != "#bots" and not DEBUG:
- util.sendmsg(
- ircsock,
- channel,
- "{} is a meanie and gets no tildes. **Tildebot now only gives out tildes in the #bots channel.**".format(
- name
- ),
- )
- return
- global challenges
- challenge = puzzle.make_puzzle()
- challenges[user] = challenge[1:]
- util.sendmsg(ircsock, channel, "{}: {}".format(name, challenge[0]))
-
-
-def challenge_response(channel, user, name, time, msg):
- global challenges
- print(msg)
- if user in challenges:
- answer, bonus = challenges[user]
- if (callable(answer) and answer(msg.lower())) or (
- msg.lower() == str(answer).lower() or msg == p.number_to_words(answer)
- ):
- give_tilde(channel, user, name, time, True, bonus)
- else:
- give_tilde(channel, user, name, time, False, 0)
- del challenges[user]
- # delete the user from challenges either way
-
-
-def rollcall(channel):
- util.sendmsg(
- ircsock, channel, "tildebot reporting! I respond to !tilde !tildescore"
- )
-
-
-def listen():
- while 1:
-
- ircmsg = ircsock.recv(2048).decode("utf-8")
- for msg in ircmsg.split("\n"):
- msg = msg.strip("\n\r")
-
- if msg[:4] == "PING":
- util.ping(ircsock, msg)
- continue
-
- formatted = util.format_message(msg)
- # print(formatted)
-
- if "" == formatted:
- continue
-
- iTime, user, _command, channel, messageText = formatted.split("\t")
- name = util.get_name(user)
-
- if msg.find(":!tildescore") != -1:
- show_tildescore(channel, user, name)
- elif msg.find(":!tilde") != -1 and user not in challenges:
- challenge(channel, user, name, iTime)
- elif user in challenges and (channel == "#bots" or DEBUG):
- challenge_response(channel, user, name, iTime, messageText)
-
- if msg.find(":!jackpot") != -1:
- show_jackpot(channel)
-
- if msg.find(":!rollcall") != -1:
- rollcall(channel)
-
- sys.stdout.flush()
- time.sleep(1)
-
-
-ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-util.connect(ircsock, args)
-listen()
diff --git a/Code/irc/topicbot.py b/Code/irc/topicbot.py
deleted file mode 100755
index 8fde669..0000000
--- a/Code/irc/topicbot.py
+++ /dev/null
@@ -1,215 +0,0 @@
-#!/usr/bin/python3
-# http://wiki.shellium.org/w/Writing_an_IRC_bot_in_Python
-
-# Import some necessary libraries.
-import socket
-import os
-import sys
-import fileinput
-import random
-import time
-import argparse
-
-import inflect
-import util
-
-parser = argparse.ArgumentParser()
-
-parser.add_argument(
- "-s",
- "--server",
- dest="server",
- default="127.0.0.1:6667",
- help="the server to connect to",
- metavar="SERVER",
-)
-parser.add_argument(
- "-c",
- "--channels",
- dest="channels",
- nargs="+",
- default=["#bot_test"],
- help="the channels to join",
- metavar="CHANNELS",
-)
-parser.add_argument(
- "-n",
- "--nick",
- dest="nick",
- default="topicbot",
- help="the nick to use",
- metavar="NICK",
-)
-
-args = parser.parse_args()
-
-p = inflect.engine()
-
-
-def get_topic(channel, user, time):
- # topic scores are saved as &^%&^%
- with open("topicscores.txt", "r") as scorefile:
- scores = scorefile.readlines()
- userscore = 1
- found = False
- with open("topicscores.txt", "w") as scorefile:
- for idx, score in enumerate(scores):
- data = score.strip("\n").split("&^%")
- if data[0] == user:
- found = True
- userscore = int(data[1]) + 1
- scores[idx] = data[0] + "&^%" + str(userscore) + "&^%" + data[2] + "\n"
- scorefile.writelines(scores)
- if not found:
- scorefile.write(user + "&^%1&^%0\n")
-
- with open("topics_" + channel + ".txt", "r") as topics:
- topic = topics.readlines()[-1].strip("\n").split("&^%", 3)
- byuser = util.get_name(topic[1])
- util.sendmsg(
- ircsock,
- channel,
- "I've told you {} times! It's \"{}\" (set by {} {})".format(
- p.number_to_words(userscore),
- topic[2],
- byuser,
- util.pretty_date(int(topic[0])),
- ),
- )
-
-
-def count_topic(channel, user, time, msg):
- with open("topics_" + channel + ".txt", "a") as topics:
- topics.write(time + "&^%" + user + "&^%" + msg + "\n")
- with open("topicscores.txt", "r") as scorefile:
- scores = scorefile.readlines()
- userscore = 1
- found = False
- with open("topicscores.txt", "w") as scorefile:
- for idx, score in enumerate(scores):
- data = score.strip("\n").split("&^%")
- if data[0] == user:
- found = True
- userscore = int(data[2]) + 1
- scores[idx] = data[0] + "&^%" + data[1] + "&^%" + str(userscore) + "\n"
- scorefile.writelines(scores)
- if not found:
- scorefile.write(user + "&^%0&^%1")
- util.sendmsg(
- ircsock,
- channel,
- "{} has changed the topic {} times!".format(user, p.number_to_words(userscore)),
- )
-
-
-def set_topic(channel, user, time, msg):
- ircsock.send("TOPIC " + channel + " :" + msg + "\n")
- count_topic(channel, user, time, msg)
-
-
-def random_topic(channel, user, time, setTopic=False):
- with open("randomtopics.txt") as rtopics:
- msg = random.choice(rtopics.readlines()).strip("\n")
- if setTopic:
- set_topic(channel, user, time, msg)
- else:
- util.sendmsg(ircsock, channel, "Suggested Topic: {}".format(msg))
-
-
-def rollcall(channel):
- util.sendmsg(
- ircsock,
- channel,
- "topicbot reporting! I respond to !topic !settopic !suggesttopic !thistory",
- )
-
-
-def topic_score(channel):
- util.sendmsg(ircsock, channel, "Not implemented yet")
-
-
-def topic_scores(channel):
- util.sendmsg(ircsock, channel, "Not implemented yet")
-
-
-def topic_history(channel, user, count):
- try:
- iCount = int(count.split()[1])
- except (ValueError, IndexError):
- iCount = 3
- if iCount > 10:
- iCount = 10
- if iCount < 1:
- iCount = 3
- with open("topics_" + channel + ".txt", "r") as topicsfile:
- # topics = topicsfile.readlines()[-iCount:].reverse()
- util.sendmsg(
- ircsock,
- channel,
- "Ok, here are the last {} topics".format(p.number_to_words(iCount)),
- )
- for idx, topic in enumerate(reversed(topicsfile.readlines()[-iCount:])):
- topic = topic.strip("\n").split("&^%", 3)
- byuser = util.get_name(topic[1])
- util.sendmsg(
- ircsock,
- channel,
- "{}: {} (set by {} {})".format(
- str(idx + 1), topic[2], byuser, util.pretty_date(int(topic[0]))
- ),
- )
-
-
-def listen():
- while 1:
-
- ircmsg = ircsock.recv(2048).decode("utf-8")
- for msg in ircmsg.split("\n"):
- msg = msg.strip("\n\r")
-
- if msg[:4] == "PING":
- util.ping(ircsock, msg)
- continue
-
- formatted = util.format_message(msg)
-
- if "" == formatted:
- time.sleep(1)
- continue
-
- # print formatted
-
- msgtime, user, command, channel, messageText = formatted.split("\t")
-
- if command == "TOPIC" and user != args.nick:
- count_topic(channel, user, msgtime, messageText)
-
- if msg.find(":!topic") != -1:
- get_topic(channel, user, msgtime)
-
- if msg.find(":!settopic") != -1:
- set_topic(channel, user, msgtime, messageText[10:])
-
- if msg.find(":!tscores") != -1:
- topic_scores(channel)
- elif msg.find(":!tscores") != -1:
- topic_score(channel)
-
- if msg.find(":!randomtopic") != -1:
- random_topic(channel, user, msgtime, True)
- if msg.find(":!suggesttopic") != -1:
- random_topic(channel, user, msgtime, False)
-
- if msg.find(":!thistory") != -1:
- topic_history(channel, user, messageText)
-
- if msg.find(":!rollcall") != -1:
- rollcall(channel)
-
- sys.stdout.flush()
- time.sleep(1)
-
-
-ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-util.connect(ircsock, args)
-listen()
diff --git a/Code/irc/tumblr.py b/Code/irc/tumblr.py
deleted file mode 100644
index 286848a..0000000
--- a/Code/irc/tumblr.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/python3
-import urllib
-from bs4 import BeautifulSoup
-import random
-import re
-
-
-def tumble(url):
- # Find the max pages
- soup = BeautifulSoup(urllib.request.urlopen(url).read(), "html.parser")
- pages = soup.findAll("span", "page-numbers")[0].text.split("/")[
- 1
- ] # this could totally fail several ways
- tries = 3
- while True:
- try:
- page = random.randrange(1, int(pages) + 1)
-
- # Parse a page
- soup = BeautifulSoup(
- urllib.request.urlopen(url + "/page/" + str(page)).read(), "html.parser"
- )
- article = random.choice(soup.findAll("article"))
- quote = article.find("blockquote").text.replace("\n", "")
- if len(article.find("footer").findAll("ul")) > 1:
- quote += re.sub(
- "\n+", " ", article.find("footer").findAll("ul")[0].text
- )
- # the hash tags
- quote += (
- "("
- + re.sub("\n+", " ", article.find("footer").findAll("ul")[1].text)
- + ")"
- )
- # and the date and notes
- else:
- quote += (
- "("
- + re.sub("\n+", " ", article.find("footer").findAll("ul")[0].text)
- + ")"
- )
- # just the date and notes
-
- return quote
- except: # sometimes we fail. let's retry a few times
- if tries == 0:
- return ""
- else:
- tries -= 1
- break
diff --git a/Code/irc/util.py b/Code/irc/util.py
deleted file mode 100644
index bd0ca6b..0000000
--- a/Code/irc/util.py
+++ /dev/null
@@ -1,282 +0,0 @@
-import json
-import time
-import random
-import re
-
-MAX_LINE = 400
-
-
-def ping(ircsock, msg):
- # print("{} => PONG {}".format(msg, msg.split(" ")[1]))
- ircsock.send("PONG {}\n".format(msg.split(" ")[1]).encode())
-
-
-def sendmsg(ircsock, chan, msg):
- print("sending {} to {}".format(msg, chan)[0:MAX_LINE])
- ircsock.send("PRIVMSG {} :{}\r\n".format(chan, msg).encode()[0:MAX_LINE])
-
-def notice(ircsock, user, chan, msg):
- print("sending notice {} to {} in {}".format(msg, user, chan)[0:MAX_LINE])
- ircsock.send("CNOTICE {} {} :{}\r\n".format(user, chan, msg).encode()[0:MAX_LINE])
-
-def part(ircsock, chan, msg="Bye!"):
- print("leaving channel {}".format(chan))
- ircsock.send("PART {} {}\r\n".format(chan, msg).encode())
-
-def quit(ircsock, msg="Quitting!"):
- print("!! quitting !!")
- ircsock.send("QUIT {}".format(msg).encode())
-
-def joinchan(ircsock, chan):
- print("joining {}".format(chan))
- ircsock.send("JOIN {}\r\n".format(chan).encode())
-
-
-def get_user_from_message(msg):
- try:
- i1 = msg.index(":") + 1
- i2 = msg.index("!")
- return msg[i1:i2]
- except ValueError:
- return ""
-
-
-def connect(ircsock, options):
- print(options)
- server, port = options.server.split(":")
- ircsock.connect((server, int(port)))
- print(ircsock)
- nick = "NICK {}\r\n".format(options.nick).encode()
- print(nick)
- ircsock.send(nick)
- login = "USER {0} {0} {0} {0}".format(options.nick).encode()
- print(login)
- ircsock.send(login)
- mode = "MODE +B {}\r\n".format(options.nick).encode()
- print(mode)
- ircsock.send(mode)
- if 'channels' in options:
- for channel in options.channels:
- joinchan(ircsock, channel)
- else:
- joinchan(ircsock, options.channel)
-
-
-# integer number to english word conversion
-# can be used for numbers as large as 999 vigintillion
-# (vigintillion --> 10 to the power 60)
-# tested with Python24 vegaseat 07dec2006
-def int2word(n):
- """
- convert an integer number n into a string of english words
- """
- # break the number into groups of 3 digits using slicing
- # each group representing hundred, thousand, million, billion, ...
- n3 = []
- r1 = ""
- # create numeric string
- ns = str(n)
- for k in range(3, 33, 3):
- r = ns[-k:]
- q = len(ns) - k
- # break if end of ns has been reached
- if q < -2:
- break
- else:
- if q >= 0:
- n3.append(int(r[:3]))
- elif q >= -1:
- n3.append(int(r[:2]))
- elif q >= -2:
- n3.append(int(r[:1]))
- r1 = r
-
- # print n3 # test
-
- # break each group of 3 digits into
- # ones, tens/twenties, hundreds
- # and form a string
- nw = ""
- for i, x in enumerate(n3):
- b1 = x % 10
- b2 = (x % 100) // 10
- b3 = (x % 1000) // 100
- # print b1, b2, b3 # test
- if x == 0:
- continue # skip
- else:
- t = thousands[i]
- if b2 == 0:
- nw = ones[b1] + t + nw
- elif b2 == 1:
- nw = tens[b1] + t + nw
- elif b2 > 1:
- nw = twenties[b2] + ones[b1] + t + nw
- if b3 > 0:
- nw = ones[b3] + "hundred " + nw
- return nw
-
-
-############# globals ################
-ones = [
- "",
- "one ",
- "two ",
- "three ",
- "four ",
- "five ",
- "six ",
- "seven ",
- "eight ",
- "nine ",
-]
-tens = [
- "ten ",
- "eleven ",
- "twelve ",
- "thirteen ",
- "fourteen ",
- "fifteen ",
- "sixteen ",
- "seventeen ",
- "eighteen ",
- "nineteen ",
-]
-twenties = [
- "",
- "",
- "twenty ",
- "thirty ",
- "forty ",
- "fifty ",
- "sixty ",
- "seventy ",
- "eighty ",
- "ninety ",
-]
-thousands = [
- "",
- "thousand ",
- "million ",
- "billion ",
- "trillion ",
- "quadrillion ",
- "quintillion ",
- "sextillion ",
- "septillion ",
- "octillion ",
- "nonillion ",
- "decillion ",
- "undecillion ",
- "duodecillion ",
- "tredecillion ",
- "quattuordecillion ",
- "sexdecillion ",
- "septendecillion ",
- "octodecillion ",
- "novemdecillion ",
- "vigintillion ",
-]
-
-
-def format_message(message):
- pattern = r"^:.*\!~(.*)@.* (.*) (.*) :(.*)"
- now = int(time.time())
- matches = re.match(pattern, message)
- if not matches:
- return ""
-
- nick = matches.group(1).strip()
- command = matches.group(2).strip()
- channel = matches.group(3).strip()
- message = matches.group(4).strip()
-
- return "%s\t%s\t%s\t%s\t%s" % (now, nick, command, channel, message)
-
-
-def get_users():
- # thanks, ~dan!
- users = []
- with open("/etc/passwd", "r") as f:
- for line in f:
- if "/bin/bash" in line:
- u = line.split(":")[0] # Grab all text before first ':'
- users.append(u)
-
- return users
-
-
-def get_name(name):
- names_file = "/home/jumblesale/Code/canonical_names/canonical_names.json"
- try:
- with open(names_file) as names_data:
- names = json.load(names_data)
- try:
- return names[name]["userName"]
- except KeyError:
- return name
- except IOError:
- return name # if we didn't already
-
-
-def pretty_date(time=False):
- """
- Get a datetime object or a int() Epoch timestamp and return a
- pretty string like 'an hour ago', 'Yesterday', '3 months ago',
- 'just now', etc
- """
- from datetime import datetime
-
- now = datetime.now()
- if type(time) is int:
- diff = now - datetime.fromtimestamp(time)
- elif isinstance(time, datetime):
- diff = now - time
- elif not time:
- diff = now - now
- second_diff = diff.seconds
- day_diff = diff.days
-
- if day_diff < 0:
- return ""
-
- if day_diff == 0:
- if second_diff < 10:
- return "just now"
- if second_diff < 60:
- return str(second_diff) + " seconds ago"
- if second_diff < 120:
- return "a minute ago"
- if second_diff < 3600:
- return str(second_diff / 60) + " minutes ago"
- if second_diff < 7200:
- return "an hour ago"
- if second_diff < 86400:
- return str(second_diff / 3600) + " hours ago"
- if day_diff == 1:
- return "Yesterday"
- if day_diff < 7:
- return str(day_diff) + " days ago"
- if day_diff < 31:
- return str(day_diff / 7) + " weeks ago"
- if day_diff < 365:
- return str(day_diff / 30) + " months ago"
- return str(day_diff / 365) + " years ago"
-
-
-def makeRainbow(word):
-
- word = word or "RAINBOW"
- output = ""
- rb = ["5", "7", "8", "3", "12", "13", "6"]
- bg = "01"
- idx = random.randrange(0, len(rb))
-
- for l in word:
- if l == " ":
- output += " "
- else:
- output += "\x03" + rb[idx % len(rb)] + "," + bg + l
- idx += 1
-
- return output
diff --git a/Code/irc/wangbot.py b/Code/irc/wangbot.py
deleted file mode 100755
index faa216b..0000000
--- a/Code/irc/wangbot.py
+++ /dev/null
@@ -1,322 +0,0 @@
-#!/usr/bin/python
-# http://wiki.shellium.org/w/Writing_an_IRC_bot_in_Python
-
-# Import some necessary libraries.
-import socket
-import os
-import sys
-from optparse import OptionParser
-import fileinput
-import random
-import time
-import re
-import operator
-
-import inflect
-import util
-
-parser = OptionParser()
-
-parser.add_option(
- "-s",
- "--server",
- dest="server",
- default="127.0.0.1:6667",
- help="the server to connect to",
- metavar="SERVER",
-)
-parser.add_option(
- "-c",
- "--channel",
- dest="channel",
- default="#bot_test",
- help="the channel to join",
- metavar="CHANNEL",
-)
-parser.add_option(
- "-n",
- "--nick",
- dest="nick",
- default="wangbot",
- help="the nick to use",
- metavar="NICK",
-)
-
-(options, args) = parser.parse_args()
-
-p = inflect.engine()
-LIMIT_GUESSING = True
-MIN_ROUNDS = 5
-MAX_ROUNDS = 12
-SCORE_FILE = "numberwangscores.txt"
-SHOW_TOP_NUM = 5
-GOOD_CHAN = "#bots"
-
-roundsLeft = 0
-bonusRound = 0
-guesses = 0
-lastGuesser = ""
-currentScores = {}
-
-
-def resetGlobals():
- global roundsLeft
- global bonusRound
- global guesses
- global lastGuesser
- global currentScores
- roundsLeft = 0
- bonusRound = 0
- guesses = 0
- lastGuesser = ""
- currentScores.clear()
-
-
-def start_numberwang(channel, user):
- if channel != "#bots":
- util.sendmsg(
- ircsock,
- channel,
- "Numberwang has been disabled in {} due to spamminess. Please join {} to start a game.".format(
- channel, GOOD_CHAN
- ),
- )
- return
-
- print(user + " started a game")
- resetGlobals()
- util.sendmsg(ircsock, channel, "It's time for Numberwang!")
- time.sleep(1)
- util.sendmsg(ircsock, channel, "Here's how to play:")
-
- util.sendmsg(ircsock, channel, "1. There are 10 rounds")
- util.sendmsg(
- ircsock, channel, "2. Each round lasts 10 seconds. You're up against the clock!"
- )
- util.sendmsg(
- ircsock, channel, "3. Play your numbers, as long as they're between 0 and 99."
- )
- util.sendmsg(ircsock, channel, "4. That's Numberwang!")
- time.sleep(2)
- util.sendmsg(ircsock, channel, "Let's get started!")
- global roundsLeft
- global bonusRound
- roundsLeft = random.randint(MIN_ROUNDS, MAX_ROUNDS)
- bonusRound = random.randint(2, roundsLeft - 1)
- print(
- "There will be {} rounds with the bonus on round {}".format(
- str(roundsLeft), str(roundsLeft - bonusRound + 1)
- )
- )
-
-
-def print_scores(channel):
- scoreStrs = []
- first = True
- for name in currentScores:
- scoreStrs.append(
- "{} is {} on {}".format(
- name,
- ("also " if not first and random.randint(1, 3) == 3 else ""),
- currentScores[name],
- )
- )
- first = False
- util.sendmsg(ircsock, channel, p.join(scoreStrs))
-
-
-def guess_numberwang(channel, user, messageText):
- global guesses
- global lastGuesser
- global currentScores
- global roundsLeft
- print(user + " guessed '" + messageText + "'")
- guess = re.sub(
- "[^0-9]", "", messageText.split()[0]
- ) # must have a number in the first 'word'
- if guess:
- if LIMIT_GUESSING and user == lastGuesser:
- util.sendmsg(
- ircsock,
- channel,
- "{}, you just guessed! Give another player a try!".format(user),
- )
- else:
- guesses += 1
- lastGuesser = user
- ###CORRECT GUESS###
- if (
- random.randint(0, 10) > 10 - guesses
- ): # the more guesses, the higher the probability
- guesses = 0
- lastGuesser = ""
- util.sendmsg(ircsock, channel, "{}: THAT'S NUMBERWANG!".format(user))
- points = random.randint(2, 10) * (
- random.randint(2, 4) if roundsLeft == bonusRound else 1
- )
- if user in currentScores.keys():
- currentScores[user] += points
- else:
- currentScores[user] = points
- roundsLeft -= 1
- time.sleep(2)
- if roundsLeft == 0:
- util.sendmsg(
- ircsock,
- channel,
- "Numberwang is now over. Thank you for playing!",
- )
- util.sendmsg(ircsock, channel, "Final scores:")
- print_scores(channel)
- save_scores()
- else:
- print_scores(channel)
- newRoundStr = ""
- if roundsLeft == 1:
- newRoundStr += "The last round is Wangernumb!"
- elif roundsLeft == bonusRound:
- newRoundStr += "**Bonus Round!**"
- else:
- newRoundStr += "New Round!"
- if random.randint(1, 10) > 8:
- newRoundStr += " Let's rotate the board!"
- util.sendmsg(
- ircsock, channel, "{} Start guessing!".format(newRoundStr)
- )
-
- ###INCORRECT GUESS###
- else:
- util.sendmsg(
- ircsock,
- channel,
- "{}, {}, {} Numberwang!".format(
- random.choice(["Sorry", "I'm sorry", "No", "Nope"]),
- user,
- random.choice(
- [
- "that's not",
- "that is not",
- "that isn't",
- "that is not",
- "that won't make",
- "that will not make",
- ]
- ),
- ),
- )
-
-
-def stop_numberwang(channel, user):
- print(user + " stopped a game")
- resetGlobals()
- util.sendmsg(
- ircsock,
- channel,
- "Numberwang has been stopped. No points have been awarded. {} is such a party pooper!".format(
- user
- ),
- )
-
-
-def save_scores():
- with open(SCORE_FILE, "r+") as scorefile:
- scores = scorefile.readlines()
- scorefile.seek(0)
- scorefile.truncate()
- for line in scores:
- for name in currentScores:
- score = line.strip("\n").split("&^%")
- if score[0] == name:
- line = "{}&^%{}\n".format(
- score[0], int(score[1]) + currentScores[name]
- )
- del currentScores[name]
- break
- scorefile.write(line)
-
- for name in currentScores: # new wangers
- scorefile.write("{}&^%{}\n".format(name, currentScores[name]))
-
-
-def show_highscores(channel):
- with open(SCORE_FILE, "r") as scorefile:
- scores = []
- for line in scorefile.readlines():
- sline = line.strip("\n").split("&^%")
- scores.append((int(sline[1]), sline[0]))
- scores = sorted(scores, reverse=True)[:SHOW_TOP_NUM]
-
- util.sendmsg(ircsock, channel, "====TOP WANGERS====")
- for score in scores:
- util.sendmsg(
- ircsock, channel, " :== ~{} ({} points!) ==".format(score[1], score[0])
- )
-
-
-def show_user_score(channel, user):
- with open(SCORE_FILE, "r") as scorefile:
- for line in scorefile.readlines():
- score = line.strip("\n").split("&^%")
- if user == score[0]:
- util.sendmsg(
- ircsock,
- channel,
- "{}: Your global numberwang score is {}!".format(user, score[1]),
- )
- return
- # if we don't find a score line
- util.sendmsg(
- ircsock, channel, "{} You haven't scored any points yet!".format(user)
- )
-
-
-def rollcall(channel):
- util.sendmsg(
- ircsock,
- channel,
- "Is it time for Numberwang? It might be! Start a new game with !numberwang or stop a current game with !wangernumb Get your score with !myscore and the list of top wangers with !topwangers",
- )
-
-
-def listen():
- while 1:
-
- ircmsg = ircsock.recv(2048).decode("utf-8")
- ircmsg = ircmsg.strip("\n\r")
-
- if ircmsg[:4] == "PING":
- util.ping(ircsock, ircmsg)
-
- formatted = util.format_message(ircmsg)
-
- if "" == formatted:
- continue
-
- # print formatted
-
- _time, user, _command, channel, messageText = formatted.split("\t")
-
- if ircmsg.find(":!numberwang") != -1 and roundsLeft == 0:
- start_numberwang(channel, user)
-
- if channel == GOOD_CHAN:
- if ircmsg.find(":!wangernumb") != -1 and roundsLeft > 0:
- stop_numberwang(channel, user)
- if roundsLeft > 0:
- guess_numberwang(channel, user, messageText)
-
- if ircmsg.find(":!topwangers") != -1:
- show_highscores(channel)
- if ircmsg.find(":!myscore") != -1:
- show_user_score(channel, user)
-
- if ircmsg.find(":!rollcall") != -1:
- rollcall(channel)
-
- sys.stdout.flush()
- time.sleep(1)
-
-
-ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-util.connect(ircsock, options)
-listen()
diff --git a/Code/irc/welch.py b/Code/irc/welch.py
deleted file mode 100644
index 563d96b..0000000
--- a/Code/irc/welch.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import random
-
-
-def get_thing():
- file = "/home/krowbar/logs/welchdata.txt"
- thing = ""
- return (
- "Thing Mr. Welch can no longer do in a RPG #"
- + random.choice(list(open(file))).rstrip()
- )
diff --git a/Code/irc/whosaid.py b/Code/irc/whosaid.py
deleted file mode 100755
index 95fa54f..0000000
--- a/Code/irc/whosaid.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/python3
-import fileinput
-import time
-import calendar
-import re
-import operator
-
-MAX_NODES = 5
-
-logfile = "/home/archangelic/irc/log"
-timeCutoff = calendar.timegm(time.gmtime()) - (3 * 7 * 24 * 60 * 60) # 3 weeks
-
-nameFix = {
- "jumblesal": "jumblesale",
- "hardmath1": "kc",
- "hardmath123": "kc",
- "bendorphan": "endorphant",
- "endorphan": "endorphant",
- "synergian": "synergiance",
-}
-
-
-def whoSaid(word):
- word = word.lower()
- userData = (
- {}
- ) # hash keyed by "user" that contains a hash of mentioned other users with count
- # Get a list of all user names by checking the logs for people who have said things
- with open(logfile, "r") as log:
- for line in log:
- try:
- time, user, message = line.split("\t", 3)
- time = int(time)
- if user in nameFix:
- user = nameFix[user]
- else:
- user = user.lower()
- except ValueError:
- continue # There are some bad lines in the log file that we'll ignore if we can't parse
-
- if time > timeCutoff and message[0] is not "!" and word in message.lower():
- if user in userData:
- userData[user] += 1
- else:
- userData[user] = 1
- userData = sorted(userData.items(), key=operator.itemgetter(1), reverse=True)
- return {"timecutoff": timeCutoff, "data": userData}
diff --git a/Code/irc/wikiphilosophy.py b/Code/irc/wikiphilosophy.py
deleted file mode 100644
index be1eab1..0000000
--- a/Code/irc/wikiphilosophy.py
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/python3
-from bs4 import BeautifulSoup
-import random
-import requests
-
-
-def get_philosophy(word, max_steps=20):
- step_words = [word]
- steps = 0
-
- url = "https://en.wikipedia.org/wiki/%s" % word
- while steps < max_steps:
- print("url: {}".format(url))
- soup = BeautifulSoup(requests.get(url).content, "html.parser")
- title = soup.find("h1", id="firstHeading")
- content = soup.find("div", id="mw-content-text")
- if not content:
- break
- item = [
- item
- for item in content.find_all("a")
- if not item.get("class")
- and not item.get("target")
- and item.get("title")
- and not "Wikipedia:" in item.get("title")
- and not "Category:" in item.get("title")
- and not "Help:" in item.get("title")
- and not "Portal:" in item.get("title")
- and not "Special:" in item.get("title")
- and not "Talk:" in item.get("title")
- and not "Template:" in item.get("title")
- and not "File:" in item.get("title")
- and "Edit section:" not in item.get("title")
- and "Commons:" not in item.get("title")
- and not item.get("title") in step_words
- ][0]
- step_words.append(item.get("title"))
- # print item.get('title') + "\n"
- url = "https://en.wikipedia.org{}".format(item.get("href"))
- steps += 1
- return step_words
-
-
-def containsAny(str, set):
- return 1 in [c in str for c in set]
-
-
-def get_philosophy_lower(word, max_steps=20):
- step_words = [word]
- steps = 0
-
- url = "https://en.wikipedia.org/wiki/{}".format(word.replace(" ", "%20"))
- while steps < max_steps:
- print("url: {}".format(url))
- soup = BeautifulSoup(requests.get(url).content, "html.parser")
-
- if soup.find(id="noarticletext"):
- step_words.append("(not found)")
- break
-
- title = soup.find("h1", id="firstHeading")
- content = soup.find("div", id="mw-content-text")
- if not content:
- break
- links = [
- item
- for item in content.find_all("a")
- if not item.get("class")
- and item.text
- and item.text[0].islower()
- and not containsAny(item.text, ":()")
- and item.get("title")
- and not containsAny(item.get("title"), ":()")
- and not item.get("title") in step_words
- ]
- if not links:
- step_words.append("(dead end)")
- break
- item = links[0] # grab the first good link item
- # print "Checking %s %s" % (item.get('title'), item.text)
- step_words.append(item.get("title"))
- if item.get("title") == "Philosophy":
- break
- # print item.get('title') + "\n"
- url = "https://en.wikipedia.org%s" % item.get("href")
- steps += 1
- return step_words
diff --git a/Code/irc/xkcdApropos.py b/Code/irc/xkcdApropos.py
deleted file mode 100644
index fd73042..0000000
--- a/Code/irc/xkcdApropos.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/python3
-import duckduckgo
-import urllib
-from bs4 import BeautifulSoup
-
-
-def xkcd(query):
- res = duckduckgo.get_zci("site:xkcd.com " + query)
- title = ""
- try: # ddg returns a url for these searches. i want a title as well
- title = BeautifulSoup(urllib.urlopen(res).read(), "html.parser").title.text
- except:
- pass # just swallow the error. maybe the result wasn't a url or something else bad happened
- return [(((title + " - ") if title else "") + res)]
-
-
-# never mind, blocked by ddg
-# def xkcd_links(query):
-# url = "https://duckduckgo.com/html/?q=site%3Axkcd.com+" + query.replace(' ', '+')
-# soup = BeautifulSoup(urllib.urlopen(url).read(), 'html.parser')
-# items = soup.find_all("a", class_="result__a")
-# print items
-# items = list(filter(lambda i: i[0:8] == 'xkcd.com',
-# [i.find(class_="result__title").text.strip() for i in items]))
-# print items
-# def pretty_link(item):
-# url = item.find(class_="result__url").text.strip()
-# title = item.find(class_="result__title").text.strip()
-# return (title + ' - ' + url) if title else url
-#
-# links = map(lambda url: pretty_link(url), items)
-# return links