General fixes. Works.

Formatted source files.
Fixed command-line arguments processing.
Added ability to specify logging levels by name (ALL, FINE, FINER...)

Generally fixed unnoticed problems in Configuration and Priestess
classes. Stopped spuriously committing configuration file path to
preferences store, changed some Priestess methods to use her own
tokenizer, and fixed a null check in setCultableChannels method.
This commit is contained in:
Sandy Mossgrave 2023-05-07 19:56:57 +00:00
parent 7d97078e43
commit f2eec77df0
14 changed files with 1508 additions and 1504 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
/priestessconfig.txt
/priestessconfig.txtbkup
/target/
dependency-reduced-pom.xml

View File

@ -100,9 +100,10 @@ public class Command {
commands.put(c.name, c);
}
/*= "use emoji";
public static final String REPLYTO_COMMAND = "reply to";
public static final String SENDTO_COMMAND = "send to";*/
/*
* = "use emoji"; public static final String REPLYTO_COMMAND = "reply to";
* public static final String SENDTO_COMMAND = "send to";
*/
/**
* @return the name

View File

@ -9,8 +9,5 @@ package com.tinyplantnews.priestess;
* @author atomb
*/
public enum CommandStatus {
NOMATCH,
SUCCESS,
FAILURE,
BADPERM;
NOMATCH, SUCCESS, FAILURE, BADPERM;
}

View File

@ -30,8 +30,10 @@ import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
/**
* "Ms Merry," you may say, "You literally import the Preferences class, and yet
* you insist on writing your own configuration store. Why?" "Iunno," I reply.
*
* @author twi
* @author sandy
*/
public class Configuration {
@ -80,12 +82,6 @@ public class Configuration {
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) {
@ -104,13 +100,14 @@ public class Configuration {
}
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;
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");
log.log(Level.FINE, "read empty value for key (" + key + ") from configuration file");
configuration.put(key, "");
} else {
String value = s.substring(s.indexOf(delimiter) + delimiter.length());
@ -119,7 +116,8 @@ public class Configuration {
});
} catch (FileNotFoundException ex) {
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Error loading configuration file from (" + CONFIG_FILE_LOCATION + ") {0}", ex);
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE,
"Error loading configuration file from (" + CONFIG_FILE_LOCATION + ") {0}", ex);
}
}
@ -137,6 +135,14 @@ public class Configuration {
CONFIG_FILE_LOCATION = aCONFIG_FILE_LOCATION;
if (persistent) {
props.setProperty(CONFIG_FILE_KEY, CONFIG_FILE_LOCATION);
prefs.put(CONFIG_FILE_KEY, CONFIG_FILE_LOCATION);
try {
prefs.flush();
} catch (BackingStoreException e) {
log.log(Level.WARNING,
"Backing store exception while committing configuration file path to persistent storage {0}",
e.getMessage());
}
}
}
@ -164,12 +170,14 @@ public class Configuration {
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);
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");
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))) {

View File

@ -23,7 +23,6 @@ class GuildProfile {
g = g1;
}
private HashMap<String, Snowflake> customemojis = new HashMap<>();
private HashMap<String, Snowflake> approvingcustomemojis = new HashMap<>();
@ -71,11 +70,7 @@ class GuildProfile {
}
public GuildEmoji getEmojiByNumber(int i) {
return g.getGuildEmojiById(
approvingcustomemojis.get(approvingcustomemojinames
.get(i)
)
).block();
return g.getGuildEmojiById(approvingcustomemojis.get(approvingcustomemojinames.get(i))).block();
}
}

View File

@ -89,7 +89,8 @@ public class InstantiableConfiguration {
}
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;
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) -> {
@ -104,7 +105,8 @@ public class InstantiableConfiguration {
});
} catch (FileNotFoundException ex) {
Logger.getLogger(InstantiableConfiguration.class.getName()).log(Level.SEVERE, "Error loading configuration file from (" + CONFIG_FILE_LOCATION + ") {0}", ex);
Logger.getLogger(InstantiableConfiguration.class.getName()).log(Level.SEVERE,
"Error loading configuration file from (" + CONFIG_FILE_LOCATION + ") {0}", ex);
}
}
@ -146,11 +148,13 @@ public class InstantiableConfiguration {
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);
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");
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))) {

View File

@ -45,6 +45,7 @@ public class JKRSONTag {
public void setVal(String val) {
this.val = val;
}
private String key;
private String val;

View File

@ -17,6 +17,14 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
* OH IT STORES THE PROPHECIES
*
* The configuration file (priestessconfig.txt) contains a key, 'messages', for which the value is a path to the messages store.
* The messages store contains prophecies that are given every few bean goose invocations.
* This class first loads the file, scans it and generates a list of indices for the messages it contains.
*
* This class could definitely use a cache of the message starts--a special block at the beginning/end of the file with a list of message starts/ends,
* but I'm not up to writing that today - 7 May 2023
*
* @author atomb
*/
@ -32,8 +40,10 @@ public class MessageStore {
Thread t;
private boolean messagesCached = false;
private long loadStart = Long.MAX_VALUE;
public MessageStore(File f1) throws FileNotFoundException, IOException {
loadStart = System.nanoTime();
f = new RandomAccessFile(f1.getPath(), "r");
System.out.println("Message store is " + f.length() + " bytes long");
fr = f.getChannel();
@ -131,7 +141,10 @@ public class MessageStore {
System.out.println("Found invocation message at position " + i);
}
messagesCached = true;
}
long loadEnd = System.nanoTime();
long tdiff = loadEnd - loadStart;
log.log(Level.FINE, "Prophecies loaded in {0} ms", tdiff);
}
}
}

