Works. Wiped git repository due to hardcoded bot token.

Moved bot token to configuration file, which must be supplied manually before Priestess is first run.

Added Preferences to configuration file search path.

Added command "is minecraft running".
Removed Herobrine.
This commit is contained in:
Sandy Mossgrave 2023-05-07 05:51:57 +00:00
commit 7d97078e43
15 changed files with 1963 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/messages.txt
/priestessconfig.txt
/priestessconfig.txtbkup
/target/

63
pom.xml Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tinyplantnews.priestess</groupId>
<artifactId>Priestess</artifactId>
<version>0.0</version>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.tinyplantnews.priestess.Priestess</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.discord4j</groupId>
<artifactId>discord4j-core</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
<exec.mainClass>com.tinyplantnews.priestess.Priestess</exec.mainClass>
</properties>
</project>

View File

@ -0,0 +1,126 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
import static com.tinyplantnews.priestess.CommandStatus.*;
import com.tinyplantnews.priestess.Priestess.Callback;
import discord4j.core.object.entity.Message;
import discord4j.core.object.entity.channel.MessageChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
/**
*
* @author atomb
*/
public class Command {
public static final HashMap<String, Command> commands = new HashMap<>();
private static final Logger log = Logger.getLogger(Priestess.class.getName());
public static void replyTo(Message m, String s){
if(m != null){
m.getChannel().block().createMessage(s).withMessageReference(m.getId()).block();
} else {
log.log(Level.INFO, s);
}
}
static void tryAll(String commandline, Message m, int permissionLevel) {
for(String s: commands.keySet()){
if(commandline.startsWith(s)){
log.log(Level.INFO, "Matched command " + s);
switch(commands.get(s).tryExecuteCommand(commandline, permissionLevel, m)){
case SUCCESS:
return;
case BADPERM:
replyTo(m, "Insufficient permissions for command.");
return;
case FAILURE:
replyTo(m, String.format("Command %s failed for some reason idfk", s));
return;
}
}
}
replyTo(m, "Unknown command");
}
private final String name;
private final int namelen;
private final int permissionLevel;
private Callback callback;
/**
*
* @param commandline
* @param permissionLevel
* @param a
* @return
*/
public CommandStatus tryExecuteCommand(String commandline, int permissionLevel, Message m){
if(!commandline.startsWith(this.name)){
return NOMATCH;
}
if(permissionLevel < this.permissionLevel){
return BADPERM;
}
String payload = commandline.substring(this.namelen).trim();
this.callback.apply(new CommandArgs(m, payload, permissionLevel));
return SUCCESS;
}
public Command setCallback(Callback c){
this.callback = c;
return this;
}
Command(String name, int permissionLevel){
this.name = name;
this.namelen = name.length();
this.permissionLevel = permissionLevel;
this.callback = new Callback(){
@Override
public Publisher<Mono> apply(CommandArgs t) {
t.replyWith(String.format("Callback for function %s not registered", name));
return null;
}
};
}
public static void registerCommand(Command c){
commands.put(c.name, c);
}
/*= "use emoji";
public static final String REPLYTO_COMMAND = "reply to";
public static final String SENDTO_COMMAND = "send to";*/
/**
* @return the name
*/
public String getName() {
return name;
}
public static Command commandThatJustRepliesWith(String name, String reply){
Command c = new Command(name, 1);
c.setCallback(new Callback(){
@Override
public Publisher<Mono> apply(CommandArgs t) {
t.replyWith(reply);
return Mono.empty();
}
});
return c;
}
}

View File

@ -0,0 +1,51 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
import discord4j.core.object.entity.Message;
import discord4j.core.object.entity.channel.MessageChannel;
/**
*
* @author atomb
*/
public class CommandArgs {
private Message m;
private String payload;
private final int permCalledWith;
public CommandArgs(Message m, String payload, int permCalledWith) {
this.m = m;
this.payload = payload;
this.permCalledWith = permCalledWith;
}
/**
* @return the m
*/
public Message getMessage() {
return m;
}
/**
* @return the payload
*/
public String getPayload() {
return payload;
}
public void replyWith(String s){
Command.replyTo(m, s);
}
/**
* @return the permCalledWith
*/
public int getPermCalledWith() {
return permCalledWith;
}
}

View File

@ -0,0 +1,16 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Enum.java to edit this template
*/
package com.tinyplantnews.priestess;
/**
*
* @author atomb
*/
public enum CommandStatus {
NOMATCH,
SUCCESS,
FAILURE,
BADPERM;
}

View File

