Compare commits

..

3 Commits

16 changed files with 101 additions and 45 deletions

10
bot.py
View File

@ -72,10 +72,16 @@ class Fulvia(irc.IRCClient):
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.
"""
self.cmd_priv = {}
"""
A dictionary with command names as keys and required privilege
levels as values.
"""
self.doc = {}
"""
A dictionary of command names to their docstring and example, if
@ -132,6 +138,7 @@ class Fulvia(irc.IRCClient):
self._callables[func.__name__] = func
for command in func.commands:
self._commands[command] = func.__name__
self.cmd_priv[command] = func.priv
if func.hook:
self._callables[func.__name__] = func
@ -160,6 +167,7 @@ class Fulvia(irc.IRCClient):
self._callables.pop(func.__name__)
for command in func.commands:
self._commands.pop(command)
self.cmd_priv.pop(command)
if func.hook:
self._callables.pop(func.__name__)

7
db.py
View File

@ -4,6 +4,7 @@ The bot's database connection class.
"""
import os
import sqlite3
import threading
class FulviaDB(object):
"""
@ -13,6 +14,7 @@ class FulviaDB(object):
def __init__(self, config):
path = config.core.db_filename
self.filename = path
self.db_lock = threading.Lock()
def connect(self):
"""Return a raw database connection object."""
@ -26,5 +28,8 @@ class FulviaDB(object):
called per PEP 249.
"""
with self.connect() as conn:
self.db_lock.acquire()
cur = conn.cursor()
return cur.execute(*args, **kwargs)
res = cur.execute(*args, **kwargs)
self.db_lock.release()
return res

View File

@ -85,11 +85,12 @@ def process_callable(func, config):
func._docs = {}
func.example = getattr(func, "example", [(None, None)])
func.thread = getattr(func, 'thread', True)
func.hook = getattr(func, 'hook', False)
func.rate = getattr(func, 'rate', 0)
func.channel_rate = getattr(func, 'channel_rate', 0)
func.global_rate = getattr(func, 'global_rate', 0)
func.thread = getattr(func, "thread", True)
func.hook = getattr(func, "hook", False)
func.rate = getattr(func, "rate", 0)
func.channel_rate = getattr(func, "channel_rate", 0)
func.global_rate = getattr(func, "global_rate", 0)
func.priv = getattr(func, "priv", 0)
if hasattr(func, 'commands'):
if hasattr(func, 'example'):

View File

@ -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
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.
Users on the admin list in Sopels configuration are exempted from rate
Users on the admin list in Fulvia's configuration are exempted from rate
limits.
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.
"""
def actual_decorator(function):
function.priv = 5
@functools.wraps(function)
def guarded(bot, trigger, *args, **kwargs):
if not trigger.admin:
@ -199,6 +201,8 @@ def require_owner(message=None):
If they are not, `message` will be said if given.
"""
def actual_decorator(function):
function.priv = 10
@functools.wraps(function)
def guarded(bot, trigger, *args, **kwargs):
if not trigger.owner:

View File

@ -86,7 +86,7 @@ def me(bot, trigger):
@module.commands('selfmode')
@module.example(".mode +B")
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)
add_mode = mode.startswith("+")
bot.mode(bot.nickname, add_mode, mode)

View File

@ -73,7 +73,7 @@ def unban(bot, trigger):
@module.require_chanmsg
@module.require_privilege(OP, 'You are not a channel operator.')
@module.commands('kickban', 'kb')
@module.commands('kickban')
def kickban(bot, trigger):
"""
This gives admins the ability to kickban a user.

View File

@ -218,9 +218,7 @@ def roll(bot, trigger):
trigger.group(2), pretty_str, result))
@module.commands("choice")
@module.commands("ch")
@module.commands("choose")
@module.commands("choice", "choose")
@module.example(".choose opt1,opt2,opt3")
def choose(bot, trigger):
"""

View File

@ -21,7 +21,7 @@ class Hangman():
self.blanks[n] = ' '
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()
wrd = list(lines[ random.randint(0, len(lines))-1 ].strip())
return wrd

View File

@ -22,12 +22,18 @@ def help(bot, trigger):
ex = random.choice(examples)
bot.msg(docstring)
if ex:
if ex[0]:
bot.msg("Ex. In: " + ex[0])
if ex[1]:
bot.msg("Ex. Out: " + ex[1])
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)
bot.msg(msg)

View File

@ -18,7 +18,7 @@ def interactive_shell(bot, trigger):
if bot.memory['iconsole_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'
exitmsg = 'Interactive shell closed'

View File

@ -10,5 +10,9 @@ def pingAll(bot, trigger):
Says the nick of everyone in the channel. Great way to get thier
attention, or just annoy them.
"""
msg = " ".join(bot.channels[trigger.channel].users)
bot.say(msg)
names = list(bot.channels[trigger.channel].users.keys())
if "Ishd" in names:
names.remove("Ishd")
if "Ishd2" in names:
names.remove("Ishd2")
bot.say(" ".join(names))

