Finalized Zino's changes, and many other things.

* added broadcast command
* added interface for distinguishing command line commands
* added button to switch from english to dutch (work in progress)
* fixed playlist viewer and now uses list models.
This commit is contained in:
Andrew Lalis 2017-07-22 15:46:03 +02:00 committed by Andrew Lalis
parent b842ce17c2
commit 117254e27d
13 changed files with 168 additions and 79 deletions

View File

@ -30,7 +30,9 @@ import java.util.*;
*/
public class HandieBot {
//Application name is the name of application as it appears on display window.
public static final String APPLICATION_NAME = "HandieBot";
//The token required for logging into Discord. This is secure and must not be in the source code on GitHub.
private static final String TOKEN;
static {
TOKEN = readToken();
@ -39,15 +41,16 @@ public class HandieBot {
System.exit(-1);
}
}
//Variable to enable or disable GUI.
private static boolean USE_GUI = true;
public static final ResourceBundle resourceBundle = ResourceBundle.getBundle("Strings");
//Resource bundle for localized strings.
public static ResourceBundle resourceBundle = ResourceBundle.getBundle("Strings");
//Discord client object.
public static IDiscordClient client;
//Display objects.
private static BotWindow window;
public static BotWindow window;
public static BotLog log;
//The cross-guild music player.

View File

@ -1,5 +1,6 @@
package handiebot.command;
import handiebot.command.commands.admin.BroadcastCommand;
import handiebot.command.commands.admin.QuitCommand;
import handiebot.command.commands.admin.SetPrefixCommand;
import handiebot.command.commands.misc.TengwarCommand;
@ -42,6 +43,7 @@ public class Commands {
commands.add(new InfoCommand());
commands.add(new SetPrefixCommand());
commands.add(new QuitCommand());
commands.add(new BroadcastCommand());
commands.add(new TengwarCommand());
}

View File

@ -0,0 +1,33 @@
package handiebot.command.commands.admin;
import handiebot.command.CommandContext;
import handiebot.command.types.CommandLineCommand;
import handiebot.command.types.ContextCommand;
import handiebot.utils.MessageUtils;
import sx.blah.discord.handle.obj.IGuild;
import static handiebot.HandieBot.*;
import static handiebot.utils.MessageUtils.sendMessage;
/**
* @author Andrew Lalis
* Command to broadcast a message to all guilds the bot is connected to.
*/
public class BroadcastCommand extends ContextCommand implements CommandLineCommand {
public BroadcastCommand() {
super("broadcast",
"<message>",
resourceBundle.getString("commands.command.broadcast.description"),
8);
}
@Override
public void execute(CommandContext context) {
String message = MessageUtils.getTextFromArgs(context.getArgs(), 0);
for (IGuild guild : client.getGuilds()){
sendMessage(message, musicPlayer.getChatChannel(guild));
}
}
}

View File

@ -1,6 +1,7 @@
package handiebot.command.commands.admin;
import handiebot.HandieBot;
import handiebot.command.types.CommandLineCommand;
import handiebot.command.types.StaticCommand;
import static handiebot.HandieBot.resourceBundle;
@ -9,7 +10,7 @@ import static handiebot.HandieBot.resourceBundle;
* @author Andrew Lalis
* Command to quit the entire bot. This shuts down every guild's support, and the GUI.
*/
public class QuitCommand extends StaticCommand {
public class QuitCommand extends StaticCommand implements CommandLineCommand {
public QuitCommand() {
super("quit",

View File

@ -20,8 +20,7 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
import static handiebot.HandieBot.*;
import static handiebot.utils.MessageUtils.sendMessage;
import static handiebot.utils.YoutubeSearch.WATCH_URL;
@ -101,13 +100,16 @@ public class PlaylistCommand extends ContextCommand {
if (context.getArgs().length >= 2) {
Playlist playlist = new Playlist(context.getArgs()[1]);
playlist.save();
if (context.getArgs().length > 2) {
for (int i = 2; i < context.getArgs().length; i++) {
String url = context.getArgs()[i];
playlist.loadTrack(url);
}
playlist.save();
}
log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.playlist.createdPlaylist.log"), playlist.getName(), playlist.getTrackCount()));
sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.createdPlaylist.message"), playlist.getName(), this.getPrefixedName(context.getGuild()), playlist.getName()), context.getChannel());
window.updatePlaylistNames();//Refresh the list of names in the GUI.
} else {
sendMessage(resourceBundle.getString("commands.command.playlist.error.createPlaylistName"), context.getChannel());
}
@ -126,6 +128,7 @@ public class PlaylistCommand extends ContextCommand {
if (success){
log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.playlist.delete.log"), context.getArgs()[1]));
sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.delete.message"), context.getArgs()[1]), context.getChannel());
window.updatePlaylistNames();//Refresh the list of names in the GUI.
} else {
log.log(BotLog.TYPE.ERROR, MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.delete.log"), context.getArgs()[1]));
sendMessage(resourceBundle.getString("commands.command.playlist.error.delete.message"), context.getChannel());

View File

@ -0,0 +1,8 @@
package handiebot.command.types;
/**
* @author Andrew Lalis
* If a command implements this Interface, it is marked as safe to use in the command line.
*/
public interface CommandLineCommand {
}

View File

@ -8,6 +8,6 @@ import sx.blah.discord.handle.impl.events.guild.channel.message.reaction.Reactio
*/
public interface ReactionListener {
public void onReactionEvent(ReactionEvent event);
void onReactionEvent(ReactionEvent event);
}

View File

@ -22,7 +22,7 @@ import static handiebot.HandieBot.resourceBundle;
* on the playlist.
*/
public class Playlist {
//TODO: Externalize strings.
private String name;
private List<UnloadedTrack> tracks;
@ -119,7 +119,7 @@ public class Playlist {
* @param listLength The number of items in a potential list to choose from.
* @return A pseudo-random choice as to which item to pick from the list.
*/
public static int getShuffledIndex(int listLength){
private static int getShuffledIndex(int listLength){
float threshold = 0.2f;
int trueLength = listLength - (int)(threshold*(float)listLength);
Random rand = new Random();
@ -160,13 +160,11 @@ public class Playlist {
*/
public void load(){
String path = System.getProperty("user.home")+"/.handiebot/playlist/"+name.replace(" ", "_")+".txt";
log.log(BotLog.TYPE.MUSIC, "Loading playlist from: "+path);
File playlistFile = new File(path);
if (playlistFile.exists()){
try {
List<String> lines = Files.readAllLines(Paths.get(playlistFile.toURI()));
int trackCount = Integer.parseInt(lines.remove(0));
this.name = name;
this.tracks = new ArrayList<>(trackCount);
for (int i = 0; i < trackCount; i++){
String[] words = lines.remove(0).split(" / ");

View File

@ -2,7 +2,6 @@ package handiebot.view;
import handiebot.HandieBot;
import handiebot.lavaplayer.playlist.Playlist;
import handiebot.lavaplayer.playlist.UnloadedTrack;
import javax.imageio.ImageIO;
import javax.swing.*;
@ -30,6 +29,7 @@ public class BotWindow extends JFrame {
private JList<String> playlistNamesList;
private JList<String> currentPlaylistList;
private ListSelectionListener playlistListener;
private JPanel playlistDisplayPanel;
public BotWindow(){
super(HandieBot.APPLICATION_NAME);
@ -37,6 +37,7 @@ public class BotWindow extends JFrame {
//Output area.
outputArea = new JTextPane();
outputArea.setBackground(Color.white);
outputArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(outputArea);
scrollPane.setAutoscrolls(true);
@ -49,24 +50,24 @@ public class BotWindow extends JFrame {
this.playlistNamesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
this.currentPlaylistList = new JList<>(this.currentPlaylistModel);
this.currentPlaylistList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
this.playlistListener = e -> {
System.out.println("user updated list.");
updatePlaylistData();
};
this.playlistNamesList.addListSelectionListener(this.playlistListener);
updatePlaylistData();
JPanel playlistDisplayPanel = new JPanel(new BorderLayout());
updatePlaylistNames();
this.playlistNamesList.addListSelectionListener(new PlaylistSelectionListener(this.currentPlaylistModel));
//Song names scroll pane.
JScrollPane songNamesScrollPane = new JScrollPane(this.currentPlaylistList);
songNamesScrollPane.setPreferredSize(new Dimension(250, 200));
playlistDisplayPanel.add(songNamesScrollPane, BorderLayout.PAGE_END);
//Create the panel to hold both of the sub-panels.
playlistDisplayPanel = new JPanel();
playlistDisplayPanel.setPreferredSize(new Dimension(250, 0));
playlistDisplayPanel.setLayout(new BorderLayout());
//Playlist name scroll pane.
JScrollPane playlistNamesScrollPane = new JScrollPane(playlistNamesList);
playlistNamesScrollPane.setPreferredSize(new Dimension(250, 1000));
playlistDisplayPanel.add(playlistNamesScrollPane, BorderLayout.CENTER);
playlistNamesScrollPane.setColumnHeaderView(new JLabel("Playlists"));
playlistDisplayPanel.add(playlistNamesScrollPane, BorderLayout.PAGE_START);
//Song names scroll pane.
JScrollPane songNamesScrollPane = new JScrollPane(this.currentPlaylistList);
songNamesScrollPane.setColumnHeaderView(new JLabel("Selected Playlist"));
playlistDisplayPanel.add(songNamesScrollPane, BorderLayout.CENTER);
getContentPane().add(playlistDisplayPanel, BorderLayout.EAST);
@ -99,7 +100,6 @@ public class BotWindow extends JFrame {
e.printStackTrace();
}
setJMenuBar(new MenuBar(this));
//SelectionController controller = new SelectionController();
setPreferredSize(new Dimension(800, 600));
pack();
setLocationRelativeTo(null);
@ -107,50 +107,21 @@ public class BotWindow extends JFrame {
}
/**
* Sets the playlist data in the window.
* Updates the list of playlist names.
*/
private void updatePlaylistData() {
public void updatePlaylistNames(){
List<String> playlistNames = Playlist.getAvailablePlaylists();
this.playlistNamesList.removeListSelectionListener(this.playlistListener);
this.playlistNamesModel.clear();
for (String name : playlistNames){
this.playlistNamesModel.addElement(name);
}
this.playlistNamesList.addListSelectionListener(this.playlistListener);
String selectedValue = this.playlistNamesList.getSelectedValue();
System.out.println("selected value: "+selectedValue);
if (selectedValue != null && Playlist.playlistExists(selectedValue)){
Playlist playlist = new Playlist(selectedValue);
playlist.load();
List<UnloadedTrack> tracks = playlist.getTracks();
this.currentPlaylistModel.clear();
for (int i = 0; i < playlist.getTrackCount(); i++){
this.currentPlaylistModel.addElement(tracks.get(i).getTitle());
}
}
/*
this.playlistNamesList.addListSelectionListener(e -> {
String name = playlistNamesList.getSelectedValue();
String path = System.getProperty("user.home") + "/.handiebot/playlist/" + name + ".txt";
File playlistFile = new File(path);
if (playlistFile.exists()) {
try {
List<String> lines = Files.readAllLines(Paths.get(playlistFile.toURI()));
int trackCount = Integer.parseInt(lines.remove(0));
String[] words = {"A"};
StringBuilder sb = new StringBuilder();
for (int i1 = 0; i1 < trackCount; i1++) {
words = lines.remove(0).split(" / ");
sb.append(i1 +1).append(". ").append(words[0]).append("\n");
}
if(!words[0].equals("A")) {
pane.setText(sb.toString());
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
});*/
/**
* Sets the playlists panel as visible or invisible.
*/
public void togglePlaylistsVisibility(){
this.playlistDisplayPanel.setVisible(!this.playlistDisplayPanel.isVisible());
}
public JTextPane getOutputArea(){

View File

@ -1,11 +1,19 @@
package handiebot.view;
import handiebot.command.Commands;
import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.types.Command;
import handiebot.command.types.CommandLineCommand;
import handiebot.command.types.ContextCommand;
import handiebot.command.types.StaticCommand;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import static handiebot.HandieBot.log;
import static handiebot.command.Commands.commands;
/**
* @author Andrew Lalis
* Class to listen for commands from the console command line.
@ -26,7 +34,7 @@ public class CommandLineListener implements KeyListener {
String command = words[0];
String[] args = new String[words.length-1];
System.arraycopy(words, 1, args, 0, words.length - 1);
executeCommand(command, args);
executeCommand(command, new CommandContext(HandieBot.client.getOurUser(), null, null, args));
}
}
@ -35,15 +43,21 @@ public class CommandLineListener implements KeyListener {
}
/**
* Executes a given command on the command line.
* Executes a given command on the command line. This must be written separate from the {@code executeCommand}
* method in {@code Commands}, because here, no permissions may be checked.
* @param command The first word typed, or the command itself.
* @param args The list of arguments for the command.
* @param context The list of arguments for the command.
*/
private void executeCommand(String command, String[] args) {
switch (command) {
case "quit":
Commands.executeCommand("quit", null);
break;
private void executeCommand(String command, CommandContext context) {
for (Command cmd : commands){
if (cmd.getName().equals(command) && (cmd instanceof CommandLineCommand)){
log.log(BotLog.TYPE.COMMAND, "Command issued: "+command);
if (cmd instanceof StaticCommand){
((StaticCommand) cmd).execute();
} else if (cmd instanceof ContextCommand){
((ContextCommand) cmd).execute(context);
}
}
}
}
}

View File

@ -5,6 +5,8 @@ import handiebot.view.actions.ActionItem;
import handiebot.view.actions.CommandAction;
import javax.swing.*;
import java.util.Locale;
import java.util.ResourceBundle;
import static handiebot.HandieBot.resourceBundle;
@ -13,7 +15,7 @@ import static handiebot.HandieBot.resourceBundle;
* Custom menu bar to be added to the console control panel.
*/
public class MenuBar extends JMenuBar {
//TODO: Implement a way to restart the program in nederlands.
private BotWindow window;
private int language;
@ -24,6 +26,10 @@ public class MenuBar extends JMenuBar {
this.add(fileMenu);
JMenu viewMenu = new JMenu(resourceBundle.getString("menu.viewMenu.view"));
JMenu language = new JMenu(resourceBundle.getString("menu.viewMenu.language"));
language.add(new ActionItem(resourceBundle.getString("menu.viewMenu.language.english"), e -> resourceBundle = ResourceBundle.getBundle("Strings", Locale.US)));
language.add(new ActionItem(resourceBundle.getString("menu.viewMenu.language.dutch"), e -> resourceBundle = ResourceBundle.getBundle("Strings", Locale.forLanguageTag("nl"))));
viewMenu.add(language);
viewMenu.add(new ActionItem(resourceBundle.getString("menu.viewMenu.playlistsVisible"), e -> window.togglePlaylistsVisibility()));
this.add(viewMenu);
}

View File

@ -0,0 +1,47 @@
package handiebot.view;
import handiebot.lavaplayer.playlist.Playlist;
import handiebot.lavaplayer.playlist.UnloadedTrack;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.util.List;
/**
* @author Andrew Lalis
* Listens for if the user selects a playlist from the list.
*/
public class PlaylistSelectionListener implements ListSelectionListener {
private DefaultListModel<String> songsListModel;
public PlaylistSelectionListener(DefaultListModel<String> songsListModel){
this.songsListModel = songsListModel;
}
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()){
updatePlaylistData((JList<String>) e.getSource());
}
}
/**
* Updates the list of songs for a selected playlist.
* Does not update the list of playlists.
*/
private void updatePlaylistData(JList<String> playlistNamesList) {
String selectedValue = playlistNamesList.getSelectedValue();
if (selectedValue != null && Playlist.playlistExists(selectedValue)){
Playlist playlist = new Playlist(selectedValue);
playlist.load();
List<UnloadedTrack> tracks = playlist.getTracks();
songsListModel.clear();
for (int i = 0; i < playlist.getTrackCount(); i++){
songsListModel.addElement(tracks.get(i).getTitle());
}
}
}
}

View File

@ -16,9 +16,11 @@ window.close.title=Confirm shutdown
#MenuBar
menu.fileMenu.title=File
menu.fileMenu.quit=Quit
menu.editMenu.edit=Edit
menu.viewMenu.view=View
menu.viewMenu.language=Language
menu.viewMenu.language.english=English
menu.viewMenu.language.dutch=Dutch
menu.viewMenu.playlistsVisible=Playlists
#Actions
action.menu.playlist=Edit playlists
action.menu.playlist.add=Add
@ -40,6 +42,7 @@ commands.command.info.embed.helpCommand=Receive a message with a detailed list o
commands.command.info.embed.playCommand=Play a song, or add it to the queue if one is already playing. A URL can be a YouTube or SoundCloud link.
commands.command.info.embed.queueCommand=Show a list of songs that will soon be played.
commands.command.quit.description=Shuts down the bot on all servers.
commands.command.broadcast.description=Broadcasts a message to every server the bot is connected to.
commands.command.setPrefix.description=Sets the prefix for commands.
commands.command.setPrefix.changed=Changed command prefix to "{0}"
commands.command.setPrefix.noPrefixError=You must provide a new prefix.