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: | 213 / 221 |
Referenced in: | [show references] |