]> jfr.im git - irc/quakenet/newserv.git/blob - a4stats/nterfacer_a4stats.c
cf45e95babb39010ffeb3bde417f8081180153bf
[irc/quakenet/newserv.git] / a4stats / nterfacer_a4stats.c
1 #include <stdio.h>
2 #include <stdarg.h>
3 #include "../lib/version.h"
4 #include "../core/error.h"
5 #include "../nterfacer/library.h"
6 #include "../nterfacer/nterfacer.h"
7 #include "a4stats.h"
8
9 MODULE_VERSION("");
10
11 static struct service_node *a4stats_node;
12
13 static void a4stats_nt_query_cb(const struct DBAPIResult *result, void *uarg) {
14 struct rline *ri = uarg;
15 int i;
16
17 if (result) {
18 ri_append(ri, "%s", result->success ? "OK" : "FAILED");
19
20 if (result->success) {
21 ri_append(ri, "%d", result->fields);
22
23 while (result->next(result)) {
24 for (i = 0; i < result->fields; i++) {
25 const char *field = result->get(result, i);
26 ri_append(ri, "%s", field ? field : "");
27 }
28 }
29 }
30
31 result->clear(result);
32 } else
33 ri_append(ri, "FAILED");
34
35 ri_final(ri);
36 }
37
38 static int handle_getchannel(struct rline *ri, int argc, char **argv) {
39 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, "SELECT timestamp, active, privacy, deleted FROM ? WHERE name = ?", "Ts", "channels", argv[0]);
40 return 0;
41 }
42
43 static int handle_getlines(struct rline *ri, int argc, char **argv) {
44 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, "SELECT h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11,"
45 "h12, h13, h14, h15, h16, h17, h18, h19, h20, h21, h22, h23 "
46 "FROM ? WHERE name = ?", "Ts", "channels", argv[0]);
47
48 return 0;
49 }
50
51 static int handle_getusers(struct rline *ri, int argc, char **argv) {
52 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, "SELECT users.account, users.accountid, users.seen, users.rating, users.lines, users.chars, users.words, users.h0, users.h1, users.h2, users.h3, users.h4, users.h5, users.h6, users.h7, users.h8, users.h9, users.h10, users.h11, users."
53 "h12, users.h13, users.h14, users.h15, users.h16, users.h17, users.h18, users.h19, users.h20, users.h21, users.h22, users.h23, users."
54 "last, users.quote, users.quotereset, users.mood_happy, users.mood_sad, users.questions, users.yelling, users.caps, users."
55 "slaps, users.slapped, users.highlights, users.kicks, users.kicked, users.ops, users.deops, users.actions, users.skitzo, users.foul, users."
56 "firstseen, users.curnick FROM ? LEFT JOIN ? ON channels.id = users.channelid WHERE channels.name = ? AND users.quote IS NOT NULL ORDER BY lines DESC LIMIT 25", "TTs", "users", "channels", argv[0]);
57
58 return 0;
59 }
60
61 static int handle_getkicks(struct rline *ri, int argc, char **argv) {
62 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, "SELECT kicks.kicker, kicks.kickerid, ukicker.seen, ukicker.curnick, kicks.victim, kicks.victimid, uvictim.seen, uvictim.curnick, kicks.timestamp, kicks.reason "
63 "FROM ? "
64 "LEFT JOIN ? ON channels.id = kicks.channelid "
65 "LEFT JOIN ? AS ukicker ON (ukicker.channelid = channels.id AND kicks.kickerid = ukicker.accountid AND kicks.kicker = ukicker.account) "
66 "LEFT JOIN ? AS uvictim ON (uvictim.channelid = channels.id AND kicks.victimid = uvictim.accountid AND kicks.victim = uvictim.account) "
67 "WHERE channels.name = ? ORDER BY kicks.timestamp DESC LIMIT 10", "TTTTs", "kicks", "channels", "users", "users", argv[0]);
68
69 return 0;
70 }
71
72 static int handle_gettopics(struct rline *ri, int argc, char **argv) {
73 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, "SELECT topics.topic, topics.timestamp, topics.setby, topics.setbyid, users.seen, users.curnick "
74 "FROM ? "
75 "LEFT JOIN ? ON channels.id = topics.channelid "
76 "LEFT JOIN ? ON (users.channelid = channels.id AND topics.setbyid = users.accountid AND topics.setby = users.account) "
77 "WHERE channels.name = ? ORDER BY topics.timestamp DESC LIMIT 10", "TTTs", "topics", "channels", "users", argv[0]);
78
79 return 0;
80 }
81
82 static int handle_getuser(struct rline *ri, int argc, char **argv) {
83 #define USER_QUERY(b) "SELECT users.account, users.accountid, users.seen, users.rating, users.lines, users.chars, users.words, " \
84 "users.h0, users.h1, users.h2, users.h3, users.h4, users.h5, users.h6, users.h7, users.h8, users.h9, " \
85 "users.h10, users.h11, users.h12, users.h13, users.h14, users.h15, users.h16, users.h17, users.h18, " \
86 "users.h19, users.h20, users.h21, users.h22, users.h23, users.last, users.quote, users.quotereset, " \
87 "users.mood_happy, users.mood_sad, users.questions, users.yelling, users.caps, users.slaps, users.slapped, " \
88 "users.highlights, users.kicks, users.kicked, users.ops, users.deops, users.actions, users.skitzo, " \
89 "users.foul, users.firstseen, users.curnick FROM ? LEFT JOIN ? ON channels.id = users.channelid WHERE channels.name = ? AND " b
90
91 /*
92 Possible cases:
93 accountid = 0, account = "username" -> new-style account, look up latest account for user
94 accountid = 0, account = "#username" -> legacy account or user not authed, look up using "username" (remove #)
95 accountid = <some value>, account = <unused> -> new-style account, look up by account id
96 */
97
98 if (argv[1][0] == '#') {
99 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, USER_QUERY("(users.accountid = 0 AND users.account = ?)"),
100 "TTss", "users", "channels", argv[0], &(argv[1][1]));
101 } else if (atoi(argv[2]) == 0) {
102 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, USER_QUERY("(users.accountid != 0 AND users.account = ?) ORDER BY users.accountid DESC LIMIT 1"),
103 "TTss", "users", "channels", argv[0], argv[1]);
104 } else {
105 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, USER_QUERY("(users.accountid = ?)"),
106 "TTss", "users", "channels", argv[0], argv[2]);
107 }
108
109 return 0;
110 }
111
112 static int handle_getrelations(struct rline *ri, int argc, char **argv) {
113 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, "SELECT relations.first, relations.firstid, ufirst.curnick, ufirst.seen, relations.second, relations.secondid, usecond.curnick, usecond.seen, relations.seen, relations.score "
114 "FROM ? "
115 "LEFT JOIN ? ON relations.channelid = channels.id "
116 "LEFT JOIN ? AS ufirst ON (ufirst.channelid = channels.id AND relations.firstid = ufirst.accountid AND relations.first = ufirst.account) "
117 "LEFT JOIN ? AS usecond ON (usecond.channelid = channels.id AND relations.secondid = usecond.accountid AND relations.second = usecond.account) "
118 "WHERE channels.name = ? ORDER BY score DESC LIMIT 25",
119 "TTTTs", "relations", "channels", "users", "users", argv[0]);
120
121 return 0;
122 }
123
124 static int handle_getuserrelations(struct rline *ri, int argc, char **argv) {
125 #define USERREALTIONS_QUERY(b) "SELECT relations.first, relations.firstid, ufirst.curnick, ufirst.seen, relations.second, relations.secondid, usecond.curnick, usecond.seen, relations.seen, relations.score " \
126 "FROM ? " \
127 "LEFT JOIN ? ON relations.channelid = channels.id " \
128 "LEFT JOIN ? AS ufirst ON (ufirst.channelid = channels.id AND relations.firstid = ufirst.accountid AND relations.first = ufirst.account) " \
129 "LEFT JOIN ? AS usecond ON (usecond.channelid = channels.id AND relations.secondid = usecond.accountid AND relations.second = usecond.account) " \
130 "WHERE channels.name = ? " b " ORDER BY score DESC LIMIT 10"
131 /*
132 Possible cases:
133 accountid = 0, account = "username" -> new-style account, look up latest account for user
134 accountid = 0, account = "#username" -> legacy account or user not authed, look up using "username" (remove #)
135 accountid = <some value>, account = <unused> -> new-style account, look up by account id
136 */
137
138 if (argv[1][0] == '#') {
139 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, USERREALTIONS_QUERY("(relations.first = ? AND relations.firstid = 0 OR relations.second = ? AND relations.secondid = 0)"),
140 "TTTTsss", "relations", "channels", "users", "users", argv[0], &(argv[1][1]), &(argv[1][1]));
141 } else if (atoi(argv[2]) == 0) {
142 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, USERREALTIONS_QUERY("(ROW(relations.first, relations.firstid) = "
143 "(SELECT account, accountid FROM ? WHERE accountid != 0 AND account = ? ORDER BY accountid DESC LIMIT 1) OR "
144 "ROW(relations.second, relations.secondid) = (SELECT account, accountid FROM ? WHERE accountid != 0 AND account = ? ORDER BY accountid DESC LIMIT 1))"),
145 "TTTTsTsTs", "relations", "channels", "users", "users", argv[0], "users", argv[1], "users", argv[1]);
146 } else {
147 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, USERREALTIONS_QUERY("(relations.firstid = ? OR relations.secondid = ?)"),
148 "TTTTsssss", "relations", "channels", "users", "users", argv[0], argv[1], argv[2], argv[1], argv[2]);
149 }
150
151 return 0;
152 }
153
154 static int handle_setprivacy(struct rline *ri, int argc, char **argv) {
155 a4statsdb->squery(a4statsdb, "UPDATE ? SET privacy = ? WHERE name = ?", "Tss", "channels", argv[1], argv[0]);
156 return ri_final(ri);
157 }
158
159 static int handle_finduser(struct rline *ri, int argc, char **argv) {
160 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, "SELECT users.account, users.accountid, users.seen, users.curnick, channels.name, channels.privacy "
161 "FROM ? "
162 "LEFT JOIN ? ON channels.id = users.channelid "
163 "WHERE (lines > 5 OR accountid != 0) AND (LOWER(curnick) LIKE ? OR LOWER(account) LIKE ?) ORDER BY seen DESC LIMIT 150",
164 "TTss", "users", "channels", argv[0], argv[0]);
165
166 return 0;
167 }
168
169 static int handle_findchan(struct rline *ri, int argc, char **argv) {
170 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, "SELECT name, privacy FROM ? WHERE active = 1 AND LOWER(name) LIKE ? LIMIT 25",
171 "Ts", "channels", argv[0]);
172
173 return 0;
174 }
175
176 static int handle_getuserchans(struct rline *ri, int argc, char **argv) {
177 #define USERCHANS_QUERY(b) "SELECT channels.name, channels.privacy, users.account, users.accountid, users.curnick, users.firstseen, users.seen " \
178 "FROM ? " \
179 "JOIN ? ON channels.id = users.channelid " \
180 "WHERE channels.active = 1 AND " b " ORDER BY channels.name"
181 /*
182 Possible cases:
183 accountid = 0, account = "username" -> new-style account, look up latest account for user
184 accountid = 0, account = "#username" -> legacy account or user not authed, look up using "username" (remove #)
185 accountid = <some value>, account = <unused> -> new-style account, look up by account id
186 */
187
188 if (argv[0][0] == '#') {
189 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, USERCHANS_QUERY("users.accountid = 0 AND users.account = ?"),
190 "TTs", "users", "channels", &(argv[0][1]));
191 } else if (atoi(argv[1]) == 0) {
192 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, USERCHANS_QUERY("ROW(users.account, users.accountid) = (SELECT account, accountid FROM ? WHERE users.accountid != 0 AND users.account = ? ORDER BY users.accountid DESC LIMIT 1)"),
193 "TTTs", "users", "channels", "users", argv[0]);
194 } else {
195 a4statsdb->query(a4statsdb, a4stats_nt_query_cb, ri, USERCHANS_QUERY("users.accountid = ?"),
196 "TTs", "users", "channels", argv[1]);
197 }
198
199 return 0;
200 }
201
202 void _init(void) {
203 a4stats_node = register_service("a4stats");
204 if (!a4stats_node)
205 return;
206
207 register_handler(a4stats_node, "getchannel", 1, handle_getchannel);
208 register_handler(a4stats_node, "getlines", 1, handle_getlines);
209 register_handler(a4stats_node, "getusers", 1, handle_getusers);
210 register_handler(a4stats_node, "getkicks", 1, handle_getkicks);
211 register_handler(a4stats_node, "gettopics", 1, handle_gettopics);
212 register_handler(a4stats_node, "getuser", 3, handle_getuser);
213 register_handler(a4stats_node, "getrelations", 1, handle_getrelations);
214 register_handler(a4stats_node, "getuserrelations", 3, handle_getuserrelations);
215 register_handler(a4stats_node, "setprivacy", 2, handle_setprivacy);
216 register_handler(a4stats_node, "finduser", 1, handle_finduser);
217 register_handler(a4stats_node, "findchan", 1, handle_findchan);
218 register_handler(a4stats_node, "getuserchans", 2, handle_getuserchans);
219 }
220
221 void _fini(void) {
222 if (a4stats_node)
223 deregister_service(a4stats_node);
224 }