!7 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(); long msgID; S context; bool bot, isPrivate; Channel channel; User author; S text; S editedText; new L reactions; S extraInfo; // like images posted long channelID() { ret channel == null ? 0 : channel.channelID; } S userName() { ret author == null ? null : author.name; } } cmodule GLines > DynCRUD { S saveProfile; start { dbIndexing(Line, 'msgID, Line, 'context, Line, 'globalID); 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(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 conceptsWhere Line(+channel, author := user); } L linesBy(long userID) { User user = conceptWhere User(+userID); if (user == null) null; ret conceptsWhere Line(author := user); } L linesInChannel(long channelID) { Channel channel = conceptWhere Channel(+channelID); if (channel == null) null; ret 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); } L botLines() { ret conceptsWhere Line(bot := true); } L allChannelIDs() { ret collect channelID(list(Channel)); } }