Compare commits

...
42 Commits
Author SHA1 Message Date
Michaili Kandunknown 49c3ec7960 Switch to lavalink's lavaplayer fork (#1490)
* Switch to lavalink's lavaplayer fork

* Create logback turbofilter to suppress YouTube auth tokens warning

* added license header

---------

Co-authored-by: unknown <john.a.grosh@gmail.com>
2024-03-04 19:38:04 -05:00
John Grosh 6752695b43 rename github action file and update readme (#1494) 2024-03-04 19:35:32 -05:00
John Grosh bb17c984a2 Codefactor Fixes (#1493)
* codefactor fixes

* anotha one
2024-03-04 19:35:17 -05:00
Michaili K a7807b96e2 Always self-deafen (#1491) 2024-03-04 19:08:27 -05:00
2e9dd5df49 add LinearQueue (#1194)
* add LinearQueue

* rework queueType to be server-specific

* rework QueueType enum and command

* add QueueType supplier

* fix queue type formatting

* add QueueSupplier util

* fix code issues

* Fix unit tests

* add suggested changes on PR

---------

Co-authored-by: Michaili K <mysteriouscursor+git@protonmail.com>
Co-authored-by: Michail <git@michaili.dev>
2024-03-04 18:49:01 -05:00
Maoyue 貓月andunknown 1ad16181d0 Support new Discord username (#1397)
* Support new username

* Support new username, again

* Make a separate username formatting function

* Add some missing usage

* switch away from using static imports, add shortcuts for jda User and metadata UserInfo, add null check

---------

Co-authored-by: unknown <john.a.grosh@gmail.com>
2024-03-02 19:39:16 -05:00
John Grosh bb53bad1ff Update test.yml 2024-03-01 20:11:30 -05:00
Kevin S 8a1066cc87 Allow configurable default skip ratio (#1140)
* Allow configurable default skip ratio

Revert "Allow configurable default skip ratio"

This reverts commit 10e38b4132ee2b28fb583603d69e6228da211ecd.

* FIX: Use -1 to denote a guild has not set a skip ratio, and opted to use the bot default.
2024-03-01 19:42:39 -05:00
Michaili Kandunknown 23c2dfa78e Make JMusicBot error & shut down if ran in an unsupported manner (#1486)
* Make JMusicBot error & shut down if ran in an unsupported manner

* add info about message content intent

* five second rule

---------

Co-authored-by: unknown <john.a.grosh@gmail.com>
2024-03-01 19:26:58 -05:00
Michaili Kandunknown 18bf6ac0f6 Remove channel topic info (#1485)
* Remove channel topic info

As Discord has severely increased the ratelimits for updating the channel topic, updating the channel topic has become not viable for bots.

* fix compilation error

---------

Co-authored-by: unknown <john.a.grosh@gmail.com>
2024-03-01 19:19:18 -05:00
John Grosh fd6de60fc7 Merge pull request #1489 from jagrosh/add-more-sources
Add more sources and check more nulls
2024-03-01 19:00:50 -05:00
unknown 4c8ff655d8 update github actions to jdk 11 2024-03-01 18:26:10 -05:00
John Grosh ac8661c904 Merge pull request #1487 from MichailiK/fix/server-settings-logging
Don't warn about failing to load server settings when the file isn't found
2024-03-01 18:24:09 -05:00
unknown 4f926baf76 update circleci settings 2024-03-01 18:08:46 -05:00
unknown c5ecf89852 switch to java 11 2024-03-01 18:04:26 -05:00
unknown a52103620a update version 2024-03-01 18:00:02 -05:00
unknown fa5e33e2a5 fix formatting and nulls in metadata 2024-03-01 17:53:08 -05:00
unknown 781a94ea91 Merge branch 'master' of https://github.com/jagrosh/MusicBot 2024-03-01 17:50:39 -05:00
Michail ff1ede9d12 Log "loaded config" message again after successfully initializing the GUI 2024-02-28 21:33:45 +01:00
Michail 90d9a1631a Create a new serversettings.json file if one doesn't exist 2024-02-28 21:33:10 +01:00
John Grosh c4502bbe16 Merge pull request #1460 from jagrosh/dependabot/maven/ch.qos.logback-logback-classic-1.2.13
Bump ch.qos.logback:logback-classic from 1.2.3 to 1.2.13
2024-02-28 15:13:45 -05:00
Michail 2d4f2671c9 Don't warn about failing to load server settings when the file isn't found 2024-02-28 21:01:08 +01:00
John Grosh e595de2033 Merge pull request #1479 from MichailiK/fix/eval-shenanigans
Fix eval command failing in DMs
2024-02-10 17:28:33 -05:00
Michail 53875f2791 Properly fix eval command failing in DMs 2024-02-10 23:25:11 +01:00
Michail cd7e643228 Possible workaround for Eval command failing 2024-02-10 23:10:07 +01:00
dependabot[bot] 0e1f0d3bfa Bump ch.qos.logback:logback-classic from 1.2.3 to 1.2.13
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.2.3 to 1.2.13.
- [Commits](https://github.com/qos-ch/logback/compare/v_1.2.3...v_1.2.13)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-05 22:17:47 +00:00
John Grosh 2c76164f6d Merge pull request #1304 from LachlanPond/master
Catch JDA exception when there is no internet connection
2023-03-06 16:31:28 -05:00
Lachlan Pond 788cd40f2c Added import for ErrorResponseException 2023-03-04 13:59:42 +10:00
Michaili K 1e12e4e01d Bump JDA to 4.4.1_353 2023-03-01 20:52:53 +01:00
Lachlan Pond c4cbfe2a11 Catch JDA exception when there is no internet connection 2023-02-27 21:52:22 +10:00
John Grosh bbed9e8990 Merge pull request #1294 from lrbn86/master
Sanitize the playlist name
2023-02-14 12:22:51 -05:00
lrbn86 354a0b3def Sanitize the playlist name 2023-02-13 18:31:14 -05:00
John Grosh 2faef67b30 Merge branch 'master' of https://github.com/jagrosh/MusicBot 2022-09-02 21:56:23 -04:00
John Grosh aee076c854 add more sources 2022-09-02 21:56:19 -04:00
John Grosh b76f6fa07c Merge pull request #1141 from t3rminus/fix-warnings-deprecations
Fix warnings and deprecations
2022-09-02 21:55:14 -04:00
John Grosh ec4e9e624c Merge pull request #1209 from jagrosh/dependabot/maven/org.jsoup-jsoup-1.15.3
Bump jsoup from 1.14.2 to 1.15.3
2022-09-02 21:53:47 -04:00
dependabot[bot] a7be2c4602 Bump jsoup from 1.14.2 to 1.15.3
Bumps [jsoup](https://github.com/jhy/jsoup) from 1.14.2 to 1.15.3.
- [Release notes](https://github.com/jhy/jsoup/releases)
- [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES)
- [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.14.2...jsoup-1.15.3)

---
updated-dependencies:
- dependency-name: org.jsoup:jsoup
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-01 23:43:19 +00:00
John Grosh 6007d3e99b Merge pull request #1192 from yo-louk/queue-links
Queue entry formatting fix
2022-07-07 16:00:25 -04:00
yo-louk a17b2f20ee check uri type before formatting queue entry 2022-07-07 21:26:18 +02:00
John Grosh 4375d4caa8 Merge pull request #1155 from MichailiK/pr/ci/simplify-release
Update generated description in release workflow
2022-04-09 15:01:04 -04:00
Michaili K c22807bcbb Update make-release.yml 2022-04-09 18:25:01 +02:00
Kevin S 8457f944cd Fix warnings and deprecations 2022-03-31 18:30:19 -07:00
37 changed files with 707 additions and 299 deletions

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@ jobs:
build:
docker:
# specify the version you desire here
- image: circleci/openjdk:8u171-jdk
- image: cimg/openjdk:11.0
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
@@ -1,19 +1,22 @@
name: Test
name: Build and Test
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 8
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '8'
java-version: '11'
distribution: 'adopt'
cache: maven
- name: Build with Maven
- name: Build and Test with Maven
run: mvn --batch-mode --update-snapshots verify
+3 -3
View File
@@ -18,10 +18,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up JDK 8
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '8'
java-version: '11'
distribution: 'adopt'
cache: maven
- name: Set Version
@@ -67,4 +67,4 @@ jobs:
### Setup
https://jmusicbot.com/setup
https://jmusicbot.com/config
# Download *only* the `.jar` file below!
# Download: [JMusicBot-${{ github.event.inputs.version_number }}.jar](https://github.com/jagrosh/MusicBot/releases/download/${{ github.event.inputs.version_number }}/JMusicBot-${{ github.event.inputs.version_number }}.jar)
+2 -2
View File
@@ -7,8 +7,8 @@
[![Release](https://img.shields.io/github/release/jagrosh/MusicBot.svg)](https://github.com/jagrosh/MusicBot/releases/latest)
[![License](https://img.shields.io/github/license/jagrosh/MusicBot.svg)](https://github.com/jagrosh/MusicBot/blob/master/LICENSE)
[![Discord](https://discordapp.com/api/guilds/147698382092238848/widget.png)](https://discord.gg/0p9LSGoRLu6Pet0k)<br>
[![CircleCI](https://img.shields.io/circleci/project/github/jagrosh/MusicBot/master.svg)](https://circleci.com/gh/jagrosh/MusicBot)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/gdu6nyte5psj6xfk/branch/master?svg=true)](https://ci.appveyor.com/project/jagrosh/musicbot/branch/master)
[![CircleCI](https://dl.circleci.com/status-badge/img/gh/jagrosh/MusicBot/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/jagrosh/MusicBot/tree/master)
[![Build and Test](https://github.com/jagrosh/MusicBot/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/jagrosh/MusicBot/actions/workflows/build-and-test.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/jagrosh/musicbot/badge)](https://www.codefactor.io/repository/github/jagrosh/musicbot)
A cross-platform Discord music bot with a clean interface, and that is easy to set up and run yourself!
+33 -30
View File
@@ -5,7 +5,7 @@
<artifactId>JMusicBot</artifactId>
<version>Snapshot</version>
<packaging>jar</packaging>
<repositories>
<repository>
<id>dv8tion</id>
@@ -25,14 +25,24 @@
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
<repository>
<id>m2.duncte123.dev</id>
<name>m2-duncte123</name>
<url>https://m2.duncte123.dev/releases</url>
</repository>
<repository>
<id>arbjergDev-snapshots</id>
<name>Lavalink Repository</name>
<url>https://maven.lavalink.dev/snapshots</url>
</repository>
</repositories>
<dependencies>
<!-- Discord Dependencies -->
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>4.4.0_352</version>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>4.4.1_353</version>
</dependency>
<dependency>
<groupId>com.jagrosh</groupId>
@@ -40,36 +50,29 @@
<version>3.0.5</version>
<type>pom</type>
</dependency>
<!-- Music Dependencies -->
<!-- using a fork of this to fix some issues faster -->
<!-- dependency>
<groupId>com.sedmelluq</groupId>
<artifactId>lavaplayer</artifactId>
<version>1.3.78</version>
</dependency -->
<dependency>
<groupId>com.github.jagrosh</groupId>
<artifactId>lavaplayer</artifactId>
<version>jmusicbot-SNAPSHOT</version>
</dependency>
<!-- this is needed, but isn't actually hosted anywhere anymore... uh -->
<!--dependency>
<groupId>com.sedmelluq</groupId>
<artifactId>lavaplayer-natives-extra</artifactId>
<version>1.3.13</version>
</dependency-->
<groupId>dev.arbjerg</groupId>
<artifactId>lavaplayer</artifactId>
<version>727959e9f621fc457b3a5adafcfffb55fdeaa538-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.jagrosh</groupId>
<artifactId>JLyrics</artifactId>
<version>master-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.dunctebot</groupId>
<artifactId>sourcemanagers</artifactId>
<version>1.9.0</version>
</dependency>
<!-- Misc Internal Dependencies -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>com.typesafe</groupId>
@@ -79,9 +82,9 @@
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.2</version>
<version>1.15.3</version>
</dependency>
<!-- Testing Dependencies -->
<dependency>
<groupId>junit</groupId>
@@ -96,7 +99,7 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
@@ -138,10 +141,10 @@
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
+1 -1
View File
@@ -11,7 +11,7 @@ LOOP=true
download() {
if [ $DOWNLOAD == true ]; then
URL=$(curl -s https://api.github.com/repos/jagrosh/MusicBot/releases/latest \
| grep -i browser_download_url.*\.jar \
| grep -i "browser_download_url.*\.jar" \
| sed 's/.*\(http.*\)"/\1/')
FILENAME=$(echo $URL | sed 's/.*\/\([^\/]*\)/\1/')
if [ -f $FILENAME ]; then
@@ -139,7 +139,6 @@ public class Bot
{
ah.stopAndClear();
ah.getPlayer().destroy();
nowplaying.updateTopic(g.getIdLong(), ah, true);
}
});
jda.shutdown();
@@ -43,6 +43,7 @@ public class BotConfig
successEmoji, warningEmoji, errorEmoji, loadingEmoji, searchingEmoji;
private boolean stayInChannel, songInGame, npImages, updatealerts, useEval, dbots;
private long owner, maxSeconds, aloneTimeUntilStop;
private double skipratio;
private OnlineStatus status;
private Activity game;
private Config aliases, transforms;
@@ -91,6 +92,7 @@ public class BotConfig
playlistsFolder = config.getString("playlistsfolder");
aliases = config.getConfig("aliases");
transforms = config.getConfig("transforms");
skipratio = config.getDouble("skipratio");
dbots = owner == 113156185389092864L;
// we may need to write a new config file
@@ -231,6 +233,11 @@ public class BotConfig
return token;
}
public double getSkipRatio()
{
return skipratio;
}
public long getOwnerId()
{
return owner;
@@ -266,6 +273,11 @@ public class BotConfig
return game;
}
public boolean isGameNone()
{
return game != null && game.getName().equalsIgnoreCase("none");
}
public OnlineStatus getStatus()
{
return status;
@@ -15,6 +15,7 @@
*/
package com.jagrosh.jmusicbot;
import com.jagrosh.jdautilities.command.CommandClient;
import com.jagrosh.jdautilities.command.CommandClientBuilder;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import com.jagrosh.jdautilities.examples.command.*;
@@ -34,6 +35,7 @@ import net.dv8tion.jda.api.*;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.utils.cache.CacheFlag;
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,7 +48,7 @@ public class JMusicBot
public final static Logger LOG = LoggerFactory.getLogger(JMusicBot.class);
public final static Permission[] RECOMMENDED_PERMS = {Permission.MESSAGE_READ, Permission.MESSAGE_WRITE, Permission.MESSAGE_HISTORY, Permission.MESSAGE_ADD_REACTION,
Permission.MESSAGE_EMBED_LINKS, Permission.MESSAGE_ATTACH_FILES, Permission.MESSAGE_MANAGE, Permission.MESSAGE_EXT_EMOJI,
Permission.MANAGE_CHANNEL, Permission.VOICE_CONNECT, Permission.VOICE_SPEAK, Permission.NICKNAME_CHANGE};
Permission.VOICE_CONNECT, Permission.VOICE_SPEAK, Permission.NICKNAME_CHANGE};
public final static GatewayIntent[] INTENTS = {GatewayIntent.DIRECT_MESSAGES, GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_VOICE_STATES};
/**
@@ -85,7 +87,85 @@ public class JMusicBot
EventWaiter waiter = new EventWaiter();
SettingsManager settings = new SettingsManager();
Bot bot = new Bot(waiter, config, settings);
CommandClient client = createCommandClient(config, settings, bot);
if(!prompt.isNoGUI())
{
try
{
GUI gui = new GUI(bot);
bot.setGUI(gui);
gui.init();
LOG.info("Loaded config from " + config.getConfigLocation());
}
catch(Exception e)
{
LOG.error("Could not start GUI. If you are "
+ "running on a server or in a location where you cannot display a "
+ "window, please run in nogui mode using the -Dnogui=true flag.");
}
}
// attempt to log in and start
try
{
JDA jda = JDABuilder.create(config.getToken(), Arrays.asList(INTENTS))
.enableCache(CacheFlag.MEMBER_OVERRIDES, CacheFlag.VOICE_STATE)
.disableCache(CacheFlag.ACTIVITY, CacheFlag.CLIENT_STATUS, CacheFlag.EMOTE, CacheFlag.ONLINE_STATUS)
.setActivity(config.isGameNone() ? null : Activity.playing("loading..."))
.setStatus(config.getStatus()==OnlineStatus.INVISIBLE || config.getStatus()==OnlineStatus.OFFLINE
? OnlineStatus.INVISIBLE : OnlineStatus.DO_NOT_DISTURB)
.addEventListeners(client, waiter, new Listener(bot))
.setBulkDeleteSplittingEnabled(true)
.build();
bot.setJDA(jda);
// check if something about the current startup is not supported
String unsupportedReason = OtherUtil.getUnsupportedBotReason(jda);
if (unsupportedReason != null)
{
prompt.alert(Prompt.Level.ERROR, "JMusicBot", "JMusicBot cannot be run on this Discord bot: " + unsupportedReason);
try{ Thread.sleep(5000);}catch(InterruptedException ignored){} // this is awful but until we have a better way...
jda.shutdown();
System.exit(1);
}
// other check that will just be a warning now but may be required in the future
// check if the user has changed the prefix and provide info about the
// message content intent
if(!"@mention".equals(config.getPrefix()))
{
prompt.alert(Prompt.Level.INFO, "JMusicBot", "You currently have a custom prefix set. "
+ "If your prefix is not working, make sure that the 'MESSAGE CONTENT INTENT' is Enabled "
+ "on https://discord.com/developers/applications/" + jda.getSelfUser().getId() + "/bot");
}
}
catch (LoginException ex)
{
prompt.alert(Prompt.Level.ERROR, "JMusicBot", ex + "\nPlease make sure you are "
+ "editing the correct config.txt file, and that you have used the "
+ "correct token (not the 'secret'!)\nConfig Location: " + config.getConfigLocation());
System.exit(1);
}
catch(IllegalArgumentException ex)
{
prompt.alert(Prompt.Level.ERROR, "JMusicBot", "Some aspect of the configuration is "
+ "invalid: " + ex + "\nConfig Location: " + config.getConfigLocation());
System.exit(1);
}
catch(ErrorResponseException ex)
{
prompt.alert(Prompt.Level.ERROR, "JMusicBot", ex + "\nInvalid reponse returned when "
+ "attempting to connect, please make sure you're connected to the internet");
System.exit(1);
}
}
private static CommandClient createCommandClient(BotConfig config, SettingsManager settings, Bot bot)
{
// instantiate about command
AboutCommand aboutCommand = new AboutCommand(Color.BLUE.brighter(),
"a music bot that is [easy to host yourself!](https://github.com/jagrosh/MusicBot) (v" + OtherUtil.getCurrentVersion() + ")",
new String[]{"High-quality music playback", "FairQueue™ Technology", "Easy to host yourself"},
@@ -128,11 +208,12 @@ public class JMusicBot
new VolumeCmd(bot),
new PrefixCmd(bot),
new QueueTypeCmd(bot),
new SetdjCmd(bot),
new SkipratioCmd(bot),
new SettcCmd(bot),
new SetvcCmd(bot),
new AutoplaylistCmd(bot),
new DebugCmd(bot),
new PlaylistCmd(bot),
@@ -142,63 +223,23 @@ public class JMusicBot
new SetstatusCmd(bot),
new ShutdownCmd(bot)
);
// enable eval if applicable
if(config.useEval())
cb.addCommand(new EvalCmd(bot));
boolean nogame = false;
if(config.getStatus()!=OnlineStatus.UNKNOWN)
// set status if set in config
if(config.getStatus() != OnlineStatus.UNKNOWN)
cb.setStatus(config.getStatus());
if(config.getGame()==null)
// set game
if(config.getGame() == null)
cb.useDefaultGame();
else if(config.getGame().getName().equalsIgnoreCase("none"))
{
else if(config.isGameNone())
cb.setActivity(null);
nogame = true;
}
else
cb.setActivity(config.getGame());
if(!prompt.isNoGUI())
{
try
{
GUI gui = new GUI(bot);
bot.setGUI(gui);
gui.init();
}
catch(Exception e)
{
LOG.error("Could not start GUI. If you are "
+ "running on a server or in a location where you cannot display a "
+ "window, please run in nogui mode using the -Dnogui=true flag.");
}
}
// attempt to log in and start
try
{
JDA jda = JDABuilder.create(config.getToken(), Arrays.asList(INTENTS))
.enableCache(CacheFlag.MEMBER_OVERRIDES, CacheFlag.VOICE_STATE)
.disableCache(CacheFlag.ACTIVITY, CacheFlag.CLIENT_STATUS, CacheFlag.EMOTE, CacheFlag.ONLINE_STATUS)
.setActivity(nogame ? null : Activity.playing("loading..."))
.setStatus(config.getStatus()==OnlineStatus.INVISIBLE || config.getStatus()==OnlineStatus.OFFLINE
? OnlineStatus.INVISIBLE : OnlineStatus.DO_NOT_DISTURB)
.addEventListeners(cb.build(), waiter, new Listener(bot))
.setBulkDeleteSplittingEnabled(true)
.build();
bot.setJDA(jda);
}
catch (LoginException ex)
{
prompt.alert(Prompt.Level.ERROR, "JMusicBot", ex + "\nPlease make sure you are "
+ "editing the correct config.txt file, and that you have used the "
+ "correct token (not the 'secret'!)\nConfig Location: " + config.getConfigLocation());
System.exit(1);
}
catch(IllegalArgumentException ex)
{
prompt.alert(Prompt.Level.ERROR, "JMusicBot", "Some aspect of the configuration is "
+ "invalid: " + ex + "\nConfig Location: " + config.getConfigLocation());
System.exit(1);
}
return cb.build();
}
}
@@ -82,7 +82,7 @@ public class Listener extends ListenerAdapter
owner.openPrivateChannel().queue(pc -> pc.sendMessage(msg).queue());
}
}
catch(Exception ex) {} // ignored
catch(Exception ignored) {} // ignored
}, 0, 24, TimeUnit.HOURS);
}
}
@@ -15,8 +15,9 @@
*/
package com.jagrosh.jmusicbot.audio;
import com.jagrosh.jmusicbot.JMusicBot;
import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist;
import com.jagrosh.jmusicbot.queue.AbstractQueue;
import com.jagrosh.jmusicbot.settings.QueueType;
import com.jagrosh.jmusicbot.settings.RepeatMode;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
@@ -27,7 +28,6 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import com.jagrosh.jmusicbot.queue.FairQueue;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioTrack;
@@ -49,8 +49,7 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
public final static String PLAY_EMOJI = "\u25B6"; // ▶
public final static String PAUSE_EMOJI = "\u23F8"; // ⏸
public final static String STOP_EMOJI = "\u23F9"; // ⏹
private final FairQueue<QueuedTrack> queue = new FairQueue<>();
private final List<AudioTrack> defaultQueue = new LinkedList<>();
private final Set<String> votes = new HashSet<>();
@@ -59,12 +58,20 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
private final long guildId;
private AudioFrame lastFrame;
private AbstractQueue<QueuedTrack> queue;
protected AudioHandler(PlayerManager manager, Guild guild, AudioPlayer player)
{
this.manager = manager;
this.audioPlayer = player;
this.guildId = guild.getIdLong();
this.setQueueType(manager.getBot().getSettingsManager().getSettings(guildId).getQueueType());
}
public void setQueueType(QueueType type)
{
queue = type.createInstance(queue);
}
public int addTrackToFront(QueuedTrack qtrack)
@@ -92,7 +99,7 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
return queue.add(qtrack);
}
public FairQueue<QueuedTrack> getQueue()
public AbstractQueue<QueuedTrack> getQueue()
{
return queue;
}
@@ -175,7 +182,7 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
{
if(!playFromDefault())
{
manager.getBot().getNowplayingHandler().onTrackUpdate(guildId, null, this);
manager.getBot().getNowplayingHandler().onTrackUpdate(null);
if(!manager.getBot().getConfig().getStay())
manager.getBot().closeAudioConnection(guildId);
// unpause, in the case when the player was paused and the track has been skipped.
@@ -194,7 +201,7 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
public void onTrackStart(AudioPlayer player, AudioTrack track)
{
votes.clear();
manager.getBot().getNowplayingHandler().onTrackUpdate(guildId, track, this);
manager.getBot().getNowplayingHandler().onTrackUpdate(track);
}
@@ -214,9 +221,9 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
{
User u = guild.getJDA().getUserById(rm.user.id);
if(u==null)
eb.setAuthor(rm.user.username + "#" + rm.user.discrim, null, rm.user.avatar);
eb.setAuthor(FormatUtil.formatUsername(rm.user), null, rm.user.avatar);
else
eb.setAuthor(u.getName() + "#" + u.getDiscriminator(), null, u.getEffectiveAvatarUrl());
eb.setAuthor(FormatUtil.formatUsername(u), null, u.getEffectiveAvatarUrl());
}
try
@@ -258,24 +265,7 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
.setColor(guild.getSelfMember().getColor())
.build()).build();
}
public String getTopicFormat(JDA jda)
{
if(isMusicPlaying(jda))
{
long userid = getRequestMetadata().getOwner();
AudioTrack track = audioPlayer.getPlayingTrack();
String title = track.getInfo().title;
if(title==null || title.equals("Unknown Title"))
title = track.getInfo().uri;
return "**"+title+"** ["+(userid==0 ? "autoplay" : "<@"+userid+">")+"]"
+ "\n" + getStatusEmoji() + " "
+ "[" + FormatUtil.formatTime(track.getDuration()) + "] "
+ FormatUtil.volumeIcon(audioPlayer.getVolume());
}
else return "No music playing " + STOP_EMOJI + " " + FormatUtil.volumeIcon(audioPlayer.getVolume());
}
public String getStatusEmoji()
{
return audioPlayer.isPaused() ? PAUSE_EMOJI : PLAY_EMOJI;
@@ -98,42 +98,9 @@ public class NowplayingHandler
}
toRemove.forEach(id -> lastNP.remove(id));
}
public void updateTopic(long guildId, AudioHandler handler, boolean wait)
{
Guild guild = bot.getJDA().getGuildById(guildId);
if(guild==null)
return;
Settings settings = bot.getSettingsManager().getSettings(guildId);
TextChannel tchan = settings.getTextChannel(guild);
if(tchan!=null && guild.getSelfMember().hasPermission(tchan, Permission.MANAGE_CHANNEL))
{
String otherText;
String topic = tchan.getTopic();
if(topic==null || topic.isEmpty())
otherText = "\u200B";
else if(topic.contains("\u200B"))
otherText = topic.substring(topic.lastIndexOf("\u200B"));
else
otherText = "\u200B\n "+topic;
String text = handler.getTopicFormat(bot.getJDA()) + otherText;
if(!text.equals(tchan.getTopic()))
{
try
{
// normally here if 'wait' was false, we'd want to queue, however,
// new discord ratelimits specifically limiting changing channel topics
// mean we don't want a backlog of changes piling up, so if we hit a
// ratelimit, we just won't change the topic this time
tchan.getManager().setTopic(text).complete(wait);
}
catch(PermissionException | RateLimitedException ignore) {}
}
}
}
// "event"-based methods
public void onTrackUpdate(long guildId, AudioTrack track, AudioHandler handler)
public void onTrackUpdate(AudioTrack track)
{
// update bot status if applicable
if(bot.getConfig().getSongInStatus())
@@ -143,9 +110,6 @@ public class NowplayingHandler
else
bot.resetGame();
}
// update channel topic if applicable
updateTopic(guildId, handler, false);
}
public void onMessageDelete(Guild guild, long messageId)
@@ -15,6 +15,7 @@
*/
package com.jagrosh.jmusicbot.audio;
import com.dunctebot.sourcemanagers.DuncteBotSources;
import com.jagrosh.jmusicbot.Bot;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
@@ -40,6 +41,7 @@ public class PlayerManager extends DefaultAudioPlayerManager
TransformativeAudioSourceManager.createTransforms(bot.getConfig().getTransforms()).forEach(t -> registerSourceManager(t));
AudioSourceManagers.registerRemoteSources(this);
AudioSourceManagers.registerLocalSource(this);
DuncteBotSources.registerAll(this, "en-US");
source(YoutubeAudioSourceManager.class).setPlaylistPageCount(10);
}
@@ -16,6 +16,7 @@
package com.jagrosh.jmusicbot.audio;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
import com.jagrosh.jmusicbot.queue.Queueable;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.api.entities.User;
@@ -36,13 +37,13 @@ public class QueuedTrack implements Queueable
public QueuedTrack(AudioTrack track, RequestMetadata rm)
{
this.track = track;
this.track.setUserData(rm);
this.track.setUserData(rm == null ? RequestMetadata.EMPTY : rm);
}
@Override
public long getIdentifier()
{
return track.getUserData(RequestMetadata.class).getOwner();
return track.getUserData() == null ? 0L : track.getUserData(RequestMetadata.class).getOwner();
}
public AudioTrack getTrack()
@@ -53,6 +54,9 @@ public class QueuedTrack implements Queueable
@Override
public String toString()
{
return "`[" + FormatUtil.formatTime(track.getDuration()) + "]` [**" + track.getInfo().title + "**]("+track.getInfo().uri+") - <@" + track.getUserData(RequestMetadata.class).getOwner() + ">";
String entry = "`[" + FormatUtil.formatTime(track.getDuration()) + "]` ";
AudioTrackInfo trackInfo = track.getInfo();
entry = entry + (trackInfo.uri.startsWith("http") ? "[**" + trackInfo.title + "**]("+trackInfo.uri+")" : "**" + trackInfo.title + "**");
return entry + " - <@" + track.getUserData(RequestMetadata.class).getOwner() + ">";
}
}
@@ -86,6 +86,7 @@ public abstract class MusicCommand extends Command
try
{
event.getGuild().getAudioManager().openAudioConnection(userState.getChannel());
event.getGuild().getAudioManager().setSelfDeafened(true);
}
catch(PermissionException ex)
{
@@ -0,0 +1,75 @@
/*
* Copyright 2022 John Grosh <john.a.grosh@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.commands.admin;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.commands.AdminCommand;
import com.jagrosh.jmusicbot.settings.QueueType;
import com.jagrosh.jmusicbot.settings.Settings;
/**
*
* @author Wolfgang Schwendtbauer
*/
public class QueueTypeCmd extends AdminCommand
{
public QueueTypeCmd(Bot bot)
{
super();
this.name = "queuetype";
this.help = "changes the queue type";
this.arguments = "[" + String.join("|", QueueType.getNames()) + "]";
this.aliases = bot.getConfig().getAliases(this.name);
}
@Override
protected void execute(CommandEvent event)
{
String args = event.getArgs();
QueueType value;
Settings settings = event.getClient().getSettingsFor(event.getGuild());
if (args.isEmpty())
{
QueueType currentType = settings.getQueueType();
event.reply(currentType.getEmoji() + " Current queue type is: `" + currentType.getUserFriendlyName() + "`.");
return;
}
try
{
value = QueueType.valueOf(args.toUpperCase());
}
catch (IllegalArgumentException e)
{
event.replyError("Invalid queue type. Valid types are: [" + String.join("|", QueueType.getNames()) + "]");
return;
}
if (settings.getQueueType() != value)
{
settings.setQueueType(value);
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
if (handler != null)
handler.setQueueType(value);
}
event.reply(value.getEmoji() + " Queue type was set to `" + value.getUserFriendlyName() + "`.");
}
}
@@ -21,6 +21,7 @@ import com.jagrosh.jdautilities.menu.OrderedMenu;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.commands.DJCommand;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
@@ -113,7 +114,7 @@ public class ForceRemoveCmd extends DJCommand
}
else
{
event.replySuccess("Successfully removed `"+count+"` entries from **"+target.getName()+"**#"+target.getDiscriminator()+".");
event.replySuccess("Successfully removed `"+count+"` entries from "+FormatUtil.formatUsername(target)+".");
}
}
}
@@ -20,7 +20,7 @@ import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.RequestMetadata;
import com.jagrosh.jmusicbot.commands.DJCommand;
import net.dv8tion.jda.api.entities.User;
import com.jagrosh.jmusicbot.utils.FormatUtil;
/**
*
@@ -43,7 +43,7 @@ public class ForceskipCmd extends DJCommand
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
RequestMetadata rm = handler.getRequestMetadata();
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title
+"** "+(rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + rm.user.username + "**)"));
+"** "+(rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + FormatUtil.formatUsername(rm.user) + "**)"));
handler.getPlayer().stopTrack();
}
}
@@ -6,7 +6,7 @@ import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.QueuedTrack;
import com.jagrosh.jmusicbot.commands.DJCommand;
import com.jagrosh.jmusicbot.queue.FairQueue;
import com.jagrosh.jmusicbot.queue.AbstractQueue;
/**
* Command that provides users the ability to move a track in the playlist.
@@ -57,7 +57,7 @@ public class MoveTrackCmd extends DJCommand
// Validate that from and to are available
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
FairQueue<QueuedTrack> queue = handler.getQueue();
AbstractQueue<QueuedTrack> queue = handler.getQueue();
if (isUnavailablePosition(queue, from))
{
String reply = String.format("`%d` is not a valid position in the queue!", from);
@@ -78,7 +78,7 @@ public class MoveTrackCmd extends DJCommand
event.replySuccess(reply);
}
private static boolean isUnavailablePosition(FairQueue<QueuedTrack> queue, int position)
private static boolean isUnavailablePosition(AbstractQueue<QueuedTrack> queue, int position)
{
return (position < 1 || position > queue.size());
}
@@ -18,6 +18,7 @@ package com.jagrosh.jmusicbot.commands.general;
import com.jagrosh.jdautilities.command.Command;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.settings.QueueType;
import com.jagrosh.jmusicbot.settings.RepeatMode;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
@@ -63,6 +64,9 @@ public class SettingsCmd extends Command
+ "\nRepeat Mode: " + (s.getRepeatMode() == RepeatMode.OFF
? s.getRepeatMode().getUserFriendlyName()
: "**"+s.getRepeatMode().getUserFriendlyName()+"**")
+ "\nQueue Type: " + (s.getQueueType() == QueueType.FAIR
? s.getQueueType().getUserFriendlyName()
: "**"+s.getQueueType().getUserFriendlyName()+"**")
+ "\nDefault Playlist: " + (s.getDefaultPlaylist() == null ? "None" : "**" + s.getDefaultPlaylist() + "**")
)
.setFooter(event.getJDA().getGuilds().size() + " servers | "
@@ -20,10 +20,10 @@ import java.util.concurrent.TimeUnit;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jdautilities.menu.Paginator;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.JMusicBot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.QueuedTrack;
import com.jagrosh.jmusicbot.commands.MusicCommand;
import com.jagrosh.jmusicbot.settings.QueueType;
import com.jagrosh.jmusicbot.settings.RepeatMode;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
@@ -95,7 +95,7 @@ public class QueueCmd extends MusicCommand
}
Settings settings = event.getClient().getSettingsFor(event.getGuild());
long fintotal = total;
builder.setText((i1,i2) -> getQueueTitle(ah, event.getClient().getSuccess(), songs.length, fintotal, settings.getRepeatMode()))
builder.setText((i1,i2) -> getQueueTitle(ah, event.getClient().getSuccess(), songs.length, fintotal, settings.getRepeatMode(), settings.getQueueType()))
.setItems(songs)
.setUsers(event.getAuthor())
.setColor(event.getSelfMember().getColor())
@@ -103,7 +103,7 @@ public class QueueCmd extends MusicCommand
builder.build().paginate(event.getChannel(), pagenum);
}
private String getQueueTitle(AudioHandler ah, String success, int songslength, long total, RepeatMode repeatmode)
private String getQueueTitle(AudioHandler ah, String success, int songslength, long total, RepeatMode repeatmode, QueueType queueType)
{
StringBuilder sb = new StringBuilder();
if(ah.getPlayer().getPlayingTrack()!=null)
@@ -113,6 +113,7 @@ public class QueueCmd extends MusicCommand
}
return FormatUtil.filter(sb.append(success).append(" Current Queue | ").append(songslength)
.append(" entries | `").append(FormatUtil.formatTime(total)).append("` ")
.append(repeatmode.getEmoji() != null ? "| "+repeatmode.getEmoji() : "").toString());
.append("| ").append(queueType.getEmoji()).append(" `").append(queueType.getUserFriendlyName()).append('`')
.append(repeatmode.getEmoji() != null ? " | "+repeatmode.getEmoji() : "").toString());
}
}
@@ -20,7 +20,7 @@ import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.RequestMetadata;
import com.jagrosh.jmusicbot.commands.MusicCommand;
import net.dv8tion.jda.api.entities.User;
import com.jagrosh.jmusicbot.utils.FormatUtil;
/**
*
@@ -43,7 +43,11 @@ public class SkipCmd extends MusicCommand
{
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
RequestMetadata rm = handler.getRequestMetadata();
if(event.getAuthor().getIdLong() == rm.getOwner())
double skipRatio = bot.getSettingsManager().getSettings(event.getGuild()).getSkipRatio();
if(skipRatio == -1) {
skipRatio = bot.getConfig().getSkipRatio();
}
if(event.getAuthor().getIdLong() == rm.getOwner() || skipRatio == 0)
{
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**");
handler.getPlayer().stopTrack();
@@ -62,12 +66,12 @@ public class SkipCmd extends MusicCommand
}
int skippers = (int)event.getSelfMember().getVoiceState().getChannel().getMembers().stream()
.filter(m -> handler.getVotes().contains(m.getUser().getId())).count();
int required = (int)Math.ceil(listeners * bot.getSettingsManager().getSettings(event.getGuild()).getSkipRatio());
int required = (int)Math.ceil(listeners * skipRatio);
msg += skippers + " votes, " + required + "/" + listeners + " needed]`";
if(skippers>=required)
{
msg += "\n" + event.getClient().getSuccess() + " Skipped **" + handler.getPlayer().getPlayingTrack().getInfo().title
+ "** " + (rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + rm.user.username + "**)");
+ "** " + (rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + FormatUtil.formatUsername(rm.user) + "**)");
handler.getPlayer().stopTrack();
}
event.reply(msg);
@@ -20,6 +20,7 @@ import javax.script.ScriptEngineManager;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.commands.OwnerCommand;
import net.dv8tion.jda.api.entities.ChannelType;
/**
*
@@ -45,8 +46,10 @@ public class EvalCmd extends OwnerCommand
se.put("bot", bot);
se.put("event", event);
se.put("jda", event.getJDA());
se.put("guild", event.getGuild());
se.put("channel", event.getChannel());
if (event.getChannelType() != ChannelType.PRIVATE) {
se.put("guild", event.getGuild());
se.put("channel", event.getChannel());
}
try
{
event.reply(event.getClient().getSuccess()+" Evaluated Successfully:\n```\n"+se.eval(event.getArgs())+" ```");
@@ -72,6 +72,7 @@ public class PlaylistCmd extends OwnerCommand
protected void execute(CommandEvent event)
{
String pname = event.getArgs().replaceAll("\\s+", "_");
pname = pname.replaceAll("[*?|\\/\":<>]", "");
if(pname == null || pname.isEmpty())
{
event.replyError("Please provide a name for the playlist!");
@@ -0,0 +1,130 @@
/*
* Copyright 2022 John Grosh (jagrosh).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.queue;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author Wolfgang Schwendtbauer
* @param <T>
*/
public abstract class AbstractQueue<T extends Queueable>
{
protected AbstractQueue(AbstractQueue<T> queue)
{
this.list = queue != null ? queue.getList() : new LinkedList<>();
}
protected final List<T> list;
public abstract int add(T item);
public void addAt(int index, T item)
{
if(index >= list.size())
list.add(item);
else
list.add(index, item);
}
public int size() {
return list.size();
}
public T pull() {
return list.remove(0);
}
public boolean isEmpty()
{
return list.isEmpty();
}
public List<T> getList()
{
return list;
}
public T get(int index) {
return list.get(index);
}
public T remove(int index)
{
return list.remove(index);
}
public int removeAll(long identifier)
{
int count = 0;
for(int i=list.size()-1; i>=0; i--)
{
if(list.get(i).getIdentifier()==identifier)
{
list.remove(i);
count++;
}
}
return count;
}
public void clear()
{
list.clear();
}
public int shuffle(long identifier)
{
List<Integer> iset = new ArrayList<>();
for(int i=0; i<list.size(); i++)
{
if(list.get(i).getIdentifier()==identifier)
iset.add(i);
}
for(int j=0; j<iset.size(); j++)
{
int first = iset.get(j);
int second = iset.get((int)(Math.random()*iset.size()));
T temp = list.get(first);
list.set(first, list.get(second));
list.set(second, temp);
}
return iset.size();
}
public void skip(int number)
{
if (number > 0) {
list.subList(0, number).clear();
}
}
/**
* Move an item to a different position in the list
* @param from The position of the item
* @param to The new position of the item
* @return the moved item
*/
public T moveItem(int from, int to)
{
T item = list.remove(from);
list.add(to, item);
return item;
}
}
@@ -15,9 +15,7 @@
*/
package com.jagrosh.jmusicbot.queue;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
@@ -25,15 +23,21 @@ import java.util.Set;
* @author John Grosh (jagrosh)
* @param <T>
*/
public class FairQueue<T extends Queueable> {
private final List<T> list = new ArrayList<>();
private final Set<Long> set = new HashSet<>();
public class FairQueue<T extends Queueable> extends AbstractQueue<T>
{
public FairQueue(AbstractQueue<T> queue)
{
super(queue);
}
protected final Set<Long> set = new HashSet<>();
@Override
public int add(T item)
{
int lastIndex;
for(lastIndex=list.size()-1; lastIndex>-1; lastIndex--)
if(list.get(lastIndex).getIdentifier()==item.getIdentifier())
if(list.get(lastIndex).getIdentifier() == item.getIdentifier())
break;
lastIndex++;
set.clear();
@@ -46,99 +50,5 @@ public class FairQueue<T extends Queueable> {
list.add(lastIndex, item);
return lastIndex;
}
public void addAt(int index, T item)
{
if(index >= list.size())
list.add(item);
else
list.add(index, item);
}
public int size()
{
return list.size();
}
public T pull()
{
return list.remove(0);
}
public boolean isEmpty()
{
return list.isEmpty();
}
public List<T> getList()
{
return list;
}
public T get(int index)
{
return list.get(index);
}
public T remove(int index)
{
return list.remove(index);
}
public int removeAll(long identifier)
{
int count = 0;
for(int i=list.size()-1; i>=0; i--)
{
if(list.get(i).getIdentifier()==identifier)
{
list.remove(i);
count++;
}
}
return count;
}
public void clear()
{
list.clear();
}
public int shuffle(long identifier)
{
List<Integer> iset = new ArrayList<>();
for(int i=0; i<list.size(); i++)
{
if(list.get(i).getIdentifier()==identifier)
iset.add(i);
}
for(int j=0; j<iset.size(); j++)
{
int first = iset.get(j);
int second = iset.get((int)(Math.random()*iset.size()));
T temp = list.get(first);
list.set(first, list.get(second));
list.set(second, temp);
}
return iset.size();
}
public void skip(int number)
{
for(int i=0; i<number; i++)
list.remove(0);
}
/**
* Move an item to a different position in the list
* @param from The position of the item
* @param to The new position of the item
* @return the moved item
*/
public T moveItem(int from, int to)
{
T item = list.remove(from);
list.add(to, item);
return item;
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2022 John Grosh (jagrosh).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.queue;
/**
*
* @author Wolfgang Schwendtbauer
* @param <T>
*/
public class LinearQueue<T extends Queueable> extends AbstractQueue<T>
{
public LinearQueue(AbstractQueue<T> queue)
{
super(queue);
}
@Override
public int add(T item)
{
list.add(item);
return list.size() - 1;
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2022 John Grosh <john.a.grosh@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.queue;
/**
*
* @author Wolfgang Schwendtbauer
*/
@FunctionalInterface
public interface QueueSupplier
{
<T extends Queueable> AbstractQueue<T> apply(AbstractQueue<T> queue);
}
@@ -0,0 +1,69 @@
/*
* Copyright 2022 John Grosh <john.a.grosh@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.settings;
import com.jagrosh.jmusicbot.queue.AbstractQueue;
import com.jagrosh.jmusicbot.queue.FairQueue;
import com.jagrosh.jmusicbot.queue.LinearQueue;
import com.jagrosh.jmusicbot.queue.Queueable;
import com.jagrosh.jmusicbot.queue.QueueSupplier;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
*
* @author Wolfgang Schwendtbauer
*/
public enum QueueType
{
LINEAR("\u23E9", "Linear", LinearQueue::new), // ⏩
FAIR("\uD83D\uDD22", "Fair", FairQueue::new); // 🔢
private final String userFriendlyName;
private final String emoji;
private final QueueSupplier supplier;
QueueType(final String emoji, final String userFriendlyName, QueueSupplier supplier)
{
this.userFriendlyName = userFriendlyName;
this.emoji = emoji;
this.supplier = supplier;
}
public static List<String> getNames()
{
return Arrays.stream(QueueType.values())
.map(type -> type.name().toLowerCase())
.collect(Collectors.toList());
}
public <T extends Queueable> AbstractQueue<T> createInstance(AbstractQueue<T> previous)
{
return supplier.apply(previous);
}
public String getUserFriendlyName()
{
return userFriendlyName;
}
public String getEmoji()
{
return emoji;
}
}
@@ -36,10 +36,11 @@ public class Settings implements GuildSettingsProvider
private int volume;
private String defaultPlaylist;
private RepeatMode repeatMode;
private QueueType queueType;
private String prefix;
private double skipRatio;
public Settings(SettingsManager manager, String textId, String voiceId, String roleId, int volume, String defaultPlaylist, RepeatMode repeatMode, String prefix, double skipRatio)
public Settings(SettingsManager manager, String textId, String voiceId, String roleId, int volume, String defaultPlaylist, RepeatMode repeatMode, String prefix, double skipRatio, QueueType queueType)
{
this.manager = manager;
try
@@ -71,9 +72,10 @@ public class Settings implements GuildSettingsProvider
this.repeatMode = repeatMode;
this.prefix = prefix;
this.skipRatio = skipRatio;
this.queueType = queueType;
}
public Settings(SettingsManager manager, long textId, long voiceId, long roleId, int volume, String defaultPlaylist, RepeatMode repeatMode, String prefix, double skipRatio)
public Settings(SettingsManager manager, long textId, long voiceId, long roleId, int volume, String defaultPlaylist, RepeatMode repeatMode, String prefix, double skipRatio, QueueType queueType)
{
this.manager = manager;
this.textId = textId;
@@ -84,6 +86,7 @@ public class Settings implements GuildSettingsProvider
this.repeatMode = repeatMode;
this.prefix = prefix;
this.skipRatio = skipRatio;
this.queueType = queueType;
}
// Getters
@@ -127,6 +130,11 @@ public class Settings implements GuildSettingsProvider
return skipRatio;
}
public QueueType getQueueType()
{
return queueType;
}
@Override
public Collection<String> getPrefixes()
{
@@ -181,4 +189,10 @@ public class Settings implements GuildSettingsProvider
this.skipRatio = skipRatio;
this.manager.writeSettings();
}
public void setQueueType(QueueType queueType)
{
this.queueType = queueType;
this.manager.writeSettings();
}
}
@@ -19,6 +19,7 @@ import com.jagrosh.jdautilities.command.GuildSettingsManager;
import com.jagrosh.jmusicbot.utils.OtherUtil;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.util.HashMap;
import net.dv8tion.jda.api.entities.Guild;
import org.json.JSONException;
@@ -29,16 +30,17 @@ import org.slf4j.LoggerFactory;
*
* @author John Grosh (john.a.grosh@gmail.com)
*/
public class SettingsManager implements GuildSettingsManager
public class SettingsManager implements GuildSettingsManager<Settings>
{
private final static double SKIP_RATIO = .55;
private final static String SETTINGS_FILE = "serversettings.json";
private final HashMap<Long,Settings> settings;
public SettingsManager()
{
this.settings = new HashMap<>();
try {
JSONObject loadedSettings = new JSONObject(new String(Files.readAllBytes(OtherUtil.getPath("serversettings.json"))));
JSONObject loadedSettings = new JSONObject(new String(Files.readAllBytes(OtherUtil.getPath(SETTINGS_FILE))));
loadedSettings.keySet().forEach((id) -> {
JSONObject o = loadedSettings.getJSONObject(id);
@@ -55,16 +57,28 @@ public class SettingsManager implements GuildSettingsManager
o.has("default_playlist")? o.getString("default_playlist") : null,
o.has("repeat_mode") ? o.getEnum(RepeatMode.class, "repeat_mode"): RepeatMode.OFF,
o.has("prefix") ? o.getString("prefix") : null,
o.has("skip_ratio") ? o.getDouble("skip_ratio") : SKIP_RATIO));
o.has("skip_ratio") ? o.getDouble("skip_ratio") : -1,
o.has("queue_type") ? o.getEnum(QueueType.class, "queue_type") : QueueType.FAIR));
});
} catch (NoSuchFileException e) {
// create an empty json file
try {
LoggerFactory.getLogger("Settings").info("serversettings.json will be created in " + OtherUtil.getPath("serversettings.json").toAbsolutePath());
Files.write(OtherUtil.getPath("serversettings.json"), new JSONObject().toString(4).getBytes());
} catch(IOException ex) {
LoggerFactory.getLogger("Settings").warn("Failed to create new settings file: "+ex);
}
return;
} catch(IOException | JSONException e) {
LoggerFactory.getLogger("Settings").warn("Failed to load server settings (this is normal if no settings have been set yet): "+e);
LoggerFactory.getLogger("Settings").warn("Failed to load server settings: "+e);
}
LoggerFactory.getLogger("Settings").info("serversettings.json loaded from " + OtherUtil.getPath("serversettings.json").toAbsolutePath());
}
/**
* Gets non-null settings for a Guild
*
*
* @param guild the guild to get settings for
* @return the existing settings, or new settings for that guild
*/
@@ -73,17 +87,17 @@ public class SettingsManager implements GuildSettingsManager
{
return getSettings(guild.getIdLong());
}
public Settings getSettings(long guildId)
{
return settings.computeIfAbsent(guildId, id -> createDefaultSettings());
}
private Settings createDefaultSettings()
{
return new Settings(this, 0, 0, 0, 100, null, RepeatMode.OFF, null, SKIP_RATIO);
return new Settings(this, 0, 0, 0, 100, null, RepeatMode.OFF, null, -1, QueueType.FAIR);
}
protected void writeSettings()
{
JSONObject obj = new JSONObject();
@@ -104,12 +118,14 @@ public class SettingsManager implements GuildSettingsManager
o.put("repeat_mode", s.getRepeatMode());
if(s.getPrefix() != null)
o.put("prefix", s.getPrefix());
if(s.getSkipRatio() != SKIP_RATIO)
if(s.getSkipRatio() != -1)
o.put("skip_ratio", s.getSkipRatio());
if(s.getQueueType() != QueueType.FAIR)
o.put("queue_type", s.getQueueType().name());
obj.put(Long.toString(key), o);
});
try {
Files.write(OtherUtil.getPath("serversettings.json"), obj.toString(4).getBytes());
Files.write(OtherUtil.getPath(SETTINGS_FILE), obj.toString(4).getBytes());
} catch(IOException ex){
LoggerFactory.getLogger("Settings").warn("Failed to write to file: "+ex);
}
@@ -15,9 +15,11 @@
*/
package com.jagrosh.jmusicbot.utils;
import com.jagrosh.jmusicbot.audio.RequestMetadata.UserInfo;
import java.util.List;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.VoiceChannel;
/**
@@ -37,6 +39,28 @@ public class FormatUtil {
seconds %= 60;
return (hours>0 ? hours+":" : "") + (minutes<10 ? "0"+minutes : minutes) + ":" + (seconds<10 ? "0"+seconds : seconds);
}
public static String formatUsername(String username, String discrim)
{
if(discrim == null || discrim.equals("0000"))
{
return username;
}
else
{
return username + "#" + discrim;
}
}
public static String formatUsername(UserInfo userinfo)
{
return formatUsername(userinfo.username, userinfo.discrim);
}
public static String formatUsername(User user)
{
return formatUsername(user.getName(), user.getDiscriminator());
}
public static String progressBar(double percent)
{
@@ -0,0 +1,44 @@
/*
* Copyright 2024 John Grosh <john.a.grosh@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.utils;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.turbo.TurboFilter;
import ch.qos.logback.core.spi.FilterReply;
import org.slf4j.Marker;
/**
* A TurboFilter, currently only used to suppress specific log messages from libraries.
*
* @author Michaili K. <git@michaili.dev>
*/
public class LogBackTurboFilter extends TurboFilter
{
@Override
public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t)
{
// Suppresses the auth token warning from the YoutubeAudioSourceManager
// https://github.com/jagrosh/MusicBot/pull/1490#issuecomment-1974070225
if (logger.getName().equals("com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAccessTokenTracker")
&& format.equals("YouTube auth tokens can't be retrieved because email and password is not set in YoutubeAudioSourceManager, age restricted videos will throw exceptions.")
) {
return FilterReply.DENY;
}
return FilterReply.NEUTRAL;
}
}
@@ -23,8 +23,12 @@ import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Path;
import java.nio.file.Paths;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.ApplicationInfo;
import net.dv8tion.jda.api.entities.User;
import okhttp3.*;
import org.json.JSONException;
import org.json.JSONObject;
@@ -60,7 +64,7 @@ public class OtherUtil
{
result = Paths.get(new File(JMusicBot.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile().getPath() + File.separator + path);
}
catch(URISyntaxException ex) {}
catch(URISyntaxException ignored) {}
}
return result;
}
@@ -80,7 +84,7 @@ public class OtherUtil
reader.lines().forEach(line -> sb.append("\r\n").append(line));
return sb.toString().trim();
}
catch(IOException ex)
catch(IOException ignored)
{
return null;
}
@@ -207,4 +211,20 @@ public class OtherUtil
return null;
}
}
/**
* Checks if the bot JMusicBot is being run on is supported & returns the reason if it is not.
* @return A string with the reason, or null if it is supported.
*/
public static String getUnsupportedBotReason(JDA jda)
{
if (jda.getSelfUser().getFlags().contains(User.UserFlag.VERIFIED_BOT))
return "The bot is verified. Using JMusicBot in a verified bot is not supported.";
ApplicationInfo info = jda.retrieveApplicationInfo().complete();
if (info.isBotPublic())
return "\"Public Bot\" is enabled. Using JMusicBot as a public bot is not supported. Please disable it in the Developer Dashboard.";
return null;
}
}
+2
View File
@@ -14,4 +14,6 @@
<appender-ref ref="Simple"/>
</root>
<turboFilter class="com.jagrosh.jmusicbot.utils.LogBackTurboFilter" />
</configuration>
+8
View File
@@ -99,6 +99,13 @@ stayinchannel = false
maxtime = 0
// This sets the ratio of users that must vote to skip the currently playing song.
// Guild owners can define their own skip ratios, but this will be used if a guild
// has not defined their own skip ratio.
skipratio = 0.55
// This sets the amount of seconds the bot will stay alone on a voice channel until it
// automatically leaves the voice channel and clears the queue. If not set or set
// to any number less than or equal to zero, the bot won't leave when alone.
@@ -167,6 +174,7 @@ aliases {
movetrack = [ move ]
pause = []
playnext = []
queuetype = []
repeat = []
skipto = [ jumpto ]
stop = [ leave ]
@@ -29,7 +29,7 @@ public class FairQueueTest
@Test
public void differentIdentifierSize()
{
FairQueue<Q> queue = new FairQueue<>();
FairQueue<Q> queue = new FairQueue<>(null);
int size = 100;
for(int i=0; i<size; i++)
queue.add(new Q(i));
@@ -39,7 +39,7 @@ public class FairQueueTest
@Test
public void sameIdentifierSize()
{
FairQueue<Q> queue = new FairQueue<>();
FairQueue<Q> queue = new FairQueue<>(null);
int size = 100;
for(int i=0; i<size; i++)
queue.add(new Q(0));