zynk/zynk.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

197 lines
5.7 KiB
C

#ifndef ZYNK_H
#define ZYNK_H
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <time.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netdb.h>
#include <dlfcn.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <openssl/ssl.h>
#ifndef ZYNK_VERSION
#define ZYNK_VERSION "0.0.0"
#endif
#define VERSION ZYNK_VERSION
#define DEFAULT_SERVER "irc.libera.chat"
#define OPENCODE_BIN "/usr/bin/opencode"
#define CLR_RESET "\033[0m"
#define CLR_DIM "\033[2m"
#define CLR_RED "\033[31m"
#define CLR_GREEN "\033[32m"
#define CLR_YELLOW "\033[33m"
#define CLR_BLUE "\033[34m"
#define CLR_MAGENTA "\033[35m"
#define CLR_CYAN "\033[36m"
enum {
RBUF_SZ = 16384,
DEF_PORT = 6697,
POLL_MS = 100,
AI_TIMEOUT = 600,
MAX_CHANS = 32,
MAX_CHAN_OPS = 64,
RATE_LIMIT_WINDOW = 10,
RATE_LIMIT_MAX = 32,
MAX_LINES_PER_POLL = 50,
MAX_HISTORY = 12,
GITLOG_COUNT = 1,
GITLOG_MAX = 10,
CHANGELOG_COUNT = 1,
CHANGELOG_MAX = 10,
OUTGOING_DELAY_MS = 100,
TELL_DELIVER_MAX = 5,
AI_COOLDOWN_SECS = 5,
};
typedef struct {
int fd;
char host[256];
int port;
char nick[32];
char user[32];
char real[256];
char pass[256];
char auto_join[256];
int connected;
char rbuf[16384];
int rlen;
int dropping_line;
int poll_lines_left;
SSL *ssl;
int use_tls;
int code_restart;
} Session;
/* SQLite3 dynamic loading */
extern void *sqlite_lib;
extern int (*db_open)(const char*,void**);
extern int (*db_exec)(void*,const char*,int(*)(void*,int,char**,char**),void*,char**);
extern int (*db_close)(void*);
extern int (*db_prepare)(void*,const char*,int,void**,const char**);
extern int (*db_bind_text)(void*,int,const char*,int,void(*)(void*));
extern int (*db_step)(void*);
extern int (*db_finalize)(void*);
extern const unsigned char *(*db_column_text)(void*,int);
extern void (*db_free)(void*);
extern int (*db_bind_int64)(void*,int,long long);
extern long long (*db_last_rowid)(void*);
#define SQLITE_OK 0
#define SQLITE_ERROR 1
#define SQLITE_DONE 101
#define SQLITE_ROW 100
#define SQLITE_INTEGER 1
#define SQLITE_FLOAT 2
#define SQLITE_TEXT 3
#define SQLITE_BLOB 4
#define SQLITE_NULL 5
#define SQLITE_STATIC ((void(*)(void*))0)
#define SQLITE_TRANSIENT ((void(*)(void*))-1)
typedef struct { void *ptr; } sqlite3;
#define DB ((sqlite3*)db_handle)
extern void *db_handle;
/* DB key-value operations */
int db_set(const char *key, const char *value, const char *nick);
char *db_get_raw(const char *key);
char *db_get(const char *key);
char *db_get_random(void);
void db_rotate_history(const char *prefix, const char *value);
#define db_update_lastq(v) db_rotate_history("lastq", v)
#define db_update_lasta(v) db_rotate_history("lasta", v)
/* DB AI pending operations */
int ai_start(const char *question, const char *target, const char *nick, long long *out_id);
char *ai_get_question(long long id);
int ai_set_answer(long long id, const char *answer);
char *ai_get_answer(long long id);
char *ai_get_nick(long long id);
void ai_reset(long long id);
/* DB seen/tell operations */
void seen_update(const char *nick, const char *channel, const char *action);
char *seen_lookup(const char *nick);
void tell_add(const char *from, const char *to, const char *msg);
/* Channel op tracking */
typedef struct { char name[64]; char ops[MAX_CHAN_OPS][32]; int nops; } ChanEntry;
extern ChanEntry chans[MAX_CHANS];
extern int nchans;
ChanEntry *chan_get(const char *name);
ChanEntry *chan_get_or_add(const char *name);
int chan_is_op(const char *name, const char *nick);
void chan_add_op(const char *name, const char *nick);
void chan_del_op(const char *name, const char *nick);
void chan_rename_op(const char *oldnick, const char *newnick);
/* Rate limiting */
int rate_limit_check(const char *nick, const char *channel, const char *userhost);
extern char current_userhost[128];
/* Text utilities */
void strip_ai_phrases(char *s);
void strip_formatting(char *s);
void strip_opencode_header(char *s);
int has_crlf(const char *s);
int has_ctl(const char *s);
int secure_state_open(const char *path, int flags);
int valid_irc_word(const char *s);
void normalize_city(const char *in, char *display, size_t dsz, char *url, size_t usz);
void collapse_spaces(char *s);
const char *json_find(const char *haystack, const char *key);
/* Curl / fetch */
char *fetch_url(const char *url);
char *fetch_forecast(const char *city_url, const char *city_display);
/* Logging */
void log_stamp(void);
/* Signal / globals */
extern volatile sig_atomic_t running;
extern const char *g_opencode_bin;
extern volatile pid_t pending_ai_pid;
extern volatile long long pending_ai_id;
extern time_t start_time;
extern time_t last_ai_completion;
/* IRC protocol helpers */
void irc_msg(Session *s, const char *t, const char *txt);
void irc_reply(Session *s, const char *target, const char *nick, const char *txt);
void irc_join(Session *s, const char *chan);
void irc_nick(Session *s, const char *nn);
int net_send(Session *s, const char *fmt, ...);
/* Hot reload */
void reload_do(Session *s, const char *reply_target, int exec_new);
/* AI pipeline */
void ai_ask(Session *s, const char *question, const char *reply_target, const char *src_nick, const char *agent);
/* Compile helper */
int try_compile_and_restart(Session *s, const char *target);
/* DB init/cleanup */
int db_init(void);
void db_close_cleanup(void);
void sqlite_cleanup(void);
/* AI child task */
void ai_child_task(Session *s, long long id, const char *agent);
#endif