2026-07-23 01:46:37 +02:00
|
|
|
#ifndef CMD_CHANGELOG_H
|
|
|
|
|
#define CMD_CHANGELOG_H
|
|
|
|
|
|
|
|
|
|
#include "zynk.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* cmd_changelog - Handle the "!changelog" command.
|
|
|
|
|
*
|
|
|
|
|
* Reads CHANGELOG.md from the project root and displays the last N date/title
|
|
|
|
|
* lines (default: CHANGELOG_COUNT). Lines starting with a date (YYYY.MM.DD)
|
|
|
|
|
* are treated as entries. Accepts an optional count argument (CHANGELOG_MAX).
|
|
|
|
|
* Rate-limited.
|
|
|
|
|
*
|
|
|
|
|
* Parameters:
|
|
|
|
|
* s - The IRC session.
|
|
|
|
|
* msg - The raw message text.
|
|
|
|
|
* reply_target - Channel or nick to reply to.
|
|
|
|
|
* src_nick - The nick of the user who sent the command.
|
|
|
|
|
*
|
|
|
|
|
* Returns: 1 if the command was matched, 0 otherwise.
|
|
|
|
|
*/
|
|
|
|
|
int cmd_changelog(Session *s, const char *msg, const char *reply_target, const char *src_nick) {
|
|
|
|
|
if (strncmp(msg, "!changelog", 10) != 0) return 0;
|
|
|
|
|
if (msg[10] != ' ' && msg[10] != '\0') return 0;
|
0.41.2 Implement comprehensive flood protection for IRC bot.
Phase 1: Incoming Protocol-Level Protection
- Added MAX_LINES_PER_POLL = 50 constant to limit lines processed per
poll iteration in irc_feed(), preventing burst flooding.
- Added poll_lines_left counter to Session struct, reset each poll cycle.
- Changed RateLimitEntry to track by user@host instead of nick, preventing
trivial bypass via nick cycling.
- Parse userhost from IRC prefix (nick!user@host) in irc_handle().
- Added violation tracking to RateLimitEntry for escalating penalties.
Phase 2: Outgoing Message Throttling
- Added OUTGOING_DELAY_MS = 100 constant for minimum delay between sends.
- Modified net_send() with timestamp-based throttling using clock_gettime().
- Added 200ms delay between lines in !gitlog output (cmd_gitlog.h).
- Added 150ms delay between AI response chunks (ai.h).
Phase 3: Rate Limit Improvements
- Increased RATE_LIMIT_MAX from 8 to 32 for larger tracking buffer.
- Added TELL_DELIVER_MAX = 5 to limit messages per nick delivery.
- Added AI_COOLDOWN_SECS = 5 to prevent rapid AI query re-submission.
Phase 4: Command-Specific Fixes
- Added rate_limit_check() to cmd_greeting_or_chat.h for casual chat.
- Limited tell_deliver() to 5 messages per nick, excess stays queued.
- Added 5-second cooldown after AI query completion before next query.
Files modified: zynk.h, zynk.c, irc.h, net.h, ai.h, db.h, and all
19 command header files to pass current_userhost to rate_limit_check().
2026-07-25 20:37:07 +02:00
|
|
|
if (rate_limit_check(src_nick, reply_target, current_userhost) < 0) return 1;
|
2026-07-23 01:46:37 +02:00
|
|
|
int count = CHANGELOG_COUNT;
|
|
|
|
|
const char *rest = msg[10] == ' ' ? msg + 11 : "";
|
|
|
|
|
while (*rest == ' ') rest++;
|
|
|
|
|
if (*rest) {
|
|
|
|
|
if (isdigit((unsigned char)*rest)) {
|
|
|
|
|
count = atoi(rest);
|
|
|
|
|
if (count <= 0) count = CHANGELOG_COUNT;
|
|
|
|
|
if (count > CHANGELOG_MAX) count = CHANGELOG_MAX;
|
|
|
|
|
while (*rest && isdigit((unsigned char)*rest)) rest++;
|
|
|
|
|
while (*rest == ' ') rest++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
char remote_url[512] = {0};
|
|
|
|
|
FILE *fp = popen("git remote get-url origin 2>/dev/null", "r");
|
|
|
|
|
if (fp) {
|
|
|
|
|
if (fgets(remote_url, sizeof remote_url, fp)) {
|
|
|
|
|
size_t rlen = strlen(remote_url);
|
|
|
|
|
while (rlen > 0 && (remote_url[rlen-1] == '\n' || remote_url[rlen-1] == '\r')) rlen--;
|
|
|
|
|
remote_url[rlen] = 0;
|
|
|
|
|
if (strncmp(remote_url, "ssh://git@", 10) == 0) {
|
|
|
|
|
char *host = remote_url + 10;
|
|
|
|
|
char *slash = strchr(host, '/');
|
|
|
|
|
if (slash) {
|
|
|
|
|
char tmp[512];
|
|
|
|
|
snprintf(tmp, sizeof tmp, "https://%.*s%s", (int)(slash - host), host, slash);
|
|
|
|
|
strncpy(remote_url, tmp, sizeof remote_url - 1);
|
|
|
|
|
remote_url[sizeof remote_url - 1] = 0;
|
|
|
|
|
}
|
|
|
|
|
} else if (strncmp(remote_url, "git@", 4) == 0) {
|
|
|
|
|
char *host = remote_url + 4;
|
|
|
|
|
char *path = strchr(host, ':');
|
|
|
|
|
if (path) {
|
|
|
|
|
char tmp[512];
|
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 with secure_state_open()
+ fdopen() across hot reload, compile-restart, and rebuild paths.
- Hardened TLS initialization: enforces minimum TLS 1.2, disables
compression, checks SSL_CTX_set_default_verify_paths() return value.
- CMake compiler/linker hardening: -Wformat=2, -Wstrict-prototypes,
-fstack-protector-strong, _FORTIFY_SOURCE=3, RELRO, noexecstack.
- Added !calc input validation: max 256 chars, numeric-only characters.
- Added !stock symbol validation: alphanumerics plus .-^= only.
- Fixed !quit, !reload, !restart to require channel context (DMs rejected).
- Fixed log timestamp format: %y (2-digit) to %Y (4-digit year).
- Fixed asprintf return value check in !forecast error paths.
- Fixed !reload fork-in-place mode: only saves TLS state when exec_new=1.
- Cleaned up reload state file on failure and in fork child.
- Fixed git SSH-to-HTTPS URL conversion in !gitlog and !changelog.
- AI plan mode no longer passes --dangerously-skip-permissions.
- Added IRC buffer overflow protection: oversized lines are dropped.
- Added doc comments to ZYNK_RELOAD_FILE, secure_state_open(),
RateLimitEntry, and rate_limits.
2026-07-24 03:21:49 +02:00
|
|
|
snprintf(tmp, sizeof tmp, "https://%.*s/%s", (int)(path - host), host, path + 1);
|
2026-07-23 01:46:37 +02:00
|
|
|
strncpy(remote_url, tmp, sizeof remote_url - 1);
|
|
|
|
|
remote_url[sizeof remote_url - 1] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
size_t ulen = strlen(remote_url);
|
|
|
|
|
if (ulen > 0 && remote_url[ulen-1] == '/') remote_url[--ulen] = 0;
|
|
|
|
|
if (ulen > 4 && strcmp(remote_url + ulen - 4, ".git") == 0) { ulen -= 4; remote_url[ulen] = 0; }
|
|
|
|
|
}
|
|
|
|
|
pclose(fp);
|
|
|
|
|
}
|
|
|
|
|
FILE *f = fopen("CHANGELOG.md", "r");
|
Nick-prefixed channel replies for all non-admin commands. (0.40.8)
- Added irc_reply(): 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.
- Converted AI async responses (ai_check_completion()) to use irc_reply(),
so AI answers delivered after the original query also show the 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, !calc,
!time, !uptime, and greeting/chat responses.
- 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.
2026-07-24 04:03:04 +02:00
|
|
|
if (!f) { irc_reply(s, reply_target, src_nick, "changelog: CHANGELOG.md not found"); return 1; }
|
2026-07-23 01:46:37 +02:00
|
|
|
char *line = NULL;
|
|
|
|
|
size_t cap = 0;
|
|
|
|
|
const char *headers[256];
|
|
|
|
|
int total = 0;
|
|
|
|
|
while (getline(&line, &cap, f) != -1) {
|
|
|
|
|
size_t len = strlen(line);
|
|
|
|
|
while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r')) line[--len] = 0;
|
|
|
|
|
if (len > 10 && isdigit((unsigned char)line[0]) && isdigit((unsigned char)line[1]) &&
|
|
|
|
|
isdigit((unsigned char)line[2]) && isdigit((unsigned char)line[3]) &&
|
|
|
|
|
line[4] == '.' && isdigit((unsigned char)line[5]) && isdigit((unsigned char)line[6]) &&
|
|
|
|
|
line[7] == '.' && isdigit((unsigned char)line[8]) && isdigit((unsigned char)line[9]) &&
|
|
|
|
|
line[10] == ' ') {
|
|
|
|
|
if (total < 256) headers[total++] = strdup(line);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(line);
|
|
|
|
|
fclose(f);
|
|
|
|
|
int showed = count < total ? count : total;
|
|
|
|
|
struct timespec ts_msg = { .tv_sec = 0, .tv_nsec = 200000000 };
|
|
|
|
|
for (int i = 0; i < showed; i++) {
|
Nick-prefixed channel replies for all non-admin commands. (0.40.8)
- Added irc_reply(): 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.
- Converted AI async responses (ai_check_completion()) to use irc_reply(),
so AI answers delivered after the original query also show the 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, !calc,
!time, !uptime, and greeting/chat responses.
- 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.
2026-07-24 04:03:04 +02:00
|
|
|
irc_reply(s, reply_target, src_nick, headers[i]);
|
2026-07-23 01:46:37 +02:00
|
|
|
free((void *)headers[i]);
|
|
|
|
|
if (i + 1 < showed) nanosleep(&ts_msg, NULL);
|
|
|
|
|
}
|
|
|
|
|
for (int i = showed; i < total; i++) free((void *)headers[i]);
|
Nick-prefixed channel replies for all non-admin commands. (0.40.8)
- Added irc_reply(): 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.
- Converted AI async responses (ai_check_completion()) to use irc_reply(),
so AI answers delivered after the original query also show the 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, !calc,
!time, !uptime, and greeting/chat responses.
- 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.
2026-07-24 04:03:04 +02:00
|
|
|
if (total == 0) { irc_reply(s, reply_target, src_nick, "changelog: no entries found"); return 1; }
|
2026-07-23 01:46:37 +02:00
|
|
|
if (*remote_url) {
|
|
|
|
|
char link[1024];
|
|
|
|
|
snprintf(link, sizeof link, "Full changelog: %s/blob/master/CHANGELOG.md", remote_url);
|
Nick-prefixed channel replies for all non-admin commands. (0.40.8)
- Added irc_reply(): 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.
- Converted AI async responses (ai_check_completion()) to use irc_reply(),
so AI answers delivered after the original query also show the 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, !calc,
!time, !uptime, and greeting/chat responses.
- 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.
2026-07-24 04:03:04 +02:00
|
|
|
irc_reply(s, reply_target, src_nick, link);
|
2026-07-23 01:46:37 +02:00
|
|
|
}
|
|
|
|
|
log_stamp(); fprintf(stderr, CLR_YELLOW "CHANGELOG %s from %s (count=%d, showed=%d)" CLR_RESET "\n", reply_target, src_nick, count, showed);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|