port to new balun ircrobots framework

This commit is contained in:
xfnw 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
*journal
auth.py

186
bot.py
View file

@ -1,102 +1,134 @@
#!/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):
async def on_connect(self):
print('Connected!')
from auth import username, password
import shared
self.modules = {}
self.cmd = {}
self.rawm = {}
self.help = {}
self.db = dataset.connect('sqlite:///database.db')
self.t=0
def is_admin(func):
async def decorator(self,channel,nick,msg):
if nick.lower() in self.users and self.users[nick.lower()].account in self.admins:
await func(self,channel,nick,msg)
else:
await message(self,'core',channel,'you do not have permission to do that')
return decorator
print('loading modules...')
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')
#def is_chanop(func):
async def loadMods(self):
for i in [s for s in os.listdir('modules') if ".py" in s and '.swp' not in s]:
i = i[:-3]
print('loading', i)
m = __import__("modules."+i)
m = eval('m.'+i)
await m.init(self)
self.modules[i] = m
def command(commandname):
def decorator(func):
shared.commands[commandname] = func
return func
return decorator
async def on_invite(self, channel, by):
await self.modules['invite'].invite(self, channel, by)
# print('{} invited me to {}!'.format(by, channel))
# self.t = time.time()+1
# await self.join(channel)
def listener(listenername):
def decorator(func):
shared.listeners.append((listenername, func))
return func
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:
chan = source
if source != self.nickname:
async def message(self,modname,channel,msg):
await self.send(build("PRIVMSG",[channel,f'[\x036{modname}\x0f] {msg}']))
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)
class Server(BaseServer):
async def line_read(self, line: Line):
print(f"{self.name} < {line.format()}")
if 'on_'+line.command.lower() in dir(self):
asyncio.create_task(self.__getattribute__('on_'+line.command.lower())(line))
for listener in shared.listeners:
if listener[0] == line.command:
asyncio.create_task(listener[1](self,line))
async def parseCommand(self, chan, source, msg):
if msg[:len(self.prefix)] == self.prefix:
msg = msg[len(self.prefix):]
cmd = msg.split(' ')[0]
msg = msg[len(cmd)+1:]
if len(cmd) < 1:
return
if cmd in self.cmd:
await self.cmd[cmd](self, chan, source, msg)
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 line_send(self, line: Line):
print(f"{self.name} > {line.format()}")
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):
# if source != self.nickname:
# for i in self.rawm:
# await self.rawm[i](self, source, source, msg)
# depricated, to support old modules
async def message(self,channel,msg):
await self.send(build("PRIVMSG",[channel,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__":
client = Kim('kim', realname='owens bot')
client.admins = ['lickthecheese', 'ben', 'coffeeowl', 'gbmor', 'tomasino', 'ubergeek', 'deepend', 'calamitous', 'khuxkm']
client.prefix = 'kim: '
client.run('team.tilde.chat', tls=True, tls_verify=False)
asyncio.run(main())

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):
await self.quit('{} told me to commit {}'.format(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):
await self.message(chan, '[\x036admin\x0f] reloading modules...')
self.oldcmd = self.cmd
self.cmd = {}
self.rawm = {}
self.help = {}
shared.oldcmd = shared.commands
shared.commands = {}
shared.rawm = {}
shared.listeners = []
shared.help = {}
try:
for i in self.modules:
importlib.reload(self.modules[i])
await self.modules[i].init(self)
for i in shared.modules:
importlib.reload(shared.modules[i])
await shared.modules[i].init(self)
#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:
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):
await self.message(chan, '[\x036admin\x0f] bye {}'.format(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 rawcmd(self, chan, source, msg):
await self.send_raw(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():
self.t = time.time() + 5
try:
await self.join(i['name'])
await asyncio.sleep(3)
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!')
await self.send(build("JOIN",[i['name']]))
await asyncio.sleep(1)
print('joined {}'.format(i['name']))
await self.message(chan, '[\x036admin\x0f] Sucess! i may be laggy for a bit while i sort through all these channels...')
async def aexec(self, code):
# 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')
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')
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))
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():
def __init__(self, ms):
self.ms = str(ms)
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,
'reload': reloadmods,
'commit': commit,
'part': part,
'join': join,
'raw': rawcmd,
'eval': ev,
'send': send,
'joins': joins,
'shut': shut,
'schans': schans,
'addalias': addalias
'addalias': addalias,
'addtrigger': addtrigger,
'addot': addot,
}
@command('admin')
@is_admin
async def adminHandle(self, chan, source, msg):
if await self.is_admin(source):
msg = msg.split(' ')
if len(msg) < 1 or not msg[0] in commands:
await self.message(chan, '[\x036admin\x0f] Invalid command')
return
print('[ADMIN MODULE] {} told me to {}!!!'.format(source,msg[0]))
await commands[msg.pop(0)](self, chan, source, ' '.join(msg))
else:
await self.message(chan, '[\x036admin\x0f] You do not have permission to do this')
asyncio.create_task(commands[msg.pop(0)](self, chan, source, ' '.join(msg)))
async def init(self):
self.chandb = self.db['chan']
self.chandb = shared.db['chan']
self.admins = ['lickthecheese']
return
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']

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):
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')
from bot import *
@listener('INVITE')
async def on_invite(self,line):
self.send(build("JOIN",[line.params[1]]))
async def init(self):
pass

View file

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