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.
This commit is contained in:
parent
e51a77123e
commit
feccad5853
21 changed files with 95 additions and 78 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
|
@ -1,4 +1,14 @@
|
|||
2026.07.24 - Secure file ops, TLS hardening, input validation, security docs. (0.40.7)
|
||||
26.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.
|
||||
|
||||
26.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.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
project(zynk VERSION 0.40.7 LANGUAGES C)
|
||||
project(zynk VERSION 0.40.8 LANGUAGES C)
|
||||
|
||||
# Prefer C99; the code is compatible with C99/C11
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
|
|
|
|||
14
ai.h
14
ai.h
|
|
@ -234,17 +234,17 @@ void ai_child_task(Session *s, long long id, const char *agent) {
|
|||
void ai_ask(Session *s, const char *question, const char *reply_target, const char *src_nick, const char *agent) {
|
||||
if (rate_limit_check(src_nick, reply_target) < 0) return;
|
||||
if (pending_ai_pid > 0) {
|
||||
irc_msg(s, reply_target, "Already processing an AI query, please wait.");
|
||||
irc_reply(s, reply_target, src_nick, "Already processing an AI query, please wait.");
|
||||
return;
|
||||
}
|
||||
db_update_lastq(question);
|
||||
long long id = 0;
|
||||
if (ai_start(question, reply_target, src_nick, &id) != 0) {
|
||||
irc_msg(s, reply_target, "Error starting AI query");
|
||||
irc_reply(s, reply_target, src_nick, "Error starting AI query");
|
||||
return;
|
||||
}
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) { ai_reset(id); irc_msg(s, reply_target, "Error forking"); return; }
|
||||
if (pid == -1) { ai_reset(id); irc_reply(s, reply_target, src_nick, "Error forking"); return; }
|
||||
if (pid == 0) { ai_child_task(s, id, agent); }
|
||||
pending_ai_pid = pid;
|
||||
pending_ai_id = id;
|
||||
|
|
@ -294,7 +294,7 @@ void ai_check_completion(Session *s) {
|
|||
while (pos < alen) {
|
||||
size_t end = pos + AI_CHUNK_MAX;
|
||||
if (end >= alen) {
|
||||
irc_msg(s, target, answer + pos);
|
||||
irc_reply(s, target, nick, answer + pos);
|
||||
chunks++;
|
||||
break;
|
||||
}
|
||||
|
|
@ -302,7 +302,7 @@ void ai_check_completion(Session *s) {
|
|||
if (end <= pos) end = pos + AI_CHUNK_MAX;
|
||||
char save = answer[end];
|
||||
answer[end] = 0;
|
||||
irc_msg(s, target, answer + pos);
|
||||
irc_reply(s, target, nick, answer + pos);
|
||||
answer[end] = save;
|
||||
pos = end + 1;
|
||||
chunks++;
|
||||
|
|
@ -327,11 +327,11 @@ void ai_check_completion(Session *s) {
|
|||
if (target) {
|
||||
char msg[96];
|
||||
snprintf(msg, sizeof msg, "AI query timed out after %d seconds.", AI_TIMEOUT);
|
||||
irc_msg(s, target, msg);
|
||||
irc_reply(s, target, nick, msg);
|
||||
log_stamp(); fprintf(stderr, CLR_RED "AI done PID %d from %s: timed out for %s" CLR_RESET "\n", done_pid, nick ? nick : "?", target);
|
||||
}
|
||||
} else if (target) {
|
||||
irc_msg(s, target, "AI query failed.");
|
||||
irc_reply(s, target, nick, "AI query failed.");
|
||||
log_stamp(); fprintf(stderr, CLR_RED "AI done PID %d from %s: failed for %s" CLR_RESET "\n", done_pid, nick ? nick : "?", target);
|
||||
}
|
||||
free(target);
|
||||
|
|
|
|||
2
cmd_ai.h
2
cmd_ai.h
|
|
@ -24,7 +24,7 @@ int cmd_ai(Session *s, const char *msg, const char *reply_target, const char *sr
|
|||
const char *rest = msg[3] == ' ' ? msg + 4 : "";
|
||||
while (*rest == ' ') rest++;
|
||||
if (!*rest) {
|
||||
irc_msg(s, reply_target, "Usage: !ai <question>");
|
||||
irc_reply(s, reply_target, src_nick, "Usage: !ai <question>");
|
||||
return 1;
|
||||
}
|
||||
char question[2048];
|
||||
|
|
|
|||
18
cmd_calc.h
18
cmd_calc.h
|
|
@ -26,24 +26,24 @@ int cmd_calc(Session *s, const char *msg, const char *reply_target, const char *
|
|||
const char *rest = msg[5] == ' ' ? msg + 6 : "";
|
||||
while (*rest == ' ') rest++;
|
||||
if (!*rest) {
|
||||
irc_msg(s, reply_target, "Usage: !calc <expression> (e.g. !calc 2+2, !calc (3*7)+1)");
|
||||
irc_reply(s, reply_target, src_nick, "Usage: !calc <expression> (e.g. !calc 2+2, !calc (3*7)+1)");
|
||||
return 1;
|
||||
}
|
||||
size_t expr_len = strlen(rest);
|
||||
if (expr_len > 256) {
|
||||
irc_msg(s, reply_target, "Calc: expression too long");
|
||||
irc_reply(s, reply_target, src_nick, "Calc: expression too long");
|
||||
return 1;
|
||||
}
|
||||
for (const unsigned char *p = (const unsigned char *)rest; *p; p++) {
|
||||
if (!isdigit(*p) && !strchr(".()+-*/%^ \t", *p)) {
|
||||
irc_msg(s, reply_target, "Calc: only numeric expressions are allowed");
|
||||
irc_reply(s, reply_target, src_nick, "Calc: only numeric expressions are allowed");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int in_pipe[2], out_pipe[2];
|
||||
if (pipe(in_pipe) < 0 || pipe(out_pipe) < 0) { irc_msg(s, reply_target, "Calc: internal error"); return 1; }
|
||||
if (pipe(in_pipe) < 0 || pipe(out_pipe) < 0) { irc_reply(s, reply_target, src_nick, "Calc: internal error"); return 1; }
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) { close(in_pipe[0]); close(in_pipe[1]); close(out_pipe[0]); close(out_pipe[1]); irc_msg(s, reply_target, "Calc: fork failed"); return 1; }
|
||||
if (pid < 0) { close(in_pipe[0]); close(in_pipe[1]); close(out_pipe[0]); close(out_pipe[1]); irc_reply(s, reply_target, src_nick, "Calc: fork failed"); return 1; }
|
||||
if (pid == 0) {
|
||||
close(in_pipe[1]);
|
||||
close(out_pipe[0]);
|
||||
|
|
@ -62,7 +62,7 @@ int cmd_calc(Session *s, const char *msg, const char *reply_target, const char *
|
|||
close(out_pipe[0]);
|
||||
kill(pid, SIGTERM);
|
||||
waitpid(pid, NULL, 0);
|
||||
irc_msg(s, reply_target, "Calc: evaluation failed");
|
||||
irc_reply(s, reply_target, src_nick, "Calc: evaluation failed");
|
||||
return 1;
|
||||
}
|
||||
close(in_pipe[1]);
|
||||
|
|
@ -85,11 +85,9 @@ int cmd_calc(Session *s, const char *msg, const char *reply_target, const char *
|
|||
char *nl = strchr(buf, '\n');
|
||||
if (nl) *nl = 0;
|
||||
if (len == 0) {
|
||||
irc_msg(s, reply_target, "Calc: no result (check expression syntax)");
|
||||
irc_reply(s, reply_target, src_nick, "Calc: no result (check expression syntax)");
|
||||
} else {
|
||||
char resp[640];
|
||||
snprintf(resp, sizeof resp, "%s: %s", src_nick, buf);
|
||||
irc_msg(s, reply_target, resp);
|
||||
irc_reply(s, reply_target, src_nick, buf);
|
||||
}
|
||||
log_stamp(); fprintf(stderr, CLR_YELLOW "CALC %s from %s:" CLR_RESET " %s\n", reply_target, src_nick, rest);
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ int cmd_changelog(Session *s, const char *msg, const char *reply_target, const c
|
|||
pclose(fp);
|
||||
}
|
||||
FILE *f = fopen("CHANGELOG.md", "r");
|
||||
if (!f) { irc_msg(s, reply_target, "changelog: CHANGELOG.md not found"); return 1; }
|
||||
if (!f) { irc_reply(s, reply_target, src_nick, "changelog: CHANGELOG.md not found"); return 1; }
|
||||
char *line = NULL;
|
||||
size_t cap = 0;
|
||||
const char *headers[256];
|
||||
|
|
@ -89,16 +89,16 @@ int cmd_changelog(Session *s, const char *msg, const char *reply_target, const c
|
|||
int showed = count < total ? count : total;
|
||||
struct timespec ts_msg = { .tv_sec = 0, .tv_nsec = 200000000 };
|
||||
for (int i = 0; i < showed; i++) {
|
||||
irc_msg(s, reply_target, headers[i]);
|
||||
irc_reply(s, reply_target, src_nick, headers[i]);
|
||||
free((void *)headers[i]);
|
||||
if (i + 1 < showed) nanosleep(&ts_msg, NULL);
|
||||
}
|
||||
for (int i = showed; i < total; i++) free((void *)headers[i]);
|
||||
if (total == 0) { irc_msg(s, reply_target, "changelog: no entries found"); return 1; }
|
||||
if (total == 0) { irc_reply(s, reply_target, src_nick, "changelog: no entries found"); return 1; }
|
||||
if (*remote_url) {
|
||||
char link[1024];
|
||||
snprintf(link, sizeof link, "Full changelog: %s/blob/master/CHANGELOG.md", remote_url);
|
||||
irc_msg(s, reply_target, link);
|
||||
irc_reply(s, reply_target, src_nick, link);
|
||||
}
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ int cmd_forecast(Session *s, const char *msg, const char *reply_target, const ch
|
|||
if (body) {
|
||||
char *line = strtok(body, "\n");
|
||||
while (line) {
|
||||
irc_msg(s, reply_target, line);
|
||||
irc_reply(s, reply_target, src_nick, line);
|
||||
line = strtok(NULL, "\n");
|
||||
if (line) {
|
||||
struct timespec ts_msg = { .tv_sec = 0, .tv_nsec = 200000000 };
|
||||
|
|
@ -356,7 +356,7 @@ int cmd_forecast(Session *s, const char *msg, const char *reply_target, const ch
|
|||
}
|
||||
free(body);
|
||||
} else {
|
||||
irc_msg(s, reply_target, "Forecast: could not fetch data");
|
||||
irc_reply(s, reply_target, src_nick, "Forecast: could not fetch data");
|
||||
}
|
||||
} else {
|
||||
char *body = fetch_forecast(city_url, city);
|
||||
|
|
@ -367,7 +367,7 @@ int cmd_forecast(Session *s, const char *msg, const char *reply_target, const ch
|
|||
} else {
|
||||
snprintf(resp, sizeof resp, "Forecast: could not fetch data");
|
||||
}
|
||||
irc_msg(s, reply_target, resp);
|
||||
irc_reply(s, reply_target, src_nick, resp);
|
||||
}
|
||||
log_stamp(); fprintf(stderr, CLR_MAGENTA "FORECAST %s from %s (full=%d):" CLR_RESET " %s\n", reply_target, src_nick, full, city);
|
||||
return 1;
|
||||
|
|
|
|||
14
cmd_gitlog.h
14
cmd_gitlog.h
|
|
@ -73,9 +73,9 @@ int cmd_gitlog(Session *s, const char *msg, const char *reply_target, const char
|
|||
pclose(fp);
|
||||
}
|
||||
int out_pipe[2];
|
||||
if (pipe(out_pipe) < 0) { irc_msg(s, reply_target, "gitlog: internal error"); return 1; }
|
||||
if (pipe(out_pipe) < 0) { irc_reply(s, reply_target, src_nick, "gitlog: internal error"); return 1; }
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) { close(out_pipe[0]); close(out_pipe[1]); irc_msg(s, reply_target, "gitlog: fork failed"); return 1; }
|
||||
if (pid < 0) { close(out_pipe[0]); close(out_pipe[1]); irc_reply(s, reply_target, src_nick, "gitlog: fork failed"); return 1; }
|
||||
if (pid == 0) {
|
||||
close(out_pipe[0]);
|
||||
dup2(out_pipe[1], STDOUT_FILENO);
|
||||
|
|
@ -105,7 +105,7 @@ int cmd_gitlog(Session *s, const char *msg, const char *reply_target, const char
|
|||
while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r')) len--;
|
||||
buf[len] = 0;
|
||||
if (len == 0) {
|
||||
irc_msg(s, reply_target, "gitlog: no commits found");
|
||||
irc_reply(s, reply_target, src_nick, "gitlog: no commits found");
|
||||
return 1;
|
||||
}
|
||||
char *line = strtok(buf, "\n");
|
||||
|
|
@ -133,17 +133,17 @@ int cmd_gitlog(Session *s, const char *msg, const char *reply_target, const char
|
|||
if (*remote_url) {
|
||||
char link[4096];
|
||||
snprintf(link, sizeof link, "%s - %s/commit/%s", display, remote_url, hash);
|
||||
irc_msg(s, reply_target, link);
|
||||
irc_reply(s, reply_target, src_nick, link);
|
||||
} else {
|
||||
irc_msg(s, reply_target, display);
|
||||
irc_reply(s, reply_target, src_nick, display);
|
||||
}
|
||||
} else {
|
||||
collapse_spaces(rest);
|
||||
if (*rest) irc_msg(s, reply_target, rest);
|
||||
if (*rest) irc_reply(s, reply_target, src_nick, rest);
|
||||
}
|
||||
} else {
|
||||
collapse_spaces(line);
|
||||
if (*line) irc_msg(s, reply_target, line);
|
||||
if (*line) irc_reply(s, reply_target, src_nick, line);
|
||||
}
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,24 +32,24 @@ void cmd_greeting_or_chat(Session *s, const char *msg, const char *reply_target,
|
|||
char resp[1024];
|
||||
int matched = 0;
|
||||
if (strcasecmp(cmd, "hi") == 0 || strcasecmp(cmd, "hey") == 0 || strcasecmp(cmd, "hello") == 0) {
|
||||
snprintf(resp, sizeof resp, "Hi, %s!", src_nick); matched = 1;
|
||||
snprintf(resp, sizeof resp, "Hi!"); matched = 1;
|
||||
} else if (strcasecmp(cmd, "bye") == 0 || strcasecmp(cmd, "goodbye") == 0) {
|
||||
snprintf(resp, sizeof resp, "Bye, %s!", src_nick); matched = 1;
|
||||
snprintf(resp, sizeof resp, "Bye!"); matched = 1;
|
||||
} else if (strcasecmp(cmd, "jojo") == 0) {
|
||||
snprintf(resp, sizeof resp, "Jo, %s!", src_nick); matched = 1;
|
||||
snprintf(resp, sizeof resp, "Jo!"); matched = 1;
|
||||
} else if (strcasecmp(cmd, "whatsup") == 0) {
|
||||
snprintf(resp, sizeof resp, "Not much, %s!", src_nick); matched = 1;
|
||||
snprintf(resp, sizeof resp, "Not much!"); matched = 1;
|
||||
} else if (strcasecmp(cmd, "waddup") == 0) {
|
||||
snprintf(resp, sizeof resp, "Yo %s!", src_nick); matched = 1;
|
||||
snprintf(resp, sizeof resp, "Yo!"); matched = 1;
|
||||
} else if (strcasecmp(cmd, "waddup yo") == 0) {
|
||||
snprintf(resp, sizeof resp, "Yo!"); matched = 1;
|
||||
} else if (strcasecmp(cmd, "deine muddi") == 0 || strcasecmp(cmd, "deine mutter") == 0) {
|
||||
snprintf(resp, sizeof resp, "Ey, %s, lass das!", src_nick); matched = 1;
|
||||
snprintf(resp, sizeof resp, "Ey, lass das!"); matched = 1;
|
||||
} else if (strcasecmp(cmd, "penis") == 0) {
|
||||
snprintf(resp, sizeof resp, "..."); matched = 1;
|
||||
}
|
||||
if (matched) {
|
||||
irc_msg(s, reply_target, resp);
|
||||
irc_reply(s, reply_target, src_nick, resp);
|
||||
log_stamp(); fprintf(stderr, CLR_CYAN "CHAT %s from %s:" CLR_RESET " %s\n", reply_target, src_nick, resp);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ int cmd_help(Session *s, const char *msg, const char *reply_target, const char *
|
|||
p += n; rem -= n;
|
||||
#endif
|
||||
if (p > buf && p[-1] == ',') p[-1] = '\0';
|
||||
irc_msg(s, reply_target, buf);
|
||||
irc_reply(s, reply_target, src_nick, buf);
|
||||
log_stamp(); fprintf(stderr, CLR_CYAN "HELP from %s in %s" CLR_RESET "\n", src_nick, reply_target);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ int cmd_ping(Session *s, const char *msg, const char *reply_target, const char *
|
|||
if (strncmp(msg, "!ping", 5) != 0) return 0;
|
||||
if (msg[5] != ' ' && msg[5] != '\0') return 0;
|
||||
if (rate_limit_check(src_nick, reply_target) < 0) return 1;
|
||||
char pong_buf[96]; snprintf(pong_buf, sizeof pong_buf, "%s: pong!", src_nick);
|
||||
irc_msg(s, reply_target, pong_buf);
|
||||
irc_reply(s, reply_target, src_nick, "pong!");
|
||||
log_stamp(); fprintf(stderr, CLR_YELLOW "PING from %s in %s" CLR_RESET "\n", src_nick, reply_target);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
10
cmd_seen.h
10
cmd_seen.h
|
|
@ -27,7 +27,7 @@ int cmd_seen(Session *s, const char *msg, const char *reply_target, const char *
|
|||
const char *rest = msg + 6;
|
||||
while (*rest == ' ') rest++;
|
||||
if (!*rest) {
|
||||
irc_msg(s, reply_target, "Usage: !seen <nick>");
|
||||
irc_reply(s, reply_target, src_nick, "Usage: !seen <nick>");
|
||||
return 1;
|
||||
}
|
||||
char target[32];
|
||||
|
|
@ -35,21 +35,21 @@ int cmd_seen(Session *s, const char *msg, const char *reply_target, const char *
|
|||
while (*rest && *rest != ' ' && i < 31) target[i++] = *rest++;
|
||||
target[i] = 0;
|
||||
if (strcasecmp(target, src_nick) == 0) {
|
||||
irc_msg(s, reply_target, "You are right here, duh!");
|
||||
irc_reply(s, reply_target, src_nick, "You are right here, duh!");
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(target, s->nick) == 0) {
|
||||
irc_msg(s, reply_target, "I am right here!");
|
||||
irc_reply(s, reply_target, src_nick, "I am right here!");
|
||||
return 1;
|
||||
}
|
||||
char *result = seen_lookup(target);
|
||||
if (result) {
|
||||
irc_msg(s, reply_target, result);
|
||||
irc_reply(s, reply_target, src_nick, result);
|
||||
free(result);
|
||||
} else {
|
||||
char resp[128];
|
||||
snprintf(resp, sizeof resp, "I have never seen %s", target);
|
||||
irc_msg(s, reply_target, resp);
|
||||
irc_reply(s, reply_target, src_nick, resp);
|
||||
}
|
||||
log_stamp(); fprintf(stderr, CLR_CYAN "SEEN %s from %s in %s" CLR_RESET "\n", target, src_nick, reply_target);
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ int cmd_stock(Session *s, const char *msg, const char *reply_target, const char
|
|||
const char *rest = msg[6] == ' ' ? msg + 7 : "";
|
||||
while (*rest == ' ') rest++;
|
||||
if (!*rest) {
|
||||
irc_msg(s, reply_target, "Usage: !stock <SYMBOL>[,SYMBOL...] (max 3, e.g. !stock AAPL or !stock AAPL,MSFT,GOOGL)");
|
||||
irc_reply(s, reply_target, src_nick, "Usage: !stock <SYMBOL>[,SYMBOL...] (max 3, e.g. !stock AAPL or !stock AAPL,MSFT,GOOGL)");
|
||||
return 1;
|
||||
}
|
||||
char buf[2048];
|
||||
|
|
@ -103,7 +103,7 @@ int cmd_stock(Session *s, const char *msg, const char *reply_target, const char
|
|||
}
|
||||
if (!buf[0]) snprintf(buf, sizeof buf, "Stock: could not fetch data");
|
||||
collapse_spaces(buf);
|
||||
irc_msg(s, reply_target, buf);
|
||||
irc_reply(s, reply_target, src_nick, buf);
|
||||
log_stamp(); fprintf(stderr, CLR_MAGENTA "STOCK %s from %s:" CLR_RESET " %s\n", reply_target, src_nick, buf);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ int cmd_tell(Session *s, const char *msg, const char *reply_target, const char *
|
|||
const char *rest = msg + 6;
|
||||
while (*rest == ' ') rest++;
|
||||
if (!*rest) {
|
||||
irc_msg(s, reply_target, "Usage: !tell <nick> <message>");
|
||||
irc_reply(s, reply_target, src_nick, "Usage: !tell <nick> <message>");
|
||||
return 1;
|
||||
}
|
||||
char target[32];
|
||||
|
|
@ -35,17 +35,17 @@ int cmd_tell(Session *s, const char *msg, const char *reply_target, const char *
|
|||
target[i] = 0;
|
||||
while (*rest == ' ') rest++;
|
||||
if (!*rest) {
|
||||
irc_msg(s, reply_target, "Usage: !tell <nick> <message>");
|
||||
irc_reply(s, reply_target, src_nick, "Usage: !tell <nick> <message>");
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(target, src_nick) == 0) {
|
||||
irc_msg(s, reply_target, "You can't tell yourself something!");
|
||||
irc_reply(s, reply_target, src_nick, "You can't tell yourself something!");
|
||||
return 1;
|
||||
}
|
||||
tell_add(src_nick, target, rest);
|
||||
char resp[256];
|
||||
snprintf(resp, sizeof resp, "Ok, I'll tell %s when they're around.", target);
|
||||
irc_msg(s, reply_target, resp);
|
||||
irc_reply(s, reply_target, src_nick, resp);
|
||||
log_stamp(); fprintf(stderr, CLR_CYAN "TELL %s from %s in %s:" CLR_RESET " -> %s: %s\n", reply_target, src_nick, reply_target, target, rest);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
12
cmd_time.h
12
cmd_time.h
|
|
@ -117,7 +117,6 @@ int cmd_time(Session *s, const char *msg, const char *reply_target, const char *
|
|||
if (strncmp(msg, "!time", 5) != 0) return 0;
|
||||
if (msg[5] != ' ' && msg[5] != '\0') return 0;
|
||||
if (rate_limit_check(src_nick, reply_target) < 0) return 1;
|
||||
char resp[256];
|
||||
const char *rest = msg[5] == ' ' ? msg + 6 : "";
|
||||
while (*rest == ' ') rest++;
|
||||
if (*rest) {
|
||||
|
|
@ -145,7 +144,7 @@ int cmd_time(Session *s, const char *msg, const char *reply_target, const char *
|
|||
if (system_works) {
|
||||
char buf[64];
|
||||
strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S", &local_tm);
|
||||
snprintf(resp, sizeof resp, "%s: %s", src_nick, buf);
|
||||
irc_reply(s, reply_target, src_nick, buf);
|
||||
} else {
|
||||
const TzEntry *tz = tz_lookup(tzname);
|
||||
if (tz) {
|
||||
|
|
@ -159,9 +158,11 @@ int cmd_time(Session *s, const char *msg, const char *reply_target, const char *
|
|||
}
|
||||
char buf[128];
|
||||
tz_format(now, off, tzname, off != tz->off, buf, sizeof buf);
|
||||
snprintf(resp, sizeof resp, "%s: %s", src_nick, buf);
|
||||
irc_reply(s, reply_target, src_nick, buf);
|
||||
} else {
|
||||
snprintf(resp, sizeof resp, "%s: Unknown timezone: %s", src_nick, tzname);
|
||||
char resp[256];
|
||||
snprintf(resp, sizeof resp, "Unknown timezone: %s", tzname);
|
||||
irc_reply(s, reply_target, src_nick, resp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -169,9 +170,8 @@ int cmd_time(Session *s, const char *msg, const char *reply_target, const char *
|
|||
struct tm *tm = localtime(&now);
|
||||
char buf[64];
|
||||
strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S", tm);
|
||||
snprintf(resp, sizeof resp, "%s: %s", src_nick, buf);
|
||||
irc_reply(s, reply_target, src_nick, buf);
|
||||
}
|
||||
irc_msg(s, reply_target, resp);
|
||||
log_stamp(); fprintf(stderr, CLR_YELLOW "TIME from %s in %s" CLR_RESET "\n", src_nick, reply_target);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
10
cmd_uptime.h
10
cmd_uptime.h
|
|
@ -28,14 +28,14 @@ int cmd_uptime(Session *s, const char *msg, const char *reply_target, const char
|
|||
long secs = diff % 60;
|
||||
char buf[128];
|
||||
if (days > 0)
|
||||
snprintf(buf, sizeof buf, "%s: up %ldd %ldh %ldm %lds", src_nick, days, hours, mins, secs);
|
||||
snprintf(buf, sizeof buf, "up %ldd %ldh %ldm %lds", days, hours, mins, secs);
|
||||
else if (hours > 0)
|
||||
snprintf(buf, sizeof buf, "%s: up %ldh %ldm %lds", src_nick, hours, mins, secs);
|
||||
snprintf(buf, sizeof buf, "up %ldh %ldm %lds", hours, mins, secs);
|
||||
else if (mins > 0)
|
||||
snprintf(buf, sizeof buf, "%s: up %ldm %lds", src_nick, mins, secs);
|
||||
snprintf(buf, sizeof buf, "up %ldm %lds", mins, secs);
|
||||
else
|
||||
snprintf(buf, sizeof buf, "%s: up %lds", src_nick, secs);
|
||||
irc_msg(s, reply_target, buf);
|
||||
snprintf(buf, sizeof buf, "up %lds", secs);
|
||||
irc_reply(s, reply_target, src_nick, buf);
|
||||
log_stamp(); fprintf(stderr, CLR_YELLOW "UPTIME from %s in %s" CLR_RESET "\n", src_nick, reply_target);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ int cmd_version(Session *s, const char *msg, const char *reply_target, const cha
|
|||
if (msg[8] != ' ' && msg[8] != '\0') return 0;
|
||||
if (rate_limit_check(src_nick, reply_target) < 0) return 1;
|
||||
char buf[96]; snprintf(buf, sizeof buf, "zynk %s", VERSION);
|
||||
irc_msg(s, reply_target, buf);
|
||||
irc_reply(s, reply_target, src_nick, buf);
|
||||
log_stamp(); fprintf(stderr, CLR_BLUE "VERSION from %s in %s" CLR_RESET "\n", src_nick, reply_target);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ int cmd_weather(Session *s, const char *msg, const char *reply_target, const cha
|
|||
} else {
|
||||
snprintf(resp, sizeof resp, "Weather: could not fetch data");
|
||||
}
|
||||
irc_msg(s, reply_target, resp);
|
||||
irc_reply(s, reply_target, src_nick, resp);
|
||||
log_stamp(); fprintf(stderr, CLR_MAGENTA "WEATHER %s from %s:" CLR_RESET " %s\n", reply_target, src_nick, resp);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
17
cmd_zynk.h
17
cmd_zynk.h
|
|
@ -31,16 +31,16 @@ int cmd_zynk(Session *s, const char *msg, const char *reply_target, const char *
|
|||
if (msg[5] == '\0') {
|
||||
char *rand = db_get_random();
|
||||
if (rand) {
|
||||
irc_msg(s, reply_target, rand);
|
||||
irc_reply(s, reply_target, src_nick, rand);
|
||||
log_stamp(); fprintf(stderr, CLR_MAGENTA "ZYNK %s from %s:" CLR_RESET " random %s\n", reply_target, src_nick, rand);
|
||||
free(rand);
|
||||
} else {
|
||||
irc_msg(s, reply_target, "No zynkss saved yet.");
|
||||
irc_reply(s, reply_target, src_nick, "No zynkss saved yet.");
|
||||
}
|
||||
return 1;
|
||||
} else if (msg[5] == '!') {
|
||||
if (msg[6] == '\0') {
|
||||
irc_msg(s, reply_target, "Usage: !zynk! <key> = <value>");
|
||||
irc_reply(s, reply_target, src_nick, "Usage: !zynk! <key> = <value>");
|
||||
return 1;
|
||||
}
|
||||
force = 1;
|
||||
|
|
@ -50,7 +50,7 @@ int cmd_zynk(Session *s, const char *msg, const char *reply_target, const char *
|
|||
}
|
||||
while (*rest == ' ') rest++;
|
||||
if (!*rest) {
|
||||
irc_msg(s, reply_target, "Usage: !zynk <key> [= <value>]");
|
||||
irc_reply(s, reply_target, src_nick, "Usage: !zynk <key> [= <value>]");
|
||||
return 1;
|
||||
}
|
||||
const char *eq = strstr(rest, " = ");
|
||||
|
|
@ -58,7 +58,7 @@ int cmd_zynk(Session *s, const char *msg, const char *reply_target, const char *
|
|||
if (eq) {
|
||||
char key[256];
|
||||
size_t kl = eq - rest;
|
||||
if (kl == 0) { irc_msg(s, reply_target, "Key cannot be empty"); return 1; }
|
||||
if (kl == 0) { irc_reply(s, reply_target, src_nick, "Key cannot be empty"); return 1; }
|
||||
if (kl >= sizeof key) kl = sizeof key - 1;
|
||||
memcpy(key, rest, kl); key[kl] = 0;
|
||||
for (char *p = key; *p; p++) *p = tolower((unsigned char)*p);
|
||||
|
|
@ -66,14 +66,13 @@ int cmd_zynk(Session *s, const char *msg, const char *reply_target, const char *
|
|||
char *existing = db_get_raw(key);
|
||||
if (existing) {
|
||||
free(existing);
|
||||
snprintf(resp, sizeof resp, "zynk already exists (use !zynk! to overwrite)");
|
||||
irc_msg(s, reply_target, resp);
|
||||
irc_reply(s, reply_target, src_nick, "zynk already exists (use !zynk! to overwrite)");
|
||||
log_stamp(); fprintf(stderr, CLR_MAGENTA "ZYNK %s from %s:" CLR_RESET " blocked re-add of %s\n", reply_target, src_nick, key);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (tgt[0] == '#' && !chan_is_op(tgt, src_nick)) {
|
||||
irc_msg(s, reply_target, "You need op to overwrite a zynk");
|
||||
irc_reply(s, reply_target, src_nick, "You need op to overwrite a zynk");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -98,7 +97,7 @@ int cmd_zynk(Session *s, const char *msg, const char *reply_target, const char *
|
|||
snprintf(resp, sizeof resp, "%s: not found", key);
|
||||
}
|
||||
}
|
||||
irc_msg(s, reply_target, resp);
|
||||
irc_reply(s, reply_target, src_nick, resp);
|
||||
log_stamp(); fprintf(stderr, CLR_MAGENTA "ZYNK %s from %s:" CLR_RESET " %s\n", reply_target, src_nick, resp);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
12
irc.h
12
irc.h
|
|
@ -242,6 +242,16 @@ void irc_msg(Session *s, const char *t, const char *txt) {
|
|||
free(clean);
|
||||
}
|
||||
|
||||
void irc_reply(Session *s, const char *target, const char *nick, const char *txt) {
|
||||
if (target && target[0] == '#' && nick && *nick) {
|
||||
char prefixed[2048];
|
||||
snprintf(prefixed, sizeof prefixed, "%s: %s", nick, txt);
|
||||
irc_msg(s, target, prefixed);
|
||||
} else {
|
||||
irc_msg(s, target, txt ? txt : "");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* irc_nick - Send a NICK command to change the bot's nickname.
|
||||
*
|
||||
|
|
@ -391,7 +401,7 @@ static void handle_privmsg(Session *s, const char *params, const char *src_nick)
|
|||
cmd_greeting_or_chat(s, msg, reply_target, src_nick);
|
||||
#endif
|
||||
if (msg[0] == '!' && msg[1] && isalpha((unsigned char)msg[1]))
|
||||
irc_msg(s, reply_target, "Not implemented...");
|
||||
irc_reply(s, reply_target, src_nick, "Not implemented...");
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
1
zynk.h
1
zynk.h
|
|
@ -171,6 +171,7 @@ extern time_t start_time;
|
|||
|
||||
/* 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, ...);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue