Nathan McRae c0028b2978 Add prefix-checking to message
We want to make sure the message doesn't match any of the station's listed prefixes.

Also, we give a finite number of tries to generate a valid message just in case prefixes somehow cover all messages.
Make it a setting so it can be increased if needed.
2025-02-08 12:38:55 -08:00

216 lines
7.9 KiB
Java

package name.nathanmcrae.numbersstation;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import com.tearsofaunicorn.wordpress.api.model.Post;
import com.tearsofaunicorn.wordpress.api.WordpressClient;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.Level;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.apache.commons.cli.*;
public class Main extends Application {
private static final Logger logger = Logger.getLogger(Main.class.getName());
// TODO: get git info
private static final String VERSION = "0.0.1";
private static Path statePath = null;
public static Path getStatePath() {
if (statePath == null) {
String stateHome = System.getenv("XDG_STATE_HOME");
if (stateHome == null || stateHome.isEmpty() || !Paths.get(stateHome).isAbsolute()) {
String userHome = System.getProperty("user.home");
statePath = Paths.get(userHome, ".local", "state", "numbers-station");
} else {
statePath = Paths.get(stateHome, "numbers-station");
}
}
return statePath;
}
@Override
public void start(Stage primaryStage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("MainView.fxml"));
primaryStage.setTitle("Numbers Station");
primaryStage.setScene(new Scene(root));
primaryStage.show();
logger.info("Application started");
} catch (IOException e) {
logger.log(Level.SEVERE, "Failed to load main view", e);
}
}
private static void setupLogger() {
try {
Path logFile = getStatePath().resolve("main.log");
if (!Files.exists(logFile.getParent())) {
Files.createDirectories(logFile.getParent());
}
FileHandler fileHandler = new FileHandler(logFile.toString(), true);
fileHandler.setFormatter(new CustomFormatter());
logger.addHandler(fileHandler);
} catch (IOException e) {
System.out.println("Failed to set up logger: " + e.getMessage());
}
}
private static String parseArguments(String[] args) {
Options options = new Options();
Option help = new Option("h", "help", false, "Show help");
options.addOption(help);
Option version = new Option("v", "version", false, "Show version");
options.addOption(version);
Option station = new Option("s", "station", true, "Specify station name");
station.setArgName("station-name");
options.addOption(station);
CommandLineParser parser = new DefaultParser();
HelpFormatter formatter = new HelpFormatter();
String stationName = null;
try {
CommandLine cmd = parser.parse(options, args);
if (cmd.hasOption("help")) {
formatter.printHelp("numbers-station", options);
System.exit(0);
}
if (cmd.hasOption("version")) {
System.out.println("Numbers Station version " + VERSION);
System.exit(0);
}
if (cmd.hasOption("station")) {
stationName = cmd.getOptionValue("station");
}
} catch (ParseException e) {
System.out.println(e.getMessage());
formatter.printHelp("numbers-station", options);
System.exit(1);
}
return stationName;
}
public static void main(String[] args) {
String stationName = parseArguments(args);
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
logger.log(Level.SEVERE, "Unhandled exception caught", throwable);
});
Platform.setImplicitExit(false);
Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> {
logger.log(Level.SEVERE, "Unhandled exception in JavaFX application thread", throwable);
});
setupLogger();
if (stationName != null) {
// TODO: errors in runStation should trigger a notification
runStation(stationName);
} else {
launch(args);
}
}
public static void runStation(String stationName) {
if (stationName == null || stationName == "") {
logger.log(Level.SEVERE, "Station name must be provided and not empty");
// TODO: Notification
System.exit(1);
}
Result<MainSettings, Exception> result = MainSettings.load();
if (!result.hasSuccess()) {
logger.log(Level.SEVERE, "Unable to load settings");
// TODO: Notification
System.exit(1);
}
MainSettings settings = result.getSuccess().get();
StationSettings loadedStation = settings.getStations().stream()
.filter(station -> station.getName().equals(stationName))
.findFirst()
.orElse(null);
if (loadedStation == null) {
logger.log(Level.SEVERE, "Unable to find station " + stationName);
// TODO: Notification
System.exit(1);
}
logger.info("Loaded station " + stationName);
Path stationDirPath = loadedStation.stationPath();
try {
boolean generateMessage = false;
if (!Files.exists(stationDirPath)) {
Files.createDirectories(stationDirPath);
}
Path nextMessagePath = stationDirPath.resolve("next-message.txt");
String messageText;
if (!Files.exists(nextMessagePath)) {
messageText = loadedStation.generateMessage(settings.getMessageGenerationAttempts());
} else {
messageText = new String(Files.readAllBytes(nextMessagePath));
}
// Send message using appropriate method
switch (loadedStation.getMessageMethod()) {
case StationSettings.MessageMethod.SFTP:
//loadedStation.uploadNextMessageToSFTP();
break;
case StationSettings.MessageMethod.WORDPRESS:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = dateFormat.format(new Date());
System.setProperty("wordpress.username", loadedStation.getUsername());
System.setProperty("wordpress.password", loadedStation.getPassword());
System.setProperty("wordpress.url", loadedStation.getAddress());
Post post = new Post(dateStr, messageText);
WordpressClient client = new WordpressClient();
String newPostId = client.newPost(post);
break;
default:
logger.log(Level.SEVERE, "Message method " + loadedStation.getMessageMethod() + " not supported");
// TODO: Notification
System.exit(1);
}
String newMessageText = loadedStation.generateMessage(settings.getMessageGenerationAttempts());
Files.write(nextMessagePath, newMessageText.getBytes(StandardCharsets.UTF_8));
} catch (IOException | StationSettings.MessageGenerationException e) {
logger.log(Level.SEVERE, "Exception while posting message to station " + stationName, e);
// TODO: Notification
System.exit(1);
}
System.exit(0);
}
}