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.YamlMapping;
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.neovisionaries.ws.client.WebSocketFactory;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.*;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
@ -20,6 +22,8 @@ import java.util.function.BiConsumer;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import moe.nekojimi.chords.commands.*;
|
||||
import moe.nekojimi.musicsearcher.providers.MetaSearcher;
|
||||
|
@ -42,6 +46,8 @@ import net.dv8tion.jda.api.utils.cache.CacheFlag;
|
|||
public final class Main extends ListenerAdapter
|
||||
{
|
||||
|
||||
private static final CommandOptions options = new CommandOptions();
|
||||
|
||||
private final File dataDirectory;
|
||||
private final File playlistsDirectory;
|
||||
|
||||
|
@ -66,6 +72,13 @@ public final class Main extends ListenerAdapter
|
|||
*/
|
||||
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:
|
||||
EnumSet<GatewayIntent> intents = EnumSet.of(
|
||||
// We need messages in guilds to accept commands from users
|
||||
|
@ -91,49 +104,21 @@ public final class Main extends ListenerAdapter
|
|||
builder.addEventListeners(listener);
|
||||
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();
|
||||
listener.setJda(jda);
|
||||
}
|
||||
private final Consumer<Song> nowPlayingConsumer = (Song song) ->
|
||||
{
|
||||
if (song != null)
|
||||
jda.getPresence().setActivity(Activity.of(Activity.ActivityType.LISTENING, song.toString()));
|
||||
else
|
||||
jda.getPresence().setActivity(null);
|
||||
};
|
||||
private final Consumer<Song> nowPlayingConsumer = new NowPlayingConsumer();
|
||||
|
||||
private final BiConsumer<SongRequest, Exception> downloaderMessageHandler = (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 final BiConsumer<SongRequest, Exception> downloaderMessageHandler = new DownloaderMessageHandler();
|
||||
|
||||
public Main() throws IOException
|
||||
{
|
||||
|
@ -171,7 +156,6 @@ public final class Main extends ListenerAdapter
|
|||
log("INFO", "Started OK!");
|
||||
}
|
||||
|
||||
|
||||
private void addCommand(Command command)
|
||||
{
|
||||
commands.put(command.getKeyword(), command);
|
||||
|
@ -407,6 +391,7 @@ public final class Main extends ListenerAdapter
|
|||
{
|
||||
return currentVoiceChannel;
|
||||
}
|
||||
|
||||
public QueueManager getQueueManager()
|
||||
{
|
||||
return queueManager;
|
||||
|
@ -417,5 +402,62 @@ public final class Main extends ListenerAdapter
|
|||
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