161 lines
4.6 KiB
Python
Executable File
161 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
When was this user last seen.
|
|
"""
|
|
import os
|
|
import time
|
|
import threading
|
|
from datetime import datetime
|
|
from sqlite3 import OperationalError
|
|
|
|
from requests.structures import CaseInsensitiveDict
|
|
|
|
import tools
|
|
import config
|
|
from tools.time import relativeTime
|
|
from module import commands, example, hook, require_chanmsg, rate
|
|
|
|
|
|
def load_database(bot):
|
|
"""
|
|
Loads all entries from the 'seen' table in the bot's database and
|
|
returns them.
|
|
"""
|
|
data = CaseInsensitiveDict()
|
|
seens = bot.db.execute("SELECT * FROM seen").fetchall()
|
|
|
|
for seen in seens:
|
|
nick, seen = seen[0].lower(), seen[1:]
|
|
data[nick] = seen
|
|
return data
|
|
|
|
|
|
def setup(bot):
|
|
con = bot.db.connect()
|
|
cur = con.cursor()
|
|
try:
|
|
cur.execute("SELECT * FROM seen").fetchone()
|
|
except OperationalError:
|
|
cur.execute("CREATE TABLE seen("
|
|
"nick TEXT PRIMARY KEY,"
|
|
"first_timestamp INTEGER,"
|
|
"first_channel TEXT,"
|
|
"first_message TEXT,"
|
|
"last_timestamp INTEGER,"
|
|
"last_channel TEXT,"
|
|
"last_message TEXT)")
|
|
con.commit()
|
|
con.close()
|
|
|
|
bot.memory["seen_lock"] = threading.Lock()
|
|
bot.memory["seen"] = load_database(bot)
|
|
bot.memory["seen_last_dump"] = time.time()
|
|
|
|
@rate(1)
|
|
@commands('seen')
|
|
@example(".seen Dad -l", "Last heard from Dad at [1997-03-12 16:30:00] "\
|
|
+"with \"Just going to the store for some cigarettes 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/first seen.
|
|
|
|
-l, --last - reports when the user was last seen. This is the default.
|
|
-f, --first - reports when the user was first seen.
|
|
-m, --message - includes the first/last message the user sent.
|
|
-c, --context - includes irc logs before and after their last message as context. Implies --message.
|
|
"""
|
|
if len(trigger.args) < 2:
|
|
return bot.reply("Seen who?")
|
|
|
|
parser = tools.FulviaArgparse()
|
|
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")
|
|
parser.add_argument("-c", "--context", action="store_true")
|
|
try:
|
|
args = parser.parse_args(trigger.args[1:])
|
|
except Exception as e:
|
|
return bot.reply(type(e).__name__ + ": " + str(e))
|
|
|
|
if args.nick == bot.nick:
|
|
return bot.reply("I'm right here!")
|
|
|
|
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{args.nick}")
|
|
|
|
timestamp = datetime.fromtimestamp(timestamp)
|
|
t_format = config.default_time_format
|
|
timestamp = datetime.strftime(timestamp, t_format)
|
|
reltime = relativeTime(datetime.now(), 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 args.message:
|
|
msg += f'\x03 with "\x0308{message}\x03"'
|
|
|
|
bot.msg(msg)
|
|
|
|
if args.context:
|
|
num_con_lines = 2
|
|
fname = os.path.join(bot.log_path, bot.hostname,channel,timestamp[1:11])
|
|
fname += ".log"
|
|
if not os.path.isfile(fname):
|
|
return bot.msg("Context not available.")
|
|
with open(fname, "r") as file:
|
|
data = file.read().splitlines()
|
|
matches = [line for line in data if line.startswith(timestamp)]
|
|
if not matches:
|
|
return bot.msg("Context not available")
|
|
index = data.index(matches[0]) # a bit lazy, but w/e
|
|
start = max([index - num_con_lines, 0])
|
|
end = min([index + num_con_lines+1, len(data) - 1])
|
|
bot.msg("Context:")
|
|
bot.msg("\n".join(data[start:end]))
|
|
del data
|
|
|
|
|
|
def dump_seen_db(bot):
|
|
"""
|
|
Dumps the seen database into the bot's database.
|
|
"""
|
|
bot.memory["seen_lock"].acquire()
|
|
for nick, seen in bot.memory["seen"].items():
|
|
bot.db.execute("INSERT OR REPLACE INTO seen "
|
|
"(nick, first_timestamp, first_channel, first_message,"
|
|
"last_timestamp, last_channel, last_message)"
|
|
"VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
(nick,) + seen)
|
|
bot.memory["seen_lock"].release()
|
|
|
|
|
|
@hook(True)
|
|
@require_chanmsg
|
|
def seen_hook(bot, trigger):
|
|
bot.memory["seen_lock"].acquire()
|
|
last = (time.time(), trigger.channel, ' '.join(trigger.args))
|
|
if not trigger.nick in bot.memory["seen"]:
|
|
first = (time.time(), trigger.channel, ' '.join(trigger.args))
|
|
else:
|
|
first = bot.memory["seen"][trigger.nick][:3]
|
|
seen = first + last
|
|
bot.memory["seen"][trigger.nick] = seen
|
|
bot.memory["seen_lock"].release()
|
|
|
|
if time.time() - bot.memory["seen_last_dump"] > 1:
|
|
# only dump once a minute at most
|
|
dump_seen_db(bot)
|
|
bot.memory["seen_last_dump"] = time.time()
|