!752 static class Line { S id; S text; S type; new L on; Source source; } static PersistentLog data; static new Map byID; static long nextID; p { data = new PersistentLog("data"); for (Line line : data) index(line); load("nextID"); } synchronized answer { if "dn size" ret "Dialog network: " + n(data.size(), "line"); if "dn add *" exceptionToUser{ new Line line; line.text = m.unq(0); line.type = "user-added"; add(line, new Source(s)); ret "Yo! Your ID: " + line.id; } if (matchAny(litlist("dn add * on *", "dn add * to *"), s, m)) exceptionToUser { S ids = m.unq(1); new Line line; line.text = m.unq(0); line.type = "user-added"; line.on.addAll(splitIDs(ids)); add(line, new Source(s)); ret "Yo! Your ID: " + line.id; } if "dn show *" exceptionToUser { S id = clearID(m.unq(0)); Line line = byID.get(id); ret line == null ? format("Line * not found", id) : structure(line); } if "dn refs *" exceptionToUser { S id = clearID(m.unq(0)); L refs = findRefsTo(id); ret empty(refs) ? "No references found" : formatList(refs); } if "dn search *" exceptionToUser { S query = m.unq(0); L refs = search(query); ret empty(refs) ? "No results" : formatList(refs); } if "dn clear yes i am sure" { if (master()) { clear(); ret "OK"; } } if "dn dump" ret "OK " + structure(data); if "dn copy from sister" exceptionToUser { O sisterBot = getBot("#1002626"); if (sisterBot == null) ret "No sister bot (install #1002626!)"; S url = getString(get(sisterBot, "sister"), "robotURL"); S q = "dn dump"; S a = loadPage(url + "?" + makePostData("q", q)); a = htmldecode(a); a = dropPrefixMandatory("OK ", a); L newData = cast unstructure(a); // we're trusting the other server here as its URL was given to us by master if (empty(newData)) ret "Empty data, did nothing"; int l = l(data); clear(); data.addAll(newData); for (Line line : newData) index(line); fixNextID(); ret "OK, cleared " + l + " entries, added " + l(newData) + " entries."; } } static S formatList(L lines) { new L l; for (Line line : lines) { S text = line.text; l.add(line.id + ": " + (text.contains("\n") ? quote(text) : text)); } ret slackSnippet(fromLines(l)); } static synchronized Line add(S s) { new Line line; line.text = s; add(line, new Source("")); ret line; } static void add(Line line, Source source) { line.source = source; line.id = newID(); data.add(line); index(line); } static void index(Line line) { byID.put(line.id, line); } static S newID() { S id = "_" + ++nextID; save("nextID"); ret id; } static L findRefsTo(S id) { new L l; for (Line line : data) if (line.on.contains(id)) l.add(line); ret l; } static L search(S query) { new L l; for (Line line : data) if (indexOfIgnoreCase(line.text, query) >= 0) l.add(line); ret l; } static S clearID(S _id) { S id = dropPrefix("_", _id); if (!isInteger(id)) fail("woot bad id: " + quote(_id)); ret "_" + id; } static L splitIDs(S s) { new L l; for (S id : s.split("[ ,]+")) l.add(clearID(id)); ret l; } static void clear() { pcall { logQuoted("data.clear", loadTextFile(data.file)); } data.clear(); byID.clear(); } static synchronized L findStarters() { new L l; for (Line line : data) if (empty(line.on)) l.add(line); ret l; } static void fixNextID() { long id = 0; for (Line line : data) id = max(id, parseLong(dropPrefix("_", line.id))); nextID = id+1; save("nextID"); } static L getReferences(Line line) { ret findRefsTo(line.id); } // here's the magic! might as well match loosely static Line findStarter(S s) { for (Line line : findStarters()) if (match(s, line.text)) ret line; ret null; }