zynk/CHANGELOG.md

29 KiB
Raw Blame History

2026.07.26 - Fix Wikipedia article links using wrong space encoding. (0.43.2)

  • Fixed Wikipedia article links in !wp responses using + instead of _ for spaces (e.g. CLever+Audio+Plug-inCLever_Audio_Plug-in). The wp_url_encode() function was used for both API query parameters (where + is correct) and article title URLs (where Wikipedia expects _). Added wp_url_encode_title() that encodes spaces as underscores for use in article link construction, and switched all article link encodings to use it.

2026.07.26 - Improved Wikipedia results formatting and article extracts. (0.43.1)

  • Changed multi-result mode from a single pipe-separated message to individual messages: each result is now sent as its own IRC message (Title — snippet) followed by a separate "Read full article: LINK" message. This makes results easier to read in busy channels and ensures each article link is immediately visible.
  • Removed numbering from multi-result messages (was 1. Title — ..., now just Title — ...).
  • Changed single-result extract to show the first chapter instead of only the introductory paragraph: removed exintro=true from the Extracts API URL so the API returns the full article content up to the character limit.
  • Increased the extract character limit from 500 to 800 characters (exchars=800) to show more article content per result.
  • Added truncation indicator ("...") appended to single-result extracts when the text reaches 790+ characters (near the exchars limit).
  • Added "Read full article: LINK" follow-up message to every single-result response (previously only sent the bare URL as a second message, now uses the "Read full article: " prefix).

2026.07.26 - Wikipedia search command. (0.43.0)

  • Added !wp <search terms> command: searches Wikipedia using the MediaWiki API (action=query&list=search) and displays results. Accepts multiple keywords (e.g. !wp quantum physics). Rate-limited via rate_limit_check().
  • Single-result mode: when the search returns exactly one article, fetches the first chapter via the Extracts API (action=query&prop=extracts, up to 800 characters) and sends the article title, intro text, and a "Read full article: LINK" message. Appends "..." if the extract was truncated.
  • Multi-result mode: when the search returns 25 articles, sends a compact numbered list with short snippets (truncated to 60 chars) separated by pipes. Each result is followed by a "Read full article: LINK" message. Appends "..." if the result list was truncated.
  • Added self-contained JSON parser helpers: wp_json_get_string() extracts string values by key, wp_json_next_array_item() iterates JSON array objects, wp_decode_json_string() handles JSON escape sequences including \uXXXX Unicode.
  • Added wp_url_encode() for percent-encoding search queries and article titles in API URLs (RFC 3986 compliant, spaces as +).
  • Added wp_codepoint_to_utf8() to encode Unicode code points as UTF-8 bytes (handles U+0000 through U+10FFFF).
  • Added wp_strip_html() to remove HTML tags from Wikipedia search result snippets, and wp_collapse_spaces() to normalize whitespace.
  • Added wp_hex_digit() to convert hexadecimal characters to numeric values.
  • Added WITH_WP CMake feature toggle (ON by default) with compile definition HAS_CMD_WP.
  • Added !wp to the !help command listing.
  • Added comprehensive doc comments to all nine functions in cmd_wp.h.

