Compare commits

...
60 Commits
Author SHA1 Message Date
John Grosh 85aebe8a89 Update make-release.yml 2022-04-05 19:06:50 -04:00
John Grosh 3c306a8d7a Update make-release.yml 2022-04-05 19:04:50 -04:00
John Grosh cadaf94cf7 Update make-release.yml 2022-04-05 18:55:05 -04:00
John Grosh 15a47a7b4d Update make-release.yml 2022-04-05 18:51:36 -04:00
John Grosh 92fca22059 Update make-release.yml 2022-04-05 18:42:44 -04:00
John Grosh 0cdc0d2d34 Update make-release.yml 2022-04-05 18:29:06 -04:00
John Grosh 1d1dfe103e Create make-release.yml 2022-04-05 18:25:29 -04:00
John Grosh ab39d2cd48 Merge branch 'master' of https://github.com/jagrosh/MusicBot 2022-04-05 17:47:09 -04:00
John Grosh 32d165d1b3 add natives to base repo because I don't think any other repo is opening up soon 2022-04-05 17:46:59 -04:00
John Grosh c404e92932 Create test.yml 2022-01-06 16:10:02 -05:00
John Grosh 6af86cae5d Update bug-report.yml 2022-01-06 10:24:11 -05:00
John Grosh 9421730229 Delete feature-request.md 2022-01-06 10:18:58 -05:00
John Grosh b76e31c7e2 Delete bug-report.md 2022-01-06 10:18:48 -05:00
John Grosh 6056ff2c13 Merge pull request #1032 from yo-louk/queue-links
Queue links
2021-11-13 14:14:21 -05:00
yo-louk 8c92a7a75a changed QueuedTrack class instead 2021-11-12 00:55:16 +01:00
yo-louk e942f3ef21 changed list format 2021-11-12 00:31:31 +01:00
John Grosh 157da6c0da Update config.yml 2021-10-04 17:36:01 -04:00
John Grosh 03b909a3f5 whoops 2021-10-04 08:09:12 -04:00
Louk e08c1491c2 Voice channel mention (#932)
* Voice channel mention

I recently noticed voice channels can be mentioned just like text channels. A small change to the code to implement this feature.

* Voice channel mention

* vc.getName() changed to vc.getAsMention()
2021-10-02 15:17:00 -04:00
John Grosh 95de05ff2d Merge pull request #940 from MichailiK/feature/use-lyrics-anytime
Allow usage of LyricsCmd without playing music
2021-10-02 15:15:31 -04:00
Michaili K e242fa9557 Allow usage of LyricsCmd without playing music 2021-10-01 17:47:36 +02:00
John Grosh b83028fb97 use lavaplayer fork, fix update alerts, fix special case repeat mode parse 2021-09-27 14:19:48 -04:00
Michaili K 7273b5bf42 Add single repeat (#359)
* Add single repeat mode

* Toggle between NONE and REPEAT modes when no argument has been given

* Allow single as repeat mode

* Add one to the arguments

* Add backwards comparability for old versions of MusicBot.

* Use JSONObject#getEnum for parsing repeat mode

* Rename repeat modes & fix repeat one not repeating properly

* Add copyright notice & author to RepeatMode

* Fix spacing in SettingsManager

* Rename REPEAT_ONE to One in RepeatMode

* Rename repeat to repeat_mode

* Use variable on track end in AudioHandler & update RepeatMode & its usages

* Use user friendly name in RepeatCmd

* Remove unused constants

* Fix spacing in SettingsManager

* Rename repeat modes to off/all/single respectively
2021-09-27 19:51:57 +02:00
John Grosh 6f6064521b Create feature-request.yml 2021-09-21 22:08:51 -04:00
John Grosh 48263ec60a Merge pull request #825 from MichailiK/fix/path-loading-bug
Fix Windows path loading bug
2021-09-18 16:23:30 -04:00
Michaili K c6e5b8cbda Add "clear" alias to stop command (#847)
* Add "clear" alias to stop command

* Use "leave" as an alias to stop instead of "clear"
2021-09-18 16:21:55 -04:00
John Grosh 3b8892d2c0 fix queuedtrack string 2021-09-17 18:09:46 -04:00
John Grosh 54cb757dc5 Merge branch 'master' of https://github.com/jagrosh/MusicBot 2021-09-17 17:06:18 -04:00
John Grosh c4de816c7c added skip ratio and more request metadata beyond user id 2021-09-17 17:06:09 -04:00
John Grosh ccb421e110 Update bug-report.yml 2021-09-17 13:49:19 -04:00
John Grosh 23334e4253 Update bug-report.yml 2021-09-17 13:48:57 -04:00
John Grosh 826aec2492 Create bug-report.yml 2021-09-17 13:46:23 -04:00
John Grosh c397afcf8d Merge branch 'master' of https://github.com/jagrosh/MusicBot 2021-09-16 20:49:58 -04:00
John Grosh 95c07eab8e updated pom with working repos, and add comments with what we need to fix 2021-09-16 20:48:49 -04:00
John Grosh 58e0e5737d update pom with lowercase for circleci 2021-09-16 18:50:50 -04:00
John Grosh 84bc57e3cf Merge branch 'master' of https://github.com/jagrosh/MusicBot 2021-09-16 18:45:04 -04:00
John Grosh 67f6169e61 update pom 2021-09-16 18:45:02 -04:00
John Grosh 6f7c47757b Create FUNDING.yml 2021-09-16 18:42:46 -04:00
John Grosh 0a353e0fa3 Update README.md 2021-09-16 18:36:14 -04:00
Michaili K ddf95da19f Fix Windows path loading bug 2021-09-16 20:30:17 +02:00
John Grosh d5bb16d143 Update config.yml 2021-09-16 09:31:49 -04:00
John Grosh d47cfee8bf Create CNAME 2021-09-15 18:09:07 -04:00
John Grosh 1ee089e7ad Delete CNAME 2021-09-15 17:42:24 -04:00
John Grosh 7255e4b6db Create CNAME 2021-09-15 17:42:18 -04:00
John Grosh 4e314da86d Update README.md 2021-09-14 17:32:39 -04:00
John Grosh ec06cf10e9 update reference.conf 2021-08-30 21:09:48 -04:00
John Grosh ad972185d4 update deps
add transformative audio source
fix intents
2021-08-30 02:17:34 -04:00
John Grosh bc64b45d5c whoops 2021-04-02 14:21:39 -04:00
John Grosh 699c2a0e6e update deps and add noprompt mode 2021-04-02 14:02:16 -04:00
John Grosh ebf6e43084 Merge pull request #583 from MichailiK/feature/auto-leave-when-alone
Add "alone time until stop"
2021-02-09 09:55:50 -05:00
Michaili K 6527afb88c Ignore all bots when checking if the bot is alone 2021-02-09 15:52:41 +01:00
Michaili K 18e142ed88 Store guild ID instead of the guild object in AloneInVoiceHandler 2021-02-09 15:09:59 +01:00
John Grosh 797d306a4e Update README.md 2021-02-08 17:10:24 -05:00
John Grosh 267bf71d47 Update feature-request.md 2021-02-08 10:03:08 -05:00
John Grosh 88a89f2178 Update config.yml 2021-02-08 09:37:16 -05:00
John Grosh 8d2f60eba5 Merge origin/master 2021-02-06 19:53:03 -05:00
John Grosh b4c9a125e5 deps update and ref cleanup 2021-02-06 19:52:48 -05:00
Michaili K 36c53cfd41 Add "alone time until stop" feature & config 2021-01-13 01:58:21 +01:00
John Grosh d1223e9795 Update config.yml 2020-12-03 12:33:45 -05:00
John Grosh cebdbc3aa5 initial version of automation script 2020-11-04 18:56:10 -05:00
45 changed files with 883 additions and 161 deletions

No files matched your search

+11
View File
@@ -39,3 +39,14 @@ jobs:
# run tests!
- run: mvn integration-test
workflows:
version: 2
build:
jobs:
- build:
filters:
branches:
ignore:
- gh-pages
- gh-pages-generated
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: jagrosh
patreon: jagrosh
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
-29
View File
@@ -1,29 +0,0 @@
---
name: Bug Report
about: Report a bug or unexpected behavior
title: "[Bug Report] Title Here"
labels: ''
assignees: ''
---
**Describe the bug**
Replace this text with a clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1.
2.
3.
**Expected behavior**
Replace this text with a clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem here.
**Debug Info**
Please run the `debug` command and paste it here or upload it as an attachment. If you cannot run the command, please provide information about your system such as operating system version, JMusicBot version, etc.
**Additional context**
Replace this text with any other context about the problem.
+57
View File
@@ -0,0 +1,57 @@
name: Bug Report
description: Report a bug or unexpected behavior
title: "[Bug Report] Title Here "
labels: []
assignees: []
body:
- type: textarea
id: description
attributes:
label: Bug Description
description: Please provide a clear and concise description of what the bug is.
placeholder: The bug is...
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to Reproduce
description: Provide steps you can take to reliably reproduce this bug
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Result
description: What did you expect to happen when this bug occurred?
placeholder: I expected...
validations:
required: true
- type: textarea
id: debug
attributes:
label: Debug Output
description: Please run the `debug` command and paste it here or upload it as an attachment. If you cannot run the command, please provide information about your system such as operating system version, JMusicBot version, etc.
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Additional Info
description: Any additional info that you think might be useful or relevant to this bug
- type: checkboxes
id: checks
attributes:
label: Checklist
description: Before submitting your issue, please affirm the following
options:
- label: I have looked for information about this [within the documentation](https://jmusicbot.com/)
required: true
- label: I have searched for similar issues [on the issues page](https://github.com/jagrosh/MusicBot/issues?q=is%3Aissue)
required: true
- label: "I am running the latest version of the bot: ![Release](https://img.shields.io/github/release/jagrosh/MusicBot.svg)"
required: true
+6 -3
View File
@@ -1,5 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Community Support Server
url: https://discord.gg/0p9LSGoRLu6Pet0k
about: For general questions, please ask in the support server
- name: Question
url: https://github.com/jagrosh/MusicBot/discussions/categories/questions
about: For general questions, please check the Discussions
- name: Troubleshooting
url: https://github.com/jagrosh/MusicBot/discussions/categories/troubleshooting
about: For troubleshooting help, please check the Discussions
-26
View File
@@ -1,26 +0,0 @@
---
name: Feature Request
about: Suggest an idea for this project
title: "[Feature Request] Title Here"
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
Replace this text with a clear and concise description of what the problem is.
**Describe the solution you'd like**
Replace this text with a clear and concise description of what you want to happen.
**Describe the basic flow/steps of using this feature**
How this feature would be used:
1.
2.
3.
**Additional context**
Add any other context or screenshots about the feature request here.
**Have you confirmed that this idea is not already described here?**
https://github.com/jagrosh/MusicBot/projects/1
@@ -0,0 +1,52 @@
name: Feature Request
description: Suggest an idea for this project
title: "[Feature Request] Title Here"
labels: []
assignees: []
body:
- type: textarea
id: problem
attributes:
label: Is your feature request related to a problem? Please describe.
description: Please provide a clear and concise description of what the problem is.
placeholder: A common problem users face is...
validations:
required: true
- type: textarea
id: solution
attributes:
label: What is your ideal solution to the problem?
description: Describe a clear and concise description of what you want to happen.
placeholder: My solution to the above problem is...
validations:
required: true
- type: textarea
id: procedure
attributes:
label: How would this feature be used?
description: Describe the basic flow/steps of using this feature.
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Additional Info
description: Any additional info that you think might be useful or relevant to this feature request
- type: checkboxes
id: checks
attributes:
label: Checklist
description: Before submitting your issue, please affirm the following
options:
- label: I have checked [the documentation](https://jmusicbot.com/) to make sure this feature doesn't already exist
required: true
- label: I have searched for [similar feature requests](https://github.com/jagrosh/MusicBot/labels/enhancement)
required: true
- label: I have confirmed that my requests is **not** on the [list of things that will not be added](https://jmusicbot.com/things-that-wont-be-added)
required: true
- label: "I am running the latest version of the bot: ![Release](https://img.shields.io/github/release/jagrosh/MusicBot.svg)"
required: true
+69
View File
@@ -0,0 +1,69 @@
name: Make Release
on:
workflow_dispatch:
inputs:
version_number:
description: 'Version Number'
required: true
type: string
info:
description: 'Description of this Release'
required: true
type: string
jobs:
build_jar:
name: Build Jar
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up JDK 8
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'adopt'
cache: maven
- name: Set Version
uses: datamonsters/replace-action@v2
with:
files: 'pom.xml'
replacements: 'Snapshot=${{ github.event.inputs.version_number }}'
- name: Build with Maven
run: mvn --batch-mode --update-snapshots verify
- name: Rename jar
run: mv target/*-All.jar JMusicBot-${{ github.event.inputs.version_number }}.jar
- name: Upload jar
uses: actions/upload-artifact@v3
with:
name: jar
path: JMusicBot-${{ github.event.inputs.version_number }}.jar
if-no-files-found: error
create_release:
name: Create Release
runs-on: ubuntu-latest
needs: build_jar
steps:
- name: Download a Build Artifact
uses: actions/download-artifact@v3.0.0
with:
name: jar
path: .
- name: Show Artifacts
run: ls -R
- name: Create Release
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.event.inputs.version_number }}
name: Version ${{ github.event.inputs.version_number }}
draft: true
prerelease: false
artifacts: "*.jar"
body: |
${{ github.event.inputs.info }}
---
### Setup
https://jmusicbot.com/setup
https://jmusicbot.com/config
# Download *only* the `.jar` file below!
+19
View File
@@ -0,0 +1,19 @@
name: Test
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 8
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'adopt'
cache: maven
- name: Build with Maven
run: mvn --batch-mode --update-snapshots verify
+1
View File
@@ -0,0 +1 @@
jmusicbot.com
+23 -4
View File
@@ -13,7 +13,7 @@
A cross-platform Discord music bot with a clean interface, and that is easy to set up and run yourself!
[![Setup](http://i.imgur.com/VvXYp5j.png)](https://github.com/jagrosh/MusicBot/wiki/Setup)
[![Setup](http://i.imgur.com/VvXYp5j.png)](https://jmusicbot.com/setup)
## Features
* Easy to run (just make sure Java is installed, and run!)
@@ -22,20 +22,39 @@ A cross-platform Discord music bot with a clean interface, and that is easy to s
* Smooth playback
* Server-specific setup for the "DJ" role that can moderate the music
* Clean and beautiful menus
* Channel-topic playback bar
* Supports many sites, including Youtube, Soundcloud, and more
* Supports many online radio/streams
* Supports local files
* Playlist support (both web/youtube, and local)
## Supported sources and formats
JMusicBot supports all sources and formats supported by [lavaplayer](https://github.com/sedmelluq/lavaplayer#supported-formats):
### Sources
* YouTube
* SoundCloud
* Bandcamp
* Vimeo
* Twitch streams
* Local files
* HTTP URLs
### Formats
* MP3
* FLAC
* WAV
* Matroska/WebM (AAC, Opus or Vorbis codecs)
* MP4/M4A (AAC codec)
* OGG streams (Opus, Vorbis and FLAC codecs)
* AAC streams
* Stream playlists (M3U and PLS)
## Example
![Loading Example...](https://i.imgur.com/kVtTKvS.gif)
## Setup
Please see the [Setup Page](https://github.com/jagrosh/MusicBot/wiki/Setup) in the wiki to run this bot yourself!
Please see the [Setup Page](https://jmusicbot.com/setup) to run this bot yourself!
## Questions/Suggestions/Bug Reports
**Please read the [Suggested/Planned Features List](https://github.com/jagrosh/MusicBot/projects/1) before suggesting a feature**. If you'd like to suggest changes to how the bot functions, recommend more customization options, or report bugs, feel free to either open an [Issue](https://github.com/jagrosh/MusicBot/issues) on this repository, or join [my Discord server](https://discord.gg/0p9LSGoRLu6Pet0k). (Note: I will not accept any feature requests that will require additional API keys, nor any non-music features). If you like this bot, be sure to add a star to the libraries that make this possible: [**JDA**](https://github.com/DV8FromTheWorld/JDA) and [**lavaplayer**](https://github.com/sedmelluq/lavaplayer)
**Please read the [Issues List](https://github.com/jagrosh/MusicBot/issues) before suggesting a feature**. If you have a question, need troubleshooting help, or want to brainstorm a new feature, please start a [Discussion](https://github.com/jagrosh/MusicBot/discussions). If you'd like to suggest a feature or report a reproducible bug, please open an [Issue](https://github.com/jagrosh/MusicBot/issues) on this repository. If you like this bot, be sure to add a star to the libraries that make this possible: [**JDA**](https://github.com/DV8FromTheWorld/JDA) and [**lavaplayer**](https://github.com/sedmelluq/lavaplayer)!
## Editing
This bot (and the source code here) might not be easy to edit for inexperienced programmers. The main purpose of having the source public is to show the capabilities of the libraries, to allow others to understand how the bot works, and to allow those knowledgeable about java, JDA, and Discord bot development to contribute. There are many requirements and dependencies required to edit and compile it, and there will not be support provided for people looking to make changes on their own. Instead, consider making a feature request (see the above section). If you choose to make edits, please do so in accordance with the Apache 2.0 License.
+33 -11
View File
@@ -7,15 +7,19 @@
<packaging>jar</packaging>
<repositories>
<repository>
<id>dv8tion</id>
<name>m2-dv8tion</name>
<url>https://m2.dv8tion.net/releases</url>
</repository>
<repository>
<id>central</id>
<name>bintray</name>
<url>https://jcenter.bintray.com</url>
</repository>
<repository>
<id>bintray-sedmelluq-com.sedmelluq</id>
<name>bintray</name>
<url>https://dl.bintray.com/sedmelluq/com.sedmelluq</url>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
@@ -23,29 +27,42 @@
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>4.2.0_214</version>
<version>4.3.0_324</version>
</dependency>
<dependency>
<!-- using a fork of this to fix some issues faster -->
<!-- dependency>
<groupId>com.sedmelluq</groupId>
<artifactId>lavaplayer</artifactId>
<version>1.3.55</version>
</dependency>
<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>
</dependency-->
<dependency>
<groupId>com.jagrosh</groupId>
<artifactId>jda-utilities</artifactId>
<version>3.0.5</version>
<type>pom</type>
</dependency>
<dependency>
<!-- jitpack for now, we need to version this correctly later -->
<dependency>
<groupId>com.github.jagrosh</groupId>
<artifactId>JLyrics</artifactId>
<version>-SNAPSHOT</version>
</dependency>
<!--dependency>
<groupId>com.jagrosh</groupId>
<artifactId>JLyrics</artifactId>
<version>0.4</version>
</dependency>
<version>0.6</version>
</dependency-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
@@ -56,6 +73,11 @@
<artifactId>config</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
+35
View File
@@ -0,0 +1,35 @@
#!/bin/sh
# This will have this script check for a new version of JMusicBot every
# startup (and download it if the latest version isn't currently downloaded)
DOWNLOAD=true
# This will cause the script to run in a loop so that the bot auto-restarts
# when you use the shutdown command
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 \
| sed 's/.*\(http.*\)"/\1/')
FILENAME=$(echo $URL | sed 's/.*\/\([^\/]*\)/\1/')
if [ -f $FILENAME ]; then
echo "Latest version already downloaded (${FILENAME})"
else
curl -L $URL -o $FILENAME
fi
fi
}
run() {
java -Dnogui=true -jar $(ls -t JMusicBot* | head -1)
}
while
download
run
$LOOP
do
continue
done
@@ -18,6 +18,7 @@ package com.jagrosh.jmusicbot;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import com.jagrosh.jmusicbot.audio.AloneInVoiceHandler;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.NowplayingHandler;
import com.jagrosh.jmusicbot.audio.PlayerManager;
@@ -42,6 +43,7 @@ public class Bot
private final PlayerManager players;
private final PlaylistLoader playlists;
private final NowplayingHandler nowplaying;
private final AloneInVoiceHandler aloneInVoiceHandler;
private boolean shuttingDown = false;
private JDA jda;
@@ -58,6 +60,8 @@ public class Bot
this.players.init();
this.nowplaying = new NowplayingHandler(this);
this.nowplaying.init();
this.aloneInVoiceHandler = new AloneInVoiceHandler(this);
this.aloneInVoiceHandler.init();
}
public BotConfig getConfig()
@@ -94,6 +98,11 @@ public class Bot
{
return nowplaying;
}
public AloneInVoiceHandler getAloneInVoiceHandler()
{
return aloneInVoiceHandler;
}
public JDA getJDA()
{
@@ -23,6 +23,7 @@ import com.typesafe.config.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity;
@@ -42,11 +43,10 @@ public class BotConfig
private String token, prefix, altprefix, helpWord, playlistsFolder,
successEmoji, warningEmoji, errorEmoji, loadingEmoji, searchingEmoji;
private boolean stayInChannel, songInGame, npImages, updatealerts, useEval, dbots;
private long owner, maxSeconds;
private long owner, maxSeconds, aloneTimeUntilStop;
private OnlineStatus status;
private Activity game;
private Config aliases;
private Config aliases, transforms;
private boolean valid = false;
@@ -67,7 +67,7 @@ public class BotConfig
if(path.toFile().exists())
{
if(System.getProperty("config.file") == null)
System.setProperty("config.file", System.getProperty("config", "config.txt"));
System.setProperty("config.file", System.getProperty("config", path.toAbsolutePath().toString()));
ConfigFactory.invalidateCaches();
}
@@ -94,8 +94,10 @@ public class BotConfig
updatealerts = config.getBoolean("updatealerts");
useEval = config.getBoolean("eval");
maxSeconds = config.getLong("maxtime");
aloneTimeUntilStop = config.getLong("alonetimeuntilstop");
playlistsFolder = config.getString("playlistsfolder");
aliases = config.getConfig("aliases");
transforms = config.getConfig("transforms");
dbots = owner == 113156185389092864L;
// we may need to write a new config file
@@ -298,6 +300,11 @@ public class BotConfig
{
return FormatUtil.formatTime(maxSeconds * 1000);
}
public long getAloneTimeUntilStop()
{
return aloneTimeUntilStop;
}
public boolean isTooLong(AudioTrack track)
{
@@ -317,4 +324,9 @@ public class BotConfig
return new String[0];
}
}
public Config getTransforms()
{
return transforms;
}
}
@@ -59,8 +59,7 @@ public class JMusicBot
Logger log = LoggerFactory.getLogger("Startup");
// create prompt to handle startup
Prompt prompt = new Prompt("JMusicBot", "Switching to nogui mode. You can manually start in nogui mode by including the -Dnogui=true flag.",
"true".equalsIgnoreCase(System.getProperty("nogui", "false")));
Prompt prompt = new Prompt("JMusicBot", "Switching to nogui mode. You can manually start in nogui mode by including the -Dnogui=true flag.");
// get and check latest version
String version = OtherUtil.checkVersion(prompt);
@@ -123,6 +122,7 @@ public class JMusicBot
new PrefixCmd(bot),
new SetdjCmd(bot),
new SkipratioCmd(bot),
new SettcCmd(bot),
new SetvcCmd(bot),
@@ -173,7 +173,7 @@ public class JMusicBot
{
JDA jda = JDABuilder.create(config.getToken(), Arrays.asList(INTENTS))
.enableCache(CacheFlag.MEMBER_OVERRIDES, CacheFlag.VOICE_STATE)
.disableCache(CacheFlag.ACTIVITY, CacheFlag.CLIENT_STATUS, CacheFlag.EMOTE)
.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)
@@ -24,8 +24,10 @@ import net.dv8tion.jda.api.entities.VoiceChannel;
import net.dv8tion.jda.api.events.ReadyEvent;
import net.dv8tion.jda.api.events.ShutdownEvent;
import net.dv8tion.jda.api.events.guild.GuildJoinEvent;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
import net.dv8tion.jda.api.events.message.guild.GuildMessageDeleteEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,9 +71,9 @@ public class Listener extends ListenerAdapter
{
bot.getThreadpool().scheduleWithFixedDelay(() ->
{
User owner = bot.getJDA().getUserById(bot.getConfig().getOwnerId());
if(owner!=null)
try
{
User owner = bot.getJDA().retrieveUserById(bot.getConfig().getOwnerId()).complete();
String currentVersion = OtherUtil.getCurrentVersion();
String latestVersion = OtherUtil.getLatestVersion();
if(latestVersion!=null && !currentVersion.equalsIgnoreCase(latestVersion))
@@ -80,6 +82,7 @@ public class Listener extends ListenerAdapter
owner.openPrivateChannel().queue(pc -> pc.sendMessage(msg).queue());
}
}
catch(Exception ex) {} // ignored
}, 0, 24, TimeUnit.HOURS);
}
}
@@ -89,7 +92,13 @@ public class Listener extends ListenerAdapter
{
bot.getNowplayingHandler().onMessageDelete(event.getGuild(), event.getMessageIdLong());
}
@Override
public void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event)
{
bot.getAloneInVoiceHandler().onVoiceUpdate(event);
}
@Override
public void onShutdown(ShutdownEvent event)
{
@@ -0,0 +1,98 @@
/*
* Copyright 2021 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.audio;
import com.jagrosh.jmusicbot.Bot;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
*
* @author Michaili K (mysteriouscursor+git@protonmail.com)
*/
public class AloneInVoiceHandler
{
private final Bot bot;
private final HashMap<Long, Instant> aloneSince = new HashMap<>();
private long aloneTimeUntilStop = 0;
public AloneInVoiceHandler(Bot bot)
{
this.bot = bot;
}
public void init()
{
aloneTimeUntilStop = bot.getConfig().getAloneTimeUntilStop();
if(aloneTimeUntilStop > 0)
bot.getThreadpool().scheduleWithFixedDelay(() -> check(), 0, 5, TimeUnit.SECONDS);
}
private void check()
{
Set<Long> toRemove = new HashSet<>();
for(Map.Entry<Long, Instant> entrySet: aloneSince.entrySet())
{
if(entrySet.getValue().getEpochSecond() > Instant.now().getEpochSecond() - aloneTimeUntilStop) continue;
Guild guild = bot.getJDA().getGuildById(entrySet.getKey());
if(guild == null)
{
toRemove.add(entrySet.getKey());
continue;
}
((AudioHandler) guild.getAudioManager().getSendingHandler()).stopAndClear();
guild.getAudioManager().closeAudioConnection();
toRemove.add(entrySet.getKey());
}
toRemove.forEach(id -> aloneSince.remove(id));
}
public void onVoiceUpdate(GuildVoiceUpdateEvent event)
{
if(aloneTimeUntilStop <= 0) return;
Guild guild = event.getEntity().getGuild();
if(!bot.getPlayerManager().hasHandler(guild)) return;
boolean alone = isAlone(guild);
boolean inList = aloneSince.containsKey(guild.getIdLong());
if(!alone && inList)
aloneSince.remove(guild.getIdLong());
else if(alone && !inList)
aloneSince.put(guild.getIdLong(), Instant.now());
}
private boolean isAlone(Guild guild)
{
if(guild.getAudioManager().getConnectedChannel() == null) return false;
return guild.getAudioManager().getConnectedChannel().getMembers().stream()
.noneMatch(x ->
!x.getVoiceState().isDeafened()
&& !x.getUser().isBot());
}
}
@@ -17,6 +17,7 @@ package com.jagrosh.jmusicbot.audio;
import com.jagrosh.jmusicbot.JMusicBot;
import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist;
import com.jagrosh.jmusicbot.settings.RepeatMode;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
@@ -115,11 +116,12 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
return audioPlayer;
}
public long getRequester()
public RequestMetadata getRequestMetadata()
{
if(audioPlayer.getPlayingTrack()==null || audioPlayer.getPlayingTrack().getUserData(Long.class)==null)
return 0;
return audioPlayer.getPlayingTrack().getUserData(Long.class);
if(audioPlayer.getPlayingTrack() == null)
return RequestMetadata.EMPTY;
RequestMetadata rm = audioPlayer.getPlayingTrack().getUserData(RequestMetadata.class);
return rm == null ? RequestMetadata.EMPTY : rm;
}
public boolean playFromDefault()
@@ -154,10 +156,15 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
@Override
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason)
{
RepeatMode repeatMode = manager.getBot().getSettingsManager().getSettings(guildId).getRepeatMode();
// if the track ended normally, and we're in repeat mode, re-add it to the queue
if(endReason==AudioTrackEndReason.FINISHED && manager.getBot().getSettingsManager().getSettings(guildId).getRepeatMode())
if(endReason==AudioTrackEndReason.FINISHED && repeatMode != RepeatMode.OFF)
{
queue.add(new QueuedTrack(track.makeClone(), track.getUserData(Long.class)==null ? 0L : track.getUserData(Long.class)));
QueuedTrack clone = new QueuedTrack(track.makeClone(), track.getUserData(RequestMetadata.class));
if(repeatMode == RepeatMode.ALL)
queue.add(clone);
else
queue.addAt(0, clone);
}
if(queue.isEmpty())
@@ -195,16 +202,17 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
Guild guild = guild(jda);
AudioTrack track = audioPlayer.getPlayingTrack();
MessageBuilder mb = new MessageBuilder();
mb.append(FormatUtil.filter(manager.getBot().getConfig().getSuccess()+" **Now Playing in "+guild.getSelfMember().getVoiceState().getChannel().getName()+"...**"));
mb.append(FormatUtil.filter(manager.getBot().getConfig().getSuccess()+" **Now Playing in "+guild.getSelfMember().getVoiceState().getChannel().getAsMention()+"...**"));
EmbedBuilder eb = new EmbedBuilder();
eb.setColor(guild.getSelfMember().getColor());
if(getRequester() != 0)
RequestMetadata rm = getRequestMetadata();
if(rm.getOwner() != 0L)
{
User u = guild.getJDA().getUserById(getRequester());
User u = guild.getJDA().getUserById(rm.user.id);
if(u==null)
eb.setAuthor("Unknown (ID:"+getRequester()+")", null, null);
eb.setAuthor(rm.user.username + "#" + rm.user.discrim, null, rm.user.avatar);
else
eb.setAuthor(u.getName()+"#"+u.getDiscriminator(), null, u.getEffectiveAvatarUrl());
eb.setAuthor(u.getName() + "#" + u.getDiscriminator(), null, u.getEffectiveAvatarUrl());
}
try
@@ -251,7 +259,7 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
{
if(isMusicPlaying(jda))
{
long userid = getRequester();
long userid = getRequestMetadata().getOwner();
AudioTrack track = audioPlayer.getPlayingTrack();
String title = track.getInfo().title;
if(title==null || title.equals("Unknown Title"))
@@ -20,6 +20,7 @@ import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager;
import com.typesafe.config.Config;
import net.dv8tion.jda.api.entities.Guild;
/**
@@ -37,6 +38,7 @@ public class PlayerManager extends DefaultAudioPlayerManager
public void init()
{
TransformativeAudioSourceManager.createTransforms(bot.getConfig().getTransforms()).forEach(t -> registerSourceManager(t));
AudioSourceManagers.registerRemoteSources(this);
AudioSourceManagers.registerLocalSource(this);
source(YoutubeAudioSourceManager.class).setPlaylistPageCount(10);
@@ -1,5 +1,5 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
* Copyright 2021 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.
@@ -30,19 +30,19 @@ public class QueuedTrack implements Queueable
public QueuedTrack(AudioTrack track, User owner)
{
this(track, owner.getIdLong());
this(track, new RequestMetadata(owner));
}
public QueuedTrack(AudioTrack track, long owner)
public QueuedTrack(AudioTrack track, RequestMetadata rm)
{
this.track = track;
this.track.setUserData(owner);
this.track.setUserData(rm);
}
@Override
public long getIdentifier()
{
return track.getUserData(Long.class);
return track.getUserData(RequestMetadata.class).getOwner();
}
public AudioTrack getTrack()
@@ -53,6 +53,6 @@ public class QueuedTrack implements Queueable
@Override
public String toString()
{
return "`[" + FormatUtil.formatTime(track.getDuration()) + "]` **" + track.getInfo().title + "** - <@" + track.getUserData(Long.class) + ">";
return "`[" + FormatUtil.formatTime(track.getDuration()) + "]` [**" + track.getInfo().title + "**]("+track.getInfo().uri+") - <@" + track.getUserData(RequestMetadata.class).getOwner() + ">";
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2021 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.audio;
import net.dv8tion.jda.api.entities.User;
/**
*
* @author John Grosh (john.a.grosh@gmail.com)
*/
public class RequestMetadata
{
public static final RequestMetadata EMPTY = new RequestMetadata(null);
public final UserInfo user;
public RequestMetadata(User user)
{
this.user = user == null ? null : new UserInfo(user.getIdLong(), user.getName(), user.getDiscriminator(), user.getEffectiveAvatarUrl());
}
public long getOwner()
{
return user == null ? 0L : user.id;
}
public class RequestInfo
{
public final String query, url;
private RequestInfo(String query, String url)
{
this.query = query;
this.url = url;
}
}
public class UserInfo
{
public final long id;
public final String username, discrim, avatar;
private UserInfo(long id, String username, String discrim, String avatar)
{
this.id = id;
this.username = username;
this.discrim = discrim;
this.avatar = avatar;
}
}
}
@@ -0,0 +1,104 @@
/*
* Copyright 2021 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.audio;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager;
import com.sedmelluq.discord.lavaplayer.track.AudioItem;
import com.sedmelluq.discord.lavaplayer.track.AudioReference;
import com.typesafe.config.Config;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author John Grosh (john.a.grosh@gmail.com)
*/
public class TransformativeAudioSourceManager extends YoutubeAudioSourceManager
{
private final static Logger log = LoggerFactory.getLogger(TransformativeAudioSourceManager.class);
private final String name, regex, replacement, selector, format;
public TransformativeAudioSourceManager(String name, Config object)
{
this(name, object.getString("regex"), object.getString("replacement"), object.getString("selector"), object.getString("format"));
}
public TransformativeAudioSourceManager(String name, String regex, String replacement, String selector, String format)
{
this.name = name;
this.regex = regex;
this.replacement = replacement;
this.selector = selector;
this.format = format;
}
@Override
public String getSourceName()
{
return name;
}
@Override
public AudioItem loadItem(AudioPlayerManager apm, AudioReference ar)
{
if(ar.identifier == null || !ar.identifier.matches(regex))
return null;
try
{
String url = ar.identifier.replaceAll(regex, replacement);
Document doc = Jsoup.connect(url).get();
String value = doc.selectFirst(selector).ownText();
String formattedValue = String.format(format, value);
return super.loadItem(apm, new AudioReference(formattedValue, null));
}
catch (PatternSyntaxException ex)
{
log.info(String.format("Invalid pattern syntax '%s' in source '%s'", regex, name));
}
catch (IOException ex)
{
log.warn(String.format("Failed to resolve URL in source '%s': ", name), ex);
}
catch (Exception ex)
{
log.warn(String.format("Exception in source '%s'", name), ex);
}
return null;
}
public static List<TransformativeAudioSourceManager> createTransforms(Config transforms)
{
try
{
return transforms.root().entrySet().stream()
.map(e -> new TransformativeAudioSourceManager(e.getKey(), transforms.getConfig(e.getKey())))
.collect(Collectors.toList());
}
catch (Exception ex)
{
log.warn("Invalid transform ", ex);
return Collections.emptyList();
}
}
}
@@ -70,7 +70,7 @@ public abstract class MusicCommand extends Command
GuildVoiceState userState = event.getMember().getVoiceState();
if(!userState.inVoiceChannel() || userState.isDeafened() || (current!=null && !userState.getChannel().equals(current)))
{
event.replyError("You must be listening in "+(current==null ? "a voice channel" : "**"+current.getName()+"**")+" to use that!");
event.replyError("You must be listening in "+(current==null ? "a voice channel" : current.getAsMention())+" to use that!");
return;
}
@@ -89,7 +89,7 @@ public abstract class MusicCommand extends Command
}
catch(PermissionException ex)
{
event.reply(event.getClient().getError()+" I am unable to connect to **"+userState.getChannel().getName()+"**!");
event.reply(event.getClient().getError()+" I am unable to connect to "+userState.getChannel().getAsMention()+"!");
return;
}
}
@@ -62,7 +62,7 @@ public class SetvcCmd extends AdminCommand
else
{
s.setVoiceChannel(list.get(0));
event.reply(event.getClient().getSuccess()+" Music can now only be played in **"+list.get(0).getName()+"**");
event.reply(event.getClient().getSuccess()+" Music can now only be played in "+list.get(0).getAsMention());
}
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2018 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.commands.AdminCommand;
import com.jagrosh.jmusicbot.settings.Settings;
/**
*
* @author John Grosh (john.a.grosh@gmail.com)
*/
public class SkipratioCmd extends AdminCommand
{
public SkipratioCmd(Bot bot)
{
this.name = "setskip";
this.help = "sets a server-specific skip percentage";
this.arguments = "<0 - 100>";
this.aliases = bot.getConfig().getAliases(this.name);
}
@Override
protected void execute(CommandEvent event)
{
try
{
int val = Integer.parseInt(event.getArgs().endsWith("%") ? event.getArgs().substring(0,event.getArgs().length()-1) : event.getArgs());
if( val < 0 || val > 100)
{
event.replyError("The provided value must be between 0 and 100!");
return;
}
Settings s = event.getClient().getSettingsFor(event.getGuild());
s.setSkipRatio(val / 100.0);
event.replySuccess("Skip percentage has been set to `" + val + "%` of listeners on *" + event.getGuild().getName() + "*");
}
catch(NumberFormatException ex)
{
event.replyError("Please include an integer between 0 and 100 (default is 55). This number is the percentage of listening users that must vote to skip a song.");
}
}
}
@@ -18,6 +18,7 @@ package com.jagrosh.jmusicbot.commands.dj;
import com.jagrosh.jdautilities.command.CommandEvent;
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;
@@ -40,9 +41,9 @@ public class ForceskipCmd extends DJCommand
public void doCommand(CommandEvent event)
{
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
User u = event.getJDA().getUserById(handler.getRequester());
RequestMetadata rm = handler.getRequestMetadata();
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title
+"** (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
+"** "+(rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + rm.user.username + "**)"));
handler.getPlayer().stopTrack();
}
}
@@ -18,6 +18,7 @@ package com.jagrosh.jmusicbot.commands.dj;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.commands.DJCommand;
import com.jagrosh.jmusicbot.settings.RepeatMode;
import com.jagrosh.jmusicbot.settings.Settings;
/**
@@ -31,7 +32,7 @@ public class RepeatCmd extends DJCommand
super(bot);
this.name = "repeat";
this.help = "re-adds music to the queue when finished";
this.arguments = "[on|off]";
this.arguments = "[off|all|single]";
this.aliases = bot.getConfig().getAliases(this.name);
this.guildOnly = true;
}
@@ -40,27 +41,35 @@ public class RepeatCmd extends DJCommand
@Override
protected void execute(CommandEvent event)
{
boolean value;
String args = event.getArgs();
RepeatMode value;
Settings settings = event.getClient().getSettingsFor(event.getGuild());
if(event.getArgs().isEmpty())
if(args.isEmpty())
{
value = !settings.getRepeatMode();
if(settings.getRepeatMode() == RepeatMode.OFF)
value = RepeatMode.ALL;
else
value = RepeatMode.OFF;
}
else if(event.getArgs().equalsIgnoreCase("true") || event.getArgs().equalsIgnoreCase("on"))
else if(args.equalsIgnoreCase("false") || args.equalsIgnoreCase("off"))
{
value = true;
value = RepeatMode.OFF;
}
else if(event.getArgs().equalsIgnoreCase("false") || event.getArgs().equalsIgnoreCase("off"))
else if(args.equalsIgnoreCase("true") || args.equalsIgnoreCase("on") || args.equalsIgnoreCase("all"))
{
value = false;
value = RepeatMode.ALL;
}
else if(args.equalsIgnoreCase("one") || args.equalsIgnoreCase("single"))
{
value = RepeatMode.SINGLE;
}
else
{
event.replyError("Valid options are `on` or `off` (or leave empty to toggle)");
event.replyError("Valid options are `off`, `all` or `single` (or leave empty to toggle between `off` and `all`)");
return;
}
settings.setRepeatMode(value);
event.replySuccess("Repeat mode is now `"+(value ? "ON" : "OFF")+"`");
event.replySuccess("Repeat mode is now `"+value.getUserFriendlyName()+"`");
}
@Override
@@ -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.RepeatMode;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.api.EmbedBuilder;
@@ -56,10 +57,12 @@ public class SettingsCmd extends Command
EmbedBuilder ebuilder = new EmbedBuilder()
.setColor(event.getSelfMember().getColor())
.setDescription("Text Channel: " + (tchan == null ? "Any" : "**#" + tchan.getName() + "**")
+ "\nVoice Channel: " + (vchan == null ? "Any" : "**" + vchan.getName() + "**")
+ "\nVoice Channel: " + (vchan == null ? "Any" : vchan.getAsMention())
+ "\nDJ Role: " + (role == null ? "None" : "**" + role.getName() + "**")
+ "\nCustom Prefix: " + (s.getPrefix() == null ? "None" : "`" + s.getPrefix() + "`")
+ "\nRepeat Mode: **" + (s.getRepeatMode() ? "On" : "Off") + "**"
+ "\nRepeat Mode: " + (s.getRepeatMode() == RepeatMode.OFF
? s.getRepeatMode().getUserFriendlyName()
: "**"+s.getRepeatMode().getUserFriendlyName()+"**")
+ "\nDefault Playlist: " + (s.getDefaultPlaylist() == null ? "None" : "**" + s.getDefaultPlaylist() + "**")
)
.setFooter(event.getJDA().getGuilds().size() + " servers | "
@@ -36,21 +36,29 @@ public class LyricsCmd extends MusicCommand
super(bot);
this.name = "lyrics";
this.arguments = "[song name]";
this.help = "shows the lyrics to the currently-playing song";
this.help = "shows the lyrics of a song";
this.aliases = bot.getConfig().getAliases(this.name);
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
this.bePlaying = true;
}
@Override
public void doCommand(CommandEvent event)
{
event.getChannel().sendTyping().queue();
String title;
if(event.getArgs().isEmpty())
title = ((AudioHandler)event.getGuild().getAudioManager().getSendingHandler()).getPlayer().getPlayingTrack().getInfo().title;
{
AudioHandler sendingHandler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
if (sendingHandler.isMusicPlaying(event.getJDA()))
title = sendingHandler.getPlayer().getPlayingTrack().getInfo().title;
else
{
event.replyError("There must be music playing to use that!");
return;
}
}
else
title = event.getArgs();
event.getChannel().sendTyping().queue();
client.getLyrics(title).thenAccept(lyrics ->
{
if(lyrics == null)
@@ -24,6 +24,7 @@ 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.RepeatMode;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.api.MessageBuilder;
@@ -37,8 +38,6 @@ import net.dv8tion.jda.api.exceptions.PermissionException;
*/
public class QueueCmd extends MusicCommand
{
private final static String REPEAT = "\uD83D\uDD01"; // 🔁
private final Paginator.Builder builder;
public QueueCmd(Bot bot)
@@ -104,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, boolean repeatmode)
private String getQueueTitle(AudioHandler ah, String success, int songslength, long total, RepeatMode repeatmode)
{
StringBuilder sb = new StringBuilder();
if(ah.getPlayer().getPlayingTrack()!=null)
@@ -114,6 +113,6 @@ 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 ? "| " + REPEAT : "").toString());
.append(repeatmode.getEmoji() != null ? "| "+repeatmode.getEmoji() : "").toString());
}
}
@@ -18,6 +18,7 @@ package com.jagrosh.jmusicbot.commands.music;
import com.jagrosh.jdautilities.command.CommandEvent;
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;
@@ -41,7 +42,8 @@ public class SkipCmd extends MusicCommand
public void doCommand(CommandEvent event)
{
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
if(event.getAuthor().getIdLong()==handler.getRequester())
RequestMetadata rm = handler.getRequestMetadata();
if(event.getAuthor().getIdLong() == rm.getOwner())
{
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**");
handler.getPlayer().stopTrack();
@@ -60,13 +62,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 * .55);
msg+= skippers+" votes, "+required+"/"+listeners+" needed]`";
int required = (int)Math.ceil(listeners * bot.getSettingsManager().getSettings(event.getGuild()).getSkipRatio());
msg += skippers + " votes, " + required + "/" + listeners + " needed]`";
if(skippers>=required)
{
User u = event.getJDA().getUserById(handler.getRequester());
msg+="\n"+event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title
+"**"+(handler.getRequester()==0 ? "" : " (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
msg += "\n" + event.getClient().getSuccess() + " Skipped **" + handler.getPlayer().getPlayingTrack().getInfo().title
+ "** " + (rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + rm.user.username + "**)");
handler.getPlayer().stopTrack();
}
event.reply(msg);
@@ -30,6 +30,7 @@ public class Prompt
private final String noguiMessage;
private boolean nogui;
private boolean noprompt;
private Scanner scanner;
public Prompt(String title)
@@ -39,14 +40,15 @@ public class Prompt
public Prompt(String title, String noguiMessage)
{
this(title, noguiMessage, "true".equalsIgnoreCase(System.getProperty("nogui")));
this(title, noguiMessage, "true".equalsIgnoreCase(System.getProperty("nogui")), "true".equalsIgnoreCase(System.getProperty("noprompt")));
}
public Prompt(String title, String noguiMessage, boolean nogui)
public Prompt(String title, String noguiMessage, boolean nogui, boolean noprompt)
{
this.title = title;
this.noguiMessage = noguiMessage == null ? "Switching to nogui mode. You can manually start in nogui mode by including the -Dnogui=true flag." : noguiMessage;
this.nogui = nogui;
this.noprompt = noprompt;
}
public boolean isNoGUI()
@@ -108,6 +110,8 @@ public class Prompt
public String prompt(String content)
{
if(noprompt)
return null;
if(nogui)
{
if(scanner==null)
@@ -46,7 +46,7 @@ public class PlaylistLoader
{
if(folderExists())
{
File folder = new File(config.getPlaylistsFolder());
File folder = new File(OtherUtil.getPath(config.getPlaylistsFolder()).toString());
return Arrays.asList(folder.listFiles((pathname) -> pathname.getName().endsWith(".txt")))
.stream().map(f -> f.getName().substring(0,f.getName().length()-4)).collect(Collectors.toList());
}
@@ -0,0 +1,46 @@
/*
* Copyright 2020 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;
/**
*
* @author Michaili K
*/
public enum RepeatMode
{
OFF(null, "Off"),
ALL("\uD83D\uDD01", "All"), // 🔁
SINGLE("\uD83D\uDD02", "Single"); // 🔂
private final String emoji;
private final String userFriendlyName;
private RepeatMode(String emoji, String userFriendlyName)
{
this.emoji = emoji;
this.userFriendlyName = userFriendlyName;
}
public String getEmoji()
{
return emoji;
}
public String getUserFriendlyName()
{
return userFriendlyName;
}
}
@@ -35,10 +35,11 @@ public class Settings implements GuildSettingsProvider
protected long roleId;
private int volume;
private String defaultPlaylist;
private boolean repeatMode;
private RepeatMode repeatMode;
private String prefix;
public Settings(SettingsManager manager, String textId, String voiceId, String roleId, int volume, String defaultPlaylist, boolean repeatMode, 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)
{
this.manager = manager;
try
@@ -69,9 +70,10 @@ public class Settings implements GuildSettingsProvider
this.defaultPlaylist = defaultPlaylist;
this.repeatMode = repeatMode;
this.prefix = prefix;
this.skipRatio = skipRatio;
}
public Settings(SettingsManager manager, long textId, long voiceId, long roleId, int volume, String defaultPlaylist, boolean repeatMode, String prefix)
public Settings(SettingsManager manager, long textId, long voiceId, long roleId, int volume, String defaultPlaylist, RepeatMode repeatMode, String prefix, double skipRatio)
{
this.manager = manager;
this.textId = textId;
@@ -81,6 +83,7 @@ public class Settings implements GuildSettingsProvider
this.defaultPlaylist = defaultPlaylist;
this.repeatMode = repeatMode;
this.prefix = prefix;
this.skipRatio = skipRatio;
}
// Getters
@@ -109,7 +112,7 @@ public class Settings implements GuildSettingsProvider
return defaultPlaylist;
}
public boolean getRepeatMode()
public RepeatMode getRepeatMode()
{
return repeatMode;
}
@@ -119,6 +122,11 @@ public class Settings implements GuildSettingsProvider
return prefix;
}
public double getSkipRatio()
{
return skipRatio;
}
@Override
public Collection<String> getPrefixes()
{
@@ -156,7 +164,7 @@ public class Settings implements GuildSettingsProvider
this.manager.writeSettings();
}
public void setRepeatMode(boolean mode)
public void setRepeatMode(RepeatMode mode)
{
this.repeatMode = mode;
this.manager.writeSettings();
@@ -167,4 +175,10 @@ public class Settings implements GuildSettingsProvider
this.prefix = prefix;
this.manager.writeSettings();
}
public void setSkipRatio(double skipRatio)
{
this.skipRatio = skipRatio;
this.manager.writeSettings();
}
}
@@ -19,7 +19,6 @@ 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.Paths;
import java.util.HashMap;
import net.dv8tion.jda.api.entities.Guild;
import org.json.JSONException;
@@ -32,6 +31,7 @@ import org.slf4j.LoggerFactory;
*/
public class SettingsManager implements GuildSettingsManager
{
private final static double SKIP_RATIO = .55;
private final HashMap<Long,Settings> settings;
public SettingsManager()
@@ -41,14 +41,21 @@ public class SettingsManager implements GuildSettingsManager
JSONObject loadedSettings = new JSONObject(new String(Files.readAllBytes(OtherUtil.getPath("serversettings.json"))));
loadedSettings.keySet().forEach((id) -> {
JSONObject o = loadedSettings.getJSONObject(id);
// Legacy version support: On versions 0.3.3 and older, the repeat mode was represented as a boolean.
if (!o.has("repeat_mode") && o.has("repeat") && o.getBoolean("repeat"))
o.put("repeat_mode", RepeatMode.ALL);
settings.put(Long.parseLong(id), new Settings(this,
o.has("text_channel_id") ? o.getString("text_channel_id") : null,
o.has("voice_channel_id")? o.getString("voice_channel_id"): null,
o.has("dj_role_id") ? o.getString("dj_role_id") : null,
o.has("volume") ? o.getInt("volume") : 100,
o.has("default_playlist")? o.getString("default_playlist"): null,
o.has("repeat") ? o.getBoolean("repeat") : false,
o.has("prefix") ? o.getString("prefix") : null));
o.has("text_channel_id") ? o.getString("text_channel_id") : null,
o.has("voice_channel_id")? o.getString("voice_channel_id") : null,
o.has("dj_role_id") ? o.getString("dj_role_id") : null,
o.has("volume") ? o.getInt("volume") : 100,
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));
});
} catch(IOException | JSONException e) {
LoggerFactory.getLogger("Settings").warn("Failed to load server settings (this is normal if no settings have been set yet): "+e);
@@ -74,7 +81,7 @@ public class SettingsManager implements GuildSettingsManager
private Settings createDefaultSettings()
{
return new Settings(this, 0, 0, 0, 100, null, false, null);
return new Settings(this, 0, 0, 0, 100, null, RepeatMode.OFF, null, SKIP_RATIO);
}
protected void writeSettings()
@@ -93,10 +100,12 @@ public class SettingsManager implements GuildSettingsManager
o.put("volume",s.getVolume());
if(s.getDefaultPlaylist() != null)
o.put("default_playlist", s.getDefaultPlaylist());
if(s.getRepeatMode())
o.put("repeat", true);
if(s.getRepeatMode()!=RepeatMode.OFF)
o.put("repeat_mode", s.getRepeatMode());
if(s.getPrefix() != null)
o.put("prefix", s.getPrefix());
if(s.getSkipRatio() != SKIP_RATIO)
o.put("skip_ratio", s.getSkipRatio());
obj.put(Long.toString(key), o);
});
try {
@@ -74,7 +74,7 @@ public class FormatUtil {
{
String out = " Multiple voice channels found matching \""+query+"\":";
for(int i=0; i<6 && i<list.size(); i++)
out+="\n - "+list.get(i).getName()+" (ID:"+list.get(i).getId()+")";
out+="\n - "+list.get(i).getAsMention()+" (ID:"+list.get(i).getId()+")";
if(list.size()>6)
out+="\n**And "+(list.size()-6)+" more...**";
return out;
@@ -52,17 +52,17 @@ public class OtherUtil
*/
public static Path getPath(String path)
{
Path result = Paths.get(path);
// special logic to prevent trying to access system32
if(path.toLowerCase().startsWith(WINDOWS_INVALID_PATH))
if(result.toAbsolutePath().toString().toLowerCase().startsWith(WINDOWS_INVALID_PATH))
{
String filename = path.substring(WINDOWS_INVALID_PATH.length());
try
{
path = new File(JMusicBot.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile().getPath() + File.separator + filename;
result = Paths.get(new File(JMusicBot.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile().getPath() + File.separator + path);
}
catch(URISyntaxException ex) {}
}
return Paths.get(path);
return result;
}
/**
Binary file not shown.
Binary file not shown.
+24 -4
View File
@@ -28,7 +28,6 @@ owner = 0 // OWNER ID
// The prefix is used to control the commands
// If you use !!, the play command will be !!play
// If you do not set this, the prefix will be a mention of the bot (@Botname play)
// If you make this blank, the bot will not use a prefix
prefix = "@mention"
@@ -100,6 +99,13 @@ stayinchannel = false
maxtime = 0
// 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.
alonetimeuntilstop = 0
// This sets an alternative folder to be used as the Playlists folder
// This can be a relative or absolute path
@@ -113,7 +119,7 @@ updatealerts=true
// Changing this changes the lyrics provider
// Currently available providers: "A-Z Lyrics", "Genius", "MusicMatch"
// Currently available providers: "A-Z Lyrics", "Genius", "MusicMatch", "LyricsFreak"
// At the time of writing, I would recommend sticking with A-Z Lyrics or MusicMatch,
// as Genius tends to have a lot of non-song results and you might get something
// completely unrelated to what you want.
@@ -151,6 +157,7 @@ aliases {
// Admin commands
prefix = [ setprefix ]
setdj = []
setskip = [ setskippercent, skippercent, setskipratio ]
settc = []
setvc = []
@@ -162,10 +169,23 @@ aliases {
playnext = []
repeat = []
skipto = [ jumpto ]
stop = []
stop = [ leave ]
volume = [ vol ]
}
// Transforms are used to modify specific play inputs and convert them to different kinds of inputs
// These are quite complicated to use, and have limited use-cases, but in theory allow for rough
// whitelists or blacklists, roundabout loading from some sources, and customization of how things are
// requested.
//
// These are NOT EASY to set up, so if you want to use these, you'll need to look through the code
// for how they work and what fields are needed. Also, it's possible this feature might get entirely
// removed in the future if I find a better way to do this.
transforms = {}
// If you set this to true, it will enable the eval command for the bot owner. This command
// allows the bot owner to run arbitrary code from the bot's account.
//
@@ -180,4 +200,4 @@ aliases {
eval=false
/// END OF JMUSICBOT CONFIG ///
/// END OF JMUSICBOT CONFIG ///