zynk/cmd_changelog.h

107 lines
4.2 KiB
C
Raw Permalink Normal View History

#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;
2026-07-25 20:37:07 +02:00
if (rate_limit_check(src_nick, reply_target, current_userhost) < 0) return 1;
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];
snprintf(tmp, sizeof tmp, "https://%.*s/%s", (int)(path - host), host, path + 1);
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");
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];
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++) {
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_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_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;
}
#endif