diff options
-rw-r--r-- | pttbbs/util/angel.c | 305 |
1 files changed, 188 insertions, 117 deletions
diff --git a/pttbbs/util/angel.c b/pttbbs/util/angel.c index c36df764..8d799566 100644 --- a/pttbbs/util/angel.c +++ b/pttbbs/util/angel.c @@ -6,166 +6,237 @@ #define EXPAND_AND_QUOTE(x) QUOTE(x) #define STR_ANGELBEATS_PERF_MIN_PERIOD \ EXPAND_AND_QUOTE(ANGELBEATS_PERF_MIN_PERIOD) +#define die(format...) { fprintf(stderr, format); exit(1); } + +#define REPORT_AUTHOR "[天使公會]" +#define REPORT_SUBJECT "小天使統計資料" #ifndef PLAY_ANGEL int main(){ return 0; } #else -int total[MAX_USERS + 1]; -int (*list)[2]; -int count; -char* mailto = "SYSOP"; +const char *blacklist[] = { + // List angels that you DON'T want to be listed here. + "PttAngels", + "AngelParty", + NULL, +}; +void slurp(FILE* to, FILE* from) { + char buf[4096]; // 4K block + int count; -void readData(); -void sendResult(); -void slurp(FILE* to, FILE* from); + while ((count = fread(buf, 1, sizeof(buf), from)) > 0) { + char * p = buf; + while (count > 0) { + int i = fwrite(p, 1, count, to); -int main(int argc, char* argv[]){ - if (argc > 1) - mailto = argv[1]; + if (i <= 0) return; - readData(); - sendResult(); - return 0; + p += i; + count -= i; + } + } } void appendLogFile(FILE *output, const char *filename, - const char *prefix) { + const char *prefix, + int delete_file) { FILE *fp = fopen(filename, "r"); if (!fp) return; - remove(filename); + if (delete_file) + remove(filename); fputs(prefix, output); slurp(output, fp); fclose(fp); } -void readData(){ - int i, j; - int k; - userec_t user; - FILE* fp; - - attach_SHM(); +typedef struct { + int uid; + int is_angel; + int masters; + int masters_week; + int masters_month; + int masters_quater; +} AngelRecord; - fp = fopen(BBSHOME "/.PASSWDS", "rb"); - j = count = 0; - while (fread(&user, sizeof(userec_t), 1, fp) == 1) { - ++j; /* j == uid */ - if (user.myangel[0]) { - i = searchuser(user.myangel, NULL); - if (i) - ++total[i]; - } - if (user.userlevel & PERM_ANGEL) { - ++count; - ++total[j]; /* make all angel have total > 0 */ - } else { /* don't have PERM_ANGEL */ - total[j] = INT_MIN; - } +int buildMasterInfo(AngelRecord *rec, int num_recs) { + userec_t user; + int uid = 0, angel_uid; + FILE *fp; + int count = 0; + time4_t now = time4(NULL); + + memset(rec, 0, sizeof(*rec) * num_recs); + + fp = fopen(FN_PASSWD, "rb"); + while (fread(&user, sizeof(user), 1, fp) == 1) { + AngelRecord *r = rec + uid, *angel = NULL; + uid++; + r->uid = uid; + assert(uid <= num_recs); + if (uid % 1000 == 0) + fprintf(stderr, "."); + if (!*user.userid) + continue; + if (user.userlevel & PERM_ANGEL) { + r->is_angel = 1; + count++; + } + if (!*user.myangel) + continue; + angel_uid = searchuser(user.myangel, NULL); + if (!angel_uid) + continue; + angel = rec + (angel_uid - 1); + angel->masters++; + if (now - user.timeplayangel < DAY_SECONDS * 7) + angel->masters_week++; + if (now - user.timeplayangel < DAY_SECONDS * 30) + angel->masters_month++; + if (now - user.timeplayangel < DAY_SECONDS * 90) + angel->masters_quater++; } fclose(fp); - - list = (int(*)[2]) malloc(count * sizeof(int[2])); - k = j = 0; - for (i = 1; i <= MAX_USERS; ++i) - if (total[i] > 0) { - list[j][0] = total[i] - 1; - list[j][1] = i; - ++j; - } - - qsort(list, count, sizeof(int[2]), cmp_int_desc); + return count; } -int mailalertuser(char* userid) -{ - userinfo_t *uentp=NULL; - if (userid[0] && (uentp = search_ulist_userid(userid))) - uentp->alerts|=ALERT_NEW_MAIL; - return 0; +int sortAngelRecord(const void *pb, const void *pa) { + const AngelRecord *a = (const AngelRecord *)pa, + *b = (const AngelRecord *)pb; + assert(a->is_angel == b->is_angel); + if (a->masters_month != b->masters_month) + return a->masters_month - b->masters_month; + return a->masters - b->masters; } -void sendResult(){ +int generateReport(FILE *fp, AngelRecord *rec, int num_recs, int delete_file) { + time4_t t = time4(NULL); int i; - FILE* fp; - time4_t t; - fileheader_t header; - struct stat st; - char filename[512]; - - sprintf(filename, BBSHOME "/home/%c/%s", mailto[0], mailto); - if (stat(filename, &st) == -1) { - if (Mkdir(filename) == -1) { - fprintf(stderr, "mail box create error %s \n", filename); - return; - } - } - else if (!(st.st_mode & S_IFDIR)) { - fprintf(stderr, "mail box error\n"); - return; - } - stampfile(filename, &header); - fp = fopen(filename, "w"); - if (fp == NULL) { - fprintf(stderr, "Cannot open file %s\n", filename); - return; - } - - t = time(NULL); - fprintf(fp, "作者: " BBSMNAME " 站方統計\n" - "標題: 小天使統計資料\n" - "時間: %s\n" - "\n現在全站小天使有 %d 位:\n", - ctime4(&t), count); - for (i = 0; i < count; ++i) - fprintf(fp, "%15s %5d 人\n", SHM->userid[list[i][1] - 1], list[i][0]); - if (i % 4 != 0) - fputc('\n', fp); - - appendLogFile(fp, BBSHOME "/log/angel_perf.txt", + fprintf(fp, "作者: %s\n標題: %s\n時間: %s\n", + REPORT_AUTHOR, REPORT_SUBJECT, ctime4(&t)); + + fprintf(fp, "現在全站小天使有 %d 位:\n", num_recs); + fprintf(fp, + " (後面數字為全部小主人數 | 一週內 | 一月內 | 一季內" + " 的活躍小主人數,\n" + " 活躍小主人指的是在該段時間內有傳送訊息給任一小天使的主人)\n"); + for (i = 0; i < num_recs; i++) + fprintf(fp, "%15s | %6d | %6d | %6d | %6d\n", + getuserid(rec[i].uid), + rec[i].masters, + rec[i].masters_week, + rec[i].masters_month, + rec[i].masters_quater); + fputs("\n", fp); + + appendLogFile(fp, "log/angel_perf.txt", "\n== 本周小天使活動資料記錄 ==\n" - " (說明: Sample 指的是新小主人找天使時有在線上的次數\n" + " (說明: Sample 指的是新小主人新找天使時有在線上的次數\n" " Pause1 指的是 Sample 中有幾次神諭呼叫器設停收\n" " Pause2 指的是 Sample 中有幾次神諭呼叫器設關閉\n" - " 因此,Sample 與其它人差太多代表不常上線\n" - " Pause2 接近 Sample 代表此天使都在打混\n" " 另外, Sample 每" STR_ANGELBEATS_PERF_MIN_PERIOD - "秒最多更新一次)\n" - ); - appendLogFile(fp, BBSHOME "/log/changeangel.log", - "\n== 本周更換小天使記錄 ==\n"); + "秒最多更新一次)\n", + delete_file); - fputs("\n--\n\n 本資料由 angel 程式產生\n\n", fp); - fclose(fp); + appendLogFile(fp, "log/changeangel.log", + "\n== 本周更換小天使記錄 ==\n", + delete_file); - strcpy(header.title, "小天使統計資料"); - strcpy(header.owner, "站方統計"); - sethomedir(filename, mailto); - append_record(filename, &header, sizeof(header)); - mailalertuser(mailto); + fprintf(fp, "\n--\n 本資料由%s%s自動產生\n\n", BBSNAME, REPORT_AUTHOR); + return 0; } -void slurp(FILE* to, FILE* from) -{ - char buf[4096]; // 4K block - int count; +void usage(const char *myname) { + fprintf(stderr, "Usage: %s [-m user][-b board]\n", myname); + exit(1); +} - while ((count = fread(buf, 1, sizeof(buf), from)) > 0) { - char * p = buf; - while (count > 0) { - int i = fwrite(p, 1, count, to); +int main(int argc, char *argv[]){ + AngelRecord *rec, *angels; + int count, i, iangel; + int uid, bid; + const char *myname = argv[0]; + const char *target = NULL; + int target_is_user = 0; + char target_name[PATHLEN]; + char output_path[PATHLEN] = "", output_dir[PATHLEN] = ""; + struct fileheader_t fhdr; + FILE *fp = stdout; + + chdir(BBSHOME); + attach_SHM(); - if (i <= 0) return; + while (argc > 2) { + if (strcmp(argv[1], "-m") == 0) { + target = argv[2]; + target_is_user = 1; + argc -= 2, argv += 2; + if ((uid = searchuser(target, target_name)) < 1) + die("Invalid user: %s\n", target); + target = target_name; + sethomepath(output_path, target); + sethomedir(output_dir, target); + } else if (strcmp(argv[1], "-b") == 0) { + boardheader_t *bp; + target = argv[2]; + target_is_user = 0; + argc -= 2, argv += 2; + if ((bid = getbnum(target)) < 1) + die("Invalid board: %s\n", target); + bp = getbcache(bid); + strlcpy(target_name, bp->brdname, sizeof(target_name)); + target = target_name; + setbpath(output_path, target); + setbfile(output_dir, target, ".DIR"); + } else { + usage(myname); + } + } + if (argc != 1) + usage(myname); + + if (target) { + stampfile(output_path, &fhdr); + fp = fopen(output_path, "wt"); + if (!fp) + die("Failed to create: %s\n", output_path); + } - p += i; - count -= i; - } + rec = (AngelRecord *)malloc(sizeof(AngelRecord) * MAX_USERS); + assert(rec); + count = buildMasterInfo(rec, MAX_USERS); + + angels = (AngelRecord *)malloc(sizeof(AngelRecord) * count); + assert(angels); + for (i = 0, iangel = 0; i < MAX_USERS; i++) { + if (!rec[i].is_angel) + continue; + memcpy(angels + iangel, rec + i, sizeof(AngelRecord)); + iangel++; } + qsort(angels, count, sizeof(AngelRecord), sortAngelRecord); + generateReport(fp, angels, count, target ? 1 : 0); + + if (target) { + fclose(fp); + strlcpy(fhdr.title, REPORT_SUBJECT, sizeof(fhdr.title)); + strlcpy(fhdr.owner, REPORT_AUTHOR, sizeof(fhdr.owner)); + append_record(output_dir, &fhdr, sizeof(fhdr)); + + if (target_is_user) { + userinfo_t *uentp = search_ulistn(uid, 1); + if (uentp) + uentp->alerts |= ALERT_NEW_MAIL; + } else { + touchbtotal(bid); + } + } + return 0; } #endif /* defined PLAY_ANGEL */ |