Warning: session_start(): open(/var/lib/php/sessions/sess_vjf5e2d5al75376gdoatbp3fj1, 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 JDA40.
set flag DynModule.
// Note: Bot is always started & logs lines,
// but doesn't respond when enabled is false.
cm DiscordBot > DynPrintLogAndEnabled {
transient JDA bot;
transient new Set msgsReactedTo;
transient double deleteDelay = 60.0;
transient bool doDelete;
transient int maxMonologue = 5;
transient bool selfTalk = false;
transient Color imageEmbedMysteriousLineColor = colorFromHex("36393f");
transient new InheritableThreadLocal currentChannel;
transient new InheritableThreadLocal respondingTo;
transient new L lastPosted;
transient GazelleContextCache_v2 contextCache;
transient Set acceptablePurposes = litciset("");
switchable S gazoogleDefaultCountry = "us";
// map user command message id to bot message id
// (for editing)
Map cmdToResponse = syncMap();
start-thread {
vm_cleanPrints();
logModuleOutput();
dm_useLocalMechListCopies();
print("Making cache");
time "Made cache" {
contextCache = new GazelleContextCache_v2(me());
ownResource(contextCache.listenToMessages());
contextCache.fill();
}
// TODO: log JDA output
bot = discordBot40(new ListenerAdapter {
public void onMessageUpdate(MessageUpdateEvent e) pcall {
temp enter();
ret if !enabled || !licensed();
long msgID = e.getMessage().getIdLong();
S content = e.getMessage().getContentRaw();
print(content := quote(content));
O lineConcept = dm_discord_lineForMsgID_unimported(msgID);
S rendered = msgID + ": " + content;
if (lineConcept == null)
ret with print("Weird: Message author, but not found: " + rendered);
call(lineConcept, '_setField, editedText := content);
print("Message edited: " + rendered);
handleMessage(e, e.getAuthor(), e.getMember(), e.getMessage(), false);
}
public void onMessageReceived(MessageReceivedEvent e) pcall {
temp enter();
ret if !licensed();
handleMessage(e, e.getAuthor(), e.getMember(), e.getMessage(), true);
}
void handleMessage(GenericMessageEvent e, User author, Member member, Message message, bool newMsg) {
bool bot = author.isBot();
long msgID = message.getIdLong();
long userID = author.getIdLong();
S userName = member == null ? null : member.getNickname(); // the changeable nick name - null sometimes?
if (userName == null && member != null) userName = member.getEffectiveName();
if (userName == null) userName = author.getName();
final Message msg = message;
print("Channel type: " + e.getChannelType());
bool isPrivate = e.getChannelType() == ChannelType.PRIVATE;
S content = trim(backtickUnquote(trim(msg.getContentRaw())));
print("Msg from " + userName + ": " + content);
if (empty(content)) ret;
fS originalContent = content;
O user = userConcept(author);
if (newMsg) {
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);
}
ret if !enabled;
final MessageChannel ch = e.getChannel();
long channelID = ch.getIdLong();
L linesInChannel = dm_discord_linesInChannel(channelID);
// monologue prevention
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;
}
temp tempSetTL(currentChannel, ch);
temp tempSetTL(respondingTo, msg);
// module meta ("gazelle stop" etc.)
S userForModulesManager = "discord user " + userID;
if (!bot) {
for (T3S t : dm_gazelle_allRulesWithPurpose("moduleMeta")) {
PairS p = splitAtDoubleArrow_pair(t.a);
if (eqic(p.b, "stop module") && nempty(p.a) && matchX2(p.a, content)) {
print("Stopping modules");
postText((S) dm_call(dm_gazelle_modulesManager(), 'deleteAllModulesForUser, userForModulesManager, silentIfEmpty := true));
}
}
}
new Matches m;
try {
content = fixNewLines(content);
content = replacePrefix("!eval\n", "!eval ", content);
// G command - power word search
if (regexpFindIC("^g\\b", content)) {
S answer = loadPageWithParams("https://gazelle.rocks/beaHTML/274191", text := content, user := userName);
if (nempty(answer))
ret with postInChannel(ch, newMsg, msgID, answer);
}
// GAZOOGLE command - TODO: find out when it's just a sentence starting with "gazoogle"
S googleQuery = regexpFirstGroupIC("^gazoogle\\b[,:.!]*\\s*(.*)", content);
if (nempty(googleQuery)) {
/*if (newMsg)*/ sendTyping();
MapSO urlParams = litmap(gl := gazoogleDefaultCountry);
ret with postInChannel(ch, newMsg, msgID, or2_rev("Gazoogle doesn't know :(", lines(map(dm_googleSearch_all(googleQuery, urlParams),
result -> result! + " <" + result.url() + ">"))));
}
// GAZELLETUBE command (YouTube search)
S ytQuery = regexpFirstGroupIC("^gaz\\w*tube\\b[,:.!]*\\s*(.*)", content);
if (nempty(ytQuery)) {
/*if (newMsg)*/ sendTyping();
ret with postInChannel(ch, newMsg, msgID,
or2_rev("GazelleTube is showing a black screen :(",
mapSingle(dm_youTubeSearch(ytQuery),
result -> result.url())));
}
if (swicOneOf(content, "!eval ", "!fresh ", "!real-eval ", "!safe-eval ") || eqic(content, "!fresh")) {
O safetyCheck = null;
bool authed = dm_discord_userCanEval(userID);
if (swic_trim(content, "!safe-eval ", m)) {
content = "!eval " + m.rest();
authed = false;
}
if (regexpMatches("![a-z\\-]+\\s+again", content)) {
GazelleLine last = dm_discord_nextToLastEvalByUser(userID);
if (last == null) ret with postInChannel(ch, newMsg, msgID, "No last eval found");
content = replaceSuffix("again", afterSpace(last.text), content);
}
if (!authed)
safetyCheck = botEval_strictSafetyCheck();
/*if (newMsg)*/ sendTyping();
var post = voidfunc(S s) {
if (s != null)
postInChannel(ch, newMsg, msgID, shorten(ifEmpty(s, [[""]]), 1000))
};
if (startsWith(content, "!eval ", m))
ret with dm_bot_execFreshRealEval(post, m.rest(), +safetyCheck);
else
ret with dm_bot_execEvalCmd(post, content, +safetyCheck, alwaysFresh := true);
}
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("made by user", "discord", "tokenize out with javaTok");
if (cic(pairB(tok_splitAtDoubleArrow_pair(s)), userName)) {
s = optCurly(userName) + " says: " + s;
comments.add("with user name");
}
gazelle_parsePublicRuleOptions(opt, comments);
s = replace(s, " + ", "\n+ ");
s = jreplace1(s, "=>", "\n=>");
s = gazelle_processSquareBracketAnnotations(s, comments);
temp tempSetTL(dm_gazelle_addRuleWithComment_renderWithoutComments, true);
dm_gazelle_addRuleWithComment(s, lines_rtrim(comments));
ret with postText(dm_gazelle_addRuleWithComment_msg!);
}
if (swic_trim(content, "!phrase ", m)) {
gazelle_createRuleForPhrase(m.rest());
ret with postText(dm_gazelle_addRuleWithComment_msg!);
}
// Go into normal reasoning
if (bot && !selfTalk) ret;
S purpose = null;
bool debug = false, skipBad = true;
Set restrictToRules = null;
bool change;
do {
change = false;
if (swic_trim(content, "!withBad ", m)) {
skipBad = false;
content = m.rest(); set change;
}
if (swic_trim(content, "!preprocess ", m)) {
purpose = 'preprocess;
content = m.rest(); set change;
}
if (swic_trim(content, "!debug ", m)) {
set debug;
content = m.rest(); set change;
}
if (swic(content, "!using[", m)) {
int i = indexOf(m.rest(), ']');
restrictToRules = litset(substring(m.rest(), 0, i));
content = trimSubstring(m.rest(), i+1);
}
} while (change);
print("debug=" + debug + ", content=" + content);
LS preContext = takeLast(2, dropLast(collect text(linesInChannel)));
print("preContext=" + preContext);
GazelleContextCache_v2 contextCache = DiscordBot.this.contextCache;
if (restrictToRules != null) {
contextCache = gazelle_cloneContextCache(contextCache);
Set _restrictToRules = restrictToRules;
print("restrictToRules=" + restrictToRules);
contextCache.cachedCtx.engine.dropRulesWhere(r -> !contains(_restrictToRules, r.globalID));
print("Using rules: " + collect globalID(contextCache.cachedCtx.engine.rules));
}
O[] params = litmapparams(+userName, +skipBad, +preContext,
badComments := mechCISet("Knock-out rule comments"),
acceptablePurposes := nempty(purpose)
? litciset(purpose)
: acceptablePurposes,
respondingToHuman := !bot,
+debug,
+userID,
contextMaker := contextCache,
debugPreprocessing := true,
sendToModules := func(S input) -> L {
gazelle_wrapLinesAsTree(gazelle_answersFromModules(userForModulesManager, input))
});
if (eq(purpose, 'preprocess))
ret with postText(lines_rtrim(takeFirst(10, gazelle_preprocess(content, params))));
L l;
if (nempty(purpose))
l = gazelle_postprocess(dm_gazelle_reasonAboutChatInput_v2(userName, content, params));
else
l = gazelle_reason_repeat(content, params);
int idx = 0;
for (final GazelleTree t : l) {
final int _idx = idx++;
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);
}
if (eqic(t.lineType, "eval")) {
S code = t.rule().out;
if (!mechSet("Gazelle | Allowed Evals").contains(code))
print("Eval not allowed: " + code);
else {
/*if (newMsg)*/ sendTyping();
S out = strOrNull(dm_javaEvalOrInterpret(code));
if (nempty(out))
postInChannelWithDelete(ch, out, voidfunc(Message msg2) {
Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine,
outMsgID := msg2.getIdLong(),
outText := out,
inMsgID := msg.getIdLong(),
inUserID := userID,
inChannelID := channelID,
inText := originalContent,
tree := t,
treeIndex := _idx);
dm_gazelle_saveReasoningForLine(reasoning);
});
}
continue;
}
// Now that we actually post, store recently used mapping
gazelle_storeRecentlyUsedMappings(ll(t),
context := "discord channel " + channelID);
fS out = tok_dropCurlyBrackets(t.line);
print(">> " + t);
print("POSTING: " + out);
postInChannelWithDelete(ch, out, voidfunc(Message msg2) {
Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine,
outMsgID := msg2.getIdLong(),
outText := out,
inMsgID := msg.getIdLong(),
inUserID := userID,
inChannelID := channelID,
inText := originalContent,
tree := t,
treeIndex := _idx);
dm_gazelle_saveReasoningForLine(reasoning);
});
}
} catch print error {
postInChannel(ch, newMsg, msgID, 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
MessageChannel getChannel(long channelID) {
ret bot.getTextChannelById(channelID);
}
/*void postInChannel(long channelID, S msg) {
postInChannel(getChannel(channelID), msg);
}*/
void postInChannel(MessageChannel channel, S msg) {
postInChannel(channel, true, 0, msg);
}
void postInChannel(MessageChannel channel, bool newMsg, long userMsgID, S msg) {
if (!newMsg) {
Long responseID = cmdToResponse.get(userMsgID);
if (responseID != null) {
editMessage(channel.getIdLong(), responseID, msg);
ret;
}
}
S postID = cast dm_call(gazelle_postedLinesCRUD(), 'postingLine, channel.getId(), msg);
channel.sendMessage(msg).queue(m -> {
dm_call(gazelle_postedLinesCRUD(), 'donePosting, postID, "discord msg " + m.getId());
cmdToResponse.put(userMsgID, m.getIdLong());
change();
});
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) {
S postID = cast dm_call(gazelle_postedLinesCRUD(), 'postingLine, channel.getId(), msg);
channel.sendMessage(msg).queue(msg2 -> {
dm_pcall(gazelle_postedLinesCRUD(), 'donePosting, postID, "discord msg " + msg2.getId());
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 postText(S text) {
postInChannel(currentChannel!, text);
}
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(); // TODO: posted lines
}
LS linesInChannelBy(long channelID, long userID) {
ret collect text(dm_discord_linesInChannelBy(channelID, userID));
}
MessageChannel currentChannel() { ret currentChannel!; }
Message respondingTo() { ret respondingTo!; }
void sendTyping() {
currentChannel().sendTyping().queue();
}
// for testing, outdated
L reasonAbout(S line, O... _) {
ret gazelle_reasonAbout(line, paramsPlus(_, ctx := contextCache!));
}
void rebuildCache {
contextCache.fill();
}
void editMessage(long channelID, long msgID, S text) {
getChannel(channelID).editMessageById(str(msgID), text).queue();
}
S respondingToUserID() {
ret respondingTo! == null ? null : "discord user " + respondingTo->getAuthor().getIdLong();
}
void uploadFileInChannel(File file) {
uploadFileInChannel(file, "", null);
}
void uploadFileInChannel(File file, S msg, IVF1 onPost) {
var channel = currentChannel!;
MessageAction a = empty(msg)
? channel.sendFile(file, fileName)
: channel.sendMessage(msg).addFile(file, fileName);
a.queue(onPost, onQueueError);
}
}