aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--soundchan.properties.example20
-rw-r--r--src/main/java/soundchan/BotListener/BotListener.java51
-rw-r--r--src/main/java/soundchan/BotListener/MediaWatcher.java147
-rw-r--r--src/main/java/soundchan/BotListener/MediaWatcherListener.java12
-rw-r--r--src/main/java/soundchan/LocalAudioManager.java52
5 files changed, 257 insertions, 25 deletions
diff --git a/soundchan.properties.example b/soundchan.properties.example
index 0655076..b600032 100644
--- a/soundchan.properties.example
+++ b/soundchan.properties.example
@@ -1,20 +1,30 @@
+// Example properties file for SoundChan
+//
+// Flag conditions are enabled with any of the following values:
+// true, on, enable, yes, 1
+// any other string (or empty) will leave the condition disabled
+
+
//The Bot Token you will have received from the discord developers page
botToken=BOT_TOKEN_FROM_DISCORD
//The local file path to the directory of your sounds. Don't forget to escape your slashes
localFilePath=C:\\PATH\\TO\\SOUNDS\\DIRECTORY
+//Flag for watching the sound directory for changes
+watchLocalFilePath=FLAG_CONDITION
+
//The user for the
followingUser=USERNAME
-//Flag conditions are enabled with any of the following values:
-// true, on, enable, yes, 1
-//any other string (or empty) will leave the condition disabled
-
//If you want SoundChan to play an audio file whit their name when a user joins the channel or have that information come from below file
//This is a flag condition
-audioOnUserJoin=on/off
+audioOnUserJoin=FLAG_CONDITION
//The file where users and sound clips are related, see usersound.properties.example for more info
//If this is not set, it will default to usersounds.properties
userAudioFilePath=C:\\PATH\\TO\\USER\\SOUND\\FILE
+
+//Flag for watching the user sound file for changes
+//If audioOnUserJoin is not enabled, then this will do nothing
+watchUserSoundFile=FLAG_CONDITION \ No newline at end of file
diff --git a/src/main/java/soundchan/BotListener/BotListener.java b/src/main/java/soundchan/BotListener/BotListener.java
index 415e350..b8b4bad 100644
--- a/src/main/java/soundchan/BotListener/BotListener.java
+++ b/src/main/java/soundchan/BotListener/BotListener.java
@@ -19,10 +19,12 @@ import net.dv8tion.jda.core.hooks.ListenerAdapter;
import net.dv8tion.jda.core.managers.AudioManager;
import soundchan.*;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
+import java.nio.file.WatchEvent;
+import java.sql.SQLOutput;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
public class BotListener extends ListenerAdapter{
@@ -32,6 +34,7 @@ public class BotListener extends ListenerAdapter{
private final AudioPlayerManager playerManager;
private final Map<Long, GuildMusicManager> musicManagers;
private BotListenerHelpers helper = new BotListenerHelpers();
+ private Map<String, Future<?> > otherTasks;
// From configuration file
private static String followingUser;
@@ -56,17 +59,35 @@ public class BotListener extends ListenerAdapter{
localFilePath = properties.getProperty("localFilePath");
followingUser = properties.getProperty("followingUser");
audioOnUserJoin = settingEnableCheck(properties.getProperty("audioOnUserJoin"));
+ otherTasks = new HashMap<>();
if(audioOnUserJoin) {
String userAudioPath = properties.getProperty("userAudioFilePath");
if(userAudioPath == null || userAudioPath.contentEquals("")) {
- localManager = new LocalAudioManager(localFilePath, "usersound.properties");
+ userAudioPath = "usersound.properties";
}
- else {
- localManager = new LocalAudioManager(localFilePath, userAudioPath);
+ localManager = new LocalAudioManager(localFilePath, userAudioPath);
+
+ if(settingEnableCheck(properties.getProperty("watchUserSoundFile"))) {
+ addWatcherTask(new MediaWatcherListener() {
+ @Override
+ public void onWatchEvent(WatchEvent event) {
+ localManager.UpdateUserAudio();
+ }
+ }, userAudioPath, "watchUserSoundFile", false);
}
}
else
localManager = new LocalAudioManager(localFilePath);
+
+ if(settingEnableCheck(properties.getProperty("watchLocalFilePath"))) {
+ addWatcherTask(new MediaWatcherListener() {
+ @Override
+ public void onWatchEvent(WatchEvent event) {
+ localManager.UpdateFiles();
+ }
+ }, localFilePath, "watchLocalFilePath", true);
+ }
+
}
private synchronized GuildMusicManager getGuildAudioPlayer() {
@@ -425,6 +446,8 @@ public class BotListener extends ListenerAdapter{
* @return True if it matches a value to enable, False otherwise
*/
private static boolean settingEnableCheck(String value) {
+ if(value == null)
+ return false;
value = value.toLowerCase();
if(value.contentEquals("true") || value.contentEquals("1") ||
value.contentEquals("yes") || value.contentEquals("on") ||
@@ -434,4 +457,18 @@ public class BotListener extends ListenerAdapter{
return false;
}
+ /**
+ * Adds a new MediaWatcher to the list of running tasks
+ * @param listener Listener that will get callback during watching of media
+ * @param filepath Path to either directory or file
+ * @param taskName Thing to name task as
+ * @param watchSubDirs Also watch any subdirectories in the given directory (doesn't do anything if watching a file)
+ */
+ private void addWatcherTask(@NotNull MediaWatcherListener listener, String filepath, String taskName, boolean watchSubDirs) {
+ ExecutorService executorService = Executors.newSingleThreadExecutor();
+ MediaWatcher watcher = new MediaWatcher(listener, filepath, watchSubDirs);
+ otherTasks.put(taskName, executorService.submit(watcher));
+ executorService.shutdown();
+ }
+
}
diff --git a/src/main/java/soundchan/BotListener/MediaWatcher.java b/src/main/java/soundchan/BotListener/MediaWatcher.java
new file mode 100644
index 0000000..2784eec
--- /dev/null
+++ b/src/main/java/soundchan/BotListener/MediaWatcher.java
@@ -0,0 +1,147 @@
+package soundchan.BotListener;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+
+import static java.lang.Thread.sleep;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+
+public class MediaWatcher implements Runnable {
+
+ private MediaWatcherListener listener;
+ private String mediaFilename;
+ private Path mediaDir;
+ private WatchService watchService;
+ private WatchKey watchKey;
+ private ArrayList<WatchKey> subDirKeys;
+ private boolean isDirectory;
+ private int sleepTime = 5000;
+
+ @SuppressWarnings("unchecked")
+ static <T> WatchEvent<T> cast(WatchEvent<?> event) {
+ return (WatchEvent<T>) event;
+ }
+
+ /**
+ * Creates a MediaWatcher, which monitors changes to files either within a directory or for a specific file.
+ * Defaults to scanning every 5 seconds.
+ * @param listener Object that will get a callback when there is a watch event
+ * @param filepath Path to either directory or specific file
+ * @param watchSubDirs Also watch any subdirectories in the given directory (doesn't do anything if watching a file)
+ */
+ public MediaWatcher(MediaWatcherListener listener, String filepath, boolean watchSubDirs) {
+ this.listener = listener;
+ startWatchService(filepath, watchSubDirs);
+ }
+
+ /**
+ * Creates a MediaWatcher, which monitors changes to files either within a directory or for a specific file.
+ * @param listener Object that will get a callback when there is a watch event
+ * @param filepath Path to either directory or specific file
+ * @param watchSubDirs Also watch any subdirectories in the given directory (doesn't do anything if watching a file)
+ * @param sleepTime How long to put the scanner thread to sleep between rescans (time in milliseconds)
+ */
+ public MediaWatcher(MediaWatcherListener listener, String filepath, boolean watchSubDirs, int sleepTime) {
+ this.listener = listener;
+ this.sleepTime = sleepTime;
+ startWatchService(filepath, watchSubDirs);
+ }
+
+ /**
+ * Sets up watch service for the file/directory
+ * @param filepath Path to file or directory to be scanned
+ * @param watchSubDirs Also watch any subdirectories in the given directory (doesn't do anything if watching a file)
+ */
+ private void startWatchService(String filepath, boolean watchSubDirs) {
+ File mediaFile = new File(filepath);
+ this.mediaFilename = mediaFile.getName();
+ if(mediaFile.isFile()) {
+ try {
+ this.mediaDir = mediaFile.getCanonicalFile().getParentFile().toPath();
+ } catch (IOException e) {
+ System.out.println("Error getting parent path of " + mediaFilename);
+ }
+ isDirectory = false;
+ } else if(mediaFile.isDirectory()) {
+ this.mediaDir = mediaFile.toPath();
+ isDirectory = true;
+ subDirKeys = new ArrayList<>();
+ }
+ try {
+ this.watchService = FileSystems.getDefault().newWatchService();
+ this.watchKey = mediaDir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
+ if(isDirectory && watchSubDirs) {
+ Files.walkFileTree(mediaDir, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
+ try {
+ WatchKey temp = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
+ subDirKeys.add(temp);
+ return FileVisitResult.CONTINUE;
+ } catch (IOException e) {
+ System.out.println("Error setting up watch service with sub dirs in " + filepath);
+ return FileVisitResult.SKIP_SUBTREE;
+ }
+ }
+ });
+ }
+ } catch (IOException e) {
+ System.out.println("Error setting up watcher for " + filepath);
+ }
+ }
+
+ /**
+ * Called by an executor, checks for changes to file(s)
+ */
+ public void run() {
+ try {
+ while(true) {
+ WatchKey key = watchService.take(); // Wait for an event to happen
+
+ // Check this event happened in a place we are monitoring, otherwise ignore it
+ if(!isDirectory && this.watchKey != key) {
+ System.out.println("Error with WatchKey");
+ continue;
+ } else if(isDirectory) {
+ if(this.watchKey != key) { // Our event doesn't happen in the root of the sounds directory
+ boolean noKeyMatch = true;
+ for(WatchKey subKey : subDirKeys) { // Check if it happened in on of the sub directories
+ if(subKey == key) {
+ noKeyMatch = false;
+ break;
+ }
+ }
+ if(noKeyMatch) {
+ System.out.println("Error with WatchKey");
+ continue;
+ }
+ }
+ }
+
+ for(WatchEvent<?> event : key.pollEvents()) {
+ WatchEvent<Path> pathEvent = cast(event);
+ if(isDirectory) {
+ listener.onWatchEvent(event);
+ } else {
+ if(pathEvent.context().endsWith(mediaFilename)) {
+ listener.onWatchEvent(event);
+ }
+ }
+ }
+
+ if(!key.reset()) {
+ break;
+ }
+
+ sleep(sleepTime);
+ }
+ } catch(InterruptedException e) {
+ return;
+ }
+ }
+}
diff --git a/src/main/java/soundchan/BotListener/MediaWatcherListener.java b/src/main/java/soundchan/BotListener/MediaWatcherListener.java
new file mode 100644
index 0000000..8c146f6
--- /dev/null
+++ b/src/main/java/soundchan/BotListener/MediaWatcherListener.java
@@ -0,0 +1,12 @@
+package soundchan.BotListener;
+
+import java.nio.file.WatchEvent;
+
+public interface MediaWatcherListener {
+
+ /**
+ * Called by MediaWatcher when there is a file event. Any created, deleted, or modified events will be passed for use.
+ * @param event The type of event that triggered the update.
+ */
+ void onWatchEvent(WatchEvent event);
+}
diff --git a/src/main/java/soundchan/LocalAudioManager.java b/src/main/java/soundchan/LocalAudioManager.java
index 28d25b9..ea5b317 100644
--- a/src/main/java/soundchan/LocalAudioManager.java
+++ b/src/main/java/soundchan/LocalAudioManager.java
@@ -17,11 +17,12 @@ public class LocalAudioManager {
public Map<String, String> filenameDict;
public Map<String, String> usernameDict;
private String filepath;
+ private String userSoundFilepath;
public LocalAudioManager(String filepath_in){
filepath = filepath_in;
- filenameDict = new HashMap<>();
- PopulateFiles();
+ userSoundFilepath = null;
+ filenameDict = PopulateFiles();
}
/**
@@ -31,10 +32,9 @@ public class LocalAudioManager {
*/
public LocalAudioManager(String filepath_in, String userSoundFile) {
filepath = filepath_in;
- filenameDict = new HashMap<>();
- usernameDict = new HashMap<>();
- PopulateFiles();
- MapUserAudio(userSoundFile);
+ userSoundFilepath = userSoundFile;
+ filenameDict = PopulateFiles();
+ usernameDict = MapUserAudio();
}
/**
@@ -90,31 +90,57 @@ public class LocalAudioManager {
channel.sendMessage(toPrint).queue();
}
- private void PopulateFiles(){
+ /**
+ * Updates the map of sound files
+ */
+ public void UpdateFiles() {
+ filenameDict = PopulateFiles();
+ }
+
+ /**
+ * Updates the map of usernames to sound files
+ */
+ public void UpdateUserAudio() {
+ if(userSoundFilepath != null | userSoundFilepath.contentEquals("")) {
+ usernameDict = MapUserAudio();
+ }
+ }
+
+ /**
+ * Creates a map of the sounds in the sound directory
+ * @return A map with the filename (without extension) is the key for the filename (with extension)
+ */
+ private Map<String, String> PopulateFiles(){
File folder = new File(filepath);
File[] listOfFiles = folder.listFiles();
+ Map<String, String> fileDict = new HashMap<>();
+
for (File file : listOfFiles) {
if (file.isFile()) {
String filename = file.getName();
- filenameDict.put(filename.substring(0, filename.indexOf('.')), filename);
+ fileDict.put(filename.substring(0, filename.indexOf('.')), filename);
}
}
+ return fileDict;
}
/**
* Reads in users and their respective sounds from file, then builds a map of users to the filenames. This assumes
* filenames for the sounds are valid, but doesn't check for them.
- * @param userSoundFile The file (with path if required) with listing of users and the sounds to play when they join
+ * @return A map with the usernames as the keys for the filename of the sound
*/
- private void MapUserAudio(String userSoundFile) {
- Properties userSoundProp = LoadProperties(userSoundFile);
+ private Map<String, String> MapUserAudio() {
+ Properties userSoundProp = LoadProperties(userSoundFilepath);
Set<String> users = userSoundProp.stringPropertyNames();
+
+ Map<String, String> userDict = new HashMap<>();
+
for(String user : users) {
String soundFile = userSoundProp.getProperty(user);
- usernameDict.put(user, soundFile);
+ userDict.put(user, soundFile);
}
-
+ return userDict;
}
/**