import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import java.math.*;
import org.jsoup.*;
import org.jsoup.nodes.*;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.text.*;
import java.text.NumberFormat;
import javax.net.ssl.*;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import net.dv8tion.jda.core.events.Event;
import net.dv8tion.jda.core.events.user.update.*;
import net.dv8tion.jda.core.events.guild.member.*;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.requests.restaction.MessageAction;
import java.util.function.Consumer;
import javax.swing.border.*;
import java.awt.datatransfer.StringSelection;
import javax.swing.event.AncestorListener;
import javax.swing.event.AncestorEvent;
import javax.swing.Timer;
import javax.swing.undo.UndoManager;
import javax.imageio.metadata.*;
import javax.imageio.stream.*;
import java.awt.geom.*;
import net.dv8tion.jda.core.*;
import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.hooks.*;
import net.dv8tion.jda.core.events.message.*;
import net.dv8tion.jda.core.events.message.react.*;
class main {
abstract static class DynNLRulesBot extends DynTalkBot2 {
void init() {
super.init();
useAGIBlueForDropPunctuation = false;
preprocessAtSelfToMyName = false;
dropPunctuation = false;
print("Total rules in all guilds: " + totalRuleCount());
}
static class Rule {
GlobalID globalID = aGlobalIDObj();
String text;
Rule() {} Rule(String text) {
this.text = text;}
}
String renderRule(Rule r) {
return r == null ? null : "Rule " + r.globalID + ": " + r.text;
}
class ByServer extends DynTalkBot2.ByServer {
List rules = new ArrayList();
Set priorityChannels = new HashSet(); // where we always respond
boolean enableMagicQuotes = true;
synchronized String processSimplifiedLine(String s, Object... __) {
String __1 = super.processSimplifiedLine(s, __); if (!empty(__1)) return __1;
print("s=" + s);
Matches m = new Matches();
String s2 = dropMyPrefixOrNull(s);
if (s2 != null) s = s2;
else if (!contains(priorityChannels, optPar("channelID",__))) return null;
Message msg = (Message) (optPar("msg",__));
print("msg=" + msg);
Message.Attachment attachment = msg == null ? null : first(msg.getAttachments());
if (attachment != null) {
long guildID = idForByServer(this);
File file = prepareCacheProgramFile(guildID + "/" + attachment.getFileName());
print("Downloading attachment: " + file);
deleteFile(file);
if (!attachment.download(file)) return "Couldn't download attachment";
List lines = tlft(loadTextFile(file));
print("First line: " + first(lines));
if (!swic(first(lines), "rule ")) print("Ignoring attachment");
else {
List> toImport = new ArrayList(); // (globalID, text)
for (String line : lines) {
List l = regexpICFullMatch_groups("Rule ([a-z]+):(.*)$", line);
if (nempty(l))
toImport.add(listToPair(l));
}
String found = "Found " + nRules(toImport) + " in attachment";
int oldCount = l(rules);
Map existing = indexByField("globalID",rules);
int updated = 0, added = 0;
for (Pair rule : toImport) {
GlobalID id = new GlobalID(rule.a);
String text = trim(rule.b);
Rule r = existing.get(id);
if (r == null) {
r = new Rule(text);
r.globalID = id;
rules.add(r);
existing.put(id, r);
++added;
} else if (neq(r.text, text)) {
r.text = text;
++updated;
}
}
if (added+updated > 0) change();
return found + ". Had: " + oldCount + ". Added: " + added + ". Updated: " + updated + ". New count: " + l(rules);
}
}
// duplicated patterns go here
if (match("on * say *", s, m) || match("on * or * say *", s, m) || match("on keyword * and * say *", s, m) || match("on keyword * or * say *", s, m) || match("on keyword * say *", s, m) || match("on *, image-google X", s, m) || match("on *, google X", s, m) || match("when <*> comes online, say *", s, m)) {
Rule r = firstWhereIC(rules, "text" , s);
if (r != null) return "Rule exists: " + r.globalID;
rules.add(r = new Rule(s));
change();
return "Rule added with ID " + r.globalID;
}
if (match("rules", s))
return or2(lines(map(r -> renderRule(r), rules)), "No rules defined yet");
if (match("rules as file", s)) {
uploadFileInChannel(longPar("channelID",__),
toUtf8(lines(map(r -> renderRule(r), rules))),
genericTextFileName(), null, null);
return null;
}
if (match("delete rule *", s, m) || match("delete rule with id *", s, m)) {
String __2 = checkAuth(__); if (!empty(__2)) return __2;
Rule r = firstWhere(rules, "globalID" , new GlobalID(m.unq(0)));
if (r == null) return "Rule " + m.unq(0) + " not found";
rules.remove(r);
change();
return "Rule deleted, " + nRule(rules) + " remain";
}
if (match("delete all rules", s)) {
appendToTextFile(programFile("backups.txt"), guildStructure());
{ String __3 = checkAuth(__); if (!empty(__3)) return __3; }
rules.clear();
change();
return "All rules deleted";
}
if (match("priority channel on", s)) {
String __4 = checkAuth(__); if (!empty(__4)) return __4;
priorityChannels.add(longOptPar("channelID",__)); change();
return "OK";
}
if (match("priority channel off", s)) {
String __5 = checkAuth(__); if (!empty(__5)) return __5;
priorityChannels.remove(optPar("channelID",__)); change();
return "OK";
}
if (match("disable magic quotes", s)) {
String __6 = checkAuth(__); if (!empty(__6)) return __6;
enableMagicQuotes = false; change();
return "OK";
}
if (eqic(s, "help"))
return ltrim("\r\nI execute simple English rules.\r\n\r\nStuff to say.\r\n@me `on \"hello bot\" say \"who are you\"`\r\n@me `on keyword \"time\" say \"it is always time for a tea\"`\r\n@me `on \"what is X\" google X`\r\n@me `on \"show me X\" image-google X`\r\n@me `when @user comes online, say \"hello friend\"`\r\n@me `rules` / `rules as file` - list rules\r\n@me `delete rule abcdefghijkl` - delete rule with ID\r\n@me `priority channel on` - respond to every msg\r\n@me `priority channel off` - respond to msgs with my name only\r\n@me `masters` / `add master ` / `delete master `\r\n@me `help` / `support` / `source code`\r\n@me `download` - download my brain for this guild\r\n\r\nTo fill me with rules, just upload the file you got from `rules as file` to the channel.\r\n\r\n[Bot made by https://BotCompany.de]\r\n ").replace("@me", atSelf());
// find rule to execute
LinkedHashSet outs = new LinkedHashSet();
for (Rule r : rules) { try {
// IMPORTANT: patterns are duplicated above
if (match("on * say *", r.text, m)) {
String in = m.unq(0), out = m.unq(1);
if (match(in, s))
outs.add(rewrite(out, __));
}
if (match("on * or * say *", r.text, m)) {
String in1 = m.unq(0), in2 = m.unq(1), out = m.unq(2);
if (match(in1, s) || match(in2, s))
outs.add(rewrite(out, __));
}
if (match("on keyword * and * say *", r.text, m)) {
String in1 = m.unq(0), in2 = m.unq(1), out = m.unq(2);
if (find3(in1, s) && find3(in2, s))
outs.add(rewrite(out, __));
}
if (match("on keyword * or * say *", r.text, m)) {
String in1 = m.unq(0), in2 = m.unq(1), out = m.unq(2);
if (find3(in1, s) || find3(in2, s))
outs.add(rewrite(out, __));
}
if (match("on keyword * say *", r.text, m)) {
String in = m.unq(0), out = m.unq(1);
if (find3(in, s))
outs.add(rewrite(out, __));
}
if (match("on *, image-google X", r.text, m))
if (flexMatchIC(replaceWord(m.unq(0), "X", "*"), s, m)) {
boolean nsfw = discord_isNSFWChannel_gen(optPar("channel",__));
print("nsfw", nsfw);
BufferedImage img = nsfw ? quickVisualize_nsfw(m.unq(0)) : quickVisualize(m.unq(0));
if (img == null) return "No image found, sorry!";
postImage(paramsToMap(__), uploadToImageServer(img, m.unq(0)));
}
if (match("on *, google X", r.text, m))
if (flexMatchIC(replaceWord(m.unq(0), "X", "*"), s, m)) {
boolean nsfw = discord_isNSFWChannel_gen(optPar("channel",__));
print("nsfw", nsfw);
return discord_google(m.unq(0), "results" , 1, "safeSearch" , !nsfw);
}
} catch (Throwable __e) { _handleException(__e); }}
if (match("download", s)) {
Guild guild = (Guild) (optPar("guild",__));
MessageChannel channel = (MessageChannel) (optPar("channel",__));
return serveGuildBackup(channel, guild,
structure_nullingInstancesOfClass(_getClass(module()), ByServer.this));
}
if (match("stats", s))
return "I have " + nRules(rules);
{ String __7 = random(outs); if (!empty(__7)) return __7; }
if (enableMagicQuotes)
{ String __8 = answer_magicQuoteInput(s, "useTranspiled" , true); if (!empty(__8)) return __8; }
return null;
}
String rewrite(String answer, Object... __) {
if (containsIC(answer, "")) {
String name = (String) (optPar("name",__));
if (empty(name)) name = discord_optParUserName(__);
if (nempty(name))
answer = replaceIC(answer, "", name);
}
return answer;
}
void onOnlineStatusChange(UserUpdateOnlineStatusEvent e) {
User user = e.getMember().getUser();
print("User status change: " + user + " " + e.getOldValue() + " -> " + e.getNewValue());
// only react to user coming online
if (e.getOldValue() != OnlineStatus.OFFLINE) return;
long id = user.getIdLong();
LinkedHashSet outs = new LinkedHashSet();
Matches m = new Matches();
for (Rule r : rules) { try {
// IMPORTANT: patterns are duplicated above
if (match("when <*> comes online, say *", r.text, m)) {
long _id = parseLongOpt(m.unq(0));
String out = m.unq(1);
if (id == _id)
outs.add(rewrite(out, "name" , user.getName()));
}
} catch (Throwable __e) { _handleException(__e); }}
postInChannel(preferredChannelID, random(outs));
}
}
long totalRuleCount() {
return intSumAsLong(map(syncCloneValues(dataByServer), bs -> l(bs.rules)));
}
}
static volatile StringBuffer local_log = new StringBuffer(); // not redirected
static volatile Appendable print_log = local_log; // might be redirected, e.g. to main bot
// in bytes - will cut to half that
static volatile int print_log_max = 1024*1024;
static volatile int local_log_max = 100*1024;
static boolean print_silent = false; // total mute if set
static Object print_byThread_lock = new Object();
static volatile ThreadLocal