port to new balun ircrobots framework

This commit is contained in:
vulpine 2021-01-30 21:05:34 -05:00
parent c7746b72d2
commit 04e7b7c064
8 changed files with 258 additions and 182 deletions

2
.gitignore vendored
View File

@ -5,3 +5,5 @@
sizelog sizelog
*journal *journal
auth.py

188
bot.py
View File

@ -1,102 +1,134 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import asyncio, os, importlib
import pydle, asyncio, dataset, sys, os, time from irctokens import build, Line
from ircrobots import Bot as BaseBot
from ircrobots import Server as BaseServer
from ircrobots import ConnectionParams, SASLUserPass, SASLSCRAM
class Kim(pydle.Client): from auth import username, password
async def on_connect(self): import shared
print('Connected!')
self.modules = {} def is_admin(func):
self.cmd = {} async def decorator(self,channel,nick,msg):
self.rawm = {} if nick.lower() in self.users and self.users[nick.lower()].account in self.admins:
self.help = {} await func(self,channel,nick,msg)
self.db = dataset.connect('sqlite:///database.db') else:
self.t=0 await message(self,'core',channel,'you do not have permission to do that')
return decorator
print('loading modules...') #def is_chanop(func):
await self.loadMods()
print('joining channels')
for i in self.chansjoin:
await self.join(i)
print('Done!')
# tilde +B bot
await self.set_mode(self.nickname, '+B')
async def loadMods(self): def command(commandname):
for i in [s for s in os.listdir('modules') if ".py" in s and '.swp' not in s]: def decorator(func):
i = i[:-3] shared.commands[commandname] = func
print('loading', i) return func
m = __import__("modules."+i) return decorator
m = eval('m.'+i)
await m.init(self)
self.modules[i] = m
async def on_invite(self, channel, by): def listener(listenername):
await self.modules['invite'].invite(self, channel, by) def decorator(func):
# print('{} invited me to {}!'.format(by, channel)) shared.listeners.append((listenername, func))
# self.t = time.time()+1 return func
# await self.join(channel) return decorator
def rawm(rname):
def decorator(func):
shared.rawm[rname] = func
return func
return decorator
async def on_message(self, chan, source, msg):
if chan == self.nickname: async def message(self,modname,channel,msg):
chan = source await self.send(build("PRIVMSG",[channel,f'[\x036{modname}\x0f] {msg}']))
if source != self.nickname:
if time.time() > self.t:
if msg == '!botlist':
await self.message(chan, 'helo im kim, a learning chatbot https://tildegit.org/xfnw/kim/')
await self.parseCommand(chan, source, msg)
for i in self.rawm:
await self.rawm[i](self, chan, source, msg)
async def parseCommand(self, chan, source, msg): class Server(BaseServer):
if msg[:len(self.prefix)] == self.prefix: async def line_read(self, line: Line):
print(f"{self.name} < {line.format()}")
msg = msg[len(self.prefix):] if 'on_'+line.command.lower() in dir(self):
cmd = msg.split(' ')[0] asyncio.create_task(self.__getattribute__('on_'+line.command.lower())(line))
msg = msg[len(cmd)+1:] for listener in shared.listeners:
if len(cmd) < 1: if listener[0] == line.command:
return asyncio.create_task(listener[1](self,line))
if cmd in self.cmd: async def line_send(self, line: Line):
await self.cmd[cmd](self, chan, source, msg) print(f"{self.name} > {line.format()}")
return
# fuzzy search for commands
results = [i for i in self.cmd if i.startswith(cmd)]
if len(results) == 1:
await self.cmd[results[0]](self, chan, source, msg)
async def is_admin(self, nickname): async def on_001(self, line):
asyncio.create_task(self.load_modules())
# Check the WHOIS info to see if the source has identified with NickServ.
# This is a blocking operation, so use yield.
info = await self.whois(nickname)
if 'account' in info:
account = info['account']
else:
# they are not nickserv registered
return False
return account in self.admins async def load_modules(self):
for i in [s for s in os.listdir('modules') if '.py' in s and '.swp' not in s]:
i = i[:-3]
m = importlib.import_module('modules.' + i)
asyncio.create_task(m.init(self))
shared.modules[i] = m
#async def on_private_message(self, trash, source, msg): # depricated, to support old modules
# if source != self.nickname: async def message(self,channel,msg):
# for i in self.rawm: await self.send(build("PRIVMSG",[channel,msg]))
# await self.rawm[i](self, source, source, msg)
async def on_privmsg(self, line):
if line.tags and "batch" in line.tags and line.tags["batch"] == '1':
return
channel = line.params[0]
nick = line.source.split('!')[0]
msg = line.params[1]
if channel == self.nickname:
channel = nick
await self.handle_rawm(channel,nick,msg)
await self.handle_command(channel,nick,msg)
async def handle_rawm(self,channel,nick,msg):
for i in shared.rawm:
await shared.rawm[i](self,channel,nick,msg)
async def handle_command(self,channel,nick,msg):
if msg[:len(shared.prefix)] == shared.prefix:
msg = msg[len(shared.prefix):]
cmd = msg.split(' ')[0]
msg = msg[len(cmd)+1:]
if len(cmd) < 1:
return
if cmd in shared.commands:
await shared.commands[cmd](self,channel,nick,msg)
return
results = [i for i in shared.commands if i.startswith(cmd)]
if len(results) == 1:
await shared.commands[results[0]](self,channel,nick,msg)
class Bot(BaseBot):
def create_server(self, name: str):
return Server(self, name)
async def main():
bot = Bot()
sasl_params = SASLUserPass(username, password)
params = ConnectionParams(
"kim",
host = "irc.tilde.chat",
port = 6697,
tls = True,
sasl = sasl_params)
await bot.add_server("tilde", params)
await bot.run()
if __name__ == "__main__": if __name__ == "__main__":
client = Kim('kim', realname='owens bot') asyncio.run(main())
client.admins = ['lickthecheese', 'ben', 'coffeeowl', 'gbmor', 'tomasino', 'ubergeek', 'deepend', 'calamitous', 'khuxkm']
client.prefix = 'kim: '
client.run('team.tilde.chat', tls=True, tls_verify=False)