@ -0,0 +1,200 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
import java.awt.FileDialog;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
/**
*
* @author twi
*/
public class Configuration {
private static String CONFIG_FILE_LOCATION = null;
public static final String CONFIG_FILE_KEY = "PRIESTESS_CONFIG";
public static final Properties props = System.getProperties();
public static final Preferences prefs = Preferences.userNodeForPackage(Configuration.class);
public static final String DEFAULT_CONFIG_FILE_NAME = "priestessconfig.txt";
private static HashMap<String, String> configuration = new HashMap<>();
private static final Logger log = Logger.getLogger(Configuration.class.getName());
public static String findConfigurationFilePath() {
if (getCONFIG_FILE_LOCATION() != null) {
return getCONFIG_FILE_LOCATION();
}
setCONFIG_FILE_LOCATION(setConfigurationFilePath(), false);
return getCONFIG_FILE_LOCATION();
}
private static String setConfigurationFilePath() {
String s = System.getenv(CONFIG_FILE_KEY);
if (s != null && !s.isBlank()) {
return s;
} else if (props.containsKey(CONFIG_FILE_KEY)) {
return props.getProperty(CONFIG_FILE_KEY);
}
s = prefs.get(CONFIG_FILE_KEY, "");
if (s != null && !s.isBlank()) {
return s;
}
if (Files.exists(Paths.get(DEFAULT_CONFIG_FILE_NAME), LinkOption.NOFOLLOW_LINKS)) {
return DEFAULT_CONFIG_FILE_NAME;
}
//JDialog jd = new JDialog();
//jd.add(new JLabel("Choose where to put Priestess Working Directory"));
//jd.setVisible(true);
JOptionPane.showConfirmDialog(null, "Select where cult files will be stored.");
JFileChooser jfc = new JFileChooser("Config File for Discord Cult Priestess");
jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int rv;
boolean approved = false;
while (!approved) {
rv = jfc.showDialog(null, "Select");
if (rv == JFileChooser.APPROVE_OPTION) {
File f = jfc.getSelectedFile();
if (f.isDirectory() && f.canRead() && f.canWrite()) {
prefs.put(CONFIG_FILE_KEY, f.getAbsolutePath());
try {
prefs.flush();
} catch (BackingStoreException e) {
log.log(Level.WARNING, "Backing store exception while committing configuration file path to persistent storage {0}", e.getMessage());
}
return f.getAbsolutePath();
}
} else if(rv == JFileChooser.CANCEL_OPTION) {
return "";
}
}
return "";
}
private static final String delimiter = ": ";
public static void getConfigurationFromFile() {
if (CONFIG_FILE_LOCATION == null) {
setConfigurationFilePath();
}
try {
if (Files.isDirectory(Paths.get(CONFIG_FILE_LOCATION), LinkOption.NOFOLLOW_LINKS)) {
CONFIG_FILE_LOCATION = CONFIG_FILE_LOCATION + System.getProperty("file.separator") + DEFAULT_CONFIG_FILE_NAME;
}
BufferedReader br = new BufferedReader(new FileReader(Paths.get(CONFIG_FILE_LOCATION).toFile()));
br.lines().forEach((s) -> {
String key = s.substring(0, s.indexOf(delimiter));
if (s.length() == s.indexOf(delimiter) + delimiter.length()) {
System.out.println("read empty value for key (" + key + ") from configuration file");
configuration.put(key, "");
} else {
String value = s.substring(s.indexOf(delimiter) + delimiter.length());
configuration.put(key, value);
}
});
} catch (FileNotFoundException ex) {
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Error loading configuration file from (" + CONFIG_FILE_LOCATION + ") {0}", ex);
}
}
/**
* @return the CONFIG_FILE_LOCATION
*/
public static String getCONFIG_FILE_LOCATION() {
return CONFIG_FILE_LOCATION;
}
/**
* @param aCONFIG_FILE_LOCATION the CONFIG_FILE_LOCATION to set
*/
public static void setCONFIG_FILE_LOCATION(String aCONFIG_FILE_LOCATION, boolean persistent) {
CONFIG_FILE_LOCATION = aCONFIG_FILE_LOCATION;
if (persistent) {
props.setProperty(CONFIG_FILE_KEY, CONFIG_FILE_LOCATION);
}
}
public static void setConfigurationParameter(String key, String value) {
configuration.put(key, value);
}
public static String getConfigurationParameter(String key) {
if (configuration.containsKey(key)) {
return configuration.get(key);
} else {
configuration.put(key, "");
return "";
}
}
public static void commitConfigurationToFile() {
try {
Files.deleteIfExists(Paths.get(CONFIG_FILE_LOCATION + "bkup"));
try {
Files.copy(Paths.get(CONFIG_FILE_LOCATION), Paths.get(CONFIG_FILE_LOCATION + "bkup"));
} catch (IOException ioe) {
if(ioe instanceof NoSuchFileException) {
log.log(Level.FINE, "No existing configuration file--not creating backup");
} else {
log.log(Level.WARNING, "Exception encountered while trying to create configuration file backup: {0}", ioe);
}
}
} catch (IOException ex) {
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
System.err.println("Making backup configuration file failed. Exiting without committing configuration to file");
return;
}
try ( BufferedWriter br = new BufferedWriter(new FileWriter(Paths.get(CONFIG_FILE_LOCATION).toFile(), false))) {
configuration.forEach((key, value) -> {
try {
br.append(key + delimiter + value + "\n");
} catch (IOException ex) {
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
}
});
br.flush();
br.close();
} catch (FileNotFoundException ex) {
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static String enumerateConfiguration() {
StringBuilder sb = new StringBuilder();
configuration.forEach((key, value) -> {
sb.append(key + delimiter + value + "\n");
});
return sb.toString();
}
}

View File

@ -0,0 +1,18 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
import java.util.logging.Level;
/**
*
* @author atomb
*/
public class CustomLoggingLevel extends Level {
public CustomLoggingLevel(int i) {
super("custom", i);
}
}

View File

@ -0,0 +1,81 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
import discord4j.common.util.Snowflake;
import discord4j.core.object.entity.Guild;
import discord4j.core.object.entity.GuildEmoji;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.function.BiConsumer;
/**
*
* @author twi
*/
class GuildProfile {
private final Guild g;
public GuildProfile(Guild g1) {
g = g1;
}
private HashMap<String, Snowflake> customemojis = new HashMap<>();
private HashMap<String, Snowflake> approvingcustomemojis = new HashMap<>();
//Just so it's seekable, for the random approving emoji methods.
private ArrayList<String> approvingcustomemojinames = new ArrayList<>();
/**
* @return the customemojis
*/
public HashMap<String, Snowflake> getCustomemojis() {
return customemojis;
}
/**
* @param customemojis the customemojis to set
*/
public void setCustomemojis(HashMap<String, Snowflake> customemojis) {
this.customemojis = customemojis;
}
public String toFile() {
return "";
}
public String emojisToString() {
StringBuilder s = new StringBuilder("");
customemojis.forEach((String name, Snowflake snowflake) -> {
s.append(new JKRSONTag(name, snowflake.asString()));
});
return s.toString();
}
public void setApprovingEmoji(String name, Snowflake snowflake) {
approvingcustomemojis.put(name, snowflake);
approvingcustomemojinames.add(name);
}
public int emojiCount() {
return customemojis.size();
}
public int approvingEmojiCount() {
return approvingcustomemojis.size();
}
public GuildEmoji getEmojiByNumber(int i) {
return g.getGuildEmojiById(
approvingcustomemojis.get(approvingcustomemojinames
.get(i)
)
).block();
}
}

View File

@ -0,0 +1,181 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
import java.awt.FileDialog;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
/**
*
* @author twi
*/
public class InstantiableConfiguration {
private static String CONFIG_FILE_LOCATION = null;
public static final String CONFIG_FILE_KEY = "PRIESTESS_CONFIG";
public static final Properties p = System.getProperties();
public static final String DEFAULT_CONFIG_FILE_NAME = "priestessconfig.txt";
private static HashMap<String, String> configuration = new HashMap<>();
private static final Logger log = Logger.getLogger(InstantiableConfiguration.class.getName());
public static String findConfigurationFilePath() {
if (getCONFIG_FILE_LOCATION() != null) {
return getCONFIG_FILE_LOCATION();
}
setCONFIG_FILE_LOCATION(setConfigurationFilePath(), false);
return getCONFIG_FILE_LOCATION();
}
private static String setConfigurationFilePath() {
String s = System.getenv(CONFIG_FILE_KEY);
if (s != null && s.length() > 0) {
return s;
} else if (p.containsKey(CONFIG_FILE_KEY)) {
return p.getProperty(CONFIG_FILE_KEY);
}
if (Files.exists(Paths.get(DEFAULT_CONFIG_FILE_NAME), LinkOption.NOFOLLOW_LINKS)) {
return DEFAULT_CONFIG_FILE_NAME;
}
//JDialog jd = new JDialog();
//jd.add(new JLabel("Choose where to put Priestess Working Directory"));
//jd.setVisible(true);
JOptionPane.showConfirmDialog(null, "Select where cult files will be stored.");
JFileChooser jfc = new JFileChooser("Config File for Discord Cult Priestess");
jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int rv;
boolean approved = false;
while (!approved) {
rv = jfc.showDialog(null, "Select");
if (rv == JFileChooser.APPROVE_OPTION) {
File f = jfc.getSelectedFile();
if (f.isDirectory() && f.canRead() && f.canWrite()) {
p.setProperty(CONFIG_FILE_KEY, f.getAbsolutePath());
return f.getAbsolutePath();
}
}
}
return "";
}
private static final String delimiter = ": ";
public static void getConfigurationFromFile() {
if (CONFIG_FILE_LOCATION == null) {
setConfigurationFilePath();
}
try {
if (Files.isDirectory(Paths.get(CONFIG_FILE_LOCATION), LinkOption.NOFOLLOW_LINKS)) {
CONFIG_FILE_LOCATION = CONFIG_FILE_LOCATION + System.getProperty("file.separator") + DEFAULT_CONFIG_FILE_NAME;
}
BufferedReader br = new BufferedReader(new FileReader(Paths.get(CONFIG_FILE_LOCATION).toFile()));
br.lines().forEach((s) -> {
String key = s.substring(0, s.indexOf(delimiter));
if (s.length() == s.indexOf(delimiter) + delimiter.length()) {
System.out.println("read empty value for key (" + key + ") from configuration file");
configuration.put(key, "");
} else {
String value = s.substring(s.indexOf(delimiter) + delimiter.length());
configuration.put(key, value);
}
});
} catch (FileNotFoundException ex) {
Logger.getLogger(InstantiableConfiguration.class.getName()).log(Level.SEVERE, "Error loading configuration file from (" + CONFIG_FILE_LOCATION + ") {0}", ex);
}
}
/**
* @return the CONFIG_FILE_LOCATION
*/
public static String getCONFIG_FILE_LOCATION() {
return CONFIG_FILE_LOCATION;
}
/**
* @param aCONFIG_FILE_LOCATION the CONFIG_FILE_LOCATION to set
*/
public static void setCONFIG_FILE_LOCATION(String aCONFIG_FILE_LOCATION, boolean persistent) {
CONFIG_FILE_LOCATION = aCONFIG_FILE_LOCATION;
if (persistent) {
p.setProperty(CONFIG_FILE_KEY, CONFIG_FILE_LOCATION);
}
}
public static void setConfigurationParameter(String key, String value) {
configuration.put(key, value);
}
public static String getConfigurationParameter(String key) {
if (configuration.containsKey(key)) {
return configuration.get(key);
} else {
configuration.put(key, "");
return "";
}
}
public static void commitConfigurationToFile() {
try {
Files.deleteIfExists(Paths.get(CONFIG_FILE_LOCATION + "bkup"));
try {
Files.copy(Paths.get(CONFIG_FILE_LOCATION), Paths.get(CONFIG_FILE_LOCATION + "bkup"));
} catch (IOException ioe) {
log.log(Level.WARNING, "Exception encountered while trying to create confirguation file backup: {0}", ioe);
}
} catch (IOException ex) {
Logger.getLogger(InstantiableConfiguration.class.getName()).log(Level.SEVERE, null, ex);
System.err.println("Making backup configuration file failed. Exiting without committing configuration to file");
return;
}
try ( BufferedWriter br = new BufferedWriter(new FileWriter(Paths.get(CONFIG_FILE_LOCATION).toFile(), false))) {
configuration.forEach((key, value) -> {
try {
br.append(key + delimiter + value + "\n");
} catch (IOException ex) {
Logger.getLogger(InstantiableConfiguration.class.getName()).log(Level.SEVERE, null, ex);
}
});
br.flush();
br.close();
} catch (FileNotFoundException ex) {
Logger.getLogger(InstantiableConfiguration.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(InstantiableConfiguration.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static String enumerateConfiguration() {
StringBuilder sb = new StringBuilder();
configuration.forEach((key, value) -> {
sb.append(key + delimiter + value + "\n");
});
return sb.toString();
}
}

View File

@ -0,0 +1,51 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
/**
*
* @author twi
*/
public class JKRSONTag {
public JKRSONTag(String key1, String val1) {
key = key1;
val = val1;
}
public JKRSONTag() {
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key the key to set
*/
public void setKey(String key) {
this.key = key;
}
/**
* @return the val
*/
public String getVal() {
return val;
}
/**
* @param val the val to set
*/
public void setVal(String val) {
this.val = val;
}
private String key;
private String val;
}

View File

@ -0,0 +1,42 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
import discord4j.common.util.Snowflake;
import java.util.HashMap;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author twi
*/
public class JKRowlingSON {
public static final String TAG_PATTERN = "^\\{([a-z]*):([\\\"\\{a-z][a-z:\\}\\\"]*)\\}";
public static String keyVal(String key, String val) {
return String.format("\"%s:\" \"%s\"", key, val);
}
private static Pattern tagPattern = Pattern.compile(TAG_PATTERN);
public static JKRSONTag decode(String tag) {
Matcher m = tagPattern.matcher(tag);
JKRSONTag db = new JKRSONTag();
if (m.matches()) {
db.setKey(m.group(1));
db.setVal(m.group(2));
} else {
Logger.getLogger(JKRowlingSON.class.toString()).log(Level.SEVERE, "Could not decode " + tag);
}
return db;
}
}

View File

@ -0,0 +1,137 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author atomb
*/
public class MessageStore {
private RandomAccessFile f;
private long filelength = 0;
private FileChannel fr = null;
private MessageCacher mc;
private int[] messagestarts = null;
Thread t;
private boolean messagesCached = false;
public MessageStore(File f1) throws FileNotFoundException, IOException {
f = new RandomAccessFile(f1.getPath(), "r");
System.out.println("Message store is " + f.length() + " bytes long");
fr = f.getChannel();
filelength = f.length();
mc = new MessageCacher();
t = new Thread(mc);
t.start();
}
public String messageAt(int i) {
waitUntilLoaded();
int start = messagestarts[i];
StringBuilder s = new StringBuilder();
try {
f.seek(start);
int a = 0;
int j = 0;
char c = '\0';
while ((j = f.read()) != -1 && ((char) j != '}')) {
c = (char) j;
s.append(c);
a++;
}
} catch (IOException ex) {
Logger.getLogger(MessageStore.class.getName()).log(Level.SEVERE, null, ex);
}
while (s.charAt(0) == '{') {
s.deleteCharAt(0);
}
return s.toString();
}
public String randomMessage() {
return messageAt((int) (messagestarts.length * Math.random()));
}
boolean messagesAvailable() {
return messagesCached;
}
void waitUntilLoaded() {
if (t.isAlive()) {
try {
t.join();
} catch (InterruptedException ex) {
Logger.getLogger(MessageStore.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public int messageCount() {
return messagestarts.length;
}
private class MessageCacher implements Runnable {
@Override
public void run() {
Logger log = Logger.getLogger(this.getClass().getName());
log.setLevel(Level.FINE);
log.log(Level.INFO, "Attempting to characterize message store");
try {
ArrayList<Integer> messagestartsal = new ArrayList<>(); //contains index of every '{'
char c;
int a = 0;
int i = 0;
int j = 0;
ByteBuffer b = ByteBuffer.allocate(20);
b.rewind();
log.log(Level.INFO, "Starting to characterize message store at position (" + fr.position()
+ ") with channel size " + fr.size());
while ((j = f.read()) != -1) {
c = (char) j;
if (c == '{') {
messagestartsal.add(a);
}
a++;
}
Integer[] messagestarts1 = messagestartsal.toArray(new Integer[0]);
messagestarts = new int[messagestarts1.length];
for (i = 0; i < messagestarts1.length; i++) {
messagestarts[i] = messagestarts1[i].intValue();
}
} catch (IOException ex) {
Logger.getLogger(MessageStore.class.getName()).log(Level.SEVERE, null, ex);
}
for (int i : messagestarts) {
System.out.println("Found invocation message at position " + i);
}
messagesCached = true;
}
}
}

View File

@ -0,0 +1,863 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Project/Maven2/JavaApp/src/main/java/${packagePath}/${mainClassName}.java to edit this template
*/
package com.tinyplantnews.priestess;
import discord4j.common.util.Snowflake;
import discord4j.core.DiscordClientBuilder;
import discord4j.core.GatewayDiscordClient;
import discord4j.core.event.domain.interaction.ChatInputInteractionEvent;
import discord4j.core.event.domain.lifecycle.ConnectEvent;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.core.object.entity.Guild;
import discord4j.core.object.entity.GuildEmoji;
import discord4j.core.object.entity.Member;
import discord4j.core.object.entity.Message;
import discord4j.core.object.entity.User;
import discord4j.core.object.entity.channel.GuildChannel;
import discord4j.core.object.entity.channel.TextChannel;
import discord4j.core.object.presence.ClientActivity;
import discord4j.core.object.presence.ClientPresence;
import discord4j.core.object.presence.Status;
import discord4j.core.object.reaction.ReactionEmoji;
import discord4j.core.spec.MessageCreateSpec;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
/**
*
* @author twi
*/
public class Priestess {
public static String magicPhrase = "bean goose";
public static String cultChannelName = "bean-goose";
private static void trySetDebugLevel(String level) {
try {
debug_level = Integer.parseInt(level);
log.setLevel(new CustomLoggingLevel(debug_level));
log.log(Level.INFO, "Logging level set to {0}", level);
} catch (NumberFormatException nfe) {
log.log(Level.WARNING, "Invalid custom logging level ({0})", level);
}
}
private static String botToken = "";
private ReactionEmoji good;
GatewayDiscordClient dc;
static HashMap<String, Guild> guildnames = new HashMap<>();
static Map<Guild, GuildProfile> guildemojimaps = new HashMap<>();
private ArrayList<Snowflake> cultableChannelIDs = new ArrayList<>();
public static final int MAX_PERMS = Integer.MAX_VALUE;
public static final int LOCAL_PERMS = MAX_PERMS - 1;
public static final String WHITELIST = "whitelist";
public static final String CATLIST = "catlist";
private BufferedReader user_reader;
public static final String MESSAGES = "messages";
public static final String TESTSUITE = "testsuite";
private MessageStore ms = null;
public static final String DEBUG_LEVEL = "debug_level";
public static String NEWLINE = System.getProperty("line.separator");
private static final int default_debug_level = Level.INFO.intValue();
private static int debug_level = default_debug_level;
private static final Logger log = Logger.getLogger(Priestess.class.getName());
private static boolean allChannelsCultable = false;
private String[] approvingEmojis = {
"U+1F44D",
"U+270C",
"U+1F525",
"U+2764",
"U+1F496",
"U+1F63B"
};
private int invocationsThisSession = 0;
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
if (args[i] == TESTSUITE) {
return;
}
if (args[i].startsWith("--")) {
System.out.println("Command-line switch (" + args[i] + ") detected.");
switch (args[i].substring(2)) {
case "config-file":
i++;
Configuration.setCONFIG_FILE_LOCATION(args[i], false);
break;
case "config-file-persistent":
i++;
Configuration.setCONFIG_FILE_LOCATION(args[i], true);
break;
case "debug-level":
i++;
Configuration.setConfigurationParameter(DEBUG_LEVEL, args[i]);
trySetDebugLevel(args[i]);
break;
//There is precisely no reason for me to include this.
case "windows-lineendings":
NEWLINE = "\r\n";
break;
case "allchannels":
allChannelsCultable = true;
log.log(Level.INFO, "Listening to all channels in joined guilds.");
break;
default:
}
}
}
new Priestess().start();
}
private String randomApprovingEmoji() {
return approvingEmojis[(int) Math.floor((Math.random() * approvingEmojis.length))];
}
private void registerEmoji(Guild g, Snowflake emojiFlake, String emojiName) {
System.out.println(String.format("\nCustom Emoji Detected: \nServer:\t%s\nFlake:\t%s\nName:\t%s", g, emojiFlake, emojiName));
if (!guildemojimaps.containsKey(g)) {
guildemojimaps.put(g, new GuildProfile(g));
}
guildemojimaps.get(g).getCustomemojis().put(emojiName, emojiFlake);
}
private String cdr(String needle, String haystack) {
if (haystack.startsWith(needle)) {
return haystack.substring(needle.length()).trim();
} else {
return haystack;
}
}
/**
* I hope it needs not be said, but commands must be unique.
*
* @param t
* @param m
* @param c
* @return
*/
private Publisher<Mono> dispatchCommand(MessageCreateEvent t, Message m, String c) {
int permissionLevel = resolveAuthorPermissionLevel(t, m);
return dispatchCommand(t, m, c, permissionLevel);
}
private Publisher<Mono> dispatchCommand(MessageCreateEvent t, Message m, String c, int permissionLevel) {
String commandline = cdr(magicPhrase, c);
System.out.println("Attempting to run command (" + commandline + ") with permission level " + permissionLevel);
Command.tryAll(commandline, m, permissionLevel); //System.out.println("payload " + payload);
return Mono.empty();
}
Function<MessageCreateEvent, Publisher<Mono>> guildedmessagehandler = (MessageCreateEvent t) -> {
Message m = t.getMessage();
String c = m.getContent();
Snowflake cid = m.getChannel().block().getId();
if (allChannelsCultable || cultableChannelIDs.contains(cid)) {
Guild g = m.getGuild().block();
GuildProfile gp = guildemojimaps.get(g);
if (gp == null) {
log.log(Level.INFO,
"Message from unregistered guild, "
+ "not registering now.");
//register guild?
return Mono.empty();
}
if (c.equalsIgnoreCase(magicPhrase)) {
invokeDeity(g, m);
} else if (c.startsWith(magicPhrase)) {
return dispatchCommand(t, m, c);
}
}
return Mono.empty();
};
Function<MessageCreateEvent, Publisher<Mono>> directmessagehandler = (var t) -> {
//System.out.println("ya boi");
Message m = t.getMessage();
String c = m.getContent();
log.log(Level.FINE, "processing direct message {0}", c);
log.log(Level.FINE, "User snowflake for message {0} is {1}", new Object[]{c, m.getAuthor().get().getId().asString()});
if (c.equalsIgnoreCase(magicPhrase)) {
//System.out.println("approved");
m.addReaction(ReactionEmoji.codepoints(randomApprovingEmoji())).subscribe();
} else if (c.startsWith(magicPhrase)) {
return dispatchCommand(t, m, c);
}
return Mono.empty();
};
private static final String alphabet = "abcdefghijklmnopqrstuvwxyz";
private String randomLetter() {
return "" + alphabet.charAt((int) (Math.random() * alphabet.length()));
}
private HashMap<String, Thread> flywheel = new HashMap<>();
private void start() {
if (debug_level != default_debug_level) {
log.log(Level.INFO, String.format("Starting Priestess with debug level {0}", debug_level));
} else {
log.log(Level.INFO, String.format("Starting Priestess normally"));
}
setup();
if(botToken.isBlank()) {
log.log(Level.SEVERE, "bot token is null. aborting.");
return;
}
dc = DiscordClientBuilder.create(botToken).build().login().block();
if (dc == null) {
log.log(Level.SEVERE, "gatewaydiscordclient was null. aborting.");
return;
}
String boot_time = "";
Date d = new Date();
SimpleDateFormat df = new SimpleDateFormat("dd MMM YYYY kk:mm");
boot_time = df.format(d);
dc.updatePresence(ClientPresence.of(Status.ONLINE, ClientActivity.playing("since " + boot_time))).subscribe();
dc.getGuilds().flatMap((g) -> {
if (!guildemojimaps.containsKey(g)) {
guildemojimaps.put(g, new GuildProfile(g));
}
if (!guildnames.containsKey(g.getName())) {
guildnames.put(g.getName(), g);
}
return Mono.empty();
}
).subscribe();
dc.getGuilds().flatMap((g)
-> g.getChannels().flatMap((channel) -> {
log.log(Level.INFO, "Checking cultability of channel {0}", channel.getName());
if (channel.getName().equals(cultChannelName)) {
System.out.println("Channel " + channel.getName() + " in " + g.getName() + " is cultable");
cultableChannelIDs.add(channel.getId());
}
return Mono.empty();
})
).subscribe();
dc.getGuilds().flatMap(g -> g.getEmojis()).flatMap(g2 -> {
//system.out.println("emoji");
registerEmoji(g2.getGuild().block(), g2.getId(), g2.getName());
return Mono.empty();
}
).subscribe();
dc.on(ConnectEvent.class, event -> {
log.log(Level.INFO, "connected");
return Mono.empty();
}).subscribe();
dc.on(ChatInputInteractionEvent.class).flatMap(event -> {
return event.reply().withContent("agreed");
}).subscribe();
SysInListener sil = new SysInListener();
Thread t = new Thread(sil);
t.start();
flywheel.put("System.in listener", t);
dc.on(MessageCreateEvent.class).filter(event -> !event.getMessage().getAuthor().get().isBot())
.filter(g -> !g.getGuild().blockOptional().isEmpty()).flatMap(guildedmessagehandler)
.subscribe();
dc.on(MessageCreateEvent.class).filter(event -> !event.getMessage().getAuthor().get().isBot())
.filter(g -> g.getGuild().blockOptional().isEmpty()).flatMap(directmessagehandler)
.blockLast();
}
private int invocationsBeforeThisSession = 0;
private void setup() {
Configuration.findConfigurationFilePath();
Configuration.getConfigurationFromFile();
botToken = Configuration.getConfigurationParameter(TOKEN_KEY);
String previousInvocations = Configuration.getConfigurationParameter(INVOCATIONS_KEY);
try {
if (previousInvocations.isBlank()) {
previousInvocations = "0";
}
invocationsBeforeThisSession = Integer.parseInt(previousInvocations);
} catch (NumberFormatException nfe) {
System.err.println("Error reading invocations from last boot.");
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, nfe.getMessage());
System.out.println("Invocations this boot: " + invocationsThisSession);
System.out.println("Invocation count from configuration file: " + previousInvocations);
}
user_reader = new BufferedReader(new InputStreamReader(System.in));
setupWhiteList();
ms = setupMessageStore();
long free0 = Runtime.getRuntime().freeMemory();
log.log(Level.FINE, "Initialization complete. approx. {0} bytes free in memory.\n Attempting garbage collection.", free0);
Runtime.getRuntime().gc();
long free1 = Runtime.getRuntime().freeMemory();
long freediff = free1 - free0;
log.log(Level.FINE, "Post-initialization garbage collection freed ", freediff);
registerDefaultCommands();
}
boolean replcontinue = true;
public static final String TOKEN_KEY = "token";
public static final String INVOCATIONS_KEY = "invocations";
private void shutdown() {
replcontinue = false;
dc.logout().subscribe();
System.out.println("Logged out");
try {
user_reader.close();
} catch (IOException ex) {
Logger.getLogger(Priestess.class.getName()).log(Level.SEVERE, null, ex);
}
for (Thread t : flywheel.values()) {
t.interrupt();
}
int invocationsTotal = invocationsBeforeThisSession + invocationsThisSession;
System.out.println("Bean Goose has been invoked " + invocationsTotal + " times so far.");
Configuration.setConfigurationParameter(INVOCATIONS_KEY, Integer.toString(invocationsTotal));
Configuration.commitConfigurationToFile();
System.out.println("Configuration committed to file.");
}
private void invokeDeity(Guild g, Message m) {
int ae_custom = guildemojimaps.get(g).approvingEmojiCount();
int emojicount = approvingEmojis.length + ae_custom;
//System.out.println(approvingEmojis.length + " default, " + ae_custom + " custom approving emojis.");
int which = (int) (Math.random() * emojicount);
if (which < approvingEmojis.length) {
m.addReaction(ReactionEmoji.codepoints(randomApprovingEmoji())).subscribe();
} else {
which -= approvingEmojis.length;
m.addReaction(ReactionEmoji.custom(guildemojimaps.get(g).getEmojiByNumber(which))).subscribe();
}
invocationsThisSession++;
dispatchMilestone(invocationsThisSession + invocationsBeforeThisSession, g, m);
}
private HashMap<Snowflake, Integer> rolewhitelist = new HashMap<>();
private int resolveAuthorPermissionLevel(MessageCreateEvent t, Message m) {
int level = 0;
Optional gio = t.getGuildId();
if (gio.isPresent()) {
Member a = m.getAuthorAsMember().block();
for (Snowflake snowflake : (Snowflake[]) a.getRoleIds().toArray(new Snowflake[3])) {
if (rolewhitelist.containsKey(snowflake)) {
int candy = rolewhitelist.get(snowflake).intValue();
if (candy > level) {
level = candy;
}
}
}
}
User a = m.getAuthor().get();
Snowflake aid = a.getId();
if (rolewhitelist.containsKey(aid)) {
int candy = rolewhitelist.get(aid).intValue();
if (candy > level) {
level = candy;
}
}
if (fromCatluck(m)) {
level = MAX_PERMS;
}
return level;
}
private boolean fromCatluck(Message m) {
String s = Configuration.getConfigurationParameter(CATLIST);
Optional<User> ao = m.getAuthor();
if (ao.isPresent()) {
for (String s1 : s.split(" ")) {
if (s1.equals(ao.get().getId().asString())) {
System.out.println("Literally catluck detected.");
return true;
}
}
}
return false;
}
private void dispatchMilestone(int i, Guild g, Message m) {
if ((i % 5) == 0) {
MessageCreateSpec mcs = MessageCreateSpec.create()
.withMessageReference(m.getId())
.withContent(randomMessage());
m.getChannel().block().createMessage(mcs).subscribe();
}
}
private ArrayList<String> messages = new ArrayList<>();
private String randomMessage() {
if (ms.messagesAvailable()) {
return ms.randomMessage();
}
if (messages.isEmpty()) {
return "bean goose approves";
}
return messages.get((int) (Math.random() * messages.size()));
}
private void setupWhiteList() {
String rolelist = Configuration.getConfigurationParameter(WHITELIST);
if (!rolelist.isBlank()) {
String subtokenDelimiter = "/";
String[] tokens = rolelist.split(" ");
for (String token : tokens) {
String sflake = token.substring(0, token.indexOf(subtokenDelimiter));
String perm = token.substring(token.indexOf(subtokenDelimiter) + subtokenDelimiter.length(), token.length());
System.out.println("Adding snowflake (" + sflake + ") to whitelist with permission level (" + perm + ").");
Snowflake a = Snowflake.of(sflake);
rolewhitelist.put(a, Integer.parseInt(perm));
}
}
}
private MessageStore setupMessageStore() {
String mesglist = Configuration.getConfigurationParameter(MESSAGES);
File f = Paths.get(mesglist).toFile();
try {
MessageStore ms1 = new MessageStore(f);
return ms1;
} catch (IOException e) {
if (!f.exists()) {
} else {
log.log(Level.SEVERE, e.getMessage());
}
}
return null;
}
private void reloadMessageStore() {
MessageStore ms1 = setupMessageStore();
ms1.waitUntilLoaded();
ms = ms1;
}
private Guild resolveGuildByNameOrId(String token) {
log.log(Level.INFO, "Finding guild by name or ID " + token);
if (guildnames.containsKey(token)) {
return guildnames.get(token);
}
return this.dc.getGuildById(Snowflake.of(token)).block();
}
private class SysInListener implements Runnable {
@Override
public void run() {
try {
while (replcontinue) {
while (!user_reader.ready()) {
Thread.sleep(1000);
if (!replcontinue) {
user_reader.close();
return;
}
}
String a = user_reader.readLine();
dispatchCommand(null, null, a, LOCAL_PERMS);
}
user_reader.close();
} catch (IOException ex) {
Logger.getLogger(Priestess.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.INFO, "System.in Listener interrupted.");
}
System.out.println("break repl");
}
}
private static void tryPush(ArrayList<String> a, StringBuilder s) {
if (!s.isEmpty()) {
a.add(s.toString());
}
}
public static String[] tokenize(String payload) {
ArrayList<String> a = new ArrayList<>();
StringBuilder s = new StringBuilder();
boolean inQuote = false;
for (char c : payload.toCharArray()) {
//log.log(Level.FINE, String.format("c: %c, inQuote: %s", c, (inQuote ? "yes" : "no")));
if (!inQuote && c == '\"') {
inQuote = true;
tryPush(a, s);
s.delete(0, s.length());
} else if (inQuote && c == '\"') {
inQuote = false;
tryPush(a, s);
s.delete(0, s.length());
} else if (!inQuote && c == ' ') {
tryPush(a, s);
s.delete(0, s.length());
} else {
s.append(c);
}
}
tryPush(a, s);
if (inQuote) {
log.log(Level.WARNING, "Unterminated quote passed to tokenizer");
return null;
}
return a.toArray(new String[a.size()]);
}
/**
* I wanted the replacement string to be {xxxx} without the dollar sign, but
* unit tests failed when the help/usage was nothing but {xxxx}. Instead of
* investigating, I gave up and added a dollar sign to make it something
* like Python's f-strings. Or JS' backtick-strings, I can never remember
* which is which.
*/
public static class Callback implements Function<CommandArgs, Publisher<Mono>> {
final Logger log = Logger.getLogger(Callback.class.getName());
String[] usages = {"Undefined command usage"};
String help = "${usage}";
@Override
public Publisher<Mono> apply(CommandArgs t) {
throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
}
public String getHelp(String commandName) {
String s = getUsage(commandName);
return help/*.replaceAll("\\$\\{usage\\}", s)*/.replaceAll("\\$\\{commandname\\}", commandName);
}
public String getUsage(String commandName) {
StringBuilder u = new StringBuilder();
for (int i = 0; i < usages.length; i++) {
String s = usages[i];
s.replaceAll("\\$\\{commandname\\}", commandName);
u.append(s);
if (i != usages.length - 1) {
u.append(NEWLINE);
}
}
return u.toString();
}
}
public final Callback registerEmojiCommand = new Callback() {
String usage = "{commandname} EMOJI";
String help
= "register emoji for use in basic invocations."
+ ""
+ ""
+ ""
+ "";
@Override
public Publisher<Mono> apply(CommandArgs o) {
String[] tokens = tokenize(o.getPayload());
String emojiname = tokens[0];
Guild g;
if (tokens.length == 3 && "in".equals(tokens[1])) {
g = resolveGuildByNameOrId(tokens[2]);
} else {
if (o.getMessage() != null) {
g = o.getMessage().getGuild().block();
} else {
o.replyWith("Could not determine what guild to register emoji with.");
return null;
}
}
Snowflake s = (Snowflake) guildemojimaps.get(g).getCustomemojis().get(emojiname);
if (s != null) {
GuildEmoji emoji = g.getGuildEmojiById(s).block();
o.getMessage().addReaction(ReactionEmoji.custom(emoji)).subscribe();
guildemojimaps.get(o.getMessage().getGuild().block()).setApprovingEmoji(emoji.getName(), emoji.getId());
} else {
log.log(Level.WARNING, "tried registering emoji ''{0}'' but no dice", emojiname);
}
return null;
}
};
public final Callback commitConfigurationCommand = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
System.out.println("Committing configuration to file.");
Configuration.commitConfigurationToFile();
return Mono.empty();
}
};
/**
* Show system status
*/
public final Callback showCommand = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
System.out.println("System Status:");
guildemojimaps.forEach((guild, gp) -> {
System.out.println("\n" + guild.getName() + ":");
gp.getCustomemojis().forEach((name, snow) -> {
System.out.println("\n\t" + name + ": " + snow.asString());
});
});
log.log(Level.INFO, "Configuration file location is " + Configuration.getCONFIG_FILE_LOCATION());
return Mono.empty();
}
};
public final Callback setConfigCommand = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
String pay2 = cdr("set", o.getPayload());
String[] paytokens = pay2.split(" ");
if (paytokens.length == 2) {
log.log(Level.INFO, "setting configuration parameter " + paytokens[0] + " to " + paytokens[1]);
Configuration.setConfigurationParameter(paytokens[0], paytokens[1]);
} else {
log.log(Level.INFO, "Invalid argument count for set configuration parameter command");
}
return Mono.empty();
}
};
public final Callback showConfigCommand = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
log.log(Level.INFO, "Showing configuration.");
log.log(Level.INFO, Configuration.enumerateConfiguration());
return Mono.empty();
}
};
public final Callback permissionLevelCommand = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
o.replyWith(String.format("Your permission level in this context is %s", o.getPermCalledWith()));
return Mono.empty();
}
};
public final Callback setCultableChannelsCommand = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
String p = o.getPayload();
if (p.isBlank()) {
o.replyWith("Culting all channels");
allChannelsCultable = true;
} else {
String[] tokens = tokenize(p);
if (isAffirmative(tokens[0])) {
o.replyWith("Culting all channels");
allChannelsCultable = true;
} else if (isNegatory(tokens[0])) {
o.replyWith("Removing all-channel cult");
allChannelsCultable = false;
} else if (tokens.length >= 2) {
Guild g = resolveGuildByNameOrId(tokens[0]);
if (g != null) {
GuildChannel c = g.getChannels().filter(new Predicate<GuildChannel>() {
@Override
public boolean test(GuildChannel t) {
return t.getName().equals(tokens[1]);
}
}).blockFirst();
if (g != null) {
if (tokens.length == 3) {
if (isAffirmative(tokens[2])) {
cultableChannelIDs.add(c.getId());
o.replyWith("Adding channel to cultable channels list");
} else if (isNegatory(tokens[2])) {
o.replyWith("Removing channel from cultable channels list");
cultableChannelIDs.remove(c);
} else {
o.replyWith("Could not determine whether to cult or uncult channel.");
}
}
} else {
o.replyWith("Could not resolve channel to cult or uncult.");
}
}
} else {
o.replyWith("Unknown parameters for command");
}
}
return Mono.empty();
}
private boolean isAffirmative(String token) {
return "yes".equalsIgnoreCase(token);
}
private boolean isNegatory(String token) {
return "no".equalsIgnoreCase(token);
}
};
public final Callback shutdownCommand = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
log.log(Level.INFO, "Shutting down.");
shutdown();
return Mono.empty();
}
};
public final Callback l = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
String[] arglist = o.getPayload().split(" ");
if (arglist.length != 2) {
return Mono.empty();
}
Snowflake s = Snowflake.of(arglist[0]);
return Mono.empty();
}
};
public final Callback sendToCommand = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
String[] arglist = o.getPayload().split(" ");
if (arglist.length < 3) {
o.replyWith("Not enough arguments.");
return Mono.empty();
}
Guild a = resolveGuildByNameOrId(arglist[0]);
//guildnames.get(arglist[0]);
/*
for (Object a0 : guildnames.entrySet().toArray()) {
Map.Entry<String, Guild> a3 = (Map.Entry<String, Guild>) a0;
Guild a1 = (Guild) a3.getValue();
if (a1.getId().asString().equals(arglist[0])) {
a = a1;
}
}*/
if (a == null) {
o.replyWith("Could not find guild");
return Mono.empty();
}
a.getChannels().flatMap((ch) -> {
if (ch.getName().equals(arglist[1])) {
String argrest = "";
for (int i = 2; i < arglist.length; i++) {
argrest += arglist[i] + " ";
}
argrest = argrest.substring(0, argrest.length() - 1);
TextChannel ch2 = (TextChannel) ch;
MessageCreateSpec mcs = MessageCreateSpec.create().withContent(argrest);
ch2.createMessage(mcs).subscribe();
}
return Mono.empty();
}).blockLast();
return Mono.empty();
}
};
public final Callback reloadMessageStoreCommand = new Callback() {
@Override
public Publisher<Mono> apply(CommandArgs o) {
log.log(Level.INFO, "Attempting to reload messages.");
reloadMessageStore();
log.log(Level.INFO, "Messages reloaded, {0} in registry.", ms.messageCount());
return Mono.empty();
}
};
private void registerDefaultCommands() {
Command.registerCommand(new Command("status", 1).setCallback(showCommand));
Command.registerCommand(new Command("reload messages", 1).setCallback(reloadMessageStoreCommand));
Command.registerCommand(new Command("use emoji", 1).setCallback(registerEmojiCommand));
Command.registerCommand(new Command("show config", 1).setCallback(showConfigCommand));
Command.registerCommand(new Command("set", 1).setCallback(setConfigCommand));
Command.registerCommand(new Command("shutdown", 50).setCallback(shutdownCommand));
Command.registerCommand(new Command("send to", 1).setCallback(sendToCommand));
Command.registerCommand(new Command("cult", 9000).setCallback(setCultableChannelsCommand));
Command.registerCommand(new Command("perm", 0).setCallback(permissionLevelCommand));
Command.registerCommand(Command.commandThatJustRepliesWith("is minecraft online", "i dunno log on and find out smfh"));
}
}

