Warning: session_start(): open(/var/lib/php/sessions/sess_ujm11k2pu77f51tc696de68ro3, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
!7
// See #1023660 for the older 99 lines version
concept PhysicalSlice { new Ref slicePage; }
concept Page {
new Ref slice;
S globalID = aGlobalID();
S url;
}
concept Entry {
S globalID = aGlobalID();
new Ref page;
int count;
S key, value;
S ip;
new Ref signer;
}
concept Signer {
S globalID = aGlobalID();
S publicKey;
bool trusted;
S approvedBy;
}
concept Session {
S cookie;
Page slicePage;
}
static int displayLength = 140;
static int lines;
sbool allowMultipleCasesInValues = true;
static ConceptFieldIndexDesc idx_latestEntries, idx_latestCreatedPages, idx_latestChangedPages;
p {
dbIndexingCI(Page, 'url, Entry, 'key, Entry, 'value);
dbIndexing(Signer, 'publicKey);
dbIndexing(Session, 'cookie);
idx_latestCreatedPages = new ConceptFieldIndexDesc(Page, 'created);
idx_latestChangedPages = new ConceptFieldIndexDesc(Page, '_modified);
idx_latestEntries = new ConceptFieldIndexDesc(Entry, 'created);
// Approve this machine's key
PKIKeyPair machineKey = agiBot_trustedKeyForMachine();
if (machineKey != null) {
print("Approving this machine's key: " + machineKey.publicKey);
cset(uniq_sync(Signer, publicKey := machineKey.publicKey), trusted := true, approvedBy := "local");
}
lines = countLines(mySource());
}
// DB functions
sbool hasPage(S url) { ret hasConceptWhereIC(Page, +url); }
sS getValue(Page page, S key) {
ret getString value(highestByField count(objectsWhereIC(findBackRefs(page, Entry), +key)));
}
sS pageDisplayName(Page page) {
S name = getValue(page, "read as");
bool unnaturalName = nempty(name) && !eq(makeAGIDomain(name), page.url);
ret unnaturalName ? name + " " + squareBracketed(page.url) : or2(name, unpackAGIDomainOpt(page.url));
}
// Serve page
set flag NoNanoHTTPD. html {
ret new Request().serve(uri, params);
}
sclass Request {
S cookie;
Session session;
O serve(S uri, SS params) {
new Matches m;
cookie = cookieFromUser();
if (cookie != null)
session = uniq_sync(Session, +cookie);
else
session = unlistedWithValues(Session);
if (swic(uri, "/bot/", m)) ret serveBot("/" + m.rest(), params);
S q = params.get('q);
S domain = or2(params.get('domain), domain());
if (nempty(q)) {
domain = makeAGIDomain(q);
if (l(domain) > maximumDomainPartLength()) // escape with "domain="
ret hrefresh("http://agi.blue/" + hquery(+domain, key := "read as", value := q));
ret hrefresh("http://" + domain + (eq(q, domain) ? "" : "/" + hquery(key := "read as", value := q)));
//uri = "/"; replaceMapWithParams(params, key := "read as", value := q);
}
S url = domain + dropTrailingSlash(uri);
Page page, bool newPage = unpair uniqCI2_sync(Page, +url);
if (newPage) dbLog("New page", +url);
S top = hcomment("cookie: " + takeFirst(4, session.cookie)) + p(ahref("http://agi.blue", hsnippetimg(#1101682, width := 565/5, height := 800/5, title := "Robot by Peerro @ DeviantArt")))
+ p(small(b(ahref("http://agi.blue", "agi.blue"))
+ " | " + 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")));
if (eqicOneOf(url, "agi.blue", "www.agi.blue"))
ret hhtml(hhead_title("agi.blue Overview") // HOME PAGE
+ hbody_fullcenter(top
+ hform(b("GIVE ME INPUT:") + "
" + htextinput('q) + " " + hsubmit("Ask"))
+ h1("agi.blue has " + nPages(countConcepts(Page)))
+ p(nlToBr(nemptyLines(map(pageToHTMLLink(), sortedByFieldDesc _modified(list(Page))))))
));
S key = trim(params.get('key)), value = trim(params.get('value));
L entries = GetEntriesAndPost().go(page, params).entries;
S get = params.get('get);
if (nempty(get))
ret serveText(jsonEncode(collect value(llNotNulls(firstWhereIC(entries, key := get)))));
S key2 = key, value2 = value; if (nempty(key) && nempty(value)) key2 = value2 = ""; // input reset
bool withHidden = eq(params.get('withHidden), "1");
new Set hide;
if (!withHidden) for (Entry e : entries) if (eqic(e.key, 'hide) && isSquareBracketedInt(e.value)) addAll(hide, e.count, parseInt(unSquareBracket(e.value)));
new MultiMap mmMeta;
for (Entry e : entries) if (isSquareBracketedInt(e.key)) mmMeta.put(parseInt(unSquareBracket(e.key)), e.value);
new MultiMap mm;
for (Entry e : entries) mm.put(e.key, e.value);
S name = or2(/* ouch */ last(mm.get("read as")), /* end ouch */ unpackAGIDomain(page.url), page.url);
L valueRefs = conceptsWhereIC Entry(value := name);
Set valueRefPages = asSet(ccollect page(valueRefs));
valueRefPages.remove(page);
S mainContents =
top + h1(ahref_unstyled("http://" + url, htmlEncode2(name)) + (newPage ? " [huh????]" : ""))
+ p(nlToBr(nemptyLines(map(entries, func(Entry e) -> S {
!withHidden && (hide.contains(e.count) || eqic(e.key, "read as") && eqic(e.value, name)) ? "" : "[" + e.count + "] " +
htmlencode2(e.key) + ": " + b(
isURL(e.value)
|| cic(mmMeta.get(e.count), "is a URL")
|| isAGIDomain(e.value)
? ahref(fixAGILink(absoluteURL(e.value)), htmlencode2(shorten(displayLength, e.value)))
: ahref(agiBlue_linkForPhrase(e.value), htmlencode2(shorten(displayLength, e.value)))
) }))))
+ hpostform(h3("Add an entry")
+ "Key: " + hinputfield(key := key2) + " Value: " + hinputfield(value := value2) + "
" + hsubmit("Add")
);
S sideContents =
hform(b("GIVE ME INPUT:") + " " + htextinput('q) + " " + hsubmit("Ask"))
+ h3("References (" + l(valueRefPages) + ")")
+ p(nlToBr(nemptyLines(map(pageToHTMLLink(), takeFirst(10, valueRefPages)))));
// serve a page
ret hhtml(hhead_title(pageDisplayName(page)) + hbody(
tag('table, tr(
td(mainContents, align := 'center, valign := 'top) +
td(sideContents, align := 'right, valign := 'top)),
width := "100%", height := "100%")));
}
}
svoid dbLog(O... params) {
logStructure(programFile("db.log"), ll(params));
}
// uri = without the "/bot" in front
sO serveBot(S uri, SS params) {
S q = params.get('q), url = params.get('url);
if (nempty(q)) url = makeAGIDomain(q);
if (eqic(uri, "/hello")) ret serveJSON("hello");
if (eqic(uri, "/hasPage")) ret serveJSON(hasPage(url));
if (eqic(uri, "/randomPageContaining")) {
assertNempty(q);
ret servePageToBot(random(filter(list(Page), p -> cic(p.url, q))), params);
}
if (eqic(uri, "/allPagesEndingWith")) {
assertNempty(q);
ret servePagesToBot(filter(list(Page), p -> ewic(p.url, q)), params);
}
if (eqicOneOf(uri, "/postSigned", "/makePhysicalSlice", "/approveTrustRequest")) {
S text = rtrim(params.get('text));
S key = getSignerKey(text);
if (empty(key)) ret subBot_serve500("Please include your public key");
if (!isSignedWithKey(text, key)) ret subBot_serve500("Signature didn't verify");
text = dropLastTwoLines(text); // drop signer + sig line
Signer signer = uniq_sync Signer(publicKey := key);
if (eqic(uri, "/makePhysicalSlice")) {
if (!signer.trusted) ret subBot_serve500("Untrusted signer");
Page page = findPageFromParams(jsonDecodeMap(text));
if (page == null) ret subBot_serve500("Page not found");
ret jsonEncode(uniq2_sync(PhysicalSlice, slicePage := page).b ? "Slice made" : "Slice exists");
}
if (eqic(uri, "/postSigned")) {
new L out;
for (S line : tlft(text)) {
SS map = jsonDecodeMap(line);
new GetEntriesAndPost x;
x.signer = signer;
Page page = findOrMakePageFromParams(map);
if (page == null) continue with out.add("Page not found: " + quote(url));
x.go(page, map);
out.add(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value");
}
ret serveJSON(out);
}
if (eqic(uri, "/approveTrustRequest")) {
if (!signer.trusted) ret subBot_serve500("Untrusted signer");
Signer toApprove = conceptWhere Signer(publicKey := trim(text));
if (toApprove == null) ret subBot_serve500("Signer to approve not found");
cset(toApprove, trusted := true, approvedBy := signer.globalID);
ret serveJSON("Approved: " + trim(text));
}
ret subBot_serve500("CONFUSION");
}
if (eqic(uri, "/post")) {
new GetEntriesAndPost x;
x.go(conceptWhereCI Page(+url), params);
ret serveJSON(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value");
}
if (eqic(uri, "/latestEntries"))
ret serveJSON_shallowLineBreaks(map(takeFirst(10, idx_latestEntries.objectIterator()), entryToMap(true)));
if (eqic(uri, "/latestPages"))
ret serveJSON_shallowLineBreaks(map(takeFirst(10, idx_latestCreatedPages.objectIterator()), pageToMap()));
if (eqic(uri, "/latestChangedPages"))
ret serveJSON_shallowLineBreaks(map(takeFirst(10, idx_latestChangedPages.objectIterator()), pageToMap()));
if (eqic(uri, "/totalPageCount"))
ret serveJSON(countConcepts(Page));
if (eqic(uri, "/physicalSliceCount"))
ret serveJSON(countConcepts(PhysicalSlice));
if (eqic(uri, "/trustedSignersCount"))
ret serveJSON(countConcepts(Signer, trusted := true));
if (eqic(uri, "/valueSearch")) {
S value = params.get('value);
L entries = conceptsWhereIC Entry(+value);
ret serveJSON_shallowLineBreaks(map(takeFirst(100, entries), entryToMap(true));
}
// end of bot functions
ret subBot_serve404();
}
static IF1 pageToMap(O... _) {
bool withEntries = boolPar withEntries(_);
ret (IF1) p -> {
L entries = findBackRefs(p, Entry);
ret litorderedmap(
url := p.url,
nEntries := l(entries),
created := p.created,
modified := p._modified,
entries := !withEntries ? null : map(entries, entryToMap(false)));
};
}
static IF1 entryToMap(bool withPage) {
ret (IF1) e -> litorderedmap(
created := e.created,
i := e.count,
key := e.key,
value := e.value,
url := withPage ? e.page->url : null,
signer := getString globalID(e.signer!));
}
sO servePageToBot(Page page, SS params) {
if (page == null) ret serveJSON(null);
params = asCIMap(params);
Map map = pageToMap(withEntries := valueIs1 withEntries(params)).get(page);
ret serveJSON(map);
}
sO servePagesToBot(Iterable pages, SS params) {
ret serveJSON(map(pageToMap(), pages));
}
sclass GetEntriesAndPost {
L entries;
Entry entry;
bool newEntry;
Signer signer;
GetEntriesAndPost go(Page page, SS params) {
S key = trim(params.get('key)), value = trim(params.get('value));
withDBLock {
entries = findBackRefs(page, Entry);
if (nempty(key) && nempty(value)) {
S ip = subBot_clientIP();
entry = firstThat(e -> eqic(e.key, key) && eq_icIf(!allowMultipleCasesInValues, e.value, value) && eq(e.ip, ip), entries);
if (entry == null) {
print("SAVING");
Entry e = cnew Entry(+page, +key, +value, +ip, count := l(entries) + 1, +signer);
page.change(); // bump modification date
entry = e;
newEntry = true;
entries.add(entry);
dbLog("New entry", page := page.url, globalID := e.globalID, count := e.count, +key, +value);
}
}
}
sortByFieldInPlace created(entries);
numberEntriesInConceptField count(entries);
this;
}
}
static Page findPageFromParams(Map map) {
S url = nempty(getString q(map)) ? makeAGIDomain(getString q(map)) : getString url(map);
ret empty(url) ? null : conceptWhereCI Page(+url);
}
static Page findOrMakePageFromParams(Map map) {
S url = nempty(getString q(map)) ? makeAGIDomain(getString q(map)) : getString url(map);
ret empty(url) ? null : uniqCI_sync Page(+url);
}
static F1 pageToHTMLLink() {
ret func(Page p) -> S {
ahref(fixAGILink("http://" + p.url), htmlEncode2(pageDisplayName(p)))
};
}