From 86b0d4e3371f77b81a296beb694120f27ed570a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Wed, 20 Jan 2021 20:00:50 +0200 Subject: [PATCH] Convert to python3 and hook up to o3-base --- botcmd.py | 12 ++-- eliza.py | 34 +++++----- o2_botcmd.py | 186 +++++++++++++++++++++++++-------------------------- 3 files changed, 114 insertions(+), 118 deletions(-) diff --git a/botcmd.py b/botcmd.py index 217ab41..b8d89e2 100644 --- a/botcmd.py +++ b/botcmd.py @@ -1,9 +1,11 @@ +import o2_botcmd + # initialize(*, config) # Called to initialize the IRC bot # Runs before even logger is brought up, and blocks further bringup until it's done # config is a configpatser.ConfigParser object containig contents of bot.conf def initialize(*, config): - ... + o2_botcmd.init() # on_connect(*, irc) # Called after IRC bot has connected and sent the USER/NICk commands but not yet attempted anything else @@ -11,14 +13,14 @@ def initialize(*, config): # Blocks the bot until it's done, including PING/PONG handling # irc is the IRC API object def on_connect(*, irc): - ... + pass # on_quit(*, irc) # Called just before IRC bot sends QUIT # Blocks the bot until it's done, including PING/PONG handling # irc is the IRC API object def on_quit(*, irc): - ... + o2_botcmd.cron.ctrl('QUIT') # handle_message(*, prefix, message, nick, channel, irc) # Called for PRIVMSGs. @@ -29,7 +31,7 @@ def on_quit(*, irc): # irc is the IRC API object # All strings are bytestrings def handle_message(*, prefix, message, nick, channel, irc): - ... + o2_botcmd.parse(nick, channel, b'PRIVMSG', [channel, message], irc) # handle_nonmessage(*, prefix, command, arguments, irc) # Called for all other commands than PINGs and PRIVMSGs. @@ -39,4 +41,4 @@ def handle_message(*, prefix, message, nick, channel, irc): # irc is the IRC API object # All strings are bytestrings def handle_nonmessage(*, prefix, command, arguments, irc): - ... + o2_botcmd.parse(prefix.split(b'!')[0], None, command, arguments, irc) diff --git a/eliza.py b/eliza.py index c4f6834..a06b157 100644 --- a/eliza.py +++ b/eliza.py @@ -4,29 +4,29 @@ # a cheezy little Eliza knock-off by Joe Strout # with some updates by Jeff Epler # hacked into a module and updated by Jez Higgins -# last revised: 28 February 2005 +# converted to python3 by nortti +# last revised: 20 January 2021 #---------------------------------------------------------------------- -import string import re import random class eliza: def __init__(self): - self.keys = map(lambda x:re.compile(x[0], re.IGNORECASE),gPats) - self.values = map(lambda x:x[1],gPats) + self.keys = list(map(lambda x:re.compile(x[0], re.IGNORECASE),gPats)) + self.values = list(map(lambda x:x[1],gPats)) #---------------------------------------------------------------------- # translate: take a string, replace any words found in dict.keys() # with the corresponding dict.values() #---------------------------------------------------------------------- def translate(self,str,dict): - words = string.split(string.lower(str)) + words = str.lower().split() keys = dict.keys(); for i in range(0,len(words)): if words[i] in keys: words[i] = dict[words[i]] - return string.join(words) + return ' '.join(words) #---------------------------------------------------------------------- # respond: take a string, a set of regexps, and a corresponding @@ -42,13 +42,13 @@ class eliza: # chosen randomly from among the available options resp = random.choice(self.values[i]) # we've got a response... stuff in reflected text where indicated - pos = string.find(resp,'%') + pos = resp.find('%') while pos > -1: - num = string.atoi(resp[pos+1:pos+2]) + num = int(resp[pos+1:pos+2]) resp = resp[:pos] + \ self.translate(match.group(num),gReflections) + \ resp[pos+2:] - pos = string.find(resp,'%') + pos = resp.find('%') # fix munged punctuation at the end if resp[-2:] == '?.': resp = resp[:-2] + '.' if resp[-2:] == '??': resp = resp[:-2] + '?' @@ -291,20 +291,20 @@ gPats = [ # command_interface #---------------------------------------------------------------------- def command_interface(): - print "Therapist\n---------" - print "Talk to the program by typing in plain English, using normal upper-" - print 'and lower-case letters and punctuation. Enter "quit" when done.' - print '='*72 - print "Hello. How are you feeling today?" + print("Therapist\n---------") + print("Talk to the program by typing in plain English, using normal upper-") + print('and lower-case letters and punctuation. Enter "quit" when done.') + print('='*72) + print("Hello. How are you feeling today?") s = "" therapist = eliza(); while s != "quit": - try: s = raw_input(">") + try: s = input(">") except EOFError: s = "quit" - print s + print(s) while s[-1] in "!.": s = s[:-1] - print therapist.respond(s) + print(therapist.respond(s)) if __name__ == "__main__": diff --git a/o2_botcmd.py b/o2_botcmd.py index 1152237..90fbe7d 100644 --- a/o2_botcmd.py +++ b/o2_botcmd.py @@ -3,8 +3,7 @@ import threading import random import re import time - -concmd=['/q', '/lt', '/st', '/lg', '/lm', '/sm'] +import functools blacklist = [] @@ -58,9 +57,9 @@ class Cron(threading.Thread): self.cronctrllock.release() self.timedjobslock.acquire() - self.timedjobs = map((lambda (time, fn): (time-1, fn)), self.timedjobs) - torun = map((lambda (time, fn): fn), filter((lambda (time, fn): time<=0), self.timedjobs)) - self.timedjobs = filter((lambda (time, fn): time>0), self.timedjobs) + self.timedjobs = list(map((lambda time_fn: (time_fn[0]-1, time_fn[1])), self.timedjobs)) + torun = list(map((lambda time_fn: time_fn[1]), filter((lambda time_fn: time_fn[0]<=0), self.timedjobs))) + self.timedjobs = list(filter((lambda time_fn: time_fn[0]>0), self.timedjobs)) self.timedjobslock.release() for fn in torun: @@ -182,23 +181,26 @@ def init(): loadgods() def chmode(irc, chan, nick, mode, args): - set_unset = mode[0] - mode = mode[1:] + if type(nick) != str: nick = nick.decode() + set_unset = mode[0].encode() + mode = mode[1:].encode() if isauthorized(irc, chan, nick): if args == ['']: - irc.send('MODE %s %s %s' % (chan, set_unset+mode, nick)) + irc.send_raw(b'MODE %s %s %s' % (chan, set_unset+mode, nick.encode())) else: nicks = [] for nick in args: - nicks.append(nick) + nicks.append(nick.encode()) if len(nicks) == 4: - irc.send('MODE %s %s %s' % (chan, set_unset+mode*4, ' '.join(nicks))) + irc.send_raw(b'MODE %s %s %s' % (chan, set_unset+mode*4, b' '.join(nicks))) nicks = [] if nicks: - irc.send('MODE %s %s %s' % (chan, set_unset+mode*len(nicks), ' '.join(nicks))) + irc.send_raw(b'MODE %s %s %s' % (chan, set_unset+mode*len(nicks), b' '.join(nicks))) def istrusted(chan, account): + if type(chan) != str: chan = chan.decode() + if type(account) != str: account = account.decode() trustedlock.acquire() if chan in trusted and account in trusted[chan]: trustedlock.release() @@ -262,18 +264,21 @@ def removeaccountcheck(id): def getaccountcheckidbynick(nick): global accountcheck, accountchecklock + + if type(nick) != str: nick = nick.decode() accountchecklock.acquire() - getid = lambda (id, nick, account): id - filterbynick = lambda (id, cknick, account): cknick == nick - ids = map(getid, filter(filterbynick, accountcheck)) + getid = lambda id_nick_account: id_nick_account[0] + filterbynick = lambda id_cknick_account: id_cknick_account[1] == nick + ids = list(map(getid, filter(filterbynick, accountcheck))) accountchecklock.release() return ids def getaccount(irc, nick): + if type(nick) != str: nick = nick.decode() id = initaccountcheck(nick) - irc.send('WHOIS ' + nick) + irc.send_raw(b'WHOIS ' + nick.encode()) cron.queuejob(5, (lambda : setaccountcheckvalue(id, ''))) account = None @@ -288,11 +293,12 @@ def getaccount(irc, nick): return account def isauthorized(irc, chan, nick): + if type(nick) != str: nick = nick.decode() account = getaccount(irc, nick) if account: return istrusted(chan, account) else: - irc.msg(nick, zwsp + 'Identify with NickServ') + irc.bot_response(nick.encode(), 'Identify with NickServ') class ArgsfmtError(Exception): def __init__(self, msg): @@ -363,7 +369,7 @@ def parsecmd(line, args): if len(argtypes) >= 1 and ARG_UNL in argtypes[:-1]: # Disallow non-final unlimited arguments raise ArgsfmtError('Non-final unlimited argument') - if len(filter((lambda type: type == ARG_OPT or type == ARG_UNL), argtypes)) > 1: # Disallow more than one optional or unlimited argument per argument string + if len(list(filter((lambda type: type == ARG_OPT or type == ARG_UNL), argtypes))) > 1: # Disallow more than one optional or unlimited argument per argument string raise ArgsfmtError('Ambiguous argument format') # Remove the command @@ -408,27 +414,23 @@ def parsecmd(line, args): else: return out -def parse((line, irc)): +def parse(nick, chan, command, arguments, irc): global blacklist global msgs, msgslock global trusted, trustedlock, gods, godslock global doctor, die_expr - line = line.split(' ') - nick = line[0].split('!')[0][1:] - chan = line[2] if line[2][0] == '#' else nick - - zwsp = '\xe2\x80\x8b' + zwsp = '\u200b' if nick in blacklist: return - elif len(line) >= 4 and len(line[3]) >= len(zwsp)+1 and line[3][:len(zwsp)+1] == ':'+zwsp: # If line begins with ZWSP + elif len(arguments) >= 2 and len(arguments[1]) >= len(zwsp.encode('utf-8')) and arguments[1][:len(zwsp.encode('utf-8'))] == zwsp.encode('utf-8'): # If line begins with ZWSP return - if line[1]=='PRIVMSG' and line[3][:2] != ': ': + if command==b'PRIVMSG' and arguments[1][:1] != b' ': reply = chan - cmdline = [line[3][1:]] + line[4:] + cmdline = arguments[1].decode('utf-8').split(' ') while '' in cmdline: cmdline.remove('') @@ -439,14 +441,14 @@ def parse((line, irc)): newchan, newcmdline = parsecmd(cmdline, 'channel {command}') newcmdline = newcmdline.split(' ') if isauthorized(irc, newchan, nick): - chan = newchan + chan = newchan.encode() cmdline = newcmdline else: - irc.msg(chan, zwsp + 'Usage #chan channel command') + irc.bot_response(chan, usage('#chan')) if matchcmd(cmdline, '#echo'): text = parsecmd(cmdline, '{text}') - irc.msg(reply, zwsp+text) + irc.bot_response(reply, text) elif matchcmd(cmdline, '#op'): args = parsecmd(cmdline, '{args}') chmode(irc, chan, nick, '+o', args.split(' ')) @@ -457,57 +459,63 @@ def parse((line, irc)): args = parsecmd(cmdline, '{args}') chmode(irc, chan, nick, '+v', args.split(' ')) elif matchcmd(cmdline, '#quiet'): - arg = parsecmd(cmdline, 'nick') - chmode(irc, chan, nick, '+q', [arg + '!*@*']) + if matchcmd(cmdline, '#quiet', 'nick'): + arg = parsecmd(cmdline, 'nick') + chmode(irc, chan, nick, '+q', [arg + '!*@*']) + else: + irc.bot_response(reply, usage('#quiet')) elif matchcmd(cmdline, '#dequiet'): - arg = parsecmd(cmdline, 'nick') - chmode(irc, chan, nick, '-q', [arg + '!*@*']) + if matchcmd(cmdline, '#dequiet', 'nick'): + arg = parsecmd(cmdline, 'nick') + chmode(irc, chan, nick, '-q', [arg + '!*@*']) + else: + irc.bot_response(reply, usage('#dequiet')) elif matchcmd(cmdline, '#devoice'): args = parsecmd(cmdline, '{args}') chmode(irc, chan, nick, '-v', args.split(' ')) elif matchcmd(cmdline, '#kick'): if matchcmd(cmdline, '#kick', 'nick {reason}'): kicknick, kickreason = parsecmd(cmdline, 'nick {reason}') - if kicknick.lower() == irc.nick: - irc.send('KICK %s %s :Fuck you' % (chan, nick)) + if kicknick.lower() == irc.get_nick().decode('utf-8'): + irc.send_raw(b'KICK %s %s :Fuck you' % (chan, nick)) else: if isauthorized(irc, chan, nick): - irc.send('KICK %s %s :%s'%(chan, kicknick, kickreason)) + irc.send_raw(b'KICK %s %s :%s'%(chan, kicknick.encode(), kickreason.encode())) else: - irc.msg(reply, zwsp + 'Usage #kick nick reason') + irc.bot_response(reply, usage('#kick')) elif matchcmd(cmdline, '#src'): - irc.msg(reply, zwsp + 'https://github.com/JuEeHa/oonbotti2') + irc.bot_response(reply, 'https://github.com/JuEeHa/oonbotti2') elif matchcmd(cmdline, '#prefix') and chan == '#osdev-offtopic': - irc.msg(reply, zwsp + 'gopher://ayu.smar.fi:7070/0/hash-prefix') + irc.bot_response(reply, 'gopher://ayu.smar.fi:7070/0/hash-prefix') elif matchcmd(cmdline, '#msg'): if matchcmd(cmdline, '#msg', 'nick {message}'): msgnick, message = parsecmd(cmdline, 'nick {message}') if chan == nick: # In a query: origin = "[query]" else: # In a channel - origin = chan + origin = chan.decode() with msgslock: if msgnick not in msgs: msgs[msgnick] = [] - msgs[msgnick].append((nick, origin, message)) + msgs[msgnick].append((nick.decode(), origin, message)) savemessages() else: - irc.msg(reply, zwsp + 'Usage: #msg nick message') + irc.bot_response(reply, usage('#msg')) elif matchcmd(cmdline, '#trusted?'): if matchcmd(cmdline, '#trusted?', '[nick]'): trustnick = parsecmd(cmdline, '[nick]') if trustnick == '': - trustnick = nick + trustnick = nick.decode() account = getaccount(irc, trustnick) if account: if istrusted(chan, account): - irc.msg(reply, zwsp + '%s is trusted' % trustnick) + irc.bot_response(reply, '%s is trusted' % trustnick) else: - irc.msg(reply, zwsp + '%s is not trusted' % trustnick) + irc.bot_response(reply, '%s is not trusted' % trustnick) else: - irc.msg(reply, zwsp + 'Failed to get account for %s' % trustnick) + irc.bot_response(reply, 'Failed to get account for %s' % trustnick) else: - irc.msg(reply, zwsp + 'Usage: #trusted? [nick]') + irc.bot_response(reply, usage('#truste?')) elif matchcmd(cmdline, '#trust'): if matchcmd(cmdline, '#trust', 'nick'): trustnick = parsecmd(cmdline, 'nick') @@ -517,9 +525,9 @@ def parse((line, irc)): addtrusted(chan, account) savetrusted() else: - irc.msg(reply, zwsp + 'Failed to get account for %s' % trustnick) + irc.bot_response(reply, 'Failed to get account for %s' % trustnick) else: - irc.msg(reply, zwsp + 'Usage #trust nick') + irc.bot_response(reply, usage('#trust')) elif matchcmd(cmdline, '#untrust'): if matchcmd(cmdline, '#untrust', 'nick'): untrustnick = parsecmd(cmdline, 'nick') @@ -531,20 +539,20 @@ def parse((line, irc)): account = untrustnick if account: godslock.acquire() - if chan not in gods or account not in gods[chan]: - rmtrusted(chan, untrustnick) + if chan.decode() not in gods or account not in gods[chan.decode()]: + rmtrusted(chan.decode(), untrustnick) godslock.release() savetrusted() else: - irc.msg(reply, zwsp + 'Failed to get account for %s' % untrustnick) + irc.bot_response(reply, 'Failed to get account for %s' % untrustnick) else: - irc.msg(reply, zwsp + 'Usage #untrust nick') + irc.bot_response(reply, usage('#untrust')) elif matchcmd(cmdline, '#ls-trusted'): trustedlock.acquire() - if chan in trusted: + if chan.decode() in trusted: lines = [] line = '' - for account in trusted[chan]: + for account in trusted[chan.decode()]: if line == '': line = account elif len(line + ', ' + account) <= 255: # Playing it safe not to get truncated @@ -556,22 +564,22 @@ def parse((line, irc)): lines.append(line) for line in lines: - irc.msg(nick, zwsp + '%s: %s' % (chan, line)) + irc.bot_response(nick, '%s: %s' % (chan.decode(), line)) trustedlock.release() elif matchcmd(cmdline, '#invite'): - irc.msg(chan, zwsp + '%s: #invite has been removed. Use manual invite' % nick) + irc.bot_response(chan, '%s: #invite has been removed. Use manual invite' % nick) elif matchcmd(cmdline, '#help'): if matchcmd(cmdline, '#help', '[command]'): command = parsecmd(cmdline, '[command]') helptext = help(command) if helptext: - irc.msg(reply, zwsp+helptext) + irc.bot_response(reply, helptext) elif matchcmd(cmdline, '#esoteric') and chan == '#esoteric': - irc.msg(reply, zwsp + 'Nothing here') - elif cmdline[0] in [irc.nick, irc.nick+',', irc.nick+':']: + irc.bot_response(reply, 'Nothing here') + elif cmdline[0] in [irc.get_nick().decode(), irc.get_nick().decode()+',', irc.get_nick().decode()+':']: question = parsecmd(cmdline, '{question}') if len(question) < 2 or question[:2] != ':D': # Mandated by #osdev-offtopic law - irc.msg(reply, zwsp + '%s: %s' % (nick, doctor.respond(question))) + irc.bot_response(reply, '%s: %s' % (nick.decode(), doctor.respond(question))) elif die_expr.match(cmdline[0]): die = cmdline[0][1:].split('d') times = int(die[0]) if die[0] else 1 @@ -590,18 +598,18 @@ def parse((line, irc)): if die == '%': if times != 1: - irc.msg(reply, zwsp + 'Not supported') + irc.bot_response(reply, 'Not supported') else: - irc.msg(reply, zwsp + '%s%s' % (random.randint(0,9), random.randint(0,9))) + irc.bot_response(reply, '%s%s' % (random.randint(0,9), random.randint(0,9))) elif die < 1: - irc.msg(reply, zwsp + 'This die is not available in your space-time region.') + irc.bot_response(reply, 'This die is not available in your space-time region.') elif times < 1: - irc.msg(reply, zwsp + 'What exactly do you want me to do?') + irc.bot_response(reply, 'What exactly do you want me to do?') elif times > 128: - irc.msg(reply, zwsp + 'Sorry, I don\'t have that many. Can I borrow yours?') + irc.bot_response(reply, 'Sorry, I don\'t have that many. Can I borrow yours?') else: - rolls = [random.randint(1, die) for i in xrange(times)] - result = reduce((lambda x, y: x + y), rolls) + rolls = [random.randint(1, die) for i in range(times)] + result = functools.reduce((lambda x, y: x + y), rolls) if times > 1: text = '%s (%s)' % (str(result), ', '.join([str(i) for i in rolls])) @@ -613,46 +621,32 @@ def parse((line, irc)): elif plus < 0: text = '%i (%s - %i)' % (result + plus, text, -plus) - irc.msg(reply, zwsp + text) - elif line[1] == '330': # WHOIS: is logged in as - whoisnick = line[3] - account = line[4] + irc.bot_response(reply, text) + elif command == b'330': # WHOIS: is logged in as + whoisnick = arguments[1].decode('utf-8') + account = arguments[2].decode('utf-8') for id in getaccountcheckidbynick(whoisnick): setaccountcheckvalue(id, account) - elif line[1] == '318': # WHOIS: End of /WHOIS list. - whoisnick = line[3] + elif command == '318': # WHOIS: End of /WHOIS list. + whoisnick = arguments[1].decode('utf-8') for id in getaccountcheckidbynick(whoisnick): if getaccountcheckvalue(id) == None: setaccountcheckvalue(id, '') # Mark as failed, '' is used because None is already reserved - elif line[1] == 'INVITE' and line[2] == irc.nick and line[3][1:] in irc.chan.split(' '): - if isauthorized(irc, line[3][1:], nick): - irc.send('JOIN ' + line[3]) - elif line[1] == '482': - irc.msg(line[3], zwsp + 'Not op') + elif command == b'INVITE' and arguments[0] == irc.get_nick() and arguments[1] in irc.get_channel().split(' '): + if isauthorized(irc, arguments[1], nick): + irc.send_raw(b'JOIN ' + arguments[1]) + elif command == b'482': + irc.bot_response(arguments[1], 'Not op') msgs_changed = False with msgslock: - if (line[1] == 'PRIVMSG' or line[1] == 'JOIN') and nick in msgs: - for sender, origin, msg in msgs.pop(nick): - irc.msg(nick, zwsp + '%s <%s> %s' % (origin, sender, msg)) + if (command == b'PRIVMSG' or command == b'JOIN') and nick.decode('utf-8') in msgs: + for sender, origin, msg in msgs.pop(nick.decode()): + irc.bot_response(nick, '%s <%s> %s' % (origin, sender, msg)) msgs_changed = True if msgs_changed: savemessages() -def execcmd(cmdline): - if cmdline[0] == '/q': - cron.ctrl('QUIT') - elif cmdline[0] == '/lt': - loadtrusted() - elif cmdline[0] == '/st': - savetrusted() - elif cmdline[0] == '/lg': - loadgods() - elif cmdline[0] == '/lm': - loadmessages() - elif cmdline[0] == '/sm': - savemessages() - def usage(cmd, message = True): usage = {'#echo': 'text', '#op': '[nick]',