2026.07.25 - Comprehensive flood protection: per-poll limits, userhost rate limiting, outgoing throttle. (0.42.0)

  • Added MAX_LINES_PER_POLL = 50 constant and per-poll line counting in irc_feed(): the bot now drops excess lines when a single poll iteration delivers more than 50 complete IRC messages, preventing burst flooding from overwhelming the bot or triggering IRC server excess flood disconnects.
  • Added poll_lines_left counter to Session struct, reset at the start of each poll cycle in main() before calling irc_feed().
  • Changed RateLimitEntry to track by user@host instead of nick: the rate limiter now keys on the full userhost extracted from the IRC prefix (nick!user@host) rather than the nick alone, preventing trivial bypass via nick cycling. Added userhost[128] field replacing the old nick[32] field.
  • Added current_userhost[128] global variable, parsed from the IRC message prefix in irc_handle() before dispatching to command handlers. Updated rate_limit_check() signature to accept the userhost parameter.
  • Added violation tracking to RateLimitEntry with a violations counter: repeated rate limit hits from the same userhost increment the counter, enabling future escalating penalties (warnings at three violations, kick requests at 5).
  • Increased RATE_LIMIT_MAX from 8 to 32, expanding the rate limit tracking buffer to handle more concurrent users without evicting entries.
  • Added outgoing message throttle to net_send(): uses clock_gettime(CLOCK_MONOTONIC) timestamp tracking to enforce a minimum OUTGOING_DELAY_MS = 100 millisecond delay between consecutive sends, preventing the bot from flooding the IRC server with rapid bursts.
  • Added 200ms inter-line delay in !gitlog output (cmd_gitlog.h): each commit line is now separated by a 200ms nanosleep() to avoid triggering IRC server flood protection on multi-commit responses.
  • Added 150ms inter-chunk delay in AI response delivery (ai.h): multi-chunk AI answers now have a 150ms pause between chunks to pace outgoing traffic.
  • Added AI_COOLDOWN_SECS = 5 constant: after an AI query completes, a 5-second cooldown prevents immediate re-submission, reducing unnecessary load. Added last_ai_completion timestamp tracked in ai.h and checked in ai_ask().
  • Added rate limiting to casual chat and greeting responses (cmd_greeting_or_chat.h): the rate_limit_check() call now gates hardcoded greeting matches (hi, hey, hello, etc.) in addition to AI-forwarded messages.
  • Limited tell_deliver() to TELL_DELIVER_MAX = 5 messages per nick per delivery: excess pending messages remain in the database for the next time the user speaks, preventing a single user from triggering a flood of delivery messages.
  • Updated all 19 command header files to pass current_userhost to the updated rate_limit_check() signature.

2026.07.25 - Welcome message for new users, channel tracking docs. (0.41.2)

  • Added a welcome message when users join a channel: handle_join() now sends "Welcome, <nick>!" to the channel for non-bot users. The bot's own joins are silently ignored to avoid echo loops.
  • Added src_nick parameter to handle_join() to identify the joining user; the call site in irc_handle() was updated to pass src_nick.
  • Added doc comments to chans[] (global channel tracking array) and nchans (count of tracked channels) in irc.h.
  • Added doc comments to yt_url_encode() in cmd_yt.h describing percent-encoding behavior, parameters, and return value.

