From bf5c619da08233845c9709f4f569512b561f073e Mon Sep 17 00:00:00 2001 From: Joscha Date: Sun, 14 Apr 2019 21:49:30 +0000 Subject: [PATCH] Update to yaboli 1.1.2 --- .gitignore | 14 +- bot.conf.default | 6 + requirements.txt | 1 + tom.conf.default | 9 - tom.py | 548 +++++++++++++++++++++++------------------------ 5 files changed, 286 insertions(+), 292 deletions(-) create mode 100644 bot.conf.default create mode 100644 requirements.txt delete mode 100644 tom.conf.default diff --git a/.gitignore b/.gitignore index 65f800f..191feb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ -**/__pycache__ -*.cookie +# python stuff +__pycache__/ + +# venv stuff +bin/ +include/ +lib/ +lib64 +pyvenv.cfg + +# config files *.conf +cookie_jar diff --git a/bot.conf.default b/bot.conf.default new file mode 100644 index 0000000..0bb29e7 --- /dev/null +++ b/bot.conf.default @@ -0,0 +1,6 @@ +[general] +nick = tom +cookiefile = bot.cookie + +[rooms] +test diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2ee85a2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +yaboli >=1.1.2, <1.2.0 diff --git a/tom.conf.default b/tom.conf.default deleted file mode 100644 index 9218da5..0000000 --- a/tom.conf.default +++ /dev/null @@ -1,9 +0,0 @@ -[general] -nick = tom -cookiefile = tom.cookie - -[rooms] -# Format: -# room -# room=password -test diff --git a/tom.py b/tom.py index 2a56884..4009ac2 100644 --- a/tom.py +++ b/tom.py @@ -1,337 +1,323 @@ -import asyncio -import configparser import logging import re import yaboli -from yaboli.utils import * - ELEMENTS = [ - ("H", "Hydrogen"), - ("He", "Helium"), - ("Li", "Lithium"), - ("Be", "Beryllium"), - ("B", "Boron"), - ("C", "Carbon"), - ("N", "Nitrogen"), - ("O", "Oxygen"), - ("F", "Fluorine"), - ("Ne", "Neon"), - ("Na", "Sodium"), - ("Mg", "Magnesium"), - ("Al", "Aluminium"), - ("Si", "Silicon"), - ("P", "Phosphorus"), - ("S", "Sulfur"), - ("Cl", "Chlorine"), - ("Ar", "Argon"), - ("K", "Potassium"), - ("Ca", "Calcium"), - ("Sc", "Scandium"), - ("Ti", "Titanium"), - ("V", "Vanadium"), - ("Cr", "Chromium"), - ("Mn", "Manganese"), - ("Fe", "Iron"), - ("Co", "Cobalt"), - ("Ni", "Nickel"), - ("Cu", "Copper"), - ("Zn", "Zinc"), - ("Ga", "Gallium"), - ("Ge", "Germanium"), - ("As", "Arsenic"), - ("Se", "Selenium"), - ("Br", "Bromine"), - ("Kr", "Krypton"), - ("Rb", "Rubidium"), - ("Sr", "Strontium"), - ("Y", "Yttrium"), - ("Zr", "Zirconium"), - ("Nb", "Niobium"), - ("Mo", "Molybdenum"), - ("Tc", "Technetium"), - ("Ru", "Ruthenium"), - ("Rh", "Rhodium"), - ("Pd", "Palladium"), - ("Ag", "Silver"), - ("Cd", "Cadmium"), - ("In", "Indium"), - ("Sn", "Tin"), - ("Sb", "Antimony"), - ("Te", "Tellurium"), - ("I", "Iodine"), - ("Xe", "Xenon"), - ("Cs", "Caesium"), - ("Ba", "Barium"), - ("La", "Lanthanum"), - ("Ce", "Cerium"), - ("Pr", "Praseodymium"), - ("Nd", "Neodymium"), - ("Pm", "Promethium"), - ("Sm", "Samarium"), - ("Eu", "Europium"), - ("Gd", "Gadolinium"), - ("Tb", "Terbium"), - ("Dy", "Dysprosium"), - ("Ho", "Holmium"), - ("Er", "Erbium"), - ("Tm", "Thulium"), - ("Yb", "Ytterbium"), - ("Lu", "Lutetium"), - ("Hf", "Hafnium"), - ("Ta", "Tantalum"), - ("W", "Tungsten"), - ("Re", "Rhenium"), - ("Os", "Osmium"), - ("Ir", "Iridium"), - ("Pt", "Platinum"), - ("Au", "Gold"), - ("Hg", "Mercury"), - ("Tl", "Thallium"), - ("Pb", "Lead"), - ("Bi", "Bismuth"), - ("Po", "Polonium"), - ("At", "Astatine"), - ("Rn", "Radon"), - ("Fr", "Francium"), - ("Ra", "Radium"), - ("Ac", "Actinium"), - ("Th", "Thorium"), - ("Pa", "Protactinium"), - ("U", "Uranium"), - ("Np", "Neptunium"), - ("Pu", "Plutonium"), - ("Am", "Americium"), - ("Cm", "Curium"), - ("Bk", "Berkelium"), - ("Cf", "Californium"), - ("Es", "Einsteinium"), - ("Fm", "Fermium"), - ("Md", "Mendelevium"), - ("No", "Nobelium"), - ("Lr", "Lawrencium"), - ("Rf", "Rutherfordium"), - ("Db", "Dubnium"), - ("Sg", "Seaborgium"), - ("Bh", "Bohrium"), - ("Hs", "Hassium"), - ("Mt", "Meitnerium"), - ("Ds", "Darmstadtium"), - ("Rg", "Roentgenium"), - ("Cn", "Copernicium"), - ("Nh", "Nihonium"), - ("Fl", "Flerovium"), - ("Mc", "Moscovium"), - ("Lv", "Livermorium"), - ("Ts", "Tennessine"), - ("Og", "Oganesson"), - # Isotopes, but hey, they make the words look better - ("D", "Deuterium"), - ("T", "Tritium"), - # Non-elements and eastereggs - ("E", "Euphorium"), - ("Xyzzy", "Plugh"), - ("Plugh", "Xyzzy"), - ("hunter2", "*******"), + ("H", "Hydrogen"), + ("He", "Helium"), + ("Li", "Lithium"), + ("Be", "Beryllium"), + ("B", "Boron"), + ("C", "Carbon"), + ("N", "Nitrogen"), + ("O", "Oxygen"), + ("F", "Fluorine"), + ("Ne", "Neon"), + ("Na", "Sodium"), + ("Mg", "Magnesium"), + ("Al", "Aluminium"), + ("Si", "Silicon"), + ("P", "Phosphorus"), + ("S", "Sulfur"), + ("Cl", "Chlorine"), + ("Ar", "Argon"), + ("K", "Potassium"), + ("Ca", "Calcium"), + ("Sc", "Scandium"), + ("Ti", "Titanium"), + ("V", "Vanadium"), + ("Cr", "Chromium"), + ("Mn", "Manganese"), + ("Fe", "Iron"), + ("Co", "Cobalt"), + ("Ni", "Nickel"), + ("Cu", "Copper"), + ("Zn", "Zinc"), + ("Ga", "Gallium"), + ("Ge", "Germanium"), + ("As", "Arsenic"), + ("Se", "Selenium"), + ("Br", "Bromine"), + ("Kr", "Krypton"), + ("Rb", "Rubidium"), + ("Sr", "Strontium"), + ("Y", "Yttrium"), + ("Zr", "Zirconium"), + ("Nb", "Niobium"), + ("Mo", "Molybdenum"), + ("Tc", "Technetium"), + ("Ru", "Ruthenium"), + ("Rh", "Rhodium"), + ("Pd", "Palladium"), + ("Ag", "Silver"), + ("Cd", "Cadmium"), + ("In", "Indium"), + ("Sn", "Tin"), + ("Sb", "Antimony"), + ("Te", "Tellurium"), + ("I", "Iodine"), + ("Xe", "Xenon"), + ("Cs", "Caesium"), + ("Ba", "Barium"), + ("La", "Lanthanum"), + ("Ce", "Cerium"), + ("Pr", "Praseodymium"), + ("Nd", "Neodymium"), + ("Pm", "Promethium"), + ("Sm", "Samarium"), + ("Eu", "Europium"), + ("Gd", "Gadolinium"), + ("Tb", "Terbium"), + ("Dy", "Dysprosium"), + ("Ho", "Holmium"), + ("Er", "Erbium"), + ("Tm", "Thulium"), + ("Yb", "Ytterbium"), + ("Lu", "Lutetium"), + ("Hf", "Hafnium"), + ("Ta", "Tantalum"), + ("W", "Tungsten"), + ("Re", "Rhenium"), + ("Os", "Osmium"), + ("Ir", "Iridium"), + ("Pt", "Platinum"), + ("Au", "Gold"), + ("Hg", "Mercury"), + ("Tl", "Thallium"), + ("Pb", "Lead"), + ("Bi", "Bismuth"), + ("Po", "Polonium"), + ("At", "Astatine"), + ("Rn", "Radon"), + ("Fr", "Francium"), + ("Ra", "Radium"), + ("Ac", "Actinium"), + ("Th", "Thorium"), + ("Pa", "Protactinium"), + ("U", "Uranium"), + ("Np", "Neptunium"), + ("Pu", "Plutonium"), + ("Am", "Americium"), + ("Cm", "Curium"), + ("Bk", "Berkelium"), + ("Cf", "Californium"), + ("Es", "Einsteinium"), + ("Fm", "Fermium"), + ("Md", "Mendelevium"), + ("No", "Nobelium"), + ("Lr", "Lawrencium"), + ("Rf", "Rutherfordium"), + ("Db", "Dubnium"), + ("Sg", "Seaborgium"), + ("Bh", "Bohrium"), + ("Hs", "Hassium"), + ("Mt", "Meitnerium"), + ("Ds", "Darmstadtium"), + ("Rg", "Roentgenium"), + ("Cn", "Copernicium"), + ("Nh", "Nihonium"), + ("Fl", "Flerovium"), + ("Mc", "Moscovium"), + ("Lv", "Livermorium"), + ("Ts", "Tennessine"), + ("Og", "Oganesson"), + # Isotopes, but hey, they make the words look better + ("D", "Deuterium"), + ("T", "Tritium"), + # Non-elements and eastereggs + ("E", "Euphorium"), + ("Xyzzy", "Plugh"), + ("Plugh", "Xyzzy"), + ("hunter2", "*******"), ] ELEMDICT = {symbol.lower(): name for (symbol, name) in ELEMENTS} ELEMLENGTHS = set(len(symbol) for (symbol, _) in ELEMENTS) class Node: - def __init__(self, stump): - self.stump = stump - self.edges = [] + def __init__(self, stump): + self.stump = stump + self.edges = [] class Edge: - LETTER = 0 - ELEMENT = 1 + LETTER = 0 + ELEMENT = 1 - def __init__(self, start, end, weight, edgetype, text): - self.start = start - self.end = end - self.weight = weight - self.edgetype = edgetype - self.text = text + def __init__(self, start, end, weight, edgetype, text): + self.start = start + self.end = end + self.weight = weight + self.edgetype = edgetype + self.text = text def elem_prefixes(text): - elems = [] - for i in ELEMLENGTHS: - if len(text) < i: continue + elems = [] + for i in ELEMLENGTHS: + if len(text) < i: continue - start, rest = text[:i], text[i:] - elem = ELEMDICT.get(start.lower(), None) - if elem: - elems.append((elem, rest)) + start, rest = text[:i], text[i:] + elem = ELEMDICT.get(start.lower(), None) + if elem: + elems.append((elem, rest)) - return elems + return elems def create_graph(text): - graph = {} - stumps = [text[i:] for i in range(len(text))] + graph = {} + stumps = [text[i:] for i in range(len(text))] - # goal node - graph[""] = Node("") + # goal node + graph[""] = Node("") - # creating all the nodes - for stump in stumps: - graph[stump] = Node(stump) + # creating all the nodes + for stump in stumps: + graph[stump] = Node(stump) - # creating single-letter links - weight = len(text) - for start in stumps: - char, end = start[:1], start[1:] - nstart = graph.get(start) - nend = graph.get(end) - edge = Edge(nstart, nend, weight, Edge.LETTER, char) - nstart.edges.append(edge) + # creating single-letter links + weight = len(text) + for start in stumps: + char, end = start[:1], start[1:] + nstart = graph.get(start) + nend = graph.get(end) + edge = Edge(nstart, nend, weight, Edge.LETTER, char) + nstart.edges.append(edge) - # creating element links - for stump in stumps: - elems = elem_prefixes(stump) - for (elem, rest) in elems: - nstart = graph.get(stump) - nend = graph.get(rest) - edge = Edge(nstart, nend, 1, Edge.ELEMENT, elem) - nstart.edges.append(edge) + # creating element links + for stump in stumps: + elems = elem_prefixes(stump) + for (elem, rest) in elems: + nstart = graph.get(stump) + nend = graph.get(rest) + edge = Edge(nstart, nend, 1, Edge.ELEMENT, elem) + nstart.edges.append(edge) - return graph + return graph def smallest(graph, nodes): - smallest = None - for node in nodes: - if not smallest or node.length < smallest.length: - smallest = node - return smallest + smallest = None + for node in nodes: + if not smallest or node.length < smallest.length: + smallest = node + return smallest def dijkstra(graph, start, end): - unvisited = set() - visited = set() + unvisited = set() + visited = set() - nstart = graph.get(start) - nstart.step = None - nstart.length = 0 - for edge in nstart.edges: - node = edge.end - unvisited.add(node) - node.step = edge - node.length = edge.weight - visited.add(nstart) + nstart = graph.get(start) + nstart.step = None + nstart.length = 0 + for edge in nstart.edges: + node = edge.end + unvisited.add(node) + node.step = edge + node.length = edge.weight + visited.add(nstart) - while unvisited: - small = smallest(graph, unvisited) - unvisited.remove(small) - visited.add(small) + while unvisited: + small = smallest(graph, unvisited) + unvisited.remove(small) + visited.add(small) - if small.stump == "": - break + if small.stump == "": + break - for edge in small.edges: - node = edge.end - if node in visited: continue - length = small.length + edge.weight - if node in unvisited: - if length < node.length: - node.step = edge - node.length = length - else: - unvisited.add(node) - node.step = edge - node.length = length + for edge in small.edges: + node = edge.end + if node in visited: continue + length = small.length + edge.weight + if node in unvisited: + if length < node.length: + node.step = edge + node.length = length + else: + unvisited.add(node) + node.step = edge + node.length = length - path = [] - node = graph.get(end) - while node.step: - path.append(node.step) - node = node.step.start + path = [] + node = graph.get(end) + while node.step: + path.append(node.step) + node = node.step.start - return reversed(path) + return reversed(path) def format_path(path): - parts = [] - part = "" + parts = [] + part = "" - for edge in path: - if edge.edgetype == Edge.LETTER: - part += edge.text - else: - if part: - parts.append(part) - part = "" - parts.append(edge.text) + for edge in path: + if edge.edgetype == Edge.LETTER: + part += edge.text + else: + if part: + parts.append(part) + part = "" + parts.append(edge.text) - if part: - parts.append(part) + if part: + parts.append(part) - return "-".join(parts) + return "-".join(parts) def shortest_path(text): - graph = create_graph(text) - path = dijkstra(graph, text, "") - return path + graph = create_graph(text) + path = dijkstra(graph, text, "") + return path class Tom(yaboli.Module): - DESCRIPTION = ( - "'tom' attempts to spell a word using the symbols of the periodic table." - " For example, 'Hi' becomes 'Hydrogen Iodine'.\n" - "Because of this, only the letters a-z and A-Z may be used in a word.\n" - "'tom' uses en.wikipedia.org/wiki/List_of_chemical_elements for the element names.\n" - ) - COMMANDS = "!tom - attempts to spell the word using chemical elements\n" - AUTHOR = "Created by @Garmy using github.com/Garmelon/yaboli\n" - CREDITS = "Inspired by Tomsci. Algorithm based on a suggestion by Xyzzy.\n" + DESCRIPTION = "spell a word using chemical elements, if possible" + HELP_GENERAL = "/me spells a word using chemical elements, if possible" + HELP_SPECIFIC = [ + "'tom' attempts to spell a word using the symbols of the periodic" + " table. For example, 'Hi' becomes 'Hydrogen Iodine'.", + "Because of this, only the letters a-z and A-Z may be used in a word.", + "'tom' uses en.wikipedia.org/wiki/List_of_chemical_elements for the" + " element names.", + "", + "!tom - attempts to spell the word using chemical elements", + "", + "Inspired by Tomsci. Algorithm based on a suggestion by Xyzzy.", + "Made by @Garmy with https://github.com/Garmelon/yaboli.", + ] - SHORT_DESCRIPTION = "spell a word using chemical elements, if possible" - SHORT_HELP = "/me spells a word using chemical elements, if possible" + ALLOWED_CHARS = r"a-zA-Z*\w" + ELEM_RE = "([" + ALLOWED_CHARS + "]*)([^" + ALLOWED_CHARS + "]*)" - LONG_DESCRIPTION = DESCRIPTION + COMMANDS + CREDITS - LONG_HELP = DESCRIPTION + COMMANDS + AUTHOR + CREDITS + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) - ALLOWED_CHARS = r"a-zA-Z*\w" - ELEM_RE = "([" + ALLOWED_CHARS + "]*)([^" + ALLOWED_CHARS + "]*)" + if self.standalone: + self.register_botrulez(kill=True, restart=True) - async def on_command_general(self, room, message, command, argstr): - await self.command_tom(room, message, command, argstr) + self.register_general("tom", self.cmd_tom) - @yaboli.command("tom") - async def command_tom(self, room, message, argstr): - args = [] - while argstr: - match = re.match(self.ELEM_RE, argstr) - if not match: break + async def cmd_tom(self, room, message, args_): + # The weird naming of args_ is because I don't want to touch the below + # function and it's easier to just rename the function parameter. + argstr = args_.raw - args.append((match.group(1), match.group(2))) - argstr = argstr[match.end():] + args = [] + while argstr: + match = re.match(self.ELEM_RE, argstr) + if not match: break - spellings = [] - for fst, snd in args: - if fst: - spelling = format_path(shortest_path(fst)) + snd - else: - spelling = snd - spellings.append(spelling) + args.append((match.group(1), match.group(2))) + argstr = argstr[match.end():] - text = "".join(spellings) - await room.send(text, message.mid) + spellings = [] + for fst, snd in args: + if fst: + spelling = format_path(shortest_path(fst)) + snd + else: + spelling = snd + spellings.append(spelling) -def main(configfile): - logging.basicConfig(level=logging.INFO) + text = "".join(spellings) + await message.reply(text) - config = configparser.ConfigParser(allow_no_value=True) - config.read(configfile) - - nick = config.get("general", "nick") - cookiefile = config.get("general", "cookiefile", fallback=None) - module = Tom() - - bot = yaboli.ModuleBot(module, nick, cookiefile=cookiefile) - - for room, password in config.items("rooms"): - if not password: - password = None - bot.join_room(room, password=password) - - asyncio.get_event_loop().run_forever() if __name__ == "__main__": - main("tom.conf") + yaboli.enable_logging(level=logging.DEBUG) + yaboli.run(Tom)