Prettify help text layouts
This commit is contained in:
parent
4bdce886ab
commit
a77bde4288
@ -11,15 +11,17 @@ import and use a decorator from this module. For example:
|
||||
|
||||
# ...
|
||||
|
||||
@info("help [command] show the manual for all or [commands]", cmds=["help"])
|
||||
@info("help [command]", "show the manual for all or [commands]", cmds=["help"])
|
||||
@command("help")
|
||||
def cmd_help(self, msg, cmd):
|
||||
# ...
|
||||
|
||||
|
||||
The `info` decorator takes a mandatory string parameter describing the command. The second, optional, list parameter
|
||||
`cmds` is a list of short names thart are aliases for the function that aide in help lookup. In all cases, the cases,
|
||||
commands will be prefixed with the default command prefix (`from pyircbot.modulebase.command.prefix`).
|
||||
The `info` decorator takes mandatory string parameters describing the command. The first is an argspec; simply the name
|
||||
of the command with parameters marked using `<` and `>`. Optional parameters should be encased in `[`square brackets`]`.
|
||||
The second parameter is a short text description of the command. The third, optional, list parameter `cmds` is a list of
|
||||
short names thart are aliases for the function that aide in help lookup. In all cases, the cases, commands will be
|
||||
prefixed with the default command prefix (`from pyircbot.modulebase.command.prefix`).
|
||||
|
||||
|
||||
Class Reference
|
||||
|
@ -32,7 +32,7 @@ class ASCII(ModuleBase):
|
||||
self.running_asciis = defaultdict(lambda: None)
|
||||
self.killed_channels = defaultdict(lambda: False)
|
||||
|
||||
@info("listascii list available asciis", cmds=["listascii"])
|
||||
@info("listascii", "list available asciis", cmds=["listascii"])
|
||||
@command("listascii")
|
||||
def cmd_listascii(self, msg, cmd):
|
||||
"""
|
||||
@ -48,7 +48,7 @@ class ASCII(ModuleBase):
|
||||
self.bot.act_PRIVMSG(msg.args[0], "...and {} more".format(len(fnames) - self.config.get("list_max")))
|
||||
return
|
||||
|
||||
@info("ascii <name> print an ascii", cmds=["ascii"])
|
||||
@info("ascii <name>", "print an ascii", cmds=["ascii"])
|
||||
@command("ascii", require_args=True)
|
||||
def cmd_ascii(self, msg, cmd):
|
||||
if self.channel_busy(msg.args[0]):
|
||||
@ -68,7 +68,7 @@ class ASCII(ModuleBase):
|
||||
except FileNotFoundError:
|
||||
return
|
||||
|
||||
@info("stopascii stop the currently scrolling ascii", cmds=["stopascii"])
|
||||
@info("stopascii", "stop the currently scrolling ascii", cmds=["stopascii"])
|
||||
@command("stopascii")
|
||||
def cmd_stopascii(self, msg, cmd):
|
||||
"""
|
||||
@ -119,7 +119,7 @@ class ASCII(ModuleBase):
|
||||
del self.running_asciis[channel]
|
||||
del self.killed_channels[channel]
|
||||
|
||||
@info("asciiedit <args> customize an ascii with input", cmds=["asciiedit"])
|
||||
@info("asciiedit <args>", "customize an ascii with input", cmds=["asciiedit"])
|
||||
@command("asciiedit", require_args=True)
|
||||
def cmd_asciiedit(self, msg, cmd):
|
||||
ascii_name = cmd.args.pop(0)
|
||||
|
@ -20,7 +20,7 @@ class BitcoinPrice(ModuleBase):
|
||||
self.cache = None
|
||||
self.cacheAge = 0
|
||||
|
||||
@info("btc retrieve the current price of bitcoin", cmds=["btc"])
|
||||
@info("btc", "retrieve the current price of bitcoin", cmds=["btc"])
|
||||
@command("btc", "bitcoin")
|
||||
def btc(self, msg, cmd):
|
||||
replyTo = msg.prefix.nick if "#" not in msg.args[0] else msg.args[0]
|
||||
|
@ -80,7 +80,7 @@ class Calc(ModuleBase):
|
||||
seconds = int(remaining - (minutes * 60))
|
||||
return "Please wait %s minute(s) and %s second(s)." % (minutes, seconds)
|
||||
|
||||
@info("quote [key[ =[ value]]] set or update facts", cmds=["quote"])
|
||||
@info("quote [key[ =[ value]]]", "set or update facts", cmds=["quote"])
|
||||
@regex(r'(?:^\.?(?:calc|quote)(?:\s+?(?:([^=]+)(?:\s?(=)\s?(.+)?)?)?)?)', types=['PRIVMSG'])
|
||||
def cmd_calc(self, message, match):
|
||||
word, changeit, value = match.groups()
|
||||
@ -136,7 +136,7 @@ class Calc(ModuleBase):
|
||||
randCalc["definition"], randCalc["by"]))
|
||||
self.updateTimeSince(channel, "calc")
|
||||
|
||||
@info("match <value> search for facts by key", cmds=["match"])
|
||||
@info("match <value>", "search for facts by key", cmds=["match"])
|
||||
@command("match", require_args=True)
|
||||
def cmd_match(self, msg, cmd):
|
||||
if self.config["delayMatch"] > 0 and self.timeSince(msg.args[0], "match") < self.config["delayMatch"]:
|
||||
|
@ -21,7 +21,7 @@ class CryptoWallet(ModuleBase):
|
||||
def getMods(self):
|
||||
return (self.bot.getBestModuleForService("attributes"), self.bot.getBestModuleForService("bitcoinrpc"))
|
||||
|
||||
@info("setaddr <currency> <address> set withdraw address", cmds=["setaddr"])
|
||||
@info("setaddr <currency> <address>", "set withdraw address", cmds=["setaddr"])
|
||||
@command("setaddr", require_args=2, allow_private=True)
|
||||
def handle_setaddr(self, msg, cmd):
|
||||
if not self.check_login(msg.prefix, msg.args[0]):
|
||||
@ -46,7 +46,7 @@ class CryptoWallet(ModuleBase):
|
||||
self.bot.act_PRIVMSG(msg.args[0], ".setaddr: Your address has been saved as: {}. Please verify that this is "
|
||||
"correct or your coins could be lost.".format(cmd.args[1]))
|
||||
|
||||
@info("getbal <currency> retrieve your balance ", cmds=["getbal"])
|
||||
@info("getbal <currency>", "retrieve your balance ", cmds=["getbal"])
|
||||
@command("getbal", require_args=1, allow_private=True)
|
||||
def handle_getbal(self, msg, cmd):
|
||||
if not self.check_login(msg.prefix, msg.args[0]):
|
||||
@ -72,7 +72,7 @@ class CryptoWallet(ModuleBase):
|
||||
self.bot.act_PRIVMSG(msg.args[0],
|
||||
"{}: your balance is: {} {}".format(msg.prefix.nick, amount, cmd.args[0].upper()))
|
||||
|
||||
@info("withdraw <currency> <amount> send coins to your withdraw address", cmds=["withdraw"])
|
||||
@info("withdraw <currency> <amount>", "send coins to your withdraw address", cmds=["withdraw"])
|
||||
@command("withdraw", require_args=2, allow_private=True)
|
||||
def handle_withdraw(self, msg, cmd):
|
||||
if not self.check_login(msg.prefix, msg.args[0]):
|
||||
@ -132,7 +132,7 @@ class CryptoWallet(ModuleBase):
|
||||
self.bot.act_PRIVMSG(msg.args[0], "{}: .withdraw: Transaction create failed. Maybe the transaction was too "
|
||||
"large for the network? Try a smaller increment.".format(msg.prefix.nick))
|
||||
|
||||
@info("send <currency> <amount> <nick_or_address> send coins elsewhere", cmds=["send"])
|
||||
@info("send <currency> <amount> <nick_or_address>", "send coins elsewhere", cmds=["send"])
|
||||
@command("send", require_args=3, allow_private=True)
|
||||
def handle_send(self, msg, cmd):
|
||||
if not self.check_login(msg.prefix, msg.args[0]):
|
||||
@ -217,7 +217,7 @@ class CryptoWallet(ModuleBase):
|
||||
self.bot.act_PRIVMSG(msg.args[0], "{}: uh-oh, something went wrong doing that."
|
||||
.format(msg.prefix.nick))
|
||||
|
||||
@info("getaddr <currency> get deposit address", cmds=["getaddr"])
|
||||
@info("getaddr <currency>", "get deposit address", cmds=["getaddr"])
|
||||
@command("getaddr", require_args=1, allow_private=True)
|
||||
def handle_getaddr(self, msg, cmd):
|
||||
if not self.check_login(msg.prefix, msg.args[0]):
|
||||
@ -237,7 +237,7 @@ class CryptoWallet(ModuleBase):
|
||||
self.bot.act_PRIVMSG(msg.args[0], "{}: your {} deposit address is: {}"
|
||||
.format(msg.prefix.nick, cmd.args[0].upper(), walletaddr))
|
||||
|
||||
@info("curinfo list supported coins", cmds=["curinfo"])
|
||||
@info("curinfo", "list supported coins", cmds=["curinfo"])
|
||||
@command("curinfo", allow_private=True)
|
||||
def handle_curinfo(self, msg, cmd):
|
||||
attr, rpc = self.getMods()
|
||||
|
@ -29,7 +29,7 @@ class DuckHunt(ModuleBase):
|
||||
|
||||
self.startHunt()
|
||||
|
||||
@info("huntscore show your duckhunt score", cmds=["huntscore"])
|
||||
@info("huntscore", "show your duckhunt score", cmds=["huntscore"])
|
||||
@command("huntscore", allow_private=True)
|
||||
def hunt(self, msg, cmd):
|
||||
scores = self.loadScores()
|
||||
@ -61,7 +61,7 @@ class DuckHunt(ModuleBase):
|
||||
(prime, runts, shots, misses))
|
||||
# self.bot.act_PRIVMSG(fromWho, "More info & highscores: http://duckhunt.xmopx.net/")
|
||||
|
||||
@info("shoot shoot active targets", cmds=["shoot"])
|
||||
@info("shoot", "shoot active targets", cmds=["shoot"])
|
||||
@command("shoot")
|
||||
def cmd_shoot(self, msg, args):
|
||||
if self.isDuckOut:
|
||||
|
@ -33,7 +33,7 @@ class Inventory(ModuleBase):
|
||||
) ;""")
|
||||
c.close()
|
||||
|
||||
@info("have <item> give the bot an item", cmds=["have"])
|
||||
@info("have <item>", "give the bot an item", cmds=["have"])
|
||||
@command("have")
|
||||
def checkInv(self, msg, cmd):
|
||||
if len(cmd.args) < 1:
|
||||
@ -57,7 +57,7 @@ class Inventory(ModuleBase):
|
||||
self.bot.act_PRIVMSG(msg.args[0], self.config["recv_msg"] %
|
||||
{"item": newItem, "adjective": "these " if newItem[-1:] == "s" else "this "})
|
||||
|
||||
@info("inventory show the bot's inventory", cmds=["inventory", "inv"])
|
||||
@info("inventory", "show the bot's inventory", cmds=["inventory", "inv"])
|
||||
@command("inventory", "inv")
|
||||
def cmd_inv(self, msg, cmd):
|
||||
inv = self.getinventory()
|
||||
|
@ -15,7 +15,7 @@ BASE_URL = "http://lmgtfy.com/?q="
|
||||
|
||||
class LMGTFY(ModuleBase):
|
||||
|
||||
@info("lmgtfy <term> display a condescending internet query", cmds=["lmgtfy"])
|
||||
@info("lmgtfy <term>", "display a condescending internet query", cmds=["lmgtfy"])
|
||||
@command("lmgtfy", require_args=True)
|
||||
def handleMessage(self, msg, cmd):
|
||||
link = self.createLink(cmd.args_str)
|
||||
|
@ -29,49 +29,70 @@ class info(object):
|
||||
command has the alias "rtfm"
|
||||
:type cmds: list
|
||||
"""
|
||||
def __init__(self, docstring, cmds=None):
|
||||
def __init__(self, cmdspec, docstring, cmds=None):
|
||||
self.cmdspec = cmdspec
|
||||
self.docstring = docstring
|
||||
self.commands = cmds or []
|
||||
self.aliases = cmds or []
|
||||
|
||||
def __call__(self, func):
|
||||
if hasattr(func, "irchelp"):
|
||||
func.irchelp.append(self.docstring)
|
||||
func.irchelp.append(self)
|
||||
else:
|
||||
setattr(func, "irchelp", [self.docstring])
|
||||
|
||||
if hasattr(func, "irchelpc"):
|
||||
func.irchelpc.append(self.commands)
|
||||
else:
|
||||
setattr(func, "irchelpc", [self.commands])
|
||||
|
||||
setattr(func, "irchelp", [self])
|
||||
return func
|
||||
|
||||
|
||||
class ModInfo(ModuleBase):
|
||||
|
||||
@info("help [command] show the manual for all or [commands]", cmds=["help"])
|
||||
@info("help [command]", "show the manual for all or [commands]", cmds=["help"])
|
||||
@command("help")
|
||||
def cmd_help(self, msg, cmd):
|
||||
"""
|
||||
Get help on a command
|
||||
"""
|
||||
if cmd.args:
|
||||
for modname, module, helptext, helpcommands in self.iter_modules():
|
||||
if cmd.args[0] in ["{}{}".format(command.prefix, i) for i in helpcommands]:
|
||||
self.bot.act_PRIVMSG(msg.args[0], "RTFM: {}: {}".format(cmd.args[0], helptext))
|
||||
for modname, module, cmdspec, docstring, aliases in self.iter_modules():
|
||||
if cmd.args[0] in ["{}{}".format(command.prefix, i) for i in aliases]:
|
||||
self.bot.act_PRIVMSG(msg.args[0], "RTFM: {}: ({}{}) {}"
|
||||
.format(cmd.args[0], command.prefix, cmdspec, docstring))
|
||||
else:
|
||||
for modname, module, helptext, helpcommands in self.iter_modules():
|
||||
self.bot.act_PRIVMSG(msg.args[0], "{}: {}{}".format(modname, command.prefix, helptext))
|
||||
rows = []
|
||||
for modname, module, cmdspec, docstring, aliases in self.iter_modules():
|
||||
rows.append((modname, command.prefix + cmdspec, docstring))
|
||||
rows.sort(key=lambda item: item[0] + item[1])
|
||||
self.send_columnized(msg.args[0], rows)
|
||||
|
||||
@info("helpindex show a short list of all commands", cmds=["helpindex"])
|
||||
def send_columnized(self, channel, rows):
|
||||
if not rows:
|
||||
return
|
||||
widths = []
|
||||
# Find how many col widths we must calculate
|
||||
for _ in rows[0]:
|
||||
widths.append(0)
|
||||
# Find widest value per col
|
||||
for row in rows:
|
||||
for col, value in enumerate(row):
|
||||
print(col, value)
|
||||
vlen = len(value)
|
||||
if vlen > widths[col]:
|
||||
widths[col] = vlen
|
||||
# Print each row
|
||||
for row in rows:
|
||||
message = ""
|
||||
for colid, col in enumerate(row):
|
||||
message += str(col)
|
||||
message += (" " * (widths[colid] - len(col) + 1))
|
||||
self.bot.act_PRIVMSG(channel, message)
|
||||
|
||||
@info("helpindex", "show a short list of all commands", cmds=["helpindex"])
|
||||
@command("helpindex")
|
||||
def cmd_helpindex(self, msg, cmd):
|
||||
"""
|
||||
Short index of commands
|
||||
"""
|
||||
commands = []
|
||||
for modname, module, helptext, helpcommands in self.iter_modules():
|
||||
commands += ["{}{}".format(command.prefix, i) for i in helpcommands]
|
||||
for modname, module, cmdspec, docstring, aliases in self.iter_modules():
|
||||
commands += ["{}{}".format(command.prefix, i) for i in aliases]
|
||||
|
||||
self.bot.act_PRIVMSG(msg.args[0], "{}: commands: {}".format(msg.prefix.nick, ", ".join(commands)))
|
||||
|
||||
@ -80,12 +101,12 @@ class ModInfo(ModuleBase):
|
||||
Iterator that cycles through module methods that are tagged with help information. The iterator yields tuples
|
||||
of:
|
||||
|
||||
(module_name, module_object, helptext, command_list)
|
||||
(module_name, module_object, command_spec, command_help, command_list)
|
||||
"""
|
||||
for modname, module in self.bot.moduleInstances.items():
|
||||
for attr_name in dir(module):
|
||||
attr = getattr(module, attr_name)
|
||||
if callable(attr) and hasattr(attr, "irchelp"):
|
||||
for cmdhelp, cmdaliases in zip(getattr(attr, "irchelp"), getattr(attr, "irchelpc")):
|
||||
yield (modname, module, cmdhelp, cmdaliases, )
|
||||
for cmdinfo in attr.irchelp:
|
||||
yield (modname, module, cmdinfo.cmdspec, cmdinfo.docstring, cmdinfo.aliases)
|
||||
raise StopIteration()
|
||||
|
@ -20,7 +20,7 @@ class NFLLive(ModuleBase):
|
||||
self.cache = None
|
||||
self.cacheAge = 0
|
||||
|
||||
@info("nfl show nfl schedule & score", cmds=["nfl"])
|
||||
@info("nfl", "show nfl schedule & score", cmds=["nfl"])
|
||||
@command("nfl")
|
||||
def nflitup(self, message, cmd):
|
||||
games = self.getNflGamesCached()
|
||||
|
@ -36,9 +36,9 @@ class NickUser(ModuleBase):
|
||||
else:
|
||||
self.handlePm(msg.prefix, msg.trailing)
|
||||
|
||||
@info("setpass [<oldpass>] <password> set or change password", cmds=["setpass"])
|
||||
@info("login <password> authenticate with the bot", cmds=["login"])
|
||||
@info("logout log out of the bot", cmds=["logout"])
|
||||
@info("setpass [<oldpass>] <password>", "set or change password", cmds=["setpass"])
|
||||
@info("login <password>", "authenticate with the bot", cmds=["login"])
|
||||
@info("logout", "log out of the bot", cmds=["logout"])
|
||||
def handlePm(self, prefix, trailing):
|
||||
cmd = messageHasCommand(".setpass", trailing)
|
||||
if cmd:
|
||||
|
@ -33,7 +33,7 @@ class RandQuote(ModuleBase):
|
||||
) ;""")
|
||||
c.close()
|
||||
|
||||
@info("randquote print a random quote", cmds=["randquote", "randomquote", "rq"])
|
||||
@info("randquote", "print a random quote", cmds=["randquote", "randomquote", "rq"])
|
||||
@command("randquote", "randomquote", "rq")
|
||||
def fetchquotes(self, msg, cmd):
|
||||
c = self.db.query("SELECT * FROM `chat` ORDER BY RANDOM() LIMIT 1;")
|
||||
|
@ -106,7 +106,7 @@ class Remind(ModuleBase):
|
||||
def ondisable(self):
|
||||
self.disabled = True
|
||||
|
||||
@info("remind <time> have the bot remind you", cmds=["remind", "at"])
|
||||
@info("remind <time>", "have the bot remind you", cmds=["remind", "at"])
|
||||
@command("remind", "at", allow_private=True)
|
||||
def remindat(self, msg, cmd):
|
||||
regex = re.compile(r'(\d+):(\d+)(?::(\d+))?([^\s\d]+)? (.*)')
|
||||
@ -198,7 +198,7 @@ class Remind(ModuleBase):
|
||||
else:
|
||||
return zonestr
|
||||
|
||||
@info("after <duration> <reminder> have the bot remind after", cmds=["after", "in"])
|
||||
@info("after <duration> <reminder>", "have the bot remind after", cmds=["after", "in"])
|
||||
@command("after", "in", allow_private=True)
|
||||
def remindin(self, msg, cmd):
|
||||
replyTo = msg.args[0]
|
||||
|
@ -134,7 +134,7 @@ class SMS(ModuleBase):
|
||||
"""
|
||||
cherrypy.engine.exit()
|
||||
|
||||
@info("text-<name> text somebody on the VIP list", cmds=["text"])
|
||||
@info("text-<name>", "text somebody on the VIP list", cmds=["text"])
|
||||
@regex(r'(?:^\.text\-([a-zA-Z0-9]+)(?:\s+(.+))?)', types=['PRIVMSG'])
|
||||
def cmd_text(self, msg, match):
|
||||
"""
|
||||
|
@ -38,7 +38,7 @@ class Seen(ModuleBase):
|
||||
# self.log.info("Seen: %s on %s" % (nick.lower(), datest))
|
||||
sql.commit()
|
||||
|
||||
@info("seen <nick> print last time user was seen", cmds=["seen"])
|
||||
@info("seen <nick>", "print last time user was seen", cmds=["seen"])
|
||||
@command("seen", require_args=True)
|
||||
def lastSeen(self, message, command):
|
||||
sql = self.getSql()
|
||||
|
@ -61,7 +61,7 @@ class Tell(ModuleBase):
|
||||
# Delete
|
||||
self.db.query("DELETE FROM `tells` WHERE `id`=?", (tell["id"],))
|
||||
|
||||
@info("tell <person> <message> relay a message when the target is online", cmds=["tell"])
|
||||
@info("tell <person> <message>", "relay a message when the target is online", cmds=["tell"])
|
||||
@command("tell", allow_private=True)
|
||||
def tellcmds(self, msg, cmd):
|
||||
if len(cmd.args) < 2:
|
||||
|
@ -14,7 +14,7 @@ from pyircbot.modules.ModInfo import info
|
||||
|
||||
class Urban(ModuleBase):
|
||||
|
||||
@info("urban <term> lookup an urban dictionary definition", cmds=["urban", "u"])
|
||||
@info("urban <term>", "lookup an urban dictionary definition", cmds=["urban", "u"])
|
||||
@command("urban", "u")
|
||||
def urban(self, msg, cmd):
|
||||
definitions = requests.get("http://www.urbandictionary.com/iphone/search/define",
|
||||
|
@ -32,7 +32,7 @@ class Weather(ModuleBase):
|
||||
self.log.error("Weather: An 'attributes' service is required")
|
||||
return
|
||||
|
||||
@info("weather [location] display the forecast", cmds=["weather", "w"])
|
||||
@info("weather [location]", "display the forecast", cmds=["weather", "w"])
|
||||
@command("weather", "w")
|
||||
def cmd_weather(self, msg, cmd):
|
||||
hasUnit = self.attr.get(msg.prefix.nick, "weather-unit")
|
||||
@ -50,7 +50,7 @@ class Weather(ModuleBase):
|
||||
|
||||
self.bot.act_PRIVMSG(msg.args[0], "%s: %s" % (msg.prefix.nick, self.getWeather(weatherZip, hasUnit)))
|
||||
|
||||
@info("setloc <location> set your home location for weather lookups", cmds=["setloc"])
|
||||
@info("setloc <location>", "set your home location for weather lookups", cmds=["setloc"])
|
||||
@command("setloc", allow_private=True)
|
||||
def cmd_setloc(self, msg, cmd):
|
||||
# if not cmd.args:
|
||||
@ -79,7 +79,7 @@ class Weather(ModuleBase):
|
||||
if self.attr.get(msg.prefix.nick, "weather-zip") is None:
|
||||
self.bot.act_PRIVMSG(reply_to, "Tip: choose C or F with .wunit <C/F>")
|
||||
|
||||
@info("wunit <c|f> set preferred weather unit", cmds=["wunit"])
|
||||
@info("wunit <c|f>", "set preferred weather unit", cmds=["wunit"])
|
||||
@command("wunit", allow_private=True)
|
||||
def cmd_wunit(self, msg, cmd):
|
||||
if cmd.args[0].lower() not in ['c', 'f']:
|
||||
|
@ -34,7 +34,7 @@ class Youtube(ModuleBase):
|
||||
) # http://stackoverflow.com/a/16742742
|
||||
return ISO_8601_period_rx.match(stamp).groupdict()
|
||||
|
||||
@info("yt search for youtube videos", cmds=["yt", "youtube"])
|
||||
@info("yt", "search for youtube videos", cmds=["yt", "youtube"])
|
||||
@command("yt", "youtube")
|
||||
def youtube(self, msg, cmd):
|
||||
j = get("https://www.googleapis.com/youtube/v3/search",
|
||||
|
@ -10,14 +10,8 @@ from tests.lib import * # NOQA - fixtures
|
||||
@pytest.fixture
|
||||
def nickbot(fakebot):
|
||||
"""
|
||||
Provide a bot loaded with the Calc module. Clear the database.
|
||||
Provide a bot loaded with the Nickuser module. Clear the database.
|
||||
"""
|
||||
fakebot.botconfig["module_configs"]["Calc"] = {
|
||||
"allowDelete": True,
|
||||
"delaySubmit": 0,
|
||||
"delayCalc": 0,
|
||||
"delayCalcSpecific": 0,
|
||||
"delayMatch": 0}
|
||||
fakebot.loadmodule("SQLite")
|
||||
with closing(fakebot.moduleInstances["SQLite"].opendb("attributes.db")) as db:
|
||||
for table in ["attribute", "items", "values"]:
|
||||
|
Loading…
Reference in New Issue
Block a user