Download Jar. Libraryless. Click here for Pure Java version (14324L/100K).
1 | !7 |
2 | |
3 | // See #1023660 for the older 99 lines version |
4 | |
5 | concept Page { |
6 | S url; |
7 | } |
8 | |
9 | concept Entry { |
10 | S globalID = aGlobalID(); |
11 | new Ref<Page> page; |
12 | int count; |
13 | S key, value; |
14 | S ip; |
15 | new Ref<Signer> signer; |
16 | } |
17 | |
18 | concept Signer { |
19 | S globalID = aGlobalID(); |
20 | S publicKey; |
21 | } |
22 | |
23 | static int lines; |
24 | sbool allowMultipleCasesInValues = true; |
25 | static ConceptFieldIndexDesc idx_latestEntries, idx_latestCreatedPages, idx_latestChangedPages; |
26 | |
27 | p { |
28 | dbIndexingCI(Page, 'url, Entry, 'key, Entry, 'value); |
29 | dbIndexing(Signer, 'publicKey); |
30 | idx_latestCreatedPages = new ConceptFieldIndexDesc(Page, 'created); |
31 | idx_latestChangedPages = new ConceptFieldIndexDesc(Page, '_modified); |
32 | idx_latestEntries = new ConceptFieldIndexDesc(Entry, 'created); |
33 | lines = countLines(mySource()); |
34 | } |
35 | |
36 | // DB functions |
37 | |
38 | sbool hasPage(S url) { ret hasConceptWhereIC(Page, +url); } |
39 | sS getValue(Page page, S key) { |
40 | ret getString value(highestByField count(objectsWhereIC(findBackRefs(page, Entry), +key))); |
41 | } |
42 | sS pageDisplayName(Page page) { |
43 | S name = getValue(page, "read as"); |
44 | bool unnaturalName = nempty(name) && !eq(makeAGIDomain(name), page.url); |
45 | ret unnaturalName ? name + " " + squareBracketed(page.url) : or2(name, unpackAGIDomainOpt(page.url)); |
46 | } |
47 | |
48 | // Serve page |
49 | |
50 | set flag NoNanoHTTPD. html { |
51 | new Matches m; |
52 | |
53 | if (swic(uri, "/bot/", m)) ret serveBot("/" + m.rest(), params); |
54 | |
55 | S q = params.get('q); |
56 | S domain = or2(params.get('domain), domain()); |
57 | if (nempty(q)) { |
58 | domain = makeAGIDomain(q); |
59 | if (l(domain) > maximumDomainPartLength()) // escape with "domain=" |
60 | ret hrefresh("http://agi.blue/" + hquery(+domain, key := "read as", value := q)); |
61 | ret hrefresh("http://" + domain + (eq(q, domain) ? "" : "/" + hquery(key := "read as", value := q))); |
62 | //uri = "/"; replaceMapWithParams(params, key := "read as", value := q); |
63 | } |
64 | S url = domain + dropTrailingSlash(uri); |
65 | Page page, bool newPage = unpair uniqCI2_sync(Page, +url); |
66 | if (newPage) dbLog("New page", +url); |
67 | |
68 | S top = hcomment("new") + p(ahref("http://agi.blue", hsnippetimg(#1101682, width := 565/5, height := 800/5, title := "Robot by Peerro @ DeviantArt"))) |
69 | + p(small(b(ahref("http://agi.blue", "agi.blue")) |
70 | + " | " + targetBlank(progLink(), "source code") + " of this web site (" + nLines(lines) + ") | " + targetBlank("https://gitter.im/agi-blue/community", "sponsor https?") + " | by " + targetBlank("https://BotCompany.de", "BC") + " | " + targetBlank("http://fiverr.tinybrain.de/", "Fiverr") + " | " + targetBlank("https://discordapp.com/invite/SEAjPqk", "Discord") + " | " + targetBlank("https://www.youtube.com/watch?v=b6jtRdV3Ev8", "Video"))); |
71 | |
72 | if (eqicOneOf(url, "agi.blue", "www.agi.blue")) |
73 | ret hhtml(hhead_title("agi.blue Overview") // HOME PAGE |
74 | + hbody_fullcenter(top |
75 | + hform(b("GIVE ME INPUT:") + "<br><br>" + htextinput('q) + " " + hsubmit("Ask")) |
76 | + h1("agi.blue has " + nPages(countConcepts(Page))) |
77 | + p(nlToBr(nemptyLines(map(func(Page p) -> S { |
78 | ahref(fixAGILink("http://" + p.url), htmlEncode2(pageDisplayName(p))) |
79 | }, sortedByFieldDesc _modified(list(Page)))))) |
80 | )); |
81 | |
82 | S key = trim(params.get('key)), value = trim(params.get('value)); |
83 | L<Entry> entries = GetEntriesAndPost().go(page, params).entries; |
84 | |
85 | S get = params.get('get); |
86 | if (nempty(get)) |
87 | ret serveText(jsonEncode(collect value(llNotNulls(firstWhereIC(entries, key := get))))); |
88 | |
89 | S key2 = key, value2 = value; if (nempty(key) && nempty(value)) key2 = value2 = ""; // input reset |
90 | |
91 | bool withHidden = eq(params.get('withHidden), "1"); |
92 | new Set<Int> hide; |
93 | if (!withHidden) for (Entry e : entries) if (eqic(e.key, 'hide) && isSquareBracketedInt(e.value)) addAll(hide, e.count, parseInt(unSquareBracket(e.value))); |
94 | new MultiMap<Int, S> mmMeta; |
95 | for (Entry e : entries) if (isSquareBracketedInt(e.key)) mmMeta.put(parseInt(unSquareBracket(e.key)), e.value); |
96 | new MultiMap<S> mm; |
97 | for (Entry e : entries) mm.put(e.key, e.value); |
98 | |
99 | S name = or2(/* ouch */ last(mm.get("read as")), /* end ouch */ unpackAGIDomain(page.url), page.url); |
100 | |
101 | ret hhtml(hhead_title(pageDisplayName(page)) + hbody(hfullcenterAndTopRight( |
102 | top + h1(ahref_unstyled("http://" + url, htmlEncode2(name)) + (newPage ? " [huh????]" : "")) |
103 | + p(nlToBr(nemptyLines(map(entries, func(Entry e) -> S { |
104 | !withHidden && (hide.contains(e.count) || eqic(e.key, "read as") && eqic(e.value, name)) ? "" : "[" + e.count + "] " + |
105 | htmlencode2(e.key) + ": " + b( |
106 | isURL(e.value) |
107 | || cic(mmMeta.get(e.count), "is a URL") |
108 | || isAGIDomain(e.value) |
109 | ? ahref(fixAGILink(absoluteURL(e.value)), htmlencode2(e.value)) |
110 | : ahref(agiBlue_linkForPhrase(e.value), htmlencode2(e.value)) |
111 | ) })))) |
112 | + hpostform(h3("Add an entry") |
113 | + "Key: " + hinputfield(key := key2) + " Value: " + hinputfield(value := value2) + "<br><br>" + hsubmit("Add") |
114 | ) |
115 | , hform(b("GIVE ME INPUT:") + " " + htextinput('q) + " " + hsubmit("Ask"))))); |
116 | } |
117 | |
118 | svoid dbLog(O... params) { |
119 | logStructure(programFile("db.log"), ll(params)); |
120 | } |
121 | |
122 | // uri = without the "/bot" in front |
123 | sO serveBot(S uri, SS params) { |
124 | S q = params.get('q), url = params.get('url); |
125 | if (nempty(q)) url = makeAGIDomain(q); |
126 | if (eqic(uri, "/hello")) ret serveJSON("hello"); |
127 | if (eqic(uri, "/hasPage")) ret serveJSON(hasPage(url)); |
128 | if (eqic(uri, "/randomPageContaining")) { |
129 | assertNempty(q); |
130 | ret servePageToBot(random(filter(list(Page), p -> cic(p.url, q))), params); |
131 | } |
132 | if (eqic(uri, "/allPagesEndingWith")) { |
133 | assertNempty(q); |
134 | ret servePagesToBot(filter(list(Page), p -> ewic(p.url, q)), params); |
135 | } |
136 | if (eqic(uri, "/postSigned")) { |
137 | S text = rtrim(params.get('text)); |
138 | S key = getSignerKey(text); |
139 | if (empty(key)) ret subBot_serve500("Please include your public key"); |
140 | if (!isSignedWithKey(text, key)) ret subBot_serve500("Signature didn't verify"); |
141 | text = dropLastTwoLines(text); // drop signer + sig line |
142 | new L out; |
143 | for (S line : tlft(text)) { |
144 | SS map = jsonDecodeMap(line); |
145 | new GetEntriesAndPost x; |
146 | x.signer = uniq_sync(Signer, publicKey := key); |
147 | url = map.get('url); |
148 | if (nempty(map.get('q))) url = makeAGIDomain(map.get('q)); |
149 | Page page = conceptWhereCI Page(+url); |
150 | if (page == null) continue with out.add("Page not found: " + quote(url)); |
151 | x.go(page, map); |
152 | out.add(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value"); |
153 | } |
154 | ret serveJSON(out); |
155 | } |
156 | |
157 | if (eqic(uri, "/post")) { |
158 | new GetEntriesAndPost x; |
159 | x.go(conceptWhereCI Page(+url), params); |
160 | ret serveJSON(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value"); |
161 | } |
162 | if (eqic(uri, "/latestEntries")) |
163 | ret serveJSON_shallowLineBreaks(map(takeFirst(10, idx_latestEntries.objectIterator()), entryToMap(true))); |
164 | if (eqic(uri, "/latestPages")) |
165 | ret serveJSON_shallowLineBreaks(map(takeFirst(10, idx_latestCreatedPages.objectIterator()), pageToMap())); |
166 | if (eqic(uri, "/latestChangedPages")) |
167 | ret serveJSON_shallowLineBreaks(map(takeFirst(10, idx_latestChangedPages.objectIterator()), pageToMap())); |
168 | ret subBot_serve404(); |
169 | } |
170 | |
171 | static IF1<Page, Map> pageToMap(O... _) { |
172 | bool withEntries = boolPar withEntries(_); |
173 | ret (IF1<Page, Map>) p -> { |
174 | L<Entry> entries = findBackRefs(p, Entry); |
175 | ret litorderedmap( |
176 | url := p.url, |
177 | nEntries := l(entries), |
178 | created := p.created, |
179 | modified := p._modified, |
180 | entries := !withEntries ? null : map(entries, entryToMap(false))); |
181 | }; |
182 | } |
183 | |
184 | static IF1<Entry, Map> entryToMap(bool withPage) { |
185 | ret (IF1<Entry, Map>) e -> litorderedmap( |
186 | created := e.created, |
187 | i := e.count, |
188 | key := e.key, |
189 | value := e.value, |
190 | url := withPage ? e.page->url : null, |
191 | signer := getString globalID(e.signer!)); |
192 | } |
193 | |
194 | sO servePageToBot(Page page, SS params) { |
195 | if (page == null) ret serveJSON(null); |
196 | params = asCIMap(params); |
197 | Map map = pageToMap(withEntries := valueIs1 withEntries(params)).get(page); |
198 | ret serveJSON(map); |
199 | } |
200 | |
201 | sO servePagesToBot(Iterable<Page> pages, SS params) { |
202 | ret serveJSON(map(pageToMap(), pages)); |
203 | } |
204 | |
205 | sclass GetEntriesAndPost { |
206 | L<Entry> entries; |
207 | Entry entry; |
208 | bool newEntry; |
209 | Signer signer; |
210 | |
211 | GetEntriesAndPost go(Page page, SS params) { |
212 | S key = trim(params.get('key)), value = trim(params.get('value)); |
213 | withDBLock { |
214 | entries = findBackRefs(page, Entry); |
215 | if (nempty(key) && nempty(value)) { |
216 | S ip = subBot_clientIP(); |
217 | entry = firstThat(e -> eqic(e.key, key) && eq_icIf(!allowMultipleCasesInValues, e.value, value) && eq(e.ip, ip), entries); |
218 | if (entry == null) { |
219 | print("SAVING"); |
220 | Entry e = cnew Entry(+page, +key, +value, +ip, count := l(entries) + 1, +signer); |
221 | page.change(); // bump modification date |
222 | entry = e; |
223 | newEntry = true; |
224 | entries.add(entry); |
225 | dbLog("New entry", page := page.url, globalID := e.globalID, count := e.count, +key, +value); |
226 | } |
227 | } |
228 | } |
229 | |
230 | sortByFieldInPlace created(entries); |
231 | numberEntriesInConceptField count(entries); |
232 | this; |
233 | } |
234 | } |
Began life as a copy of #1023558
download show line numbers debug dex old transpilations
Travelled to 6 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1023687 |
Snippet name: | agi.blue source [backup before slices] |
Eternal ID of this version: | #1023687/1 |
Text MD5: | f1301f4a4a5e48ef4fefd7937e187ad2 |
Transpilation MD5: | 1cf5ea27693aa1b8bb48530fa75bf555 |
Author: | stefan |
Category: | javax / html |
Type: | JavaX module (desktop) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2019-07-02 20:22:44 |
Source code size: | 9352 bytes / 234 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 294 / 871 |
Referenced in: | [show references] |