2017-01-01 14:59:01 -08:00
|
|
|
|
2017-11-16 19:00:07 -08:00
|
|
|
from pyircbot.modulebase import ModuleBase, ModuleHook, MissingDependancyException, regex, command
|
2017-11-22 22:09:25 -08:00
|
|
|
from pyircbot.modules.ModInfo import info
|
2014-10-11 20:47:31 -07:00
|
|
|
import datetime
|
|
|
|
import time
|
|
|
|
import math
|
|
|
|
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2014-10-11 20:47:31 -07:00
|
|
|
class Calc(ModuleBase):
|
2015-11-01 18:03:11 -08:00
|
|
|
def __init__(self, bot, moduleName):
|
2017-01-01 14:59:01 -08:00
|
|
|
ModuleBase.__init__(self, bot, moduleName)
|
|
|
|
|
|
|
|
self.timers = {}
|
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
self.sqlite = self.bot.getBestModuleForService("sqlite")
|
2017-01-01 14:59:01 -08:00
|
|
|
if self.sqlite is None:
|
2017-11-16 19:00:07 -08:00
|
|
|
raise MissingDependancyException("Calc: SQLIite service is required.")
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
self.sql = self.sqlite.opendb("calc.db")
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
if not self.sql.tableExists("calc_addedby"):
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
c.execute("""
|
|
|
|
CREATE TABLE `calc_addedby` (
|
|
|
|
`id` INTEGER PRIMARY KEY,
|
|
|
|
`username` varchar(32),
|
|
|
|
`userhost` varchar(128)
|
|
|
|
) ;
|
|
|
|
""")
|
|
|
|
c.close()
|
|
|
|
if not self.sql.tableExists("calc_channels"):
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
c.execute("""
|
|
|
|
CREATE TABLE `calc_channels` (
|
|
|
|
`id` INTEGER PRIMARY KEY,
|
|
|
|
`channel` varchar(32)
|
|
|
|
) ;
|
|
|
|
""")
|
|
|
|
if not self.sql.tableExists("calc_definitions"):
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
c.execute("""
|
|
|
|
CREATE TABLE `calc_definitions` (
|
|
|
|
`id` INTEGER PRIMARY KEY,
|
|
|
|
`word` INTEGET,
|
|
|
|
`definition` varchar(512),
|
|
|
|
`addedby` INTEGER,
|
|
|
|
`date` timestamp,
|
|
|
|
`status` varchar(16)
|
|
|
|
) ;
|
|
|
|
""")
|
|
|
|
if not self.sql.tableExists("calc_words"):
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
c.execute("""
|
|
|
|
CREATE TABLE `calc_words` (
|
|
|
|
`id` INTEGER PRIMARY KEY,
|
|
|
|
`channel` INTEGER,
|
|
|
|
`word` varchar(512),
|
|
|
|
`status` varchar(32),
|
|
|
|
unique(`channel`,`word`)
|
|
|
|
);
|
|
|
|
""")
|
|
|
|
c.close()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
def timeSince(self, channel, timetype):
|
2017-01-01 14:59:01 -08:00
|
|
|
if channel not in self.timers:
|
2015-11-01 18:03:11 -08:00
|
|
|
self.createDefaultTimers(channel)
|
2017-01-01 14:59:01 -08:00
|
|
|
return time.time() - self.timers[channel][timetype]
|
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
def updateTimeSince(self, channel, timetype):
|
2017-01-01 14:59:01 -08:00
|
|
|
if channel not in self.timers:
|
2015-11-01 18:03:11 -08:00
|
|
|
self.createDefaultTimers(channel)
|
|
|
|
self.timers[channel][timetype] = time.time()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
def createDefaultTimers(self, channel):
|
2017-01-01 14:59:01 -08:00
|
|
|
self.timers[channel] = {"add": 0, "calc": 0, "calcspec": 0, "match": 0}
|
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
def remainingToStr(self, total, elasped):
|
2017-01-01 14:59:01 -08:00
|
|
|
remaining = total - elasped
|
|
|
|
minutes = int(math.floor(remaining / 60))
|
|
|
|
seconds = int(remaining - (minutes * 60))
|
2015-11-01 18:03:11 -08:00
|
|
|
return "Please wait %s minute(s) and %s second(s)." % (minutes, seconds)
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2017-11-22 22:09:25 -08:00
|
|
|
@info("quote [key[ =[ value]]] set or update facts", cmds=["quote"])
|
2017-11-16 19:00:07 -08:00
|
|
|
@regex(r'(?:^\.?(?:calc|quote)(?:\s+?(?:([^=]+)(?:\s?(=)\s?(.+)?)?)?)?)', types=['PRIVMSG'])
|
2017-11-22 20:20:52 -08:00
|
|
|
def cmd_calc(self, message, match):
|
2017-11-16 19:00:07 -08:00
|
|
|
word, changeit, value = match.groups()
|
|
|
|
if word:
|
|
|
|
word = word.strip()
|
|
|
|
if value:
|
|
|
|
value = value.strip()
|
|
|
|
channel = message.args[0]
|
|
|
|
sender = message.prefix.nick
|
|
|
|
|
|
|
|
if word and changeit:
|
|
|
|
# Add a new calc or delete
|
|
|
|
if self.config["allowDelete"] and not value:
|
|
|
|
result = self.deleteCalc(channel, word)
|
|
|
|
if result:
|
|
|
|
self.bot.act_PRIVMSG(channel, "Calc deleted, %s." % sender)
|
2015-11-01 18:03:11 -08:00
|
|
|
else:
|
2017-11-16 19:00:07 -08:00
|
|
|
self.bot.act_PRIVMSG(channel, "Sorry %s, I don't know what '%s' is." % (sender, word))
|
|
|
|
else:
|
|
|
|
if self.config["delaySubmit"] > 0 and self.timeSince(channel, "add") < self.config["delaySubmit"]:
|
|
|
|
self.bot.act_PRIVMSG(channel, self.remainingToStr(self.config["delaySubmit"],
|
|
|
|
self.timeSince(channel, "add")))
|
2015-11-01 18:03:11 -08:00
|
|
|
else:
|
2017-11-16 19:00:07 -08:00
|
|
|
self.addNewCalc(channel, word, value, sender, message.prefix.hostname)
|
|
|
|
self.bot.act_PRIVMSG(channel, "Thanks for the info, %s." % sender)
|
|
|
|
self.updateTimeSince(channel, "add")
|
|
|
|
elif word:
|
|
|
|
# Lookup the word in calc
|
|
|
|
|
|
|
|
if self.config["delayCalcSpecific"] > 0 and \
|
|
|
|
self.timeSince(channel, "calcspec") < self.config["delayCalcSpecific"]:
|
|
|
|
self.bot.act_PRIVMSG(sender, self.remainingToStr(self.config["delayCalcSpecific"],
|
|
|
|
self.timeSince(channel, "calcspec")))
|
2015-11-01 18:03:11 -08:00
|
|
|
else:
|
2017-11-16 19:00:07 -08:00
|
|
|
randCalc = self.getSpecificCalc(channel, word)
|
|
|
|
if randCalc is None:
|
|
|
|
self.bot.act_PRIVMSG(channel, "Sorry %s, I don't know what '%s' is." % (sender, word))
|
2015-11-01 18:03:11 -08:00
|
|
|
else:
|
2017-11-16 19:00:07 -08:00
|
|
|
self.bot.act_PRIVMSG(channel, "%s \x03= %s \x0314[added by: %s]" %
|
|
|
|
(randCalc["word"], randCalc["definition"], randCalc["by"]))
|
|
|
|
self.updateTimeSince(channel, "calcspec")
|
|
|
|
else:
|
|
|
|
if self.config["delayCalc"] > 0 and self.timeSince(channel, "calc") < self.config["delayCalc"]:
|
|
|
|
self.bot.act_PRIVMSG(sender, self.remainingToStr(self.config["delayCalc"],
|
|
|
|
self.timeSince(channel, "calc")))
|
2015-11-01 18:03:11 -08:00
|
|
|
else:
|
2017-11-16 19:00:07 -08:00
|
|
|
randCalc = self.getRandomCalc(channel)
|
|
|
|
if randCalc is None:
|
|
|
|
self.bot.act_PRIVMSG(channel, "This channel has no calcs, %s :(" % (sender,))
|
2015-11-01 18:03:11 -08:00
|
|
|
else:
|
2017-11-16 19:00:07 -08:00
|
|
|
self.bot.act_PRIVMSG(channel, "%s \x03= %s \x0314[added by: %s]" % (randCalc["word"],
|
|
|
|
randCalc["definition"], randCalc["by"]))
|
|
|
|
self.updateTimeSince(channel, "calc")
|
|
|
|
|
|
|
|
@command("match", require_args=True)
|
2017-11-22 20:20:52 -08:00
|
|
|
def cmd_match(self, msg, cmd):
|
2017-11-16 19:00:07 -08:00
|
|
|
if self.config["delayMatch"] > 0 and self.timeSince(msg.args[0], "match") < self.config["delayMatch"]:
|
|
|
|
self.bot.act_PRIVMSG(msg.prefix.nick, self.remainingToStr(self.config["delayMatch"],
|
|
|
|
self.timeSince(msg.args[0], "match")))
|
|
|
|
else:
|
|
|
|
term = cmd.args_str
|
|
|
|
if not term.strip():
|
|
|
|
return
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
channelId = self.getChannelId(msg.args[0])
|
|
|
|
c.execute("SELECT * FROM `calc_words` WHERE `word` LIKE ? AND `channel`=? ORDER BY `word` ASC ;",
|
|
|
|
("%%" + term + "%%", channelId))
|
|
|
|
rows = c.fetchall()
|
|
|
|
if not rows:
|
|
|
|
self.bot.act_PRIVMSG(msg.args[0], "%s: Sorry, no matches" % msg.prefix.nick)
|
|
|
|
else:
|
|
|
|
matches = []
|
|
|
|
for row in rows[0:10]:
|
|
|
|
if not row:
|
|
|
|
break
|
|
|
|
matches.append(row["word"])
|
|
|
|
self.bot.act_PRIVMSG(msg.args[0], "%s: %s match%s (%s\x03)" %
|
|
|
|
(msg.prefix.nick, len(matches), "es" if len(matches) > 1 else
|
2017-01-01 14:59:01 -08:00
|
|
|
"", ", \x03".join(matches)))
|
2017-11-16 19:00:07 -08:00
|
|
|
self.updateTimeSince(msg.args[0], "match")
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2017-11-16 19:00:07 -08:00
|
|
|
def addNewCalc(self, channel, word, definition, name, host):
|
2015-11-01 18:03:11 -08:00
|
|
|
" Find the channel ID"
|
|
|
|
channelId = self.getChannelId(channel)
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
" Check if we need to add a user"
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
c.execute("SELECT * FROM `calc_addedby` WHERE `username`=? AND `userhost`=? ;", (name, host))
|
|
|
|
rows = c.fetchall()
|
2017-01-01 14:59:01 -08:00
|
|
|
if not rows:
|
2015-11-01 18:03:11 -08:00
|
|
|
c.execute("INSERT INTO `calc_addedby` (`username`, `userhost`) VALUES (?, ?) ;", (name, host,))
|
|
|
|
c.execute("SELECT * FROM `calc_addedby` WHERE `username`=? AND `userhost`=? ;", (name, host))
|
|
|
|
rows = c.fetchall()
|
|
|
|
addedId = rows[0]["id"]
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
" Check if the word exists"
|
|
|
|
c.execute("SELECT * FROM `calc_words` WHERE `channel`=? AND `word`=? ;", (channelId, word))
|
|
|
|
rows = c.fetchall()
|
2017-01-01 14:59:01 -08:00
|
|
|
if not rows:
|
|
|
|
c.execute("INSERT INTO `calc_words` (`channel`, `word`, `status`) VALUES (?, ?, ?) ;",
|
|
|
|
(channelId, word, 'approved'))
|
2015-11-01 18:03:11 -08:00
|
|
|
c.execute("SELECT * FROM `calc_words` WHERE `channel`=? AND `word`=? ;", (channelId, word))
|
|
|
|
rows = c.fetchall()
|
|
|
|
wordId = rows[0]["id"]
|
|
|
|
" Add definition "
|
2017-01-01 14:59:01 -08:00
|
|
|
c.execute("INSERT INTO `calc_definitions` (`word`, `definition`, `addedby`, `date`, `status`) VALUES "
|
|
|
|
"(?, ?, ?, ?, ?) ;", (wordId, definition, addedId, datetime.datetime.now(), 'approved',))
|
2015-11-01 18:03:11 -08:00
|
|
|
c.close()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
def getSpecificCalc(self, channel, word):
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
channelId = self.getChannelId(channel)
|
2017-01-01 14:59:01 -08:00
|
|
|
c.execute("SELECT `cw`.`word`, (SELECT `cdq`.`id` FROM `calc_definitions` `cdq` WHERE `cdq`.`word`=`cw`.`id` "
|
|
|
|
"AND `cdq`.`status`='approved' ORDER BY `cdq`.`date` DESC LIMIT 1) as `definitionId` FROM "
|
|
|
|
"`calc_words` `cw` WHERE `cw`.`channel`=? AND `cw`.`status`='approved' AND `cw`.`word`=? "
|
|
|
|
"COLLATE NOCASE ORDER BY RANDOM() LIMIT 1 ;", (channelId, word.lower()))
|
2015-11-01 18:03:11 -08:00
|
|
|
word = c.fetchone()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
|
|
|
if word is None:
|
2015-11-01 18:03:11 -08:00
|
|
|
return None
|
2017-01-01 14:59:01 -08:00
|
|
|
|
|
|
|
c.execute("SELECT `ca`.`username`, `cd`.`definition` FROM `calc_definitions` `cd` JOIN `calc_addedby` `ca` ON "
|
|
|
|
"`ca`.`id` = `cd`.`addedby` WHERE `cd`.`id`=? LIMIT 1 ;", (word["definitionId"], ))
|
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
who = c.fetchone()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
|
|
|
if who is None:
|
2015-11-01 18:03:11 -08:00
|
|
|
return None
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
c.close()
|
2017-01-01 14:59:01 -08:00
|
|
|
return {"word": word["word"], "definition": who["definition"], "by": who["username"]}
|
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
def getRandomCalc(self, channel):
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
channelId = self.getChannelId(channel)
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
for i in range(0, 5):
|
2017-01-01 14:59:01 -08:00
|
|
|
c.execute("SELECT `cw`.`word`, (SELECT `cdq`.`id` FROM `calc_definitions` `cdq` WHERE "
|
|
|
|
"`cdq`.`word`=`cw`.`id` AND `cdq`.`status`='approved' ORDER BY `cdq`.`date` DESC LIMIT 1) as "
|
|
|
|
"`definitionId` FROM `calc_words` `cw` WHERE `cw`.`channel`=? AND `cw`.`status`='approved' "
|
|
|
|
"ORDER BY RANDOM() LIMIT 1 ;", (channelId,))
|
2015-11-01 18:03:11 -08:00
|
|
|
word = c.fetchone()
|
2017-01-01 14:59:01 -08:00
|
|
|
if word is None:
|
2015-11-01 18:03:11 -08:00
|
|
|
return None
|
2017-01-01 14:59:01 -08:00
|
|
|
c.execute("SELECT `ca`.`username`, `cd`.`definition` FROM `calc_definitions` `cd` JOIN `calc_addedby` `ca` "
|
|
|
|
"ON `ca`.`id` = `cd`.`addedby` WHERE `cd`.`id`=? LIMIT 1 ;", (word["definitionId"], ))
|
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
who = c.fetchone()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
|
|
|
if who is None:
|
2015-11-01 18:03:11 -08:00
|
|
|
continue
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
c.close()
|
2017-01-01 14:59:01 -08:00
|
|
|
return {"word": word["word"], "definition": who["definition"], "by": who["username"]}
|
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
def deleteCalc(self, channel, word):
|
|
|
|
" Return true if deleted something, false if it doesnt exist"
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
channelId = self.getChannelId(channel)
|
|
|
|
c.execute("SELECT * FROM `calc_words` WHERE `channel`=? and `word`=? ;", (channelId, word))
|
|
|
|
rows = c.fetchall()
|
2017-01-01 14:59:01 -08:00
|
|
|
if not rows:
|
2015-11-01 18:03:11 -08:00
|
|
|
c.close()
|
|
|
|
return False
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
wordId = rows[0]["id"]
|
2017-01-01 14:59:01 -08:00
|
|
|
|
|
|
|
# c.execute("DELETE FROM `calc_words` WHERE `id`=? ;", (wordId,))
|
|
|
|
# c.execute("DELETE FROM `calc_definitions` WHERE `word`=? ;", (wordId,))
|
2015-11-01 18:03:11 -08:00
|
|
|
c.execute("UPDATE `calc_definitions` SET `status`='deleted' WHERE `word`=? ;", (wordId,))
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
c.close()
|
|
|
|
return True
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 18:03:11 -08:00
|
|
|
def getChannelId(self, channel):
|
|
|
|
c = self.sql.getCursor()
|
|
|
|
c.execute("SELECT * FROM `calc_channels` WHERE `channel` = ?", (channel,))
|
|
|
|
rows = c.fetchall()
|
2017-01-01 14:59:01 -08:00
|
|
|
if not rows:
|
2015-11-01 18:03:11 -08:00
|
|
|
c.execute("INSERT INTO `calc_channels` (`channel`) VALUES (?);", (channel,))
|
|
|
|
c.execute("SELECT * FROM `calc_channels` WHERE `channel` = ?", (channel,))
|
|
|
|
rows = c.fetchall()
|
|
|
|
chId = rows[0]["id"]
|
|
|
|
c.close()
|
|
|
|
return chId
|