1 | !7 |
2 | |
3 | cmodule GazelleWebServer > DynPrintLogAndEnabled {
|
4 | transient MyHTTPD server; |
5 | new Set<S> authedCookies; |
6 | transient new GazelleContextCache contextCache; |
7 | |
8 | transient new ThreadLocal<O[]> bodyParams; |
9 | transient new ThreadLocal<Bool> authed; |
10 | |
11 | int absoluteMaxResults = 1000, defaultMaxResults = 100; |
12 | |
13 | void start() ctex {
|
14 | super.start(); |
15 | if (!enabled) ret; |
16 | |
17 | dm_useLocalMechListCopies(); |
18 | |
19 | server = new MyHTTPD(80); |
20 | server.serveFunction = func(S uri, SS parms) {
|
21 | serve(uri, parms) |
22 | }; |
23 | server.start(); |
24 | print("HTTP server started on port " + server.getPort());
|
25 | } |
26 | |
27 | void cleanMeUp {
|
28 | server.stop(); |
29 | server = null; |
30 | } |
31 | |
32 | O serve(S uri, SS params) enter {
|
33 | new Matches m; |
34 | |
35 | S cookie = serveHttp_cookieHandling(); |
36 | bool authed = nempty(cookie) && syncContains(authedCookies, cookie); |
37 | temp tempSetTL(this.authed, authed); |
38 | print("Cookie: " + cookie + ", authed: " + authed);
|
39 | S master = trim(loadTextFile(javaxSecretDir("gazelle-master-pw")));
|
40 | S attempt = params.get('_pass);
|
41 | if (nempty(attempt) && nempty(cookie) && nempty(master)) {
|
42 | if (eq(attempt, master)) {
|
43 | print("Login SUCCEEDED");
|
44 | syncAdd(authedCookies, cookie); |
45 | change(); |
46 | authed = true; |
47 | } else |
48 | print("Login FAILED");
|
49 | } |
50 | |
51 | if (eq(uri, "/favicon.ico")) |
52 | ret serveFile(loadLibrary(#1400189), faviconMimeType()); |
53 | |
54 | O[] bodyParams = litobjectarray(style := "font-family: Roboto Mono; " + (authed ? "background-color: #FFFF88" : "")); |
55 | temp tempSetTL(this.bodyParams, bodyParams); |
56 | |
57 | if (eq(uri, "/login")) |
58 | ret hhtml_head_title_body("Login" + " | Gazelle",
|
59 | hprelude() + |
60 | (nempty(master) ? hpostform(hpasswordfield('_pass)) : "No master PW"), bodyParams);
|
61 | |
62 | if (swic(uri, "/texts/", m)) {
|
63 | S textID = m.rest(); |
64 | if (!possibleMD5(textID)) ret serve404(); |
65 | ret serveTextFileAsUTF8(javaxDataDir("Gazelle Texts/" + textID));
|
66 | } |
67 | |
68 | if (eq(uri, "/lastOutput")) {
|
69 | L<GazelleLine> lines = reversed(takeLast(10, dm_discord_allBotLines())); |
70 | ret hframe("Last Output", hcenter(hheading("Last Output")
|
71 | + htmlTable2( |
72 | map(lines, line -> {
|
73 | L applications = dm_gazelle_applicationsForMsgID(line.msgID); |
74 | //print("Got " + n2(applications, "application") + " for " + line.msgID + " from " + dm_gazelle_longFeedbackCRUD());
|
75 | ret litorderedmap( |
76 | "Date" := spanTitle("Message ID: " + line.msgID, htmlencode2(localDateWithMinutes(line.timestamp))),
|
77 | "Gazelle said:" := htmlencode2(line.text), |
78 | "Applied rules" := joinWithSpace(map(f<S, S> ruleLink, collect ruleID(applications))) |
79 | ); |
80 | }), htmlEncode := false, tableParams := litparams(cellpadding := 5)) |
81 | )); |
82 | } |
83 | |
84 | if (eq(uri, "/lastInput")) {
|
85 | L<GazelleLine> lines = reversed(takeLast(30, dm_discord_allLines())); |
86 | ret hframe("Last Input", hcenter(hheading("Last Input")
|
87 | + htmlTable2( |
88 | map(lines, line -> {
|
89 | //L applications = dm_gazelle_applicationsForMsgID(line.msgID); |
90 | ret litorderedmap( |
91 | "Date" := spanTitle("Message ID: " + line.msgID, htmlencode2(localDateWithMinutes(line.timestamp))),
|
92 | "Author" := htmlencode(line.user), |
93 | "Bot?" := htmlencode(line.bot ? "yes" : ""), |
94 | "Line" := htmlencode2(line.text), |
95 | //"Applied rules" := joinWithSpace(map(f<S, S> ruleLink, collect ruleID(applications))), |
96 | "More" := moreForMsgID(line.msgID) |
97 | ); |
98 | }), htmlEncode := false, tableParams := litparams(cellpadding := 5)) |
99 | )); |
100 | } |
101 | |
102 | if (swic(uri, "/revisit/", m)) {
|
103 | long msgID = assertNotZero(parseLong(m.rest())); |
104 | GazelleLine line = dm_discord_lineForMsgID(msgID); |
105 | if (line == null) ret "Message not found: " + msgID; |
106 | long startTime = sysNow(); |
107 | L<GazelleTree> tree = dm_gazelle_revisitChatLine(msgID, |
108 | ctx := contextCache!); |
109 | long endTime = sysNow(); |
110 | |
111 | //print("Tree to string.");
|
112 | S tv = treeViewToString_withoutHijackPrint(gazelle_treeView(tree), |
113 | stringifier := func(TreeView<GazelleTree> t) -> S { gazelleTreeToHTML(t.node()) });
|
114 | //print("children=" + l(tree) + ", tv = " + quote(tv));
|
115 | ret hframe("Revisiting msg " + msgID,
|
116 | hheading("Revisiting message " + msgID + ": " + htmlEncode2(line.text))
|
117 | + nlToBr_withIndents(tv) |
118 | + p((endTime-startTime) + " ms")); |
119 | } |
120 | |
121 | Request request = new(authed, params); |
122 | |
123 | if (swic(uri, "/rule/", m)) {
|
124 | S ruleID = assertGlobalID(m.rest()); |
125 | ret request.serveRule(ruleID, params); |
126 | } |
127 | |
128 | if (eq(uri, "/search")) |
129 | ret search(params); |
130 | |
131 | if (eq(uri, "/wikipedia")) |
132 | ret request.serveWikipedia(); |
133 | |
134 | if (eq(uri, "/commands")) |
135 | ret hhtml_head_title_body("Commands | Gazelle",
|
136 | hprelude() + |
137 | linesLL(hheading("Commands"),
|
138 | h3("!eval"),
|
139 | |
140 | p([[You can evaluate Java code directly through Discord. |
141 | Unless you are specifically authorized, only a ]] + ahref(rawSelfLink("safeIdentifiers"), "safe subset of identifiers") + " is allowed."),
|
142 | |
143 | p("Example: " + tt("!eval 1+2")),
|
144 | p("In rare cases " + tt("!eval") + " may fail and you need to type " + tt("!real-eval") + " instead (which invokes an actual Java compiler).")), bodyParams);
|
145 | |
146 | if (eq(uri, "/safeIdentifiers")) |
147 | ret hhtml_head_title_body("Safe Java Identifiers | Gazelle",
|
148 | hprelude() + |
149 | linesLL( |
150 | hheading("Safe Java(X) identifiers for !eval"),
|
151 | hpre(lines(sortedIC(codeAnalysis_allSafeIdentifiers())))), bodyParams); |
152 | |
153 | if (!eq(uri, "/")) |
154 | ret serve404(); |
155 | |
156 | //final Map<S, Int> feedbackStats = dm_gazelle_feedbackStats(); |
157 | final Map<S, Int> feedbackStats2 = dm_gazelle_feedbackStatsByJudgement(); |
158 | final Map<S, Int> feedbackStats3 = dm_gazelle_feedbackStatsForNonJudged(); |
159 | |
160 | // Home Page / Main List |
161 | |
162 | L<T3<S>> rules = dm_allRulesFromRulesModuleWithCommentsAndIDs(); |
163 | // dm_gazelle_allRulesWithComment("discord");
|
164 | |
165 | bool showAll = eq("1", params.get('showAll));
|
166 | |
167 | L<Map> mapped = mapReversed(rules, |
168 | func(T3<S> t) -> SS {
|
169 | S ruleID = t.c; |
170 | ret litorderedmap( |
171 | "Rule ID" := !showAll ? null : htmlEncode2(ruleID), |
172 | "Rule Text" := htmlEncode_nlToBr(t.a), |
173 | "Comments" := !showAll ? null : nlToBr( |
174 | mapEachLine(/*withoutLine("discord",*/ t.b/*)*/, func(S s) -> S {
|
175 | new Matches m; |
176 | if "use helper table mech list *" |
177 | ret "use helper table mech list " + |
178 | ahref(neatMechListURL($1), m.get(0)); |
179 | ret s; |
180 | })), |
181 | "# of applications" := strOr(feedbackStats3.get(ruleID), "-"), |
182 | "Feedback" := ahref(rawSelfLink("rule/" + ruleID),
|
183 | "+" + toInt(feedbackStats2.get(ruleID + "/good")) |
184 | /*+ " / " + |
185 | toInt(feedbackStats3.get(ruleID))*/ |
186 | + " / " + |
187 | "-" + toInt(feedbackStats2.get(ruleID + "/bad")))); |
188 | }); |
189 | |
190 | S sortBy = params.get('sortBy);
|
191 | bool desc = eq("1", params.get('desc));
|
192 | if (nempty(sortBy)) |
193 | if (eq(sortBy, "Feedback")) |
194 | mapped = sortByCalculatedField(mapped, func(Map map) -> Int {
|
195 | S s = cast map.get("Feedback");
|
196 | ret parseFirstInt(s) - parseSecondInt(s); |
197 | }); |
198 | else if (eq(sortBy, "# of applications")) |
199 | mapped = sortByMapKeyAlphaNum(mapped, sortBy); |
200 | else |
201 | mapped = sortByMapKey(mapped, sortBy); |
202 | if (desc) reverseInPlace(mapped); |
203 | |
204 | ret hhtml_head_title_body("Gazelle - Next-Gen Chat Bot",
|
205 | hprelude() + |
206 | hopeningTag link(rel :="icon", href := "/favicon.ico?v=2") + |
207 | hcenter( |
208 | p(b("GAZELLE 2019", style := "font-size: 5em")) +
|
209 | p(hsnippetimg(#1101500, height := 150, title := "Gazelle")) + |
210 | p(span("<b>Hello! I am a next-generation chat bot in training.</b>", style := "font-size: 1.4em") + "<br>"
|
211 | + span(targetBlank("https://BotCompany.de", "Maker.") + " "
|
212 | + targetBlank("https://slides.com/stefanreich/how-about-thinking-machines/", "Slides.") + " "
|
213 | + targetBlank("https://discordapp.com/invite/SEAjPqk", b("Join my Discord server!")), style := "font-size: 1.2em"))
|
214 | + hform(p(ahref(rawSelfLink("commands"), "Commands.")
|
215 | + " " + ahref(rawSelfLink("lastOutput"), "Last output.")
|
216 | //+ " " + ahref(rawSelfLink("search"), "Search.")
|
217 | + " | " + hinputfield('q, style := "width: 120px") + " " + hsubmit("Search")), action := "/search")
|
218 | |
219 | + h3("Rules (" + l(rules) + ")")
|
220 | + htmlTable2(mapped, |
221 | htmlEncode := false, |
222 | paramsByColName := litmap( |
223 | "Feedback" := litobjectarray(align := 'center), |
224 | "# of applications" := litobjectarray(align := 'center)), |
225 | replaceHeaders := litmap( |
226 | "Rule Text" := "Rule (input + more input => output)", |
227 | "# of applications" := sortLink(params, "# of applications"), |
228 | "Feedback" := sortLink(params, "Feedback")), |
229 | tdParams := litobjectarray(valign := 'top) |
230 | ) |
231 | + p(ahref(hqueryWithoutNanoHTTPDStuff(mapPlus(params, showAll := 1)), "Show technical stuff")) |
232 | ), bodyParams); |
233 | } |
234 | |
235 | S hframe(S title, O body) {
|
236 | print("Params: " + sfu(bodyParams!));
|
237 | ret hhtml_head_title_body(title + " | Gazelle", |
238 | hprelude() + |
239 | body, |
240 | bodyParams!); |
241 | } |
242 | |
243 | S ruleLink(S ruleID) {
|
244 | if (!isGlobalID(ruleID)) ret htmlEncode2(ruleID); |
245 | ret ahref("/rule/" + ruleID, ruleID);
|
246 | } |
247 | |
248 | S gazelleTreeToHTML(GazelleTree tree) {
|
249 | if (tree == null) ret "*"; |
250 | ret (tree.isSplitNode ? "[split] " : "") |
251 | + htmlEncode2(tree.line) + appendSquareBracketed( |
252 | joinWithComma(listPlus( |
253 | map htmlEncode2(tree.renderQualityElements()), |
254 | ruleLink(tree.ruleID())))); |
255 | } |
256 | |
257 | S moreForMsgID(long msgID) {
|
258 | ret ahref(rawSelfLink("revisit/" + msgID), "revisit", rel := "nofollow");
|
259 | } |
260 | |
261 | S search(SS params) {
|
262 | S q = trim(params.get('q));
|
263 | Set<S> types = singletonCISetIfNempty(params.get('type));
|
264 | int maxResults = min(absoluteMaxResults, toIntOr(params.get('max), defaultMaxResults));
|
265 | L results = empty(q) ? null : dm_gazelle_fullSearch(q, +types, maxResults := maxResults+1); |
266 | S title = empty(q) ? "Search" : "Search results for: " + q; |
267 | ret hframe(title, |
268 | hheading(htmlEncode2(title)) + |
269 | hform("Terms: " + hinputfield('q, value := q, autofocus := true)
|
270 | + " Type: " + hselect_list(ll("", "Rules", "Lines"), params.get('type), name := 'type)
|
271 | + " " + hsubmit("Search"), action := "/search") +
|
272 | (empty(results) ? |
273 | (empty(q) ? "" : "No results") : p( |
274 | l(results) > maxResults ? maxResults + "+ results" : n2(results, "result")) |
275 | + htmlTable2(map(takeFirst_lazy(maxResults, results), o -> {
|
276 | S type = shortClassName(o); |
277 | bool isLine = eq(type, "Line"); |
278 | long msgID = isLine ? getLong msgID(o) : 0; |
279 | Map map = litorderedmap("Type" :=
|
280 | isLine ? spanTitle("Message ID: " + msgID, type) : type);
|
281 | S text = htmlEncode2OrNull(getStringOpt text(o)); |
282 | if (isLine) |
283 | {
|
284 | map.put("Date" := localDateWithMinutes(getLong created(o)));
|
285 | map.put("By" := (S) call(o, 'userName));
|
286 | } |
287 | if (text != null) {
|
288 | if (eq(type, "Rule")) |
289 | text = ahref("/rule/" + getString globalID(o), text);
|
290 | mapPut(map, "Text", text); |
291 | } |
292 | if (isLine) |
293 | map.put("More" := moreForMsgID(msgID));
|
294 | ret map; |
295 | }), htmlEncode := false))); |
296 | } |
297 | |
298 | class Request {
|
299 | bool authed; |
300 | SS params; |
301 | |
302 | *(bool *authed, SS *params) {}
|
303 | |
304 | S serveRule(S ruleID, SS params) {
|
305 | // add rule comment |
306 | S comment = trim(params.get("comment"));
|
307 | if (nempty(comment)) {
|
308 | if (!authed) comment = "[anon] " + comment; |
309 | dm_gazelle_addRuleComments_verbose(ruleID, ll(comment)); |
310 | } |
311 | |
312 | PairS textAndComment = unnull(dm_textAndCommentForRule(ruleID)); |
313 | L feedback = dm_gazelle_feedbackForRule(ruleID); |
314 | L applications = dm_gazelle_applicationsForRule(ruleID); |
315 | Map<S, O> feedbackByContext = indexByFieldNotNull context(feedback); |
316 | S title = "Rule " + ruleID; |
317 | bool showStruct = eq("1", params.get('struct));
|
318 | L list = cloneList(feedback); |
319 | for (O o : applications) |
320 | if (!containsKey(feedbackByContext, getString context(o))) |
321 | list.add(o); |
322 | |
323 | // add feedback comments |
324 | |
325 | for (S applicationID, S text : subMapStartingWith_dropPrefix(params, "comment_")) {
|
326 | continue if empty(text = trim(text)); |
327 | if (!authed) text = "[anon] " + text; |
328 | O app = findByField(list, globalID := applicationID); |
329 | print("Processing comment for " + applicationID + " - " + app);
|
330 | if (app == null) continue; |
331 | Set<S> comments = asSet(tlft(getString comments(app))); |
332 | if (!contains(comments, text)) |
333 | call(app, '_setField, comments := appendWithNewLine(getString comments(app), text)); |
334 | } |
335 | |
336 | // Serve Rule & Applications & Feedback |
337 | |
338 | L<Map> mapped = map(list, func(O o) -> Map {
|
339 | litorderedmap( |
340 | "Judgement" := getString judgement(o), |
341 | "Rewritten Rule" := getString_htmlEncode modifiedRule(o), |
342 | "Generated Output" := getString_htmlEncode outText(o), |
343 | "Mapping" := dropPrefix("cimap", getString_htmlEncode varMap(o)),
|
344 | "Context" := getString_htmlEncode context(o), |
345 | "Comments" := hform( |
346 | appendIfNempty(nlToBr(rtrim(getString_htmlEncode comments(o))), "<br>") |
347 | + htextfield("comment_" + getString globalID(o))),
|
348 | "Struct" := showStruct ? getString matchedRuleStruct(o) : null) |
349 | }); |
350 | |
351 | S sortBy = params.get('sortBy);
|
352 | if (nempty(sortBy)) |
353 | mapped = sortByMapKey(mapped, sortBy); |
354 | |
355 | Collection<S> statements = dm_gazelle_statementsForRule(ruleID); |
356 | |
357 | ret hhtml_head_title_body(title + " | Gazelle", |
358 | hprelude() + |
359 | hheading(htmlEncode2(title)) |
360 | + h3("Rule")
|
361 | + hblock(textAndComment.a) |
362 | + (nempty(textAndComment.b) ? h3("Comments") + hblock(textAndComment.b) : "")
|
363 | + "<br><br>" + hform( |
364 | "Add comment: " + htextfield("comment"))
|
365 | + h3("Feedback")
|
366 | + htmlTable2(mapped, tdParams := litobjectarray(align := 'center, valign := 'top), htmlEncode := false) |
367 | + h3("Analysis 1")
|
368 | + hblock(lines(statements)) |
369 | + h3("Analysis 2")
|
370 | + hblock(sfuLines(objectToMap( |
371 | ai_gazelle_analyzeStatementsForRule(statements)))) |
372 | , bodyParams!); |
373 | } |
374 | |
375 | S serveWikipedia() {
|
376 | /*hheading("All Simple English Wikipedia Topics")
|
377 | + htmlTable2(map(simpleWikipediaTopics_cached(), topic -> litorderedmap("Topic" := topic))));*/
|
378 | ret hframe("Wikipedia",
|
379 | hheading("Retrieved Simple Wikipedia Pages")
|
380 | + htmlTable2(map(simpleWikipedia_allCachedTopics(), topic -> litorderedmap("Topic" := topic))));
|
381 | |
382 | } |
383 | |
384 | } // end of Request |
385 | |
386 | } // end of class |
387 | |
388 | sS rawSelfLink(S uri) { ret addSlashPrefix(uri); }
|
389 | |
390 | sS hheading(O contents) {
|
391 | ret h2(ahref(rawSelfLink(""), "Gazelle") + " | " + contents);
|
392 | } |
393 | |
394 | sS hprelude() {
|
395 | ret [[<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">]] + |
396 | hmobilefix(); |
397 | } |
398 | |
399 | sS hblock(S s) {
|
400 | ret htmlEncode_nlToBr(s); |
401 | } |
402 | |
403 | sS sortLink(SS params, S key) {
|
404 | params = cloneMap(params); |
405 | mapPutOrRemove(params, 'desc, |
406 | eq(params.get('sortBy), key) && !eq("1", params.get('desc)) ? "1" : null);
|
407 | params.put(sortBy := key); |
408 | ret ahref(hqueryWithoutNanoHTTPDStuff(params), htmlEncode2(key)); |
409 | } |
Began life as a copy of #1021679
download show line numbers debug dex old transpilations
Travelled to 7 computer(s): bhatertpkbcr, cfunsshuasjs, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt
No comments. add comment
| Snippet ID: | #1021960 |
| Snippet name: | Gazelle Web Server backup |
| Eternal ID of this version: | #1021960/1 |
| Text MD5: | 3a22e640d7ec1bf2341e23b5eaf22333 |
| Author: | stefan |
| Category: | javax / stefan's os / a.i. / web |
| Type: | JavaX source code (Dynamic Module) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2019-03-04 10:40:41 |
| Source code size: | 16350 bytes / 409 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 488 / 484 |
| Referenced in: | [show references] |