View File

@ -1,4 +1,6 @@
/*
:q:q
qqqqqqqq:qdf
* 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
*/
@ -52,8 +54,9 @@ public class Priestess {
private static void trySetDebugLevel(String level) {
try {
debug_level = Integer.parseInt(level);
log.setLevel(new CustomLoggingLevel(debug_level));
Level new_debug_level = Level.parse(level);
log.setLevel(new_debug_level);
debug_level = new_debug_level.intValue();
log.log(Level.INFO, "Logging level set to {0}", level);
} catch (NumberFormatException nfe) {
log.log(Level.WARNING, "Invalid custom logging level ({0})", level);
@ -96,14 +99,7 @@ public class Priestess {
private static boolean allChannelsCultable = false;
private String[] approvingEmojis = {
"U+1F44D",
"U+270C",
"U+1F525",
"U+2764",
"U+1F496",
"U+1F63B"
};
private String[] approvingEmojis = { "U+1F44D", "U+270C", "U+1F525", "U+2764", "U+1F496", "U+1F63B" };
private int invocationsThisSession = 0;
@ -117,19 +113,26 @@ public class Priestess {
if (args[i].startsWith("--")) {
System.out.println("Command-line switch (" + args[i] + ") detected.");
switch (args[i].substring(2)) {
String yarg = args[i].substring(2, args[i].indexOf('='));
switch (yarg) {
case "config-file":
Configuration.setCONFIG_FILE_LOCATION(yarg, false);
i++;
Configuration.setCONFIG_FILE_LOCATION(args[i], false);
break;
case "config-file-persistent":
Configuration.setCONFIG_FILE_LOCATION(yarg, true);
i++;
Configuration.setCONFIG_FILE_LOCATION(args[i], true);
break;
case "debug-level":
String newlevel = args[i].substring(args[i].indexOf('=')+1);
Configuration.setConfigurationParameter(DEBUG_LEVEL, newlevel);
trySetDebugLevel(newlevel);
i++;
break;
case "debug":
Configuration.setConfigurationParameter(DEBUG_LEVEL, Level.ALL.toString());
trySetDebugLevel(yarg);
i++;
Configuration.setConfigurationParameter(DEBUG_LEVEL, args[i]);
trySetDebugLevel(args[i]);
break;
// There is precisely no reason for me to include this.
case "windows-lineendings":
@ -140,7 +143,7 @@ public class Priestess {
log.log(Level.INFO, "Listening to all channels in joined guilds.");
break;
default:
log.log(Level.INFO, "Unknown command-line argument: {0}", yarg);
}
}
}
@ -153,7 +156,8 @@ public class Priestess {
}
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));
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));
}
@ -201,9 +205,7 @@ public class Priestess {
Guild g = m.getGuild().block();
GuildProfile gp = guildemojimaps.get(g);
if (gp == null) {
log.log(Level.INFO,
"Message from unregistered guild, "
+ "not registering now.");
log.log(Level.INFO, "Message from unregistered guild, " + "not registering now.");
// register guild?
return Mono.empty();
}
@ -223,7 +225,8 @@ public class Priestess {
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()});
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");
@ -272,27 +275,23 @@ public class Priestess {
guildnames.put(g.getName(), g);
}
return Mono.empty();
}
).subscribe();
}).subscribe();
dc.getGuilds().flatMap((g)
-> g.getChannels().flatMap((channel) -> {
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");
log.log(Level.INFO, "Channel " + channel.getName() + " in " + g.getName() + " is cultable");
cultableChannelIDs.add(channel.getId());
}
return Mono.empty();
})
).subscribe();
})).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();
}).subscribe();
dc.on(ConnectEvent.class, event -> {
@ -312,12 +311,10 @@ public class Priestess {
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();
.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();
.filter(g -> g.getGuild().blockOptional().isEmpty()).flatMap(directmessagehandler).blockLast();
}
private int invocationsBeforeThisSession = 0;
@ -348,7 +345,8 @@ public class Priestess {
ms = setupMessageStore();
long free0 = Runtime.getRuntime().freeMemory();
log.log(Level.FINE, "Initialization complete. approx. {0} bytes free in memory.\n Attempting garbage collection.", free0);
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;
@ -384,7 +382,8 @@ public class Priestess {
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.");
// 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();
@ -446,8 +445,7 @@ public class Priestess {
private void dispatchMilestone(int i, Guild g, Message m) {
if ((i % 5) == 0) {
MessageCreateSpec mcs = MessageCreateSpec.create()
.withMessageReference(m.getId())
MessageCreateSpec mcs = MessageCreateSpec.create().withMessageReference(m.getId())
.withContent(randomMessage());
m.getChannel().block().createMessage(mcs).subscribe();
}
@ -475,9 +473,11 @@ public class Priestess {
for (String token : tokens) {
String sflake = token.substring(0, token.indexOf(subtokenDelimiter));
String perm = token.substring(token.indexOf(subtokenDelimiter) + subtokenDelimiter.length(), token.length());
String perm = token.substring(token.indexOf(subtokenDelimiter) + subtokenDelimiter.length(),
token.length());
System.out.println("Adding snowflake (" + sflake + ") to whitelist with permission level (" + perm + ").");
System.out.println(
"Adding snowflake (" + sflake + ") to whitelist with permission level (" + perm + ").");
Snowflake a = Snowflake.of(sflake);
@ -486,6 +486,9 @@ public class Priestess {
}
}
/*
*
*/
private MessageStore setupMessageStore() {
String mesglist = Configuration.getConfigurationParameter(MESSAGES);
File f = Paths.get(mesglist).toFile();
@ -494,7 +497,7 @@ public class Priestess {
return ms1;
} catch (IOException e) {
if (!f.exists()) {
log.log(Level.WARNING, "Message store file does not exist.");
} else {
log.log(Level.SEVERE, e.getMessage());
}
@ -554,7 +557,8 @@ public class Priestess {
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")));
// log.log(Level.FINE, String.format("c: %c, inQuote: %s", c, (inQuote ? "yes" :
// "no")));
if (!inQuote && c == '\"') {
inQuote = true;
tryPush(a, s);
@ -581,9 +585,9 @@ public class Priestess {
/**
* 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.
* 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>> {
@ -593,7 +597,8 @@ public class Priestess {
@Override
public Publisher<Mono> apply(CommandArgs t) {
throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
throw new UnsupportedOperationException("Not supported yet."); // Generated from
// nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
}
public String getHelp(String commandName) {
@ -619,12 +624,7 @@ public class Priestess {
public final Callback registerEmojiCommand = new Callback() {
String usage = "{commandname} EMOJI";
String help
= "register emoji for use in basic invocations."
+ ""
+ ""
+ ""
+ "";
String help = "register emoji for use in basic invocations." + "" + "" + "" + "";
@Override
public Publisher<Mono> apply(CommandArgs o) {
@ -688,7 +688,7 @@ public class Priestess {
@Override
public Publisher<Mono> apply(CommandArgs o) {
String pay2 = cdr("set", o.getPayload());
String[] paytokens = pay2.split(" ");
String[] paytokens = tokenize(pay2);
if (paytokens.length == 2) {
log.log(Level.INFO, "setting configuration parameter " + paytokens[0] + " to " + paytokens[1]);
Configuration.setConfigurationParameter(paytokens[0], paytokens[1]);
@ -740,7 +740,7 @@ public class Priestess {
return t.getName().equals(tokens[1]);
}
}).blockFirst();
if (g != null) {
if (c != null) {
if (tokens.length == 3) {
if (isAffirmative(tokens[2])) {
cultableChannelIDs.add(c.getId());
@ -807,13 +807,10 @@ public class Priestess {
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;
}
}*/
* 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");
@ -856,7 +853,8 @@ public class Priestess {
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"));
Command.registerCommand(
Command.commandThatJustRepliesWith("is minecraft online", "i dunno log on and find out smfh"));
}

View File

@ -38,27 +38,19 @@ public class PriestessTest {
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");
testCallbackHelpText(p.shutdownCommand, "shut down", "ha boi");
p.shutdownCommand.help = "${commandname}";
testCallbackHelpText(p.shutdownCommand,
"shut down",
"shut down");
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");
testCallbackHelpText(p.shutdownCommand, "shut down", "shut down turns off the entire internet");
}
private void testCallbackHelpText(Priestess.Callback c, String name, String expHelp) {
@ -71,25 +63,19 @@ public class PriestessTest {
*/
@Test
public void testTokenize() {
testTokenize1("a",
new String[]{"a"});
testTokenize1("a, b",
new String[]{"a,", "b"});
testTokenize1("ya boi",
new String[]{"ya", "boi"});
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"});*/
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) {