!7 concept Msg { long msgID, channelID, userID; long date, edited; // epochSeconds S text; } standardBot1 EAMBot { init { dbIndexing(Msg, 'msgID); } allServers { bool indexAllIncoming = true; enhanceFrame { internalFrameMenuItem(f, "Show word list", rThread { S list = makeWordsList(); showText_fast_noWrap("Word list (" + countLines(list) + ")", list); }); } S makeWordsList() { new Map map; // word -> date new MultiSet ms; // word count for ping (Msg msg : sortedByField date(list(Msg))) { Long date = msg.date; for ping (S word : words2_notNextToNumbers_plusApostrophe(msg.text)) { map.put(word, date); ms.add(word); } } ret mapToLines(keysSortedByValuesDesc(map), w -> { int count = ms.get(w); ret count == 1 ? w : count + " " + w; }); } } sync S processSimplifiedLine(S s, O... _) { optPar Message msg; if (indexAllIncoming) storeMsg(msg); try answer super.processSimplifiedLine(s, _); if ((s = dropMyPrefixOrNull(s)) == null) null; optPar MessageChannel channel; if "upload word list" { S list = makeWordsList(); editMechList("NGB Word List", list); ret "OK. " + nWords(countLines(list)); } if "retrieve history" { setAll(new Scan, +channel, incremental := true).run(); ret "Retrieving..."; } if "retrieve full channel" { setAll(new Scan, +channel, incremental := false).run(); ret "Retrieving..."; } if "retrieve all channels" { L l = getGuild().getTextChannels(); new AtomicInt counter; Runnable whenDone = r { if (incAtomicInt(counter) == l(l)) postInChannel(channel, "Incremental scan of " + nChannels(l) + " done"); }; for ping (TextChannel c : l) setAll(new Scan, channel := c, incremental := true, postResult := false, +whenDone).run(); } if "mega retrieve" { try answer checkAuth(_); L l = getGuild().getTextChannels(); new AtomicInt counter; Runnable whenDone = r { if (incAtomicInt(counter) == l(l)) postInChannel(channel, "Full scan of " + nChannels(l) + " done"); }; for ping (TextChannel c : l) setAll(new Scan, channel := c, incremental := false, postResult := false, +whenDone).run(); } if "db size" ret nMessages(countConcepts(Msg)); if "random message" { Msg msg = random(list(Msg)); ret msg == null ? "No messages" : "[" + msg.msgID + "] " + msg.text; } null; } class Scan { MessageChannel channel; MessageHistory history; int scanned; bool incremental, postResult = true; Runnable whenDone; run { if (history == null) history = channel.getHistory(); history.retrievePast(100).queue(msgs -> { temp enter(); print("Got msgs: " + l(msgs)); if (empty(msgs)) { if (postResult) postInChannel(channel, "Done scanning " + discordChannelRef(channel.getIdLong()) + " (" + nMessages(scanned) + ")"); callF(whenDone); ret; } scanned += l(msgs); if (storeMsgs(msgs) && incremental) { if (postResult) postInChannel(channel, "Stopping incremental scan of " + discordChannelRef(channel.getIdLong()) + " after " + nMessages(scanned)); callF(whenDone); ret; } run(); }, onQueueError); } } bool storeMsgs(L l) { bool anySeen; for (Message msg : l) { Msg m, bool isNew = unpair uniq2(Msg, msgID := msg.getIdLong()); if (!isNew) set anySeen; cset(m, channelID := msg.getTextChannel().getIdLong(), userID := msg.getAuthor().getIdLong(), date := msg.getCreationTime().toEpochSecond(), edited := msg.isEdited() ? msg.getEditedTime().toEpochSecond() : 0, text := msg.getContentRaw()); } ret anySeen; } }