diff --git a/src/main/java/moe/nekojimi/chords/Main.java b/src/main/java/moe/nekojimi/chords/Main.java
index 4d3d3e5..2adc300 100644
--- a/src/main/java/moe/nekojimi/chords/Main.java
+++ b/src/main/java/moe/nekojimi/chords/Main.java
@@ -6,15 +6,10 @@
 package moe.nekojimi.chords;
 
 import java.io.File;
-import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.*;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
 import javax.security.auth.login.LoginException;
-import moe.nekojimi.chords.commands.Command;
-import moe.nekojimi.chords.commands.JoinCommand;
-import moe.nekojimi.musicsearcher.Query;
+import moe.nekojimi.chords.commands.*;
 import moe.nekojimi.musicsearcher.Result;
 import moe.nekojimi.musicsearcher.providers.MetaSearcher;
 import moe.nekojimi.musicsearcher.providers.Searcher;
@@ -35,8 +30,6 @@ import net.dv8tion.jda.api.utils.cache.CacheFlag;
 public class Main extends ListenerAdapter
 {
 
-    private static final double SEARCH_SCORE_THRESHOLD_AUTOPLAY = 9999; // disable autoplay it sucks
-    private static final double SEARCH_SCORE_THRESHOLD_DISPLAY = 0.6;
 
     private MusicHandler musicHandler;
     private final Downloader downloader;
@@ -44,11 +37,10 @@ public class Main extends ListenerAdapter
     private JDA jda;
 
     private final Map<String, Command> commands = new HashMap<>();
+    private final Command helpCommand;
 
     private VoiceChannel currentVoiceChannel = null;
 
-    private List<Result> lastSearchResults;
-
     private int trackNumber = 1;
 
     /**
@@ -111,6 +103,14 @@ public class Main extends ListenerAdapter
         searcher = MetaSearcher.loadYAML(new File("searchproviders.yml"));
 
         addCommand(new JoinCommand(this));
+        addCommand(new LeaveCommand(this));
+        addCommand(new PlayCommand(this));
+        addCommand(new QueueCommand(this));
+        addCommand(new RemoveCommand(this));
+        addCommand(new RestartCommand(this));
+        addCommand(new SkipCommand(this));
+        helpCommand = new HelpCommand(this);
+        addCommand(helpCommand);
     }
 
     private void addCommand(Command command)
@@ -157,176 +157,15 @@ public class Main extends ListenerAdapter
             {
                 Command command = commands.get(cmd);
                 command.call(event, List.of(arg));
-            }
-
-            //            else if (content.startsWith("!join "))
-            //                onJoinCommand(event, guild, arg);
-            else if (content.equals("!leave"))
-                onLeaveCommand(event);
-//            else if (content.equals("!join"))
-//                onJoinCommand(event);
-            else if (content.startsWith("!play "))
-                onPlayCommand(event, guild, arg);
-            else if (content.startsWith("!queue"))
-                onQueueCommand(event, guild);
-            else if (content.startsWith("!remove "))
-                onRemoveCommand(event, guild, arg);
-            else if (content.startsWith("!restart"))
-                onRestartCommand(event);
-            else if (content.startsWith("!skip"))
-                onSkipCommand(event);
-            else if (content.startsWith("!"))
-                onHelpCommand(event);
+            } else
+                helpCommand.call(event, List.of(arg));
         } catch (Exception ex)
         {
             event.getChannel().sendMessage("Error in command! " + ex.getMessage()).queue();
         }
     }
 
-    /**
-     * Handle command without arguments.
-     *
-     * @param event
-     * The event for this command
-     */
-//    private void onJoinCommand(GuildMessageReceivedEvent event)
-//    {
-//        // Note: None of these can be null due to our configuration with the JDABuilder!
-//        Member member = event.getMember();                              // Member is the context of the user for the specific guild, containing voice state and roles
-//        GuildVoiceState voiceState = member.getVoiceState();            // Check the current voice state of the user
-//        VoiceChannel channel = voiceState.getChannel();                 // Use the channel the user is currently connected to
-////        if (channel != null)
-////        {
-////            connectTo(channel);                                         // Join the channel of the user
-////            onConnecting(channel, event.getChannel());                  // Tell the user about our success
-////        } else
-////            onUnknownChannel(event.getChannel(), "your voice channel"); // Tell the user about our failure
-//        onJoinCommand(event, member.getGuild(), channel.getName());
-//    }
-
-    /**
-     * Handle command with arguments.
-     *
-     * @param event
-     * The event for this command
-     * @param guild
-     * The guild where its happening
-     * @param arg
-     * The input argument
-     */
-//    private void onJoinCommand(GuildMessageReceivedEvent event, Guild guild, String arg)
-//    {
-//        boolean isNumber = arg.matches("\\d+"); // This is a regular expression that ensures the input consists of digits
-//        VoiceChannel channel = null;
-//        if (isNumber) // The input is an id?
-//            channel = guild.getVoiceChannelById(arg);
-//        if (channel == null)                    // Then the input must be a name?
-//        {
-//            List<VoiceChannel> channels = guild.getVoiceChannelsByName(arg, true);
-//            if (!channels.isEmpty())            // Make sure we found at least one exact match
-//                channel = channels.get(0);      // We found a channel! This cannot be null.
-//        }
-//
-//        TextChannel textChannel = event.getChannel();
-//        if (channel == null)                    // I have no idea what you want mr user
-//        {
-//            onUnknownChannel(textChannel, arg); // Let the user know about our failure
-//            return;
-//        }
-//        connectTo(channel);                     // We found a channel to connect to!
-//        onConnecting(channel, textChannel);     // Let the user know, we were successful!
-//    }
-
-    private void onLeaveCommand(GuildMessageReceivedEvent event)
-    {
-        if (currentVoiceChannel != null)
-        {
-            disconnect();
-        }
-    }
-
-    private void onPlayCommand(GuildMessageReceivedEvent event, Guild guild, String arg)
-    {
-        try
-        {
-            final URL url = new URL(arg);
-            queueDownload(url, event);
-
-        } catch (MalformedURLException mux)
-        {
-            // not a URL, try parsing it as a search result
-            if (lastSearchResults != null && !lastSearchResults.isEmpty())
-            {
-                try
-                {
-                    int index = Integer.parseInt(arg);
-                    int size = lastSearchResults.size();
-                    if (index >= 1 && index <= size)
-                    {
-                        Result res = lastSearchResults.get(index - 1);
-                        queueDownload(res, event);
-//                        event.getChannel().sendMessage("Song removed.").queue();
-                    } else if (size > 1)
-                        event.getChannel().sendMessage("That's not a number between 1 and " + size + "!").queue();
-                    else if (size == 1)
-                        event.getChannel().sendMessage("There's only one song and that's not one of them!").queue();
-
-                    return;
-                } catch (NumberFormatException nfx)
-                {
-//                    event.getChannel().sendMessage(arg + " isn't a number!").queue();
-                }
-            }
-
-            // otherwise, try searching
-            CompletableFuture<List<Result>> search = searcher.search(Query.fullText(arg));
-            event.getChannel().sendMessage("Searching for \"" + arg + "\" ...").queue();
-            search.orTimeout(30, TimeUnit.SECONDS).whenCompleteAsync((List<Result> results, Throwable exec) ->
-            {
-                if (exec != null)
-                {
-                    event.getChannel().sendMessage("Failed to search! Reason: " + exec.getMessage()).queue();
-                    return;
-                }
-
-                lastSearchResults = results;
-
-                if (results.isEmpty())
-                {
-                    event.getChannel().sendMessage("Found nothing! :(").queue();
-                    return;
-                }
-
-                if (results.get(0).getScore() >= SEARCH_SCORE_THRESHOLD_AUTOPLAY)
-                {
-                    queueDownload(results.get(0).getLink(), event);
-                    return;
-                }
-
-                String resultString = ">>> :mag: __Search results:__\n";
-                int i = 1;
-                for (Result result : results)
-                {
-                    if (result.getScore() <= SEARCH_SCORE_THRESHOLD_DISPLAY && i > 5)
-                        break;
-                    if (i > 10)
-                        break;
-                    resultString += "**" + i + ":** "
-                            + "[" + result.getSourceAbbr() + "] "
-                            + "*" + result.getTitle() + "* "
-                            + "by " + (result.getArtist() != null ? result.getArtist().trim() : "unknown") + " "
-                            //                            + (result.getAlbum() != null ? "from the album *" + result.getAlbum().trim() + "*" : "")
-                            + "\n";
-                    i++;
-                }
-                resultString += "Type eg. `!play 1` to select";
-                event.getChannel().sendMessage(resultString).queue();
-            });
-//            event.getChannel().sendMessage("That's not a valid URL you idiot! " + ex.getMessage()).queue();
-        }
-    }
-
-    private Song queueDownload(final URL url, GuildMessageReceivedEvent event)
+    public Song queueDownload(final URL url, GuildMessageReceivedEvent event)
     {
         Song song = new Song(url);
         song.setRequestedBy(event.getAuthor().getName());
@@ -337,7 +176,7 @@ public class Main extends ListenerAdapter
         return song;
     }
 
-    private Song queueDownload(Result res, GuildMessageReceivedEvent event)
+    public Song queueDownload(Result res, GuildMessageReceivedEvent event)
     {
         Song song = queueDownload(res.getLink(), event);
         song.setArtist(res.getArtist());
@@ -346,101 +185,6 @@ public class Main extends ListenerAdapter
         return song;
     }
 
-    private void onRestartCommand(GuildMessageReceivedEvent event)
-    {
-        // TODO: this needs to clear the current data queue
-        boolean ok = musicHandler.restartSong();
-        if (ok)
-            event.getChannel().sendMessage("Restarted current song!").queue();
-        else
-            event.getChannel().sendMessage("Cannot restart!").queue();
-    }
-
-    private void onSkipCommand(GuildMessageReceivedEvent event)
-    {
-        boolean ok = musicHandler.nextSong(true);
-        if (ok)
-            event.getChannel().sendMessage("Skipped to next song!").queue();
-        else
-            event.getChannel().sendMessage("There's no more songs!").queue();
-    }
-
-    private void onQueueCommand(GuildMessageReceivedEvent event, Guild guild)
-    {
-        String message = ">>> ";
-        int i = 1;
-
-        final Song currentSong = musicHandler.getCurrentSong();
-        if (currentSong != null)
-            message += ":notes: **Now playing: " + currentSong + "**\n";
-        else
-            message += ":mute: **Not playing anything right now.**\n";
-
-        final Queue<Song> songQueue = musicHandler.getSongQueue();
-        if (!songQueue.isEmpty())
-        {
-            message += "__Ready to play:__\n";
-            for (Song song : songQueue)
-            {
-                message += ":bread: **" + (i) + ":** " + song + "\n";
-                i++;
-            }
-        }
-
-        final List<Song> downloadQueue = downloader.getDownloadQueue();
-        if (!downloadQueue.isEmpty())
-        {
-            message += "__Downloading:__\n";
-            for (Song song : downloadQueue)
-            {
-                message += ":inbox_tray: **" + (i) + ":** " + song + "\n";
-                i++;
-            }
-        }
-
-        if (downloadQueue.isEmpty() && songQueue.isEmpty())
-            message += ":mailbox_with_no_mail: The track queue is empty.";
-        // :inbox_tray:
-        event.getChannel().sendMessage(message).queue();
-    }
-
-    private void onRemoveCommand(GuildMessageReceivedEvent event, Guild guild, String arg)
-    {
-        try
-        {
-            int i = Integer.parseInt(arg);
-            boolean removed = musicHandler.removeSong(i - 1);
-            final int size = musicHandler.getSongQueue().size();
-            if (removed)
-                event.getChannel().sendMessage("Song removed.").queue();
-            else if (size > 1)
-                event.getChannel().sendMessage("That's not a number between 1 and " + size + "!").queue();
-            else if (size == 1)
-                event.getChannel().sendMessage("There's only one song to remove and that's not one of them!").queue();
-
-        } catch (NumberFormatException ex)
-        {
-            event.getChannel().sendMessage(arg + " isn't a number!").queue();
-        }
-    }
-
-    private void onHelpCommand(GuildMessageReceivedEvent event)
-    {
-        String help = "Commands available:\n"
-                + "!join <Channel>       - Joins a voice channel\n"
-                + "!leave                - Leaves the current voice channel\n"
-                + "!play <URL>           - Downloads the track at that URL and adds it to the queue.\n"
-                + "!play <Search>        - Searches for a track and displays a selection menu.\n"
-                + "!queue                - Show the songs currently playing and the current queue.\n"
-                + "!remove <Index>       - Remove the song at position <Index> from the queue.\n"
-                + "!skip                 - Skip the current song and play the next one.\n"
-                + "!restart              - Try playing the current song again in case it goes wrong.\n";
-//        for (String key: commands.keySet())
-//        {
-//            help += "!" + key + ":"
-//        }
-        event.getChannel().sendMessage(help).queue();
-    }
     /**
      * Connect to requested channel and start echo handler
      *
@@ -505,10 +249,6 @@ public class Main extends ListenerAdapter
         return currentVoiceChannel;
     }
 
-    public List<Result> getLastSearchResults()
-    {
-        return lastSearchResults;
-    }
 
     public int getTrackNumber()
     {
diff --git a/src/main/java/moe/nekojimi/chords/commands/HelpCommand.java b/src/main/java/moe/nekojimi/chords/commands/HelpCommand.java
new file mode 100644
index 0000000..f0d3e6f
--- /dev/null
+++ b/src/main/java/moe/nekojimi/chords/commands/HelpCommand.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 jimj316
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package moe.nekojimi.chords.commands;
+
+import java.util.List;
+import moe.nekojimi.chords.Main;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+
+public class HelpCommand extends Command
+{
+
+    public HelpCommand(Main bot)
+    {
+        super(bot, "help");
+    }
+
+    @Override
+    public void call(GuildMessageReceivedEvent event, List<String> arg)
+    {
+        String help = "Commands available:\n"
+                + "!join <Channel>       - Joins a voice channel\n"
+                + "!leave                - Leaves the current voice channel\n"
+                + "!play <URL>           - Downloads the track at that URL and adds it to the queue.\n"
+                + "!play <Search>        - Searches for a track and displays a selection menu.\n"
+                + "!queue                - Show the songs currently playing and the current queue.\n"
+                + "!remove <Index>       - Remove the song at position <Index> from the queue.\n"
+                + "!skip                 - Skip the current song and play the next one.\n"
+                + "!restart              - Try playing the current song again in case it goes wrong.\n";
+//        for (String key: commands.keySet())
+//        {
+//            help += "!" + key + ":"
+//        }
+        event.getChannel().sendMessage(help).queue();
+    }
+
+}
diff --git a/src/main/java/moe/nekojimi/chords/commands/PlayCommand.java b/src/main/java/moe/nekojimi/chords/commands/PlayCommand.java
new file mode 100644
index 0000000..1ff4031
--- /dev/null
+++ b/src/main/java/moe/nekojimi/chords/commands/PlayCommand.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 jimj316
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package moe.nekojimi.chords.commands;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import moe.nekojimi.chords.Main;
+import moe.nekojimi.musicsearcher.Query;
+import moe.nekojimi.musicsearcher.Result;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+
+/**
+ *
+ * @author jimj316
+ */
+public class PlayCommand extends Command
+{
+    private static final double SEARCH_SCORE_THRESHOLD_DISPLAY = 0.6;
+    private static final double SEARCH_SCORE_THRESHOLD_AUTOPLAY = 9999; // disable autoplay it sucks
+
+    private List<Result> lastSearchResults;
+
+    public PlayCommand(Main main)
+    {
+        super(main, "play");
+    }
+
+    @Override
+    public void call(GuildMessageReceivedEvent event, List<String> arg)
+    {
+        try
+        {
+            final URL url = new URL(arg.get(0));
+            bot.queueDownload(url, event);
+
+        } catch (MalformedURLException mux)
+        {
+            // not a URL, try parsing it as a search result
+            if (lastSearchResults != null && !lastSearchResults.isEmpty())
+            {
+                try
+                {
+                    int index = Integer.parseInt(arg.get(0));
+                    int size = lastSearchResults.size();
+                    if (index >= 1 && index <= size)
+                    {
+                        Result res = lastSearchResults.get(index - 1);
+                        bot.queueDownload(res, event);
+//                        event.getChannel().sendMessage("Song removed.").queue();
+                    } else if (size > 1)
+                        event.getChannel().sendMessage("That's not a number between 1 and " + size + "!").queue();
+                    else if (size == 1)
+                        event.getChannel().sendMessage("There's only one song and that's not one of them!").queue();
+
+                    return;
+                } catch (NumberFormatException nfx)
+                {
+//                    event.getChannel().sendMessage(arg + " isn't a number!").queue();
+                }
+            }
+
+            // otherwise, try searching
+            CompletableFuture<List<Result>> search = bot.getSearcher().search(Query.fullText(arg.stream().reduce((t, u) -> t + " " + u).get()));
+            event.getChannel().sendMessage("Searching for \"" + arg + "\" ...").queue();
+            search.orTimeout(30, TimeUnit.SECONDS).whenCompleteAsync((List<Result> results, Throwable exec) ->
+            {
+                if (exec != null)
+                {
+                    event.getChannel().sendMessage("Failed to search! Reason: " + exec.getMessage()).queue();
+                    return;
+                }
+
+                lastSearchResults = results;
+
+                if (results.isEmpty())
+                {
+                    event.getChannel().sendMessage("Found nothing! :(").queue();
+                    return;
+                }
+
+                if (results.get(0).getScore() >= SEARCH_SCORE_THRESHOLD_AUTOPLAY)
+                {
+                    bot.queueDownload(results.get(0).getLink(), event);
+                    return;
+                }
+
+                String resultString = ">>> :mag: __Search results:__\n";
+                int i = 1;
+                for (Result result : results)
+                {
+                    if (result.getScore() <= SEARCH_SCORE_THRESHOLD_DISPLAY && i > 5)
+                        break;
+                    if (i > 10)
+                        break;
+                    resultString += "**" + i + ":** "
+                            + "[" + result.getSourceAbbr() + "] "
+                            + "*" + result.getTitle() + "* "
+                            + "by " + (result.getArtist() != null ? result.getArtist().trim() : "unknown") + " "
+                            //                            + (result.getAlbum() != null ? "from the album *" + result.getAlbum().trim() + "*" : "")
+                            + "\n";
+                    i++;
+                }
+                resultString += "Type eg. `!play 1` to select";
+                event.getChannel().sendMessage(resultString).queue();
+            });
+//            event.getChannel().sendMessage("That's not a valid URL you idiot! " + ex.getMessage()).queue();
+        }
+    }
+
+}
diff --git a/src/main/java/moe/nekojimi/chords/commands/QueueCommand.java b/src/main/java/moe/nekojimi/chords/commands/QueueCommand.java
new file mode 100644
index 0000000..a2811e5
--- /dev/null
+++ b/src/main/java/moe/nekojimi/chords/commands/QueueCommand.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 jimj316
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package moe.nekojimi.chords.commands;
+
+import java.util.List;
+import java.util.Queue;
+import moe.nekojimi.chords.Main;
+import moe.nekojimi.chords.Song;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+
+public class QueueCommand extends Command
+{
+
+    public QueueCommand(Main bot)
+    {
+        super(bot, "queue");
+    }
+
+    @Override
+    public void call(GuildMessageReceivedEvent event, List<String> arg)
+    {
+        String message = ">>> ";
+        int i = 1;
+
+        final Song currentSong = bot.getMusicHandler().getCurrentSong();
+        if (currentSong != null)
+            message += ":notes: **Now playing: " + currentSong + "**\n";
+        else
+            message += ":mute: **Not playing anything right now.**\n";
+
+        final Queue<Song> songQueue = bot.getMusicHandler().getSongQueue();
+        if (!songQueue.isEmpty())
+        {
+            message += "__Ready to play:__\n";
+            for (Song song : songQueue)
+            {
+                message += ":bread: **" + (i) + ":** " + song + "\n";
+                i++;
+            }
+        }
+
+        final List<Song> downloadQueue = bot.getDownloader().getDownloadQueue();
+        if (!downloadQueue.isEmpty())
+        {
+            message += "__Downloading:__\n";
+            for (Song song : downloadQueue)
+            {
+                message += ":inbox_tray: **" + (i) + ":** " + song + "\n";
+                i++;
+            }
+        }
+
+        if (downloadQueue.isEmpty() && songQueue.isEmpty())
+            message += ":mailbox_with_no_mail: The track queue is empty.";
+        // :inbox_tray:
+        event.getChannel().sendMessage(message).queue();
+    }
+
+}
diff --git a/src/main/java/moe/nekojimi/chords/commands/RemoveCommand.java b/src/main/java/moe/nekojimi/chords/commands/RemoveCommand.java
new file mode 100644
index 0000000..40679a0
--- /dev/null
+++ b/src/main/java/moe/nekojimi/chords/commands/RemoveCommand.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 jimj316
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package moe.nekojimi.chords.commands;
+
+import java.util.List;
+import moe.nekojimi.chords.Main;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+
+public class RemoveCommand extends Command
+{
+
+    public RemoveCommand(Main bot)
+    {
+        super(bot, "remove");
+    }
+
+    @Override
+    public void call(GuildMessageReceivedEvent event, List<String> arg)
+    {
+        try
+        {
+            int i = Integer.parseInt(arg.get(0));
+            boolean removed = bot.getMusicHandler().removeSong(i - 1);
+            final int size = bot.getMusicHandler().getSongQueue().size();
+            if (removed)
+                event.getChannel().sendMessage("Song removed.").queue();
+            else if (size > 1)
+                event.getChannel().sendMessage("That's not a number between 1 and " + size + "!").queue();
+            else if (size == 1)
+                event.getChannel().sendMessage("There's only one song to remove and that's not one of them!").queue();
+
+        } catch (NumberFormatException ex)
+        {
+            event.getChannel().sendMessage(arg + " isn't a number!").queue();
+        }
+    }
+
+}
diff --git a/src/main/java/moe/nekojimi/chords/commands/RestartCommand.java b/src/main/java/moe/nekojimi/chords/commands/RestartCommand.java
new file mode 100644
index 0000000..5cdac33
--- /dev/null
+++ b/src/main/java/moe/nekojimi/chords/commands/RestartCommand.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 jimj316
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package moe.nekojimi.chords.commands;
+
+import java.util.List;
+import moe.nekojimi.chords.Main;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+
+public class RestartCommand extends Command
+{
+
+    public RestartCommand(Main bot)
+    {
+        super(bot, "restart");
+    }
+
+    @Override
+    public void call(GuildMessageReceivedEvent event, List<String> arg)
+    {
+        // TODO: this needs to clear the current data queue
+        boolean ok = bot.getMusicHandler().restartSong();
+        if (ok)
+            event.getChannel().sendMessage("Restarted current song!").queue();
+        else
+            event.getChannel().sendMessage("Cannot restart!").queue();
+    }
+
+}
diff --git a/src/main/java/moe/nekojimi/chords/commands/SkipCommand.java b/src/main/java/moe/nekojimi/chords/commands/SkipCommand.java
new file mode 100644
index 0000000..6cfe5fc
--- /dev/null
+++ b/src/main/java/moe/nekojimi/chords/commands/SkipCommand.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 jimj316
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package moe.nekojimi.chords.commands;
+
+import java.util.List;
+import moe.nekojimi.chords.Main;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+
+public class SkipCommand extends Command
+{
+
+    public SkipCommand(Main bot)
+    {
+        super(bot, "skip");
+    }
+
+    @Override
+    public void call(GuildMessageReceivedEvent event, List<String> arg)
+    {
+        boolean ok = bot.getMusicHandler().nextSong(true);
+        if (ok)
+            event.getChannel().sendMessage("Skipped to next song!").queue();
+        else
+            event.getChannel().sendMessage("There's no more songs!").queue();
+    }
+
+}