zynk/cmd_rebuild.h
hanez 3b97b4d902 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

93 lines
4.2 KiB
C

#ifndef CMD_REBUILD_H
#define CMD_REBUILD_H
#include "zynk.h"
/*
* cmd_rebuild - Handle the "!rebuild" command.
*
* Requires channel op and must be used in a channel. Optionally runs
* "git pull" if the argument "pull" is provided (e.g. "!rebuild pull").
* Always runs "make clean" and "make" in sequence. If all steps succeed,
* triggers a hot reload via reload_do(). If any step fails, reports the
* error from the build output. 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.
* tgt - The message target (channel or nick).
*
* Returns: 1 if matched, 0 otherwise.
*/
int cmd_rebuild(Session *s, const char *msg, const char *reply_target, const char *src_nick, const char *tgt) {
int do_pull = (strcmp(msg, "!rebuild pull") == 0);
if (strcmp(msg, "!rebuild") != 0 && !do_pull) return 0;
if (tgt[0] != '#') {
irc_msg(s, reply_target, "!rebuild can only be used in a channel");
return 1;
}
if (!chan_is_op(tgt, src_nick)) {
irc_msg(s, reply_target, "You need op to rebuild");
return 1;
}
if (rate_limit_check(src_nick, reply_target, current_userhost) < 0) return 1;
irc_msg(s, reply_target, do_pull ? "Pulling and rebuilding..." : "Rebuilding...");
log_stamp(); fprintf(stderr, CLR_GREEN "REBUILD from %s in %s (pull=%d)" CLR_RESET "\n", src_nick, reply_target, do_pull);
int status;
char errbuf[512];
int efd = secure_state_open("/tmp/zynk_rebuild_err", O_WRONLY | O_CREAT | O_TRUNC);
if (efd < 0) {
irc_msg(s, reply_target, "Rebuild failed: cannot create secure build log");
return 1;
}
close(efd);
if (do_pull) {
status = system("git pull 2>/tmp/zynk_rebuild_err");
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
char msg_buf[640];
efd = secure_state_open("/tmp/zynk_rebuild_err", O_RDONLY);
FILE *f = efd >= 0 ? fdopen(efd, "r") : NULL;
if (f) { size_t n = fread(errbuf, 1, sizeof errbuf - 1, f); errbuf[n] = 0; fclose(f); char *nl = strchr(errbuf, '\n'); if (nl) *nl = 0; }
else snprintf(errbuf, sizeof errbuf, "unknown error");
snprintf(msg_buf, sizeof msg_buf, "Rebuild failed (git pull): %s", errbuf);
irc_msg(s, reply_target, msg_buf);
log_stamp(); fprintf(stderr, CLR_RED "REBUILD git pull failed:" CLR_RESET " %s\n", errbuf);
return 1;
}
}
status = system("make clean 2>/tmp/zynk_rebuild_err");
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
char msg_buf[640];
efd = secure_state_open("/tmp/zynk_rebuild_err", O_RDONLY);
FILE *f = efd >= 0 ? fdopen(efd, "r") : NULL;
if (f) { size_t n = fread(errbuf, 1, sizeof errbuf - 1, f); errbuf[n] = 0; fclose(f); char *nl = strchr(errbuf, '\n'); if (nl) *nl = 0; }
else snprintf(errbuf, sizeof errbuf, "unknown error");
snprintf(msg_buf, sizeof msg_buf, "Rebuild failed (make clean): %s", errbuf);
irc_msg(s, reply_target, msg_buf);
log_stamp(); fprintf(stderr, CLR_RED "REBUILD make clean failed:" CLR_RESET " %s\n", errbuf);
return 1;
}
status = system("make 2>/tmp/zynk_rebuild_err");
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
irc_msg(s, reply_target, "Build successful, restarting...");
log_stamp(); fprintf(stderr, CLR_GREEN "REBUILD successful, restarting..." CLR_RESET "\n");
int cfd = secure_state_open("/tmp/zynk_code_restart", O_WRONLY | O_CREAT | O_TRUNC);
FILE *cf = cfd >= 0 ? fdopen(cfd, "w") : NULL;
if (cf) { fprintf(cf, "%s\n", reply_target); fclose(cf); }
reload_do(s, reply_target, 1);
} else {
efd = secure_state_open("/tmp/zynk_rebuild_err", O_RDONLY);
FILE *f = efd >= 0 ? fdopen(efd, "r") : NULL;
if (f) { size_t n = fread(errbuf, 1, sizeof errbuf - 1, f); errbuf[n] = 0; fclose(f); char *nl = strchr(errbuf, '\n'); if (nl) *nl = 0; }
else snprintf(errbuf, sizeof errbuf, "unknown error");
char msg_buf[640];
snprintf(msg_buf, sizeof msg_buf, "Rebuild failed (make): %s", errbuf);
irc_msg(s, reply_target, msg_buf);
log_stamp(); fprintf(stderr, CLR_RED "REBUILD make failed:" CLR_RESET " %s\n", errbuf);
}
return 1;
}
#endif