Add !grabquote [user] to quote a user's last line
This commit is contained in:
parent
52e579e364
commit
0a4edfe245
3 changed files with 40 additions and 3 deletions
|
|
@ -28,6 +28,7 @@ python3 bot.py
|
||||||
| `!quote` | Random quote |
|
| `!quote` | Random quote |
|
||||||
| `!quote <id>` | Quote by number |
|
| `!quote <id>` | Quote by number |
|
||||||
| `!addquote <text>` | Store a new quote, returns its id |
|
| `!addquote <text>` | Store a new quote, returns its id |
|
||||||
|
| `!grabquote [user]`| Grab the last line a user said (`!grab` alias); no user = last speaker |
|
||||||
| `!delquote <id>` | Delete a quote |
|
| `!delquote <id>` | Delete a quote |
|
||||||
| `!search <term>` | First quote containing a substring |
|
| `!search <term>` | First quote containing a substring |
|
||||||
| `!quotecount` | How many quotes are stored |
|
| `!quotecount` | How many quotes are stored |
|
||||||
|
|
@ -36,6 +37,9 @@ python3 bot.py
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Quotes live in `quotes.db` (SQLite, created on first run, gitignored).
|
- Quotes live in `quotes.db` (SQLite, created on first run, gitignored).
|
||||||
|
- `!grabquote` reads an in-memory buffer of each user's last channel line;
|
||||||
|
that buffer is per-session and resets on reconnect. Grabbed lines are stored
|
||||||
|
as `<nick> message`. Bot commands are never recorded, so you can't grab one.
|
||||||
- The bot auto-reconnects with exponential backoff and answers `PING`.
|
- The bot auto-reconnects with exponential backoff and answers `PING`.
|
||||||
- If the nick is taken it appends `_` and retries.
|
- If the nick is taken it appends `_` and retries.
|
||||||
- A NickServ password, if set via `IRC_NICKSERV_PASS`, is sent on connect —
|
- A NickServ password, if set via `IRC_NICKSERV_PASS`, is sent on connect —
|
||||||
|
|
|
||||||
33
bot.py
33
bot.py
|
|
@ -113,6 +113,10 @@ class IRCBot:
|
||||||
self.store = store
|
self.store = store
|
||||||
self.sock: socket.socket | None = None
|
self.sock: socket.socket | None = None
|
||||||
self._recv_buf = b""
|
self._recv_buf = b""
|
||||||
|
# Last non-command channel line per user, for !grabquote.
|
||||||
|
# Keyed by lowercased nick -> (original_nick, text).
|
||||||
|
self.last_lines: dict[str, tuple[str, str]] = {}
|
||||||
|
self.last_speaker: str | None = None
|
||||||
|
|
||||||
# --- low-level I/O ----------------------------------------------------
|
# --- low-level I/O ----------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -233,11 +237,17 @@ class IRCBot:
|
||||||
return
|
return
|
||||||
target, message = params[0], params[1]
|
target, message = params[0], params[1]
|
||||||
sender = prefix.split("!", 1)[0] if prefix else ""
|
sender = prefix.split("!", 1)[0] if prefix else ""
|
||||||
|
is_channel = target.startswith(("#", "&"))
|
||||||
|
|
||||||
# Reply to the channel for channel messages, or to the user for PMs.
|
# Reply to the channel for channel messages, or to the user for PMs.
|
||||||
reply_to = target if target.startswith(("#", "&")) else sender
|
reply_to = target if is_channel else sender
|
||||||
|
|
||||||
if not message.startswith(CMD_PREFIX):
|
if not message.startswith(CMD_PREFIX):
|
||||||
|
# Remember the last real line each user said, so !grabquote can
|
||||||
|
# recall it. Bot commands are deliberately not recorded.
|
||||||
|
if is_channel and sender:
|
||||||
|
self.last_lines[sender.lower()] = (sender, message)
|
||||||
|
self.last_speaker = sender
|
||||||
return
|
return
|
||||||
parts = message[len(CMD_PREFIX):].strip().split(" ", 1)
|
parts = message[len(CMD_PREFIX):].strip().split(" ", 1)
|
||||||
cmd = parts[0].lower()
|
cmd = parts[0].lower()
|
||||||
|
|
@ -293,6 +303,21 @@ def cmd_delquote(bot: IRCBot, reply_to: str, sender: str, arg: str):
|
||||||
bot.send_privmsg(reply_to, f"Deleted quote #{arg}." if ok else f"No quote #{arg}.")
|
bot.send_privmsg(reply_to, f"Deleted quote #{arg}." if ok else f"No quote #{arg}.")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_grabquote(bot: IRCBot, reply_to: str, sender: str, arg: str):
|
||||||
|
"""!grabquote [user] — store the last line a user said (default: last speaker)."""
|
||||||
|
nick = arg.split()[0] if arg.split() else bot.last_speaker
|
||||||
|
if not nick:
|
||||||
|
bot.send_privmsg(reply_to, "Nothing to grab yet.")
|
||||||
|
return
|
||||||
|
rec = bot.last_lines.get(nick.lower())
|
||||||
|
if not rec:
|
||||||
|
bot.send_privmsg(reply_to, f"I haven't seen {nick} say anything.")
|
||||||
|
return
|
||||||
|
orig_nick, text = rec
|
||||||
|
qid = bot.store.add(f"<{orig_nick}> {text}", sender)
|
||||||
|
bot.send_privmsg(reply_to, f"Grabbed quote #{qid}: <{orig_nick}> {text}")
|
||||||
|
|
||||||
|
|
||||||
def cmd_search(bot: IRCBot, reply_to: str, sender: str, arg: str):
|
def cmd_search(bot: IRCBot, reply_to: str, sender: str, arg: str):
|
||||||
"""!search <term> — first quote matching a substring."""
|
"""!search <term> — first quote matching a substring."""
|
||||||
if not arg:
|
if not arg:
|
||||||
|
|
@ -310,14 +335,16 @@ def cmd_count(bot: IRCBot, reply_to: str, sender: str, arg: str):
|
||||||
def cmd_help(bot: IRCBot, reply_to: str, sender: str, arg: str):
|
def cmd_help(bot: IRCBot, reply_to: str, sender: str, arg: str):
|
||||||
bot.send_privmsg(
|
bot.send_privmsg(
|
||||||
reply_to,
|
reply_to,
|
||||||
"Commands: !quote [id], !addquote <text>, !delquote <id>, "
|
"Commands: !quote [id], !addquote <text>, !grabquote [user], "
|
||||||
"!search <term>, !quotecount, !help",
|
"!delquote <id>, !search <term>, !quotecount, !help",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
COMMANDS = {
|
COMMANDS = {
|
||||||
"quote": cmd_quote,
|
"quote": cmd_quote,
|
||||||
"addquote": cmd_addquote,
|
"addquote": cmd_addquote,
|
||||||
|
"grabquote": cmd_grabquote,
|
||||||
|
"grab": cmd_grabquote,
|
||||||
"delquote": cmd_delquote,
|
"delquote": cmd_delquote,
|
||||||
"search": cmd_search,
|
"search": cmd_search,
|
||||||
"quotecount": cmd_count,
|
"quotecount": cmd_count,
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,12 @@ Durable memory for this project. Read at session start, update before session en
|
||||||
NickServ login landed and getting bounced (477); now defers JOIN until the
|
NickServ login landed and getting bounced (477); now defers JOIN until the
|
||||||
`900`/"now identified" confirmation. Verified join succeeds (got 353/366).
|
`900`/"now identified" confirmation. Verified join succeeds (got 353/366).
|
||||||
|
|
||||||
|
### 2026-06-04 (cont.)
|
||||||
|
- Added `!grabquote [user]` (alias `!grab`): stores the last line a user said,
|
||||||
|
formatted `<nick> message`. Backed by an in-memory `last_lines` buffer
|
||||||
|
(per-session, resets on reconnect); bot commands are excluded from it. No-arg
|
||||||
|
form grabs the last real speaker. Offline-tested.
|
||||||
|
|
||||||
## External references
|
## External references
|
||||||
|
|
||||||
- Libera.Chat: <https://libera.chat> — server `irc.libera.chat:6697` (TLS).
|
- Libera.Chat: <https://libera.chat> — server `irc.libera.chat:6697` (TLS).
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue