!include #1004681 // Concepts // Anyone's line in a chat (should be called ChatLine I guess) sclass UserLine extends Concept { ELPost post; *() {} *(ELPost *post) { _doneLoading(); } void _doneLoading() { userLines.put(postToID(post), this); } S text() { ret trim(post.text); } S s() { ret text(); } } // A question posed by this program sclass Question extends Concept { new Ref inResponseTo; new Ref userLine; *() {} *(UserLine inResponseTo) { this.inResponseTo.set(inResponseTo); } } // User's answer to one of our questions sclass UserAnswer extends Concept { new Ref inResponseTo; new Ref userLine; } // An answer given by this program sclass Answer extends Concept { new Ref userLine; } static new Map userLines; static File eventLogFile; static L posts; static File lastFile; static long lastLength; static int chatIdx; static void scanChatIfNecessary { eventLogFile = latestEventLogFile(); long l = eventLogFile.length(); bool sameFile; if (!((sameFile = sameFile(eventLogFile, lastFile)) && l == lastLength)) { lastFile = eventLogFile; lastLength = l; posts = scanEventLogForPosts(eventLogFile); psl(posts); if (!sameFile) chatIdx = 0; scanChat(); chatIdx = l(posts); } } static void scanChat { // scan over all new posts in chat for (int i = chatIdx; i < l(posts); i++) { ELPost p = posts.get(i); parsePossibleAnswer(i); processNewUserLine(getUserLine(p)); } } static void parsePossibleAnswer(int chatIdx) { ELPost p = posts.get(chatIdx); S s = trim(p.text); if (startsWith(s, "@")) { s = substring(s, 1); int i = min(xIndexOf(s, ' '), xIndexOf(s, ':')); S n = substring(s, 0, i); if (!isInteger(n)) ret; S answer = trimDropPrefixTrim(":", substring(s, i)); int questionIdx = chatIdx-parseInt(n); registerUserAnswer(chatIdx, questionIdx, answer); } else if (findBackRef(getUserLine(p), Question.class) == null) { registerUserAnswer(chatIdx, chatIdx-1, s); } } static void registerUserAnswer(int chatIdx, int questionIdx, S answer) { ELPost p = posts.get(chatIdx); ELPost q = get(posts, questionIdx); if (q == null) print("Didn't find question to answer " + quote(answer)); else { print("Possible answer: " + q.text + " => " + quote(answer)); UserLine ulQ = getUserLine(q); Question qu = findBackRefOfType(ulQ, Question.class); if (qu == null) print(" Wasn't our question"); else { UserLine ul = getUserLine(p); UserAnswer ua = findBackRefOfType(ul, UserAnswer.class); if (ua != null) print("User answer already registered."); else { ua = new UserAnswer(); ua.userLine.set(ul); ua.inResponseTo.set(qu); print(" Registered user answer as " + ua.id); } } } } static S postToID(ELPost p) { ret p.chatTime + ": " + p.text; } static UserLine getUserLine(ELPost p) { UserLine ul = userLines.get(postToID(p)); if (ul == null) ul = new UserLine(p); // automatically adds to userLines index ret ul; } static UserLine postToChat(S text) { S chatTime = matchOK(sendToLocalBot("Chat.", "please post * to chat file *", text, f2s(eventLogFile))); if (chatTime == null) null; new ELPost post; post.chatTime = chatTime; post.text = text; ret getUserLine(post); } static void action { while licensed { pcall { scanChatIfNecessary(); } pcall { dbActions(); } sleepSeconds(1); } }