!7 set flag makeConceptsTable_debug. concept User { long userID; S name; toString { ret name; } } concept Channel { long channelID; S name; toString { ret name; } } sclass Reaction { long created; User user; S emoji; toString { ret emoji + " (" + user + ")"; } } concept Line { S globalID /*= aGlobalIDUnlessLoading()*/; // deprecated GlobalID globalIDObj = aGlobalIDObj(); long msgID; S context; bool bot, isPrivate; Channel channel; User author; S text; S editedText; L reactions; S extraInfo; // like images posted long channelID() { ret channel == null ? 0 : channel.channelID; } S userName() { ret author == null ? null : author.name; } // save space, transition void _doneLoading2() { if (empty(reactions)) _setField(reactions := null); if (globalID != null) { _setField(globalIDObj := globalIDObj(globalID)); _setField(globalID := null); } } } module GLines > DynCRUD { S saveProfile; start { dbIndexing(Line, 'msgID, Line, 'context, Line, 'globalIDObj); conceptsObject().saveWrapper = voidfunc(Runnable r) { setField(saveProfile := profileThisThreadToString(r)); }; addCountToName(); } // API User uniqUser(long userID) { ret uniq_sync User(+userID); } // old Channel uniqChannel(long channelID) { ret uniq_sync Channel(+channelID); } // new Channel uniqChannel(S channelName) { long channelID = md5_long(channelName); ret uniq_sync Channel(+channelID, name := channelName); } Reaction nuReaction(O... params) { ret nu Reaction(params); } long getChannelID(S name) { ret getLongOrZero channelID(conceptWhere Channel(name := dropPrefix("#", name))); } Line lastLineInChannel(long channelID) { Channel channel = conceptWhere Channel(+channelID); if (channel == null) null; ret last(conceptsWhere Line(+channel)); } Line nextToLastLineInChannel(long channelID) { Channel channel = conceptWhere Channel(+channelID); if (channel == null) null; ret nextToLast(asList(conceptsWhere Line(+channel))); } L linesInChannelBy(long channelID, long userID) { Channel channel = conceptWhere Channel(+channelID); if (channel == null) null; User user = conceptWhere User(+userID); if (user == null) null; ret asList(conceptsWhere Line(+channel, author := user)); } L linesBy(long userID) { User user = conceptWhere User(+userID); if (user == null) null; ret asList(conceptsWhere Line(author := user)); } L linesInChannel(long channelID) { Channel channel = conceptWhere Channel(+channelID); if (channel == null) null; ret asList(conceptsWhere Line(+channel)); } long idForChannel(S name) { ret getChannelID(name); } // attention: Discord user names may change long idForUser(S name) { ret getLong userID(conceptWhere User(+name)); } int monologueLength(long channelID) { L lines = linesInChannel(channelID); int n = 0; while (n < l(lines) && lines.get(l(lines)-1-n).bot) ++n; ret n; } Line lineForID(long msgID) { ret conceptWhere Line(+msgID); } Line lineForContext(S context) { ret conceptWhere Line(+context); } Line lineForGlobalID(S globalID) { ret conceptWhere Line(globalIDObj := globalIDObj(globalID)); } L botLines() { ret asList(conceptsWhere Line(bot := true)); } L allChannelIDs() { ret collect channelID(list(Channel)); } L linesModifiedAfter(long date) { ret filter(concepts(), line -> line._modified >= date); } }