View File

@ -0,0 +1,13 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.tinyplantnews.priestess;
/**
*
* @author atomb
*/
public class WordsAndWhatTheyMean {
}

View File

@ -0,0 +1,117 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/UnitTests/JUnit5TestClass.java to edit this template
*/
package com.tinyplantnews.priestess;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
/**
*
* @author atomb
*/
public class PriestessTest {
public PriestessTest() {
}
@org.junit.jupiter.api.BeforeAll
public static void setUpClass() throws Exception {
}
@org.junit.jupiter.api.AfterAll
public static void tearDownClass() throws Exception {
}
/**
* Test of main method, of class Priestess.
*/
@org.junit.jupiter.api.Test
public void testMain() {
System.out.println("main");
String[] args = {"testsuite"};
Priestess.main(args);
}
@org.junit.jupiter.api.Test
public void testCallbackHelpTexts(){
assertEquals("boi", "son".replaceAll("son", "boi"));
Priestess p = new Priestess();
p.shutdownCommand.help = "ha boi";
testCallbackHelpText(p.shutdownCommand,
"shut down",
"ha boi");
p.shutdownCommand.help = "${commandname}";
testCallbackHelpText(p.shutdownCommand,
"shut down",
"shut down");
p.shutdownCommand.help = "${commandname} turns off the entire internet";
testCallbackHelpText(p.shutdownCommand,
"shut down",
"shut down turns off the entire internet");
}
private void testCallbackHelpText(Priestess.Callback c, String name, String expHelp){
String help = c.getHelp(name);
Assertions.assertEquals(expHelp, help);
}
/**
* Test of tokenize method, of class Priestess.
*/
@Test
public void testTokenize() {
testTokenize1("a",
new String[]{"a"});
testTokenize1("a, b",
new String[]{"a,", "b"});
testTokenize1("ya boi",
new String[]{"ya", "boi"});
//Should handle these trivially. Single quotes not special.
testTokenize1("'''' ''' ' '''",
new String[]{"''''", "'''", "'", "'''"});
testTokenize1("*^@^$/><::;''",
new String[]{"*^@^$/><::;''"});
testTokenize1("*^@^$/> <::;''",
new String[]{"*^@^$/>", "<::;''"});
testTokenize1("this is not quoted, \"this is\"",
new String[]{"this", "is", "not", "quoted,", "this is"});
testTokenize1("this is not quoted, \"this is",
null);
/*testTokenize1("this is not quoted, \\\"this is",
new String[]{"this", "is", "not", "quoted,", "\"this is"});*/
}
private void testTokenize1(String payload, String[] expResult) {
System.out.println("tokenize for payload " + payload);
String[] result = Priestess.tokenize(payload);
if (expResult == null && result == null) {
return;
} else if (result == null && expResult != null) {
fail("Tokenize() returned null array");
}
for (int i = 0; i < Math.max(expResult.length, result.length); i++) {
String a = "<nothing>";
String b = "<nothing>";
if (expResult.length > i) {
a = expResult[i];
}
if (result.length > i) {
b = result[i];
}
System.out.println(String.format("\te: %s r: %s", a, b));
}
assertArrayEquals(expResult, result);
}
}