View File

@ -1,52 +1,52 @@
import importlib, time, asyncio, pydle import importlib, time, asyncio, random
from bot import *
quitmessages = [
"time to die",
'you can hide, but you can not run!',
"you're next",
'bye',
'the balun has been popped.',
]
async def commit(self, chan, source, msg): async def commit(self, chan, source, msg):
await self.quit('{} told me to commit {}'.format(source,msg)) await self.quit('{} told me to commit {}'.format(source,msg))
async def quit(self, chan, source, msg): async def quit(self, chan, source, msg):
await self.quit('{} told me to {}'.format(source,msg)) await self.send(build("QUIT",[random.choice(quitmessages)]))
async def reloadmods(self, chan, source, msg): async def reloadmods(self, chan, source, msg):
await self.message(chan, '[\x036admin\x0f] reloading modules...') await self.message(chan, '[\x036admin\x0f] reloading modules...')
self.oldcmd = self.cmd shared.oldcmd = shared.commands
self.cmd = {} shared.commands = {}
self.rawm = {} shared.rawm = {}
self.help = {} shared.listeners = []
shared.help = {}
try: try:
for i in self.modules: for i in shared.modules:
importlib.reload(self.modules[i]) importlib.reload(shared.modules[i])
await self.modules[i].init(self) await shared.modules[i].init(self)
#await self.message(chan, '[\x036admin\x0f] load {} sucess!'.format(i)) #await self.message(chan, '[\x036admin\x0f] load {} sucess!'.format(i))
await self.message(chan, '[\x036admin\x0f] done! {} modules reloaded!'.format(len(self.modules))) await self.message(chan, '[\x036admin\x0f] done! {} modules reloaded!'.format(len(shared.modules)))
except: except:
await self.message(chan, '[\x036admin\x0f] reload failed... attempting to recover...') await self.message(chan, '[\x036admin\x0f] reload failed... attempting to recover...')
self.cmd = self.oldcmd shared.commands = shared.oldcmd
async def part(self, chan, source, msg): async def rawcmd(self, chan, source, msg):
await self.message(chan, '[\x036admin\x0f] bye {}'.format(msg)) await self.send_raw(msg)
await self.part(msg)
async def join(self, chan, source, msg):
self.t = time.time()+1
await self.message(chan, '[\x036admin\x0f] joined {}'.format(msg))
await self.join(msg)
async def joins(self, chan, source, msg): async def joins(self, chan, source, msg):
await self.message(chan, '[\x036admin\x0f] I will drop commands for some seconds to ignore chanhistory...') await self.message(chan, '[\x036admin\x0f] joining slowly as to not flood...')
for i in self.chandb.all(): for i in self.chandb.all():
self.t = time.time() + 5 await self.send(build("JOIN",[i['name']]))
try: await asyncio.sleep(1)
await self.join(i['name']) print('joined {}'.format(i['name']))
await asyncio.sleep(3) await self.message(chan, '[\x036admin\x0f] Sucess! i may be laggy for a bit while i sort through all these channels...')
print('joined {}'.format(i['name']))
except pydle.client.AlreadyInChannel:
print('I am already in {}'.format(i['name']))
await asyncio.sleep(3)
await self.message(chan, '[\x036admin\x0f] Sucess!')
async def aexec(self, code): async def aexec(self, code):
# Make an async function with the code and `exec` it # Make an async function with the code and `exec` it
@ -74,7 +74,7 @@ async def send(self, c, n, m):
await self.message(c, '[\x036admin\x0f] sent') await self.message(c, '[\x036admin\x0f] sent')
async def shut(self, c, n, m): async def shut(self, c, n, m):
self.qtime[c] = time.time()+(60*10) shared.qtime[c] = time.time()+(60*10)
await self.message(c, '[\x036admin\x0f] Ok, il be back in 10 minutes') await self.message(c, '[\x036admin\x0f] Ok, il be back in 10 minutes')
async def schans(self, c, n, m): async def schans(self, c, n, m):
@ -94,11 +94,56 @@ async def addalias(self,c,n,m):
await self.message(c,'[\x036admin\x0f] added "{}" alias for "{}"'.format(al,m)) await self.message(c,'[\x036admin\x0f] added "{}" alias for "{}"'.format(al,m))
async def addot(self,c,n,m):
al = m.split(' ')[0]
m = m[len(al)+1:] # dont use the list since i want trailing spaces
if al in self.rawm:
await self.message(c,'[\x036admin\x0f] no dont overwrite a command dummy')
return
self.rawm[al]=Ot(m,al).ot
await self.message(c,'[\x036admin\x0f] added "{}" trigger for "{}"'.format(al,m))
async def addtrigger(self,c,n,m):
al = m.split(' ')[0]
m = m[len(al)+1:] # dont use the list since i want trailing spaces
if al in self.rawm:
await self.message(c,'[\x036admin\x0f] no dont overwrite a command dummy')
return
self.rawm[al]=Trigger(m,al).trigger
await self.message(c,'[\x036admin\x0f] added "{}" trigger for "{}"'.format(al,m))
class Ot():
def __init__(self, ms, al):
self.ms = str(ms)
self.al = str(al)
async def ot(alself,self,c,n,m):
if alself.al in m and n != self.nickname:
asyncio.create_task(self.on_message(c,n,alself.ms.format(m)))
self.rawm.pop(alself.al)
class Trigger():
def __init__(self, ms, al):
self.ms = str(ms)
self.al = str(al)
async def trigger(alself,self,c,n,m):
if alself.al in m:
asyncio.create_task(self.on_message(c,n,alself.ms.format(m)))
class Alias(): class Alias():
def __init__(self, ms): def __init__(self, ms):
self.ms = str(ms) self.ms = str(ms)
async def alias(alself,self,c,n,m): async def alias(alself,self,c,n,m):
asyncio.create_task(self.parseCommand(c,n,alself.ms.format(m))) asyncio.create_task(self.on_message(c,n,alself.ms.format(m)))
@ -106,30 +151,33 @@ commands = {
'quit': quit, 'quit': quit,
'reload': reloadmods, 'reload': reloadmods,
'commit': commit, 'commit': commit,
'part': part, 'raw': rawcmd,
'join': join,
'eval': ev, 'eval': ev,
'send': send, 'send': send,
'joins': joins, 'joins': joins,
'shut': shut, 'shut': shut,
'schans': schans, 'schans': schans,
'addalias': addalias 'addalias': addalias,
'addtrigger': addtrigger,
'addot': addot,
} }
@command('admin')
@is_admin
async def adminHandle(self, chan, source, msg): async def adminHandle(self, chan, source, msg):
if await self.is_admin(source):
msg = msg.split(' ') msg = msg.split(' ')
if len(msg) < 1 or not msg[0] in commands: if len(msg) < 1 or not msg[0] in commands:
await self.message(chan, '[\x036admin\x0f] Invalid command') await self.message(chan, '[\x036admin\x0f] Invalid command')
return return
print('[ADMIN MODULE] {} told me to {}!!!'.format(source,msg[0])) print('[ADMIN MODULE] {} told me to {}!!!'.format(source,msg[0]))
await commands[msg.pop(0)](self, chan, source, ' '.join(msg)) asyncio.create_task(commands[msg.pop(0)](self, chan, source, ' '.join(msg)))
else:
await self.message(chan, '[\x036admin\x0f] You do not have permission to do this')
async def init(self): async def init(self):
self.chandb = self.db['chan'] self.chandb = shared.db['chan']
self.admins = ['lickthecheese']
return
self.cmd['admin'] = adminHandle self.cmd['admin'] = adminHandle
self.help['admin'] = ['admin - various bot owner commands (more for subcommands)', 'sub-commands of admin, for more info do help admin <command>: quit reload commit part join joins eval send'] self.help['admin'] = ['admin - various bot owner commands (more for subcommands)', 'sub-commands of admin, for more info do help admin <command>: quit reload commit part join joins eval send']

