Download Jar. Libraryless. Click here for Pure Java version (29897L/191K).
1 | !7 |
2 | |
3 | set flag OurSyncCollections. |
4 | |
5 | !include #1015743 // concept AIList |
6 | |
7 | static ConceptFieldIndexCI<AIList> nameIndex; |
8 | static volatile long totalChars = -1; |
9 | |
10 | concept Session { |
11 | S cookie; |
12 | S mech_filter; |
13 | } |
14 | |
15 | p { |
16 | dbSaveEvery(60); |
17 | dbIndexing(AIList, 'name); |
18 | nameIndex = indexConceptFieldCI(AIList, 'name); |
19 | thread { mech_guessLanguage(""); } // preload |
20 | calcOnConceptChanges(10000, r calcTotalSize, true); |
21 | addConceptIndex(new IConceptIndex { |
22 | public void update(Concept c) { |
23 | pcall { if (c cast AIList) sendToUpdatesBot('mechListChange, c.name); } |
24 | } |
25 | public void remove(Concept c) { |
26 | pcall { if (c cast AIList) sendToUpdatesBot('removeMechList, c.name); } |
27 | } |
28 | }); |
29 | updateListNamesList(); |
30 | } |
31 | |
32 | set flag NoNanoHTTPD. html { |
33 | temp countDispatch('html); |
34 | O response; |
35 | new Matches m; |
36 | |
37 | S cookie = cookieFromUser(); |
38 | S filter = params.get('mech_filter); |
39 | print("cookie=" + cookie + ", filter=" + filter + ", dialogID=" + getDialogID()); |
40 | Session session = empty(cookie) ? null : uniq(Session, +cookie); |
41 | if (session != null && filter != null) { |
42 | cset(session, mech_filter := filter); |
43 | ret hrefresh(rawSelfLink()); |
44 | } |
45 | |
46 | bool authed = webAuthed(params); |
47 | |
48 | if (eq(uri, "/list-count")) ret serveText(countConcepts(AIList)); |
49 | if ((response = serveListText(uri, params, authed)) != null) ret response; |
50 | |
51 | // Append API |
52 | if (swic(uri, "/bot-list-append/", m)) { |
53 | params.put(name := urldecode(m.rest()); |
54 | uri = "/bot-list-append"; |
55 | } |
56 | |
57 | if (eq(uri, "/bot-list-append")) { |
58 | S mode = params.get('mode); |
59 | lock dbLock(); |
60 | S name = params.get("name"), text = params.get("text"); |
61 | if (!authed) |
62 | name = "Unauthorized Appends | " + name; |
63 | AIList l = getList(name); |
64 | //if (!authed) cset(l, status := "PUBLIC READ"); |
65 | |
66 | if (eq(mode, "uniqCI")) { |
67 | text = lines(listMinusSet(tlft(text), asCISet(tlft(l.text)))); |
68 | if (empty(text)) ret "No change"; |
69 | } |
70 | |
71 | if (eq(mode, "uniq")) { |
72 | text = lines(listMinusSet(tlft(text), tlft(l.text))); |
73 | if (empty(text)) ret "No change"; |
74 | } |
75 | |
76 | listAppendWithLog(l, text); |
77 | ret "Changed"; |
78 | } |
79 | |
80 | if (!authed && eq(uri, "/list-names")) |
81 | ret serveText(jsonEncode(sortedIC(collect(publicReadLists(), 'name)))); |
82 | |
83 | if (!authed && eq(uri, "/list-md5s")) |
84 | ret serveText(jsonEncode(map(publicReadLists(), func(AIList l) -> LS { ll(l.name, md5OfList(l)) }))); |
85 | |
86 | if (!authed && eq(uri, "/list-md5s-and-statuses")) |
87 | ret serveText(jsonEncode(map(publicReadLists(), func(AIList l) -> LS { ll(l.name, md5OfList(l), l.status, str(lineCountOfList(l))) }))); |
88 | |
89 | if (eq(uri, "/authed")) ret yesno(authed); |
90 | |
91 | // Show list (unauthed) |
92 | if (!authed && startsWith(uri, "/list/", m)) { |
93 | AIList l = getList_noCreate(urldecode(m.get(0))); |
94 | if (l == null || !l.isPublicRead()) ret serve404("List not found"); |
95 | ret htitle_h2(htmlencode(l.name)) + hpre_htmlencode(l.text); |
96 | } |
97 | |
98 | bool alphabetical = eq(uri, "/alphabetical"); |
99 | if (!authed && (eq(uri, "/") || alphabetical)) { |
100 | L<AIList> list = alphabetical |
101 | ? sortedByFieldIC('name, publicReadLists()) |
102 | : sortedByFieldDesc('modified, publicReadLists()); |
103 | |
104 | ret h3_title("Bot Data") |
105 | + hBoolSelector(alphabetical, "/", "By date", "/alphabetical", "Alphabetical") |
106 | + listLists(list); |
107 | } |
108 | |
109 | // Search results |
110 | S q = params.get('q); |
111 | if (nempty(q)) { |
112 | L<AIList> found = scoredSearch_AIList(q, listsForAuth(authed)); |
113 | |
114 | if (eq(uri, "/json-search")) |
115 | ret serveText(jsonEncode(collect name(found))); |
116 | |
117 | ret hmobilefix() |
118 | + htitle_h3("mech.botcompany.de: Search results for " + htmlencode(singleQuote(q))) |
119 | + listLists(found); |
120 | } |
121 | |
122 | if (!authed) ret redirectToWebAuth(); |
123 | |
124 | // AUTHED FROM HERE ON |
125 | |
126 | try object html_serveDispatches(uri); |
127 | |
128 | if (swic(uri, "/list-id/", m)) { |
129 | AIList l = getList_noCreate(urldecode(m.rest())); |
130 | ret l == null ? "List not found" : str(l.id); |
131 | } |
132 | |
133 | if (eq(uri, "/fix-unauth")) { |
134 | for (AIList l) if (swic(l.name, "Unauthorized Appends |")) |
135 | cset(l, status := ""); |
136 | ret "OK"; |
137 | } |
138 | |
139 | // API |
140 | if (eq(uri, "/download")) |
141 | ret subBot_serveFileWithName("mechlists-" + ymd_minus_hms() + ".gz", conceptsFile()); // TODO: concurrent writes? |
142 | |
143 | if (eq(uri, "/list-names")) |
144 | ret serveText(jsonEncode(sortedIC(collect(list(AIList), 'name)))); |
145 | |
146 | if (eq(uri, "/list-md5s")) |
147 | ret serveText(jsonEncode(map(list(AIList), func(AIList l) -> LS { ll(l.name, md5OfList(l)) }))); |
148 | |
149 | if (eq(uri, "/list-md5s-and-statuses")) |
150 | ret serveText(jsonEncode(map(list(AIList), func(AIList l) -> LS { ll(l.name, md5OfList(l), l.status, str(lineCountOfList(l))) }))); |
151 | |
152 | if (eq(uri, "/bot-edited-lists")) |
153 | ret serveText(jsonEncode(sortedIC(collect(conceptsWhere(AIList, botFlag := true), 'name)))); |
154 | |
155 | // Create API |
156 | if (eq(uri, "/bot-list-create")) { |
157 | lock dbLock(); |
158 | S name = params.get("name"); |
159 | getList(name); |
160 | ret "OK"; |
161 | } |
162 | |
163 | // Edit API |
164 | if (eq(uri, "/bot-list-edit")) { |
165 | lock dbLock(); |
166 | S name = params.get("name"), text = params.get("text"); |
167 | AIList l = getList(name); |
168 | if (eq(l.text, text)) ret "No change"; |
169 | listReplaceWithLog(l, text); |
170 | ret "Changed"; |
171 | } |
172 | |
173 | // Show list (authed) |
174 | if (startsWith(uri, "/list/", m)) { |
175 | S name = urldecode(m.get(0)); |
176 | AIList l = eq("1", params.get("create")) ? getList(name) : getList_noCreate(name); |
177 | if (l == null) |
178 | ret "List not found" |
179 | + (!authed ? "" : hpostform(hhidden(create := 1) + hsubmit("Create"))); |
180 | |
181 | bool delete = eq(params.get("delete"), "1"); |
182 | |
183 | // Rename list |
184 | S renameTo = params.get('renameTo); |
185 | if (nempty(renameTo) && !delete) { |
186 | if (neqic(l.name, renameTo) && getList_noCreate(renameTo) != null) |
187 | ret "Can't rename - list " + quote(renameTo) + " already exists"; |
188 | S oldName = l.name; |
189 | cset(l, name := renameTo); |
190 | updateListNamesList(); |
191 | ret nav() + "List " + quote(oldName) + " renamed to " + quote(renameTo); |
192 | } |
193 | |
194 | if (delete) { |
195 | logStructure("versions.log", litmap(id := l.id, status := "delete me", date := now())); |
196 | deleteConcept(l); |
197 | updateListNamesList(); |
198 | ret nav() + "List deleted"; |
199 | } |
200 | |
201 | // Update text |
202 | S text = params.get("human"); |
203 | S status = params.get("status"); |
204 | if (text != null) { |
205 | lock dbLock(); |
206 | //printStruct(ll(+text, +status)); |
207 | if (neqAny(text, l.text, status, unnull(l.status))) { |
208 | logStructure("versions.log", litmap(id := l.id, +text, +status, date := now())); |
209 | if (status != null) cset(l, +status); |
210 | cset(l, +text, textMD5 := md5(text), lines := -1, botFlag := false, modified := now()); |
211 | listTextChanged(l); |
212 | //printStruct("l.text=" + sfu(text)); |
213 | } |
214 | ret hrefresh(0, rawLink(uri) + "?random=" + randomID()); |
215 | } |
216 | |
217 | S textArea = htextarea(l.text, name := 'human, cols := 80, rows := 30); |
218 | |
219 | L<S> tok = javaTok(l.status); |
220 | jreplace(tok, "bb", "iframe 1018300"); |
221 | int idx = jfind(tok, "iframe <int>"); |
222 | if (idx > 0) { |
223 | S analyzer = tok.get(idx+2); |
224 | S subUri = joinSubList(tok, idx+4, smartIndexOf(tok, idx, ",")-1); |
225 | textArea = htableRaw2_singleRow(ll(textArea, iframe(relativeRawBotLink(analyzer, subUri + "?list=" + urlencode(l.name)), width := "500", height := "500")), |
226 | null, null, ll(valign := 'top)); |
227 | } |
228 | |
229 | // Serve list text (editable) |
230 | |
231 | ret nav() + htitle(l.name) |
232 | + h2(ahref(neatMechListURL(l.name), htmlencode2(l.name)) + " [" + nLines(countLines(l.text)) + "]") |
233 | + hpostform( |
234 | "Status: " + htextinput('status, value := l.status) |
235 | + h3("Contents (" + (l.botFlag ? "bot" : "human") + "-edited)") |
236 | + p(hsubmit("Save")) |
237 | + textArea |
238 | + p(hsubmit("Save")) |
239 | ) |
240 | + hpostform( |
241 | "New name: " + htextinput('renameTo, value := l.name) |
242 | + " " |
243 | + hsubmit("Rename list") + " or " + hbutton("Delete list", name := "delete", type := "submit", value := 1, onClick := "return confirm('Really delete?')") |
244 | ); |
245 | } |
246 | |
247 | // New list |
248 | S newList = trim(params.get('newList)); |
249 | if (nempty(newList)) { |
250 | AIList l = getList(newList); |
251 | ret hrefresh(rawLink("/list/" + urlencode(l.name))); |
252 | } |
253 | |
254 | // Overview |
255 | |
256 | L<AIList> list = alphabetical |
257 | ? sortedByFieldIC('name, list(AIList)) |
258 | : sortedByFieldDesc('modified, list(AIList)); |
259 | |
260 | fS prefix = session == null ? null : session.mech_filter; |
261 | if (nempty(prefix)) |
262 | list = [AIList l : list | swic(l.name, prefix)]; |
263 | |
264 | ret hmobilefix() |
265 | + hcomment("cookie=" + cookie) |
266 | + htitle_h3("mech.botcompany.de [" + n2(l(list), "list") |
267 | + (totalChars >= 0 ? ", " + toK(totalChars) + "K chars" : "") |
268 | + "]") |
269 | + htableRaw_singleRow(ll( |
270 | hform("Search: " + htextinput('q, style := "width: 200px") + " " + hsubmit("Search")), |
271 | hpostform("| New list: " + htextinput('newList, style := "background-color: yellow") + " " + hsubmit("Create list")))) |
272 | + hBoolSelector(alphabetical, "/", "By date", "/alphabetical", "Alphabetical") |
273 | + listLists(list) |
274 | + hpostform("Only show lists starting with: " |
275 | + htextinput('mech_filter, value := prefix) |
276 | + " " + hsubmit("OK")); |
277 | } |
278 | |
279 | sS nav() { |
280 | ret hmobilefix() + p(ahref(rawLink(), "<< back")); |
281 | } |
282 | |
283 | static AIList getList_noCreate(S name) { |
284 | ret getList(name, false); |
285 | } |
286 | |
287 | static AIList getList(S name) { |
288 | ret getList(name, true); |
289 | } |
290 | |
291 | static AIList getList(S name, bool create) { |
292 | lock dbLock(); |
293 | AIList l = findConcept(AIList, +name); |
294 | if (l == null) l = nameIndex.get(name); |
295 | if (l == null) if (!create) null; else { |
296 | l = cnew(AIList, +name); |
297 | logStructure("versions.log", litmap(id := l.id, +name, date := now(), mode := "List created")); |
298 | updateListNamesList(); |
299 | } |
300 | if (l.modified == 0) cset(l, modified := now()); |
301 | ret l; |
302 | } |
303 | |
304 | sS listLists(L<AIList> list) { |
305 | ret ul(map(list, func(AIList l) -> S { |
306 | int n = lineCountOfList(l); |
307 | new L<S> status; |
308 | bool german = eq('german, mech_guessLanguage_quick(l.name)); |
309 | //if (l.mighty) status.add(german ? "GENUTZT" : "USED"); |
310 | addIfNempty(status, htmlencode(l.status)); |
311 | if (n != 0) status.add(n2(n, german ? "Zeile" : "line", german ? "Zeilen": "lines")); |
312 | ret ahref(rawLink("/list/" + urlencode(l.name)), |
313 | htmlencode(or2(l.name, "[no name]")) + appendBracketed(joinWithComma(status)); |
314 | })); |
315 | } |
316 | |
317 | static L<AIList> scoredSearch_AIList(S query, Iterable<AIList> data) { |
318 | new Map<AIList, Int> scores; |
319 | L<S> prepared = scoredSearch_prepare(query); |
320 | for (AIList l : data) |
321 | putUnlessZero(scores, l, |
322 | 3*scoredSearch_score(l.name, prepared) |
323 | + 2*scoredSearch_score(l.status, prepared) |
324 | + scoredSearch_score(l.text, prepared)); |
325 | ret keysSortedByValuesDesc(scores); |
326 | } |
327 | |
328 | svoid calcTotalSize { |
329 | long total = 0; |
330 | for (AIList l) |
331 | total += l(l.text); |
332 | totalChars = total; |
333 | } |
334 | |
335 | sS export_getListText(S listName) { |
336 | AIList l = getList_noCreate(listName); |
337 | ret l == null ? "" : l.text; |
338 | } |
339 | |
340 | sS export_setListText(S listName, S text) { |
341 | AIList l = getList(listName); |
342 | if (eq(l.text, text)) ret "No change"; |
343 | logStructure("versions.log", litmap(id := l.id, +text, date := now(), mode := "bot edit")); |
344 | cset(l, +text, textMD5 := md5(text), lines := -1, botFlag := true, modified := now()); |
345 | listTextChanged(l); |
346 | ret "Changed"; |
347 | } |
348 | |
349 | sO serveListText(S uri, SS params, bool authed) { |
350 | new Matches m; |
351 | if (startsWith(uri, "/list-text/", m)) { |
352 | bool opt = eq("1", params.get("opt")); |
353 | bool withStatus = eq("1", params.get("withStatus")); |
354 | bool create = authed && eq("1", params.get("create")); |
355 | S md5 = params.get("md5"); |
356 | int md5Len = parseIntOpt(params.get('l)); |
357 | S name = urldecode(m.get(0)); |
358 | lock dbLock(); |
359 | AIList l = create ? getList(name) : getList_noCreate(name); |
360 | if (!authed && l != null && !l.isPublicRead()) |
361 | l = null; |
362 | //ret subBot_serve404("Not logged in"); |
363 | cset(l, mighty := true); |
364 | if (l == null) |
365 | if (opt) |
366 | ret serveText(jsonEncode(litmap("Text" := ""))); |
367 | else |
368 | ret serveText(jsonEncode(litmap("Error", ll("List not found", name)))); |
369 | else { |
370 | Map map = litmap("Name" := l.name, "Status" := withStatus ? l.status : null); |
371 | if (md5 != null) { |
372 | S actualMD5 = md5OfList(l); |
373 | print("md5: " + md5 + " / " + actualMD5); |
374 | if (eq(actualMD5, md5)) |
375 | ret serveText(jsonEncode(mapPlus(map, "Same" := true))); |
376 | else if (md5Len != 0 && eq(md5(takeFirst(l.text, md5Len)), md5)) |
377 | ret serveText(jsonEncode(mapPlus(map, "Appended" := true, "Text" := substring(l.text, md5Len)))); |
378 | } |
379 | ret serveText(jsonEncode(mapPlus(map, "Text" := l.text))); |
380 | } |
381 | } |
382 | null; |
383 | } |
384 | |
385 | sS md5OfList(AIList l) { |
386 | lock dbLock(); |
387 | if (l.textMD5 == null) l.textMD5 = md5(l.text); |
388 | ret l.textMD5; |
389 | } |
390 | |
391 | static int lineCountOfList(AIList l) { |
392 | lock dbLock(); |
393 | if (l.lines < 0) l.lines = countLines(l.text); |
394 | ret l.lines; |
395 | } |
396 | |
397 | static L<AIList> publicReadLists() { |
398 | ret [AIList l : list(AIList) | l.isPublicRead()]; |
399 | } |
400 | |
401 | svoid updateListNamesList { |
402 | lock dbLock(); |
403 | AIList l = getList("All public-read mech lists"); |
404 | TreeSet<S> actual = new TreeSet(collect(publicReadLists(), 'name)); |
405 | Set<S> listed = asHashSet(lines(l.text)); |
406 | if (nempty(setMinusSet(listed, actual))) |
407 | listReplaceWithLog(l, lines(actual)); |
408 | else |
409 | listAppendWithLog(l, lines(setMinusSet(actual, listed))); |
410 | } |
411 | |
412 | svoid listAppendWithLog(AIList l, S text) { |
413 | if (emptyAfterTrim(text)) ret; |
414 | logStructure("versions.log", litmap(id := l.id, +text, date := now(), mode := "bot append")); |
415 | cset(l, text := appendNewLineIfNempty(rtrim(l.text)) + text, botFlag := true, modified := now(), textMD5 := null, lines := -1); |
416 | listTextChanged(l); |
417 | } |
418 | |
419 | svoid listReplaceWithLog(AIList l, S text) { |
420 | if (eq(text, l.text)) ret; |
421 | logStructure("versions.log", litmap(id := l.id, +text, date := now(), mode := "bot edit")); |
422 | cset(l, +text, textMD5 := md5(text), lines := -1, botFlag := true, modified := now()); |
423 | listTextChanged(l); |
424 | } |
425 | |
426 | static new ThreadLocal<Bool> listTextChanged_noRecurse; |
427 | |
428 | svoid listTextChanged(AIList l) { |
429 | if (!isTrue(listTextChanged_noRecurse!) && cic(l.status, "auto global ids")) pcall { |
430 | L<S> entries = splitAtEmptyLines(l.text); |
431 | S text = joinWithEmptyLines(map nlLogic_addGlobalID(entries)); |
432 | temp tempSetThreadLocal(listTextChanged_noRecurse, true); |
433 | listReplaceWithLog(l, text); |
434 | } |
435 | |
436 | if (!isTrue(listTextChanged_noRecurse!) && cic(l.status, "gazelle ids")) pcall { |
437 | L<S> entries = splitAtEmptyLines(l.text); |
438 | S text = joinWithEmptyLines(map gazelle_addGlobalID(entries)); |
439 | temp tempSetThreadLocal(listTextChanged_noRecurse, true); |
440 | listReplaceWithLog(l, text); |
441 | } |
442 | } |
443 | |
444 | sS mechList_opt_raw_fresh(S name) { |
445 | ret export_getListText(name); |
446 | } |
447 | |
448 | static L<AIList> listsForAuth(bool authed) { |
449 | ret authed ? list(AIList) : publicReadLists(); |
450 | } |
download show line numbers debug dex old transpilations
Travelled to 17 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt, xfbsdwenvhih, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1013927 |
Snippet name: | mech[anics].botcompany.de |
Eternal ID of this version: | #1013927/199 |
Text MD5: | bd2cb8ceb7b253536d0f6cfa85e59605 |
Transpilation MD5: | 99c1928160ca703f2fafe3733d4451d9 |
Author: | stefan |
Category: | javax / a.i. / web |
Type: | JavaX module (desktop) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-03-30 18:43:09 |
Source code size: | 15249 bytes / 450 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 1370 / 7334 |
Version history: | 198 change(s) |
Referenced in: | [show references] |