Compare commits
3 Commits
a1786b3d85
...
29f360067a
Author | SHA1 | Date | |
---|---|---|---|
29f360067a | |||
c2ee013975 | |||
83889b2941 |
10
bot.py
10
bot.py
|
@ -72,10 +72,16 @@ class Fulvia(irc.IRCClient):
|
||||||
|
|
||||||
self._commands = {}
|
self._commands = {}
|
||||||
"""
|
"""
|
||||||
A dictionary containing all all commands to look for and the name
|
A dictionary containingv all commands to look for and the name
|
||||||
of the function they call.
|
of the function they call.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self.cmd_priv = {}
|
||||||
|
"""
|
||||||
|
A dictionary with command names as keys and required privilege
|
||||||
|
levels as values.
|
||||||
|
"""
|
||||||
|
|
||||||
self.doc = {}
|
self.doc = {}
|
||||||
"""
|
"""
|
||||||
A dictionary of command names to their docstring and example, if
|
A dictionary of command names to their docstring and example, if
|
||||||
|
@ -132,6 +138,7 @@ class Fulvia(irc.IRCClient):
|
||||||
self._callables[func.__name__] = func
|
self._callables[func.__name__] = func
|
||||||
for command in func.commands:
|
for command in func.commands:
|
||||||
self._commands[command] = func.__name__
|
self._commands[command] = func.__name__
|
||||||
|
self.cmd_priv[command] = func.priv
|
||||||
|
|
||||||
if func.hook:
|
if func.hook:
|
||||||
self._callables[func.__name__] = func
|
self._callables[func.__name__] = func
|
||||||
|
@ -160,6 +167,7 @@ class Fulvia(irc.IRCClient):
|
||||||
self._callables.pop(func.__name__)
|
self._callables.pop(func.__name__)
|
||||||
for command in func.commands:
|
for command in func.commands:
|
||||||
self._commands.pop(command)
|
self._commands.pop(command)
|
||||||
|
self.cmd_priv.pop(command)
|
||||||
|
|
||||||
if func.hook:
|
if func.hook:
|
||||||
self._callables.pop(func.__name__)
|
self._callables.pop(func.__name__)
|
||||||
|
|
7
db.py
7
db.py
|
@ -4,6 +4,7 @@ The bot's database connection class.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import threading
|
||||||
|
|
||||||
class FulviaDB(object):
|
class FulviaDB(object):
|
||||||
"""
|
"""
|
||||||
|
@ -13,6 +14,7 @@ class FulviaDB(object):
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
path = config.core.db_filename
|
path = config.core.db_filename
|
||||||
self.filename = path
|
self.filename = path
|
||||||
|
self.db_lock = threading.Lock()
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"""Return a raw database connection object."""
|
"""Return a raw database connection object."""
|
||||||
|
@ -26,5 +28,8 @@ class FulviaDB(object):
|
||||||
called per PEP 249.
|
called per PEP 249.
|
||||||
"""
|
"""
|
||||||
with self.connect() as conn:
|
with self.connect() as conn:
|
||||||
|
self.db_lock.acquire()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
return cur.execute(*args, **kwargs)
|
res = cur.execute(*args, **kwargs)
|
||||||
|
self.db_lock.release()
|
||||||
|
return res
|
||||||
|
|
11
loader.py
11
loader.py
|
@ -85,11 +85,12 @@ def process_callable(func, config):
|
||||||
func._docs = {}
|
func._docs = {}
|
||||||
|
|
||||||
func.example = getattr(func, "example", [(None, None)])
|
func.example = getattr(func, "example", [(None, None)])
|
||||||
func.thread = getattr(func, 'thread', True)
|
func.thread = getattr(func, "thread", True)
|
||||||
func.hook = getattr(func, 'hook', False)
|
func.hook = getattr(func, "hook", False)
|
||||||
func.rate = getattr(func, 'rate', 0)
|
func.rate = getattr(func, "rate", 0)
|
||||||
func.channel_rate = getattr(func, 'channel_rate', 0)
|
func.channel_rate = getattr(func, "channel_rate", 0)
|
||||||
func.global_rate = getattr(func, 'global_rate', 0)
|
func.global_rate = getattr(func, "global_rate", 0)
|
||||||
|
func.priv = getattr(func, "priv", 0)
|
||||||
|
|
||||||
if hasattr(func, 'commands'):
|
if hasattr(func, 'commands'):
|
||||||
if hasattr(func, 'example'):
|
if hasattr(func, 'example'):
|
||||||
|
|
|
@ -86,7 +86,7 @@ def rate(user=0, channel=0, server=0):
|
||||||
basis, in a channel, or across the server (bot). A value of zero means no
|
basis, in a channel, or across the server (bot). A value of zero means no
|
||||||
limit. If a function is given a rate of 20, that function may only be used
|
limit. If a function is given a rate of 20, that function may only be used
|
||||||
once every 20 seconds in the scope corresponding to the parameter.
|
once every 20 seconds in the scope corresponding to the parameter.
|
||||||
Users on the admin list in Sopel’s configuration are exempted from rate
|
Users on the admin list in Fulvia's configuration are exempted from rate
|
||||||
limits.
|
limits.
|
||||||
|
|
||||||
Rate-limited functions that use scheduled future commands should import
|
Rate-limited functions that use scheduled future commands should import
|
||||||
|
@ -178,6 +178,8 @@ def require_admin(message=None):
|
||||||
If they are not, `message` will be said if given.
|
If they are not, `message` will be said if given.
|
||||||
"""
|
"""
|
||||||
def actual_decorator(function):
|
def actual_decorator(function):
|
||||||
|
function.priv = 5
|
||||||
|
|
||||||
@functools.wraps(function)
|
@functools.wraps(function)
|
||||||
def guarded(bot, trigger, *args, **kwargs):
|
def guarded(bot, trigger, *args, **kwargs):
|
||||||
if not trigger.admin:
|
if not trigger.admin:
|
||||||
|
@ -199,6 +201,8 @@ def require_owner(message=None):
|
||||||
If they are not, `message` will be said if given.
|
If they are not, `message` will be said if given.
|
||||||
"""
|
"""
|
||||||
def actual_decorator(function):
|
def actual_decorator(function):
|
||||||
|
function.priv = 10
|
||||||
|
|
||||||
@functools.wraps(function)
|
@functools.wraps(function)
|
||||||
def guarded(bot, trigger, *args, **kwargs):
|
def guarded(bot, trigger, *args, **kwargs):
|
||||||
if not trigger.owner:
|
if not trigger.owner:
|
||||||
|
|
|
@ -86,7 +86,7 @@ def me(bot, trigger):
|
||||||
@module.commands('selfmode')
|
@module.commands('selfmode')
|
||||||
@module.example(".mode +B")
|
@module.example(".mode +B")
|
||||||
def self_mode(bot, trigger):
|
def self_mode(bot, trigger):
|
||||||
"""Set a user mode on Sopel. Can only be done in privmsg by an admin."""
|
"""Set a user mode on Fulvia. Can only be done in privmsg by an admin."""
|
||||||
mode = trigger.group(3)
|
mode = trigger.group(3)
|
||||||
add_mode = mode.startswith("+")
|
add_mode = mode.startswith("+")
|
||||||
bot.mode(bot.nickname, add_mode, mode)
|
bot.mode(bot.nickname, add_mode, mode)
|
||||||
|
|
|
@ -73,7 +73,7 @@ def unban(bot, trigger):
|
||||||
|
|
||||||
@module.require_chanmsg
|
@module.require_chanmsg
|
||||||
@module.require_privilege(OP, 'You are not a channel operator.')
|
@module.require_privilege(OP, 'You are not a channel operator.')
|
||||||
@module.commands('kickban', 'kb')
|
@module.commands('kickban')
|
||||||
def kickban(bot, trigger):
|
def kickban(bot, trigger):
|
||||||
"""
|
"""
|
||||||
This gives admins the ability to kickban a user.
|
This gives admins the ability to kickban a user.
|
||||||
|
|
|
@ -218,9 +218,7 @@ def roll(bot, trigger):
|
||||||
trigger.group(2), pretty_str, result))
|
trigger.group(2), pretty_str, result))
|
||||||
|
|
||||||
|
|
||||||
@module.commands("choice")
|
@module.commands("choice", "choose")
|
||||||
@module.commands("ch")
|
|
||||||
@module.commands("choose")
|
|
||||||
@module.example(".choose opt1,opt2,opt3")
|
@module.example(".choose opt1,opt2,opt3")
|
||||||
def choose(bot, trigger):
|
def choose(bot, trigger):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -21,7 +21,7 @@ class Hangman():
|
||||||
self.blanks[n] = ' '
|
self.blanks[n] = ' '
|
||||||
|
|
||||||
def _PickWord(self):
|
def _PickWord(self):
|
||||||
with open("/home/iou1name/.sopel/wordlist.txt",'r') as file:
|
with open("/home/iou1name/fulvia/static/wordlist.txt",'r') as file:
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
wrd = list(lines[ random.randint(0, len(lines))-1 ].strip())
|
wrd = list(lines[ random.randint(0, len(lines))-1 ].strip())
|
||||||
return wrd
|
return wrd
|
||||||
|
|
|
@ -22,12 +22,18 @@ def help(bot, trigger):
|
||||||
ex = random.choice(examples)
|
ex = random.choice(examples)
|
||||||
|
|
||||||
bot.msg(docstring)
|
bot.msg(docstring)
|
||||||
if ex:
|
if ex[0]:
|
||||||
bot.msg("Ex. In: " + ex[0])
|
bot.msg("Ex. In: " + ex[0])
|
||||||
if ex[1]:
|
if ex[1]:
|
||||||
bot.msg("Ex. Out: " + ex[1])
|
bot.msg("Ex. Out: " + ex[1])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
cmds = sorted(bot.doc.keys())
|
if not trigger.admin and not trigger.owner:
|
||||||
|
cmds = [cmd for cmd, priv in bot.cmd_priv.items() if priv < 5]
|
||||||
|
elif trigger.admin and not trigger.owner:
|
||||||
|
cmds = [cmd for cmd, priv in bot.cmd_priv.items() if priv <= 5]
|
||||||
|
else:
|
||||||
|
cmds = bot.cmd_priv.keys()
|
||||||
|
cmds = sorted(cmds)
|
||||||
msg = "Available commands: " + ", ".join(cmds)
|
msg = "Available commands: " + ", ".join(cmds)
|
||||||
bot.msg(msg)
|
bot.msg(msg)
|
||||||
|
|
|
@ -18,7 +18,7 @@ def interactive_shell(bot, trigger):
|
||||||
if bot.memory['iconsole_running']:
|
if bot.memory['iconsole_running']:
|
||||||
return bot.say('Console already running')
|
return bot.say('Console already running')
|
||||||
|
|
||||||
banner1 = 'Sopel interactive shell (embedded IPython)'
|
banner1 = 'Fulvia interactive shell (embedded IPython)'
|
||||||
banner2 = '`bot` and `trigger` are available. To exit, type exit'
|
banner2 = '`bot` and `trigger` are available. To exit, type exit'
|
||||||
exitmsg = 'Interactive shell closed'
|
exitmsg = 'Interactive shell closed'
|
||||||
|
|
||||||
|
|
|
@ -10,5 +10,9 @@ def pingAll(bot, trigger):
|
||||||
Says the nick of everyone in the channel. Great way to get thier
|
Says the nick of everyone in the channel. Great way to get thier
|
||||||
attention, or just annoy them.
|
attention, or just annoy them.
|
||||||
"""
|
"""
|
||||||
msg = " ".join(bot.channels[trigger.channel].users)
|
names = list(bot.channels[trigger.channel].users.keys())
|
||||||
bot.say(msg)
|
if "Ishd" in names:
|
||||||
|
names.remove("Ishd")
|
||||||
|
if "Ishd2" in names:
|
||||||
|
names.remove("Ishd2")
|
||||||
|
bot.say(" ".join(names))
|
||||||
|
|
|
@ -16,7 +16,7 @@ class Scramble():
|
||||||
random.shuffle(self.shuffled)
|
random.shuffle(self.shuffled)
|
||||||
|
|
||||||
def _PickWord(self):
|
def _PickWord(self):
|
||||||
with open("/home/iou1name/.sopel/words6.txt",'r') as file:
|
with open("/home/iou1name/fulvia/static/words6.txt",'r') as file:
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
wrd = list(lines[ random.randint(0, len(lines))-1 ].strip())
|
wrd = list(lines[ random.randint(0, len(lines))-1 ].strip())
|
||||||
return wrd
|
return wrd
|
||||||
|
@ -29,7 +29,7 @@ class Scramble():
|
||||||
def isAnagram(givenWord, givenGuess):
|
def isAnagram(givenWord, givenGuess):
|
||||||
word = [x for x in givenWord]
|
word = [x for x in givenWord]
|
||||||
guess = [x for x in givenGuess]
|
guess = [x for x in givenGuess]
|
||||||
with open('/home/iou1name/.sopel/words6.txt', 'r') as file:
|
with open('/home/iou1name/fulvia/static/words6.txt', 'r') as file:
|
||||||
words = file.readlines()
|
words = file.readlines()
|
||||||
if not ''.join(guess)+'\n' in words:
|
if not ''.join(guess)+'\n' in words:
|
||||||
return "notaword"
|
return "notaword"
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
When was this user last seen.
|
When was this user last seen.
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
|
import argparse
|
||||||
import threading
|
import threading
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from sqlite3 import OperationalError
|
from sqlite3 import OperationalError
|
||||||
|
@ -20,8 +21,7 @@ def load_database(bot):
|
||||||
seens = bot.db.execute("SELECT * FROM seen").fetchall()
|
seens = bot.db.execute("SELECT * FROM seen").fetchall()
|
||||||
|
|
||||||
for seen in seens:
|
for seen in seens:
|
||||||
nick, timestamp, channel, message = seen
|
nick, seen = seen[0], seen[1:]
|
||||||
seen = (timestamp, channel, message)
|
|
||||||
data[nick] = seen
|
data[nick] = seen
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -34,9 +34,12 @@ def setup(bot):
|
||||||
except OperationalError:
|
except OperationalError:
|
||||||
cur.execute("CREATE TABLE seen("
|
cur.execute("CREATE TABLE seen("
|
||||||
"nick TEXT PRIMARY KEY,"
|
"nick TEXT PRIMARY KEY,"
|
||||||
"timestamp INTEGER,"
|
"first_timestamp INTEGER,"
|
||||||
"channel TEXT,"
|
"first_channel TEXT,"
|
||||||
"message TEXT)")
|
"first_message TEXT,"
|
||||||
|
"last_timestamp INTEGER,"
|
||||||
|
"last_channel TEXT,"
|
||||||
|
"last_message TEXT)")
|
||||||
con.commit()
|
con.commit()
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
|
@ -50,33 +53,47 @@ def setup(bot):
|
||||||
+"with \"Just going to the store for some smokes babe I'll be right back\"")
|
+"with \"Just going to the store for some smokes babe I'll be right back\"")
|
||||||
@example(".seen Soma_QM", "I haven't seen Soma_QM")
|
@example(".seen Soma_QM", "I haven't seen Soma_QM")
|
||||||
def seen(bot, trigger):
|
def seen(bot, trigger):
|
||||||
"""Reports when and where the user was last seen."""
|
"""
|
||||||
nick = trigger.group(3)
|
Reports when and where the user was last/first seen.
|
||||||
last = False
|
--last [-l] reports when the user was last seen. This is the default.
|
||||||
if nick == "-l" or nick == "--last":
|
--first [-f] reports when the user was first seen.
|
||||||
last = True
|
--message [-m] includes the first/last message the user sent.
|
||||||
nick = trigger.group(4)
|
"""
|
||||||
|
if not trigger.group(2):
|
||||||
|
return bot.reply("Seen who?")
|
||||||
|
|
||||||
if not nick:
|
parser = argparse.ArgumentParser()
|
||||||
return bot.say("Seen who?")
|
parser.add_argument("nick")
|
||||||
|
parser.add_argument("-l", "--last", action="store_true", default=True)
|
||||||
|
parser.add_argument("-f", "--first", action="store_true")
|
||||||
|
parser.add_argument("-m", "--message", action="store_true")
|
||||||
|
args = parser.parse_args(trigger.group[3:])
|
||||||
|
|
||||||
if nick == bot.nick:
|
if args.nick == bot.nick:
|
||||||
return bot.reply("I'm right here!")
|
return bot.reply("I'm right here!")
|
||||||
|
|
||||||
if nick in bot.memory["seen"]:
|
if args.nick in bot.memory["seen"]:
|
||||||
timestamp, channel, message = bot.memory["seen"][nick]
|
if args.first:
|
||||||
|
timestamp, channel, message = bot.memory["seen"][args.nick][:3]
|
||||||
|
else:
|
||||||
|
timestamp, channel, message = bot.memory["seen"][args.nick][3:]
|
||||||
else:
|
else:
|
||||||
return bot.msg(f"I haven't seen \x0308{nick}")
|
return bot.msg(f"I haven't seen \x0308{args.nick}")
|
||||||
|
|
||||||
timestamp = datetime.fromtimestamp(timestamp)
|
timestamp = datetime.fromtimestamp(timestamp)
|
||||||
t_format = bot.config.core.default_time_format
|
t_format = bot.config.core.default_time_format
|
||||||
timestamp = datetime.strftime(timestamp, t_format)
|
timestamp = datetime.strftime(timestamp, t_format)
|
||||||
reltime = relativeTime(bot.config, datetime.now(), timestamp)
|
reltime = relativeTime(bot.config, datetime.now(), timestamp)
|
||||||
|
|
||||||
msg = f"Last heard from \x0308{nick}\x03 at {timestamp} " \
|
if args.first:
|
||||||
|
msg = "First"
|
||||||
|
else:
|
||||||
|
msg = "Last"
|
||||||
|
|
||||||
|
msg += f" heard from \x0308{args.nick}\x03 at {timestamp} " \
|
||||||
+ f"(\x0312{reltime} ago\x03) in \x0312{channel}"
|
+ f"(\x0312{reltime} ago\x03) in \x0312{channel}"
|
||||||
|
|
||||||
if last:
|
if args.message:
|
||||||
msg += f'\x03 with "\x0308{message}\x03"'
|
msg += f'\x03 with "\x0308{message}\x03"'
|
||||||
|
|
||||||
bot.say(msg)
|
bot.say(msg)
|
||||||
|
@ -89,7 +106,9 @@ def dump_seen_db(bot):
|
||||||
bot.memory["seen_lock"].acquire()
|
bot.memory["seen_lock"].acquire()
|
||||||
for nick, seen in bot.memory["seen"].items():
|
for nick, seen in bot.memory["seen"].items():
|
||||||
bot.db.execute("INSERT OR REPLACE INTO seen "
|
bot.db.execute("INSERT OR REPLACE INTO seen "
|
||||||
"(nick, timestamp, channel, message) VALUES (?, ?, ?, ?)",
|
"(nick, first_timestamp, first_channel, first_message,"
|
||||||
|
"last_timestamp, last_channel, last_message)"
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||||
(nick,) + seen)
|
(nick,) + seen)
|
||||||
bot.memory["seen_lock"].release()
|
bot.memory["seen_lock"].release()
|
||||||
|
|
||||||
|
@ -97,7 +116,12 @@ def dump_seen_db(bot):
|
||||||
@hook(True)
|
@hook(True)
|
||||||
@require_chanmsg
|
@require_chanmsg
|
||||||
def seen_hook(bot, trigger):
|
def seen_hook(bot, trigger):
|
||||||
seen = (time.time(), trigger.channel, trigger.group(0))
|
last = (time.time(), trigger.channel, trigger.group(0))
|
||||||
|
if not trigger.nick in bot.memory["seen"]:
|
||||||
|
first = (time.time(), trigger.channel, trigger.group(0))
|
||||||
|
else:
|
||||||
|
first = bot.memory["seen"][trigger.nick][:3]
|
||||||
|
seen = first + last
|
||||||
bot.memory["seen"][trigger.nick] = seen
|
bot.memory["seen"][trigger.nick] = seen
|
||||||
|
|
||||||
if time.time() - bot.memory["seen_last_dump"] > 60:
|
if time.time() - bot.memory["seen_last_dump"] > 60:
|
||||||
|
|
|
@ -14,7 +14,14 @@ def setup(bot):
|
||||||
|
|
||||||
@commands('uptime')
|
@commands('uptime')
|
||||||
def uptime(bot, trigger):
|
def uptime(bot, trigger):
|
||||||
""".uptime - Returns the uptime of Sopel."""
|
""".uptime - Returns the uptime of Fulvia."""
|
||||||
delta = datetime.timedelta(seconds=round((datetime.datetime.now() -
|
delta = datetime.timedelta(seconds=round((datetime.datetime.now() -
|
||||||
bot.memory["uptime"]).total_seconds()))
|
bot.memory["uptime"]).total_seconds()))
|
||||||
bot.say(f"I've been sitting here for {delta} and I keep going!")
|
bot.say(f"I've been sitting here for {delta} and I keep going!")
|
||||||
|
|
||||||
|
|
||||||
|
@commands('updick')
|
||||||
|
def updick(bot, trigger):
|
||||||
|
""".updick - Returns the uptime of Fulvia, measured in dicks."""
|
||||||
|
delta = datetime.datetime.now() - bot.memory["uptime"]
|
||||||
|
bot.say("8" + "="*delta.days + "D")
|
||||||
|
|
1
run.py
1
run.py
|
@ -3,7 +3,6 @@
|
||||||
Initializes the bot.
|
Initializes the bot.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Trigger():
|
||||||
"""
|
"""
|
||||||
A datetime object at which the message was received by the IRC server.
|
A datetime object at which the message was received by the IRC server.
|
||||||
If the server does not support server-time, then `time` will be the time
|
If the server does not support server-time, then `time` will be the time
|
||||||
that the message was received by Sopel.
|
that the message was received by Fulvia.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.raw = ""
|
self.raw = ""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user