summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pttbbs/util/angel.c305
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 */