2026.07.25 - Decode Unicode escapes in !yt responses. (0.41.1)

  • Fixed !yt displaying raw JSON Unicode escape sequences (e.g. \u0027 instead of ') in video titles and channel names. YouTube's oEmbed API returns non-ASCII characters as \uXXXX escapes, which were previously passed through as-is.
  • Added yt_hex_digit() to convert a hex character to its numeric value.
  • Added yt_codepoint_to_utf8() to encode a Unicode code point as UTF-8 bytes (handles U+0000 through U+10FFFF, producing 1-4 bytes).
  • Added yt_decode_unicode_escapes() to scan a string for \uXXXX sequences and replace each with the corresponding UTF-8 bytes in-place. Safe for in-place modification because UTF-8 output is always shorter than the 6-byte escape.
  • Both title and author fields are now decoded after extraction from the JSON response.
  • Added doc comments to all three new helper functions.

2026.07.25 - YouTube title lookup command, json_find docs. (0.41.0)

  • Added !yt <url> command: fetches the title and channel name of a YouTube video using the YouTube oEmbed API (https://www.youtube.com/oembed). The video URL is percent-encoded via a new yt_url_encode() helper before being passed as a query parameter. The JSON response is parsed for title and author_name fields using the existing json_find() helper, and the result is formatted as "Title — Channel".
  • Added WITH_YT CMake feature toggle (ON by default) with compile definition HAS_CMD_YT.
  • Added !yt to the !help command listing.
  • Added comprehensive doc comments to yt_url_encode() and cmd_yt() in cmd_yt.h, and to json_find() in zynk.c.
  • Updated README.md: added YouTube title lookup to the feature summary, features list, feature toggles table, minimal build command, and IRC commands utilities table.

2026.07.24 - Reduced MAX_HISTORY, moved LOAD_FUNC macro, added irc_reply docs. (0.40.9)

  • Reduced MAX_HISTORY from 100 to 12 to decrease AI context memory usage.
  • Moved LOAD_FUNC macro from zynk.h to db.h for better code organization (closer to its usage in sqlite_init_lib).
  • Added comprehensive doc comment to irc_reply() function describing its nick-prefixing behavior for channel messages.
  • Reordered includes in zynk.c to alphabetical order.

2026.07.24 - Nick-prefixed channel replies for all non-admin commands. (0.40.8)

  • Added irc_reply(): a new IRC response helper that automatically prepends the sender's nick to channel messages (e.g. "nick: pong!"), while leaving private-message responses unmodified. This makes it clear which user the bot is addressing in busy channels.
  • Converted all non-admin command handlers to use irc_reply() instead of raw irc_msg(): !ping, !zynk, !weather, !forecast, !stock, !calc, !time, !seen, !tell, !gitlog, !changelog, !ai, !uptime, !version, !help, greeting/chat responses, and the "Not implemented..." fallback for unknown !commands.
  • Converted AI async responses (ai_check_completion()) to use irc_reply(), so AI answers delivered after the original query also show the requesting user's nick.
  • Converted AI error messages in ai_ask() (rate-limit busy, fork failure, start failure) to use irc_reply().
  • Removed redundant manual nick prefixes from commands that already embedded the sender's nick in their response text: !ping ("nick: pong!"), !calc ("nick: result"), !time ("nick: 2026-07-24 ..."), !uptime ("nick: up 3d ..."), and greeting/chat responses ("Hi, nick!""nick: Hi!").
  • Admin commands (!quit, !reload, !restart, !rebuild, !code) are intentionally excluded - these are privileged operations where the nick prefix adds no value.
  • The tell_deliver() function retains its own existing format and was not modified.

2026.07.24 - Secure file ops, TLS hardening, input validation, security docs. (0.40.7)

  • Added secure_state_open(): hardened file open with O_CLOEXEC, O_NOFOLLOW, mode 0600, regular-file and ownership checks. Prevents symlink attacks, TOCTOU races, and world-writable temp files.
  • Replaced all fopen() calls for temp state files (/tmp/zynk_reload, /tmp/zynk_code_restart, /tmp/zynk_make_err, /tmp/zynk_rebuild_err) with secure_state_open() + fdopen() across hot reload, compile-restart, and rebuild paths.
  • Hardened TLS initialization: enforces minimum TLS 1.2 (SSL_CTX_set_min_proto_version), disables compression (SSL_OP_NO_COMPRESSION), and checks the return value of SSL_CTX_set_default_verify_paths() instead of ignoring failures.
  • CMake compiler/linker hardening: added -Wformat=2 -Wstrict-prototypes -fstack-protector-strong, _FORTIFY_SOURCE=3 for release builds, and RELRO + noexecstack linker flags. Fixed _FORTIFY_SOURCE redefinition warning on musl/Alpine by undefining before redefining via SHELL: prefix.
  • Added !calc input validation: rejects expressions longer than 256 chars and disallows non-numeric characters (only digits, ., (), +-*/^%, and whitespace are permitted).
  • Added !stock symbol validation: rejects symbols containing characters other than alphanumerics, ., -, ^, or =.
  • Fixed !quit, !reload, !restart to require channel context - these commands now reject DM usage with a clear error instead of silently passing the op check.
  • Fixed log timestamp format: %y (2-digit year) → %Y (4-digit year).
  • Fixed asprintf return value check in !forecast error paths (avoids using a potentially NULL error string).
  • Fixed !reload fork-in-place mode: only saves TLS session state when exec_new=1, since fork-in-place inherits the live socket.
  • Cleaned up the reload state file on failure and in fork child for fork-in-place mode to avoid stale files.
  • Fixed git SSH→HTTPS URL conversion in !gitlog and !changelog: host:path now correctly becomes host/path instead of hostpath.
  • AI plan mode (!ai without explicit agent) no longer passes --dangerously-skip-permissions.
  • Added IRC buffer overflow protection: lines exceeding RBUF_SZ are dropped instead of truncating.
  • Added doc comments to ZYNK_RELOAD_FILE, secure_state_open(), RateLimitEntry, and rate_limits.

2026.07.24 - Strip trailing space from wttr.in weather descriptions. (0.40.6)

  • Fixed trailing space in weather descriptions from wttr.in (e.g. "Partly Cloudy ""Partly Cloudy"), which caused a stray space before the closing parenthesis in !forecast full output (e.g. (Partly Cloudy ) instead of (Partly Cloudy)). Applied the same fix to the non-full cond field used by !forecast and !weather.

2026.07.23 - Full forecast mode, 24h sunrise/moon times, startup version. (0.40.5)

  • Added full keyword to !forecast: !forecast frankfurt full shows per-day detail lines with weather description, sunrise/sunset, and moonrise/moonset in 24-hour format, sent as separate IRC messages.
  • Added to_24h() helper to convert wttr.in's 12-hour AM/PM astronomy times to 24-hour format.
  • Fixed the missing first letter of weather descriptions in full mode ("value": " offset was 11 instead of 10).
  • Startup message now includes the version: [zynk 0.40.5] starting up.
  • Added doc comments to to_24h(), find_day_obj(), and other forecast helper functions.
  • Fixed "Not implemented..." fallback to only trigger on ! followed by a letter (e.g. !foo), so bare ! or !!! no longer produce the message.

2026.07.23 - Raise AI code mode limit, simplify plan mode prompt. (0.40.4)

  • Increased AI code mode response limit back to 2000 chars (was reduced to 1000 in 0.40.3, but code changes need more room).
  • Simplified the AI plan mode system prompt further: removed the explicit "tell them you are in plan mode" instruction, keeping it to a short "You are in plan mode. Answer briefly." directive.
  • Fixed !gitlog default count in README.md from 5 to 1 (matching the actual GITLOG_COUNT value set in 0.38.2).
  • Re-aligned the utility command table in README.md for cleaner formatting.
  • Minor grammar and punctuation fixes across older changelog entries.

2026.07.23 - Simplify CMake feature loop, adjust AI response limits. (0.40.3)

  • Refactored CMake feature configuration: merged the two separate foreach loops (one for building _cmd_defs and one for printing status) into a single loop, removing redundant iteration. Improved the status banner formatting for clearer feature configuration output.
  • Reduced AI code mode response limit from 2000 to 1000 chars to keep IRC replies concise.
  • Increased AI plan mode response limit from 800 to 1000 chars, giving slightly more room for explanations.
  • Simplified AI plan mode system prompts description for clarity.
  • Fixed a minor whitespace alignment in the README.md command table.

2026.07.23 - Show commit date/time in !gitlog output. (0.40.2)

  • !gitlog now shows the commit date and time in [YYYY-MM-DD HH:MM:SS] format at the beginning of each commit line (e.g. [2026-07-22 14:30:15] 184d4c4 Fix whitespace...).

2026.07.23 - Fix !zynk key-value timestamps to use localtime. (0.40.1)

  • Fixed !zynk key = value storing UTC timestamps instead of localtime: db_set() and the zynk table default used datetime('now') which returns UTC, causing !zynk <key> to display a timestamp offset by the server's timezone difference. Changed to datetime('now','localtime') so timestamps match the system's local time.
  • Also fixed db_rotate_history() (used by AI query context) and the tell table default to use localtime.

2026.07.23 - Commit links for !gitlog, full changelog link for !changelog, CMake, multi-file refactor. (0.40.0)

  • !gitlog now shows a link to each commit in the repository (e.g. 184d4c4 Fix whitespace... - https://...), using the git remote URL converted from SSH/SCP to HTTPS.
  • !changelog now shows a "Full changelog: " link to CHANGELOG.md in the repository as the last message.
  • Removed full keyword from !gitlog and !changelog (previously showed full commit messages and complete changelog entries).
  • Switched the build system from Make to CMake with feature toggles (-DWITH_<FEATURE>=ON/OFF).
  • Refactored a single-file codebase into dedicated header files: each command now lives in its own cmd_*.h file, with separate modules for database (db.h), IRC protocol (irc.h), networking (net.h), TLS (tls.h), and AI (ai.h).
  • Updated README.md to reflect the simplified !gitlog and !changelog syntax and the new CMake build instructions.

2026.07.22 - Fix whitespace collapsing in !ai, !gitlog, !changelog responses. (0.39.1)

  • Fixed collapse_spaces() to treat tabs, newlines, and carriage returns as whitespace (not just spaces), collapsing any run of mixed whitespace into a single space.
  • collapse_spaces() now strips leading and trailing whitespace from output.
  • Added collapse_spaces() call to !ai responses (after strip_ai_phrases()).
  • Added collapse_spaces() call to !changelog full entries before sending.
  • Fixed !gitlog full output still containing multiple spaces from git's tab-delimited commit fields.

2026.07.21 - Hot reload preserves IRC session (nick, channels, voice). (0.39.0)

  • reload_do() now accepts an exec_new parameter: when 0 (!reload/!restart), the child inherits the socket fd and SSL context directly and continues the event loop without reconnecting; when 1 (!rebuild/!code), the old fork+exec path is used.
  • Removed irc_part_all() from the reload path - the bot no longer parts channels on reload, so voice mode (+v) and channel presence are preserved.
  • Reset pending_ai_pid/pending_ai_id in the fork child to avoid the stale state from orphaned AI processes.
  • !reload and !restart now keep the bot's IRC session alive (no nick collision, no rejoin, no lost voice). !rebuild/!code still do a clean QUIT+exec since the binary changed.

2026.07.21 - Strip opencode header from !ai responses. (0.38.3)

  • Added strip_opencode_header(): removes the opencode header line (e.g. > plan · big-pickle) and any trailing blank lines from AI responses before sending to IRC.
  • Pipeline order: format strip → header strip → phrase strip, so ANSI codes are cleaned up first and the > prefix is reliably detected.

2026.07.21 - Full mode for !gitlog/!changelog, optional pull for !rebuild. (0.38.2)

  • Added optional full keyword to !gitlog and !changelog: !gitlog 5 full shows full commit messages (author, date, body), !changelog 5 full shows the complete entry text including bullet points.
  • !gitlog full mode collapses multiple spaces and strips leading whitespace from each commit block for cleaner IRC output.
  • Changed !rebuild to skip git pull by default; pass pull to pull before rebuilding (e.g. !rebuild pull).
  • Changed the default count GITLOG_COUNT for !gitlog from 5 to 1 and CHANGELOG_COUNT for !changelog from 5 to 1.
  • Reduced GITLOG_MAX from 10 to 5.
  • Updated !help text to reflect new [full] and [pull] arguments.
  • Updated README.md with full option for !gitlog/!changelog and pull option for !rebuild.

2026.07.21 - Exempt ops from rate limiting. (0.38.1)

  • Op users in channels are now exempt from rate limiting: rate_limit_check() calls chan_is_op() and allows ops through immediately.
  • Removed unused AI_REPLY_MAX constant.

2026.07.21 - Add !changelog command. (0.38.0)

  • Added !changelog [count] command: displays the latest changelog entries from CHANGELOG.md. Defaults to five entries (CHANGELOG_COUNT), accepts 1-10 as count. Each entry title (date line) is sent as a separate IRC message with a 200ms delay to avoid IRC server flood protection.
  • Reduced GITLOG_MAX and CHANGELOG_MAX from 20 to 10.
  • Updated !help to list !changelog.
  • Updated README.md with !changelog documentation and corrected max values.

2026.07.20 - Add !rebuild command. (0.37.0)

  • Added !rebuild command (ops only, channels): runs git pull, make clean, and make in sequence, then triggers a hot reload if all steps succeed. If any step fails, the error output from the build is reported to the channel.
  • Updated !help to list !rebuild.
  • Updated README.md with !rebuild documentation in the Admin (Ops Only) section.

2026.07.20 - Fix !seen showing UTC instead of local time. (0.36.1)

  • Fixed !seen command storing UTC timestamps instead of local time: seen_update() used datetime('now') which returns UTC, causing !seen <nick> to display a time offset by the server's timezone difference (e.g., 16:00 instead of 18:00 on a UTC+2 system). Changed to datetime('now','localtime') so seen timestamps match the system's local time.

2026.07.20 - Add !gitlog command. (0.36.0)

  • Added !gitlog [count] command: displays recent git commits of the zynk project using git log --oneline. Defaults to 5 commits (GITLOG_COUNT), accepts 1-20 as count. Each commit is sent as a separate IRC message.
  • Updated !help to list !gitlog.
  • Updated README.md with !gitlog documentation and git as a requirement.
  • Added detailed doc comment to cmd_gitlog() matching the style of all other function documentation in zynk.c.

2026.07.20 - Remove timezone abbreviation from !time response. (0.35.6)

  • !time now outputs date and time only (YYYY-MM-DD HH:MM:SS) without the timezone abbreviation, keeping IRC responses shorter and cleaner.
  • Removed 60 lines of dead timezone abbreviation-matching code from tz_format() (previously mapped 45 IANA timezone names to abbreviations like CEST, EDT, JST, etc.).
  • Updated README.md output format to reflect the change.
  • Added tzdata to README.md requirements as optional (without it, !time falls back to 45 built-in timezones).

2026.07.20 - Fix !time to work without tzdata. (0.35.5)

  • Rewrote !time to try the system localtime_r() first, then fall back to a built-in timezone table with 45 IANA zones and simplified DST rules when the system can't resolve timezones (e.g. no tzdata installed). This means !time Australia/Sydney now shows the correct AEST / AEDT even on minimal systems.
  • Added doc comments to all undocumented functions in zynk.c: seen_update, seen_lookup, tell_add, tell_deliver, valid_irc_word, normalize_city, collapse_spaces, cmd_reload, cmd_time, cmd_seen, cmd_tell, cmd_greeting_or_chat, tz_lookup, tz_format.
  • Added full list of built-in timezones to README.md (all 45 entries from tz_table) with region, offset, and DST status.

2026.07.20 - Update README with !calc pi() docs and date requirement. (0.35.4)

  • Added pi() constants section to README.md with examples (!calc pi(), !calc pi()*2, !calc pi()^2).
  • Added date to the list of system requirements in README.md.

2026.07.20 - Add pi() to !calc. (0.35.3)

  • Added pi() function support to !calc: bc -l does not define pi() on all systems, so cmd_calc now prepends a pi() definition before evaluating expressions. !calc pi(), !calc pi()*2, !calc pi()^2 etc. all work.

2026.07.20 - Fix !time timezone conversion bug. (0.35.2)

  • Fixed !time command: setenv("TZ",...) + localtime() was not reliably converting to the requested timezone. Replaced with TZ=... date via popen(), which handles all IANA timezone names correctly (e.g. !time Australia/Sydney now shows 11:15 AEST instead of UTC).

2026.07.20 - Fix !calc not working (bc stdin not connected). (0.35.1)

  • Fixed !calc command: the expression was never piped to bc's stdin, causing empty output and a "no result" error for every expression. Added a second pipe to feed the expression into bc before reading the result.

2026.07.20 - Calculator, time, seen, and tell commands. (0.35.0)

  • Added !calc <expression> command: evaluate math expressions via bc -l (supports arithmetic, powers, trigonometry, logarithms, variables, and multi-statement expressions).
  • Added !time [timezone] command: display the current time in any IANA timezone (e.g. !time US/Pacific, !time Europe/Berlin).
  • Added !seen <nick> command: track and query when users were last seen (auto-updated on PRIVMSG, PART, and QUIT).
  • Added !tell <nick> <message> command: leave offline messages for users, delivered automatically when they next speak.
  • Added seen and tell tables to the SQLite database for persistent user tracking and message storage.
  • Updated !help to list all new commands.
  • Updated README.md with detailed documentation for all commands, including !calc expression syntax and !time timezone examples.

2026.07.20 - Disabled stripping feature from 0.34.4 because it needs more testing. (0.34.5)

  • Disabled strip_opencode_header() (introduced in 0.34.4) as it requires further testing.

2026.07.20 - Strip opencode header from AI responses. (0.34.4)

  • Added strip_opencode_header() to remove the opencode model/agent header line from AI responses before sending them to IRC.

2026.07.19 - Show full AI error output on failure. (0.34.3)

  • AI error messages now include the actual opencode output instead of a generic "(opencode exited with error)" message.
  • Captured stderr from opencode (previously sent to /dev/null) so error details are no longer lost.

2026.07.19 - Collapsed double spaces in !forecast and !stock responses (0.34.2)

  • Collapsed double spaces in !forecast and !stock responses (e.g., when weather condition is empty).

2026.07.19 - Fix !weather returning HTML. (0.34.1)

  • Fixed !weather command returning <!DOCTYPE html> instead of weather data: wttr.in now returns HTML when it detects a browser User-Agent, so replaced the fake Firefox UA string with zynk/VERSION.
  • Fixed !forecast showing all days as Sunday on musl libc (Alpine Linux): strptime on musl does not compute tm_wday, so added mktime() to normalize the struct before formatting.

2026.07.19 - Uptime command, remove !hello. (0.34.0)

  • Added !uptime command: displays bot uptime in a human-readable format (days/hours/minutes/seconds).
  • Removed !hello command.

2026.07.19 - Hot reload re-registration fix, !hello, docs. (0.33.1)

  • Fixed hot reload: after TLS session restore, the bot now re-sends PASS/NICK/USER to re-register with the IRC server, fixing the issue where channels were never re-joined after !reload or !restart.
  • Added !hello command: responds with a greeting and the user's nick.
  • Added atomic build target in Makefile: compiles to a temp file and moves into place to avoid partial binaries on failure.
  • Added comprehensive doc comments to all major functions in zynk.c.

2026.07.19 - Hot reload, stock quotes, and auto-recompile. (0.33.0)

  • Added hot reload: !reload and !restart commands (ops) save TLS session state, fork a new process with the inherited socket, and resume without dropping from IRC.
  • Added !stock <SYMBOL>[,SYMBOL...] command to fetch real-time stock quotes from Yahoo Finance (up to 3 symbols).
  • Added auto-compile and restart on !code changes: when the AI code change response is received, the bot runs make and reloads on success.
  • Added User-Agent header to curl requests to avoid being blocked by servers that reject the default curl agent.
  • Increased curl read buffer from 2 KiB to 32 KiB to handle larger API responses.
  • Improved !forecast JSON parsing: returns a descriptive error message on parse failure instead of silently returning nothing.
  • Increased AI_TIMEOUT from 240s to 600s and AI_REPLY_MAX from 400 to 2048 chars for longer AI interactions.

2026.07.16 - Security hardening.

  • Fixed buffer overflow in ai_child_task: plen unsigned underflow when history entries exceed prefix buffer size, causing out-of-bounds writes via snprintf.
  • Fixed IRC injection in handle_nick_change: validate new nick for CRLF and control characters before storing in op cache.
  • Fixed control char leak in irc_msg: drop the message on strdup failure instead of sending unsanitized input on the wire.
  • Fixed CRLF/control char injection in -pass argument: reject passwords containing control characters at startup.

2026.07.15 - Numerous optimizations and fixes.

  • Fixed DB/TLS resource cleanup on partial startup failures and normal shutdown.
  • Fixed net_send() stack over-read risk when vsnprintf() truncates.
  • Fixed curl/opencode pipe readers so oversized output cannot deadlock the bot.
  • Fixed !code AI agent propagation into the forked child.
  • Added CR/LF rejection for startup IRC args to prevent local IRC command injection.
  • Normalized weather/forecast city input before building wttr.in URLs.
  • Fixed IRC op cache bugs around nick changes and long NAMES entries.
  • Added !version command to report the bot's version via IRC.
  • Fixed the malformed VERSION macro definition to ensure successful builds.
  • Added !help command to list available bot commands.
  • Added -opencode command-line option to set the path to the opencode binary, overriding OPENCODE_BIN.
  • Hardened IRC sending: strip control characters from messages and sanitize targets to prevent IRC injection.
  • Sanitized JOIN/NICK targets and PONG payload to avoid injection via CR/LF or control chars.
  • Validated -opencode path at startup to reject control characters.