commit d156ecefeee29e1ee56d2ec4979af629ea484f49 Author: Joel Kirchartz Date: Wed Feb 13 17:57:48 2019 +0000 take Arch's dicebot pinhook example and extrapolate out to log.py (proto for comics scripts) and munger.py (ported from cloudbot) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7bbc71c --- /dev/null +++ b/.gitignore @@ -0,0 +1,101 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ diff --git a/pinhook.py b/pinhook.py new file mode 100755 index 0000000..764eb4f --- /dev/null +++ b/pinhook.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +from pinhook.bot import Bot + +ph = Bot( + channels=['#fnord'], + nickname='DocVuDu', + server='irc.maddshark.net', + ops=['derkirche'] +) +ph.start() diff --git a/plugins/dice.py b/plugins/dice.py new file mode 100644 index 0000000..56a9ae5 --- /dev/null +++ b/plugins/dice.py @@ -0,0 +1,29 @@ +import random +import re + +import pinhook.plugin + +dicepattern = re.compile('(?P\d+)d(?P\d+)\+?(?P\d+)?') + +def build_output(rolls, modifier): + if len(rolls) == 1: + start = str(sum(rolls)) + else: + all_rolls = ''.join([str(i)+', ' for i in rolls]).strip(', ') + start = '{} = {}'.format(all_rolls, sum(rolls)) + if modifier: + output = start + ' + {} = {}'.format(modifier, sum(rolls) + int(modifier)) + else: + output = start + return output + +@pinhook.plugin.register('!roll') +def roll(msg): + matches = dicepattern.match(msg.arg) + if matches: + msg.logger.info('Valid dice roll: {}'.format(msg.arg)) + rolls = [random.randrange(1, int(matches.group('sides'))+1) for i in range(int(matches.group('amount')))] + output = build_output(rolls, matches.group('modifier')) + else: + output = '{}: improper format, should be NdN+N'.format(msg.nick) + return pinhook.plugin.message(output) diff --git a/plugins/log.py b/plugins/log.py new file mode 100644 index 0000000..2edc084 --- /dev/null +++ b/plugins/log.py @@ -0,0 +1,40 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 + +import pinhook.plugin + +output =[] +keeplines = 10 + +characters = []; + +def char(nick): + global characters + for char in characters: + if char.nick == nick: + break + else: + characters.push(dict(nick=nick, img=randomCharacter())) + + +@pinhook.plugin.listener('ears') +def ears(msg): + global output + global keeplines + output.append(dict(nick=msg.nick, text=msg.text)) + # only keep last X lines + if len(output) >= keeplines: + output.pop(0) + +@pinhook.plugin.register('!log', 'check ' + str(keeplines) + ' lines of backlog') +def log(msg): + global output + comic = list() + for line in output: + comic.push(dict( + nick=line.nick, + text=line.text, + char=char(line.nick) + )) + return pinhook.plugin.message(' | '.join(output)) diff --git a/plugins/munger.py b/plugins/munger.py new file mode 100644 index 0000000..c1ff8cc --- /dev/null +++ b/plugins/munger.py @@ -0,0 +1,298 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright © 2018 jkirchartz +# +# Distributed under terms of the NPL (Necessary Public License) license. + +import pinhook.plugin +from datetime import datetime +import json +import random +import re + +mcache = dict() +buffer_size = 1 + +# spongemock.py +# author: Noah Krim +# email: nkrim62@gmail.com + +def mock(text, diversity_bias=0.5, random_seed=None): + # Error handling + if diversity_bias < 0 or diversity_bias > 1: + raise ValueError('diversity_bias must be between the inclusive range [0,1]') + # Seed the random number generator + random.seed(random_seed) + # Mock the text + out = '' + last_was_upper = True + swap_chance = 0.5 + for c in text: + if c.isalpha(): + if random.random() < swap_chance: + last_was_upper = not last_was_upper + swap_chance = 0.5 + c = c.upper() if last_was_upper else c.lower() + swap_chance += (1-swap_chance)*diversity_bias + out += c + return out + +# end spongemock + +def vaporwave(text): + output = "" + for c in list(text): + a = ord(c) + if a >= 33 and a <= 126: + output += chr( (a - 33) + 65281 ) + else: + output += c + return output + + +unicode_offsets = { + "cursive": {"upper": 119951, "lower": 119945}, + "circle": {"upper": 9333, "lower": 9327} + } + +def unicode_offset(text, fun): + output = "" + for c in list(text): + a = ord(c) + if a > 64 and a < 91: + output += chr( a + unicode_offsets[fun]["upper"] ) + elif a > 96 and a < 123: + output += chr( a + unicode_offsets[fun]["lower"] ) + else: + output += c + return output + +json_mungers = {} + +json_mungers['russian'] = { + "A": ["Д"], + "B": ["Б", "Ъ", "Ь"], + "C": ["Ҫ"], + "E": ["Ԑ", "Є", "Э"], + "F": ["Ӻ", "Ғ"], + "H": ["Њ", "Ҥ", "Ӊ", "Ң"], + "I": ["Ї"], + "K": ["Қ", "Ҡ", "Ҝ", "Ԟ"], + "M": ["Ԡ"], + "N": ["И", "Ѝ", "Й"], + "O": ["Ф"], + "R": ["Я"], + "T": ["Г", "Ґ", "Ҭ"], + "U": ["Ц","Џ"], + "W": ["Ш", "Щ"], + "X": ["Ӿ", "Ҳ", "Ӽ", "Ж"], + "Y": ["Ч", "Ұ"] +} + +json_mungers['tiny'] = { + "a":"ᵃ", + "b":"ᵇ", + "c":"ᶜ", + "d":"ᵈ", + "e":"ᵉ", + "f":"ᶠ", + "g":"ᵍ", + "h":"ʰ", + "i":"ᶦ", + "j":"ʲ", + "k":"ᵏ", + "l":"ᶫ", + "m":"ᵐ", + "n":"ᶰ", + "o":"ᵒ", + "p":"ᵖ", + "q":"ᑫ", + "r":"ʳ", + "s":"ˢ", + "t":"ᵗ", + "u":"ᵘ", + "v":"ᵛ", + "w":"ʷ", + "x":"ˣ", + "y":"ʸ", + "z":"ᶻ", + "A":"ᴬ", + "B":"ᴮ", + "C":"ᶜ", + "D":"ᴰ", + "E":"ᴱ", + "F":"ᶠ", + "G":"ᴳ", + "H":"ᴴ", + "I":"ᴵ", + "J":"ᴶ", + "K":"ᴷ", + "L":"ᴸ", + "M":"ᴹ", + "N":"ᴺ", + "O":"ᴼ", + "P":"ᴾ", + "Q":"ᑫ", + "R":"ᴿ", + "S":"ˢ", + "T":"ᵀ", + "U":"ᵁ", + "V":"ⱽ", + "W":"ᵂ", + "X":"ˣ", + "Y":"ʸ", + "Z":"ᶻ", + "`":"`", + "~":"~", + "!":"﹗", + "@":"@", + "#":"#", + "$":"﹩", + "%":"﹪", + "^":"^", + "&":"﹠", + "*":"﹡", + "(":"⁽", + ")":"⁾", + "_":"⁻", + "-":"⁻", + "=":"⁼", + "+":"+", + "{":"{", + "[":"[", + "}":"}", + "]":"]", + ":":"﹕", + ";":"﹔", + "?":"﹖", +} + +json_mungers['upsidedown'] = { + 'A':'∀', + 'B':'𐐒', + 'C':'Ɔ', + 'E':'Ǝ', + 'F':'Ⅎ', + 'G':'פ', + 'H':'H', + 'I':'I', + 'J':'ſ', + 'L':'˥', + 'M':'W', + 'N':'N', + 'P':'Ԁ', + 'R':'ᴚ', + 'T':'⊥', + 'U':'∩', + 'V':'Λ', + 'Y':'⅄', + 'a':'ɐ', + 'b':'q', + 'c':'ɔ', + 'd':'p', + 'e':'ǝ', + 'f':'ɟ', + 'g':'ƃ', + 'h':'ɥ', + 'i':'ᴉ', + 'j':'ɾ', + 'k':'ʞ', + 'm':'ɯ', + 'n':'u', + 'p':'d', + 'q':'b', + 'r':'ɹ', + 't':'ʇ', + 'u':'n', + 'v':'ʌ', + 'w':'ʍ', + '1':'Ɩ', + '2':'ᄅ', + '3':'Ɛ', + '4':'ㄣ', + '5':'ϛ', + '6':'9', + '7':'ㄥ', + '8':'8', + '9':'6', + '0':'0', + '.':'˙', + ',':'\'', + '\'':',', + '"':',,', + '`':',', + '<':'>', +'>':'<', +'∴':'∵', +'&':'⅋', +'_':'‾', +'?':'¿', +'!':'¡', +'[':']', +']':'[', +'(':')', +')':'(', +'{':'}', +'}':'{' +} + +def munger(text, json_type='russian'): + output = "" + json_munger = json_mungers[json_type] + if json_type == 'russian': + text = text.upper() + for c in text: + if c in json_munger: + output += random.choice(json_munger[c]) + else: + output += c + return output + +def munge(text, function='mock'): + fun = function.lower() + if fun == 'flip': + fun = 'upsidedown' + text = text[::-1] + if fun == 'aesthetic' or fun == 'vaporwave': + return vaporwave(text) + elif fun == 'ubbi': + return re.sub(r"[aeiou]+", r"ub$0"); + elif fun == 'mock': + return mock(text) + if fun == 'circled': + return unicode_offset(text, "circle"); + elif fun in unicode_offsets: + return unicode_offset(text, fun) + elif fun in json_mungers: + return munger(text, fun) + else: + return " ".join(["no munger named", fun]) + +@pinhook.plugin.listener('track') +def track(msg): + if not str(msg.text).startswith(('!', '.', ';', ':')): + key = (msg.channel, msg.nick) + mcache[key] = str(msg.text) + +@pinhook.plugin.register("!m", "!munge -- munges previous line, or provided text") +@pinhook.plugin.register("!munge", "!munge -- munges previous line, or provided text") +def munge_command(msg): + """!m(unge) ubbi, circle, flip, vaporwave, mock, russian; works on either the last line of text in the channel, or any text placed after the munge style""" + output = "" + text = msg.arg + if len(text.split()) >= 2: + text = text.split() + output = munge(" ".join(text[1:]), text[0]) + else: + try: + if len(text): + output = munge(mcache[(msg.channel, msg.nick)], text) + else: + output = munge(mcache[(msg.channel, msg.nick)]) + except KeyError: + output = "Not Enough Messages." + + return pinhook.plugin.message(output) +