#!/bin/env python
import botlib
from time import sleep
from random import randint, choice, shuffle
import re
from copy import copy
NAME = "dee"
MAN = 'https://aleteoryx.me/deeman.md'
### "AI" ###
def send(msg):
global NAME
botlib.send(f'{NAME}: {msg}')
def act(what):
global NAME
botlib.send(f'{NAME} {msg}')
def help(nick):
global MAN
send(f'{nick}, my help is at {MAN}')
last_cmd = ('dee, roll d6', ['dee,', 'roll', 'd6'], ['dee', 'roll', 'd6'])
def handle_msg(nick, line, words, pwords):
global NAME, MAN, last_cmd
print(nick, line, words, pwords)
og_data = copy(line), copy(words), copy(pwords)
if not botlib.strip_direct_address(NAME, [], words, pwords):
return
cmd = pwords[0]
if cmd == 'again':
return handle_msg(nick, *map(copy, last_cmd))
else:
last_cmd = og_data
pwords.pop(0)
words.pop(0)
if cmd == 'help':
help(nick)
elif cmd not in ('roll', 'pick', 'shuf', 'shuffle', 'do', 'don\'t', 'should', 'are'):
send(f'{nick}, I don\'t know how to do that! My help is at {MAN}')
elif len(pwords) < 1:
send(f'{nick}, {cmd} what?')
elif cmd == 'roll':
dice(nick, words, pwords)
elif cmd == 'pick':
pick(nick, words, pwords)
elif cmd in ('shuf', 'shuffle'):
shuf(nick, words, pwords)
elif cmd == 'do':
if pwords[0].lower() == 'i':
do_i(nick, words[1:], pwords[1:])
elif pwords[:2] == ['you', 'think']:
dyt(nick)
else:
send(f'{nick}, do what?')
elif cmd == 'should':
if pwords[0].lower() == 'i':
do_i(nick, words[1:], pwords[1:])
else:
dyt(nick)
elif cmd == 'don\'t':
if pwords[0].lower() == 'i':
dyt(nick)
elif pwords[:2] == ['you', 'think']:
dyt(nick)
else:
send(f'{nick}, don\'t what?')
elif cmd == 'are':
dyt(nick)
def handle_action(line, words, pwords):
pass
### CHOICES, CHOICES ###
def pick(nick, words, pwords, ignore=['or']):
formats = [
'my sources tell me {} is the best option.',
'how about {}?',
'I\'ve got a good feeling about {}.',
'if I *had* to pick, I\'d say {}.',
'signs point to {}.',
'{}!',
'try {}... if you dare!',
'a terrible fate will befall you! ...unless you pick {}!',
'after extensive deliberation, I think {} is the only option',
'nothing ventured, nothing gained! try {}.',
]
terms = botlib.split_list(words, pwords, ignore)
picked = " ".join(choice(terms)).rstrip('?')
reply = choice(formats).format(picked)
send(f'{nick}, {reply}')
def shuf(nick, words, pwords):
terms = botlib.split_list(words, pwords, ['and'])
shuffle(terms)
send(f'{nick}: {", ".join(map(" ".join, terms))}')
def do_i(nick, words, pwords):
if 'or' not in pwords:
dyt(nick)
else:
for word in words:
if word.endswith(','):
pick(nick, words, pwords, ['or', 'do', 'should', 'i'])
break
else:
dyt(nick)
def dyt(nick):
replies = [
'yes',
'yeah!',
'absolutely!',
'sure',
'signs point to yes',
'totally!',
'no',
'nah',
'signs point to no',
'never in a million years!',
'maybe',
'dunno',
'not sure',
'ask again later'
]
send(f'{nick}, {choice(replies)}')
### DICE ###
class DiceParseException(Exception):
pass
def parse_number(word):
return int(word)
'''
dice = 'then'? spec offset?
spec = 'another' num? 'more'?
spec = 'another'? num? 'more'? size
spec = 'another'? num 'more'
size = 'd' num
offset = op num
op = 'plus' | 'minus' | '+' | '-'
num = int() or human-readable number
'''
last_dice = 6
last_line = ['1', 'd6']
def parse_dice(nick, words):
global last_dice, last_line
if words[0] == 'then':
words.pop(0)
if len(words) == 0:
return 0, 0, 0
if words[0] == 'again':
return parse_dice(nick, last_line)
else:
last_line = copy(words)
has_another = False
if words[0] == 'another':
has_another = True
words.pop(0)
if len(words) == 0:
return 1, last_dice, 0
can_more = True
try:
count = parse_number(words[0])
words.pop(0)
except (IndexError, ValueError):
count = 1
can_more = False
if len(words) == 0:
if has_another:
return count, last_dice, 0
else:
raise DiceParseException(f'roll {count} what?')
if d := re.match('d([0-9]+|[A-Za-z]+)', words[0]):
try:
last_dice = parse_number(d.group(1))
except (IndexError, ValueError):
raise DiceParseException('I don\'t have any dice like that!')
words.pop(0)
if len(words) > 0 and words[0] == 'more':
words.pop(0)
if len(words) == 0:
return count, last_dice, 0
elif words[0] == 'more':
if not can_more:
raise DiceParseException(f'roll how many more d{last_dice}?')
words.pop(0)
if len(words) == 0:
return count, last_dice, 0
if words[0] in ('plus', 'add', '+', 'minus', 'sub', 'subtract', '-'):
try:
offset = parse_number(words[1])
except (IndexError, ValueError):
raise DiceParseException(f'roll {count} d{last_dice} {words[0]} what?')
if words[0] not in ('plus', 'add', '+'):
offset *= -1
return count, last_dice, offset
raise DiceParseException(f'I don\'t know how to roll that!')
def dice(nick, words, pwords):
global MAN
result = nick
terms = botlib.split_list(words, pwords, ['and'])
print(f'{terms=}')
for term in terms:
try:
count, size, offset = parse_dice(nick, term)
except DiceParseException as e:
send(f'{nick}, {e.args[0]} See {MAN} for syntax.')
return
if count == 0:
continue
if count > 50:
send(f'{nick}, roll them yourself!')
return
rollsum = offset
# string formatting
if count == 1:
result += f', 1 d{size}'
else:
result += f', {count} d{size}s'
if offset > 0:
offset = f' + {offset}'
elif offset < 0:
offset = f' - {-offset}'
else:
offset = ''
result += f'{offset}:'
for i in range(count):
if size < 1:
roll = size
else:
roll = randint(1, size)
rollsum += roll
if i > 0:
result += f' + {roll}'
else:
result += f' {roll}'
if count > 1 or offset != '':
result += f'{offset} = {rollsum}'
if size == 0:
result += ', obviously'
if result == nick:
send(f'{nick}, done, i guess')
elif len(result) < 100:
send(result)
else:
send(f'{nick}, roll them yourself!')
### BOOT ###
botlib.parse_args()
while True:
sleep(5)
botlib.writeln(f"SKIP {botlib.lastmsg}")
lines = botlib.readmany()
for line in lines:
print(line)
botlib.handle_line(line, NAME, handle_msg, handle_action)