View File

@ -16,7 +16,7 @@ class Scramble():
random.shuffle(self.shuffled)
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()
wrd = list(lines[ random.randint(0, len(lines))-1 ].strip())
return wrd
@ -29,7 +29,7 @@ class Scramble():
def isAnagram(givenWord, givenGuess):
word = [x for x in givenWord]
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()
if not ''.join(guess)+'\n' in words:
return "notaword"

View File

@ -3,6 +3,7 @@
When was this user last seen.
"""
import time
import argparse
import threading
from datetime import datetime
from sqlite3 import OperationalError
@ -20,8 +21,7 @@ def load_database(bot):
seens = bot.db.execute("SELECT * FROM seen").fetchall()
for seen in seens:
nick, timestamp, channel, message = seen
seen = (timestamp, channel, message)
nick, seen = seen[0], seen[1:]
data[nick] = seen
return data
@ -34,9 +34,12 @@ def setup(bot):
except OperationalError:
cur.execute("CREATE TABLE seen("
"nick TEXT PRIMARY KEY,"
"timestamp INTEGER,"
"channel TEXT,"
"message TEXT)")
"first_timestamp INTEGER,"
"first_channel TEXT,"
"first_message TEXT,"
"last_timestamp INTEGER,"
"last_channel TEXT,"
"last_message TEXT)")
con.commit()
con.close()
@ -50,33 +53,47 @@ def setup(bot):
+"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")
def seen(bot, trigger):
"""Reports when and where the user was last seen."""
nick = trigger.group(3)
last = False
if nick == "-l" or nick == "--last":
last = True
nick = trigger.group(4)
"""
Reports when and where the user was last/first seen.
--last [-l] reports when the user was last seen. This is the default.
--first [-f] reports when the user was first seen.
--message [-m] includes the first/last message the user sent.
"""
if not trigger.group(2):
return bot.reply("Seen who?")
if not nick:
return bot.say("Seen who?")
parser = argparse.ArgumentParser()
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!")
if nick in bot.memory["seen"]:
timestamp, channel, message = bot.memory["seen"][nick]
if args.nick in bot.memory["seen"]:
if args.first:
timestamp, channel, message = bot.memory["seen"][args.nick][:3]
else:
timestamp, channel, message = bot.memory["seen"][args.nick][3:]
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)
t_format = bot.config.core.default_time_format
timestamp = datetime.strftime(timestamp, t_format)
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}"
if last:
if args.message:
msg += f'\x03 with "\x0308{message}\x03"'
bot.say(msg)
@ -89,7 +106,9 @@ def dump_seen_db(bot):
bot.memory["seen_lock"].acquire()
for nick, seen in bot.memory["seen"].items():
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)
bot.memory["seen_lock"].release()
@ -97,7 +116,12 @@ def dump_seen_db(bot):
@hook(True)
@require_chanmsg
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
if time.time() - bot.memory["seen_last_dump"] > 60:

View File

@ -14,7 +14,14 @@ def setup(bot):
@commands('uptime')
def uptime(bot, trigger):
""".uptime - Returns the uptime of Sopel."""
""".uptime - Returns the uptime of Fulvia."""
delta = datetime.timedelta(seconds=round((datetime.datetime.now() -
bot.memory["uptime"]).total_seconds()))
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
View File

@ -3,7 +3,6 @@
Initializes the bot.
"""
import os
import sys
from twisted.internet import reactor

View File

@ -77,7 +77,7 @@ class Trigger():
"""
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
that the message was received by Sopel.
that the message was received by Fulvia.
"""
self.raw = ""