Add command line option to bind to a local socket. Also, add command line parsing.

jda-v5
Nekojimi 2 years ago
parent b0415b0324
commit 824d08caad
  1. 44
      src/main/java/moe/nekojimi/chords/CommandOptions.java
  2. 88
      src/main/java/moe/nekojimi/chords/LocalBindSocketFactory.java
  3. 126
      src/main/java/moe/nekojimi/chords/Main.java

@ -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…
Cancel
Save