Warning: session_start(): open(/var/lib/php/sessions/sess_8s4ceil1casnvu9lkqt6oft3mi, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
!7
set flag DynModule.
sclass ReasoningForLine {
long timestamp = now();
long outMsgID;
S outText;
long inMsgID, inUserID, inChannelID;
S inText;
GazelleTree tree;
int treeIndex; // line chosen from tree
}
cmodule DiscordBot > DynPrintLogAndEnabled {
transient JDA bot;
transient new Set msgsReactedTo;
transient double deleteDelay = 60.0;
transient bool doDelete;
transient int maxMonologue = 5;
transient Color imageEmbedMysteriousLineColor = colorFromHex("36393f");
transient new InheritableThreadLocal currentChannel;
transient new InheritableThreadLocal respondingTo;
transient new L lastPosted;
transient volatile GazelleEvalContext cachedCtx;
start {
logModuleOutput();
dm_useLocalMechListCopies();
dm_vmBus_onMessages(r { cachedCtx = null },
'gazelleRulesChanged, 'gazelleFeedbackChanged);
// TODO: log JDA output
bot = discordBot(new ListenerAdapter {
public void onMessageReceived(MessageReceivedEvent e) pcall {
temp enter();
ret if !enabled || !licensed();
bool bot = e.getAuthor().isBot();
long msgID = e.getMessage().getIdLong();
long userID = e.getAuthor().getIdLong();
S userName = e.getMember().getNickname(); // the changeable nick name - null sometimes?
if (userName == null) userName = e.getMember().getEffectiveName();
if (userName == null) userName = e.getAuthor().getName();
final Message msg = e.getMessage();
print("Channel type: " + e.getChannelType());
bool isPrivate = e.getChannelType() == ChannelType.PRIVATE;
S content = trim(msg.getContentRaw());
print("Msg from " + userName + ": " + content);
if (empty(content)) ret;
fS originalContent = content;
O user = userConcept(e.getAuthor());
S crud = dm_gazelle_linesCRUD();
O channel = dm_call(crud, 'uniqChannel, msg.getChannel().getIdLong());
dm_call(crud, 'cset, channel, litobjectarray(name := msg.getChannel().getName()));
O lineConcept = dm_call(crud, 'uniqConcept, litObjectArrayAsObject(+msgID));
dm_call(crud, 'cset, lineConcept, litobjectarray(
text := content,
+bot, +isPrivate,
author := user, +channel));
vmBus_send('newDiscordLine, lineConcept);
final MessageChannel ch = e.getChannel();
long channelID = ch.getIdLong();
L linesInChannel = dm_discord_linesInChannel(channelID);
if (bot) {
int monologueLength = cast dm_call(dm_gazelle_linesCRUD(), 'monologueLength, channelID);
if (monologueLength >= maxMonologue) ret;
//Long last = get(lastPosted, l(lastPosted)-3);
//if (last != null && now() < last+10000) ret;
}
new Matches m;
try {
if (swicOneOf(content, "!eval ", "!fresh ", "!real-eval", "!safe-eval ") || eqic(content, "!fresh")) {
O safetyCheck = null;
bool authed = dm_discord_userCanEval(userID);
if (swic(content, "!safe-eval ", m)) {
content = "!eval " + m.rest();
authed = false;
}
if (!authed) {
//ret with postInChannel(ch, "Sorry, you're not authed to eval");
safetyCheck = func(S code) -> S {
S safety = joinWithComma(getCodeFragmentSafety(code));
if (eq(safety, 'safe)) ret "";
S s = "Sorry. Safety level is " + safety;
Set unknown = codeAnalysis_getUnknownIdentifiers(code);
if (nempty(unknown)) s += ". Unknown identifiers: " + joinWithComma(unknown);
ret s;
};
}
temp tempSetTL(currentChannel, ch);
temp tempSetTL(respondingTo, msg);
ret with dm_bot_execEvalCmd(voidfunc(S s) {
if (s != null)
postInChannel(ch, shorten(ifEmpty(s, [[""]]), 1000))
}, content, +safetyCheck);
}
if (eqic(content, "!rule")) {
LS lines = linesInChannelBy(channelID, userID);
if (contains(nextToLast(lines), "=>"))
content = "!rule " + nextToLast(lines);
else {
if (l(lines) < 3) fail("Too few lines");
content = "!rule " + nextToNextToLast(lines) + " => " + nextToLast(lines);
}
}
if (swicOneOf(content, m, "!rule ", "!rule\n")) {
S s = trim(m.rest());
print("Processing rule: " + s);
S opt = leadingSquareBracketStuff(s);
s = dropActuallyLeadingSquareBracketStuff(s);
if (startsWith(s, "=>"))
s = assertNotNull("No last line in channel", nextToLast(linesInChannelBy(channelID, userID))) + "\n" + s;
LS comments = ll("discord");
if (cic(pairB(tok_splitAtDoubleArrow_pair(s)), userName)) {
s = optCurly(userName) + " says: " + s;
comments.add("with user name");
}
if (match("with *", opt, m) && remoteMechListMirror_isPublic($1))
comments.add("use helper table mech list " + quote($1));
s = replace(s, " + ", "\n+ ");
s = jreplace1(s, "=>", "\n=>");
s = gazelle_processSquareBracketAnnotations(s, comments);
dm_gazelle_addRuleWithComment(s, lines_rtrim(comments));
ret with postInChannel(ch, dm_gazelle_addRuleWithComment_msg!);
}
bool skipBad = true;
if (swic_trim(content, "!withBad ", m)) {
skipBad = false;
content = m.rest();
}
LS preContext = takeLast(2, dropLast(collect text(linesInChannel)));
print("preContext=" + preContext);
GazelleEvalContext ctx = cachedCtx;
if (ctx == null) {
GazelleEvalContext _cachedCtx = dm_gazelle_stdEvalContext(dm_gazelle_allRulesWithComment("discord"));
cachedCtx = _cachedCtx;
ctx = gazelle_cloneCtx(_cachedCtx);
}
L l = dm_gazelle_reasonAboutChatInput_v2(userName, content, +skipBad, +preContext, +ctx);
int idx = 0;
for (final GazelleTree t : takeFirst(10, l)) {
if (eqic(t.lineType, "temporary fact")) {
if (dm_gazelle_hasTempFact(t.line)) continue;
print("Saving temp fact: " + t.line);
dm_gazelle_addTempFact(t.line, "discord msg " + msgID);
}
final int _idx = idx++;
fS out = tok_dropCurlyBrackets(t.line);
print(">> " + t);
print("POSTING: " + out);
postInChannelWithDelete(ch, out, voidfunc(Message msg2) {
ReasoningForLine reasoning = nu(ReasoningForLine,
outMsgID := msg2.getIdLong(),
outText := out,
inMsgID := msg.getIdLong(),
inUserID := userID,
inChannelID := channelID,
inText := originalContent,
tree := t,
treeIndex := _idx);
saveGZStructToFileVerbose(gazelle_discord_reasoningFileForMsgID(reasoning.outMsgID), reasoning);
});
}
} catch print error {
postInChannel(ch, exceptionToStringShort(error));
}
}
public void onMessageReactionAdd(MessageReactionAddEvent e) pcall {
temp enter();
ret if !enabled || !licensed();
MessageReaction r = e.getReaction();
bool bot = e.getUser().isBot();
long msgID = r.getMessageIdLong();
add(msgsReactedTo, msgID);
print("User " + e.getUser() + (bot ? " (bot)" : "") + " reacted to message " + msgID + " with " + r.getReactionEmote());
if (bot) ret;
S crud = dm_gazelle_linesCRUD();
O lineConcept = dm_call(crud, 'uniqConcept, litObjectArrayAsObject(+msgID));
L reactions = cast get(lineConcept, 'reactions);
print("lineConcept=" + lineConcept);
print("reactions=" + reactions);
O reaction = dm_call(crud, 'nuReaction, litObjectArrayAsObject(
user := userConcept(e.getUser()),
emoji := r.getReactionEmote().getName(),
created := now()));
print("reaction=" + reaction);
dm_call(crud, 'cset, lineConcept, litobjectarray(
reactions := listPlus(reactions, reaction)));
dm_discord_gatherFeedbackFromLine(dm_discord_importLine(lineConcept));
}
});
dm_registerAs('discordBot);
print("Started bot");
}
void cleanMeUp {
if (bot != null) pcall {
print("Shutting down bot");
bot.shutdown();
print("Bot shut down");
}
bot = null;
}
O userConcept(User user) {
S crud = dm_gazelle_linesCRUD();
O userConcept = dm_call(crud, 'uniqUser, user.getIdLong());
dm_call(crud, 'cset, userConcept, litobjectarray(name := user.getName()));
ret userConcept;
}
// API
void postInChannel(long channelID, S msg) {
postInChannel(bot.getTextChannelById(channelID), msg);
}
void postInChannel(MessageChannel channel, S msg) {
channel.sendMessage(msg).queue();
if (l(lastPosted) > 5) popFirst(lastPosted);
lastPosted.add(now());
}
void postInChannel(S channel, S msg) {
long id = dm_discord_channelID(channel);
if (id == 0) fail("Channel not found: " + channel);
postInChannel(id, msg);
}
void postInChannelWithDelete(MessageChannel channel, S msg, VF1 onPost) {
channel.sendMessage(msg).queue(msg2 -> {
pcallF(onPost, msg2);
final long msgId = msg2.getIdLong();
print("I sent msg: " + msgId);
if (doDelete) doLater(deleteDelay, r {
ret if contains(msgsReactedTo, msgId);
print("Deleting msg " + msgId);
msg2.delete().queue();
});
}, error -> _handleException(error));
}
void postImage(S url) {
postImage(currentChannel!, url);
}
void postImage(S url, S title) {
postImage(currentChannel!, url, title);
}
void postImage(MessageChannel channel, S url) {
postImage(channel, url, "");
}
void postImage(MessageChannel channel, S url, S description) {
channel.sendMessage(
new EmbedBuilder()
.setImage(absoluteURL(url))
//.setTitle(unnull(title))
.setDescription(unnull(description))
.setColor(imageEmbedMysteriousLineColor)
.build()).queue();
}
LS linesInChannelBy(long channelID, long userID) {
ret collect text(dm_discord_linesInChannelBy(channelID, userID));
}
MessageChannel currentChannel() { ret currentChannel!; }
Message respondingTo() { ret respondingTo!; }
}