View File

@ -1,16 +0,0 @@
async def action(self,c,n,m):
await self.message(c,'\x01ACTION {}\x01'.format(m[:400]))
async def echo(self,c,n,m):
await self.message(c,'[\x036channels\x0f] {}'.format(m[:400]))
async def init(self):
self.chansjoin = ['#bots']
self.cmd['echo']=echo
self.cmd['action']=action

View File

@ -1,26 +1,9 @@
import time
async def invite(self, channel, by): from bot import *
if self.db['invite'].find_one(blacklist=channel):
print('{} invited me to {}, a blacklisted channel'.format(by,channel))
return
if self.db['invite'].find_one(enabled='true'):
print('{} invited me to {}!'.format(by, channel))
self.t = time.time()+1
await self.join(channel)
else:
if self.chandb.find_one(name=channel):
self.t = time.time()+1
await self.join(channel)
print('whee invited to {} by {}'.format(channel,by))
return
print('ive been invited but invites are disabled')
@listener('INVITE')
async def on_invite(self,line):
self.send(build("JOIN",[line.params[1]]))
async def init(self): async def init(self):
pass pass

View File

@ -1,13 +1,14 @@
from bot import *
import dataset import dataset
import random import random
import time import time
async def rec(self, m): async def rec(self, m):
prew = self.db['prew'] prew = shared.db['prew']
noch = self.db['noun'] noch = shared.db['noun']
beg = self.db['beg'] beg = shared.db['beg']
end = self.db['end'] end = shared.db['end']
pre = '' pre = ''
words = m.split(' ') words = m.split(' ')
if words[0] == 'admin': if words[0] == 'admin':
@ -22,20 +23,20 @@ async def rec(self, m):
end.insert(dict(word=pre)) end.insert(dict(word=pre))
async def getNoun(self, words, c): async def getNoun(self, words, c):
if c in self.cstate: if c in shared.cstate:
oldnoun = self.cstate[c] oldnoun = shared.cstate[c]
else: else:
oldnoun = None oldnoun = None
self.db['remsg'].insert_ignore(dict(noun=oldnoun,msg=' '.join(words)),['id']) shared.db['remsg'].insert_ignore(dict(noun=oldnoun,msg=' '.join(words)),['id'])
nouns = [i['word'] for i in self.db['noun'].find()] nouns = [i['word'] for i in shared.db['noun'].find()]
out = {} out = {}
for i in words: for i in words:
out[i] = nouns.count(i) out[i] = nouns.count(i)
noun = min(out, key=out.get) noun = min(out, key=out.get)
conversation = self.db['conver'] conversation = shared.db['conver']
if oldnoun != None: if oldnoun != None:
print("adding", [oldnoun,noun]) print("adding", [oldnoun,noun])
conversation.insert_ignore(dict(pre=oldnoun,pro=noun),['id']) conversation.insert_ignore(dict(pre=oldnoun,pro=noun),['id'])
@ -44,27 +45,27 @@ async def getNoun(self, words, c):
print("nextnoun:",nextnoun) print("nextnoun:",nextnoun)
if len(nextnoun) > 0: if len(nextnoun) > 0:
noun = random.choice(nextnoun) noun = random.choice(nextnoun)
self.cstate[c] = noun shared.cstate[c] = noun
return noun return noun
async def genOut(self, noun): async def genOut(self, noun):
oldresponses = [i['msg'] for i in self.db['remsg'].find(noun=noun)] oldresponses = [i['msg'] for i in shared.db['remsg'].find(noun=noun)]
if len(oldresponses) > 0: if len(oldresponses) > 0:
return random.choice(oldresponses).split(' ') return random.choice(oldresponses).split(' ')
prew = self.db['prew'] prew = shared.db['prew']
beg = [ i['word'] for i in self.db['beg'].find() ] beg = [ i['word'] for i in shared.db['beg'].find() ]
end = [ i['word'] for i in self.db['end'].find() ] end = [ i['word'] for i in shared.db['end'].find() ]
nouns = [i['word'] for i in self.db['noun'].find()] nouns = [i['word'] for i in shared.db['noun'].find()]
iter=0 iter=0
out = [noun] out = [noun]
while (out[0] not in beg or nouns.count(out[0])-1 > iter * self.enmul) and iter < 7: while (out[0] not in beg or nouns.count(out[0])-1 > iter * shared.enmul) and iter < 7:
try: try:
out = [ random.choice(list(prew.find(pro=out[0])))['pre'] ] + out out = [ random.choice(list(prew.find(pro=out[0])))['pre'] ] + out
except IndexError: except IndexError:
iter += 69 iter += 69
iter += 1 iter += 1
iter = 0 iter = 0
while (out[-1] not in end or nouns.count(out[-1])-1 > iter * self.enmul) and iter < 7: while (out[-1] not in end or nouns.count(out[-1])-1 > iter * shared.enmul) and iter < 7:
try: try:
out.append(random.choice(list(prew.find(pre=out[-1])))['pro']) out.append(random.choice(list(prew.find(pre=out[-1])))['pro'])
@ -75,19 +76,19 @@ async def genOut(self, noun):
async def filter(self, c, n, m): async def filter(self, c, n, m):
if self.t > time.time() or c in self.qtime and self.qtime[c] > time.time(): if c in shared.qtime and shared.qtime[c] > time.time():
return return
if m[:len(self.prefix)] == self.prefix: if m[:len(shared.prefix)] == shared.prefix:
m = m[len(self.prefix):] m = m[len(shared.prefix):]
await go(self, c, n, m) await go(self, c, n, m)
elif m[:4] == 'kim ': elif m[:4] == 'kim ':
m = m[4:] m = m[4:]
await go(self, c, n, m) await go(self, c, n, m)
else: else:
if len(m.split(' ')) > 1: if len(m.split(' ')) > 1:
if self.learntime + self.learndelay < time.time(): if shared.learntime + shared.learndelay < time.time():
await rec(self, m) await rec(self, m)
self.learntime = time.time() shared.learntime = time.time()
async def go(self, c, n, m): async def go(self, c, n, m):
await rec(self, m) await rec(self, m)
@ -98,11 +99,11 @@ async def go(self, c, n, m):
async def init(self): async def init(self):
self.qtime = {} shared.qtime = {}
self.learntime = 0 shared.learntime = 0
self.learndelay = 4 shared.learndelay = 4
self.enmul = 40 shared.enmul = 40
self.rawm['nlp'] = filter shared.rawm['nlp'] = filter
self.cstate = {} shared.cstate = {}

14
modules/test.py Normal file
View File

@ -0,0 +1,14 @@
import asyncio
import bot
@bot.command('test')
@bot.is_admin
async def testy(self,channel,nick,msg):
await bot.message(self,'test',channel,'hi there')
async def init(self):
await self.send_raw("join #bots")

12
shared.py Normal file
View File

@ -0,0 +1,12 @@
import dataset
prefix = 'kim: '
modules = {}
listeners = []
commands = {}
rawm = {}
db = dataset.connect('sqlite:///database.db')
qtime = {}