Add command line option to bind to a local socket. Also, add command line parsing.
This commit is contained in:
parent
b0415b0324
commit
824d08caad
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jimj316
|
||||||
|
*/
|
||||||
|
public class CommandOptions
|
||||||
|
{
|
||||||
|
|
||||||
|
@Parameter(description = "The API token for Discord.", required = true)
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
@Parameter(names = "-local-addr", description = "The local address to bind to.")
|
||||||
|
private String localAddress;
|
||||||
|
|
||||||
|
public String getToken()
|
||||||
|
{
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocalAddress()
|
||||||
|
{
|
||||||
|
return localAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jimj316
|
||||||
|
*/
|
||||||
|
public class LocalBindSocketFactory extends SocketFactory
|
||||||
|
{
|
||||||
|
|
||||||
|
private InetAddress localAddress;
|
||||||
|
|
||||||
|
public InetAddress getLocalAddress()
|
||||||
|
{
|
||||||
|
return localAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocalAddress(InetAddress localAddress)
|
||||||
|
{
|
||||||
|
this.localAddress = localAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String remoteAddr, int remotePort) throws IOException, UnknownHostException
|
||||||
|
{
|
||||||
|
return createSocket(remoteAddr, remotePort, localAddress, findFreePort(localAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String remoteAddr, int remotePort, InetAddress x, int localPort) throws IOException, UnknownHostException
|
||||||
|
{
|
||||||
|
return createSocket(InetAddress.getByName(remoteAddr), remotePort, localAddress, localPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(InetAddress remoteAddr, int remotePort) throws IOException
|
||||||
|
{
|
||||||
|
return createSocket(remoteAddr, remotePort, localAddress, findFreePort(localAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(InetAddress remoteAddr, int remotePort, InetAddress x, int localPort) throws IOException
|
||||||
|
{
|
||||||
|
return new Socket(remoteAddr, remotePort, localAddress, localPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findFreePort(InetAddress localAddr)
|
||||||
|
{
|
||||||
|
int port = 0;
|
||||||
|
// For ServerSocket port number 0 means that the port number is automatically allocated.
|
||||||
|
try (ServerSocket socket = new ServerSocket(0, 0, localAddr))
|
||||||
|
{
|
||||||
|
// Disable timeout and reuse address after closing the socket.
|
||||||
|
socket.setReuseAddress(true);
|
||||||
|
port = socket.getLocalPort();
|
||||||
|
} catch (IOException ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
if (port > 0)
|
||||||
|
{
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Could not find a free port");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,11 +7,13 @@ package moe.nekojimi.chords;
|
||||||
|
|
||||||
import com.amihaiemil.eoyaml.Yaml;
|
import com.amihaiemil.eoyaml.Yaml;
|
||||||
import com.amihaiemil.eoyaml.YamlMapping;
|
import com.amihaiemil.eoyaml.YamlMapping;
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.neovisionaries.ws.client.WebSocketFactory;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.*;
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
@ -20,6 +22,8 @@ import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
import javax.security.auth.login.LoginException;
|
import javax.security.auth.login.LoginException;
|
||||||
import moe.nekojimi.chords.commands.*;
|
import moe.nekojimi.chords.commands.*;
|
||||||
import moe.nekojimi.musicsearcher.providers.MetaSearcher;
|
import moe.nekojimi.musicsearcher.providers.MetaSearcher;
|
||||||
|
@ -42,6 +46,8 @@ import net.dv8tion.jda.api.utils.cache.CacheFlag;
|
||||||
public final class Main extends ListenerAdapter
|
public final class Main extends ListenerAdapter
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private static final CommandOptions options = new CommandOptions();
|
||||||
|
|
||||||
private final File dataDirectory;
|
private final File dataDirectory;
|
||||||
private final File playlistsDirectory;
|
private final File playlistsDirectory;
|
||||||
|
|
||||||
|
@ -66,6 +72,13 @@ public final class Main extends ListenerAdapter
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) throws LoginException, IOException
|
public static void main(String[] args) throws LoginException, IOException
|
||||||
{
|
{
|
||||||
|
|
||||||
|
JCommander commander = JCommander.newBuilder()
|
||||||
|
.args(args)
|
||||||
|
.acceptUnknownOptions(true)
|
||||||
|
.addObject(options)
|
||||||
|
.build();
|
||||||
|
|
||||||
// We only need 2 gateway intents enabled for this example:
|
// We only need 2 gateway intents enabled for this example:
|
||||||
EnumSet<GatewayIntent> intents = EnumSet.of(
|
EnumSet<GatewayIntent> intents = EnumSet.of(
|
||||||
// We need messages in guilds to accept commands from users
|
// We need messages in guilds to accept commands from users
|
||||||
|
@ -91,49 +104,21 @@ public final class Main extends ListenerAdapter
|
||||||
builder.addEventListeners(listener);
|
builder.addEventListeners(listener);
|
||||||
builder.setAutoReconnect(true);
|
builder.setAutoReconnect(true);
|
||||||
|
|
||||||
|
if (options.getLocalAddress() != null)
|
||||||
|
{
|
||||||
|
final WebSocketFactory webSocketFactory = new WebSocketFactory();
|
||||||
|
final LocalBindSocketFactory localBindSocketFactory = new LocalBindSocketFactory();
|
||||||
|
localBindSocketFactory.setLocalAddress(InetAddress.getByName(options.getLocalAddress()));
|
||||||
|
webSocketFactory.setSocketFactory(localBindSocketFactory);
|
||||||
|
builder.setWebsocketFactory(webSocketFactory);
|
||||||
|
}
|
||||||
|
|
||||||
JDA jda = builder.build();
|
JDA jda = builder.build();
|
||||||
listener.setJda(jda);
|
listener.setJda(jda);
|
||||||
}
|
}
|
||||||
private final Consumer<Song> nowPlayingConsumer = (Song song) ->
|
private final Consumer<Song> nowPlayingConsumer = new NowPlayingConsumer();
|
||||||
{
|
|
||||||
if (song != null)
|
|
||||||
jda.getPresence().setActivity(Activity.of(Activity.ActivityType.LISTENING, song.toString()));
|
|
||||||
else
|
|
||||||
jda.getPresence().setActivity(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
private final BiConsumer<SongRequest, Exception> downloaderMessageHandler = (SongRequest request, Exception ex) ->
|
private final BiConsumer<SongRequest, Exception> downloaderMessageHandler = new DownloaderMessageHandler();
|
||||||
{
|
|
||||||
Song song = request.getSong();
|
|
||||||
// TextChannel channel = jda.getTextChannelById(song.getRequestedIn());
|
|
||||||
// String bracketNo = "[" + song.getNumber() + "] ";
|
|
||||||
if (ex == null)
|
|
||||||
if (song.getLocation() != null)
|
|
||||||
{
|
|
||||||
request.getInvocation().respond("Finished downloading " + song + ", added to queue!");
|
|
||||||
log("DOWN", "Downloaded " + song);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
Format format = song.getBestFormat();
|
|
||||||
String formatDetails = "";
|
|
||||||
if (format != null)
|
|
||||||
{
|
|
||||||
final int bitrate = format.getSampleRate() / 1000;
|
|
||||||
final long size = format.getSize();
|
|
||||||
String sizeFmt = (size <= 0 ? "?.??" : String.format("%.2f", size / (1024.0 * 1024.0))) + "MiB";
|
|
||||||
String bitFmt = (bitrate <= 0 ? "??" : bitrate) + "k";
|
|
||||||
formatDetails = " (" + bitFmt + ", " + sizeFmt + ")";
|
|
||||||
}
|
|
||||||
request.getInvocation().respond("Now downloading " + song + formatDetails + " ...");
|
|
||||||
log("DOWN", "Downloading " + song + "...");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
request.getInvocation().respond("Failed to download " + song + "! Reason: " + ex.getMessage());
|
|
||||||
log("DOWN", "Failed to download " + song + "! Reason: " + ex.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
public Main() throws IOException
|
public Main() throws IOException
|
||||||
{
|
{
|
||||||
|
@ -171,7 +156,6 @@ public final class Main extends ListenerAdapter
|
||||||
log("INFO", "Started OK!");
|
log("INFO", "Started OK!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void addCommand(Command command)
|
private void addCommand(Command command)
|
||||||
{
|
{
|
||||||
commands.put(command.getKeyword(), command);
|
commands.put(command.getKeyword(), command);
|
||||||
|
@ -407,6 +391,7 @@ public final class Main extends ListenerAdapter
|
||||||
{
|
{
|
||||||
return currentVoiceChannel;
|
return currentVoiceChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueueManager getQueueManager()
|
public QueueManager getQueueManager()
|
||||||
{
|
{
|
||||||
return queueManager;
|
return queueManager;
|
||||||
|
@ -417,5 +402,62 @@ public final class Main extends ListenerAdapter
|
||||||
return trackNumber;
|
return trackNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DownloaderMessageHandler implements BiConsumer<SongRequest, Exception>
|
||||||
|
{
|
||||||
|
|
||||||
|
public DownloaderMessageHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(SongRequest request, Exception ex)
|
||||||
|
{
|
||||||
|
Song song = request.getSong();
|
||||||
|
// TextChannel channel = jda.getTextChannelById(song.getRequestedIn());
|
||||||
|
// String bracketNo = "[" + song.getNumber() + "] ";
|
||||||
|
if (ex == null)
|
||||||
|
if (song.getLocation() != null)
|
||||||
|
{
|
||||||
|
request.getInvocation().respond("Finished downloading " + song + ", added to queue!");
|
||||||
|
log("DOWN", "Downloaded " + song);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
Format format = song.getBestFormat();
|
||||||
|
String formatDetails = "";
|
||||||
|
if (format != null)
|
||||||
|
{
|
||||||
|
final int bitrate = format.getSampleRate() / 1000;
|
||||||
|
final long size = format.getSize();
|
||||||
|
String sizeFmt = (size <= 0 ? "?.??" : String.format("%.2f", size / (1024.0 * 1024.0))) + "MiB";
|
||||||
|
String bitFmt = (bitrate <= 0 ? "??" : bitrate) + "k";
|
||||||
|
formatDetails = " (" + bitFmt + ", " + sizeFmt + ")";
|
||||||
|
}
|
||||||
|
request.getInvocation().respond("Now downloading " + song + formatDetails + " ...");
|
||||||
|
log("DOWN", "Downloading " + song + "...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.getInvocation().respond("Failed to download " + song + "! Reason: " + ex.getMessage());
|
||||||
|
log("DOWN", "Failed to download " + song + "! Reason: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NowPlayingConsumer implements Consumer<Song>
|
||||||
|
{
|
||||||
|
|
||||||
|
public NowPlayingConsumer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Song song)
|
||||||
|
{
|
||||||
|
if (song != null)
|
||||||
|
jda.getPresence().setActivity(Activity.of(Activity.ActivityType.LISTENING, song.toString()));
|
||||||
|
else
|
||||||
|
jda.getPresence().setActivity(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue