mirror of
https://github.com/chrisb09/MusicBot.git
synced 2026-11-06 01:12:40 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5f82f1005
|
||
|
|
d0de6c5db9
|
||
|
|
a0393d1bba | ||
|
|
3886ccbe65 | ||
|
|
fddde60e23 | ||
|
|
838ff69090 | ||
|
|
66cc718d30 | ||
|
|
ef134be9c0 | ||
|
|
c25cda721a | ||
|
|
f95b50af89 | ||
|
|
1e6904d329 | ||
|
|
5ff7b2f28e | ||
|
|
28dfdcf680 | ||
|
|
c5073b8755 | ||
|
|
5501f02a97 | ||
|
|
66e207eabe | ||
|
|
db3a57d517 | ||
|
|
81fa3fed25 | ||
|
|
9484c373ff | ||
|
|
860822d520 | ||
|
|
a0c313047f | ||
|
|
db69ea6787 | ||
|
|
5dba2c355b | ||
|
|
9fd246ac25 | ||
|
|
6a15bbb0df | ||
|
|
16bc176685 | ||
|
|
50e8bff831 | ||
|
|
9c012eb169 | ||
|
|
87771df1fb | ||
|
|
5d35f5bb4c | ||
|
|
ecd8cdf77b | ||
|
|
39e5bf7786 | ||
|
|
650a9ea996 | ||
|
|
0e0c05acf9 | ||
|
|
2e52ecac3b | ||
|
|
450ee7acb8 | ||
|
|
a7b56b67ab | ||
|
|
fedb97e1c7 | ||
|
|
68f951dc63 | ||
|
|
01eee8813a | ||
|
|
3df09a4cd2 | ||
|
|
0084ba7baf | ||
|
|
35e756df9b | ||
|
|
426e28ed8c | ||
|
|
dd72baca0d | ||
|
|
ac4e09cbd9 | ||
|
|
fd01d94cf2 | ||
|
|
95f72937c8 | ||
|
|
792cb7a8b7 | ||
|
|
03ba83f06b | ||
|
|
3b3a483b17 | ||
|
|
1874252ff6 | ||
|
|
aa8e430ff1 | ||
|
|
204bb6f04a | ||
|
|
9ec21bf329 | ||
|
|
b419191bad | ||
|
|
aa89145ae8 | ||
|
|
293733f0e7 | ||
|
|
7be02b1fe2 | ||
|
|
1ac46d9ec5 | ||
|
|
026ce87440 | ||
|
|
e5f34cc900 | ||
|
|
c9a2477f86 | ||
|
|
61a18a1db7 | ||
|
|
7fb44e296b | ||
|
|
4ae9f05776 | ||
|
|
84facb8b82 | ||
|
|
070938b326 | ||
|
|
58a9e76edb | ||
|
|
96ffcd8291 | ||
|
|
844a1bb75a | ||
|
|
6094e49ab2 | ||
|
|
88ce3e1149 | ||
|
|
463bf75783 | ||
|
|
348fc3ceec | ||
|
|
30eafb9239 | ||
|
|
c95f2248f3 | ||
|
|
b80b0c8459 | ||
|
|
4a8392fdb9 | ||
|
|
9d6584525c | ||
|
|
909b655f5c | ||
|
|
859e5c5862 | ||
|
|
43b63f1ba8 | ||
|
|
eca2563abf | ||
|
|
e6bfd1841f | ||
|
|
f140b41bf5 | ||
|
|
8733899aa0 | ||
|
|
e5fd05e601 | ||
|
|
6a5a9c7a75 | ||
|
|
8557f7ab8e | ||
|
|
48e62f1a50 | ||
|
|
6f12c33781 | ||
|
|
0afb3db269 | ||
|
|
81322ef993 | ||
|
|
b3ffbdba7d | ||
|
|
801b3769a1 | ||
|
|
49c3ec7960 | ||
|
|
6752695b43 | ||
|
|
bb17c984a2 | ||
|
|
a7807b96e2 | ||
|
|
2e9dd5df49 | ||
|
|
1ad16181d0 | ||
|
|
bb53bad1ff | ||
|
|
8a1066cc87 | ||
|
|
23c2dfa78e | ||
|
|
18bf6ac0f6 | ||
|
|
fd6de60fc7 | ||
|
|
4c8ff655d8 | ||
|
|
ac8661c904 | ||
|
|
4f926baf76 | ||
|
|
c5ecf89852 | ||
|
|
a52103620a | ||
|
|
fa5e33e2a5 | ||
|
|
781a94ea91 | ||
|
|
ff1ede9d12 | ||
|
|
90d9a1631a | ||
|
|
c4502bbe16 | ||
|
|
2d4f2671c9 | ||
|
|
e595de2033 | ||
|
|
53875f2791 | ||
|
|
cd7e643228 | ||
|
|
0e1f0d3bfa | ||
|
|
2c76164f6d | ||
|
|
788cd40f2c | ||
|
|
1e12e4e01d | ||
|
|
c4cbfe2a11 | ||
|
|
bbed9e8990 | ||
|
|
354a0b3def | ||
|
|
2faef67b30 | ||
|
|
aee076c854 | ||
|
|
b76f6fa07c | ||
|
|
ec4e9e624c | ||
|
|
a7be2c4602 | ||
|
|
6007d3e99b | ||
|
|
a17b2f20ee | ||
|
|
4375d4caa8 | ||
|
|
c22807bcbb | ||
|
|
baf629a910 | ||
|
|
c4e11d682e | ||
|
|
041ab27b94 | ||
|
|
85aebe8a89 | ||
|
|
3c306a8d7a | ||
|
|
cadaf94cf7 | ||
|
|
15a47a7b4d | ||
|
|
92fca22059 | ||
|
|
0cdc0d2d34 | ||
|
|
1d1dfe103e | ||
|
|
ab39d2cd48 | ||
|
|
32d165d1b3 | ||
|
|
8457f944cd | ||
|
|
c404e92932 | ||
|
|
6af86cae5d | ||
|
|
9421730229 | ||
|
|
b76e31c7e2 | ||
|
|
6056ff2c13 | ||
|
|
8c92a7a75a | ||
|
|
e942f3ef21 | ||
|
|
157da6c0da | ||
|
|
03b909a3f5 | ||
|
|
e08c1491c2 | ||
|
|
95de05ff2d | ||
|
|
e242fa9557 | ||
|
|
b83028fb97 | ||
|
|
7273b5bf42 | ||
|
|
6f6064521b | ||
|
|
48263ec60a | ||
|
|
c6e5b8cbda | ||
|
|
3b8892d2c0 | ||
|
|
54cb757dc5 | ||
|
|
c4de816c7c | ||
|
|
ccb421e110 | ||
|
|
23334e4253 | ||
|
|
826aec2492 | ||
|
|
c397afcf8d | ||
|
|
95c07eab8e | ||
|
|
58e0e5737d | ||
|
|
84bc57e3cf | ||
|
|
67f6169e61 | ||
|
|
6f7c47757b | ||
|
|
0a353e0fa3 | ||
|
|
ddf95da19f | ||
|
|
d5bb16d143 | ||
|
|
d47cfee8bf | ||
|
|
1ee089e7ad | ||
|
|
7255e4b6db | ||
|
|
4e314da86d | ||
|
|
ec06cf10e9 | ||
|
|
ad972185d4 | ||
|
|
bc64b45d5c | ||
|
|
699c2a0e6e | ||
|
|
ebf6e43084 | ||
|
|
6527afb88c | ||
|
|
18e142ed88 | ||
|
|
797d306a4e | ||
|
|
267bf71d47 | ||
|
|
88a89f2178 | ||
|
|
8d2f60eba5 | ||
|
|
b4c9a125e5 | ||
|
|
36c53cfd41 | ||
|
|
d1223e9795 | ||
|
|
cebdbc3aa5 | ||
|
|
be4f235071 | ||
|
|
e8df9e9523 | ||
|
|
9a79308065 | ||
|
|
7a55e76a35 | ||
|
|
c92c55ca52 | ||
|
|
2bb43b57ba | ||
|
|
403a43530f | ||
|
|
51f9024498 | ||
|
|
9f7ad33707 | ||
|
|
fa5241490f | ||
|
|
27fd75aa67 | ||
|
|
c5a583b96f | ||
|
|
f1e301b48e | ||
|
|
61d4c51276 | ||
|
|
cbdbe7bf4b | ||
|
|
baa3a60b05 | ||
|
|
338dafca0c | ||
|
|
b1dddb649b | ||
|
|
3eb284da55 | ||
|
|
5fb969283d | ||
|
|
ae8b5cfe90 | ||
|
|
b6fa7de7e2 | ||
|
|
a4f13ca2de | ||
|
|
5172deb12d | ||
|
|
179f447a70 | ||
|
|
f4a937cc6b | ||
|
|
0b4c6c9af1 | ||
|
|
f17058a344 | ||
|
|
4fcf86a5d6 | ||
|
|
94362d640b | ||
|
|
7192d55732 | ||
|
|
11c9b00510 | ||
|
|
4579d6ca09 | ||
|
|
c3a9937c90 | ||
|
|
8650f82210 | ||
|
|
3ba13d2daa | ||
|
|
dace101c30 | ||
|
|
05e8ec6814 | ||
|
|
646ed6e9b1 | ||
|
|
a501b00ab7 | ||
|
|
6ae683b749 | ||
|
|
235af8c8c6 | ||
|
|
33e4e8e5ce | ||
|
|
20b23a3b0d | ||
|
|
d616c1e386 | ||
|
|
e6f2b23ca9 | ||
|
|
fbfea7ab1f | ||
|
|
bc85fb9597 | ||
|
|
a34f06669d | ||
|
|
6508c2b62c | ||
|
|
fb53b9e8ea | ||
|
|
75abab2832 | ||
|
|
13d9fa87d8 | ||
|
|
c0d40b2594 | ||
|
|
327cfe7d21 | ||
|
|
b4e83fd3f0 | ||
|
|
c902c1eace | ||
|
|
39a787c3c9 | ||
|
|
cdc20ce500 | ||
|
|
7dcd7badb3 | ||
|
|
1a0a73ac7c | ||
|
|
eb1fb776a5 | ||
|
|
82349d380b | ||
|
|
3c5deca55e | ||
|
|
e1b9e0165a | ||
|
|
2e701d0d23 | ||
|
|
259e3d716b | ||
|
|
43398449c7 | ||
|
|
e7f00fe088 | ||
|
|
1a9364acae | ||
|
|
19234998f1 | ||
|
|
31037775d1 | ||
|
|
e0b1bb0d06 | ||
|
|
1f810aa36f | ||
|
|
e7a8cec0b4 | ||
|
|
ae07ac2598 | ||
|
|
d75098fdd9 | ||
|
|
ac87cc13d7 | ||
|
|
34bffe0148 | ||
|
|
15f3cf07a8 | ||
|
|
c0f0d45138 | ||
|
|
2eddaccb86 | ||
|
|
87b2f4f93a | ||
|
|
88970314ad | ||
|
|
0de4e2afcc | ||
|
|
2671ac18c8 | ||
|
|
68a992c79e | ||
|
|
9bf233ec79 | ||
|
|
b41dcded98 | ||
|
|
b6239ed608 | ||
|
|
9dfe613921 | ||
|
|
865119e2ae | ||
|
|
c616f9c92a | ||
|
|
2737a85a61 | ||
|
|
c88979a6dd | ||
|
|
a6f7c9b3ef | ||
|
|
2aac68b412 | ||
|
|
e1b07868fd | ||
|
|
aaec886b81 | ||
|
|
a7a4b55011 | ||
|
|
bcb7e03aac | ||
|
|
c49a559e34 | ||
|
|
af98eb5e01 | ||
|
|
e47c3efc58 | ||
|
|
058c3e18d2 | ||
|
|
38ae19cbec | ||
|
|
2eeffb374f | ||
|
|
e478fa43c8 | ||
|
|
1093f92d67 | ||
|
|
6148dc6a89 | ||
|
|
a20ade9016 | ||
|
|
fbbb7bd3a7 | ||
|
|
0c44160023 | ||
|
|
13427cdab7 | ||
|
|
d0ffc0308c | ||
|
|
fe62e03367 | ||
|
|
5deb41c651 | ||
|
|
6b454c299d | ||
|
|
dddf4c8e38 | ||
|
|
0912de74e9 | ||
|
|
3752e5b9e0 | ||
|
|
ae62d5771f | ||
|
|
434749a250 | ||
|
|
cb9644d694 | ||
|
|
8f7e53a37c | ||
|
|
b158e70495 | ||
|
|
ebed4f972a |
No files matched your search
@@ -0,0 +1,52 @@
|
||||
# Java Maven CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-java/ for more details
|
||||
#
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
# specify the version you desire here
|
||||
- image: cimg/openjdk:11.0
|
||||
|
||||
# Specify service dependencies here if necessary
|
||||
# CircleCI maintains a library of pre-built images
|
||||
# documented at https://circleci.com/docs/2.0/circleci-images/
|
||||
# - image: circleci/postgres:9.4
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
environment:
|
||||
# Customize the JVM maximum heap limit
|
||||
MAVEN_OPTS: -Xmx3200m
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "pom.xml" }}
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- v1-dependencies-
|
||||
|
||||
- run: mvn dependency:go-offline
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
key: v1-dependencies-{{ checksum "pom.xml" }}
|
||||
|
||||
# run tests!
|
||||
- run: mvn integration-test
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build:
|
||||
jobs:
|
||||
- build:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- gh-pages
|
||||
- gh-pages-generated
|
||||
@@ -0,0 +1 @@
|
||||
.gitea/CODEOWNERS linguist-detectable=false
|
||||
@@ -0,0 +1,3 @@
|
||||
# Pull requests request review from the eligible staff team(s).
|
||||
# One approval from either an Admin or Developer satisfies branch protection.
|
||||
* @trueog/Admins @trueog/Developers
|
||||
@@ -0,0 +1,9 @@
|
||||
/nbproject/
|
||||
/target/
|
||||
/Playlists/
|
||||
/test/
|
||||
*.json
|
||||
*.txt
|
||||
nb*.xml
|
||||
dockerfile
|
||||
.*!.mvn/
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
wrapperVersion=3.3.4
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||
@@ -1,26 +1,103 @@
|
||||
# JMusicBot
|
||||
A Discord music bot with a clean interface, and that is easy to set up and run yourself
|
||||
<img align="right" src="https://i.imgur.com/zrE80HY.png" height="200" width="200">
|
||||
|
||||
[](https://github.com/jagrosh/MusicBot/wiki/Setup)
|
||||
# JMusicBot (TrueOG Network fork)
|
||||
|
||||
# Features
|
||||
* Easy to run (just make sure Java is installed, and run!)
|
||||
* Fast loading of songs
|
||||
* No external keys needed (besides a Discord Bot token)
|
||||
* 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)
|
||||
A self-hosted Discord music bot, forked by the [TrueOG Network](https://true-og.net) from [chrisb09/MusicBot](https://github.com/chrisb09/MusicBot), which is in turn a modernised fork of [jagrosh/MusicBot](https://github.com/jagrosh/MusicBot).
|
||||
|
||||
# Setup
|
||||
Please see the [Setup Page](https://github.com/jagrosh/MusicBot/wiki/Setup) in the wiki to run this bot yourself!
|
||||
The upstream fork keeps the bot working on current Discord, JDA, and YouTube; this fork keeps it running on TrueOG's infrastructure.
|
||||
|
||||
# Questions/Suggestions/Bug Reports
|
||||
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). 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)
|
||||
## TrueOG Network Fork Changes
|
||||
|
||||
# Example
|
||||

|
||||
- Maven Wrapper (`./mvnw`) so builds use a pinned Maven version instead of whatever is installed
|
||||
- Selenium bumped to 4.49.0 with the CDP v153 devtools module, so YouTube PO-token extraction works with current Chromium
|
||||
- `maven-shade-plugin` now merges `META-INF/services`, so bundled `CdpInfo` (and other SPI) registrations are no longer silently dropped from the fat jar
|
||||
- YouTube PO-token capture handles the gzip-compressed `/youtubei/v1/player` request body that YouTube web client 2.20260918+ sends, and logs a warning instead of silently timing out when the body cannot be parsed
|
||||
|
||||
## Inherited From Upstream (chrisb09)
|
||||
|
||||
- JDA 6 support and current dependency updates
|
||||
- DAVE voice encryption support for current Discord voice requirements
|
||||
- Optional YouTube account login, PO token, and visitor-data support
|
||||
- Optional per-channel playback status message
|
||||
- Optional JDBC/SQLite playback analytics logging
|
||||
- Regular [lavaplayer](https://mvnrepository.com/artifact/dev.arbjerg/lavaplayer)/[lavalink](https://maven.lavalink.dev/#/releases/dev/lavalink/youtube/common) bumps
|
||||
|
||||
## Quick Links
|
||||
|
||||
- Setup guide: [docs/setup.md](/docs/setup.md)
|
||||
- Upstream fork changelog: [docs/fork-changelog.md](/docs/fork-changelog.md)
|
||||
|
||||
## Features
|
||||
|
||||
- Easy self-hosting
|
||||
- Fast song loading
|
||||
- Smooth playback
|
||||
- DJ role and server-specific settings
|
||||
- Queue management and playlist support
|
||||
- Support for many sources, streams, and local files
|
||||
|
||||
## 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
|
||||
- MP4/M4A
|
||||
- OGG streams
|
||||
- AAC streams
|
||||
- Stream playlists such as M3U and PLS
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
./mvnw clean package
|
||||
```
|
||||
|
||||
The runnable jar is written to `target/JMusicBot-<version>-All.jar`.
|
||||
|
||||
## Setup Notes
|
||||
|
||||
1. Create a Discord bot and get its token, see [here](https://jmusicbot.com/getting-a-bot-token/).
|
||||
2. Enable the `MESSAGE_CONTENT` intent in the Discord Developer Portal [here](https://discord.com/developers/applications) ([Wiki link](https://jda.wiki/using-jda/gateway-intents-and-member-cache-policy/)).
|
||||
3. Configure `config.txt`.
|
||||
4. Run the jar: `java -Dnogui=true -jar target/JMusicBot-<version>-All.jar`
|
||||
|
||||
For the detailed version, see [docs/setup.md](/docs/setup.md).
|
||||
|
||||
## YouTube Playback
|
||||
|
||||
YouTube requires a PO token, which the bot obtains at startup by driving a headless Chromium through Selenium. Chromium must be installed, and the bundled Selenium CDP module must match the installed Chromium major version (currently v153 — see `pom.xml`). If Chromium is upgraded past that, bump `selenium-java` and the `selenium-devtools-vNNN` exclusions in `pom.xml` and rebuild.
|
||||
|
||||
Google account login is also supported as a fallback:
|
||||
|
||||
```conf
|
||||
youtubeoauth2=true
|
||||
```
|
||||
|
||||
Do not use your main Google account. Use a burner or otherwise disposable account instead. Details are in [docs/setup.md](/docs/setup.md).
|
||||
|
||||
## DAVE / Voice Encryption
|
||||
|
||||
Discord requires DAVE for voice on modern API versions. The bot configures `libdave-jvm` during startup and bundles native binaries for common Linux, Windows, and macOS targets.
|
||||
|
||||
If you deploy on an unsupported architecture, add the matching native dependency in [pom.xml](/pom.xml).
|
||||
|
||||
## Upstream And Related Work
|
||||
|
||||
This fork incorporates ideas and patches related to:
|
||||
|
||||
- [PR-1670](https://github.com/jagrosh/MusicBot/pull/1670)
|
||||
- [PR-1703](https://github.com/jagrosh/MusicBot/pull/1703)
|
||||
- [PR-1772](https://github.com/jagrosh/MusicBot/pull/1772)
|
||||
@@ -0,0 +1,57 @@
|
||||
# Fork Changelog
|
||||
|
||||
This file tracks fork-specific changes that are useful to operators and contributors. It is not meant to replace commit history, but to summarize what changed at a higher level.
|
||||
|
||||
## 0.6.3
|
||||
|
||||
- Added JDBC-backed playback analytics logging.
|
||||
- Added SQLite as the default logging backend.
|
||||
- Logged queue, playback, listener, search, skip, and command activity.
|
||||
- Added `dblog.*` config settings to enable and configure logging.
|
||||
|
||||
Config:
|
||||
|
||||
```conf
|
||||
dblog.enabled = true
|
||||
dblog.jdbc_url = "jdbc:sqlite:musicbot.db"
|
||||
dblog.user = ""
|
||||
dblog.password = ""
|
||||
```
|
||||
|
||||
By default, logging is disabled.
|
||||
|
||||
## 0.6.2
|
||||
|
||||
- Fixed several status-message race conditions and repost issues.
|
||||
- Added debug logging around status-message creation, editing, deletion, and repost flow.
|
||||
- Added a config toggle to disable the status-message feature entirely.
|
||||
|
||||
Config:
|
||||
|
||||
```conf
|
||||
statusmessages = true
|
||||
```
|
||||
|
||||
Set it to `false` to disable the feature.
|
||||
|
||||
## 0.6.1
|
||||
|
||||
- Bumped the fork version after the initial status-message work had landed.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
- Updated the bot for modern JDA and Discord requirements.
|
||||
- Added DAVE voice support required for current Discord voice playback.
|
||||
- Continued dependency refreshes for lavaplayer and related components.
|
||||
- Carried forward work related to YouTube login, PO token handling, and buildability on the newer stack.
|
||||
|
||||
Imported work included in this fork:
|
||||
|
||||
- PR-1670, support for logging into a YouTube account, is included from `0.4.5` onward.
|
||||
- PR-1703, changes to allow building the project from source more easily, is included from `0.4.5` onward.
|
||||
- `jagrosh#1772`, YouTube PO token and IP rotation support, is included from `0.4.7` onward.
|
||||
|
||||
## Notes
|
||||
|
||||
- This fork may move faster than upstream in areas related to Discord API compatibility and deployment practicality.
|
||||
- When reporting issues, mention the exact fork version because behavior around playback, status messages, and logging has changed across recent releases.
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
# Setup Guide
|
||||
|
||||
This guide covers the normal setup path and the main configuration points that matter on this fork.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Java 11 or newer
|
||||
- A Discord bot token
|
||||
- A Discord application with the bot invited to your server
|
||||
|
||||
## Discord Setup
|
||||
|
||||
1. Create a bot in the [Discord Developer Portal](https://discord.com/developers/applications).
|
||||
2. Copy the bot token.
|
||||
3. Enable the `MESSAGE_CONTENT` intent.
|
||||
|
||||
This intent is required for command-based bots like this one.
|
||||
|
||||
## Privileged Intent Notes
|
||||
|
||||
This fork moved from JDA 4 to JDA 6, so `MESSAGE_CONTENT` is now an important operational requirement for the classic text-command flow.
|
||||
|
||||
Keep in mind:
|
||||
|
||||
- If the bot joins more than 100 servers, this intent must be enabled in the [Discord Developer Portal](https://discord.com/developers/applications).
|
||||
- `MESSAGE_CONTENT` is a privileged intent.
|
||||
- Discord verification requirements become relevant as the bot grows.
|
||||
- The related portal controls historically only become visible once the application is already in a fairly large number of servers.
|
||||
|
||||
For the broader Discord/JDA behavior, see the [JDA gateway intents documentation](https://jda.wiki/using-jda/gateway-intents-and-member-cache-policy/).
|
||||
|
||||
## First Configuration
|
||||
|
||||
The bot reads from `config.txt` using the values defined in [reference.conf](/git/MusicBot/src/main/resources/reference.conf).
|
||||
|
||||
The minimum settings you should care about first are:
|
||||
|
||||
```conf
|
||||
token = "YOUR_BOT_TOKEN"
|
||||
owner = 123456789012345678
|
||||
prefix = "@mention"
|
||||
```
|
||||
|
||||
Useful fork-specific options:
|
||||
|
||||
```conf
|
||||
youtubeoauth2 = true
|
||||
statusmessages = true
|
||||
dblog.enabled = false
|
||||
```
|
||||
|
||||
You may also need these for YouTube PO token support:
|
||||
|
||||
```conf
|
||||
ytpotoken = "PO_TOKEN_HERE"
|
||||
ytvisitordata = "VISITOR_DATA_HERE"
|
||||
```
|
||||
|
||||
## Running The Bot
|
||||
|
||||
Build:
|
||||
|
||||
```bash
|
||||
./mvnw clean package
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
java -jar target/JMusicBot-*-All.jar
|
||||
```
|
||||
|
||||
This fork includes changes to keep source builds practical on the newer stack as well.
|
||||
|
||||
## YouTube Login
|
||||
|
||||
If YouTube playback is unreliable, enable:
|
||||
|
||||
```conf
|
||||
youtubeoauth2 = true
|
||||
```
|
||||
|
||||
After startup, the bot prints instructions and sends the owner a DM with the authorization flow.
|
||||
|
||||
Important:
|
||||
|
||||
- Do not use your main Google account.
|
||||
- Use a burner or otherwise disposable account.
|
||||
|
||||
If you are starting from a clean config, this option should already exist with a default of `false`.
|
||||
|
||||
## YouTube PO Tokens And Visitor Data
|
||||
|
||||
This fork also supports explicit YouTube PO token and visitor-data configuration.
|
||||
|
||||
Config:
|
||||
|
||||
```conf
|
||||
ytpotoken = "PO_TOKEN_HERE"
|
||||
ytvisitordata = "VISITOR_DATA_HERE"
|
||||
```
|
||||
|
||||
You can generate these values with:
|
||||
|
||||
- [iv-org/youtube-trusted-session-generator](https://github.com/iv-org/youtube-trusted-session-generator)
|
||||
|
||||
This is useful when normal YouTube playback is not enough and you need a more explicit trusted-session style setup.
|
||||
|
||||
## DAVE / Voice Support
|
||||
|
||||
This fork includes DAVE support for current Discord voice requirements. In normal supported environments, nothing extra is required beyond using the provided shaded jar.
|
||||
|
||||
If you run on an unsupported architecture, add the correct native `libdave-jvm` dependency in [pom.xml](/git/MusicBot/pom.xml).
|
||||
|
||||
Bundled native targets include:
|
||||
|
||||
- Linux glibc x86_64
|
||||
- Linux musl x86_64
|
||||
- Windows x86_64
|
||||
- macOS
|
||||
|
||||
## Status Messages
|
||||
|
||||
The bot can maintain a single playback status message in the active text channel.
|
||||
|
||||
Config:
|
||||
|
||||
```conf
|
||||
statusmessages = true
|
||||
```
|
||||
|
||||
Disable it with:
|
||||
|
||||
```conf
|
||||
statusmessages = false
|
||||
```
|
||||
|
||||
## Analytics Logging
|
||||
|
||||
Playback analytics logging is off by default.
|
||||
|
||||
Enable SQLite logging:
|
||||
|
||||
```conf
|
||||
dblog.enabled = true
|
||||
dblog.jdbc_url = "jdbc:sqlite:musicbot.db"
|
||||
```
|
||||
|
||||
This creates a local SQLite database file and records queue, playback, listener, and command activity.
|
||||
|
||||
When analytics are enabled, the `stats` command can show server, personal, track, source, and skip reports:
|
||||
|
||||
```text
|
||||
stats server month
|
||||
stats me week source:youtube
|
||||
stats track
|
||||
stats tracks 60d limit:20 userlist:[alice,bob]
|
||||
stats listened 1y users:alice
|
||||
stats reports set #music-stats monthly
|
||||
stats reports now 2026-04
|
||||
stats reports now 2025
|
||||
```
|
||||
|
||||
Personal member reports are self-only unless the requester has Manage Server. Scheduled reports are Discord embeds; graphic rewind images are not generated yet.
|
||||
|
||||
## Windows Note
|
||||
|
||||
If you run the bot on Windows, install Java first and consider a service wrapper such as [NSSM](http://nssm.cc/) if you want it to run in the background.
|
||||
|
||||
## Related Fork Context
|
||||
|
||||
This fork incorporates or builds on work related to:
|
||||
|
||||
- [PR-1670](https://github.com/jagrosh/MusicBot/pull/1670) for YouTube account login support
|
||||
- [PR-1703](https://github.com/jagrosh/MusicBot/pull/1703) for source builds
|
||||
- [PR-1772](https://github.com/jagrosh/MusicBot/pull/1772) for YouTube PO token and IP rotation work
|
||||
|
||||
If this fork does not fit your setup, another fork you may want to inspect is [SeVile/MusicBot](https://github.com/SeVile/MusicBot).
|
||||
@@ -0,0 +1,295 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.4
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
set -euf
|
||||
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||
|
||||
# OS specific support.
|
||||
native_path() { printf %s\\n "$1"; }
|
||||
case "$(uname)" in
|
||||
CYGWIN* | MINGW*)
|
||||
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||
native_path() { cygpath --path --windows "$1"; }
|
||||
;;
|
||||
esac
|
||||
|
||||
# set JAVACMD and JAVACCMD
|
||||
set_java_home() {
|
||||
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||
if [ -n "${JAVA_HOME-}" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||
|
||||
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v java
|
||||
)" || :
|
||||
JAVACCMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v javac
|
||||
)" || :
|
||||
|
||||
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# hash string like Java String::hashCode
|
||||
hash_string() {
|
||||
str="${1:-}" h=0
|
||||
while [ -n "$str" ]; do
|
||||
char="${str%"${str#?}"}"
|
||||
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||
str="${str#?}"
|
||||
done
|
||||
printf %x\\n $h
|
||||
}
|
||||
|
||||
verbose() { :; }
|
||||
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||
|
||||
die() {
|
||||
printf %s\\n "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
trim() {
|
||||
# MWRAPPER-139:
|
||||
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||
# Needed for removing poorly interpreted newline sequences when running in more
|
||||
# exotic environments such as mingw bash on Windows.
|
||||
printf "%s" "${1}" | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
scriptDir="$(dirname "$0")"
|
||||
scriptName="$(basename "$0")"
|
||||
|
||||
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||
while IFS="=" read -r key value; do
|
||||
case "${key-}" in
|
||||
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||
esac
|
||||
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
|
||||
case "${distributionUrl##*/}" in
|
||||
maven-mvnd-*bin.*)
|
||||
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||
*)
|
||||
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||
distributionPlatform=linux-amd64
|
||||
;;
|
||||
esac
|
||||
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||
;;
|
||||
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||
esac
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||
|
||||
exec_maven() {
|
||||
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||
}
|
||||
|
||||
if [ -d "$MAVEN_HOME" ]; then
|
||||
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
exec_maven "$@"
|
||||
fi
|
||||
|
||||
case "${distributionUrl-}" in
|
||||
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||
esac
|
||||
|
||||
# prepare tmp dir
|
||||
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||
trap clean HUP INT TERM EXIT
|
||||
else
|
||||
die "cannot create temp dir"
|
||||
fi
|
||||
|
||||
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||
|
||||
# Download and Install Apache Maven
|
||||
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
verbose "Downloading from: $distributionUrl"
|
||||
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
# select .zip or .tar.gz
|
||||
if ! command -v unzip >/dev/null; then
|
||||
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
fi
|
||||
|
||||
# verbose opt
|
||||
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||
|
||||
# normalize http auth
|
||||
case "${MVNW_PASSWORD:+has-password}" in
|
||||
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
esac
|
||||
|
||||
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||
verbose "Found wget ... using wget"
|
||||
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||
verbose "Found curl ... using curl"
|
||||
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||
elif set_java_home; then
|
||||
verbose "Falling back to use Java to download"
|
||||
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
cat >"$javaSource" <<-END
|
||||
public class Downloader extends java.net.Authenticator
|
||||
{
|
||||
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||
{
|
||||
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||
}
|
||||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
setDefault( new Downloader() );
|
||||
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||
}
|
||||
}
|
||||
END
|
||||
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||
verbose " - Compiling Downloader.java ..."
|
||||
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||
verbose " - Running Downloader.java ..."
|
||||
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||
fi
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
if [ -n "${distributionSha256Sum-}" ]; then
|
||||
distributionSha256Result=false
|
||||
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
elif command -v sha256sum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
elif command -v shasum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $distributionSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# unzip and move
|
||||
if command -v unzip >/dev/null; then
|
||||
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||
else
|
||||
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||
fi
|
||||
|
||||
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||
actualDistributionDir=""
|
||||
|
||||
# First try the expected directory name (for regular distributions)
|
||||
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
|
||||
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
|
||||
actualDistributionDir="$distributionUrlNameMain"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||
if [ -z "$actualDistributionDir" ]; then
|
||||
# enable globbing to iterate over items
|
||||
set +f
|
||||
for dir in "$TMP_DOWNLOAD_DIR"/*; do
|
||||
if [ -d "$dir" ]; then
|
||||
if [ -f "$dir/bin/$MVN_CMD" ]; then
|
||||
actualDistributionDir="$(basename "$dir")"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
set -f
|
||||
fi
|
||||
|
||||
if [ -z "$actualDistributionDir" ]; then
|
||||
verbose "Contents of $TMP_DOWNLOAD_DIR:"
|
||||
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
|
||||
die "Could not find Maven distribution directory in extracted archive"
|
||||
fi
|
||||
|
||||
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
|
||||
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||
|
||||
clean || :
|
||||
exec_maven "$@"
|
||||
@@ -0,0 +1,189 @@
|
||||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.4
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||
@SET __MVNW_CMD__=
|
||||
@SET __MVNW_ERROR__=
|
||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||
@SET PSModulePath=
|
||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||
)
|
||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||
@SET __MVNW_PSMODULEP_SAVE=
|
||||
@SET __MVNW_ARG0_NAME__=
|
||||
@SET MVNW_USERNAME=
|
||||
@SET MVNW_PASSWORD=
|
||||
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
|
||||
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||
@GOTO :EOF
|
||||
: end batch / begin powershell #>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
if ($env:MVNW_VERBOSE -eq "true") {
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||
if (!$distributionUrl) {
|
||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
}
|
||||
|
||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||
"maven-mvnd-*" {
|
||||
$USE_MVND = $true
|
||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||
$MVN_CMD = "mvnd.cmd"
|
||||
break
|
||||
}
|
||||
default {
|
||||
$USE_MVND = $false
|
||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
if ($env:MVNW_REPOURL) {
|
||||
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
|
||||
}
|
||||
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||
|
||||
$MAVEN_M2_PATH = "$HOME/.m2"
|
||||
if ($env:MAVEN_USER_HOME) {
|
||||
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
|
||||
}
|
||||
|
||||
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
|
||||
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
|
||||
}
|
||||
|
||||
$MAVEN_WRAPPER_DISTS = $null
|
||||
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
|
||||
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
|
||||
} else {
|
||||
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
|
||||
}
|
||||
|
||||
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
|
||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||
|
||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
exit $?
|
||||
}
|
||||
|
||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||
}
|
||||
|
||||
# prepare tmp dir
|
||||
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||
trap {
|
||||
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
}
|
||||
|
||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||
|
||||
# Download and Install Apache Maven
|
||||
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
Write-Verbose "Downloading from: $distributionUrl"
|
||||
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||
}
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||
if ($distributionSha256Sum) {
|
||||
if ($USE_MVND) {
|
||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||
}
|
||||
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||
}
|
||||
}
|
||||
|
||||
# unzip and move
|
||||
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||
|
||||
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||
$actualDistributionDir = ""
|
||||
|
||||
# First try the expected directory name (for regular distributions)
|
||||
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
|
||||
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
|
||||
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
|
||||
$actualDistributionDir = $distributionUrlNameMain
|
||||
}
|
||||
|
||||
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||
if (!$actualDistributionDir) {
|
||||
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
|
||||
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
|
||||
if (Test-Path -Path $testPath -PathType Leaf) {
|
||||
$actualDistributionDir = $_.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$actualDistributionDir) {
|
||||
Write-Error "Could not find Maven distribution directory in extracted archive"
|
||||
}
|
||||
|
||||
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||
try {
|
||||
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||
} catch {
|
||||
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||
Write-Error "fail to move MAVEN_HOME"
|
||||
}
|
||||
} finally {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
@@ -3,81 +3,300 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.jagrosh</groupId>
|
||||
<artifactId>JMusicBot</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>0.6.7</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>m2-chew</id>
|
||||
<name>Maven Chewtral</name>
|
||||
<url>https://m2.chew.pro/releases</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>dv8tion</id>
|
||||
<name>m2-dv8tion</name>
|
||||
<url>https://m2.dv8tion.net/releases</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<name>bintray</name>
|
||||
<url>http://jcenter.bintray.com</url>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
<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</id>
|
||||
<name>Lavalink Repository</name>
|
||||
<url>https://maven.lavalink.dev/releases</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>arbjergDevSnapshots</id>
|
||||
<name>Lavalink Repository</name>
|
||||
<url>https://maven.lavalink.dev/snapshots</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!-- Discord Dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-nop</artifactId>
|
||||
<version>1.7.22</version>
|
||||
<groupId>net.dv8tion</groupId>
|
||||
<artifactId>JDA</artifactId>
|
||||
<version>6.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dv8tion</groupId>
|
||||
<artifactId>JDA</artifactId>
|
||||
<version>3.0.0_168</version>
|
||||
<groupId>moe.kyokobot.libdave</groupId>
|
||||
<artifactId>adapter-jda</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sedmelluq</groupId>
|
||||
<groupId>moe.kyokobot.libdave</groupId>
|
||||
<artifactId>impl-jni</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>moe.kyokobot.libdave</groupId>
|
||||
<artifactId>natives-linux-x86-64</artifactId>
|
||||
<version>0.1.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>moe.kyokobot.libdave</groupId>
|
||||
<artifactId>natives-linux-musl-x86-64</artifactId>
|
||||
<version>0.1.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>moe.kyokobot.libdave</groupId>
|
||||
<artifactId>natives-win-x86-64</artifactId>
|
||||
<version>0.1.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>moe.kyokobot.libdave</groupId>
|
||||
<artifactId>natives-darwin</artifactId>
|
||||
<version>0.1.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>pw.chew</groupId>
|
||||
<artifactId>jda-chewtils-command</artifactId>
|
||||
<version>${jda.chewtils.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>pw.chew</groupId>
|
||||
<artifactId>jda-chewtils-commons</artifactId>
|
||||
<version>${jda.chewtils.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>pw.chew</groupId>
|
||||
<artifactId>jda-chewtils-examples</artifactId>
|
||||
<version>${jda.chewtils.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>pw.chew</groupId>
|
||||
<artifactId>jda-chewtils-menu</artifactId>
|
||||
<version>${jda.chewtils.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>com.github.chew.jda-chewtils</groupId>
|
||||
<artifactId>jda-chewtils-command</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.chew.jda-chewtils</groupId>
|
||||
<artifactId>jda-chewtils-menu</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.chew.jda-chewtils</groupId>
|
||||
<artifactId>jda-chewtils-commons</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency> -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>com.github.chew.jda-chewtils</groupId>
|
||||
<artifactId>jda-chewtils-command</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.chew.jda-chewtils</groupId>
|
||||
<artifactId>jda-chewtils-menu</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.chew.jda-chewtils</groupId>
|
||||
<artifactId>jda-chewtils-commons</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- Music Dependencies -->
|
||||
<dependency>
|
||||
<groupId>dev.arbjerg</groupId>
|
||||
<artifactId>lavaplayer</artifactId>
|
||||
<version>1.2.36</version>
|
||||
<version>2.2.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jagrosh</groupId>
|
||||
<artifactId>JDA-Utilities</artifactId>
|
||||
<version>1.1</version>
|
||||
<groupId>dev.arbjerg</groupId>
|
||||
<artifactId>lavaplayer-ext-youtube-rotator</artifactId>
|
||||
<version>2.2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.lavalink.youtube</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>f45bbb7aebfcbc1c553769e04af6cd43afa8b7c3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jagrosh</groupId>
|
||||
<artifactId>JLyrics</artifactId>
|
||||
<version>master</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.5.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.46.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20160810</version>
|
||||
<version>20240303</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.typesafe</groupId>
|
||||
<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>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-java</artifactId>
|
||||
<version>4.49.0</version>
|
||||
<!-- https://github.com/SeleniumHQ/selenium/issues/13791 -->
|
||||
<!-- selenium-java 4.49.0 ships CDP v151/v152/v153; keep only the one matching the installed Chromium -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-devtools-v151</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-devtools-v152</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.bonigarcia</groupId>
|
||||
<artifactId>webdrivermanager</artifactId>
|
||||
<version>5.9.2</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<!-- Testing Dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<mainClass>com.jagrosh.jmusicbot.JMusicBot</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<configuration>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/filtered-resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<shadedClassifierName>All</shadedClassifierName>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>*:*</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>reference.conf</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<manifestEntries>
|
||||
<Main-Class>com.jagrosh.jmusicbot.JMusicBot</Main-Class>
|
||||
<Specification-Title>${project.artifactId}</Specification-Title>
|
||||
<Specification-Version>${project.version}</Specification-Version>
|
||||
<Implementation-Title>${project.artifactId}</Implementation-Title>
|
||||
<Implementation-Version>${project.version}</Implementation-Version>
|
||||
<Implementation-Vendor-Id>${project.groupId}</Implementation-Vendor-Id>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</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>
|
||||
<jda.chewtils.version>2.2.1</jda.chewtils.version>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
</project>
|
||||
</project>
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
jda-chewtils.version=${jda.chewtils.version}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
|
||||
* 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.
|
||||
@@ -15,97 +15,90 @@
|
||||
*/
|
||||
package com.jagrosh.jmusicbot;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.jagrosh.jdautilities.commandclient.Command.Category;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.waiter.EventWaiter;
|
||||
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;
|
||||
import com.jagrosh.jmusicbot.audio.StatusMessageHandler;
|
||||
import com.jagrosh.jmusicbot.datalog.DataLogService;
|
||||
import com.jagrosh.jmusicbot.datalog.StatsReportScheduler;
|
||||
import com.jagrosh.jmusicbot.gui.GUI;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.JDA;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
import net.dv8tion.jda.core.entities.Guild;
|
||||
import net.dv8tion.jda.core.entities.Role;
|
||||
import net.dv8tion.jda.core.entities.TextChannel;
|
||||
import net.dv8tion.jda.core.entities.VoiceChannel;
|
||||
import net.dv8tion.jda.core.events.ReadyEvent;
|
||||
import net.dv8tion.jda.core.events.ShutdownEvent;
|
||||
import net.dv8tion.jda.core.hooks.ListenerAdapter;
|
||||
import net.dv8tion.jda.core.utils.SimpleLog;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.jagrosh.jmusicbot.playlist.PlaylistLoader;
|
||||
import com.jagrosh.jmusicbot.settings.SettingsManager;
|
||||
import java.util.Objects;
|
||||
import com.jagrosh.jmusicbot.utils.YoutubeOauth2TokenHandler;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class Bot extends ListenerAdapter {
|
||||
|
||||
private final HashMap<String,Settings> settings;
|
||||
private final AudioPlayerManager manager;
|
||||
public class Bot
|
||||
{
|
||||
private final EventWaiter waiter;
|
||||
private final ScheduledExecutorService threadpool;
|
||||
private final BotConfig config;
|
||||
private final SettingsManager settings;
|
||||
private final PlayerManager players;
|
||||
private final PlaylistLoader playlists;
|
||||
private final NowplayingHandler nowplaying;
|
||||
private final StatusMessageHandler statusMessageHandler;
|
||||
private final AloneInVoiceHandler aloneInVoiceHandler;
|
||||
private final DataLogService dataLogService;
|
||||
private final StatsReportScheduler statsReportScheduler;
|
||||
private final YoutubeOauth2TokenHandler youTubeOauth2TokenHandler;
|
||||
|
||||
private boolean shuttingDown = false;
|
||||
private JDA jda;
|
||||
private GUI gui;
|
||||
//private GuildsPanel panel;
|
||||
public final Category MUSIC = new Category("Music");
|
||||
public final Category DJ = new Category("DJ", event ->
|
||||
{
|
||||
if(event.getAuthor().getId().equals(event.getClient().getOwnerId()))
|
||||
return true;
|
||||
if(event.getGuild()==null)
|
||||
return true;
|
||||
if(event.getMember().hasPermission(Permission.MANAGE_SERVER))
|
||||
return true;
|
||||
Role dj = event.getGuild().getRoleById(getSettings(event.getGuild()).getRoleId());
|
||||
return event.getMember().getRoles().contains(dj);
|
||||
});
|
||||
|
||||
public final Category ADMIN = new Category("Admin", event ->
|
||||
{
|
||||
if(event.getAuthor().getId().equals(event.getClient().getOwnerId()))
|
||||
return true;
|
||||
if(event.getGuild()==null)
|
||||
return true;
|
||||
return event.getMember().hasPermission(Permission.MANAGE_SERVER);
|
||||
});
|
||||
|
||||
public final Category OWNER = new Category("Owner");
|
||||
|
||||
public Bot(EventWaiter waiter)
|
||||
public Bot(EventWaiter waiter, BotConfig config, SettingsManager settings)
|
||||
{
|
||||
this.waiter = waiter;
|
||||
this.settings = new HashMap<>();
|
||||
manager = new DefaultAudioPlayerManager();
|
||||
threadpool = Executors.newSingleThreadScheduledExecutor();
|
||||
AudioSourceManagers.registerRemoteSources(manager);
|
||||
AudioSourceManagers.registerLocalSource(manager);
|
||||
try {
|
||||
JSONObject loadedSettings = new JSONObject(new String(Files.readAllBytes(Paths.get("serversettings.json"))));
|
||||
loadedSettings.keySet().forEach((id) -> {
|
||||
JSONObject o = loadedSettings.getJSONObject(id);
|
||||
|
||||
settings.put(id, new Settings(
|
||||
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));
|
||||
});
|
||||
} catch(IOException | JSONException e) {
|
||||
SimpleLog.getLog("Settings").warn("Failed to load server settings: "+e);
|
||||
this.config = config;
|
||||
this.settings = settings;
|
||||
this.playlists = new PlaylistLoader(config);
|
||||
this.threadpool = Executors.newSingleThreadScheduledExecutor();
|
||||
this.youTubeOauth2TokenHandler = new YoutubeOauth2TokenHandler();
|
||||
this.youTubeOauth2TokenHandler.init();
|
||||
this.players = new PlayerManager(this, config);
|
||||
this.players.init();
|
||||
this.nowplaying = new NowplayingHandler(this);
|
||||
this.nowplaying.init();
|
||||
this.statusMessageHandler = new StatusMessageHandler(this);
|
||||
this.statusMessageHandler.init();
|
||||
this.aloneInVoiceHandler = new AloneInVoiceHandler(this);
|
||||
this.aloneInVoiceHandler.init();
|
||||
DataLogService dls = null;
|
||||
if(config.getDataLogEnabled())
|
||||
{
|
||||
try
|
||||
{
|
||||
dls = new DataLogService(this, config.getDataLogJdbcUrl(), config.getDataLogUser(), config.getDataLogPassword());
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
dls = null;
|
||||
}
|
||||
}
|
||||
this.dataLogService = dls;
|
||||
this.statsReportScheduler = new StatsReportScheduler(this);
|
||||
this.statsReportScheduler.init();
|
||||
}
|
||||
|
||||
public BotConfig getConfig()
|
||||
{
|
||||
return config;
|
||||
}
|
||||
|
||||
public SettingsManager getSettingsManager()
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
|
||||
public EventWaiter getWaiter()
|
||||
@@ -113,277 +106,104 @@ public class Bot extends ListenerAdapter {
|
||||
return waiter;
|
||||
}
|
||||
|
||||
public AudioPlayerManager getAudioManager()
|
||||
public ScheduledExecutorService getThreadpool()
|
||||
{
|
||||
return manager;
|
||||
return threadpool;
|
||||
}
|
||||
|
||||
public int queueTrack(CommandEvent event, AudioTrack track)
|
||||
public PlayerManager getPlayerManager()
|
||||
{
|
||||
return setUpHandler(event).addTrack(track, event.getAuthor());
|
||||
return players;
|
||||
}
|
||||
|
||||
public AudioHandler setUpHandler(CommandEvent event)
|
||||
public PlaylistLoader getPlaylistLoader()
|
||||
{
|
||||
return setUpHandler(event.getGuild());
|
||||
return playlists;
|
||||
}
|
||||
|
||||
public AudioHandler setUpHandler(Guild guild)
|
||||
public NowplayingHandler getNowplayingHandler()
|
||||
{
|
||||
AudioHandler handler;
|
||||
if(guild.getAudioManager().getSendingHandler()==null)
|
||||
{
|
||||
AudioPlayer player = manager.createPlayer();
|
||||
if(settings.containsKey(guild.getId()))
|
||||
player.setVolume(settings.get(guild.getId()).getVolume());
|
||||
handler = new AudioHandler(player, guild, this);
|
||||
player.addListener(handler);
|
||||
guild.getAudioManager().setSendingHandler(handler);
|
||||
threadpool.scheduleWithFixedDelay(() -> updateTopic(guild,handler), 0, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
else
|
||||
handler = (AudioHandler)guild.getAudioManager().getSendingHandler();
|
||||
return handler;
|
||||
}
|
||||
|
||||
private void updateTopic(Guild guild, AudioHandler handler)
|
||||
{
|
||||
TextChannel tchan = guild.getTextChannelById(getSettings(guild).getTextId());
|
||||
if(tchan!=null && guild.getSelfMember().hasPermission(tchan, Permission.MANAGE_CHANNEL))
|
||||
{
|
||||
String otherText;
|
||||
if(tchan.getTopic()==null || tchan.getTopic().isEmpty())
|
||||
otherText = "\u200B";
|
||||
else if(tchan.getTopic().contains("\u200B"))
|
||||
otherText = tchan.getTopic().substring(tchan.getTopic().indexOf("\u200B"));
|
||||
else
|
||||
otherText = "\u200B\n "+tchan.getTopic();
|
||||
String text = FormatUtil.formattedAudio(handler, guild.getJDA(), true)+otherText;
|
||||
if(!text.equals(tchan.getTopic()))
|
||||
tchan.getManager().setTopic(text).queue();
|
||||
}
|
||||
return nowplaying;
|
||||
}
|
||||
|
||||
public void shutdown(){
|
||||
manager.shutdown();
|
||||
public StatusMessageHandler getStatusMessageHandler()
|
||||
{
|
||||
return statusMessageHandler;
|
||||
}
|
||||
|
||||
public AloneInVoiceHandler getAloneInVoiceHandler()
|
||||
{
|
||||
return aloneInVoiceHandler;
|
||||
}
|
||||
|
||||
public DataLogService getDataLogService()
|
||||
{
|
||||
return dataLogService;
|
||||
}
|
||||
|
||||
public StatsReportScheduler getStatsReportScheduler()
|
||||
{
|
||||
return statsReportScheduler;
|
||||
}
|
||||
|
||||
public YoutubeOauth2TokenHandler getYouTubeOauth2Handler()
|
||||
{
|
||||
return youTubeOauth2TokenHandler;
|
||||
}
|
||||
|
||||
public JDA getJDA()
|
||||
{
|
||||
return jda;
|
||||
}
|
||||
|
||||
public void closeAudioConnection(long guildId)
|
||||
{
|
||||
Guild guild = jda.getGuildById(guildId);
|
||||
if(guild!=null)
|
||||
threadpool.submit(() -> guild.getAudioManager().closeAudioConnection());
|
||||
}
|
||||
|
||||
public void resetGame()
|
||||
{
|
||||
Activity game = config.getGame()==null || config.getGame().getName().equalsIgnoreCase("none") ? null : config.getGame();
|
||||
if(!Objects.equals(jda.getPresence().getActivity(), game))
|
||||
jda.getPresence().setActivity(game);
|
||||
}
|
||||
|
||||
public void shutdown()
|
||||
{
|
||||
if(shuttingDown)
|
||||
return;
|
||||
shuttingDown = true;
|
||||
threadpool.shutdownNow();
|
||||
jda.getGuilds().stream().forEach(g -> {
|
||||
g.getAudioManager().closeAudioConnection();
|
||||
AudioHandler ah = (AudioHandler)g.getAudioManager().getSendingHandler();
|
||||
if(ah!=null)
|
||||
if(jda != null && jda.getStatus()!=JDA.Status.SHUTTING_DOWN)
|
||||
{
|
||||
jda.getGuilds().stream().forEach(g ->
|
||||
{
|
||||
ah.getQueue().clear();
|
||||
ah.getPlayer().destroy();
|
||||
updateTopic(g, ah);
|
||||
}
|
||||
});
|
||||
jda.shutdown();
|
||||
g.getAudioManager().closeAudioConnection();
|
||||
AudioHandler ah = (AudioHandler)g.getAudioManager().getSendingHandler();
|
||||
if(ah!=null)
|
||||
{
|
||||
ah.stopAndClear();
|
||||
ah.getPlayer().destroy();
|
||||
}
|
||||
});
|
||||
jda.shutdown();
|
||||
}
|
||||
if(gui!=null)
|
||||
gui.dispose();
|
||||
if(dataLogService != null)
|
||||
dataLogService.shutdown();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
public void setJDA(JDA jda)
|
||||
{
|
||||
this.jda = jda;
|
||||
}
|
||||
|
||||
public void setGUI(GUI gui)
|
||||
{
|
||||
this.gui = gui;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdown(ShutdownEvent event) {
|
||||
if(gui!=null)
|
||||
gui.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReady(ReadyEvent event) {
|
||||
this.jda = event.getJDA();
|
||||
if(jda.getGuilds().isEmpty())
|
||||
{
|
||||
SimpleLog.getLog("MusicBot").warn("This bot is not on any guilds! Use the following link to add the bot to your guilds!");
|
||||
SimpleLog.getLog("MusicBot").warn(event.getJDA().asBot().getInviteUrl(JMusicBot.RECOMMENDED_PERMS));
|
||||
}
|
||||
jda.getGuilds().forEach((guild) -> {
|
||||
try
|
||||
{
|
||||
String defpl = getSettings(guild).getDefaultPlaylist();
|
||||
if(defpl!=null)
|
||||
{
|
||||
if(setUpHandler(guild).playFromDefault())
|
||||
guild.getAudioManager().openAudioConnection(guild.getVoiceChannelById(getSettings(guild).getVoiceId()));
|
||||
}
|
||||
}
|
||||
catch(Exception ex) {System.err.println(ex);}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// settings
|
||||
|
||||
public Settings getSettings(Guild guild)
|
||||
{
|
||||
return settings.getOrDefault(guild.getId(), Settings.DEFAULT_SETTINGS);
|
||||
}
|
||||
|
||||
public void setTextChannel(TextChannel channel)
|
||||
{
|
||||
Settings s = settings.get(channel.getGuild().getId());
|
||||
if(s==null)
|
||||
{
|
||||
settings.put(channel.getGuild().getId(), new Settings(channel.getId(),null,null,100,null));
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setTextId(channel.getIdLong());
|
||||
}
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
public void setVoiceChannel(VoiceChannel channel)
|
||||
{
|
||||
Settings s = settings.get(channel.getGuild().getId());
|
||||
if(s==null)
|
||||
{
|
||||
settings.put(channel.getGuild().getId(), new Settings(null,channel.getId(),null,100,null));
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setVoiceId(channel.getIdLong());
|
||||
}
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
public void setRole(Role role)
|
||||
{
|
||||
Settings s = settings.get(role.getGuild().getId());
|
||||
if(s==null)
|
||||
{
|
||||
settings.put(role.getGuild().getId(), new Settings(null,null,role.getId(),100,null));
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setRoleId(role.getIdLong());
|
||||
}
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
public void setDefaultPlaylist(Guild guild, String playlist)
|
||||
{
|
||||
Settings s = settings.get(guild.getId());
|
||||
if(s==null)
|
||||
{
|
||||
settings.put(guild.getId(), new Settings(null,null,null,100,playlist));
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setDefaultPlaylist(playlist);
|
||||
}
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
public void setVolume(Guild guild, int volume)
|
||||
{
|
||||
Settings s = settings.get(guild.getId());
|
||||
if(s==null)
|
||||
{
|
||||
settings.put(guild.getId(), new Settings(null,null,null,volume,null));
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setVolume(volume);
|
||||
}
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
public void clearTextChannel(Guild guild)
|
||||
{
|
||||
Settings s = getSettings(guild);
|
||||
if(s!=Settings.DEFAULT_SETTINGS)
|
||||
{
|
||||
if(s.getVoiceId()==0 && s.getRoleId()==0)
|
||||
settings.remove(guild.getId());
|
||||
else
|
||||
s.setTextId(0);
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearVoiceChannel(Guild guild)
|
||||
{
|
||||
Settings s = getSettings(guild);
|
||||
if(s!=Settings.DEFAULT_SETTINGS)
|
||||
{
|
||||
if(s.getTextId()==0 && s.getRoleId()==0)
|
||||
settings.remove(guild.getId());
|
||||
else
|
||||
s.setVoiceId(0);
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearRole(Guild guild)
|
||||
{
|
||||
Settings s = getSettings(guild);
|
||||
if(s!=Settings.DEFAULT_SETTINGS)
|
||||
{
|
||||
if(s.getVoiceId()==0 && s.getTextId()==0)
|
||||
settings.remove(guild.getId());
|
||||
else
|
||||
s.setRoleId(0);
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSettings()
|
||||
{
|
||||
JSONObject obj = new JSONObject();
|
||||
settings.keySet().stream().forEach(key -> {
|
||||
JSONObject o = new JSONObject();
|
||||
Settings s = settings.get(key);
|
||||
if(s.getTextId()!=0)
|
||||
o.put("text_channel_id", Long.toString(s.getTextId()));
|
||||
if(s.getVoiceId()!=0)
|
||||
o.put("voice_channel_id", Long.toString(s.getVoiceId()));
|
||||
if(s.getRoleId()!=0)
|
||||
o.put("dj_role_id", Long.toString(s.getRoleId()));
|
||||
if(s.getVolume()!=100)
|
||||
o.put("volume",s.getVolume());
|
||||
if(s.getDefaultPlaylist()!=null)
|
||||
o.put("default_playlist", s.getDefaultPlaylist());
|
||||
obj.put(key, o);
|
||||
});
|
||||
try {
|
||||
Files.write(Paths.get("serversettings.json"), obj.toString(4).getBytes());
|
||||
} catch(IOException ex){
|
||||
SimpleLog.getLog("Settings").warn("Failed to write to file: "+ex);
|
||||
}
|
||||
}
|
||||
|
||||
//gui stuff
|
||||
/*public void registerPanel(GuildsPanel panel)
|
||||
{
|
||||
this.panel = panel;
|
||||
threadpool.scheduleWithFixedDelay(() -> updatePanel(), 0, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void updatePanel()
|
||||
{
|
||||
System.out.println("updating...");
|
||||
Guild guild = jda.getGuilds().get(panel.getIndex());
|
||||
panel.updatePanel((AudioHandler)guild.getAudioManager().getSendingHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuildJoin(GuildJoinEvent event) {
|
||||
if(panel!=null)
|
||||
panel.updateList(event.getJDA().getGuilds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuildLeave(GuildLeaveEvent event) {
|
||||
if(panel!=null)
|
||||
panel.updateList(event.getJDA().getGuilds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdown(ShutdownEvent event) {
|
||||
((GUI)panel.getTopLevelAncestor()).dispose();
|
||||
}*/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* Copyright 2018 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;
|
||||
|
||||
import com.jagrosh.jmusicbot.entities.Prompt;
|
||||
import com.jagrosh.jmusicbot.utils.OtherUtil;
|
||||
import com.jagrosh.jmusicbot.utils.TimeUtil;
|
||||
import com.jagrosh.jmusicbot.utils.YouTubeUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.sedmelluq.lava.extensions.youtuberotator.tools.ip.IpBlock;
|
||||
import com.typesafe.config.*;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.dv8tion.jda.api.OnlineStatus;
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author John Grosh (jagrosh)
|
||||
*/
|
||||
public class BotConfig
|
||||
{
|
||||
private final Prompt prompt;
|
||||
private final static String CONTEXT = "Config";
|
||||
private final static String START_TOKEN = "/// START OF JMUSICBOT CONFIG ///";
|
||||
private final static String END_TOKEN = "/// END OF JMUSICBOT CONFIG ///";
|
||||
|
||||
private Path path = null;
|
||||
private String token, prefix, altprefix, helpWord, playlistsFolder, logLevel,
|
||||
successEmoji, warningEmoji, errorEmoji, loadingEmoji, searchingEmoji,
|
||||
chromePath, chromeDriverPath, evalEngine;
|
||||
private YouTubeUtil.RoutingPlanner ytRoutingPlanner;
|
||||
private List<IpBlock> ytIpBlocks;
|
||||
private boolean youtubeOauth2, stayInChannel, songInGame, npImages, updatealerts, chromeHeadless, useEval, dbots;
|
||||
private boolean statusMessages;
|
||||
private boolean dataLogEnabled;
|
||||
private String dataLogJdbcUrl, dataLogUser, dataLogPassword;
|
||||
private long owner, maxSeconds, aloneTimeUntilStop;
|
||||
private int maxYTPlaylistPages;
|
||||
private double skipratio;
|
||||
private OnlineStatus status;
|
||||
private Activity game;
|
||||
private Config aliases, transforms;
|
||||
|
||||
private boolean valid = false;
|
||||
|
||||
public BotConfig(Prompt prompt)
|
||||
{
|
||||
this.prompt = prompt;
|
||||
}
|
||||
|
||||
public void load()
|
||||
{
|
||||
valid = false;
|
||||
|
||||
// read config from file
|
||||
try
|
||||
{
|
||||
// get the path to the config, default config.txt
|
||||
path = getConfigPath();
|
||||
|
||||
// load in the config file, plus the default values
|
||||
//Config config = ConfigFactory.parseFile(path.toFile()).withFallback(ConfigFactory.load());
|
||||
Config config = ConfigFactory.load();
|
||||
|
||||
// set values
|
||||
token = config.getString("token");
|
||||
prefix = config.getString("prefix");
|
||||
altprefix = config.getString("altprefix");
|
||||
helpWord = config.getString("help");
|
||||
owner = config.getLong("owner");
|
||||
successEmoji = config.getString("success");
|
||||
warningEmoji = config.getString("warning");
|
||||
errorEmoji = config.getString("error");
|
||||
loadingEmoji = config.getString("loading");
|
||||
searchingEmoji = config.getString("searching");
|
||||
game = OtherUtil.parseGame(config.getString("game"));
|
||||
status = OtherUtil.parseStatus(config.getString("status"));
|
||||
youtubeOauth2 = config.getBoolean("youtubeoauth2");
|
||||
stayInChannel = config.getBoolean("stayinchannel");
|
||||
songInGame = config.getBoolean("songinstatus");
|
||||
statusMessages = config.getBoolean("statusmessages");
|
||||
npImages = config.getBoolean("npimages");
|
||||
updatealerts = config.getBoolean("updatealerts");
|
||||
logLevel = config.getString("loglevel");
|
||||
useEval = config.getBoolean("eval");
|
||||
evalEngine = config.getString("evalengine");
|
||||
maxSeconds = config.getLong("maxtime");
|
||||
maxYTPlaylistPages = config.getInt("maxytplaylistpages");
|
||||
aloneTimeUntilStop = config.getLong("alonetimeuntilstop");
|
||||
playlistsFolder = config.getString("playlistsfolder");
|
||||
aliases = config.getConfig("aliases");
|
||||
chromePath = config.getString("chromepath");
|
||||
chromeDriverPath = config.getString("chromedriverpath");
|
||||
chromeHeadless = config.getBoolean("chromeheadless");
|
||||
ytRoutingPlanner = config.getEnum(YouTubeUtil.RoutingPlanner.class, "ytroutingplanner");
|
||||
ytIpBlocks = config.getStringList("ytipblocks").stream().map(YouTubeUtil::parseIpBlock).collect(Collectors.toList());
|
||||
transforms = config.getConfig("transforms");
|
||||
skipratio = config.getDouble("skipratio");
|
||||
dbots = owner == 113156185389092864L;
|
||||
dataLogEnabled = config.getBoolean("dblog.enabled");
|
||||
dataLogJdbcUrl = config.getString("dblog.jdbc_url");
|
||||
dataLogUser = config.getString("dblog.user");
|
||||
dataLogPassword = config.getString("dblog.password");
|
||||
|
||||
// we may need to write a new config file
|
||||
boolean write = false;
|
||||
|
||||
// validate bot token
|
||||
if(token==null || token.isEmpty() || token.equalsIgnoreCase("BOT_TOKEN_HERE"))
|
||||
{
|
||||
token = prompt.prompt("Please provide a bot token."
|
||||
+ "\nInstructions for obtaining a token can be found here:"
|
||||
+ "\nhttps://github.com/jagrosh/MusicBot/wiki/Getting-a-Bot-Token."
|
||||
+ "\nBot Token: ");
|
||||
if(token==null)
|
||||
{
|
||||
prompt.alert(Prompt.Level.WARNING, CONTEXT, "No token provided! Exiting.\n\nConfig Location: " + path.toAbsolutePath().toString());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
write = true;
|
||||
}
|
||||
}
|
||||
|
||||
// validate bot owner
|
||||
if(owner<=0)
|
||||
{
|
||||
try
|
||||
{
|
||||
owner = Long.parseLong(prompt.prompt("Owner ID was missing, or the provided owner ID is not valid."
|
||||
+ "\nPlease provide the User ID of the bot's owner."
|
||||
+ "\nInstructions for obtaining your User ID can be found here:"
|
||||
+ "\nhttps://github.com/jagrosh/MusicBot/wiki/Finding-Your-User-ID"
|
||||
+ "\nOwner User ID: "));
|
||||
}
|
||||
catch(NumberFormatException | NullPointerException ex)
|
||||
{
|
||||
owner = 0;
|
||||
}
|
||||
if(owner<=0)
|
||||
{
|
||||
prompt.alert(Prompt.Level.ERROR, CONTEXT, "Invalid User ID! Exiting.\n\nConfig Location: " + path.toAbsolutePath().toString());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
write = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(write)
|
||||
writeToFile();
|
||||
|
||||
// if we get through the whole config, it's good to go
|
||||
valid = true;
|
||||
}
|
||||
catch (ConfigException ex)
|
||||
{
|
||||
prompt.alert(Prompt.Level.ERROR, CONTEXT, ex + ": " + ex.getMessage() + "\n\nConfig Location: " + path.toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeToFile()
|
||||
{
|
||||
byte[] bytes = loadDefaultConfig().replace("BOT_TOKEN_HERE", token)
|
||||
.replace("0 // OWNER ID", Long.toString(owner))
|
||||
.trim().getBytes();
|
||||
try
|
||||
{
|
||||
Files.write(path, bytes);
|
||||
}
|
||||
catch(IOException ex)
|
||||
{
|
||||
prompt.alert(Prompt.Level.WARNING, CONTEXT, "Failed to write new config options to config.txt: "+ex
|
||||
+ "\nPlease make sure that the files are not on your desktop or some other restricted area.\n\nConfig Location: "
|
||||
+ path.toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static String loadDefaultConfig()
|
||||
{
|
||||
String original = OtherUtil.loadResource(new JMusicBot(), "/reference.conf");
|
||||
return original==null
|
||||
? "token = BOT_TOKEN_HERE\r\nowner = 0 // OWNER ID"
|
||||
: original.substring(original.indexOf(START_TOKEN)+START_TOKEN.length(), original.indexOf(END_TOKEN)).trim();
|
||||
}
|
||||
|
||||
private static Path getConfigPath()
|
||||
{
|
||||
Path path = OtherUtil.getPath(System.getProperty("config.file", System.getProperty("config", "config.txt")));
|
||||
if(path.toFile().exists())
|
||||
{
|
||||
if(System.getProperty("config.file") == null)
|
||||
System.setProperty("config.file", System.getProperty("config", path.toAbsolutePath().toString()));
|
||||
ConfigFactory.invalidateCaches();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static void writeDefaultConfig()
|
||||
{
|
||||
Prompt prompt = new Prompt(null, null, true, true);
|
||||
prompt.alert(Prompt.Level.INFO, "JMusicBot Config", "Generating default config file");
|
||||
Path path = BotConfig.getConfigPath();
|
||||
byte[] bytes = BotConfig.loadDefaultConfig().getBytes();
|
||||
try
|
||||
{
|
||||
prompt.alert(Prompt.Level.INFO, "JMusicBot Config", "Writing default config file to " + path.toAbsolutePath().toString());
|
||||
Files.write(path, bytes);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
prompt.alert(Prompt.Level.ERROR, "JMusicBot Config", "An error occurred writing the default config file: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
return valid;
|
||||
}
|
||||
|
||||
public String getConfigLocation()
|
||||
{
|
||||
return path.toFile().getAbsolutePath();
|
||||
}
|
||||
|
||||
public String getPrefix()
|
||||
{
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public String getAltPrefix()
|
||||
{
|
||||
return "NONE".equalsIgnoreCase(altprefix) ? null : altprefix;
|
||||
}
|
||||
|
||||
public String getToken()
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
public double getSkipRatio()
|
||||
{
|
||||
return skipratio;
|
||||
}
|
||||
|
||||
public long getOwnerId()
|
||||
{
|
||||
return owner;
|
||||
}
|
||||
|
||||
public boolean getDataLogEnabled()
|
||||
{
|
||||
return dataLogEnabled;
|
||||
}
|
||||
|
||||
public String getDataLogJdbcUrl()
|
||||
{
|
||||
return dataLogJdbcUrl;
|
||||
}
|
||||
|
||||
public String getDataLogUser()
|
||||
{
|
||||
return dataLogUser;
|
||||
}
|
||||
|
||||
public String getDataLogPassword()
|
||||
{
|
||||
return dataLogPassword;
|
||||
}
|
||||
|
||||
public String getSuccess()
|
||||
{
|
||||
return successEmoji;
|
||||
}
|
||||
|
||||
public String getWarning()
|
||||
{
|
||||
return warningEmoji;
|
||||
}
|
||||
|
||||
public String getError()
|
||||
{
|
||||
return errorEmoji;
|
||||
}
|
||||
|
||||
public String getLoading()
|
||||
{
|
||||
return loadingEmoji;
|
||||
}
|
||||
|
||||
public String getSearching()
|
||||
{
|
||||
return searchingEmoji;
|
||||
}
|
||||
|
||||
public Activity getGame()
|
||||
{
|
||||
return game;
|
||||
}
|
||||
|
||||
public boolean isGameNone()
|
||||
{
|
||||
return game != null && game.getName().equalsIgnoreCase("none");
|
||||
}
|
||||
|
||||
public OnlineStatus getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getHelp()
|
||||
{
|
||||
return helpWord;
|
||||
}
|
||||
|
||||
public boolean useYoutubeOauth2()
|
||||
{
|
||||
return youtubeOauth2;
|
||||
}
|
||||
|
||||
public boolean getStay()
|
||||
{
|
||||
return stayInChannel;
|
||||
}
|
||||
|
||||
public boolean getSongInStatus()
|
||||
{
|
||||
return songInGame;
|
||||
}
|
||||
|
||||
public boolean useStatusMessages()
|
||||
{
|
||||
return statusMessages;
|
||||
}
|
||||
|
||||
public String getPlaylistsFolder()
|
||||
{
|
||||
return playlistsFolder;
|
||||
}
|
||||
|
||||
public boolean getDBots()
|
||||
{
|
||||
return dbots;
|
||||
}
|
||||
|
||||
public boolean useUpdateAlerts()
|
||||
{
|
||||
return updatealerts;
|
||||
}
|
||||
|
||||
public String getLogLevel()
|
||||
{
|
||||
return logLevel;
|
||||
}
|
||||
|
||||
public String getChromePath()
|
||||
{
|
||||
return chromePath.equals("AUTO") ? null : chromePath;
|
||||
}
|
||||
|
||||
public String getChromeDriverPath()
|
||||
{
|
||||
return chromeDriverPath.equals("AUTO") ? null : chromeDriverPath;
|
||||
}
|
||||
|
||||
public boolean getChromeHeadless()
|
||||
{
|
||||
return chromeHeadless;
|
||||
}
|
||||
|
||||
public YouTubeUtil.RoutingPlanner getYTRoutingPlanner()
|
||||
{
|
||||
return ytRoutingPlanner;
|
||||
}
|
||||
|
||||
public List<IpBlock> getYTIpBlocks()
|
||||
{
|
||||
return ytIpBlocks;
|
||||
}
|
||||
|
||||
public boolean useEval()
|
||||
{
|
||||
return useEval;
|
||||
}
|
||||
|
||||
public String getEvalEngine()
|
||||
{
|
||||
return evalEngine;
|
||||
}
|
||||
|
||||
public boolean useNPImages()
|
||||
{
|
||||
return npImages;
|
||||
}
|
||||
|
||||
public long getMaxSeconds()
|
||||
{
|
||||
return maxSeconds;
|
||||
}
|
||||
|
||||
public int getMaxYTPlaylistPages()
|
||||
{
|
||||
return maxYTPlaylistPages;
|
||||
}
|
||||
|
||||
public String getMaxTime()
|
||||
{
|
||||
return TimeUtil.formatTime(maxSeconds * 1000);
|
||||
}
|
||||
|
||||
public long getAloneTimeUntilStop()
|
||||
{
|
||||
return aloneTimeUntilStop;
|
||||
}
|
||||
|
||||
public boolean isTooLong(AudioTrack track)
|
||||
{
|
||||
if(maxSeconds<=0)
|
||||
return false;
|
||||
return Math.round(track.getDuration()/1000.0) > maxSeconds;
|
||||
}
|
||||
|
||||
public String[] getAliases(String command)
|
||||
{
|
||||
try
|
||||
{
|
||||
return aliases.getStringList(command).toArray(new String[0]);
|
||||
}
|
||||
catch(NullPointerException | ConfigException.Missing e)
|
||||
{
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
|
||||
public Config getTransforms()
|
||||
{
|
||||
return transforms;
|
||||
}
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (jagrosh)
|
||||
*/
|
||||
public class Config {
|
||||
private boolean nogui;
|
||||
private String prefix;
|
||||
private String token;
|
||||
private String owner;
|
||||
private String success;
|
||||
private String warning;
|
||||
private String error;
|
||||
private String game;
|
||||
private String help;
|
||||
|
||||
public Config(boolean nogui)
|
||||
{
|
||||
this.nogui = nogui;
|
||||
List<String> lines;
|
||||
try {
|
||||
lines = Files.readAllLines(Paths.get("config.txt"));
|
||||
for(String line: lines)
|
||||
{
|
||||
String[] parts = line.split("=",2);
|
||||
String key = parts[0].trim().toLowerCase();
|
||||
String value = parts.length>1 ? parts[1].trim() : null;
|
||||
switch(key)
|
||||
{
|
||||
case "token":
|
||||
token = value;
|
||||
break;
|
||||
case "prefix":
|
||||
prefix = value;
|
||||
break;
|
||||
case "owner":
|
||||
owner = value;
|
||||
break;
|
||||
case "success":
|
||||
success = value;
|
||||
break;
|
||||
case "warning":
|
||||
warning = value;
|
||||
break;
|
||||
case "error":
|
||||
error = value;
|
||||
break;
|
||||
case "game":
|
||||
game = value;
|
||||
break;
|
||||
case "help":
|
||||
help = value;
|
||||
break;
|
||||
case "noprogressintopic":
|
||||
FormatUtil.NO_PROGRESS_BAR_IN_TOPIC = "true".equalsIgnoreCase(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
alert("'config.txt' was not found!");
|
||||
lines = new LinkedList<>();
|
||||
}
|
||||
boolean write = false;
|
||||
if(token==null || token.isEmpty())
|
||||
{
|
||||
token = prompt("Please provide a bot token."
|
||||
+ "\nInstructions for obtaining a token can be found here:"
|
||||
+ "\nhttps://github.com/jagrosh/MusicBot/wiki/Getting-a-Bot-Token."
|
||||
+ "\nBot Token: ");
|
||||
if(token==null)
|
||||
{
|
||||
alert("No token provided! Exiting.");
|
||||
System.exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.add("token="+token);
|
||||
write = true;
|
||||
}
|
||||
}
|
||||
if(owner==null || !owner.matches("\\d{17,20}"))
|
||||
{
|
||||
owner = prompt("Owner ID was missing, or the provided owner ID is not valid."
|
||||
+ "\nPlease provide the User ID of the bot's owner."
|
||||
+ "\nInstructions for obtaining your User ID can be found here:"
|
||||
+ "\nhttps://github.com/jagrosh/MusicBot/wiki/Finding-Your-User-ID"
|
||||
+ "\nOwner User ID: ");
|
||||
if(owner==null || !owner.matches("\\d{17,20}"))
|
||||
{
|
||||
alert("Invalid User ID! Exiting.");
|
||||
System.exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.add("owner="+owner);
|
||||
write = true;
|
||||
}
|
||||
}
|
||||
if(write)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
lines.stream().forEach(s -> builder.append(s).append("\r\n"));
|
||||
try {
|
||||
Files.write(Paths.get("config.txt"), builder.toString().trim().getBytes());
|
||||
} catch(IOException ex) {
|
||||
alert("Failed to write new config options to config.txt: "+ex
|
||||
+ "\nPlease make sure that the files are not on your desktop or some other restricted area.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getPrefix()
|
||||
{
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public String getToken()
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
public String getOwnerId()
|
||||
{
|
||||
return owner;
|
||||
}
|
||||
|
||||
public String getSuccess()
|
||||
{
|
||||
return success==null ? "\uD83C\uDFB6" : success;
|
||||
}
|
||||
|
||||
public String getWarning()
|
||||
{
|
||||
return warning==null ? "\uD83D\uDCA1" : warning;
|
||||
}
|
||||
|
||||
public String getError()
|
||||
{
|
||||
return error==null ? "\uD83D\uDEAB" : error;
|
||||
}
|
||||
|
||||
public String getGame()
|
||||
{
|
||||
return game;
|
||||
}
|
||||
|
||||
public String getHelp()
|
||||
{
|
||||
return help==null ? "help" : help;
|
||||
}
|
||||
|
||||
public boolean getNoGui()
|
||||
{
|
||||
return nogui;
|
||||
}
|
||||
|
||||
private void alert(String message)
|
||||
{
|
||||
if(nogui)
|
||||
System.out.println("[WARNING] "+message);
|
||||
else
|
||||
{
|
||||
try {
|
||||
JOptionPane.showMessageDialog(null, message, "JMusicBot", JOptionPane.WARNING_MESSAGE);
|
||||
} catch(Exception e) {
|
||||
nogui = true;
|
||||
alert("Switching to nogui mode. You can manually start in nogui mode by including the -nogui flag.");
|
||||
alert(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String prompt(String content)
|
||||
{
|
||||
if(nogui)
|
||||
{
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
System.out.println(content);
|
||||
return scanner.next();
|
||||
}
|
||||
else
|
||||
{
|
||||
try {
|
||||
return JOptionPane.showInputDialog(null, content, "JMusicBot", JOptionPane.WARNING_MESSAGE);
|
||||
} catch(Exception e) {
|
||||
nogui = true;
|
||||
alert("Switching to nogui mode. You can manually start in nogui mode by including the -nogui flag.");
|
||||
return prompt(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,65 +15,208 @@
|
||||
*/
|
||||
package com.jagrosh.jmusicbot;
|
||||
|
||||
import java.awt.Color;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandClient;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandClientBuilder;
|
||||
import com.jagrosh.jdautilities.commandclient.examples.*;
|
||||
import com.jagrosh.jdautilities.waiter.EventWaiter;
|
||||
import com.jagrosh.jmusicbot.commands.*;
|
||||
import com.jagrosh.jdautilities.command.CommandClient;
|
||||
import com.jagrosh.jdautilities.command.CommandClientBuilder;
|
||||
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
|
||||
import com.jagrosh.jmusicbot.commands.general.LoggedAboutCmd;
|
||||
import com.jagrosh.jmusicbot.commands.general.LoggedPingCmd;
|
||||
import com.jagrosh.jmusicbot.commands.admin.*;
|
||||
import com.jagrosh.jmusicbot.commands.dj.*;
|
||||
import com.jagrosh.jmusicbot.commands.general.*;
|
||||
import com.jagrosh.jmusicbot.commands.music.*;
|
||||
import com.jagrosh.jmusicbot.commands.owner.*;
|
||||
import com.jagrosh.jmusicbot.entities.Prompt;
|
||||
import com.jagrosh.jmusicbot.gui.GUI;
|
||||
import net.dv8tion.jda.core.AccountType;
|
||||
import net.dv8tion.jda.core.JDABuilder;
|
||||
import net.dv8tion.jda.core.OnlineStatus;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
import net.dv8tion.jda.core.entities.Game;
|
||||
import net.dv8tion.jda.core.exceptions.RateLimitedException;
|
||||
import net.dv8tion.jda.core.utils.SimpleLog;
|
||||
import com.jagrosh.jmusicbot.settings.SettingsManager;
|
||||
import com.jagrosh.jmusicbot.utils.OtherUtil;
|
||||
import moe.kyokobot.libdave.NativeDaveFactory;
|
||||
import moe.kyokobot.libdave.jda.LDJDADaveSessionFactory;
|
||||
import java.awt.Color;
|
||||
import java.util.Arrays;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import net.dv8tion.jda.api.*;
|
||||
import net.dv8tion.jda.api.audio.AudioModuleConfig;
|
||||
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;
|
||||
import ch.qos.logback.classic.Level;
|
||||
|
||||
import static com.jagrosh.jmusicbot.utils.YouTubeUtil.GetPoTokenAndVisitorData;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (jagrosh)
|
||||
*/
|
||||
public class JMusicBot {
|
||||
|
||||
public static Permission[] RECOMMENDED_PERMS = new Permission[]{Permission.MESSAGE_READ, Permission.MESSAGE_WRITE, Permission.MESSAGE_HISTORY, Permission.MESSAGE_ADD_REACTION,
|
||||
public class JMusicBot
|
||||
{
|
||||
public final static Logger LOG = LoggerFactory.getLogger(JMusicBot.class);
|
||||
public final static Permission[] RECOMMENDED_PERMS = {Permission.VIEW_CHANNEL, Permission.MESSAGE_SEND, 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, GatewayIntent.MESSAGE_CONTENT, GatewayIntent.GUILD_EXPRESSIONS, GatewayIntent.SCHEDULED_EVENTS};
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args){
|
||||
// check run mode(s)
|
||||
boolean nogui = false;
|
||||
for(String arg: args)
|
||||
if("-nogui".equalsIgnoreCase(arg))
|
||||
nogui = true;
|
||||
public static void main(String[] args)
|
||||
{
|
||||
if(args.length > 0)
|
||||
switch(args[0].toLowerCase())
|
||||
{
|
||||
case "generate-config":
|
||||
BotConfig.writeDefaultConfig();
|
||||
return;
|
||||
default:
|
||||
}
|
||||
startBot();
|
||||
}
|
||||
|
||||
private static void startBot()
|
||||
{
|
||||
// create prompt to handle startup
|
||||
Prompt prompt = new Prompt("JMusicBot");
|
||||
|
||||
// startup checks
|
||||
OtherUtil.checkVersion(prompt);
|
||||
OtherUtil.checkJavaVersion(prompt);
|
||||
|
||||
// load config
|
||||
Config config = new Config(nogui);
|
||||
BotConfig config = new BotConfig(prompt);
|
||||
config.load();
|
||||
if(!config.isValid())
|
||||
return;
|
||||
LOG.info("Loaded config from " + config.getConfigLocation());
|
||||
|
||||
// set log level from config
|
||||
((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(
|
||||
Level.toLevel(config.getLogLevel(), Level.INFO));
|
||||
|
||||
// set up the listener
|
||||
EventWaiter waiter = new EventWaiter();
|
||||
Bot bot = new Bot(waiter);
|
||||
SettingsManager settings = new SettingsManager();
|
||||
Bot bot = new Bot(waiter, config, settings);
|
||||
CommandClient client = createCommandClient(config, settings, bot);
|
||||
|
||||
AboutCommand.IS_AUTHOR = false;
|
||||
|
||||
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.ONLINE_STATUS)
|
||||
.setActivity(config.isGameNone() ? null : Activity.playing("loading..."))
|
||||
.setStatus(config.getStatus()==OnlineStatus.INVISIBLE || config.getStatus()==OnlineStatus.OFFLINE
|
||||
? OnlineStatus.INVISIBLE : OnlineStatus.DO_NOT_DISTURB)
|
||||
.setAudioModuleConfig(createAudioModuleConfig())
|
||||
.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()))
|
||||
{
|
||||
LOG.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(IllegalStateException ex)
|
||||
{
|
||||
prompt.alert(Prompt.Level.ERROR, "JMusicBot", ex.getMessage());
|
||||
LOG.error("Failed to initialize DAVE protocol support", ex);
|
||||
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);
|
||||
}
|
||||
|
||||
GetPoTokenAndVisitorData(config.getChromePath(), config.getChromeDriverPath(), config.getChromeHeadless());
|
||||
}
|
||||
|
||||
private static AudioModuleConfig createAudioModuleConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeDaveFactory.ensureAvailable();
|
||||
LOG.info("Configured DAVE protocol support using libdave-jvm.");
|
||||
return new AudioModuleConfig()
|
||||
.withDaveSessionFactory(new LDJDADaveSessionFactory(new NativeDaveFactory()));
|
||||
}
|
||||
catch(Throwable ex)
|
||||
{
|
||||
throw new IllegalStateException("Unable to initialize DAVE protocol support via libdave-jvm. "
|
||||
+ "Verify native dependencies are present for this platform (Linux glibc/musl x86_64, Windows x86_64, or macOS).", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static CommandClient createCommandClient(BotConfig config, SettingsManager settings, Bot bot)
|
||||
{
|
||||
// instantiate about command
|
||||
LoggedAboutCmd aboutCommand = new LoggedAboutCmd(bot, Color.BLUE.brighter(),
|
||||
"a music bot that is [easy to host yourself!](https://github.com/chrisb09/MusicBot) (v" + OtherUtil.getCurrentVersion() + ")",
|
||||
new String[]{"High-quality music playback", "FairQueue™ Technology", "Easy to host yourself"},
|
||||
RECOMMENDED_PERMS);
|
||||
aboutCommand.setIsAuthor(false);
|
||||
aboutCommand.setReplacementCharacter("\uD83C\uDFB6"); // 🎶
|
||||
|
||||
// set up the command client
|
||||
|
||||
CommandClientBuilder cb = new CommandClientBuilder()
|
||||
.setPrefix(config.getPrefix())
|
||||
.setOwnerId(config.getOwnerId())
|
||||
.setAlternativePrefix(config.getAltPrefix())
|
||||
.setOwnerId(Long.toString(config.getOwnerId()))
|
||||
.setEmojis(config.getSuccess(), config.getWarning(), config.getError())
|
||||
.setHelpWord(config.getHelp())
|
||||
.addCommands(
|
||||
new AboutCommand(Color.BLUE.brighter(),
|
||||
"a music bot that is [easy to host yourself!](https://github.com/jagrosh/MusicBot) (v0.0.5)",
|
||||
new String[]{"High-quality music playback", "FairQueue™ Technology", "Easy to host yourself"},
|
||||
RECOMMENDED_PERMS),
|
||||
new PingCommand(),
|
||||
.setLinkedCacheSize(200)
|
||||
.setGuildSettingsManager(settings)
|
||||
.addCommands(aboutCommand,
|
||||
new LoggedPingCmd(bot),
|
||||
new SettingsCmd(bot),
|
||||
new StatsCmd(bot),
|
||||
|
||||
new LyricsCmd(bot),
|
||||
new NowplayingCmd(bot),
|
||||
new PlayCmd(bot),
|
||||
new PlaylistsCmd(bot),
|
||||
@@ -81,57 +224,53 @@ public class JMusicBot {
|
||||
new RemoveCmd(bot),
|
||||
new SearchCmd(bot),
|
||||
new SCSearchCmd(bot),
|
||||
new SeekCmd(bot),
|
||||
new ShuffleCmd(bot),
|
||||
new SkipCmd(bot),
|
||||
|
||||
|
||||
new ForceRemoveCmd(bot),
|
||||
new ForceskipCmd(bot),
|
||||
new MoveTrackCmd(bot),
|
||||
new PauseCmd(bot),
|
||||
new PlaynextCmd(bot),
|
||||
new RepeatCmd(bot),
|
||||
new SkiptoCmd(bot),
|
||||
new StopCmd(bot),
|
||||
new VolumeCmd(bot),
|
||||
|
||||
new PrefixCmd(bot),
|
||||
new QueueTypeCmd(bot),
|
||||
new SetdjCmd(bot),
|
||||
new SkipratioCmd(bot),
|
||||
new SettcCmd(bot),
|
||||
new SetvcCmd(bot),
|
||||
|
||||
//new GuildlistCommand(waiter),
|
||||
|
||||
new AutoplaylistCmd(bot),
|
||||
new DebugCmd(bot),
|
||||
new PlaylistCmd(bot),
|
||||
new SetavatarCmd(bot),
|
||||
new SetgameCmd(bot),
|
||||
new SetnameCmd(bot),
|
||||
new SetstatusCmd(bot),
|
||||
new ShutdownCmd(bot)
|
||||
);
|
||||
if(config.getGame()==null)
|
||||
|
||||
// enable eval if applicable
|
||||
if(config.useEval())
|
||||
cb.addCommand(new EvalCmd(bot));
|
||||
|
||||
// set status if set in config
|
||||
if(config.getStatus() != OnlineStatus.UNKNOWN)
|
||||
cb.setStatus(config.getStatus());
|
||||
|
||||
// set game
|
||||
if(config.getGame() == null)
|
||||
cb.useDefaultGame();
|
||||
else if(config.isGameNone())
|
||||
cb.setActivity(null);
|
||||
else
|
||||
cb.setGame(Game.of(config.getGame()));
|
||||
CommandClient client = cb.build();
|
||||
cb.setActivity(config.getGame());
|
||||
|
||||
if(!config.getNoGui())
|
||||
{
|
||||
try {
|
||||
GUI gui = new GUI(bot);
|
||||
bot.setGUI(gui);
|
||||
gui.init();
|
||||
} catch(Exception e) {
|
||||
SimpleLog.getLog("Startup").fatal("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 -nogui flag.");
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to log in and start
|
||||
try {
|
||||
new JDABuilder(AccountType.BOT)
|
||||
.setToken(config.getToken())
|
||||
.setAudioEnabled(true)
|
||||
.setGame(Game.of("loading..."))
|
||||
.setStatus(OnlineStatus.DO_NOT_DISTURB)
|
||||
.addEventListener(client)
|
||||
.addEventListener(waiter)
|
||||
.addEventListener(bot)
|
||||
.buildAsync();
|
||||
} catch (LoginException | IllegalArgumentException | RateLimitedException ex) {
|
||||
SimpleLog.getLog("Login").fatal(ex);
|
||||
}
|
||||
return cb.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.jagrosh.jmusicbot.utils.OtherUtil;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.jagrosh.jmusicbot.utils.YoutubeOauth2TokenHandler;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||
import net.dv8tion.jda.api.events.session.ReadyEvent;
|
||||
import net.dv8tion.jda.api.events.session.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.MessageDeleteEvent;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class Listener extends ListenerAdapter
|
||||
{
|
||||
private final Bot bot;
|
||||
|
||||
public Listener(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReady(ReadyEvent event)
|
||||
{
|
||||
if(event.getJDA().getGuildCache().isEmpty())
|
||||
{
|
||||
Logger log = LoggerFactory.getLogger("MusicBot");
|
||||
log.warn("This bot is not on any guilds! Use the following link to add the bot to your guilds!");
|
||||
log.warn(event.getJDA().getInviteUrl(JMusicBot.RECOMMENDED_PERMS));
|
||||
}
|
||||
credit(event.getJDA());
|
||||
event.getJDA().getGuilds().forEach((guild) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
String defpl = bot.getSettingsManager().getSettings(guild).getDefaultPlaylist();
|
||||
VoiceChannel vc = bot.getSettingsManager().getSettings(guild).getVoiceChannel(guild);
|
||||
if(defpl!=null && vc!=null && bot.getPlayerManager().setUpHandler(guild).playFromDefault())
|
||||
{
|
||||
guild.getAudioManager().openAudioConnection(vc);
|
||||
}
|
||||
}
|
||||
catch(Exception ignore) {}
|
||||
});
|
||||
if(bot.getConfig().useUpdateAlerts())
|
||||
{
|
||||
bot.getThreadpool().scheduleWithFixedDelay(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
User owner = bot.getJDA().retrieveUserById(bot.getConfig().getOwnerId()).complete();
|
||||
String currentVersion = OtherUtil.getCurrentVersion();
|
||||
String latestVersion = OtherUtil.getLatestVersion();
|
||||
if(latestVersion!=null && !currentVersion.equalsIgnoreCase(latestVersion))
|
||||
{
|
||||
String msg = String.format(OtherUtil.NEW_VERSION_AVAILABLE, currentVersion, latestVersion);
|
||||
owner.openPrivateChannel().queue(pc -> pc.sendMessage(msg).queue());
|
||||
}
|
||||
}
|
||||
catch(Exception ignored) {} // ignored
|
||||
}, 0, 24, TimeUnit.HOURS);
|
||||
}
|
||||
if (bot.getConfig().useYoutubeOauth2())
|
||||
{
|
||||
YoutubeOauth2TokenHandler.Data data = bot.getYouTubeOauth2Handler().getData();
|
||||
if (data != null)
|
||||
{
|
||||
PrivateChannel channel = bot.getJDA().openPrivateChannelById(bot.getConfig().getOwnerId()).complete();
|
||||
channel
|
||||
.sendMessage(
|
||||
"# DO NOT AUTHORISE THIS WITH YOUR MAIN GOOGLE ACCOUNT!!!\n"
|
||||
+ "## Create or use an alternative/burner Google account!\n"
|
||||
+ "To give JMusicBot access to your Google account, go to "
|
||||
+ data.getAuthorisationUrl()
|
||||
+ " and enter the code **" + data.getCode() + "**")
|
||||
.queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageDelete(MessageDeleteEvent event)
|
||||
{
|
||||
if(event.isFromGuild()) {
|
||||
bot.getNowplayingHandler().onMessageDelete(event.getGuild(), event.getMessageIdLong());
|
||||
bot.getStatusMessageHandler().onMessageDelete(event.getGuild(), event.getMessageIdLong());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(MessageReceivedEvent event)
|
||||
{
|
||||
bot.getStatusMessageHandler().onMessageReceived(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event)
|
||||
{
|
||||
bot.getAloneInVoiceHandler().onVoiceUpdate(event);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().onVoiceUpdate(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdown(ShutdownEvent event)
|
||||
{
|
||||
bot.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuildJoin(GuildJoinEvent event)
|
||||
{
|
||||
credit(event.getJDA());
|
||||
}
|
||||
|
||||
// make sure people aren't adding clones to dbots
|
||||
private void credit(JDA jda)
|
||||
{
|
||||
Guild dbots = jda.getGuildById(110373943822540800L);
|
||||
if(dbots==null)
|
||||
return;
|
||||
if(bot.getConfig().getDBots())
|
||||
return;
|
||||
jda.getTextChannelById(119222314964353025L)
|
||||
.sendMessage("This account is running JMusicBot. Please do not list bot clones on this server, <@"+bot.getConfig().getOwnerId()+">.").complete();
|
||||
dbots.leave().queue();
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class Settings {
|
||||
public final static Settings DEFAULT_SETTINGS = new Settings(0, 0, 0, 100, null);
|
||||
|
||||
private long textId;
|
||||
private long voiceId;
|
||||
private long roleId;
|
||||
private int volume;
|
||||
private String defaultPlaylist;
|
||||
|
||||
public Settings(String textId, String voiceId, String roleId, int volume, String defaultPlaylist)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.textId = Long.parseLong(textId);
|
||||
}
|
||||
catch(NumberFormatException e)
|
||||
{
|
||||
this.textId = 0;
|
||||
}
|
||||
try
|
||||
{
|
||||
this.voiceId = Long.parseLong(voiceId);
|
||||
}
|
||||
catch(NumberFormatException e)
|
||||
{
|
||||
this.voiceId = 0;
|
||||
}
|
||||
try
|
||||
{
|
||||
this.roleId = Long.parseLong(roleId);
|
||||
}
|
||||
catch(NumberFormatException e)
|
||||
{
|
||||
this.roleId = 0;
|
||||
}
|
||||
this.volume = volume;
|
||||
this.defaultPlaylist = defaultPlaylist;
|
||||
}
|
||||
|
||||
public Settings(long textId, long voiceId, long roleId, int volume, String defaultPlaylist)
|
||||
{
|
||||
this.textId = textId;
|
||||
this.voiceId = voiceId;
|
||||
this.roleId = roleId;
|
||||
this.volume = volume;
|
||||
this.defaultPlaylist = defaultPlaylist;
|
||||
}
|
||||
|
||||
public long getTextId()
|
||||
{
|
||||
return textId;
|
||||
}
|
||||
|
||||
public long getVoiceId()
|
||||
{
|
||||
return voiceId;
|
||||
}
|
||||
|
||||
public long getRoleId()
|
||||
{
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public int getVolume()
|
||||
{
|
||||
return volume;
|
||||
}
|
||||
|
||||
public String getDefaultPlaylist()
|
||||
{
|
||||
return defaultPlaylist;
|
||||
}
|
||||
|
||||
public void setTextId(long id)
|
||||
{
|
||||
this.textId = id;
|
||||
}
|
||||
|
||||
public void setVoiceId(long id)
|
||||
{
|
||||
this.voiceId = id;
|
||||
}
|
||||
|
||||
public void setRoleId(long id)
|
||||
{
|
||||
this.roleId = id;
|
||||
}
|
||||
|
||||
public void setVolume(int volume)
|
||||
{
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
public void setDefaultPlaylist(String defaultPlaylist)
|
||||
{
|
||||
this.defaultPlaylist = defaultPlaylist;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,14 @@
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.audio;
|
||||
|
||||
import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist;
|
||||
import com.jagrosh.jmusicbot.queue.AbstractQueue;
|
||||
import com.jagrosh.jmusicbot.settings.QueueType;
|
||||
import com.jagrosh.jmusicbot.utils.TimeUtil;
|
||||
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.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
|
||||
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
|
||||
@@ -24,61 +30,101 @@ import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.playlist.Playlist;
|
||||
import com.jagrosh.jmusicbot.queue.FairQueue;
|
||||
import net.dv8tion.jda.core.audio.AudioSendHandler;
|
||||
import net.dv8tion.jda.core.entities.Guild;
|
||||
import net.dv8tion.jda.core.entities.User;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioTrack;
|
||||
import java.nio.ByteBuffer;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||
import net.dv8tion.jda.api.audio.AudioSendHandler;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class AudioHandler extends AudioEventAdapter implements AudioSendHandler {
|
||||
private final AudioPlayer audioPlayer;
|
||||
private final Guild guild;
|
||||
private final FairQueue<QueuedTrack> queue;
|
||||
private final Set<String> votes;
|
||||
private final List<AudioTrack> defaultQueue;
|
||||
private final Bot bot;
|
||||
private AudioFrame lastFrame;
|
||||
private QueuedTrack current;
|
||||
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"; // ⏹
|
||||
|
||||
public AudioHandler(AudioPlayer audioPlayer, Guild guild, Bot bot) {
|
||||
this.audioPlayer = audioPlayer;
|
||||
this.guild = guild;
|
||||
this.bot = bot;
|
||||
queue = new FairQueue<>();
|
||||
votes = new HashSet<>();
|
||||
defaultQueue = new LinkedList<>();
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(AudioHandler.class);
|
||||
|
||||
private final List<AudioTrack> defaultQueue = new LinkedList<>();
|
||||
private final Set<String> votes = new HashSet<>();
|
||||
|
||||
private final PlayerManager manager;
|
||||
private final AudioPlayer audioPlayer;
|
||||
private final long guildId;
|
||||
|
||||
private AudioFrame lastFrame;
|
||||
private AbstractQueue<QueuedTrack> queue;
|
||||
private volatile String playCountTrackIdentifier;
|
||||
private volatile long currentTrackServerPlayCount = -1L;
|
||||
|
||||
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 int addTrack(AudioTrack track, User user)
|
||||
public void setQueueType(QueueType type)
|
||||
{
|
||||
QueuedTrack qt = new QueuedTrack(track, user.getId());
|
||||
if(current==null)
|
||||
queue = type.createInstance(queue);
|
||||
}
|
||||
|
||||
public int addTrackToFront(QueuedTrack qtrack)
|
||||
{
|
||||
if(audioPlayer.getPlayingTrack()==null)
|
||||
{
|
||||
current = qt;
|
||||
audioPlayer.playTrack(track);
|
||||
audioPlayer.playTrack(qtrack.getTrack());
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return queue.add(qt);
|
||||
queue.addAt(0, qtrack);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public QueuedTrack getCurrentTrack()
|
||||
public int addTrack(QueuedTrack qtrack)
|
||||
{
|
||||
return current;
|
||||
if(audioPlayer.getPlayingTrack()==null)
|
||||
{
|
||||
audioPlayer.playTrack(qtrack.getTrack());
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
return queue.add(qtrack);
|
||||
}
|
||||
|
||||
public FairQueue<QueuedTrack> getQueue()
|
||||
public AbstractQueue<QueuedTrack> getQueue()
|
||||
{
|
||||
return queue;
|
||||
}
|
||||
|
||||
public void stopAndClear()
|
||||
{
|
||||
queue.clear();
|
||||
defaultQueue.clear();
|
||||
audioPlayer.stopTrack();
|
||||
//current = null;
|
||||
}
|
||||
|
||||
public boolean isMusicPlaying(JDA jda)
|
||||
{
|
||||
return guild(jda).getSelfMember().getVoiceState().inAudioChannel() && audioPlayer.getPlayingTrack()!=null;
|
||||
}
|
||||
|
||||
public Set<String> getVotes()
|
||||
{
|
||||
return votes;
|
||||
@@ -89,69 +135,317 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
|
||||
return audioPlayer;
|
||||
}
|
||||
|
||||
public RequestMetadata getRequestMetadata()
|
||||
{
|
||||
if(audioPlayer.getPlayingTrack() == null)
|
||||
return RequestMetadata.EMPTY;
|
||||
RequestMetadata rm = audioPlayer.getPlayingTrack().getUserData(RequestMetadata.class);
|
||||
return rm == null ? RequestMetadata.EMPTY : rm;
|
||||
}
|
||||
|
||||
public boolean playFromDefault()
|
||||
{
|
||||
if(!defaultQueue.isEmpty())
|
||||
{
|
||||
current = new QueuedTrack(defaultQueue.remove(0), null);
|
||||
audioPlayer.playTrack(current.getTrack());
|
||||
audioPlayer.playTrack(defaultQueue.remove(0));
|
||||
return true;
|
||||
}
|
||||
if(bot.getSettings(guild)==null || bot.getSettings(guild).getDefaultPlaylist()==null)
|
||||
Settings settings = manager.getBot().getSettingsManager().getSettings(guildId);
|
||||
if(settings==null || settings.getDefaultPlaylist()==null)
|
||||
return false;
|
||||
Playlist pl = Playlist.loadPlaylist(bot.getSettings(guild).getDefaultPlaylist());
|
||||
|
||||
Playlist pl = manager.getBot().getPlaylistLoader().getPlaylist(settings.getDefaultPlaylist());
|
||||
if(pl==null || pl.getItems().isEmpty())
|
||||
return false;
|
||||
pl.loadTracks(bot.getAudioManager(), () -> {
|
||||
if(pl.getTracks().isEmpty())
|
||||
{
|
||||
current = null;
|
||||
guild.getAudioManager().closeAudioConnection();
|
||||
}
|
||||
pl.loadTracks(manager, (at) ->
|
||||
{
|
||||
at.setUserData(RequestMetadata.fromDefaultPlaylist(settings.getDefaultPlaylist()));
|
||||
if(audioPlayer.getPlayingTrack()==null)
|
||||
audioPlayer.playTrack(at);
|
||||
else
|
||||
{
|
||||
defaultQueue.addAll(pl.getTracks());
|
||||
playFromDefault();
|
||||
}
|
||||
defaultQueue.add(at);
|
||||
if(manager.getBot().getDataLogService() != null)
|
||||
manager.getBot().getDataLogService().logQueueAdd(guild(manager.getBot().getJDA()), null, at, "DEFAULT_PLAYLIST", null, null, settings.getDefaultPlaylist());
|
||||
}, () ->
|
||||
{
|
||||
if(pl.getTracks().isEmpty() && !manager.getBot().getConfig().getStay())
|
||||
manager.getBot().closeAudioConnection(guildId);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Audio Events
|
||||
@Override
|
||||
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
|
||||
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason)
|
||||
{
|
||||
if(manager.getBot().getDataLogService() != null)
|
||||
manager.getBot().getDataLogService().logPlayEnd(guild(manager.getBot().getJDA()), endReason.name());
|
||||
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 && repeatMode != RepeatMode.OFF)
|
||||
{
|
||||
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())
|
||||
{
|
||||
if(!playFromDefault())
|
||||
{
|
||||
current = null;
|
||||
guild.getAudioManager().closeAudioConnection();
|
||||
manager.getBot().getNowplayingHandler().onTrackUpdate(null);
|
||||
manager.getBot().getStatusMessageHandler().onTrackUpdate(guildId);
|
||||
if(!manager.getBot().getConfig().getStay())
|
||||
manager.getBot().closeAudioConnection(guildId);
|
||||
// unpause, in the case when the player was paused and the track has been skipped.
|
||||
// this is to prevent the player being paused next time it's being used.
|
||||
player.setPaused(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
current = queue.pull();
|
||||
player.playTrack(current.getTrack());
|
||||
QueuedTrack qt = queue.pull();
|
||||
player.playTrack(qt.getTrack());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackStart(AudioPlayer player, AudioTrack track) {
|
||||
public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception)
|
||||
{
|
||||
if (
|
||||
exception.getMessage().equals("Sign in to confirm you're not a bot")
|
||||
|| exception.getMessage().equals("Please sign in")
|
||||
)
|
||||
LOGGER.error(
|
||||
"Track {} has failed to play: {}"
|
||||
+ "You will need to sign in to Google to play YouTube tracks. More info: https://jmusicbot.com/youtube-oauth2",
|
||||
track.getIdentifier(),
|
||||
exception.getMessage()
|
||||
);
|
||||
else
|
||||
LOGGER.error("Track {} has failed to play", track.getIdentifier(), exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackStart(AudioPlayer player, AudioTrack track)
|
||||
{
|
||||
votes.clear();
|
||||
manager.getBot().getNowplayingHandler().onTrackUpdate(track);
|
||||
manager.getBot().getStatusMessageHandler().onTrackUpdate(guildId);
|
||||
if(manager.getBot().getDataLogService() != null)
|
||||
{
|
||||
RequestMetadata rm = getRequestMetadata();
|
||||
String source = rm.queueInfo == null ? null : rm.queueInfo.source;
|
||||
String searchQuery = rm.queueInfo == null ? null : rm.queueInfo.searchQuery;
|
||||
String playlistName = rm.queueInfo == null ? null : rm.queueInfo.playlistName;
|
||||
manager.getBot().getDataLogService().logPlayStart(guild(manager.getBot().getJDA()), track, rm, source, searchQuery, playlistName);
|
||||
playCountTrackIdentifier = track.getIdentifier();
|
||||
currentTrackServerPlayCount = manager.getBot().getDataLogService().getGuildTrackPlayCount(guild(manager.getBot().getJDA()), track);
|
||||
}
|
||||
else
|
||||
{
|
||||
playCountTrackIdentifier = null;
|
||||
currentTrackServerPlayCount = -1L;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Formatting
|
||||
public MessageCreateData getNowPlaying(JDA jda)
|
||||
{
|
||||
if(isMusicPlaying(jda))
|
||||
{
|
||||
Guild guild = guild(jda);
|
||||
AudioTrack track = audioPlayer.getPlayingTrack();
|
||||
MessageCreateBuilder mb = new MessageCreateBuilder();
|
||||
mb.addContent(FormatUtil.filter(manager.getBot().getConfig().getSuccess()+" **Now Playing in "+guild.getSelfMember().getVoiceState().getChannel().getAsMention()+"...**"));
|
||||
EmbedBuilder eb = new EmbedBuilder();
|
||||
eb.setColor(guild.getSelfMember().getColor());
|
||||
RequestMetadata rm = getRequestMetadata();
|
||||
if(rm.getOwner() != 0L)
|
||||
{
|
||||
User u = guild.getJDA().getUserById(rm.user.id);
|
||||
if(u==null)
|
||||
eb.setAuthor(FormatUtil.formatUsername(rm.user), null, rm.user.avatar);
|
||||
else
|
||||
eb.setAuthor(FormatUtil.formatUsername(u), null, u.getEffectiveAvatarUrl());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
eb.setTitle(track.getInfo().title, track.getInfo().uri);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
eb.setTitle(track.getInfo().title);
|
||||
}
|
||||
|
||||
if(track instanceof YoutubeAudioTrack && manager.getBot().getConfig().useNPImages())
|
||||
{
|
||||
eb.setThumbnail("https://img.youtube.com/vi/"+track.getIdentifier()+"/mqdefault.jpg");
|
||||
}
|
||||
|
||||
if(track.getInfo().author != null && !track.getInfo().author.isEmpty())
|
||||
eb.setFooter("Source: " + track.getInfo().author, null);
|
||||
|
||||
double progress = (double)audioPlayer.getPlayingTrack().getPosition()/track.getDuration();
|
||||
eb.setDescription(getStatusEmoji()
|
||||
+ " "+FormatUtil.progressBar(progress)
|
||||
+ " `[" + TimeUtil.formatTime(track.getPosition()) + "/" + TimeUtil.formatTime(track.getDuration()) + "]` "
|
||||
+ FormatUtil.volumeIcon(audioPlayer.getVolume()));
|
||||
if(currentTrackServerPlayCount > 0L && track.getIdentifier().equals(playCountTrackIdentifier))
|
||||
eb.addField("Server plays", "Played " + currentTrackServerPlayCount + " time" + (currentTrackServerPlayCount == 1L ? "" : "s") + " on this server", false);
|
||||
|
||||
return mb.setEmbeds(eb.build()).build();
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProvide() {
|
||||
lastFrame = audioPlayer.provide();
|
||||
return lastFrame != null;
|
||||
public MessageCreateData getNoMusicPlaying(JDA jda)
|
||||
{
|
||||
Guild guild = guild(jda);
|
||||
return new MessageCreateBuilder()
|
||||
.setContent(FormatUtil.filter(manager.getBot().getConfig().getSuccess()+" **Now Playing...**"))
|
||||
.setEmbeds(new EmbedBuilder()
|
||||
.setTitle("No music playing")
|
||||
.setDescription(STOP_EMOJI+" "+FormatUtil.progressBar(-1)+" "+FormatUtil.volumeIcon(audioPlayer.getVolume()))
|
||||
.setColor(guild.getSelfMember().getColor())
|
||||
.build()).build();
|
||||
}
|
||||
|
||||
public MessageCreateData getStatusMessage(JDA jda)
|
||||
{
|
||||
Guild guild = guild(jda);
|
||||
if(guild == null)
|
||||
return null;
|
||||
MessageCreateBuilder mb = new MessageCreateBuilder();
|
||||
mb.setContent(FormatUtil.filter(manager.getBot().getConfig().getSuccess()+" **Now Playing...**"));
|
||||
|
||||
EmbedBuilder eb = new EmbedBuilder();
|
||||
eb.setColor(guild.getSelfMember().getColor());
|
||||
|
||||
AudioTrack track = audioPlayer.getPlayingTrack();
|
||||
if(isMusicPlaying(jda) && track != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
eb.setTitle(track.getInfo().title, track.getInfo().uri);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
eb.setTitle(track.getInfo().title);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
eb.setTitle("No music playing");
|
||||
}
|
||||
|
||||
StringBuilder desc = new StringBuilder();
|
||||
if(isMusicPlaying(jda) && track != null)
|
||||
{
|
||||
double progress = (double)track.getPosition()/track.getDuration();
|
||||
desc.append(getStatusEmoji())
|
||||
.append(" ").append(FormatUtil.progressBar(progress))
|
||||
.append(" `[").append(TimeUtil.formatTime(track.getPosition()))
|
||||
.append("/").append(TimeUtil.formatTime(track.getDuration())).append("]` ")
|
||||
.append(FormatUtil.volumeIcon(audioPlayer.getVolume()));
|
||||
if(currentTrackServerPlayCount > 0L && track.getIdentifier().equals(playCountTrackIdentifier))
|
||||
desc.append("\nPlayed ").append(currentTrackServerPlayCount).append(" time")
|
||||
.append(currentTrackServerPlayCount == 1L ? "" : "s").append(" on this server");
|
||||
}
|
||||
else
|
||||
{
|
||||
desc.append(STOP_EMOJI)
|
||||
.append(" ").append(FormatUtil.progressBar(-1))
|
||||
.append(" ").append(FormatUtil.volumeIcon(audioPlayer.getVolume()));
|
||||
}
|
||||
|
||||
List<QueuedTrack> list = queue.getList();
|
||||
if(!list.isEmpty())
|
||||
{
|
||||
QueuedTrack next = list.get(0);
|
||||
String nextTitle = next.getTrack().getInfo().title;
|
||||
String requester = next.getRequestMetadata() == null || next.getRequestMetadata().user == null
|
||||
? null
|
||||
: FormatUtil.formatUsername(next.getRequestMetadata().user);
|
||||
desc.append("\n").append("Next: **").append(nextTitle).append("**");
|
||||
if(requester != null && !requester.isEmpty())
|
||||
desc.append(" (by ").append(requester).append(")");
|
||||
}
|
||||
|
||||
long total = 0L;
|
||||
for(QueuedTrack qt : list)
|
||||
total += qt.getTrack().getDuration();
|
||||
Settings settings = manager.getBot().getSettingsManager().getSettings(guildId);
|
||||
desc.append("\n")
|
||||
.append(manager.getBot().getConfig().getSuccess())
|
||||
.append(" Queue | ").append(list.size()).append(" entries | `")
|
||||
.append(TimeUtil.formatTime(total)).append("` | ")
|
||||
.append(settings.getQueueType().getEmoji()).append(" `")
|
||||
.append(settings.getQueueType().getUserFriendlyName()).append("`");
|
||||
if(settings.getRepeatMode().getEmoji() != null)
|
||||
desc.append(" | ").append(settings.getRepeatMode().getEmoji());
|
||||
|
||||
if(!guild.getSelfMember().getVoiceState().inAudioChannel())
|
||||
desc.append("\n").append(manager.getBot().getConfig().getWarning()).append(" Disconnected");
|
||||
|
||||
eb.setDescription(FormatUtil.filter(desc.toString()));
|
||||
return mb.setEmbeds(eb.build()).build();
|
||||
}
|
||||
|
||||
public String getStatusEmoji()
|
||||
{
|
||||
return audioPlayer.isPaused() ? PAUSE_EMOJI : PLAY_EMOJI;
|
||||
}
|
||||
|
||||
// Audio Send Handler methods
|
||||
/*@Override
|
||||
public boolean canProvide()
|
||||
{
|
||||
if (lastFrame == null)
|
||||
lastFrame = audioPlayer.provide();
|
||||
|
||||
return lastFrame != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] provide20MsAudio() {
|
||||
return lastFrame.data;
|
||||
public byte[] provide20MsAudio()
|
||||
{
|
||||
if (lastFrame == null)
|
||||
lastFrame = audioPlayer.provide();
|
||||
|
||||
byte[] data = lastFrame != null ? lastFrame.getData() : null;
|
||||
lastFrame = null;
|
||||
|
||||
return data;
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public boolean canProvide()
|
||||
{
|
||||
lastFrame = audioPlayer.provide();
|
||||
return lastFrame != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpus() {
|
||||
return true;
|
||||
public ByteBuffer provide20MsAudio()
|
||||
{
|
||||
return ByteBuffer.wrap(lastFrame.getData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpus()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Private methods
|
||||
private Guild guild(JDA jda)
|
||||
{
|
||||
return jda.getGuildById(guildId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.audio;
|
||||
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.entities.Pair;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.exceptions.PermissionException;
|
||||
import net.dv8tion.jda.api.exceptions.RateLimitedException;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageEditData;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class NowplayingHandler
|
||||
{
|
||||
private final Bot bot;
|
||||
private final HashMap<Long,Pair<Long,Long>> lastNP; // guild -> channel,message
|
||||
|
||||
public NowplayingHandler(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.lastNP = new HashMap<>();
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
if(!bot.getConfig().useNPImages())
|
||||
bot.getThreadpool().scheduleWithFixedDelay(() -> updateAll(), 0, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void setLastNPMessage(Message m)
|
||||
{
|
||||
lastNP.put(m.getGuild().getIdLong(), new Pair<>(m.getChannel().getIdLong(), m.getIdLong()));
|
||||
}
|
||||
|
||||
public void clearLastNPMessage(Guild guild)
|
||||
{
|
||||
lastNP.remove(guild.getIdLong());
|
||||
}
|
||||
|
||||
private void updateAll()
|
||||
{
|
||||
Set<Long> toRemove = new HashSet<>();
|
||||
for(long guildId: lastNP.keySet())
|
||||
{
|
||||
Guild guild = bot.getJDA().getGuildById(guildId);
|
||||
if(guild==null)
|
||||
{
|
||||
toRemove.add(guildId);
|
||||
continue;
|
||||
}
|
||||
Pair<Long,Long> pair = lastNP.get(guildId);
|
||||
TextChannel tc = guild.getTextChannelById(pair.getKey());
|
||||
if(tc==null)
|
||||
{
|
||||
toRemove.add(guildId);
|
||||
continue;
|
||||
}
|
||||
AudioHandler handler = (AudioHandler)guild.getAudioManager().getSendingHandler();
|
||||
MessageCreateData msg = handler.getNowPlaying(bot.getJDA());
|
||||
if(msg==null)
|
||||
{
|
||||
msg = handler.getNoMusicPlaying(bot.getJDA());
|
||||
toRemove.add(guildId);
|
||||
}
|
||||
try
|
||||
{
|
||||
tc.editMessageById(pair.getValue(), MessageEditData.fromCreateData(msg)).queue(m->{}, t -> lastNP.remove(guildId));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
toRemove.add(guildId);
|
||||
}
|
||||
}
|
||||
toRemove.forEach(id -> lastNP.remove(id));
|
||||
}
|
||||
|
||||
// "event"-based methods
|
||||
public void onTrackUpdate(AudioTrack track)
|
||||
{
|
||||
// update bot status if applicable
|
||||
if(bot.getConfig().getSongInStatus())
|
||||
{
|
||||
if(track!=null && bot.getJDA().getGuilds().stream().filter(g -> g.getSelfMember().getVoiceState().inAudioChannel()).count()<=1)
|
||||
bot.getJDA().getPresence().setActivity(Activity.listening(track.getInfo().title));
|
||||
else
|
||||
bot.resetGame();
|
||||
}
|
||||
}
|
||||
|
||||
public void onMessageDelete(Guild guild, long messageId)
|
||||
{
|
||||
Pair<Long,Long> pair = lastNP.get(guild.getIdLong());
|
||||
if(pair==null)
|
||||
return;
|
||||
if(pair.getValue() == messageId)
|
||||
lastNP.remove(guild.getIdLong());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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.audio;
|
||||
|
||||
import com.dunctebot.sourcemanagers.DuncteBotSources;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.BotConfig;
|
||||
import com.jagrosh.jmusicbot.utils.OtherUtil;
|
||||
import com.jagrosh.jmusicbot.utils.YouTubeUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.container.MediaContainerRegistry;
|
||||
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.bandcamp.BandcampAudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.beam.BeamAudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.getyarn.GetyarnAudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.http.HttpAudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.nico.NicoAudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.twitch.TwitchStreamAudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.vimeo.VimeoAudioSourceManager;
|
||||
import com.sedmelluq.lava.extensions.youtuberotator.YoutubeIpRotatorSetup;
|
||||
import com.sedmelluq.lava.extensions.youtuberotator.planner.AbstractRoutePlanner;
|
||||
import com.sedmelluq.lava.extensions.youtuberotator.planner.BalancingIpRoutePlanner;
|
||||
import com.sedmelluq.lava.extensions.youtuberotator.planner.NanoIpRoutePlanner;
|
||||
import com.sedmelluq.lava.extensions.youtuberotator.tools.ip.IpBlock;
|
||||
import com.sedmelluq.lava.extensions.youtuberotator.tools.ip.Ipv4Block;
|
||||
import com.sedmelluq.lava.extensions.youtuberotator.tools.ip.Ipv6Block;
|
||||
import dev.lavalink.youtube.YoutubeAudioSourceManager;
|
||||
import dev.lavalink.youtube.clients.Android;
|
||||
import dev.lavalink.youtube.clients.AndroidMusic;
|
||||
import dev.lavalink.youtube.clients.AndroidVr;
|
||||
import dev.lavalink.youtube.clients.ClientOptions;
|
||||
import dev.lavalink.youtube.clients.Ios;
|
||||
import dev.lavalink.youtube.clients.MWeb;
|
||||
import dev.lavalink.youtube.clients.Music;
|
||||
import dev.lavalink.youtube.clients.Tv;
|
||||
import dev.lavalink.youtube.clients.TvHtml5Simply;
|
||||
import dev.lavalink.youtube.clients.Web;
|
||||
import dev.lavalink.youtube.clients.WebEmbedded;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class PlayerManager extends DefaultAudioPlayerManager
|
||||
{
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(PlayerManager.class);
|
||||
private final Bot bot;
|
||||
private final BotConfig config;
|
||||
|
||||
public PlayerManager(Bot bot, BotConfig config)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
TransformativeAudioSourceManager.createTransforms(bot.getConfig().getTransforms()).forEach(t -> registerSourceManager(t));
|
||||
|
||||
YoutubeAudioSourceManager yt = setupYoutubeAudioSourceManager();
|
||||
if (config.getYTRoutingPlanner() != YouTubeUtil.RoutingPlanner.NONE)
|
||||
{
|
||||
AbstractRoutePlanner routePlanner = YouTubeUtil.createRouterPlanner(config.getYTRoutingPlanner(), config.getYTIpBlocks());
|
||||
YoutubeIpRotatorSetup rotator = new YoutubeIpRotatorSetup(routePlanner);
|
||||
|
||||
rotator.forConfiguration(yt.getHttpInterfaceManager(), false)
|
||||
.withMainDelegateFilter(yt.getContextFilter())
|
||||
.setup();
|
||||
}
|
||||
|
||||
yt.setPlaylistPageCount(bot.getConfig().getMaxYTPlaylistPages());
|
||||
registerSourceManager(yt);
|
||||
|
||||
registerSourceManager(SoundCloudAudioSourceManager.createDefault());
|
||||
registerSourceManager(new BandcampAudioSourceManager());
|
||||
registerSourceManager(new VimeoAudioSourceManager());
|
||||
try
|
||||
{
|
||||
registerSourceManager(new TwitchStreamAudioSourceManager());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOGGER.warn("Skipping Twitch source initialization because Twitch lookup failed during startup: {}", e.getMessage());
|
||||
LOGGER.debug("Twitch source initialization failure:", e);
|
||||
}
|
||||
registerSourceManager(new BeamAudioSourceManager());
|
||||
registerSourceManager(new GetyarnAudioSourceManager());
|
||||
registerSourceManager(new NicoAudioSourceManager());
|
||||
registerSourceManager(new HttpAudioSourceManager(MediaContainerRegistry.DEFAULT_REGISTRY));
|
||||
|
||||
AudioSourceManagers.registerLocalSource(this);
|
||||
|
||||
DuncteBotSources.registerAll(this, "en-US");
|
||||
}
|
||||
|
||||
private YoutubeAudioSourceManager setupYoutubeAudioSourceManager()
|
||||
{
|
||||
ClientOptions playbackOnly = new ClientOptions();
|
||||
playbackOnly.setSearching(false);
|
||||
|
||||
YoutubeAudioSourceManager yt = new YoutubeAudioSourceManager(true,
|
||||
new Ios(playbackOnly),
|
||||
new Music(playbackOnly),
|
||||
new AndroidMusic(playbackOnly),
|
||||
new TvHtml5Simply(playbackOnly),
|
||||
new Tv(playbackOnly),
|
||||
new Web(),
|
||||
new MWeb(),
|
||||
new WebEmbedded(),
|
||||
new AndroidVr(playbackOnly),
|
||||
new Android(playbackOnly));
|
||||
yt.setPlaylistPageCount(bot.getConfig().getMaxYTPlaylistPages());
|
||||
|
||||
// OAuth2 setup
|
||||
if (bot.getConfig().useYoutubeOauth2())
|
||||
{
|
||||
String token = null;
|
||||
try
|
||||
{
|
||||
token = Files.readString(OtherUtil.getPath("youtubetoken.txt"));
|
||||
}
|
||||
catch (NoSuchFileException e)
|
||||
{
|
||||
/* ignored */
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn("Failed to read YouTube OAuth2 token file: {}", e.getMessage());
|
||||
return yt;
|
||||
}
|
||||
LOGGER.debug("Read YouTube OAuth2 refresh token from youtubetoken.txt");
|
||||
try
|
||||
{
|
||||
yt.useOauth2(token, false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.warn("Failed to authorise with YouTube. If this issue persists, delete the youtubetoken.txt file to reauthorise.", e);
|
||||
}
|
||||
}
|
||||
return yt;
|
||||
}
|
||||
|
||||
public Bot getBot()
|
||||
{
|
||||
return bot;
|
||||
}
|
||||
|
||||
public boolean hasHandler(Guild guild)
|
||||
{
|
||||
return guild.getAudioManager().getSendingHandler()!=null;
|
||||
}
|
||||
|
||||
public AudioHandler setUpHandler(Guild guild)
|
||||
{
|
||||
AudioHandler handler;
|
||||
if(guild.getAudioManager().getSendingHandler()==null)
|
||||
{
|
||||
AudioPlayer player = createPlayer();
|
||||
player.setVolume(bot.getSettingsManager().getSettings(guild).getVolume());
|
||||
handler = new AudioHandler(this, guild, player);
|
||||
player.addListener(handler);
|
||||
guild.getAudioManager().setSendingHandler(handler);
|
||||
}
|
||||
else
|
||||
handler = (AudioHandler) guild.getAudioManager().getSendingHandler();
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +1,67 @@
|
||||
/*
|
||||
* Copyright 2016 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.track.AudioTrack;
|
||||
import com.jagrosh.jmusicbot.queue.Queueable;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class QueuedTrack implements Queueable {
|
||||
|
||||
private final AudioTrack track;
|
||||
private final String owner;
|
||||
|
||||
public QueuedTrack(AudioTrack track, String owner)
|
||||
{
|
||||
this.track = track;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public AudioTrack getTrack()
|
||||
{
|
||||
return track;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "`["+FormatUtil.formatTime(track.getDuration())+"]` **" + track.getInfo().title +"** - <@"+owner+">";
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.utils.TimeUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
|
||||
import com.jagrosh.jmusicbot.queue.Queueable;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class QueuedTrack implements Queueable
|
||||
{
|
||||
private final AudioTrack track;
|
||||
private final RequestMetadata requestMetadata;
|
||||
|
||||
public QueuedTrack(AudioTrack track, RequestMetadata rm)
|
||||
{
|
||||
this.track = track;
|
||||
this.track.setUserData(rm == null ? RequestMetadata.EMPTY : rm);
|
||||
|
||||
this.requestMetadata = rm;
|
||||
if (this.track.isSeekable() && rm != null)
|
||||
track.setPosition(rm.requestInfo.startTimestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdentifier()
|
||||
{
|
||||
return requestMetadata.getOwner();
|
||||
}
|
||||
|
||||
public AudioTrack getTrack()
|
||||
{
|
||||
return track;
|
||||
}
|
||||
|
||||
public RequestMetadata getRequestMetadata()
|
||||
{
|
||||
return requestMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String entry = "`[" + TimeUtil.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() + ">";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.utils.TimeUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class RequestMetadata
|
||||
{
|
||||
public static final RequestMetadata EMPTY = new RequestMetadata(null, null, null);
|
||||
|
||||
public final UserInfo user;
|
||||
public final RequestInfo requestInfo;
|
||||
public final QueueInfo queueInfo;
|
||||
|
||||
public RequestMetadata(User user, RequestInfo requestInfo, QueueInfo queueInfo)
|
||||
{
|
||||
this.user = user == null ? null : new UserInfo(user.getIdLong(), user.getName(), user.getDiscriminator(), user.getEffectiveAvatarUrl());
|
||||
this.requestInfo = requestInfo;
|
||||
this.queueInfo = queueInfo;
|
||||
}
|
||||
|
||||
public long getOwner()
|
||||
{
|
||||
return user == null ? 0L : user.id;
|
||||
}
|
||||
|
||||
public static RequestMetadata fromResultHandler(AudioTrack track, CommandEvent event)
|
||||
{
|
||||
return new RequestMetadata(event.getAuthor(), new RequestInfo(event.getArgs(), track.getInfo().uri),
|
||||
new QueueInfo("PLAY", null, event.getArgs(), System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
public static RequestMetadata fromResultHandler(AudioTrack track, CommandEvent event, String source, String playlistName, String searchQuery)
|
||||
{
|
||||
return new RequestMetadata(event.getAuthor(), new RequestInfo(event.getArgs(), track.getInfo().uri),
|
||||
new QueueInfo(source, playlistName, searchQuery, System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
public static RequestMetadata fromDefaultPlaylist(String playlistName)
|
||||
{
|
||||
return new RequestMetadata(null, new RequestInfo(playlistName, null),
|
||||
new QueueInfo("DEFAULT_PLAYLIST", playlistName, null, System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
public static class RequestInfo
|
||||
{
|
||||
public final String query, url;
|
||||
public final long startTimestamp;
|
||||
|
||||
public RequestInfo(String query, String url)
|
||||
{
|
||||
this(query, url, tryGetTimestamp(query));
|
||||
}
|
||||
|
||||
private RequestInfo(String query, String url, long startTimestamp)
|
||||
{
|
||||
this.url = url;
|
||||
this.query = query;
|
||||
this.startTimestamp = startTimestamp;
|
||||
}
|
||||
|
||||
private static final Pattern youtubeTimestampPattern = Pattern.compile("youtu(?:\\.be|be\\..+)/.*\\?.*(?!.*list=)t=([\\dhms]+)");
|
||||
private static long tryGetTimestamp(String url)
|
||||
{
|
||||
if(url == null || url.isEmpty())
|
||||
return 0;
|
||||
Matcher matcher = youtubeTimestampPattern.matcher(url);
|
||||
return matcher.find() ? TimeUtil.parseUnitTime(matcher.group(1)) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class QueueInfo
|
||||
{
|
||||
public final String source;
|
||||
public final String playlistName;
|
||||
public final String searchQuery;
|
||||
public final long queuedAt;
|
||||
|
||||
public QueueInfo(String source, String playlistName, String searchQuery, long queuedAt)
|
||||
{
|
||||
this.source = source;
|
||||
this.playlistName = playlistName;
|
||||
this.searchQuery = searchQuery;
|
||||
this.queuedAt = queuedAt;
|
||||
}
|
||||
}
|
||||
|
||||
public static 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,309 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* 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 com.jagrosh.jmusicbot.settings.Settings;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
|
||||
import net.dv8tion.jda.api.requests.ErrorResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageEditData;
|
||||
|
||||
public class StatusMessageHandler
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger("StatusMessage");
|
||||
private final Bot bot;
|
||||
private final Map<Long, StatusState> states;
|
||||
|
||||
public StatusMessageHandler(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.states = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
if(!bot.getConfig().useStatusMessages())
|
||||
return;
|
||||
bot.getThreadpool().scheduleWithFixedDelay(() -> updateAll(), 0, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void onMessageReceived(MessageReceivedEvent event)
|
||||
{
|
||||
if(!bot.getConfig().useStatusMessages())
|
||||
return;
|
||||
if(!event.isFromGuild())
|
||||
return;
|
||||
Guild guild = event.getGuild();
|
||||
StatusState state = states.computeIfAbsent(guild.getIdLong(), id -> new StatusState());
|
||||
synchronized(state)
|
||||
{
|
||||
MessageChannelUnion channelUnion = event.getChannel();
|
||||
if(channelUnion == null || channelUnion.getType() != ChannelType.TEXT)
|
||||
return;
|
||||
TextChannel channel = channelUnion.asTextChannel();
|
||||
|
||||
boolean isCommand = isCommandMessage(event.getMessage(), guild);
|
||||
Settings settings = bot.getSettingsManager().getSettings(guild);
|
||||
if(isCommand && !event.getAuthor().isBot() && settings.getTextChannel(guild) == null)
|
||||
state.lastCommandChannelId = channel.getIdLong();
|
||||
|
||||
Long statusChannelId = resolveStatusChannelId(guild, state);
|
||||
if(statusChannelId == null || statusChannelId == 0L)
|
||||
return;
|
||||
if(isCommand && !event.getAuthor().isBot() && channel.getIdLong() == statusChannelId)
|
||||
state.pendingRepost = true;
|
||||
if(channel.getIdLong() != statusChannelId)
|
||||
return;
|
||||
|
||||
if(state.messageId != 0L && event.getMessageIdLong() == state.messageId)
|
||||
return;
|
||||
|
||||
if(state.pendingRepost)
|
||||
{
|
||||
long selfId = bot.getJDA().getSelfUser().getIdLong();
|
||||
if(event.getAuthor().getIdLong() == selfId)
|
||||
{
|
||||
debug(guild, state, "pending_repost_trigger", "author_is_self", event.getMessageIdLong());
|
||||
repostStatusMessage(guild, channel, state);
|
||||
state.pendingRepost = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid repost loops when the status message itself is created
|
||||
if(event.getAuthor().getIdLong() == bot.getJDA().getSelfUser().getIdLong() && state.createInFlight)
|
||||
return;
|
||||
|
||||
debug(guild, state, "repost_due_to_message", "message_author", event.getAuthor().getIdLong());
|
||||
repostStatusMessage(guild, channel, state);
|
||||
}
|
||||
}
|
||||
|
||||
public void onMessageDelete(Guild guild, long messageId)
|
||||
{
|
||||
if(!bot.getConfig().useStatusMessages())
|
||||
return;
|
||||
StatusState state = states.get(guild.getIdLong());
|
||||
if(state == null)
|
||||
return;
|
||||
synchronized(state)
|
||||
{
|
||||
if(state.createInFlight)
|
||||
{
|
||||
debug(guild, state, "delete_ignored_in_flight", "message", messageId);
|
||||
return;
|
||||
}
|
||||
if(state.messageId == messageId)
|
||||
state.messageId = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
public void onTrackUpdate(long guildId)
|
||||
{
|
||||
if(!bot.getConfig().useStatusMessages())
|
||||
return;
|
||||
StatusState state = states.computeIfAbsent(guildId, id -> new StatusState());
|
||||
state.forceUpdate = true;
|
||||
}
|
||||
|
||||
private void updateAll()
|
||||
{
|
||||
if(!bot.getConfig().useStatusMessages())
|
||||
return;
|
||||
JDA jda = bot.getJDA();
|
||||
if(jda == null)
|
||||
return;
|
||||
for(Map.Entry<Long, StatusState> entry : states.entrySet())
|
||||
{
|
||||
long guildId = entry.getKey();
|
||||
StatusState state = entry.getValue();
|
||||
synchronized(state)
|
||||
{
|
||||
Guild guild = jda.getGuildById(guildId);
|
||||
if(guild == null)
|
||||
continue;
|
||||
TextChannel channel = resolveStatusChannel(guild, state);
|
||||
if(channel == null)
|
||||
continue;
|
||||
AudioHandler handler = (AudioHandler)guild.getAudioManager().getSendingHandler();
|
||||
if(handler == null)
|
||||
continue;
|
||||
if(state.createInFlight)
|
||||
continue;
|
||||
|
||||
boolean isPlaying = handler.isMusicPlaying(jda);
|
||||
if(!isPlaying && !state.forceUpdate)
|
||||
continue;
|
||||
|
||||
if(state.pendingRepost && (state.messageId == 0L || state.channelId != channel.getIdLong()))
|
||||
continue;
|
||||
|
||||
MessageCreateData statusMessage = handler.getStatusMessage(jda);
|
||||
if(state.messageId == 0L || state.channelId != channel.getIdLong())
|
||||
{
|
||||
debug(guild, state, "repost_due_to_missing_or_channel", "channel", channel.getIdLong());
|
||||
repostStatusMessage(guild, channel, state, statusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
final long editingId = state.messageId;
|
||||
channel.editMessageById(editingId, MessageEditData.fromCreateData(statusMessage))
|
||||
.queue(m -> {}, t ->
|
||||
{
|
||||
debug(guild, state, "edit_failed", t, editingId);
|
||||
if(state.messageId == editingId && shouldResetMessageId(t))
|
||||
state.messageId = 0L;
|
||||
});
|
||||
}
|
||||
state.forceUpdate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void repostStatusMessage(Guild guild, TextChannel channel, StatusState state)
|
||||
{
|
||||
MessageCreateData statusMessage = getStatusMessage(guild);
|
||||
if(statusMessage == null)
|
||||
return;
|
||||
repostStatusMessage(guild, channel, state, statusMessage);
|
||||
}
|
||||
|
||||
private void repostStatusMessage(Guild guild, TextChannel channel, StatusState state, MessageCreateData statusMessage)
|
||||
{
|
||||
if(state.createInFlight)
|
||||
return;
|
||||
state.createInFlight = true;
|
||||
debug(guild, state, "repost_start", "channel", channel.getIdLong());
|
||||
deleteOldStatusMessage(guild, state);
|
||||
channel.sendMessage(statusMessage).queue(m ->
|
||||
{
|
||||
state.channelId = channel.getIdLong();
|
||||
state.messageId = m.getIdLong();
|
||||
state.createInFlight = false;
|
||||
debug(guild, state, "repost_success", "message", m.getIdLong());
|
||||
}, t ->
|
||||
{
|
||||
state.createInFlight = false;
|
||||
debug(guild, state, "repost_failed", t, 0L);
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteOldStatusMessage(Guild guild, StatusState state)
|
||||
{
|
||||
if(state.messageId == 0L)
|
||||
return;
|
||||
TextChannel oldChannel = guild.getTextChannelById(state.channelId);
|
||||
if(oldChannel == null)
|
||||
return;
|
||||
oldChannel.deleteMessageById(state.messageId).queue(m -> {}, t -> debug(guild, state, "delete_failed", t, state.messageId));
|
||||
}
|
||||
|
||||
private boolean shouldResetMessageId(Throwable throwable)
|
||||
{
|
||||
if(throwable instanceof ErrorResponseException)
|
||||
{
|
||||
ErrorResponse response = ((ErrorResponseException)throwable).getErrorResponse();
|
||||
return response == ErrorResponse.UNKNOWN_MESSAGE || response == ErrorResponse.UNKNOWN_CHANNEL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void debug(Guild guild, StatusState state, String action, String key, long value)
|
||||
{
|
||||
if(LOG.isDebugEnabled())
|
||||
LOG.debug("guild={} action={} {}={} msgId={} chanId={} pending={} inFlight={} force={}",
|
||||
guild.getId(), action, key, value, state.messageId, state.channelId, state.pendingRepost, state.createInFlight, state.forceUpdate);
|
||||
}
|
||||
|
||||
private void debug(Guild guild, StatusState state, String action, Throwable throwable, long messageId)
|
||||
{
|
||||
if(LOG.isDebugEnabled())
|
||||
{
|
||||
String err = throwable == null ? "null" : throwable.getClass().getSimpleName() + ": " + throwable.getMessage();
|
||||
LOG.debug("guild={} action={} error={} msgId={} chanId={} pending={} inFlight={} force={}",
|
||||
guild.getId(), action, err, messageId == 0L ? state.messageId : messageId,
|
||||
state.channelId, state.pendingRepost, state.createInFlight, state.forceUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
private MessageCreateData getStatusMessage(Guild guild)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)guild.getAudioManager().getSendingHandler();
|
||||
if(handler == null)
|
||||
return null;
|
||||
return handler.getStatusMessage(bot.getJDA());
|
||||
}
|
||||
|
||||
private TextChannel resolveStatusChannel(Guild guild, StatusState state)
|
||||
{
|
||||
Long id = resolveStatusChannelId(guild, state);
|
||||
return id == null ? null : guild.getTextChannelById(id);
|
||||
}
|
||||
|
||||
private Long resolveStatusChannelId(Guild guild, StatusState state)
|
||||
{
|
||||
Settings settings = bot.getSettingsManager().getSettings(guild);
|
||||
if(settings == null)
|
||||
return null;
|
||||
if(settings.getTextChannel(guild) != null)
|
||||
return settings.getTextChannel(guild).getIdLong();
|
||||
return state.lastCommandChannelId == 0L ? null : state.lastCommandChannelId;
|
||||
}
|
||||
|
||||
private boolean isCommandMessage(Message message, Guild guild)
|
||||
{
|
||||
String content = message.getContentRaw();
|
||||
if(content == null || content.isEmpty())
|
||||
return false;
|
||||
Settings settings = bot.getSettingsManager().getSettings(guild);
|
||||
String prefix = settings.getPrefix() != null ? settings.getPrefix() : bot.getConfig().getPrefix();
|
||||
String altprefix = bot.getConfig().getAltPrefix();
|
||||
if(prefix != null && prefix.equalsIgnoreCase("@mention"))
|
||||
{
|
||||
long botId = bot.getJDA().getSelfUser().getIdLong();
|
||||
String mention = "<@" + botId + ">";
|
||||
String mentionNick = "<@!" + botId + ">";
|
||||
if(content.startsWith(mention) || content.startsWith(mentionNick))
|
||||
return true;
|
||||
}
|
||||
if(prefix != null && !prefix.equalsIgnoreCase("@mention") && content.startsWith(prefix))
|
||||
return true;
|
||||
return altprefix != null && !altprefix.isEmpty() && content.startsWith(altprefix);
|
||||
}
|
||||
|
||||
private static class StatusState
|
||||
{
|
||||
private long channelId = 0L;
|
||||
private long messageId = 0L;
|
||||
private long lastCommandChannelId = 0L;
|
||||
private boolean pendingRepost = false;
|
||||
private boolean forceUpdate = false;
|
||||
private boolean createInFlight = false;
|
||||
}
|
||||
}
|
||||
@@ -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.track.AudioItem;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioReference;
|
||||
import com.typesafe.config.Config;
|
||||
import dev.lavalink.youtube.YoutubeAudioSourceManager;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.jagrosh.jdautilities.command.Command;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public abstract class AdminCommand extends Command
|
||||
{
|
||||
protected final Bot bot;
|
||||
|
||||
public AdminCommand(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.category = new Category("Admin", event ->
|
||||
{
|
||||
if(event.getAuthor().getId().equals(event.getClient().getOwnerId()))
|
||||
return true;
|
||||
if(event.getGuild()==null)
|
||||
return true;
|
||||
return event.getMember().hasPermission(Permission.MANAGE_SERVER);
|
||||
});
|
||||
this.guildOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void execute(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.clear();
|
||||
try
|
||||
{
|
||||
doCommand(event);
|
||||
CommandLogContext.CommandLogEntry entry = CommandLogContext.consume();
|
||||
if(entry != null && entry.manual)
|
||||
return;
|
||||
String result = entry == null || entry.result == null ? "OK" : entry.result;
|
||||
JSONObject meta = entry == null ? null : entry.meta;
|
||||
logCommand(event, result, meta);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
CommandLogContext.CommandLogEntry entry = CommandLogContext.consume();
|
||||
JSONObject meta = entry == null ? null : entry.meta;
|
||||
if(meta == null)
|
||||
meta = new JSONObject();
|
||||
meta.put("error_reason", ex.getClass().getSimpleName());
|
||||
logCommand(event, "ERROR", meta);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void doCommand(CommandEvent event);
|
||||
|
||||
protected void logCommand(CommandEvent event, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), result, meta == null ? null : meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public abstract class DJCommand extends MusicCommand
|
||||
{
|
||||
public DJCommand(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.category = new Category("DJ", event -> checkDJPermission(event));
|
||||
}
|
||||
|
||||
public static boolean checkDJPermission(CommandEvent event)
|
||||
{
|
||||
if(event.getAuthor().getId().equals(event.getClient().getOwnerId()))
|
||||
return true;
|
||||
if(event.getGuild()==null)
|
||||
return true;
|
||||
if(event.getMember().hasPermission(Permission.MANAGE_SERVER))
|
||||
return true;
|
||||
Settings settings = event.getClient().getSettingsFor(event.getGuild());
|
||||
Role dj = settings.getRole(event.getGuild());
|
||||
return dj!=null && (event.getMember().getRoles().contains(dj) || dj.getIdLong()==event.getGuild().getIdLong());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* 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;
|
||||
|
||||
import com.jagrosh.jdautilities.command.Command;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public abstract class LoggedCommand extends Command
|
||||
{
|
||||
protected final Bot bot;
|
||||
|
||||
public LoggedCommand(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void execute(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.clear();
|
||||
try
|
||||
{
|
||||
doCommand(event);
|
||||
CommandLogContext.CommandLogEntry entry = CommandLogContext.consume();
|
||||
if(entry != null && entry.manual)
|
||||
return;
|
||||
String result = entry == null || entry.result == null ? "OK" : entry.result;
|
||||
JSONObject meta = entry == null ? null : entry.meta;
|
||||
logCommand(event, result, meta);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
CommandLogContext.CommandLogEntry entry = CommandLogContext.consume();
|
||||
JSONObject meta = entry == null ? null : entry.meta;
|
||||
if(meta == null)
|
||||
meta = new JSONObject();
|
||||
meta.put("error_reason", ex.getClass().getSimpleName());
|
||||
logCommand(event, "ERROR", meta);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void doCommand(CommandEvent event);
|
||||
|
||||
protected void logCommand(CommandEvent event, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), result, meta == null ? null : meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
|
||||
* 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.
|
||||
@@ -15,22 +15,24 @@
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.commands;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.command.Command;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.Settings;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import net.dv8tion.jda.core.entities.GuildVoiceState;
|
||||
import net.dv8tion.jda.core.entities.TextChannel;
|
||||
import net.dv8tion.jda.core.entities.VoiceChannel;
|
||||
import net.dv8tion.jda.core.exceptions.PermissionException;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
import net.dv8tion.jda.api.entities.GuildVoiceState;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||
import net.dv8tion.jda.api.exceptions.PermissionException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public abstract class MusicCommand extends Command {
|
||||
|
||||
public abstract class MusicCommand extends Command
|
||||
{
|
||||
protected final Bot bot;
|
||||
protected boolean bePlaying;
|
||||
protected boolean beListening;
|
||||
@@ -39,51 +41,97 @@ public abstract class MusicCommand extends Command {
|
||||
{
|
||||
this.bot = bot;
|
||||
this.guildOnly = true;
|
||||
this.category = bot.MUSIC;
|
||||
this.category = new Category("Music");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
Settings settings = bot.getSettings(event.getGuild());
|
||||
TextChannel tchannel = event.getGuild().getTextChannelById(settings.getTextId());
|
||||
protected void execute(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.clear();
|
||||
Settings settings = event.getClient().getSettingsFor(event.getGuild());
|
||||
TextChannel tchannel = settings.getTextChannel(event.getGuild());
|
||||
if(tchannel!=null && !event.getTextChannel().equals(tchannel))
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
event.getMessage().delete().queue();
|
||||
} catch(PermissionException e){}
|
||||
event.replyInDM(event.getClient().getError()+" You can only use that command in <#"+settings.getTextId()+">!");
|
||||
} catch(PermissionException ignore){}
|
||||
event.replyInDm(event.getClient().getError()+" You can only use that command in "+tchannel.getAsMention()+"!");
|
||||
logCommand(event, "ERROR", new JSONObject().put("error_reason", "wrong_text_channel"));
|
||||
return;
|
||||
}
|
||||
if(bePlaying
|
||||
&& (event.getGuild().getAudioManager().getSendingHandler()==null
|
||||
|| ((AudioHandler)event.getGuild().getAudioManager().getSendingHandler()).getCurrentTrack()==null))
|
||||
bot.getPlayerManager().setUpHandler(event.getGuild()); // no point constantly checking for this later
|
||||
if(bePlaying && !((AudioHandler)event.getGuild().getAudioManager().getSendingHandler()).isMusicPlaying(event.getJDA()))
|
||||
{
|
||||
event.reply(event.getClient().getError()+" There must be music playing to use that!");
|
||||
logCommand(event, "ERROR", new JSONObject().put("error_reason", "no_music_playing"));
|
||||
return;
|
||||
}
|
||||
if(beListening)
|
||||
{
|
||||
VoiceChannel current = event.getGuild().getSelfMember().getVoiceState().getChannel();
|
||||
VoiceChannel current = (VoiceChannel) event.getGuild().getSelfMember().getVoiceState().getChannel();
|
||||
if(current==null)
|
||||
current = event.getGuild().getVoiceChannelById(settings.getVoiceId());
|
||||
current = settings.getVoiceChannel(event.getGuild());
|
||||
GuildVoiceState userState = event.getMember().getVoiceState();
|
||||
if(!userState.inVoiceChannel() || userState.isDeafened() || (current!=null && !userState.getChannel().equals(current)))
|
||||
if(!userState.inAudioChannel() || userState.isDeafened() || (current!=null && !userState.getChannel().equals(current)))
|
||||
{
|
||||
event.reply(event.getClient().getError()
|
||||
+" 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!");
|
||||
logCommand(event, "ERROR", new JSONObject().put("error_reason", "not_listening"));
|
||||
return;
|
||||
}
|
||||
if(!event.getGuild().getSelfMember().getVoiceState().inVoiceChannel())
|
||||
try {
|
||||
|
||||
VoiceChannel afkChannel = userState.getGuild().getAfkChannel();
|
||||
if(afkChannel != null && afkChannel.equals(userState.getChannel()))
|
||||
{
|
||||
event.replyError("You cannot use that command in an AFK channel!");
|
||||
logCommand(event, "ERROR", new JSONObject().put("error_reason", "afk_channel"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!event.getGuild().getSelfMember().getVoiceState().inAudioChannel())
|
||||
{
|
||||
try
|
||||
{
|
||||
event.getGuild().getAudioManager().openAudioConnection(userState.getChannel());
|
||||
}catch(PermissionException ex) {
|
||||
event.reply(event.getClient().getError()+" I am unable to connect to **"+userState.getChannel().getName()+"**!");
|
||||
}
|
||||
catch(PermissionException ex)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" I am unable to connect to "+userState.getChannel().getAsMention()+"!");
|
||||
logCommand(event, "ERROR", new JSONObject().put("error_reason", "cannot_connect"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
doCommand(event);
|
||||
CommandLogContext.CommandLogEntry entry = CommandLogContext.consume();
|
||||
if(entry != null && entry.manual)
|
||||
return;
|
||||
String result = entry == null || entry.result == null ? "OK" : entry.result;
|
||||
JSONObject meta = entry == null ? null : entry.meta;
|
||||
logCommand(event, result, meta);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
CommandLogContext.CommandLogEntry entry = CommandLogContext.consume();
|
||||
JSONObject meta = entry == null ? null : entry.meta;
|
||||
if(meta == null)
|
||||
meta = new JSONObject();
|
||||
meta.put("error_reason", ex.getClass().getSimpleName());
|
||||
logCommand(event, "ERROR", meta);
|
||||
throw ex;
|
||||
}
|
||||
doCommand(event);
|
||||
}
|
||||
|
||||
public abstract void doCommand(CommandEvent event);
|
||||
|
||||
protected void logCommand(CommandEvent event, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), result, meta == null ? null : meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.jagrosh.jdautilities.command.Command;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public abstract class OwnerCommand extends Command
|
||||
{
|
||||
protected final Bot bot;
|
||||
|
||||
public OwnerCommand(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.category = new Category("Owner");
|
||||
this.ownerCommand = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void execute(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.clear();
|
||||
try
|
||||
{
|
||||
doCommand(event);
|
||||
CommandLogContext.CommandLogEntry entry = CommandLogContext.consume();
|
||||
if(entry != null && entry.manual)
|
||||
return;
|
||||
String result = entry == null || entry.result == null ? "OK" : entry.result;
|
||||
JSONObject meta = entry == null ? null : entry.meta;
|
||||
logCommand(event, result, meta);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
CommandLogContext.CommandLogEntry entry = CommandLogContext.consume();
|
||||
JSONObject meta = entry == null ? null : entry.meta;
|
||||
if(meta == null)
|
||||
meta = new JSONObject();
|
||||
meta.put("error_reason", ex.getClass().getSimpleName());
|
||||
logCommand(event, "ERROR", meta);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void doCommand(CommandEvent event);
|
||||
|
||||
protected void logCommand(CommandEvent event, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), result, meta == null ? null : meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.playlist.Playlist;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.entities.Message;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class PlayCmd extends MusicCommand {
|
||||
|
||||
public PlayCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "play";
|
||||
this.arguments = "<title|URL|subcommand>";
|
||||
this.help = "plays the provided song";
|
||||
this.beListening = true;
|
||||
this.bePlaying = false;
|
||||
this.children = new Command[]{new PlaylistCmd(bot)};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(event.getClient().getWarning()+" Play Commands:\n");
|
||||
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" <song title>` - plays the first result from Youtube");
|
||||
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" <URL>` - plays the provided song, playlist, or stream");
|
||||
for(Command cmd: children)
|
||||
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ").append(cmd.getName()).append(" ").append(cmd.getArguments()).append("` - ").append(cmd.getHelp());
|
||||
event.reply(builder.toString());
|
||||
return;
|
||||
}
|
||||
String args = event.getArgs().startsWith("<") && event.getArgs().endsWith(">")
|
||||
? event.getArgs().substring(1,event.getArgs().length()-1)
|
||||
: event.getArgs();
|
||||
event.getChannel().sendMessage("\u231A Loading... `["+args+"]`").queue(m -> {
|
||||
bot.getAudioManager().loadItemOrdered(event.getGuild(), args, new ResultHandler(m,event,false));
|
||||
});
|
||||
}
|
||||
|
||||
private class ResultHandler implements AudioLoadResultHandler {
|
||||
final Message m;
|
||||
final CommandEvent event;
|
||||
final boolean ytsearch;
|
||||
private ResultHandler(Message m, CommandEvent event, boolean ytsearch)
|
||||
{
|
||||
this.m = m;
|
||||
this.event = event;
|
||||
this.ytsearch = ytsearch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack track) {
|
||||
int pos = bot.queueTrack(event, track)+1;
|
||||
m.editMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
|
||||
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
|
||||
: " to the queue at position "+pos)).queue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist playlist) {
|
||||
if(playlist.getTracks().size()==1 || playlist.isSearchResult() || playlist.getSelectedTrack()!=null)
|
||||
{
|
||||
AudioTrack single = playlist.getSelectedTrack()==null?playlist.getTracks().get(0):playlist.getSelectedTrack();
|
||||
int pos = bot.queueTrack(event, single)+1;
|
||||
m.editMessage(event.getClient().getSuccess()+" Added **"+single.getInfo().title
|
||||
+"** (`"+FormatUtil.formatTime(single.getDuration())+"`) "+(pos==0 ? "to begin playing"
|
||||
: " to the queue at position "+pos)).queue();
|
||||
}
|
||||
else
|
||||
{
|
||||
m.editMessage(event.getClient().getSuccess()+" Found "
|
||||
+(playlist.getName()==null?"a playlist":"playlist **"+playlist.getName()+"**")+" with `"
|
||||
+playlist.getTracks().size()+"` entries; added to the queue!").queue();
|
||||
playlist.getTracks().stream().forEach((track) -> {
|
||||
bot.queueTrack(event, track);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches() {
|
||||
if(ytsearch)
|
||||
m.editMessage(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.").queue();
|
||||
else
|
||||
bot.getAudioManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event,true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException throwable) {
|
||||
if(throwable.severity==Severity.COMMON)
|
||||
m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue();
|
||||
else
|
||||
m.editMessage(event.getClient().getError()+" Error loading track.").queue();
|
||||
}
|
||||
}
|
||||
|
||||
public class PlaylistCmd extends MusicCommand {
|
||||
|
||||
public PlaylistCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "playlist";
|
||||
this.aliases = new String[]{"pl"};
|
||||
this.arguments = "<name>";
|
||||
this.help = "plays the provided playlist";
|
||||
this.beListening = true;
|
||||
this.bePlaying = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a playlist name.");
|
||||
return;
|
||||
}
|
||||
Playlist playlist = Playlist.loadPlaylist(event.getArgs());
|
||||
if(playlist==null)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" I could not find `"+event.getArgs()+".txt` in the Playlists folder.");
|
||||
return;
|
||||
}
|
||||
event.getChannel().sendMessage("\u231A Loading playlist **"+event.getArgs()+"**...").queue(m -> {
|
||||
playlist.loadTracks(bot.getAudioManager(), () -> {
|
||||
StringBuilder builder = new StringBuilder(playlist.getTracks().isEmpty()
|
||||
? event.getClient().getWarning()+" No tracks were loaded!"
|
||||
: event.getClient().getSuccess()+" Loaded **"+playlist.getTracks().size()+"** tracks!");
|
||||
if(!playlist.getErrors().isEmpty())
|
||||
builder.append("\nThe following tracks failed to load:");
|
||||
playlist.getErrors().forEach(err -> builder.append("\n`[").append(err.getIndex()+1).append("]` **").append(err.getItem()).append("**: ").append(err.getReason()));
|
||||
String str = builder.toString();
|
||||
if(str.length()>2000)
|
||||
str = str.substring(0,1994)+" (...)";
|
||||
m.editMessage(str).queue();
|
||||
playlist.getTracks().forEach(track -> bot.queueTrack(event, track));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.menu.pagination.PaginatorBuilder;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.audio.QueuedTrack;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
import net.dv8tion.jda.core.exceptions.PermissionException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class QueueCmd extends MusicCommand {
|
||||
|
||||
private final PaginatorBuilder builder;
|
||||
public QueueCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "queue";
|
||||
this.help = "shows the current queue";
|
||||
this.arguments = "[pagenum]";
|
||||
this.aliases = new String[]{"list"};
|
||||
this.bePlaying = true;
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_ADD_REACTION,Permission.MESSAGE_EMBED_LINKS};
|
||||
builder = new PaginatorBuilder()
|
||||
.setColumns(1)
|
||||
.setFinalAction(m -> {try{m.clearReactions().queue();}catch(PermissionException e){}})
|
||||
.setItemsPerPage(10)
|
||||
.waitOnSinglePage(false)
|
||||
.useNumberedItems(true)
|
||||
.showPageNumbers(true)
|
||||
.setEventWaiter(bot.getWaiter())
|
||||
.setTimeout(1, TimeUnit.MINUTES)
|
||||
;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
int pagenum = 1;
|
||||
try{
|
||||
pagenum = Integer.parseInt(event.getArgs());
|
||||
}catch(NumberFormatException e){}
|
||||
List<QueuedTrack> list = ((AudioHandler)event.getGuild().getAudioManager().getSendingHandler()).getQueue().getList();
|
||||
if(list.isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+" There is no music in the queue!");
|
||||
return;
|
||||
}
|
||||
String[] songs = new String[list.size()];
|
||||
long total = 0;
|
||||
for(int i=0; i<list.size(); i++)
|
||||
{
|
||||
total += list.get(i).getTrack().getDuration();
|
||||
songs[i] = list.get(i).toString();
|
||||
}
|
||||
builder.setText(event.getClient().getSuccess()+" Current Queue | "+songs.length+" entries | `"+FormatUtil.formatTime(total)+"` ")
|
||||
.setItems(songs)
|
||||
.setUsers(event.getAuthor())
|
||||
.setColor(event.getSelfMember().getColor())
|
||||
;
|
||||
builder.build().paginate(event.getChannel(), pagenum);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.audio.QueuedTrack;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
import net.dv8tion.jda.core.entities.User;
|
||||
import net.dv8tion.jda.core.utils.PermissionUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class RemoveCmd extends MusicCommand {
|
||||
|
||||
public RemoveCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "remove";
|
||||
this.help = "removes a song from the queue";
|
||||
this.arguments = "<position|ALL>";
|
||||
this.aliases = new String[]{"delete"};
|
||||
this.beListening = true;
|
||||
this.bePlaying = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
if(handler.getQueue().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" There is nothing in the queue!");
|
||||
return;
|
||||
}
|
||||
if(event.getArgs().equalsIgnoreCase("all"))
|
||||
{
|
||||
int count = handler.getQueue().removeAll(event.getAuthor().getId());
|
||||
if(count==0)
|
||||
event.reply(event.getClient().getWarning()+" You don't have any songs in the queue!");
|
||||
else
|
||||
event.reply(event.getClient().getSuccess()+" Successfully removed your "+count+" entries.");
|
||||
return;
|
||||
}
|
||||
int pos;
|
||||
try {
|
||||
pos = Integer.parseInt(event.getArgs());
|
||||
} catch(NumberFormatException e) {
|
||||
pos = 0;
|
||||
}
|
||||
if(pos<1 || pos>handler.getQueue().size())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Position must be a valid integer between 1 and "+handler.getQueue().size()+"!");
|
||||
return;
|
||||
}
|
||||
boolean isDJ = event.getMember().hasPermission(Permission.MANAGE_SERVER);
|
||||
if(!isDJ)
|
||||
isDJ = event.getMember().getRoles().contains(event.getGuild().getRoleById(bot.getSettings(event.getGuild()).getRoleId()));
|
||||
QueuedTrack qt = handler.getQueue().get(pos-1);
|
||||
if(qt.getIdentifier().equals(event.getAuthor().getId()))
|
||||
{
|
||||
handler.getQueue().remove(pos-1);
|
||||
event.reply(event.getClient().getSuccess()+" Removed **"+qt.getTrack().getInfo().title+"** from the queue");
|
||||
}
|
||||
else if(isDJ)
|
||||
{
|
||||
handler.getQueue().remove(pos-1);
|
||||
User u;
|
||||
try {
|
||||
u = event.getJDA().getUserById(qt.getIdentifier());
|
||||
} catch(Exception e) {
|
||||
u = null;
|
||||
}
|
||||
event.reply(event.getClient().getSuccess()+" Removed **"+qt.getTrack().getInfo().title
|
||||
+"** from the queue (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
|
||||
}
|
||||
else
|
||||
{
|
||||
event.reply(event.getClient().getError()+" You cannot remove **"+qt.getTrack().getInfo().title+"** because you didn't add it!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.menu.orderedmenu.OrderedMenuBuilder;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
import net.dv8tion.jda.core.entities.Message;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SCSearchCmd extends MusicCommand {
|
||||
|
||||
private final OrderedMenuBuilder builder;
|
||||
public SCSearchCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "scsearch";
|
||||
this.arguments = "<query>";
|
||||
this.help = "searches Soundcloud for a provided query";
|
||||
this.beListening = true;
|
||||
this.bePlaying = false;
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
|
||||
builder = new OrderedMenuBuilder()
|
||||
.allowTextInput(true)
|
||||
.useNumbers()
|
||||
.useCancelButton(true)
|
||||
.setEventWaiter(bot.getWaiter())
|
||||
.setTimeout(1, TimeUnit.MINUTES)
|
||||
;
|
||||
}
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a query.");
|
||||
return;
|
||||
}
|
||||
event.getChannel().sendMessage("\uD83D\uDD0E Searching... `["+event.getArgs()+"]`").queue(m -> {
|
||||
bot.getAudioManager().loadItemOrdered(event.getGuild(), "scsearch:"+event.getArgs(), new ResultHandler(m,event));
|
||||
});
|
||||
}
|
||||
|
||||
private class ResultHandler implements AudioLoadResultHandler {
|
||||
final Message m;
|
||||
final CommandEvent event;
|
||||
private ResultHandler(Message m, CommandEvent event)
|
||||
{
|
||||
this.m = m;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack track) {
|
||||
int pos = bot.queueTrack(event, track)+1;
|
||||
m.editMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
|
||||
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
|
||||
: " to the queue at position "+pos)).queue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist playlist) {
|
||||
builder.setColor(event.getSelfMember().getColor())
|
||||
.setText(event.getClient().getSuccess()+" Search results for `"+event.getArgs()+"`:")
|
||||
.setChoices(new String[0])
|
||||
.setAction(i -> {
|
||||
AudioTrack track = playlist.getTracks().get(i-1);
|
||||
int pos = bot.queueTrack(event, track)+1;
|
||||
event.getChannel().sendMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
|
||||
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
|
||||
: " to the queue at position "+pos)).queue();
|
||||
})
|
||||
.setCancel(() -> m.delete().queue())
|
||||
.setUsers(event.getAuthor())
|
||||
;
|
||||
for(int i=0; i<4&&i<playlist.getTracks().size(); i++)
|
||||
{
|
||||
AudioTrack track = playlist.getTracks().get(i);
|
||||
builder.addChoices("`["+FormatUtil.formatTime(track.getDuration())+"]` [**"
|
||||
+track.getInfo().title+"**]("+track.getInfo().uri+")");
|
||||
}
|
||||
builder.build().display(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches() {
|
||||
m.editMessage(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.").queue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException throwable) {
|
||||
if(throwable.severity==Severity.COMMON)
|
||||
m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue();
|
||||
else
|
||||
m.editMessage(event.getClient().getError()+" Error loading track.").queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.menu.orderedmenu.OrderedMenuBuilder;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
import net.dv8tion.jda.core.entities.Message;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SearchCmd extends MusicCommand {
|
||||
|
||||
private final OrderedMenuBuilder builder;
|
||||
public SearchCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "search";
|
||||
this.aliases = new String[]{"ytsearch"};
|
||||
this.arguments = "<query>";
|
||||
this.help = "searches Youtube for a provided query";
|
||||
this.beListening = true;
|
||||
this.bePlaying = false;
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
|
||||
builder = new OrderedMenuBuilder()
|
||||
.allowTextInput(true)
|
||||
.useNumbers()
|
||||
.useCancelButton(true)
|
||||
.setEventWaiter(bot.getWaiter())
|
||||
.setTimeout(1, TimeUnit.MINUTES)
|
||||
;
|
||||
}
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a query.");
|
||||
return;
|
||||
}
|
||||
event.getChannel().sendMessage("\uD83D\uDD0E Searching... `["+event.getArgs()+"]`").queue(m -> {
|
||||
bot.getAudioManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event));
|
||||
});
|
||||
}
|
||||
|
||||
private class ResultHandler implements AudioLoadResultHandler {
|
||||
final Message m;
|
||||
final CommandEvent event;
|
||||
private ResultHandler(Message m, CommandEvent event)
|
||||
{
|
||||
this.m = m;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack track) {
|
||||
int pos = bot.queueTrack(event, track)+1;
|
||||
m.editMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
|
||||
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
|
||||
: " to the queue at position "+pos)).queue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist playlist) {
|
||||
builder.setColor(event.getSelfMember().getColor())
|
||||
.setText(event.getClient().getSuccess()+" Search results for `"+event.getArgs()+"`:")
|
||||
.setChoices(new String[0])
|
||||
.setAction(i -> {
|
||||
AudioTrack track = playlist.getTracks().get(i-1);
|
||||
int pos = bot.queueTrack(event, track)+1;
|
||||
event.getChannel().sendMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
|
||||
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
|
||||
: " to the queue at position "+pos)).queue();
|
||||
})
|
||||
.setCancel(() -> m.delete().queue())
|
||||
.setUsers(event.getAuthor())
|
||||
;
|
||||
for(int i=0; i<4&&i<playlist.getTracks().size(); i++)
|
||||
{
|
||||
AudioTrack track = playlist.getTracks().get(i);
|
||||
builder.addChoices("`["+FormatUtil.formatTime(track.getDuration())+"]` [**"
|
||||
+track.getInfo().title+"**](https://youtu.be/"+track.getIdentifier()+")");
|
||||
}
|
||||
builder.build().display(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches() {
|
||||
m.editMessage(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.").queue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException throwable) {
|
||||
if(throwable.severity==Severity.COMMON)
|
||||
m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue();
|
||||
else
|
||||
m.editMessage(event.getClient().getError()+" Error loading track.").queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import net.dv8tion.jda.core.entities.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetgameCmd extends Command {
|
||||
|
||||
public SetgameCmd(Bot bot)
|
||||
{
|
||||
this.name = "setgame";
|
||||
this.help = "sets the game the bot is playing";
|
||||
this.arguments = "[game]";
|
||||
this.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
try {
|
||||
event.getJDA().getPresence().setGame(event.getArgs().isEmpty() ? null : Game.of(event.getArgs()));
|
||||
event.reply(event.getClient().getSuccess()+" **"+event.getSelfUser().getName()
|
||||
+"** is "+(event.getArgs().isEmpty() ? "no longer playing anything." : "now playing `"+event.getArgs()+"`"));
|
||||
} catch(Exception e) {
|
||||
event.reply(event.getClient().getError()+" The game could not be set!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.Settings;
|
||||
import net.dv8tion.jda.core.EmbedBuilder;
|
||||
import net.dv8tion.jda.core.MessageBuilder;
|
||||
import net.dv8tion.jda.core.entities.Role;
|
||||
import net.dv8tion.jda.core.entities.TextChannel;
|
||||
import net.dv8tion.jda.core.entities.VoiceChannel;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SettingsCmd extends Command {
|
||||
|
||||
private final Bot bot;
|
||||
public SettingsCmd(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.name = "settings";
|
||||
this.help = "shows the bots settings";
|
||||
this.aliases = new String[]{"status"};
|
||||
this.guildOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
Settings s = bot.getSettings(event.getGuild());
|
||||
MessageBuilder builder = new MessageBuilder()
|
||||
.append("\uD83C\uDFA7 **")
|
||||
.append(event.getSelfUser().getName())
|
||||
.append("** settings:");
|
||||
TextChannel tchan = event.getGuild().getTextChannelById(s.getTextId());
|
||||
VoiceChannel vchan = event.getGuild().getVoiceChannelById(s.getVoiceId());
|
||||
Role role = event.getGuild().getRoleById(s.getRoleId());
|
||||
EmbedBuilder ebuilder = new EmbedBuilder()
|
||||
.setColor(event.getSelfMember().getColor())
|
||||
.setDescription("Text Channel: "+(tchan==null ? "Any" : "**#"+tchan.getName()+"**")
|
||||
+ "\nVoice Channel: "+(vchan==null ? "Any" : "**"+vchan.getName()+"**")
|
||||
+ "\nDJ Role: "+(role==null ? "None" : "**"+role.getName()+"**")
|
||||
+ "\nDefault Playlist: "+(s.getDefaultPlaylist()==null ? "None" : "**"+s.getDefaultPlaylist()+"**")
|
||||
)
|
||||
.setFooter(event.getJDA().getGuilds().size()+" servers | "
|
||||
+event.getJDA().getGuilds().stream().filter(g -> g.getSelfMember().getVoiceState().inVoiceChannel()).count()
|
||||
+" audio connections", null);
|
||||
event.getChannel().sendMessage(builder.setEmbed(ebuilder.build()).build()).queue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import java.util.List;
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.utils.FinderUtil;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.entities.VoiceChannel;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetvcCmd extends Command {
|
||||
|
||||
private final Bot bot;
|
||||
public SetvcCmd(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.name = "setvc";
|
||||
this.help = "sets the voice channel for playing music";
|
||||
this.arguments = "<channel|NONE>";
|
||||
this.guildOnly = true;
|
||||
this.category = bot.ADMIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a voice channel or NONE");
|
||||
}
|
||||
else if(event.getArgs().equalsIgnoreCase("none"))
|
||||
{
|
||||
bot.clearVoiceChannel(event.getGuild());
|
||||
event.reply(event.getClient().getSuccess()+" Music can now be played in any channel");
|
||||
}
|
||||
else
|
||||
{
|
||||
List<VoiceChannel> list = FinderUtil.findVoiceChannel(event.getArgs(), event.getGuild());
|
||||
if(list.isEmpty())
|
||||
event.reply(event.getClient().getWarning()+" No Voice Channels found matching \""+event.getArgs()+"\"");
|
||||
else if (list.size()>1)
|
||||
event.reply(event.getClient().getWarning()+FormatUtil.listOfVChannels(list, event.getArgs()));
|
||||
else
|
||||
{
|
||||
bot.setVoiceChannel(list.get(0));
|
||||
event.reply(event.getClient().getSuccess()+" Music can now only be played in **"+list.get(0).getName()+"**");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class ShuffleCmd extends MusicCommand {
|
||||
|
||||
public ShuffleCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "shuffle";
|
||||
this.help = "shuffles songs you have added";
|
||||
this.beListening = true;
|
||||
this.bePlaying = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
int s = handler.getQueue().shuffle(event.getAuthor().getId());
|
||||
switch (s) {
|
||||
case 0:
|
||||
event.reply(event.getClient().getError()+" You don't have any music in the queue to shuffle!");
|
||||
break;
|
||||
case 1:
|
||||
event.reply(event.getClient().getWarning()+" You only have one song in the queue!");
|
||||
break;
|
||||
default:
|
||||
event.reply(event.getClient().getSuccess()+" You successfully shuffled your "+s+" entries.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import net.dv8tion.jda.core.entities.User;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SkipCmd extends MusicCommand {
|
||||
|
||||
public SkipCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "skip";
|
||||
this.help = "votes to skip the current song";
|
||||
this.aliases = new String[]{"voteskip"};
|
||||
this.beListening = true;
|
||||
this.bePlaying = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
if(event.getAuthor().getId().equals(handler.getCurrentTrack().getIdentifier()))
|
||||
{
|
||||
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getCurrentTrack().getTrack().getInfo().title
|
||||
+"**");
|
||||
handler.getPlayer().stopTrack();
|
||||
}
|
||||
else
|
||||
{
|
||||
int listeners = (int)event.getSelfMember().getVoiceState().getChannel().getMembers().stream()
|
||||
.filter(m -> !m.getUser().isBot() && !m.getVoiceState().isDeafened()).count();
|
||||
String msg;
|
||||
if(handler.getVotes().contains(event.getAuthor().getId()))
|
||||
msg = event.getClient().getWarning()+" You already voted to skip this song `[";
|
||||
else
|
||||
{
|
||||
msg = event.getClient().getSuccess()+" You voted to skip the song `[";
|
||||
handler.getVotes().add(event.getAuthor().getId());
|
||||
}
|
||||
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]`";
|
||||
if(skippers>=required)
|
||||
{
|
||||
User u;
|
||||
try {
|
||||
u = event.getJDA().getUserById(handler.getCurrentTrack().getIdentifier());
|
||||
} catch(Exception e) {
|
||||
u = null;
|
||||
}
|
||||
msg+="\n"+event.getClient().getSuccess()+" Skipped **"+handler.getCurrentTrack().getTrack().getInfo().title
|
||||
+"**"+(handler.getCurrentTrack().getIdentifier()==null ? "" : " (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
|
||||
handler.getPlayer().stopTrack();
|
||||
}
|
||||
event.reply(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class PrefixCmd extends AdminCommand
|
||||
{
|
||||
public PrefixCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "prefix";
|
||||
this.help = "sets a server-specific prefix";
|
||||
this.arguments = "<prefix|NONE>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.replyError("Please include a prefix or NONE");
|
||||
CommandLogContext.setError("missing_prefix");
|
||||
return;
|
||||
}
|
||||
|
||||
Settings s = event.getClient().getSettingsFor(event.getGuild());
|
||||
String before = s.getPrefix();
|
||||
if(event.getArgs().equalsIgnoreCase("none"))
|
||||
{
|
||||
s.setPrefix(null);
|
||||
event.replySuccess("Prefix cleared.");
|
||||
JSONObject meta = new JSONObject().put("before", before).put("after", JSONObject.NULL);
|
||||
CommandLogContext.setMeta(meta);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setPrefix(event.getArgs());
|
||||
event.replySuccess("Custom prefix set to `" + event.getArgs() + "` on *" + event.getGuild().getName() + "*");
|
||||
JSONObject meta = new JSONObject().put("before", before).put("after", event.getArgs());
|
||||
CommandLogContext.setMeta(meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.QueueType;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Wolfgang Schwendtbauer
|
||||
*/
|
||||
public class QueueTypeCmd extends AdminCommand
|
||||
{
|
||||
public QueueTypeCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "queuetype";
|
||||
this.help = "changes the queue type";
|
||||
this.arguments = "[" + String.join("|", QueueType.getNames()) + "]";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
String args = event.getArgs();
|
||||
QueueType value;
|
||||
Settings settings = event.getClient().getSettingsFor(event.getGuild());
|
||||
QueueType before = settings.getQueueType();
|
||||
|
||||
if (args.isEmpty())
|
||||
{
|
||||
QueueType currentType = settings.getQueueType();
|
||||
event.reply(currentType.getEmoji() + " Current queue type is: `" + currentType.getUserFriendlyName() + "`.");
|
||||
CommandLogContext.setMeta(new JSONObject().put("current", currentType.name()));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
value = QueueType.valueOf(args.toUpperCase());
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
event.replyError("Invalid queue type. Valid types are: [" + String.join("|", QueueType.getNames()) + "]");
|
||||
CommandLogContext.setError("invalid_queue_type");
|
||||
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() + "`.");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before.name()).put("after", value.name()));
|
||||
}
|
||||
}
|
||||
+83
-69
@@ -1,69 +1,83 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import java.util.List;
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.utils.FinderUtil;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.entities.Role;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetdjCmd extends Command {
|
||||
|
||||
private final Bot bot;
|
||||
public SetdjCmd(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.name = "setdj";
|
||||
this.help = "sets the DJ role for certain music commands";
|
||||
this.arguments = "<rolename|NONE>";
|
||||
this.guildOnly = true;
|
||||
this.category = bot.ADMIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a role name or NONE");
|
||||
}
|
||||
else if(event.getArgs().equalsIgnoreCase("none"))
|
||||
{
|
||||
bot.clearTextChannel(event.getGuild());
|
||||
event.reply(event.getClient().getSuccess()+" DJ role cleared; Only Admins can use the DJ commands.");
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Role> list = FinderUtil.findRole(event.getArgs(), event.getGuild());
|
||||
if(list.isEmpty())
|
||||
event.reply(event.getClient().getWarning()+" No Roles found matching \""+event.getArgs()+"\"");
|
||||
else if (list.size()>1)
|
||||
event.reply(event.getClient().getWarning()+FormatUtil.listOfRoles(list, event.getArgs()));
|
||||
else
|
||||
{
|
||||
bot.setRole(list.get(0));
|
||||
event.reply(event.getClient().getSuccess()+" DJ commands can now be used by users with the **"+list.get(0).getName()+"** role.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jdautilities.commons.utils.FinderUtil;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.AdminCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetdjCmd extends AdminCommand
|
||||
{
|
||||
public SetdjCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "setdj";
|
||||
this.help = "sets the DJ role for certain music commands";
|
||||
this.arguments = "<rolename|NONE>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a role name or NONE");
|
||||
CommandLogContext.setError("missing_role");
|
||||
return;
|
||||
}
|
||||
Settings s = event.getClient().getSettingsFor(event.getGuild());
|
||||
String before = s.getRole(event.getGuild()) == null ? null : s.getRole(event.getGuild()).getId();
|
||||
if(event.getArgs().equalsIgnoreCase("none"))
|
||||
{
|
||||
s.setDJRole(null);
|
||||
event.reply(event.getClient().getSuccess()+" DJ role cleared; Only Admins can use the DJ commands.");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before).put("after", JSONObject.NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Role> list = FinderUtil.findRoles(event.getArgs(), event.getGuild());
|
||||
if(list.isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+" No Roles found matching \""+event.getArgs()+"\"");
|
||||
CommandLogContext.setError("role_not_found");
|
||||
}
|
||||
else if (list.size()>1)
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+FormatUtil.listOfRoles(list, event.getArgs()));
|
||||
CommandLogContext.setError("role_ambiguous");
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setDJRole(list.get(0));
|
||||
event.reply(event.getClient().getSuccess()+" DJ commands can now be used by users with the **"+list.get(0).getName()+"** role.");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before).put("after", list.get(0).getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+84
-69
@@ -1,69 +1,84 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import java.util.List;
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.utils.FinderUtil;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.entities.TextChannel;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SettcCmd extends Command {
|
||||
|
||||
private final Bot bot;
|
||||
public SettcCmd(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.name = "settc";
|
||||
this.help = "sets the text channel for music commands";
|
||||
this.arguments = "<channel|NONE>";
|
||||
this.guildOnly = true;
|
||||
this.category = bot.ADMIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a text channel or NONE");
|
||||
}
|
||||
else if(event.getArgs().equalsIgnoreCase("none"))
|
||||
{
|
||||
bot.clearTextChannel(event.getGuild());
|
||||
event.reply(event.getClient().getSuccess()+" Music commands can now be used in any channel");
|
||||
}
|
||||
else
|
||||
{
|
||||
List<TextChannel> list = FinderUtil.findTextChannel(event.getArgs(), event.getGuild());
|
||||
if(list.isEmpty())
|
||||
event.reply(event.getClient().getWarning()+" No Text Channels found matching \""+event.getArgs()+"\"");
|
||||
else if (list.size()>1)
|
||||
event.reply(event.getClient().getWarning()+FormatUtil.listOfTChannels(list, event.getArgs()));
|
||||
else
|
||||
{
|
||||
bot.setTextChannel(list.get(0));
|
||||
event.reply(event.getClient().getSuccess()+" Music commands can now only be used in <#"+list.get(0).getId()+">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jdautilities.commons.utils.FinderUtil;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.AdminCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SettcCmd extends AdminCommand
|
||||
{
|
||||
public SettcCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "settc";
|
||||
this.help = "sets the text channel for music commands";
|
||||
this.arguments = "<channel|NONE>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a text channel or NONE");
|
||||
CommandLogContext.setError("missing_text_channel");
|
||||
return;
|
||||
}
|
||||
Settings s = event.getClient().getSettingsFor(event.getGuild());
|
||||
String before = s.getTextChannel(event.getGuild()) == null ? null : s.getTextChannel(event.getGuild()).getId();
|
||||
if(event.getArgs().equalsIgnoreCase("none"))
|
||||
{
|
||||
s.setTextChannel(null);
|
||||
event.reply(event.getClient().getSuccess()+" Music commands can now be used in any channel");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before).put("after", JSONObject.NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
List<TextChannel> list = (List) FinderUtil.findTextChannels(event.getArgs(), event.getGuild());
|
||||
if(list.isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+" No Text Channels found matching \""+event.getArgs()+"\"");
|
||||
CommandLogContext.setError("text_channel_not_found");
|
||||
}
|
||||
else if (list.size()>1)
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+FormatUtil.listOfTChannels(list, event.getArgs()));
|
||||
CommandLogContext.setError("text_channel_ambiguous");
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setTextChannel(list.get(0));
|
||||
event.reply(event.getClient().getSuccess()+" Music commands can now only be used in <#"+list.get(0).getId()+">");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before).put("after", list.get(0).getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jdautilities.commons.utils.FinderUtil;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.AdminCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetvcCmd extends AdminCommand
|
||||
{
|
||||
public SetvcCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "setvc";
|
||||
this.help = "sets the voice channel for playing music";
|
||||
this.arguments = "<channel|NONE>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a voice channel or NONE");
|
||||
CommandLogContext.setError("missing_voice_channel");
|
||||
return;
|
||||
}
|
||||
Settings s = event.getClient().getSettingsFor(event.getGuild());
|
||||
String before = s.getVoiceChannel(event.getGuild()) == null ? null : s.getVoiceChannel(event.getGuild()).getId();
|
||||
if(event.getArgs().equalsIgnoreCase("none"))
|
||||
{
|
||||
s.setVoiceChannel(null);
|
||||
event.reply(event.getClient().getSuccess()+" Music can now be played in any channel");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before).put("after", JSONObject.NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
List<VoiceChannel> list = (List) FinderUtil.findVoiceChannels(event.getArgs(), event.getGuild());
|
||||
if(list.isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+" No Voice Channels found matching \""+event.getArgs()+"\"");
|
||||
CommandLogContext.setError("voice_channel_not_found");
|
||||
}
|
||||
else if (list.size()>1)
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+FormatUtil.listOfVChannels(list, event.getArgs()));
|
||||
CommandLogContext.setError("voice_channel_ambiguous");
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setVoiceChannel(list.get(0));
|
||||
event.reply(event.getClient().getSuccess()+" Music can now only be played in "+list.get(0).getAsMention());
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before).put("after", list.get(0).getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class SkipratioCmd extends AdminCommand
|
||||
{
|
||||
public SkipratioCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "setskip";
|
||||
this.help = "sets a server-specific skip percentage";
|
||||
this.arguments = "<0 - 100>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(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!");
|
||||
CommandLogContext.setError("invalid_skip_ratio");
|
||||
return;
|
||||
}
|
||||
Settings s = event.getClient().getSettingsFor(event.getGuild());
|
||||
double before = s.getSkipRatio();
|
||||
s.setSkipRatio(val / 100.0);
|
||||
event.replySuccess("Skip percentage has been set to `" + val + "%` of listeners on *" + event.getGuild().getName() + "*");
|
||||
CommandLogContext.setMeta(new JSONObject()
|
||||
.put("before", before)
|
||||
.put("after", s.getSkipRatio()));
|
||||
}
|
||||
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.");
|
||||
CommandLogContext.setError("invalid_skip_ratio");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2019 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.dj;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jdautilities.commons.utils.FinderUtil;
|
||||
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.datalog.CommandLogContext;
|
||||
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;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Michaili K.
|
||||
*/
|
||||
public class ForceRemoveCmd extends DJCommand
|
||||
{
|
||||
public ForceRemoveCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "forceremove";
|
||||
this.help = "removes all entries by a user from the queue";
|
||||
this.arguments = "<user>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.beListening = false;
|
||||
this.bePlaying = true;
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if (event.getArgs().isEmpty())
|
||||
{
|
||||
event.replyError("You need to mention a user!");
|
||||
CommandLogContext.setError("missing_user");
|
||||
return;
|
||||
}
|
||||
|
||||
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
|
||||
if (handler.getQueue().isEmpty())
|
||||
{
|
||||
event.replyError("There is nothing in the queue!");
|
||||
CommandLogContext.setError("empty_queue");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
User target;
|
||||
List<Member> found = FinderUtil.findMembers(event.getArgs(), event.getGuild());
|
||||
|
||||
if(found.isEmpty())
|
||||
{
|
||||
event.replyError("Unable to find the user!");
|
||||
CommandLogContext.setError("user_not_found");
|
||||
return;
|
||||
}
|
||||
else if(found.size()>1)
|
||||
{
|
||||
OrderedMenu.Builder builder = new OrderedMenu.Builder();
|
||||
for(int i=0; i<found.size() && i<4; i++)
|
||||
{
|
||||
Member member = found.get(i);
|
||||
builder.addChoice("**"+member.getUser().getName()+"**#"+member.getUser().getDiscriminator());
|
||||
}
|
||||
|
||||
builder
|
||||
.setSelection((msg, i) -> removeAllEntries(found.get(i-1).getUser(), event))
|
||||
.setText("Found multiple users:")
|
||||
.setColor(event.getSelfMember().getColor())
|
||||
.useNumbers()
|
||||
.setUsers(event.getAuthor())
|
||||
.useCancelButton(true)
|
||||
.setCancel((msg) -> {})
|
||||
.setEventWaiter(bot.getWaiter())
|
||||
.setTimeout(1, TimeUnit.MINUTES)
|
||||
|
||||
.build().display(event.getChannel());
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
target = found.get(0).getUser();
|
||||
}
|
||||
|
||||
removeAllEntries(target, event);
|
||||
|
||||
}
|
||||
|
||||
private void removeAllEntries(User target, CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
|
||||
int sizeBefore = handler.getQueue().size();
|
||||
int count = handler.getQueue().removeAll(target.getIdLong());
|
||||
if (count == 0)
|
||||
{
|
||||
event.replyWarning("**"+target.getName()+"** doesn't have any songs in the queue!");
|
||||
CommandLogContext.setError("no_entries_for_user");
|
||||
}
|
||||
else
|
||||
{
|
||||
event.replySuccess("Successfully removed `"+count+"` entries from "+FormatUtil.formatUsername(target)+".");
|
||||
}
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("target_user_id", target.getIdLong())
|
||||
.put("removed_count", count)
|
||||
.put("queue_size_before", sizeBefore)
|
||||
.put("queue_size_after", handler.getQueue().size());
|
||||
CommandLogContext.setMeta(meta);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), null, "REMOVE_ALL",
|
||||
"FORCE", null, null, null, meta.toString());
|
||||
}
|
||||
}
|
||||
+21
-16
@@ -13,41 +13,46 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.commands;
|
||||
package com.jagrosh.jmusicbot.commands.dj;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import net.dv8tion.jda.core.entities.User;
|
||||
import com.jagrosh.jmusicbot.audio.RequestMetadata;
|
||||
import com.jagrosh.jmusicbot.commands.DJCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class ForceskipCmd extends MusicCommand {
|
||||
|
||||
public class ForceskipCmd extends DJCommand
|
||||
{
|
||||
public ForceskipCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "forceskip";
|
||||
this.help = "skips the current song";
|
||||
this.aliases = new String[]{"modskip"};
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.bePlaying = true;
|
||||
this.category = bot.DJ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
User u;
|
||||
try {
|
||||
u = event.getJDA().getUserById(handler.getCurrentTrack().getIdentifier());
|
||||
} catch(Exception e) {
|
||||
u = null;
|
||||
RequestMetadata rm = handler.getRequestMetadata();
|
||||
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title
|
||||
+"** "+(rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + FormatUtil.formatUsername(rm.user) + "**)"));
|
||||
if(bot.getDataLogService() != null)
|
||||
{
|
||||
JSONObject meta = new JSONObject().put("reason", "FORCE");
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), handler.getPlayer().getPlayingTrack(),
|
||||
"SKIP_FORCE", null, null, null, null, meta.toString());
|
||||
CommandLogContext.setMeta(meta);
|
||||
}
|
||||
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getCurrentTrack().getTrack().getInfo().title
|
||||
+"** (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
|
||||
handler.getPlayer().stopTrack();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
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.QueuedTrack;
|
||||
import com.jagrosh.jmusicbot.commands.DJCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.queue.AbstractQueue;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Command that provides users the ability to move a track in the playlist.
|
||||
*/
|
||||
public class MoveTrackCmd extends DJCommand
|
||||
{
|
||||
|
||||
public MoveTrackCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "movetrack";
|
||||
this.help = "move a track in the current queue to a different position";
|
||||
this.arguments = "<from> <to>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.bePlaying = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
int from;
|
||||
int to;
|
||||
|
||||
String[] parts = event.getArgs().split("\\s+", 2);
|
||||
if(parts.length < 2)
|
||||
{
|
||||
event.replyError("Please include two valid indexes.");
|
||||
CommandLogContext.setError("missing_indexes");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Validate the args
|
||||
from = Integer.parseInt(parts[0]);
|
||||
to = Integer.parseInt(parts[1]);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
event.replyError("Please provide two valid indexes.");
|
||||
CommandLogContext.setError("invalid_indexes");
|
||||
return;
|
||||
}
|
||||
|
||||
if (from == to)
|
||||
{
|
||||
event.replyError("Can't move a track to the same position.");
|
||||
CommandLogContext.setError("same_position");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate that from and to are available
|
||||
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
|
||||
AbstractQueue<QueuedTrack> queue = handler.getQueue();
|
||||
if (isUnavailablePosition(queue, from))
|
||||
{
|
||||
String reply = String.format("`%d` is not a valid position in the queue!", from);
|
||||
event.replyError(reply);
|
||||
CommandLogContext.setError("invalid_from_position");
|
||||
return;
|
||||
}
|
||||
if (isUnavailablePosition(queue, to))
|
||||
{
|
||||
String reply = String.format("`%d` is not a valid position in the queue!", to);
|
||||
event.replyError(reply);
|
||||
CommandLogContext.setError("invalid_to_position");
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the track
|
||||
int sizeBefore = queue.size();
|
||||
QueuedTrack track = queue.moveItem(from - 1, to - 1);
|
||||
String trackTitle = track.getTrack().getInfo().title;
|
||||
String reply = String.format("Moved **%s** from position `%d` to `%d`.", trackTitle, from, to);
|
||||
event.replySuccess(reply);
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("position_from", from)
|
||||
.put("position_to", to)
|
||||
.put("queue_size_before", sizeBefore)
|
||||
.put("queue_size_after", queue.size());
|
||||
CommandLogContext.setMeta(meta);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), track.getTrack(),
|
||||
"MOVE", null, to, null, null, meta.toString());
|
||||
}
|
||||
|
||||
private static boolean isUnavailablePosition(AbstractQueue<QueuedTrack> queue, int position)
|
||||
{
|
||||
return (position < 1 || position > queue.size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.dj;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.commands.DJCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class PauseCmd extends DJCommand
|
||||
{
|
||||
public PauseCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "pause";
|
||||
this.help = "pauses the current song";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.bePlaying = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
if(handler.getPlayer().isPaused())
|
||||
{
|
||||
event.replyWarning("The player is already paused! Use `"+event.getClient().getPrefix()+"play` to unpause!");
|
||||
CommandLogContext.setError("already_paused");
|
||||
return;
|
||||
}
|
||||
handler.getPlayer().setPaused(true);
|
||||
event.replySuccess("Paused **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**. Type `"+event.getClient().getPrefix()+"play` to unpause!");
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("position_ms", handler.getPlayer().getPlayingTrack().getPosition());
|
||||
CommandLogContext.setMeta(meta);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), handler.getPlayer().getPlayingTrack(),
|
||||
"PAUSE", null, null, null, null, meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.dj;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.audio.QueuedTrack;
|
||||
import com.jagrosh.jmusicbot.audio.RequestMetadata;
|
||||
import com.jagrosh.jmusicbot.commands.DJCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import com.jagrosh.jmusicbot.utils.TimeUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class PlaynextCmd extends DJCommand
|
||||
{
|
||||
private final String loadingEmoji;
|
||||
|
||||
public PlaynextCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.loadingEmoji = bot.getConfig().getLoading();
|
||||
this.name = "playnext";
|
||||
this.arguments = "<title|URL>";
|
||||
this.help = "plays a single song next";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.beListening = true;
|
||||
this.bePlaying = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.setManualLogging();
|
||||
if(event.getArgs().isEmpty() && event.getMessage().getAttachments().isEmpty())
|
||||
{
|
||||
event.replyWarning("Please include a song title or URL!");
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "missing_query"));
|
||||
return;
|
||||
}
|
||||
String args = event.getArgs().startsWith("<") && event.getArgs().endsWith(">")
|
||||
? event.getArgs().substring(1,event.getArgs().length()-1)
|
||||
: event.getArgs().isEmpty() ? event.getMessage().getAttachments().get(0).getUrl() : event.getArgs();
|
||||
event.reply(loadingEmoji+" Loading... `["+args+"]`", m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), args, new ResultHandler(m,event,false)));
|
||||
}
|
||||
|
||||
private class ResultHandler implements AudioLoadResultHandler
|
||||
{
|
||||
private final Message m;
|
||||
private final CommandEvent event;
|
||||
private final boolean ytsearch;
|
||||
|
||||
private ResultHandler(Message m, CommandEvent event, boolean ytsearch)
|
||||
{
|
||||
this.m = m;
|
||||
this.event = event;
|
||||
this.ytsearch = ytsearch;
|
||||
}
|
||||
|
||||
private void loadSingle(AudioTrack track)
|
||||
{
|
||||
if(bot.getConfig().isTooLong(track))
|
||||
{
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+ TimeUtil.formatTime(track.getDuration())+"` > `"+ TimeUtil.formatTime(bot.getConfig().getMaxSeconds()*1000)+"`")).queue();
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "track_too_long"));
|
||||
return;
|
||||
}
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
RequestMetadata rm = RequestMetadata.fromResultHandler(track, event, "PLAYNEXT", null, event.getArgs());
|
||||
int pos = handler.addTrackToFront(new QueuedTrack(track, rm))+1;
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueAdd(event.getGuild(), event.getAuthor(), track, "PLAYNEXT", pos, event.getArgs(), null);
|
||||
String addMsg = FormatUtil.filter(event.getClient().getSuccess()+" Added **"+track.getInfo().title
|
||||
+"** (`"+ TimeUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos));
|
||||
m.editMessage(addMsg).queue();
|
||||
logCommandEvent(event, "OK", new JSONObject().put("queue_position", pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack track)
|
||||
{
|
||||
loadSingle(track);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist playlist)
|
||||
{
|
||||
AudioTrack single;
|
||||
if(playlist.getTracks().size()==1 || playlist.isSearchResult())
|
||||
single = playlist.getSelectedTrack()==null ? playlist.getTracks().get(0) : playlist.getSelectedTrack();
|
||||
else if (playlist.getSelectedTrack()!=null)
|
||||
single = playlist.getSelectedTrack();
|
||||
else
|
||||
single = playlist.getTracks().get(0);
|
||||
loadSingle(single);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches()
|
||||
{
|
||||
if(ytsearch)
|
||||
{
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.")).queue();
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "no_matches"));
|
||||
}
|
||||
else
|
||||
bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event,true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException throwable)
|
||||
{
|
||||
if(throwable.severity==FriendlyException.Severity.COMMON)
|
||||
m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue();
|
||||
else
|
||||
m.editMessage(event.getClient().getError()+" Error loading track.").queue();
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", throwable.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void logCommandEvent(CommandEvent event, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), result, meta == null ? null : meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.dj;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.DJCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.RepeatMode;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class RepeatCmd extends DJCommand
|
||||
{
|
||||
public RepeatCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "repeat";
|
||||
this.help = "re-adds music to the queue when finished";
|
||||
this.arguments = "[off|all|single]";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.guildOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
String args = event.getArgs();
|
||||
RepeatMode value;
|
||||
Settings settings = event.getClient().getSettingsFor(event.getGuild());
|
||||
RepeatMode before = settings.getRepeatMode();
|
||||
if(args.isEmpty())
|
||||
{
|
||||
if(settings.getRepeatMode() == RepeatMode.OFF)
|
||||
value = RepeatMode.ALL;
|
||||
else
|
||||
value = RepeatMode.OFF;
|
||||
}
|
||||
else if(args.equalsIgnoreCase("false") || args.equalsIgnoreCase("off"))
|
||||
{
|
||||
value = RepeatMode.OFF;
|
||||
}
|
||||
else if(args.equalsIgnoreCase("true") || args.equalsIgnoreCase("on") || args.equalsIgnoreCase("all"))
|
||||
{
|
||||
value = RepeatMode.ALL;
|
||||
}
|
||||
else if(args.equalsIgnoreCase("one") || args.equalsIgnoreCase("single"))
|
||||
{
|
||||
value = RepeatMode.SINGLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
event.replyError("Valid options are `off`, `all` or `single` (or leave empty to toggle between `off` and `all`)");
|
||||
CommandLogContext.setError("invalid_repeat_mode");
|
||||
return;
|
||||
}
|
||||
settings.setRepeatMode(value);
|
||||
event.replySuccess("Repeat mode is now `"+value.getUserFriendlyName()+"`");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before.name()).put("after", value.name()));
|
||||
}
|
||||
}
|
||||
+21
-9
@@ -13,32 +13,34 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.commands;
|
||||
package com.jagrosh.jmusicbot.commands.dj;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import net.dv8tion.jda.core.entities.User;
|
||||
import com.jagrosh.jmusicbot.commands.DJCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SkiptoCmd extends MusicCommand {
|
||||
|
||||
public class SkiptoCmd extends DJCommand
|
||||
{
|
||||
public SkiptoCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "skipto";
|
||||
this.help = "skips to the specified song";
|
||||
this.arguments = "<position>";
|
||||
this.aliases = new String[]{"jumpto"};
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.bePlaying = true;
|
||||
this.category = bot.DJ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
int index = 0;
|
||||
try
|
||||
{
|
||||
@@ -47,17 +49,27 @@ public class SkiptoCmd extends MusicCommand {
|
||||
catch(NumberFormatException e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" `"+event.getArgs()+"` is not a valid integer!");
|
||||
CommandLogContext.setError("invalid_index");
|
||||
return;
|
||||
}
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
if(index<1 || index>handler.getQueue().size())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Position must be a valid integer between 1 and "+handler.getQueue().size()+"!");
|
||||
CommandLogContext.setError("index_out_of_range");
|
||||
return;
|
||||
}
|
||||
int sizeBefore = handler.getQueue().size();
|
||||
handler.getQueue().skip(index-1);
|
||||
event.reply(event.getClient().getSuccess()+" Skipped to **"+handler.getQueue().get(0).getTrack().getInfo().title+"**");
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("position_to", index)
|
||||
.put("queue_size_before", sizeBefore)
|
||||
.put("queue_size_after", handler.getQueue().size());
|
||||
CommandLogContext.setMeta(meta);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), handler.getPlayer().getPlayingTrack(),
|
||||
"SKIP_SKIPTO", null, index, null, null, meta.toString());
|
||||
handler.getPlayer().stopTrack();
|
||||
}
|
||||
|
||||
}
|
||||
+58
-46
@@ -1,46 +1,58 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class StopCmd extends MusicCommand {
|
||||
|
||||
public StopCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "stop";
|
||||
this.help = "stops the current song and clears the queue";
|
||||
this.bePlaying = true;
|
||||
this.category = bot.DJ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
handler.getQueue().clear();
|
||||
handler.getPlayer().stopTrack();
|
||||
event.getGuild().getAudioManager().closeAudioConnection();
|
||||
event.reply(event.getClient().getSuccess()+" The player has stopped and the queue has been cleared.");
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.dj;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.commands.DJCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class StopCmd extends DJCommand
|
||||
{
|
||||
public StopCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "stop";
|
||||
this.help = "stops the current song and clears the queue";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.bePlaying = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
int sizeBefore = handler.getQueue().size();
|
||||
if(handler.getPlayer().getPlayingTrack() != null && bot.getDataLogService() != null)
|
||||
{
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("queue_size_before", sizeBefore)
|
||||
.put("queue_size_after", 0);
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), handler.getPlayer().getPlayingTrack(),
|
||||
"STOP", null, null, null, null, meta.toString());
|
||||
CommandLogContext.setMeta(meta);
|
||||
}
|
||||
handler.stopAndClear();
|
||||
event.getGuild().getAudioManager().closeAudioConnection();
|
||||
event.reply(event.getClient().getSuccess()+" The player has stopped and the queue has been cleared.");
|
||||
}
|
||||
}
|
||||
+20
-10
@@ -13,36 +13,42 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.commands;
|
||||
package com.jagrosh.jmusicbot.commands.dj;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.commands.DJCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class VolumeCmd extends MusicCommand {
|
||||
|
||||
public class VolumeCmd extends DJCommand
|
||||
{
|
||||
public VolumeCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "volume";
|
||||
this.aliases = new String[]{"vol"};
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.help = "sets or shows volume";
|
||||
this.arguments = "[0-150]";
|
||||
this.category = bot.DJ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
int volume = handler==null && bot.getSettings(event.getGuild())==null ? 100 : (handler==null ? bot.getSettings(event.getGuild()).getVolume() : handler.getPlayer().getVolume());
|
||||
Settings settings = event.getClient().getSettingsFor(event.getGuild());
|
||||
int volume = handler.getPlayer().getVolume();
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(FormatUtil.volumeIcon(volume)+" Current volume is `"+volume+"`");
|
||||
CommandLogContext.setMeta(new JSONObject().put("current", volume));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -53,12 +59,16 @@ public class VolumeCmd extends MusicCommand {
|
||||
nvolume = -1;
|
||||
}
|
||||
if(nvolume<0 || nvolume>150)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Volume must be a valid integer between 0 and 150!");
|
||||
CommandLogContext.setError("invalid_volume");
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.setUpHandler(event).getPlayer().setVolume(nvolume);
|
||||
bot.setVolume(event.getGuild(), nvolume);
|
||||
handler.getPlayer().setVolume(nvolume);
|
||||
settings.setVolume(nvolume);
|
||||
event.reply(FormatUtil.volumeIcon(nvolume)+" Volume changed from `"+volume+"` to `"+nvolume+"`");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", volume).put("after", nvolume));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* 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.general;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jdautilities.examples.command.AboutCommand;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.JMusicBot;
|
||||
import com.jagrosh.jmusicbot.utils.OtherUtil;
|
||||
import java.awt.Color;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.JDAInfo;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.ApplicationInfo;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class LoggedAboutCmd extends AboutCommand
|
||||
{
|
||||
private final Bot bot;
|
||||
private final Color color;
|
||||
private final String description;
|
||||
private final String[] features;
|
||||
private final Permission[] perms;
|
||||
private boolean isAuthor = true;
|
||||
private String replacementCharacter = "+";
|
||||
private String oauthLink;
|
||||
|
||||
public LoggedAboutCmd(Bot bot, Color color, String description, String[] features, Permission[] perms)
|
||||
{
|
||||
super(color, description, features, perms);
|
||||
this.bot = bot;
|
||||
this.color = color;
|
||||
this.description = description;
|
||||
this.features = features == null ? new String[0] : features.clone();
|
||||
this.perms = perms == null ? new Permission[0] : perms.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIsAuthor(boolean isAuthor)
|
||||
{
|
||||
super.setIsAuthor(isAuthor);
|
||||
this.isAuthor = isAuthor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReplacementCharacter(String replacementCharacter)
|
||||
{
|
||||
super.setReplacementCharacter(replacementCharacter);
|
||||
this.replacementCharacter = replacementCharacter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event)
|
||||
{
|
||||
if(oauthLink == null)
|
||||
oauthLink = resolveOauthLink(event);
|
||||
|
||||
EmbedBuilder builder = new EmbedBuilder()
|
||||
.setColor(event.isFromType(ChannelType.TEXT)
|
||||
? event.getGuild().getSelfMember().getColor()
|
||||
: color)
|
||||
.setAuthor("All about " + event.getSelfUser().getName() + "!",
|
||||
null,
|
||||
event.getSelfUser().getAvatarUrl())
|
||||
.setDescription(buildDescription(event));
|
||||
|
||||
addStats(builder, event);
|
||||
builder.setFooter("Last restart", null)
|
||||
.setTimestamp(event.getClient().getStartTime());
|
||||
|
||||
event.reply(builder.build());
|
||||
if(bot.getDataLogService() != null && event.getGuild() != null)
|
||||
{
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), "OK", new JSONObject().put("about", true).toString());
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveOauthLink(CommandEvent event)
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplicationInfo info = event.getJDA().retrieveApplicationInfo().complete();
|
||||
return info.isBotPublic() ? info.getInviteUrl(0, perms) : "";
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
JMusicBot.LOG.error("Could not generate invite link", ex);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String buildDescription(CommandEvent event)
|
||||
{
|
||||
String owner = getOwnerDisplay(event);
|
||||
String links = getLinksSection(event);
|
||||
StringBuilder builder = new StringBuilder()
|
||||
.append("Hello! I am **").append(event.getSelfUser().getName()).append("**, ")
|
||||
.append(description)
|
||||
.append("\nI ")
|
||||
.append(isAuthor ? "was written in Java" : "am owned")
|
||||
.append(" by **").append(owner).append("**")
|
||||
.append(" using Chew's [Commands Extension](https://github.com/Chew/JDA-Chewtils) (")
|
||||
.append(OtherUtil.getCommandLibraryVersion())
|
||||
.append(") and the [JDA library](https://github.com/DV8FromTheWorld/JDA) (")
|
||||
.append(JDAInfo.VERSION)
|
||||
.append(")\nType `")
|
||||
.append(event.getClient().getTextualPrefix())
|
||||
.append(event.getClient().getHelpWord())
|
||||
.append("` to see my commands!");
|
||||
|
||||
if(!links.isEmpty())
|
||||
builder.append(links);
|
||||
|
||||
builder.append("\n\nSome of my features include: ```css");
|
||||
for(String feature : features)
|
||||
{
|
||||
builder.append("\n")
|
||||
.append(event.getClient().getSuccess().startsWith("<")
|
||||
? replacementCharacter
|
||||
: event.getClient().getSuccess())
|
||||
.append(" ")
|
||||
.append(feature);
|
||||
}
|
||||
builder.append("```");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String getOwnerDisplay(CommandEvent event)
|
||||
{
|
||||
User owner = event.getJDA().getUserById(event.getClient().getOwnerId());
|
||||
return owner == null ? "<@" + event.getClient().getOwnerId() + ">" : owner.getName();
|
||||
}
|
||||
|
||||
private String getLinksSection(CommandEvent event)
|
||||
{
|
||||
String serverInvite = event.getClient().getServerInvite();
|
||||
boolean hasServerInvite = serverInvite != null && !serverInvite.isEmpty();
|
||||
boolean hasOauthLink = oauthLink != null && !oauthLink.isEmpty();
|
||||
if(!hasServerInvite && !hasOauthLink)
|
||||
return "";
|
||||
|
||||
StringBuilder builder = new StringBuilder("\n");
|
||||
if(hasServerInvite)
|
||||
builder.append("Join my server [`here`](").append(serverInvite).append(")");
|
||||
else if(hasOauthLink)
|
||||
builder.append("Please");
|
||||
|
||||
if(hasOauthLink)
|
||||
{
|
||||
if(hasServerInvite)
|
||||
builder.append(", or");
|
||||
builder.append(" [`invite`](").append(oauthLink).append(") me to your server");
|
||||
}
|
||||
builder.append("!");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private void addStats(EmbedBuilder builder, CommandEvent event)
|
||||
{
|
||||
JDA jda = event.getJDA();
|
||||
if(jda.getShardInfo() == JDA.ShardInfo.SINGLE)
|
||||
{
|
||||
builder.addField("Stats", jda.getGuilds().size() + " servers\n1 shard", true)
|
||||
.addField("Users",
|
||||
jda.getUsers().size() + " unique\n"
|
||||
+ jda.getGuilds().stream().mapToInt(Guild::getMemberCount).sum() + " total",
|
||||
true)
|
||||
.addField("Channels",
|
||||
jda.getTextChannels().size() + " Text\n" + jda.getVoiceChannels().size() + " Voice",
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
builder.addField("Stats",
|
||||
event.getClient().getTotalGuilds() + " Servers\nShard "
|
||||
+ (jda.getShardInfo().getShardId() + 1) + "/" + jda.getShardInfo().getShardTotal(),
|
||||
true)
|
||||
.addField("This shard", jda.getUsers().size() + " Users\n" + jda.getGuilds().size() + " Servers", true)
|
||||
.addField("",
|
||||
jda.getTextChannels().size() + " Text Channels\n" + jda.getVoiceChannels().size() + " Voice Channels",
|
||||
true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* 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.general;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jdautilities.examples.command.PingCommand;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class LoggedPingCmd extends PingCommand
|
||||
{
|
||||
private final Bot bot;
|
||||
|
||||
public LoggedPingCmd(Bot bot)
|
||||
{
|
||||
super();
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event)
|
||||
{
|
||||
super.execute(event);
|
||||
if(bot.getDataLogService() != null && event.getGuild() != null)
|
||||
{
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), "OK", new JSONObject().put("ping", true).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2017 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.general;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.LoggedCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.QueueType;
|
||||
import com.jagrosh.jmusicbot.settings.RepeatMode;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SettingsCmd extends LoggedCommand
|
||||
{
|
||||
private final static String EMOJI = "\uD83C\uDFA7"; // 🎧
|
||||
|
||||
public SettingsCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "settings";
|
||||
this.help = "shows the bots settings";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.guildOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
Settings s = event.getClient().getSettingsFor(event.getGuild());
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("repeat_mode", s.getRepeatMode().name())
|
||||
.put("queue_type", s.getQueueType().name())
|
||||
.put("has_default_playlist", s.getDefaultPlaylist() != null)
|
||||
.put("has_custom_prefix", s.getPrefix() != null)
|
||||
.put("has_text_channel", s.getTextChannel(event.getGuild()) != null)
|
||||
.put("has_voice_channel", s.getVoiceChannel(event.getGuild()) != null)
|
||||
.put("has_dj_role", s.getRole(event.getGuild()) != null)
|
||||
.put("stats_report_frequency", s.getStatsReportFrequency().name())
|
||||
.put("has_stats_report_channel", s.getStatsReportChannel(event.getGuild()) != null);
|
||||
CommandLogContext.setMeta(meta);
|
||||
MessageCreateBuilder builder = new MessageCreateBuilder()
|
||||
.addContent(EMOJI + " **")
|
||||
.addContent(FormatUtil.filter(event.getSelfUser().getName()))
|
||||
.addContent("** settings:");
|
||||
TextChannel tchan = s.getTextChannel(event.getGuild());
|
||||
VoiceChannel vchan = s.getVoiceChannel(event.getGuild());
|
||||
TextChannel statsChannel = s.getStatsReportChannel(event.getGuild());
|
||||
Role role = s.getRole(event.getGuild());
|
||||
EmbedBuilder ebuilder = new EmbedBuilder()
|
||||
.setColor(event.getSelfMember().getColor())
|
||||
.setDescription("Text Channel: " + (tchan == null ? "Any" : "**#" + tchan.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() == 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() + "**")
|
||||
+ "\nStats Reports: " + (s.getStatsReportFrequency().name())
|
||||
+ (statsChannel == null ? "" : " in " + statsChannel.getAsMention())
|
||||
)
|
||||
.setFooter(event.getJDA().getGuilds().size() + " servers | "
|
||||
+ event.getJDA().getGuilds().stream().filter(g -> g.getSelfMember().getVoiceState().inAudioChannel()).count()
|
||||
+ " audio connections", null);
|
||||
event.getMessage().getChannel().sendMessage(builder.setEmbeds(ebuilder.build()).build()).queue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.commands.general;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jdautilities.commons.utils.FinderUtil;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.commands.LoggedCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.datalog.StatsFilters;
|
||||
import com.jagrosh.jmusicbot.datalog.StatsAccess;
|
||||
import com.jagrosh.jmusicbot.datalog.StatsMessageFormatter;
|
||||
import com.jagrosh.jmusicbot.datalog.StatsSummary;
|
||||
import com.jagrosh.jmusicbot.datalog.StatsTimeRange;
|
||||
import com.jagrosh.jmusicbot.datalog.StatsRow;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.settings.StatsReportFrequency;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class StatsCmd extends LoggedCommand
|
||||
{
|
||||
public StatsCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "stats";
|
||||
this.help = "shows playback statistics";
|
||||
this.arguments = "<server|me|user|tracks|listened|track|skips|sources|reports>";
|
||||
this.guildOnly = true;
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(bot.getDataLogService() == null)
|
||||
{
|
||||
event.replyWarning("Playback analytics are not enabled.");
|
||||
CommandLogContext.setError("analytics_disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> tokens = new ArrayList<>(Arrays.asList(event.getArgs().trim().split("\\s+")));
|
||||
if(tokens.size() == 1 && tokens.get(0).isEmpty())
|
||||
tokens.clear();
|
||||
String subcommand = tokens.isEmpty() ? "server" : tokens.remove(0).toLowerCase();
|
||||
switch(subcommand)
|
||||
{
|
||||
case "server":
|
||||
sendSummary(event, "Server stats", parseOptions(tokens), null, Section.ALL);
|
||||
break;
|
||||
case "me":
|
||||
sendSummary(event, "Your stats", parseOptions(tokens), event.getAuthor().getIdLong(), Section.ALL);
|
||||
break;
|
||||
case "user":
|
||||
handleUserStats(event, tokens);
|
||||
break;
|
||||
case "track":
|
||||
handleTrackStats(event, parseOptions(tokens));
|
||||
break;
|
||||
case "tracks":
|
||||
case "played":
|
||||
handleTrackListStats(event, "Top played tracks", parseOptions(tokens), false);
|
||||
break;
|
||||
case "listened":
|
||||
case "listens":
|
||||
handleTrackListStats(event, "Top listened tracks", parseOptions(tokens), true);
|
||||
break;
|
||||
case "skips":
|
||||
sendSummary(event, "Skip stats", parseOptions(tokens), null, Section.SKIPS);
|
||||
break;
|
||||
case "sources":
|
||||
sendSummary(event, "Source stats", parseOptions(tokens), null, Section.SOURCES);
|
||||
break;
|
||||
case "reports":
|
||||
handleReports(event, tokens);
|
||||
break;
|
||||
case "help":
|
||||
sendUsage(event);
|
||||
break;
|
||||
default:
|
||||
tokens.add(0, subcommand);
|
||||
handleUserStats(event, tokens);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUserStats(CommandEvent event, List<String> tokens)
|
||||
{
|
||||
Options options = parseOptions(tokens);
|
||||
String targetText = String.join(" ", options.remaining).trim();
|
||||
if(targetText.isEmpty())
|
||||
{
|
||||
event.replyError("Please include a member.");
|
||||
CommandLogContext.setError("missing_member");
|
||||
return;
|
||||
}
|
||||
Long targetId = parseUserId(targetText);
|
||||
if(targetId != null)
|
||||
{
|
||||
Member member = event.getGuild().getMemberById(targetId);
|
||||
if(!StatsAccess.canViewUserStats(event.getAuthor().getIdLong(), targetId, canManageServer(event.getMember())))
|
||||
{
|
||||
event.replyError("You can only view another member's detailed stats if you can manage this server.");
|
||||
CommandLogContext.setError("missing_manage_server");
|
||||
return;
|
||||
}
|
||||
sendSummary(event, member == null ? "Stats for user " + targetId : "Stats for " + member.getEffectiveName(), options, targetId, Section.ALL);
|
||||
return;
|
||||
}
|
||||
List<Member> members = FinderUtil.findMembers(targetText, event.getGuild());
|
||||
if(members.isEmpty())
|
||||
members = findMembersByPartialName(targetText, event.getGuild().getMembers());
|
||||
if(members.isEmpty())
|
||||
{
|
||||
List<StatsRow> profileMatches = bot.getDataLogService().findUsersByProfile(event.getGuild().getIdLong(), targetText, 6);
|
||||
if(profileMatches.size() == 1)
|
||||
{
|
||||
StatsRow match = profileMatches.get(0);
|
||||
if(!StatsAccess.canViewUserStats(event.getAuthor().getIdLong(), match.getId(), canManageServer(event.getMember())))
|
||||
{
|
||||
event.replyError("You can only view another member's detailed stats if you can manage this server.");
|
||||
CommandLogContext.setError("missing_manage_server");
|
||||
return;
|
||||
}
|
||||
sendSummary(event, "Stats for " + match.getLabel(), options, match.getId(), Section.ALL);
|
||||
return;
|
||||
}
|
||||
if(profileMatches.size() > 1)
|
||||
{
|
||||
event.replyWarning("Multiple analytics users matched `" + targetText + "`: " + formatProfileMatches(profileMatches) + ". Please be more specific or use a mention.");
|
||||
CommandLogContext.setError("member_ambiguous");
|
||||
return;
|
||||
}
|
||||
event.replyWarning("No member found matching `" + targetText + "`. Try a mention like `@user` or the Discord user ID.");
|
||||
CommandLogContext.setError("member_not_found");
|
||||
return;
|
||||
}
|
||||
if(members.size() > 1)
|
||||
{
|
||||
event.replyWarning("Multiple members matched `" + targetText + "`: " + formatMemberMatches(members) + ". Please be more specific or use a mention.");
|
||||
CommandLogContext.setError("member_ambiguous");
|
||||
return;
|
||||
}
|
||||
Member target = members.get(0);
|
||||
if(!StatsAccess.canViewUserStats(event.getAuthor().getIdLong(), target.getIdLong(), canManageServer(event.getMember())))
|
||||
{
|
||||
event.replyError("You can only view another member's detailed stats if you can manage this server.");
|
||||
CommandLogContext.setError("missing_manage_server");
|
||||
return;
|
||||
}
|
||||
sendSummary(event, "Stats for " + target.getEffectiveName(), options, target.getIdLong(), Section.ALL);
|
||||
}
|
||||
|
||||
private void sendUsage(CommandEvent event)
|
||||
{
|
||||
event.reply("Use `stats server [range] [source:youtube]`, `stats me [range]`, `stats user <@user|user id|name> [range]`, `stats tracks [range] [limit:20] [userlist:[name,id]]`, `stats listened [range] [limit:20] [userlist:[name,id]]`, `stats track [range]`, `stats skips`, `stats sources`, or `stats reports`.");
|
||||
}
|
||||
|
||||
private Long parseUserId(String value)
|
||||
{
|
||||
if(value == null)
|
||||
return null;
|
||||
String cleaned = value.trim();
|
||||
if(cleaned.startsWith("<@") && cleaned.endsWith(">"))
|
||||
{
|
||||
cleaned = cleaned.substring(2, cleaned.length() - 1);
|
||||
if(cleaned.startsWith("!"))
|
||||
cleaned = cleaned.substring(1);
|
||||
}
|
||||
if(!cleaned.matches("\\d{15,25}"))
|
||||
return null;
|
||||
try
|
||||
{
|
||||
return Long.parseLong(cleaned);
|
||||
}
|
||||
catch(NumberFormatException ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Member> findMembersByPartialName(String query, List<Member> members)
|
||||
{
|
||||
String normalizedQuery = normalizeName(query);
|
||||
List<Member> matches = new ArrayList<>();
|
||||
if(normalizedQuery.isEmpty())
|
||||
return matches;
|
||||
for(Member member : members)
|
||||
{
|
||||
if(member.getUser().isBot())
|
||||
continue;
|
||||
if(nameMatches(normalizedQuery, member.getEffectiveName())
|
||||
|| nameMatches(normalizedQuery, member.getUser().getName())
|
||||
|| nameMatches(normalizedQuery, member.getNickname()))
|
||||
{
|
||||
matches.add(member);
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
private boolean nameMatches(String normalizedQuery, String name)
|
||||
{
|
||||
if(name == null || name.isEmpty())
|
||||
return false;
|
||||
return normalizeName(name).contains(normalizedQuery);
|
||||
}
|
||||
|
||||
private String normalizeName(String value)
|
||||
{
|
||||
return value == null ? "" : value.toLowerCase().replaceAll("[^a-z0-9]", "");
|
||||
}
|
||||
|
||||
private String formatMemberMatches(List<Member> members)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int max = Math.min(5, members.size());
|
||||
for(int i = 0; i < max; i++)
|
||||
{
|
||||
if(i > 0)
|
||||
builder.append(", ");
|
||||
Member member = members.get(i);
|
||||
builder.append("`").append(member.getEffectiveName()).append("`");
|
||||
}
|
||||
if(members.size() > max)
|
||||
builder.append(", and ").append(members.size() - max).append(" more");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String formatProfileMatches(List<StatsRow> rows)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int max = Math.min(5, rows.size());
|
||||
for(int i = 0; i < max; i++)
|
||||
{
|
||||
if(i > 0)
|
||||
builder.append(", ");
|
||||
builder.append("`").append(rows.get(i).getLabel()).append("`");
|
||||
}
|
||||
if(rows.size() > max)
|
||||
builder.append(", and ").append(rows.size() - max).append(" more");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private void handleTrackStats(CommandEvent event, Options options)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
AudioTrack track = handler == null ? null : handler.getPlayer().getPlayingTrack();
|
||||
if(track == null)
|
||||
{
|
||||
event.replyWarning("No track is currently playing.");
|
||||
CommandLogContext.setError("no_current_track");
|
||||
return;
|
||||
}
|
||||
long count = bot.getDataLogService().getGuildTrackPlayCount(event.getGuild(), track, options.range);
|
||||
EmbedBuilder eb = new EmbedBuilder()
|
||||
.setColor(event.getSelfMember().getColor())
|
||||
.setTitle(track.getInfo().title, track.getInfo().uri)
|
||||
.setDescription("Range: **" + options.range.getLabel() + "**\nPlayed **" + count + "** time" + (count == 1L ? "" : "s") + " on this server.");
|
||||
if(track.getInfo().author != null && !track.getInfo().author.isEmpty())
|
||||
eb.setFooter("Source: " + track.getInfo().author, null);
|
||||
event.getChannel().sendMessageEmbeds(eb.build()).queue();
|
||||
CommandLogContext.setMeta(new JSONObject().put("subcommand", "track").put("play_count", count));
|
||||
}
|
||||
|
||||
private void handleTrackListStats(CommandEvent event, String title, Options options, boolean listened)
|
||||
{
|
||||
List<Long> userIds = resolveUserList(event, options.userQueries);
|
||||
if(userIds == null)
|
||||
return;
|
||||
if(!userIds.isEmpty() && !canViewUserList(event, userIds))
|
||||
{
|
||||
event.replyError("You can only filter by other members if you can manage this server.");
|
||||
CommandLogContext.setError("missing_manage_server");
|
||||
return;
|
||||
}
|
||||
List<StatsRow> rows = listened
|
||||
? bot.getDataLogService().getTopListenedTracks(event.getGuild().getIdLong(), options.range, options.source, userIds, options.limit)
|
||||
: bot.getDataLogService().getTopRequestedTracks(event.getGuild().getIdLong(), options.range, options.source, userIds, options.limit);
|
||||
if(rows.isEmpty())
|
||||
{
|
||||
event.replyWarning("No playback analytics found for that report yet.");
|
||||
CommandLogContext.setError("no_data");
|
||||
return;
|
||||
}
|
||||
String description = "Range: **" + options.range.getLabel() + "**"
|
||||
+ (options.source == null ? "" : "\nSource: **" + options.source + "**")
|
||||
+ (userIds.isEmpty() ? "\nUsers: **all**" : "\nUsers: **" + userIds.size() + " selected**");
|
||||
EmbedBuilder eb = new EmbedBuilder()
|
||||
.setTitle(title)
|
||||
.setColor(event.getSelfMember().getColorRaw())
|
||||
.setDescription(description);
|
||||
StatsMessageFormatter.addChunkedField(eb,
|
||||
listened ? "Tracks by listening time" : "Tracks by plays",
|
||||
listened
|
||||
? StatsMessageFormatter.trackListenList(rows, options.limit)
|
||||
: StatsMessageFormatter.countList(rows, "plays", options.limit),
|
||||
false);
|
||||
event.getChannel().sendMessageEmbeds(eb.build()).queue();
|
||||
CommandLogContext.setMeta(new JSONObject().put("range", options.range.getKey())
|
||||
.put("source", options.source == null ? JSONObject.NULL : options.source)
|
||||
.put("user_count", userIds.size())
|
||||
.put("limit", options.limit)
|
||||
.put("listened", listened));
|
||||
}
|
||||
|
||||
private void handleReports(CommandEvent event, List<String> tokens)
|
||||
{
|
||||
if(tokens.isEmpty())
|
||||
{
|
||||
event.reply("Use `stats reports set <channel> <monthly|yearly|off>` or `stats reports now [month|year|YYYY-MM|MM/YYYY|YYYY]`.");
|
||||
return;
|
||||
}
|
||||
String action = tokens.remove(0).toLowerCase();
|
||||
if("now".equals(action))
|
||||
{
|
||||
if(!canManageServer(event.getMember()))
|
||||
{
|
||||
event.replyError("You need Manage Server to send stats reports.");
|
||||
CommandLogContext.setError("missing_manage_server");
|
||||
return;
|
||||
}
|
||||
StatsTimeRange range = parseReportRange(tokens);
|
||||
if(range == null)
|
||||
{
|
||||
event.replyError("Use `stats reports now month`, `stats reports now year`, `stats reports now 2026-04`, `stats reports now 04/2026`, or `stats reports now 2025`.");
|
||||
CommandLogContext.setError("invalid_report_range");
|
||||
return;
|
||||
}
|
||||
boolean sent = bot.getStatsReportScheduler().sendReport(event.getGuild(), event.getTextChannel(), range);
|
||||
if(sent)
|
||||
{
|
||||
event.replySuccess("Stats report generated for `" + range.getLabel() + "`.");
|
||||
CommandLogContext.setMeta(new JSONObject().put("subcommand", "reports_now").put("range", range.getKey()));
|
||||
}
|
||||
else
|
||||
{
|
||||
event.replyWarning("No playback analytics found for that report yet.");
|
||||
CommandLogContext.setError("no_data");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(!"set".equals(action))
|
||||
{
|
||||
event.replyWarning("Unknown reports action. Use `set` or `now`.");
|
||||
CommandLogContext.setError("unknown_reports_action");
|
||||
return;
|
||||
}
|
||||
if(!canManageServer(event.getMember()))
|
||||
{
|
||||
event.replyError("You need Manage Server to configure stats reports.");
|
||||
CommandLogContext.setError("missing_manage_server");
|
||||
return;
|
||||
}
|
||||
if(tokens.isEmpty())
|
||||
{
|
||||
event.replyError("Please include a channel and frequency, or `off`.");
|
||||
CommandLogContext.setError("missing_report_settings");
|
||||
return;
|
||||
}
|
||||
Settings settings = event.getClient().getSettingsFor(event.getGuild());
|
||||
if(tokens.size() == 1 && "off".equalsIgnoreCase(tokens.get(0)))
|
||||
{
|
||||
settings.setStatsReportFrequency(StatsReportFrequency.OFF);
|
||||
settings.setStatsReportChannel(null);
|
||||
event.replySuccess("Stats reports are now disabled.");
|
||||
CommandLogContext.setMeta(new JSONObject().put("subcommand", "reports_set").put("frequency", "OFF"));
|
||||
return;
|
||||
}
|
||||
if(tokens.size() < 2)
|
||||
{
|
||||
event.replyError("Use `stats reports set <channel> <monthly|yearly|off>`.");
|
||||
CommandLogContext.setError("missing_report_frequency");
|
||||
return;
|
||||
}
|
||||
String frequencyToken = tokens.remove(tokens.size() - 1);
|
||||
StatsReportFrequency frequency = StatsReportFrequency.parse(frequencyToken);
|
||||
if(frequency == StatsReportFrequency.OFF)
|
||||
{
|
||||
settings.setStatsReportFrequency(StatsReportFrequency.OFF);
|
||||
settings.setStatsReportChannel(null);
|
||||
event.replySuccess("Stats reports are now disabled.");
|
||||
CommandLogContext.setMeta(new JSONObject().put("subcommand", "reports_set").put("frequency", "OFF"));
|
||||
return;
|
||||
}
|
||||
String channelText = String.join(" ", tokens);
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
List<TextChannel> channels = (List)FinderUtil.findTextChannels(channelText, event.getGuild());
|
||||
if(channels.isEmpty())
|
||||
{
|
||||
event.replyWarning("No text channel found matching `" + channelText + "`.");
|
||||
CommandLogContext.setError("text_channel_not_found");
|
||||
return;
|
||||
}
|
||||
if(channels.size() > 1)
|
||||
{
|
||||
event.replyWarning("Multiple text channels matched `" + channelText + "`. Please be more specific.");
|
||||
CommandLogContext.setError("text_channel_ambiguous");
|
||||
return;
|
||||
}
|
||||
settings.setStatsReportChannel(channels.get(0));
|
||||
settings.setStatsReportFrequency(frequency);
|
||||
event.replySuccess("Stats reports will be posted " + frequency.name().toLowerCase() + " in " + channels.get(0).getAsMention() + ".");
|
||||
CommandLogContext.setMeta(new JSONObject().put("subcommand", "reports_set")
|
||||
.put("channel_id", channels.get(0).getId()).put("frequency", frequency.name()));
|
||||
}
|
||||
|
||||
private StatsTimeRange parseReportRange(List<String> tokens)
|
||||
{
|
||||
if(tokens.isEmpty())
|
||||
return StatsTimeRange.previousMonth();
|
||||
String value = String.join(" ", tokens).trim().toLowerCase();
|
||||
if(value.isEmpty() || "month".equals(value) || "monthly".equals(value))
|
||||
return StatsTimeRange.previousMonth();
|
||||
if("year".equals(value) || "yearly".equals(value))
|
||||
return StatsTimeRange.previousYear();
|
||||
if(value.matches("\\d{4}"))
|
||||
return StatsTimeRange.year(Integer.parseInt(value));
|
||||
if(value.matches("\\d{4}-\\d{1,2}"))
|
||||
{
|
||||
String[] parts = value.split("-");
|
||||
return StatsTimeRange.month(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
|
||||
}
|
||||
if(value.matches("\\d{1,2}/\\d{4}"))
|
||||
{
|
||||
String[] parts = value.split("/");
|
||||
return StatsTimeRange.month(Integer.parseInt(parts[1]), Integer.parseInt(parts[0]));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void sendSummary(CommandEvent event, String title, Options options, Long userId, Section section)
|
||||
{
|
||||
StatsSummary summary = bot.getDataLogService().getStatsSummary(event.getGuild().getIdLong(), options.range, options.source, userId);
|
||||
if(summary.isEmpty())
|
||||
{
|
||||
event.replyWarning("No playback analytics found for that report yet.");
|
||||
CommandLogContext.setError("no_data");
|
||||
return;
|
||||
}
|
||||
EmbedBuilder eb = userId == null
|
||||
? StatsMessageFormatter.summaryEmbed(title, summary, event.getSelfMember().getColorRaw())
|
||||
: StatsMessageFormatter.personalEmbed(title, summary, event.getSelfMember().getColorRaw());
|
||||
if(section == Section.SKIPS)
|
||||
{
|
||||
eb.clearFields();
|
||||
StatsMessageFormatter.addChunkedField(eb, "Skip actions", StatsMessageFormatter.countList(summary.getSkipActions(), "skips"), false);
|
||||
StatsMessageFormatter.addChunkedField(eb, "Skip votes", StatsMessageFormatter.countList(summary.getSkipVotes(), "votes"), false);
|
||||
}
|
||||
else if(section == Section.SOURCES)
|
||||
{
|
||||
eb.clearFields();
|
||||
StatsMessageFormatter.addChunkedField(eb, "Top sources", StatsMessageFormatter.countList(summary.getTopSources(), "plays"), false);
|
||||
}
|
||||
event.getChannel().sendMessageEmbeds(eb.build()).queue();
|
||||
CommandLogContext.setMeta(new JSONObject().put("range", options.range.getKey())
|
||||
.put("source", options.source == null ? JSONObject.NULL : options.source)
|
||||
.put("target_user_id", userId == null ? JSONObject.NULL : userId));
|
||||
}
|
||||
|
||||
private Options parseOptions(List<String> tokens)
|
||||
{
|
||||
StatsTimeRange range = StatsTimeRange.all();
|
||||
String source = null;
|
||||
int limit = 5;
|
||||
List<String> remaining = new ArrayList<>();
|
||||
List<String> userQueries = new ArrayList<>();
|
||||
for(String token : tokens)
|
||||
{
|
||||
if(token == null || token.isEmpty())
|
||||
continue;
|
||||
if(token.toLowerCase().startsWith("source:"))
|
||||
{
|
||||
source = StatsFilters.normalizeSource(token.substring("source:".length()));
|
||||
continue;
|
||||
}
|
||||
String lower = token.toLowerCase();
|
||||
if(lower.startsWith("limit:") || lower.startsWith("top:") || lower.startsWith("n:"))
|
||||
{
|
||||
Integer parsedLimit = parseLimit(token.substring(token.indexOf(':') + 1));
|
||||
if(parsedLimit != null)
|
||||
limit = parsedLimit;
|
||||
continue;
|
||||
}
|
||||
if(lower.startsWith("users:") || lower.startsWith("userlist:"))
|
||||
{
|
||||
String value = token.substring(token.indexOf(':') + 1).trim();
|
||||
if(value.startsWith("[") && value.endsWith("]"))
|
||||
value = value.substring(1, value.length() - 1);
|
||||
for(String part : value.split(","))
|
||||
if(!part.trim().isEmpty())
|
||||
userQueries.add(part.trim());
|
||||
continue;
|
||||
}
|
||||
StatsTimeRange parsedRange = StatsTimeRange.parse(token);
|
||||
if(parsedRange != null)
|
||||
{
|
||||
range = parsedRange;
|
||||
continue;
|
||||
}
|
||||
Integer parsedLimit = parseLimit(token);
|
||||
if(parsedLimit != null)
|
||||
{
|
||||
limit = parsedLimit;
|
||||
continue;
|
||||
}
|
||||
remaining.add(token);
|
||||
}
|
||||
return new Options(range, source, remaining, userQueries, limit);
|
||||
}
|
||||
|
||||
private Integer parseLimit(String token)
|
||||
{
|
||||
if(token == null || !token.matches("\\d+"))
|
||||
return null;
|
||||
int value = Integer.parseInt(token);
|
||||
if(value < 1)
|
||||
return 1;
|
||||
return Math.min(value, 25);
|
||||
}
|
||||
|
||||
private List<Long> resolveUserList(CommandEvent event, List<String> queries)
|
||||
{
|
||||
List<Long> userIds = new ArrayList<>();
|
||||
for(String query : queries)
|
||||
{
|
||||
Long userId = parseUserId(query);
|
||||
if(userId != null)
|
||||
{
|
||||
userIds.add(userId);
|
||||
continue;
|
||||
}
|
||||
List<Member> members = FinderUtil.findMembers(query, event.getGuild());
|
||||
if(members.isEmpty())
|
||||
members = findMembersByPartialName(query, event.getGuild().getMembers());
|
||||
if(members.isEmpty())
|
||||
{
|
||||
List<StatsRow> profileMatches = bot.getDataLogService().findUsersByProfile(event.getGuild().getIdLong(), query, 6);
|
||||
if(profileMatches.size() == 1)
|
||||
{
|
||||
userIds.add(profileMatches.get(0).getId());
|
||||
continue;
|
||||
}
|
||||
if(profileMatches.size() > 1)
|
||||
{
|
||||
event.replyWarning("Multiple analytics users matched `" + query + "`: " + formatProfileMatches(profileMatches) + ". Please be more specific or use a mention.");
|
||||
CommandLogContext.setError("member_ambiguous");
|
||||
return null;
|
||||
}
|
||||
event.replyWarning("No member found matching `" + query + "`. Try a mention or Discord user ID.");
|
||||
CommandLogContext.setError("member_not_found");
|
||||
return null;
|
||||
}
|
||||
if(members.size() > 1)
|
||||
{
|
||||
event.replyWarning("Multiple members matched `" + query + "`: " + formatMemberMatches(members) + ". Please be more specific or use a mention.");
|
||||
CommandLogContext.setError("member_ambiguous");
|
||||
return null;
|
||||
}
|
||||
userIds.add(members.get(0).getIdLong());
|
||||
}
|
||||
return userIds;
|
||||
}
|
||||
|
||||
private boolean canViewUserList(CommandEvent event, List<Long> userIds)
|
||||
{
|
||||
for(Long userId : userIds)
|
||||
if(userId != event.getAuthor().getIdLong())
|
||||
return canManageServer(event.getMember());
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean canManageServer(Member member)
|
||||
{
|
||||
return member != null && member.hasPermission(Permission.MANAGE_SERVER);
|
||||
}
|
||||
|
||||
private enum Section
|
||||
{
|
||||
ALL,
|
||||
SKIPS,
|
||||
SOURCES
|
||||
}
|
||||
|
||||
private static class Options
|
||||
{
|
||||
private final StatsTimeRange range;
|
||||
private final String source;
|
||||
private final List<String> remaining;
|
||||
private final List<String> userQueries;
|
||||
private final int limit;
|
||||
|
||||
private Options(StatsTimeRange range, String source, List<String> remaining, List<String> userQueries, int limit)
|
||||
{
|
||||
this.range = range == null ? StatsTimeRange.all() : range;
|
||||
this.source = source;
|
||||
this.remaining = remaining;
|
||||
this.userQueries = userQueries;
|
||||
this.limit = limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.music;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jlyrics.LyricsClient;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.commands.MusicCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class LyricsCmd extends MusicCommand
|
||||
{
|
||||
private final LyricsClient client = new LyricsClient();
|
||||
|
||||
public LyricsCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "lyrics";
|
||||
this.arguments = "[song name]";
|
||||
this.help = "shows the lyrics of a song";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.setManualLogging();
|
||||
String title;
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
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!");
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "no_music_playing"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
title = event.getArgs();
|
||||
event.getChannel().sendTyping().queue();
|
||||
client.getLyrics(title).thenAccept(lyrics ->
|
||||
{
|
||||
if(lyrics == null)
|
||||
{
|
||||
event.replyError("Lyrics for `" + title + "` could not be found!" + (event.getArgs().isEmpty() ? " Try entering the song name manually (`lyrics [song name]`)" : ""));
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "lyrics_not_found"));
|
||||
return;
|
||||
}
|
||||
|
||||
EmbedBuilder eb = new EmbedBuilder()
|
||||
.setAuthor(lyrics.getAuthor())
|
||||
.setColor(event.getSelfMember().getColor())
|
||||
.setTitle(lyrics.getTitle(), lyrics.getURL());
|
||||
if(lyrics.getContent().length()>15000)
|
||||
{
|
||||
event.replyWarning("Lyrics for `" + title + "` found but likely not correct: " + lyrics.getURL());
|
||||
}
|
||||
else if(lyrics.getContent().length()>2000)
|
||||
{
|
||||
String content = lyrics.getContent().trim();
|
||||
while(content.length() > 2000)
|
||||
{
|
||||
int index = content.lastIndexOf("\n\n", 2000);
|
||||
if(index == -1)
|
||||
index = content.lastIndexOf("\n", 2000);
|
||||
if(index == -1)
|
||||
index = content.lastIndexOf(" ", 2000);
|
||||
if(index == -1)
|
||||
index = 2000;
|
||||
event.reply(eb.setDescription(content.substring(0, index).trim()).build());
|
||||
content = content.substring(index).trim();
|
||||
eb.setAuthor(null).setTitle(null, null);
|
||||
}
|
||||
event.reply(eb.setDescription(content).build());
|
||||
}
|
||||
else
|
||||
event.reply(eb.setDescription(lyrics.getContent()).build());
|
||||
logCommandEvent(event, "OK", new JSONObject().put("title", lyrics.getTitle()));
|
||||
});
|
||||
}
|
||||
|
||||
private void logCommandEvent(CommandEvent event, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), result, meta == null ? null : meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.music;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.commands.MusicCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class NowplayingCmd extends MusicCommand
|
||||
{
|
||||
public NowplayingCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "nowplaying";
|
||||
this.help = "shows the song that is currently playing";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
MessageCreateData m = handler.getNowPlaying(event.getJDA());
|
||||
if(m==null)
|
||||
{
|
||||
event.getMessage().getChannel().sendMessage(handler.getNoMusicPlaying(event.getJDA())).queue();
|
||||
bot.getNowplayingHandler().clearLastNPMessage(event.getGuild());
|
||||
CommandLogContext.setMeta(new JSONObject().put("playing", false));
|
||||
}
|
||||
else
|
||||
{
|
||||
event.getMessage().getChannel().sendMessage(m).queue(msg -> bot.getNowplayingHandler().setLastNPMessage(msg));
|
||||
CommandLogContext.setMeta(new JSONObject()
|
||||
.put("playing", true)
|
||||
.put("title", handler.getPlayer().getPlayingTrack() == null ? JSONObject.NULL : handler.getPlayer().getPlayingTrack().getInfo().title));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright 2016 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.music;
|
||||
|
||||
import com.jagrosh.jmusicbot.audio.RequestMetadata;
|
||||
import com.jagrosh.jmusicbot.utils.TimeUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.jagrosh.jdautilities.command.Command;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jdautilities.menu.ButtonMenu;
|
||||
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.commands.MusicCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.exceptions.PermissionException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class PlayCmd extends MusicCommand
|
||||
{
|
||||
private final static String LOAD = "\uD83D\uDCE5"; // 📥
|
||||
private final static String CANCEL = "\uD83D\uDEAB"; // 🚫
|
||||
|
||||
private final String loadingEmoji;
|
||||
|
||||
public PlayCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.loadingEmoji = bot.getConfig().getLoading();
|
||||
this.name = "play";
|
||||
this.arguments = "<title|URL|subcommand>";
|
||||
this.help = "plays the provided song";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.beListening = true;
|
||||
this.bePlaying = false;
|
||||
this.children = new Command[]{new PlaylistCmd(bot)};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.setManualLogging();
|
||||
if(event.getArgs().isEmpty() && event.getMessage().getAttachments().isEmpty())
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
if(handler.getPlayer().getPlayingTrack()!=null && handler.getPlayer().isPaused())
|
||||
{
|
||||
if(DJCommand.checkDJPermission(event))
|
||||
{
|
||||
handler.getPlayer().setPaused(false);
|
||||
event.replySuccess("Resumed **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**.");
|
||||
if(bot.getDataLogService() != null)
|
||||
{
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("position_ms", handler.getPlayer().getPlayingTrack().getPosition());
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), handler.getPlayer().getPlayingTrack(),
|
||||
"RESUME", null, null, null, null, meta.toString());
|
||||
logCommandEvent(event, getName(), event.getArgs(), "OK", meta);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
event.replyError("Only DJs can unpause the player!");
|
||||
logCommandEvent(event, getName(), event.getArgs(), "ERROR", new JSONObject().put("error_reason", "not_dj"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder(event.getClient().getWarning()+" Play Commands:\n");
|
||||
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" <song title>` - plays the first result from Youtube");
|
||||
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" <URL>` - plays the provided song, playlist, or stream");
|
||||
for(Command cmd: children)
|
||||
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ").append(cmd.getName()).append(" ").append(cmd.getArguments()).append("` - ").append(cmd.getHelp());
|
||||
event.reply(builder.toString());
|
||||
logCommandEvent(event, getName(), event.getArgs(), "OK", new JSONObject().put("help", true));
|
||||
return;
|
||||
}
|
||||
String args = event.getArgs().startsWith("<") && event.getArgs().endsWith(">")
|
||||
? event.getArgs().substring(1,event.getArgs().length()-1)
|
||||
: event.getArgs().isEmpty() ? event.getMessage().getAttachments().get(0).getUrl() : event.getArgs();
|
||||
event.reply(loadingEmoji+" Loading... `["+args+"]`", m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), args, new ResultHandler(m,event,false)));
|
||||
}
|
||||
|
||||
private class ResultHandler implements AudioLoadResultHandler
|
||||
{
|
||||
private final Message m;
|
||||
private final CommandEvent event;
|
||||
private final boolean ytsearch;
|
||||
|
||||
private ResultHandler(Message m, CommandEvent event, boolean ytsearch)
|
||||
{
|
||||
this.m = m;
|
||||
this.event = event;
|
||||
this.ytsearch = ytsearch;
|
||||
}
|
||||
|
||||
private void loadSingle(AudioTrack track, AudioPlaylist playlist)
|
||||
{
|
||||
if(bot.getConfig().isTooLong(track))
|
||||
{
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+ TimeUtil.formatTime(track.getDuration())+"` > `"+ TimeUtil.formatTime(bot.getConfig().getMaxSeconds()*1000)+"`")).queue();
|
||||
logCommandEvent(event, getName(), event.getArgs(), "ERROR", new JSONObject().put("error_reason", "track_too_long"));
|
||||
return;
|
||||
}
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
RequestMetadata rm = RequestMetadata.fromResultHandler(track, event, "PLAY", null, event.getArgs());
|
||||
int pos = handler.addTrack(new QueuedTrack(track, rm))+1;
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueAdd(event.getGuild(), event.getAuthor(), track, "PLAY", pos, event.getArgs(), null);
|
||||
String addMsg = FormatUtil.filter(event.getClient().getSuccess()+" Added **"+track.getInfo().title
|
||||
+"** (`"+ TimeUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos));
|
||||
if(playlist==null || !event.getSelfMember().hasPermission(event.getTextChannel(), Permission.MESSAGE_ADD_REACTION))
|
||||
m.editMessage(addMsg).queue();
|
||||
else
|
||||
{
|
||||
new ButtonMenu.Builder()
|
||||
.setText(addMsg+"\n"+event.getClient().getWarning()+" This track has a playlist of **"+playlist.getTracks().size()+"** tracks attached. Select "+LOAD+" to load playlist.")
|
||||
.setChoices(LOAD, CANCEL)
|
||||
.setEventWaiter(bot.getWaiter())
|
||||
.setTimeout(30, TimeUnit.SECONDS)
|
||||
.setAction(re ->
|
||||
{
|
||||
if(re.getName().equals(LOAD))
|
||||
m.editMessage(addMsg+"\n"+event.getClient().getSuccess()+" Loaded **"+loadPlaylist(playlist, track)+"** additional tracks!").queue();
|
||||
else
|
||||
m.editMessage(addMsg).queue();
|
||||
}).setFinalAction(m ->
|
||||
{
|
||||
try{ m.clearReactions().queue(); }catch(PermissionException ignore) {}
|
||||
}).build().display(m);
|
||||
}
|
||||
logCommandEvent(event, getName(), event.getArgs(), "OK", new JSONObject().put("queue_position", pos));
|
||||
}
|
||||
|
||||
private int loadPlaylist(AudioPlaylist playlist, AudioTrack exclude)
|
||||
{
|
||||
int[] count = {0};
|
||||
playlist.getTracks().stream().forEach((track) -> {
|
||||
if(!bot.getConfig().isTooLong(track) && !track.equals(exclude))
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
RequestMetadata rm = RequestMetadata.fromResultHandler(track, event, "PLAYLIST", playlist == null ? null : playlist.getName(), event.getArgs());
|
||||
int pos = handler.addTrack(new QueuedTrack(track, rm))+1;
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueAdd(event.getGuild(), event.getAuthor(), track, "PLAYLIST", pos, event.getArgs(), playlist == null ? null : playlist.getName());
|
||||
count[0]++;
|
||||
}
|
||||
});
|
||||
return count[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack track)
|
||||
{
|
||||
loadSingle(track, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist playlist)
|
||||
{
|
||||
if(playlist.getTracks().size()==1 || playlist.isSearchResult())
|
||||
{
|
||||
AudioTrack single = playlist.getSelectedTrack()==null ? playlist.getTracks().get(0) : playlist.getSelectedTrack();
|
||||
loadSingle(single, null);
|
||||
}
|
||||
else if (playlist.getSelectedTrack()!=null)
|
||||
{
|
||||
AudioTrack single = playlist.getSelectedTrack();
|
||||
loadSingle(single, playlist);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = loadPlaylist(playlist, null);
|
||||
if(playlist.getTracks().size() == 0)
|
||||
{
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" The playlist "+(playlist.getName()==null ? "" : "(**"+playlist.getName()
|
||||
+"**) ")+" could not be loaded or contained 0 entries")).queue();
|
||||
logCommandEvent(event, getName(), event.getArgs(), "ERROR", new JSONObject().put("error_reason", "playlist_empty"));
|
||||
}
|
||||
else if(count==0)
|
||||
{
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" All entries in this playlist "+(playlist.getName()==null ? "" : "(**"+playlist.getName()
|
||||
+"**) ")+"were longer than the allowed maximum (`"+bot.getConfig().getMaxTime()+"`)")).queue();
|
||||
logCommandEvent(event, getName(), event.getArgs(), "ERROR", new JSONObject().put("error_reason", "playlist_entries_too_long"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getSuccess()+" Found "
|
||||
+(playlist.getName()==null?"a playlist":"playlist **"+playlist.getName()+"**")+" with `"
|
||||
+ playlist.getTracks().size()+"` entries; added to the queue!"
|
||||
+ (count<playlist.getTracks().size() ? "\n"+event.getClient().getWarning()+" Tracks longer than the allowed maximum (`"
|
||||
+ bot.getConfig().getMaxTime()+"`) have been omitted." : ""))).queue();
|
||||
logCommandEvent(event, getName(), event.getArgs(), "OK", new JSONObject().put("playlist_name", playlist.getName()).put("added_count", count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches()
|
||||
{
|
||||
if(ytsearch)
|
||||
{
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.")).queue();
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logSearchEventWithMeta(event.getGuild(), event.getAuthor(), event.getArgs(), null, null,
|
||||
new JSONObject().put("error_reason", "no_matches").toString());
|
||||
logCommandEvent(event, getName(), event.getArgs(), "ERROR", new JSONObject().put("error_reason", "no_matches"));
|
||||
}
|
||||
else
|
||||
bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event,true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException throwable)
|
||||
{
|
||||
if(throwable.severity==Severity.COMMON)
|
||||
m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue();
|
||||
else
|
||||
m.editMessage(event.getClient().getError()+" Error loading track.").queue();
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logSearchEventWithMeta(event.getGuild(), event.getAuthor(), event.getArgs(), null, null,
|
||||
new JSONObject().put("error_reason", throwable.getMessage()).toString());
|
||||
logCommandEvent(event, getName(), event.getArgs(), "ERROR", new JSONObject().put("error_reason", throwable.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
public class PlaylistCmd extends MusicCommand
|
||||
{
|
||||
public PlaylistCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "playlist";
|
||||
this.aliases = new String[]{"pl"};
|
||||
this.arguments = "<name>";
|
||||
this.help = "plays the provided playlist";
|
||||
this.beListening = true;
|
||||
this.bePlaying = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.setManualLogging();
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a playlist name.");
|
||||
logCommandEvent(event, getName(), event.getArgs(), "ERROR", new JSONObject().put("error_reason", "missing_playlist"));
|
||||
return;
|
||||
}
|
||||
Playlist playlist = bot.getPlaylistLoader().getPlaylist(event.getArgs());
|
||||
if(playlist==null)
|
||||
{
|
||||
event.replyError("I could not find `"+event.getArgs()+".txt` in the Playlists folder.");
|
||||
logCommandEvent(event, getName(), event.getArgs(), "ERROR", new JSONObject().put("error_reason", "playlist_not_found"));
|
||||
return;
|
||||
}
|
||||
event.getChannel().sendMessage(loadingEmoji+" Loading playlist **"+event.getArgs()+"**... ("+playlist.getItems().size()+" items)").queue(m ->
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
playlist.loadTracks(bot.getPlayerManager(), (at) ->
|
||||
{
|
||||
RequestMetadata rm = RequestMetadata.fromResultHandler(at, event, "PLAYLIST", event.getArgs(), event.getArgs());
|
||||
int pos = handler.addTrack(new QueuedTrack(at, rm))+1;
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueAdd(event.getGuild(), event.getAuthor(), at, "PLAYLIST", pos, event.getArgs(), event.getArgs());
|
||||
}, () -> {
|
||||
StringBuilder builder = new StringBuilder(playlist.getTracks().isEmpty()
|
||||
? event.getClient().getWarning()+" No tracks were loaded!"
|
||||
: event.getClient().getSuccess()+" Loaded **"+playlist.getTracks().size()+"** tracks!");
|
||||
if(!playlist.getErrors().isEmpty())
|
||||
builder.append("\nThe following tracks failed to load:");
|
||||
playlist.getErrors().forEach(err -> builder.append("\n`[").append(err.getIndex()+1).append("]` **").append(err.getItem()).append("**: ").append(err.getReason()));
|
||||
String str = builder.toString();
|
||||
if(str.length()>2000)
|
||||
str = str.substring(0,1994)+" (...)";
|
||||
m.editMessage(FormatUtil.filter(str)).queue();
|
||||
if(bot.getDataLogService() != null)
|
||||
{
|
||||
playlist.getErrors().forEach(err ->
|
||||
bot.getDataLogService().logPlaylistError(event.getGuild(), event.getAuthor(), event.getArgs(), err.getItem(), err.getReason()));
|
||||
}
|
||||
logCommandEvent(event, getName(), event.getArgs(), "OK", new JSONObject()
|
||||
.put("playlist_name", event.getArgs())
|
||||
.put("loaded_count", playlist.getTracks().size())
|
||||
.put("error_count", playlist.getErrors().size()));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void logCommandEvent(CommandEvent event, String commandName, String args, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), commandName,
|
||||
args, result, meta == null ? null : meta.toString());
|
||||
}
|
||||
}
|
||||
+23
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
|
||||
* 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.
|
||||
@@ -13,51 +13,61 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.commands;
|
||||
package com.jagrosh.jmusicbot.commands.music;
|
||||
|
||||
import java.util.List;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.playlist.Playlist;
|
||||
import com.jagrosh.jmusicbot.commands.MusicCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class PlaylistsCmd extends MusicCommand {
|
||||
|
||||
public class PlaylistsCmd extends MusicCommand
|
||||
{
|
||||
public PlaylistsCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "playlists";
|
||||
this.help = "shows the available playlists";
|
||||
this.aliases = new String[]{"pls"};
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.guildOnly = true;
|
||||
this.beListening = false;
|
||||
this.beListening = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
if(!Playlist.folderExists())
|
||||
Playlist.createFolder();
|
||||
if(!Playlist.folderExists())
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(!bot.getPlaylistLoader().folderExists())
|
||||
bot.getPlaylistLoader().createFolder();
|
||||
if(!bot.getPlaylistLoader().folderExists())
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+" Playlists folder does not exist and could not be created!");
|
||||
CommandLogContext.setError("playlist_folder_missing");
|
||||
return;
|
||||
}
|
||||
List<String> list = Playlist.getPlaylists();
|
||||
List<String> list = bot.getPlaylistLoader().getPlaylistNames();
|
||||
if(list==null)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Failed to load available playlists!");
|
||||
CommandLogContext.setError("playlist_list_failed");
|
||||
}
|
||||
else if(list.isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+" There are no playlists in the Playlists folder!");
|
||||
CommandLogContext.setMeta(new JSONObject().put("count", 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(event.getClient().getSuccess()+" Available playlists:\n");
|
||||
list.forEach(str -> builder.append("`").append(str).append("` "));
|
||||
builder.append("\nType `").append(event.getClient().getTextualPrefix()).append("play playlist <name>` to play a playlist");
|
||||
event.reply(builder.toString());
|
||||
CommandLogContext.setMeta(new JSONObject().put("count", list.size()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.music;
|
||||
|
||||
import java.util.List;
|
||||
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.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.audio.QueuedTrack;
|
||||
import com.jagrosh.jmusicbot.commands.MusicCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.QueueType;
|
||||
import com.jagrosh.jmusicbot.settings.RepeatMode;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import com.jagrosh.jmusicbot.utils.TimeUtil;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.exceptions.PermissionException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class QueueCmd extends MusicCommand
|
||||
{
|
||||
private final Paginator.Builder builder;
|
||||
|
||||
public QueueCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "queue";
|
||||
this.help = "shows the current queue";
|
||||
this.arguments = "[pagenum]";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.bePlaying = true;
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_ADD_REACTION,Permission.MESSAGE_EMBED_LINKS};
|
||||
builder = new Paginator.Builder()
|
||||
.setColumns(1)
|
||||
.setFinalAction(m -> {try{m.clearReactions().queue();}catch(PermissionException ignore){}})
|
||||
.setItemsPerPage(10)
|
||||
.waitOnSinglePage(false)
|
||||
.useNumberedItems(true)
|
||||
.showPageNumbers(true)
|
||||
.wrapPageEnds(true)
|
||||
.setEventWaiter(bot.getWaiter())
|
||||
.setTimeout(1, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
int pagenum = 1;
|
||||
try
|
||||
{
|
||||
pagenum = Integer.parseInt(event.getArgs());
|
||||
}
|
||||
catch(NumberFormatException ignore){}
|
||||
AudioHandler ah = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
List<QueuedTrack> list = ah.getQueue().getList();
|
||||
if(list.isEmpty())
|
||||
{
|
||||
MessageCreateData nowp = ah.getNowPlaying(event.getJDA());
|
||||
MessageCreateData nonowp = ah.getNoMusicPlaying(event.getJDA());
|
||||
MessageCreateData built = new MessageCreateBuilder()
|
||||
.setContent(event.getClient().getWarning() + " There is no music in the queue!")
|
||||
.setEmbeds((nowp==null ? nonowp : nowp).getEmbeds().get(0)).build();
|
||||
event.getMessage().getChannel().sendMessage(built).queue(m ->
|
||||
{
|
||||
if(nowp!=null)
|
||||
bot.getNowplayingHandler().setLastNPMessage(m);
|
||||
});
|
||||
CommandLogContext.setMeta(new JSONObject().put("queue_size", 0));
|
||||
return;
|
||||
}
|
||||
String[] songs = new String[list.size()];
|
||||
long total = 0;
|
||||
for(int i=0; i<list.size(); i++)
|
||||
{
|
||||
total += list.get(i).getTrack().getDuration();
|
||||
songs[i] = list.get(i).toString();
|
||||
}
|
||||
Settings settings = event.getClient().getSettingsFor(event.getGuild());
|
||||
long fintotal = total;
|
||||
CommandLogContext.setMeta(new JSONObject()
|
||||
.put("queue_size", list.size())
|
||||
.put("total_ms", total)
|
||||
.put("repeat_mode", settings.getRepeatMode().name())
|
||||
.put("queue_type", settings.getQueueType().name()));
|
||||
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())
|
||||
;
|
||||
builder.build().paginate(event.getChannel(), pagenum);
|
||||
}
|
||||
|
||||
private String getQueueTitle(AudioHandler ah, String success, int songslength, long total, RepeatMode repeatmode, QueueType queueType)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(ah.getPlayer().getPlayingTrack()!=null)
|
||||
{
|
||||
sb.append(ah.getStatusEmoji()).append(" **")
|
||||
.append(ah.getPlayer().getPlayingTrack().getInfo().title).append("**\n");
|
||||
}
|
||||
return FormatUtil.filter(sb.append(success).append(" Current Queue | ").append(songslength)
|
||||
.append(" entries | `").append(TimeUtil.formatTime(total)).append("` ")
|
||||
.append("| ").append(queueType.getEmoji()).append(" `").append(queueType.getUserFriendlyName()).append('`')
|
||||
.append(repeatmode.getEmoji() != null ? " | "+repeatmode.getEmoji() : "").toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2016 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.music;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.audio.QueuedTrack;
|
||||
import com.jagrosh.jmusicbot.commands.MusicCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class RemoveCmd extends MusicCommand
|
||||
{
|
||||
public RemoveCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "remove";
|
||||
this.help = "removes a song from the queue";
|
||||
this.arguments = "<position|ALL>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.beListening = true;
|
||||
this.bePlaying = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
if(handler.getQueue().isEmpty())
|
||||
{
|
||||
event.replyError("There is nothing in the queue!");
|
||||
CommandLogContext.setError("empty_queue");
|
||||
return;
|
||||
}
|
||||
if(event.getArgs().equalsIgnoreCase("all"))
|
||||
{
|
||||
int sizeBefore = handler.getQueue().size();
|
||||
int count = handler.getQueue().removeAll(event.getAuthor().getIdLong());
|
||||
if(count==0)
|
||||
{
|
||||
event.replyWarning("You don't have any songs in the queue!");
|
||||
CommandLogContext.setError("no_entries_for_user");
|
||||
}
|
||||
else
|
||||
event.replySuccess("Successfully removed your "+count+" entries.");
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("removed_count", count)
|
||||
.put("queue_size_before", sizeBefore)
|
||||
.put("queue_size_after", handler.getQueue().size());
|
||||
CommandLogContext.setMeta(meta);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), null,
|
||||
"REMOVE_ALL", "SELF", null, null, null, meta.toString());
|
||||
return;
|
||||
}
|
||||
int pos;
|
||||
try {
|
||||
pos = Integer.parseInt(event.getArgs());
|
||||
} catch(NumberFormatException e) {
|
||||
pos = 0;
|
||||
}
|
||||
if(pos<1 || pos>handler.getQueue().size())
|
||||
{
|
||||
event.replyError("Position must be a valid integer between 1 and "+handler.getQueue().size()+"!");
|
||||
CommandLogContext.setError("invalid_position");
|
||||
return;
|
||||
}
|
||||
Settings settings = event.getClient().getSettingsFor(event.getGuild());
|
||||
boolean isDJ = event.getMember().hasPermission(Permission.MANAGE_SERVER);
|
||||
if(!isDJ)
|
||||
isDJ = event.getMember().getRoles().contains(settings.getRole(event.getGuild()));
|
||||
QueuedTrack qt = handler.getQueue().get(pos-1);
|
||||
if(qt.getIdentifier()==event.getAuthor().getIdLong())
|
||||
{
|
||||
handler.getQueue().remove(pos-1);
|
||||
event.replySuccess("Removed **"+qt.getTrack().getInfo().title+"** from the queue");
|
||||
JSONObject meta = new JSONObject().put("position", pos);
|
||||
CommandLogContext.setMeta(meta);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), qt.getTrack(),
|
||||
"REMOVE", "SELF", pos, null, null, meta.toString());
|
||||
}
|
||||
else if(isDJ)
|
||||
{
|
||||
handler.getQueue().remove(pos-1);
|
||||
User u;
|
||||
try {
|
||||
u = event.getJDA().getUserById(qt.getIdentifier());
|
||||
} catch(Exception e) {
|
||||
u = null;
|
||||
}
|
||||
event.replySuccess("Removed **"+qt.getTrack().getInfo().title
|
||||
+"** from the queue (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
|
||||
JSONObject meta = new JSONObject().put("position", pos).put("removed_by_dj", true);
|
||||
CommandLogContext.setMeta(meta);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), qt.getTrack(),
|
||||
"REMOVE", "DJ", pos, null, null, meta.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
event.replyError("You cannot remove **"+qt.getTrack().getInfo().title+"** because you didn't add it!");
|
||||
CommandLogContext.setError("not_owner");
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
-42
@@ -1,42 +1,34 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class NowplayingCmd extends MusicCommand {
|
||||
|
||||
public NowplayingCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "nowplaying";
|
||||
this.help = "shows the song that is currently playing";
|
||||
this.aliases = new String[]{"np","current"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
event.reply(FormatUtil.formattedAudio((AudioHandler)event.getGuild().getAudioManager().getSendingHandler(), event.getJDA(), false));
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2016 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.music;
|
||||
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SCSearchCmd extends SearchCmd
|
||||
{
|
||||
public SCSearchCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.searchPrefix = "scsearch:";
|
||||
this.name = "scsearch";
|
||||
this.help = "searches Soundcloud for a provided query";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2016 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.music;
|
||||
|
||||
import com.jagrosh.jmusicbot.audio.RequestMetadata;
|
||||
import com.jagrosh.jmusicbot.utils.TimeUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jdautilities.menu.OrderedMenu;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.audio.QueuedTrack;
|
||||
import com.jagrosh.jmusicbot.commands.MusicCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SearchCmd extends MusicCommand
|
||||
{
|
||||
protected String searchPrefix = "ytsearch:";
|
||||
private final OrderedMenu.Builder builder;
|
||||
private final String searchingEmoji;
|
||||
|
||||
public SearchCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.searchingEmoji = bot.getConfig().getSearching();
|
||||
this.name = "search";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.arguments = "<query>";
|
||||
this.help = "searches Youtube for a provided query";
|
||||
this.beListening = true;
|
||||
this.bePlaying = false;
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
|
||||
builder = new OrderedMenu.Builder()
|
||||
.allowTextInput(true)
|
||||
.useNumbers()
|
||||
.useCancelButton(true)
|
||||
.setEventWaiter(bot.getWaiter())
|
||||
.setTimeout(1, TimeUnit.MINUTES);
|
||||
}
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.setManualLogging();
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.replyError("Please include a query.");
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "missing_query"));
|
||||
return;
|
||||
}
|
||||
event.reply(searchingEmoji+" Searching... `["+event.getArgs()+"]`",
|
||||
m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), searchPrefix + event.getArgs(), new ResultHandler(m,event)));
|
||||
}
|
||||
|
||||
private class ResultHandler implements AudioLoadResultHandler
|
||||
{
|
||||
private final Message m;
|
||||
private final CommandEvent event;
|
||||
|
||||
private ResultHandler(Message m, CommandEvent event)
|
||||
{
|
||||
this.m = m;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack track)
|
||||
{
|
||||
if(bot.getConfig().isTooLong(track))
|
||||
{
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+ TimeUtil.formatTime(track.getDuration())+"` > `"+bot.getConfig().getMaxTime()+"`")).queue();
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "track_too_long"));
|
||||
return;
|
||||
}
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
RequestMetadata rm = RequestMetadata.fromResultHandler(track, event, "SEARCH", null, event.getArgs());
|
||||
int pos = handler.addTrack(new QueuedTrack(track, rm))+1;
|
||||
if(bot.getDataLogService() != null)
|
||||
{
|
||||
bot.getDataLogService().logSearchSelection(event.getGuild(), event.getAuthor(), event.getArgs(), 1, track);
|
||||
bot.getDataLogService().logQueueAdd(event.getGuild(), event.getAuthor(), track, "SEARCH", pos, event.getArgs(), null);
|
||||
}
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getSuccess()+" Added **"+track.getInfo().title
|
||||
+"** (`"+ TimeUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
|
||||
: " to the queue at position "+pos))).queue();
|
||||
logCommandEvent(event, "OK", new JSONObject().put("queue_position", pos).put("selected_index", 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist playlist)
|
||||
{
|
||||
builder.setColor(event.getSelfMember().getColor())
|
||||
.setText(FormatUtil.filter(event.getClient().getSuccess()+" Search results for `"+event.getArgs()+"`:"))
|
||||
.setChoices(new String[0])
|
||||
.setSelection((msg,i) ->
|
||||
{
|
||||
AudioTrack track = playlist.getTracks().get(i-1);
|
||||
if(bot.getConfig().isTooLong(track))
|
||||
{
|
||||
event.replyWarning("This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+ TimeUtil.formatTime(track.getDuration())+"` > `"+bot.getConfig().getMaxTime()+"`");
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "track_too_long"));
|
||||
return;
|
||||
}
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
RequestMetadata rm = RequestMetadata.fromResultHandler(track, event, "SEARCH", null, event.getArgs());
|
||||
int pos = handler.addTrack(new QueuedTrack(track, rm))+1;
|
||||
if(bot.getDataLogService() != null)
|
||||
{
|
||||
bot.getDataLogService().logSearchSelection(event.getGuild(), event.getAuthor(), event.getArgs(), i, track);
|
||||
bot.getDataLogService().logQueueAdd(event.getGuild(), event.getAuthor(), track, "SEARCH", pos, event.getArgs(), null);
|
||||
}
|
||||
event.replySuccess("Added **" + FormatUtil.filter(track.getInfo().title)
|
||||
+ "** (`" + TimeUtil.formatTime(track.getDuration()) + "`) " + (pos==0 ? "to begin playing"
|
||||
: " to the queue at position "+pos));
|
||||
})
|
||||
.setCancel((msg) -> {})
|
||||
.setUsers(event.getAuthor())
|
||||
;
|
||||
for(int i=0; i<4 && i<playlist.getTracks().size(); i++)
|
||||
{
|
||||
AudioTrack track = playlist.getTracks().get(i);
|
||||
builder.addChoices("`["+ TimeUtil.formatTime(track.getDuration())+"]` [**"+track.getInfo().title+"**]("+track.getInfo().uri+")");
|
||||
}
|
||||
builder.build().display(m);
|
||||
logCommandEvent(event, "OK", new JSONObject().put("result_count", playlist.getTracks().size()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches()
|
||||
{
|
||||
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.")).queue();
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logSearchEventWithMeta(event.getGuild(), event.getAuthor(), event.getArgs(), null, null,
|
||||
new JSONObject().put("error_reason", "no_matches").toString());
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "no_matches"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException throwable)
|
||||
{
|
||||
if(throwable.severity==Severity.COMMON)
|
||||
m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue();
|
||||
else
|
||||
m.editMessage(event.getClient().getError()+" Error loading track.").queue();
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logSearchEventWithMeta(event.getGuild(), event.getAuthor(), event.getArgs(), null, null,
|
||||
new JSONObject().put("error_reason", throwable.getMessage()).toString());
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", throwable.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void logCommandEvent(CommandEvent event, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), result, meta == null ? null : meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.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.DJCommand;
|
||||
import com.jagrosh.jmusicbot.commands.MusicCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.utils.TimeUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* @author Whew., Inc.
|
||||
*/
|
||||
public class SeekCmd extends MusicCommand
|
||||
{
|
||||
private final static Logger LOG = LoggerFactory.getLogger("Seeking");
|
||||
|
||||
public SeekCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "seek";
|
||||
this.help = "seeks the current song";
|
||||
this.arguments = "[+ | -] <HH:MM:SS | MM:SS | SS>|<0h0m0s | 0m0s | 0s>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.beListening = true;
|
||||
this.bePlaying = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
|
||||
AudioTrack playingTrack = handler.getPlayer().getPlayingTrack();
|
||||
if (!playingTrack.isSeekable())
|
||||
{
|
||||
event.replyError("This track is not seekable.");
|
||||
CommandLogContext.setError("not_seekable");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!DJCommand.checkDJPermission(event) && playingTrack.getUserData(RequestMetadata.class).getOwner() != event.getAuthor().getIdLong())
|
||||
{
|
||||
event.replyError("You cannot seek **" + playingTrack.getInfo().title + "** because you didn't add it!");
|
||||
CommandLogContext.setError("not_owner");
|
||||
return;
|
||||
}
|
||||
|
||||
String args = event.getArgs();
|
||||
TimeUtil.SeekTime seekTime = TimeUtil.parseTime(args);
|
||||
if (seekTime == null)
|
||||
{
|
||||
event.replyError("Invalid seek! Expected format: " + arguments + "\nExamples: `1:02:23` `+1:10` `-90`, `1h10m`, `+90s`");
|
||||
CommandLogContext.setError("invalid_seek");
|
||||
return;
|
||||
}
|
||||
|
||||
long currentPosition = playingTrack.getPosition();
|
||||
long trackDuration = playingTrack.getDuration();
|
||||
|
||||
long seekMilliseconds = seekTime.relative ? currentPosition + seekTime.milliseconds : seekTime.milliseconds;
|
||||
if (seekMilliseconds > trackDuration)
|
||||
{
|
||||
event.replyError("Cannot seek to `" + TimeUtil.formatTime(seekMilliseconds) + "` because the current track is `" + TimeUtil.formatTime(trackDuration) + "` long!");
|
||||
CommandLogContext.setError("seek_out_of_bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
playingTrack.setPosition(seekMilliseconds);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
event.replyError("An error occurred while trying to seek: " + e.getMessage());
|
||||
LOG.warn("Failed to seek track " + playingTrack.getIdentifier(), e);
|
||||
CommandLogContext.setError("seek_failed");
|
||||
return;
|
||||
}
|
||||
event.replySuccess("Successfully seeked to `" + TimeUtil.formatTime(playingTrack.getPosition()) + "/" + TimeUtil.formatTime(playingTrack.getDuration()) + "`!");
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("seek_from_ms", currentPosition)
|
||||
.put("seek_to_ms", playingTrack.getPosition());
|
||||
CommandLogContext.setMeta(meta);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), playingTrack,
|
||||
"SEEK", null, null, null, null, meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.music;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.commands.MusicCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class ShuffleCmd extends MusicCommand
|
||||
{
|
||||
public ShuffleCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "shuffle";
|
||||
this.help = "shuffles songs you have added";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.beListening = true;
|
||||
this.bePlaying = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
int sizeBefore = handler.getQueue().size();
|
||||
int s = handler.getQueue().shuffle(event.getAuthor().getIdLong());
|
||||
switch (s)
|
||||
{
|
||||
case 0:
|
||||
event.replyError("You don't have any music in the queue to shuffle!");
|
||||
CommandLogContext.setError("no_entries_to_shuffle");
|
||||
break;
|
||||
case 1:
|
||||
event.replyWarning("You only have one song in the queue!");
|
||||
CommandLogContext.setMeta(new JSONObject().put("shuffled_count", 1));
|
||||
break;
|
||||
default:
|
||||
event.replySuccess("You successfully shuffled your "+s+" entries.");
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("shuffled_count", s)
|
||||
.put("queue_size_before", sizeBefore)
|
||||
.put("queue_size_after", handler.getQueue().size());
|
||||
CommandLogContext.setMeta(meta);
|
||||
if(bot.getDataLogService() != null)
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), null,
|
||||
"SHUFFLE", null, null, null, null, meta.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2016 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.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 com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SkipCmd extends MusicCommand
|
||||
{
|
||||
public SkipCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "skip";
|
||||
this.help = "votes to skip the current song";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.beListening = true;
|
||||
this.bePlaying = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
RequestMetadata rm = handler.getRequestMetadata();
|
||||
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+"**");
|
||||
if(bot.getDataLogService() != null)
|
||||
{
|
||||
JSONObject meta = new JSONObject().put("reason", "REQUESTER");
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), handler.getPlayer().getPlayingTrack(),
|
||||
"SKIP_REQUESTER", null, null, null, null, meta.toString());
|
||||
CommandLogContext.setMeta(meta);
|
||||
}
|
||||
handler.getPlayer().stopTrack();
|
||||
}
|
||||
else
|
||||
{
|
||||
int listeners = (int)event.getSelfMember().getVoiceState().getChannel().getMembers().stream()
|
||||
.filter(m -> !m.getUser().isBot() && !m.getVoiceState().isDeafened()).count();
|
||||
String msg;
|
||||
if(handler.getVotes().contains(event.getAuthor().getId()))
|
||||
msg = event.getClient().getWarning()+" You already voted to skip this song `[";
|
||||
else
|
||||
{
|
||||
msg = event.getClient().getSuccess()+" You voted to skip the song `[";
|
||||
handler.getVotes().add(event.getAuthor().getId());
|
||||
if(bot.getDataLogService() != null)
|
||||
{
|
||||
JSONObject meta = new JSONObject().put("vote", "added");
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), handler.getPlayer().getPlayingTrack(),
|
||||
"SKIP_VOTE", null, null, null, null, meta.toString());
|
||||
}
|
||||
}
|
||||
int skippers = (int)event.getSelfMember().getVoiceState().getChannel().getMembers().stream()
|
||||
.filter(m -> handler.getVotes().contains(m.getUser().getId())).count();
|
||||
int required = (int)Math.ceil(listeners * skipRatio);
|
||||
msg += skippers + " votes, " + required + "/" + listeners + " needed]`";
|
||||
CommandLogContext.setMeta(new JSONObject()
|
||||
.put("votes", skippers)
|
||||
.put("required", required)
|
||||
.put("listeners", listeners));
|
||||
if(skippers>=required)
|
||||
{
|
||||
msg += "\n" + event.getClient().getSuccess() + " Skipped **" + handler.getPlayer().getPlayingTrack().getInfo().title
|
||||
+ "** " + (rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + FormatUtil.formatUsername(rm.user) + "**)");
|
||||
if(bot.getDataLogService() != null)
|
||||
{
|
||||
JSONObject meta = new JSONObject()
|
||||
.put("reason", "VOTE")
|
||||
.put("votes", skippers)
|
||||
.put("required", required)
|
||||
.put("listeners", listeners);
|
||||
bot.getDataLogService().logQueueEventWithMeta(event.getGuild(), event.getAuthor(), handler.getPlayer().getPlayingTrack(),
|
||||
"SKIP_VOTE_ACTION", null, null, null, null, meta.toString());
|
||||
CommandLogContext.setMeta(meta);
|
||||
}
|
||||
handler.getPlayer().stopTrack();
|
||||
}
|
||||
event.reply(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2016 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.owner;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.OwnerCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class AutoplaylistCmd extends OwnerCommand
|
||||
{
|
||||
public AutoplaylistCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.guildOnly = true;
|
||||
this.name = "autoplaylist";
|
||||
this.arguments = "<name|NONE>";
|
||||
this.help = "sets the default playlist for the server";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a playlist name or NONE");
|
||||
CommandLogContext.setError("missing_playlist");
|
||||
return;
|
||||
}
|
||||
Settings settings = event.getClient().getSettingsFor(event.getGuild());
|
||||
String before = settings.getDefaultPlaylist();
|
||||
if(event.getArgs().equalsIgnoreCase("none"))
|
||||
{
|
||||
settings.setDefaultPlaylist(null);
|
||||
event.reply(event.getClient().getSuccess()+" Cleared the default playlist for **"+event.getGuild().getName()+"**");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before).put("after", JSONObject.NULL));
|
||||
return;
|
||||
}
|
||||
String pname = event.getArgs().replaceAll("\\s+", "_");
|
||||
if(bot.getPlaylistLoader().getPlaylist(pname)==null)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Could not find `"+pname+".txt`!");
|
||||
CommandLogContext.setError("playlist_not_found");
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.setDefaultPlaylist(pname);
|
||||
event.reply(event.getClient().getSuccess()+" The default playlist for **"+event.getGuild().getName()+"** is now `"+pname+"`");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", before).put("after", pname));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2017 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.owner;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.OwnerCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.utils.OtherUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.PlayerLibrary;
|
||||
import net.dv8tion.jda.api.JDAInfo;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||
import net.dv8tion.jda.api.utils.FileUpload;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class DebugCmd extends OwnerCommand
|
||||
{
|
||||
private final static String[] PROPERTIES = {"java.version", "java.vm.name", "java.vm.specification.version",
|
||||
"java.runtime.name", "java.runtime.version", "java.specification.version", "os.arch", "os.name"};
|
||||
|
||||
public DebugCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "debug";
|
||||
this.help = "shows debug info";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.setMeta(new JSONObject().put("channel_type", event.getChannelType().name()));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("```\nSystem Properties:");
|
||||
for(String key: PROPERTIES)
|
||||
sb.append("\n ").append(key).append(" = ").append(System.getProperty(key));
|
||||
sb.append("\n\nJMusicBot Information:")
|
||||
.append("\n Version = ").append(OtherUtil.getCurrentVersion())
|
||||
.append("\n Owner = ").append(bot.getConfig().getOwnerId())
|
||||
.append("\n Prefix = ").append(bot.getConfig().getPrefix())
|
||||
.append("\n AltPrefix = ").append(bot.getConfig().getAltPrefix())
|
||||
.append("\n MaxSeconds = ").append(bot.getConfig().getMaxSeconds())
|
||||
.append("\n NPImages = ").append(bot.getConfig().useNPImages())
|
||||
.append("\n SongInStatus = ").append(bot.getConfig().getSongInStatus())
|
||||
.append("\n StayInChannel = ").append(bot.getConfig().getStay())
|
||||
.append("\n UseEval = ").append(bot.getConfig().useEval())
|
||||
.append("\n UpdateAlerts = ").append(bot.getConfig().useUpdateAlerts());
|
||||
sb.append("\n\nDependency Information:")
|
||||
.append("\n JDA Version = ").append(JDAInfo.VERSION)
|
||||
.append("\n JDA-Utilities Version = ").append(OtherUtil.getCommandLibraryVersion())
|
||||
.append("\n Lavaplayer Version = ").append(PlayerLibrary.VERSION);
|
||||
long total = Runtime.getRuntime().totalMemory() / 1024 / 1024;
|
||||
long used = total - (Runtime.getRuntime().freeMemory() / 1024 / 1024);
|
||||
sb.append("\n\nRuntime Information:")
|
||||
.append("\n Total Memory = ").append(total)
|
||||
.append("\n Used Memory = ").append(used);
|
||||
sb.append("\n\nDiscord Information:")
|
||||
.append("\n ID = ").append(event.getJDA().getSelfUser().getId())
|
||||
.append("\n Guilds = ").append(event.getJDA().getGuildCache().size())
|
||||
.append("\n Users = ").append(event.getJDA().getUserCache().size());
|
||||
sb.append("\n```");
|
||||
|
||||
if(event.isFromType(ChannelType.PRIVATE)
|
||||
|| event.getSelfMember().hasPermission(event.getGuildChannel(), Permission.MESSAGE_ATTACH_FILES))
|
||||
event.getChannel().sendFiles(FileUpload.fromData(sb.toString().getBytes(), "debug_information.txt")).queue();
|
||||
else
|
||||
event.reply("Debug Information: " + sb.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2016 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.commands.owner;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.OwnerCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (jagrosh)
|
||||
*/
|
||||
public class EvalCmd extends OwnerCommand
|
||||
{
|
||||
private final String engine;
|
||||
|
||||
public EvalCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "eval";
|
||||
this.help = "evaluates nashorn code";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.engine = bot.getConfig().getEvalEngine();
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.setManualLogging();
|
||||
ScriptEngine se = new ScriptEngineManager().getEngineByName(engine);
|
||||
if(se == null)
|
||||
{
|
||||
event.replyError("The eval engine provided in the config (`"+engine+"`) doesn't exist. This could be due to an invalid "
|
||||
+ "engine name, or the engine not existing in your version of java (`"+System.getProperty("java.version")+"`).");
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "engine_not_found"));
|
||||
return;
|
||||
}
|
||||
se.put("bot", bot);
|
||||
se.put("event", event);
|
||||
se.put("jda", event.getJDA());
|
||||
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())+" ```");
|
||||
logCommandEvent(event, "OK", new JSONObject().put("invoked", true));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" An exception was thrown:\n```\n"+e+" ```");
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "eval_exception"));
|
||||
}
|
||||
}
|
||||
|
||||
private void logCommandEvent(CommandEvent event, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
null, result, meta == null ? null : meta.toString());
|
||||
}
|
||||
|
||||
}
|
||||
+81
-78
@@ -13,143 +13,162 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.commands;
|
||||
package com.jagrosh.jmusicbot.commands.owner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.playlist.Playlist;
|
||||
import com.jagrosh.jmusicbot.commands.OwnerCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class PlaylistCmd extends Command {
|
||||
|
||||
private final Bot bot;
|
||||
public class PlaylistCmd extends OwnerCommand
|
||||
{
|
||||
public PlaylistCmd(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.category = bot.OWNER;
|
||||
this.ownerCommand = true;
|
||||
super(bot);
|
||||
this.guildOnly = false;
|
||||
this.name = "playlist";
|
||||
this.arguments = "<append|delete|make|setdefault>";
|
||||
this.help = "playlist management";
|
||||
this.children = new Command[]{
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.children = new OwnerCommand[]{
|
||||
new ListCmd(),
|
||||
new AppendlistCmd(),
|
||||
new DeletelistCmd(),
|
||||
new MakelistCmd(),
|
||||
new DefaultlistCmd()
|
||||
new DefaultlistCmd(bot)
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandEvent event) {
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(event.getClient().getWarning()+" Playlist Management Commands:\n");
|
||||
for(Command cmd: this.children)
|
||||
for(com.jagrosh.jdautilities.command.Command cmd: this.children)
|
||||
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ").append(cmd.getName())
|
||||
.append(" ").append(cmd.getArguments()==null ? "" : cmd.getArguments()).append("` - ").append(cmd.getHelp());
|
||||
event.reply(builder.toString());
|
||||
CommandLogContext.setMeta(new JSONObject().put("subcommands", this.children.length));
|
||||
}
|
||||
|
||||
public class MakelistCmd extends Command {
|
||||
|
||||
public class MakelistCmd extends OwnerCommand
|
||||
{
|
||||
public MakelistCmd()
|
||||
{
|
||||
super(PlaylistCmd.this.bot);
|
||||
this.name = "make";
|
||||
this.aliases = new String[]{"create"};
|
||||
this.help = "makes a new playlist";
|
||||
this.arguments = "<name>";
|
||||
this.category = bot.OWNER;
|
||||
this.ownerCommand = true;
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
String pname = event.getArgs().replaceAll("\\s+", "_");
|
||||
if(Playlist.loadPlaylist(pname)==null)
|
||||
pname = pname.replaceAll("[*?|\\/\":<>]", "");
|
||||
if(pname == null || pname.isEmpty())
|
||||
{
|
||||
event.replyError("Please provide a name for the playlist!");
|
||||
CommandLogContext.setError("missing_playlist_name");
|
||||
}
|
||||
else if(bot.getPlaylistLoader().getPlaylist(pname) == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.createFile(Paths.get("Playlists"+File.separator+pname+".txt"));
|
||||
bot.getPlaylistLoader().createPlaylist(pname);
|
||||
event.reply(event.getClient().getSuccess()+" Successfully created playlist `"+pname+"`!");
|
||||
CommandLogContext.setMeta(new JSONObject().put("playlist_name", pname));
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" I was unable to create the playlist: "+e.getLocalizedMessage());
|
||||
CommandLogContext.setError("create_playlist_failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Playlist `"+pname+"` already exists!");
|
||||
CommandLogContext.setError("playlist_exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DeletelistCmd extends Command {
|
||||
|
||||
public class DeletelistCmd extends OwnerCommand
|
||||
{
|
||||
public DeletelistCmd()
|
||||
{
|
||||
super(PlaylistCmd.this.bot);
|
||||
this.name = "delete";
|
||||
this.aliases = new String[]{"remove"};
|
||||
this.help = "deletes an existing playlist";
|
||||
this.arguments = "<name>";
|
||||
this.guildOnly = false;
|
||||
this.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
String pname = event.getArgs().replaceAll("\\s+", "_");
|
||||
if(Playlist.loadPlaylist(pname)==null)
|
||||
if(bot.getPlaylistLoader().getPlaylist(pname)==null)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Playlist `"+pname+"` doesn't exist!");
|
||||
CommandLogContext.setError("playlist_not_found");
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.delete(Paths.get("Playlists"+File.separator+pname+".txt"));
|
||||
bot.getPlaylistLoader().deletePlaylist(pname);
|
||||
event.reply(event.getClient().getSuccess()+" Successfully deleted playlist `"+pname+"`!");
|
||||
CommandLogContext.setMeta(new JSONObject().put("playlist_name", pname));
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" I was unable to delete the playlist: "+e.getLocalizedMessage());
|
||||
CommandLogContext.setError("delete_playlist_failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AppendlistCmd extends Command {
|
||||
|
||||
public class AppendlistCmd extends OwnerCommand
|
||||
{
|
||||
public AppendlistCmd()
|
||||
{
|
||||
super(PlaylistCmd.this.bot);
|
||||
this.name = "append";
|
||||
this.aliases = new String[]{"add"};
|
||||
this.help = "appends songs to an existing playlist";
|
||||
this.arguments = "<name> <URL> | <URL> | ...";
|
||||
this.guildOnly = false;
|
||||
this.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
String[] parts = event.getArgs().split("\\s+", 2);
|
||||
if(parts.length<2)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a playlist name and URLs to add!");
|
||||
CommandLogContext.setError("missing_append_args");
|
||||
return;
|
||||
}
|
||||
String pname = parts[0];
|
||||
Playlist playlist = Playlist.loadPlaylist(pname);
|
||||
Playlist playlist = bot.getPlaylistLoader().getPlaylist(pname);
|
||||
if(playlist==null)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Playlist `"+pname+"` doesn't exist!");
|
||||
CommandLogContext.setError("playlist_not_found");
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -164,86 +183,70 @@ public class PlaylistCmd extends Command {
|
||||
}
|
||||
try
|
||||
{
|
||||
Files.write(Paths.get("Playlists"+File.separator+pname+".txt"), builder.toString().trim().getBytes());
|
||||
event.reply(event.getClient().getSuccess()+" Successfully added "+urls.length+" songs to playlist `"+pname+"`!");
|
||||
bot.getPlaylistLoader().writePlaylist(pname, builder.toString());
|
||||
event.reply(event.getClient().getSuccess()+" Successfully added "+urls.length+" items to playlist `"+pname+"`!");
|
||||
CommandLogContext.setMeta(new JSONObject().put("playlist_name", pname).put("added_count", urls.length));
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" I was unable to append to the playlist: "+e.getLocalizedMessage());
|
||||
CommandLogContext.setError("append_playlist_failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DefaultlistCmd extends Command {
|
||||
|
||||
public DefaultlistCmd()
|
||||
public class DefaultlistCmd extends AutoplaylistCmd
|
||||
{
|
||||
public DefaultlistCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "setdefault";
|
||||
this.aliases = new String[]{"default"};
|
||||
this.help = "sets the default playlist for the server";
|
||||
this.arguments = "<playlistname|NONE>";
|
||||
this.guildOnly = true;
|
||||
this.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Please include a playlist name or NONE");
|
||||
}
|
||||
if(event.getArgs().equalsIgnoreCase("none"))
|
||||
{
|
||||
bot.setDefaultPlaylist(event.getGuild(), null);
|
||||
event.reply(event.getClient().getSuccess()+" Cleared the default playlist for **"+event.getGuild().getName()+"**");
|
||||
return;
|
||||
}
|
||||
String pname = event.getArgs().replaceAll("\\s+", "_");
|
||||
if(Playlist.loadPlaylist(pname)==null)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Could not find `"+pname+".txt`!");
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.setDefaultPlaylist(event.getGuild(), pname);
|
||||
event.reply(event.getClient().getSuccess()+" The default playlist for **"+event.getGuild().getName()+"** is now `"+pname+"`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ListCmd extends Command {
|
||||
|
||||
public class ListCmd extends OwnerCommand
|
||||
{
|
||||
public ListCmd()
|
||||
{
|
||||
super(PlaylistCmd.this.bot);
|
||||
this.name = "all";
|
||||
this.aliases = new String[]{"available","list"};
|
||||
this.help = "lists all available playlists";
|
||||
this.guildOnly = true;
|
||||
this.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
if(!Playlist.folderExists())
|
||||
Playlist.createFolder();
|
||||
if(!Playlist.folderExists())
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(!bot.getPlaylistLoader().folderExists())
|
||||
bot.getPlaylistLoader().createFolder();
|
||||
if(!bot.getPlaylistLoader().folderExists())
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+" Playlists folder does not exist and could not be created!");
|
||||
CommandLogContext.setError("playlist_folder_missing");
|
||||
return;
|
||||
}
|
||||
List<String> list = Playlist.getPlaylists();
|
||||
List<String> list = bot.getPlaylistLoader().getPlaylistNames();
|
||||
if(list==null)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Failed to load available playlists!");
|
||||
CommandLogContext.setError("playlist_list_failed");
|
||||
}
|
||||
else if(list.isEmpty())
|
||||
{
|
||||
event.reply(event.getClient().getWarning()+" There are no playlists in the Playlists folder!");
|
||||
CommandLogContext.setMeta(new JSONObject().put("count", 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(event.getClient().getSuccess()+" Available playlists:\n");
|
||||
list.forEach(str -> builder.append("`").append(str).append("` "));
|
||||
event.reply(builder.toString());
|
||||
CommandLogContext.setMeta(new JSONObject().put("count", list.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
-13
@@ -13,34 +13,38 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.commands;
|
||||
package com.jagrosh.jmusicbot.commands.owner;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.OwnerCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import com.jagrosh.jmusicbot.utils.OtherUtil;
|
||||
import net.dv8tion.jda.core.entities.Icon;
|
||||
import net.dv8tion.jda.api.entities.Icon;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetavatarCmd extends Command {
|
||||
|
||||
public class SetavatarCmd extends OwnerCommand
|
||||
{
|
||||
public SetavatarCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "setavatar";
|
||||
this.help = "sets the avatar of the bot";
|
||||
this.arguments = "<url>";
|
||||
this.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.setManualLogging();
|
||||
String url;
|
||||
if(event.getArgs().isEmpty())
|
||||
if(!event.getMessage().getAttachments().isEmpty() && event.getMessage().getAttachments().get(0).isImage())
|
||||
@@ -53,17 +57,33 @@ public class SetavatarCmd extends Command {
|
||||
if(s==null)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Invalid or missing URL");
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "invalid_avatar_url"));
|
||||
}
|
||||
else
|
||||
{
|
||||
try {
|
||||
event.getSelfUser().getManager().setAvatar(Icon.from(s)).queue(
|
||||
v -> event.reply(event.getClient().getSuccess()+" Successfully changed avatar."),
|
||||
t -> event.reply(event.getClient().getError()+" Failed to set avatar."));
|
||||
v -> {
|
||||
event.reply(event.getClient().getSuccess()+" Successfully changed avatar.");
|
||||
logCommandEvent(event, "OK", new JSONObject()
|
||||
.put("source", event.getArgs().isEmpty() ? "attachment" : "url"));
|
||||
},
|
||||
t -> {
|
||||
event.reply(event.getClient().getError()+" Failed to set avatar.");
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "avatar_set_failed"));
|
||||
});
|
||||
} catch(IOException e) {
|
||||
event.reply(event.getClient().getError()+" Could not load from provided URL.");
|
||||
logCommandEvent(event, "ERROR", new JSONObject().put("error_reason", "avatar_load_failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void logCommandEvent(CommandEvent event, String result, JSONObject meta)
|
||||
{
|
||||
if(bot.getDataLogService() == null || event == null || event.getGuild() == null)
|
||||
return;
|
||||
bot.getDataLogService().logCommandEvent(event.getGuild(), event.getAuthor(), getName(),
|
||||
event.getArgs(), result, meta == null ? null : meta.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2017 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.owner;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.OwnerCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetgameCmd extends OwnerCommand
|
||||
{
|
||||
public SetgameCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "setgame";
|
||||
this.help = "sets the game the bot is playing";
|
||||
this.arguments = "[action] [game]";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.guildOnly = false;
|
||||
this.children = new OwnerCommand[]{
|
||||
new SetlistenCmd(),
|
||||
new SetstreamCmd(),
|
||||
new SetwatchCmd()
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
String title = event.getArgs().toLowerCase().startsWith("playing") ? event.getArgs().substring(7).trim() : event.getArgs();
|
||||
try
|
||||
{
|
||||
event.getJDA().getPresence().setActivity(title.isEmpty() ? null : Activity.playing(title));
|
||||
event.reply(event.getClient().getSuccess()+" **"+event.getSelfUser().getName()
|
||||
+"** is "+(title.isEmpty() ? "no longer playing anything." : "now playing `"+title+"`"));
|
||||
CommandLogContext.setMeta(new JSONObject()
|
||||
.put("activity_type", "PLAYING")
|
||||
.put("title", title.isEmpty() ? JSONObject.NULL : title));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" The game could not be set!");
|
||||
CommandLogContext.setError("set_game_failed");
|
||||
}
|
||||
}
|
||||
|
||||
private class SetstreamCmd extends OwnerCommand
|
||||
{
|
||||
private SetstreamCmd()
|
||||
{
|
||||
super(SetgameCmd.this.bot);
|
||||
this.name = "stream";
|
||||
this.aliases = new String[]{"twitch","streaming"};
|
||||
this.help = "sets the game the bot is playing to a stream";
|
||||
this.arguments = "<username> <game>";
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
String[] parts = event.getArgs().split("\\s+", 2);
|
||||
if(parts.length<2)
|
||||
{
|
||||
event.replyError("Please include a twitch username and the name of the game to 'stream'");
|
||||
CommandLogContext.setError("missing_stream_args");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
event.getJDA().getPresence().setActivity(Activity.streaming(parts[1], "https://twitch.tv/"+parts[0]));
|
||||
event.replySuccess("**"+event.getSelfUser().getName()
|
||||
+"** is now streaming `"+parts[1]+"`");
|
||||
CommandLogContext.setMeta(new JSONObject()
|
||||
.put("activity_type", "STREAMING")
|
||||
.put("title", parts[1])
|
||||
.put("user", parts[0]));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" The game could not be set!");
|
||||
CommandLogContext.setError("set_game_failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SetlistenCmd extends OwnerCommand
|
||||
{
|
||||
private SetlistenCmd()
|
||||
{
|
||||
super(SetgameCmd.this.bot);
|
||||
this.name = "listen";
|
||||
this.aliases = new String[]{"listening"};
|
||||
this.help = "sets the game the bot is listening to";
|
||||
this.arguments = "<title>";
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.replyError("Please include a title to listen to!");
|
||||
CommandLogContext.setError("missing_listen_title");
|
||||
return;
|
||||
}
|
||||
String title = event.getArgs().toLowerCase().startsWith("to") ? event.getArgs().substring(2).trim() : event.getArgs();
|
||||
try
|
||||
{
|
||||
event.getJDA().getPresence().setActivity(Activity.listening(title));
|
||||
event.replySuccess("**"+event.getSelfUser().getName()+"** is now listening to `"+title+"`");
|
||||
CommandLogContext.setMeta(new JSONObject().put("activity_type", "LISTENING").put("title", title));
|
||||
} catch(Exception e) {
|
||||
event.reply(event.getClient().getError()+" The game could not be set!");
|
||||
CommandLogContext.setError("set_game_failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SetwatchCmd extends OwnerCommand
|
||||
{
|
||||
private SetwatchCmd()
|
||||
{
|
||||
super(SetgameCmd.this.bot);
|
||||
this.name = "watch";
|
||||
this.aliases = new String[]{"watching"};
|
||||
this.help = "sets the game the bot is watching";
|
||||
this.arguments = "<title>";
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
if(event.getArgs().isEmpty())
|
||||
{
|
||||
event.replyError("Please include a title to watch!");
|
||||
CommandLogContext.setError("missing_watch_title");
|
||||
return;
|
||||
}
|
||||
String title = event.getArgs();
|
||||
try
|
||||
{
|
||||
event.getJDA().getPresence().setActivity(Activity.watching(title));
|
||||
event.replySuccess("**"+event.getSelfUser().getName()+"** is now watching `"+title+"`");
|
||||
CommandLogContext.setMeta(new JSONObject().put("activity_type", "WATCHING").put("title", title));
|
||||
} catch(Exception e) {
|
||||
event.reply(event.getClient().getError()+" The game could not be set!");
|
||||
CommandLogContext.setError("set_game_failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
-51
@@ -1,51 +1,62 @@
|
||||
/*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import net.dv8tion.jda.core.exceptions.RateLimitedException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetnameCmd extends Command {
|
||||
|
||||
public SetnameCmd(Bot bot)
|
||||
{
|
||||
this.name = "setname";
|
||||
this.help = "sets the name of the bot";
|
||||
this.arguments = "<name>";
|
||||
this.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
try {
|
||||
String oldname = event.getSelfUser().getName();
|
||||
event.getSelfUser().getManager().setName(event.getArgs()).complete(false);
|
||||
event.reply(event.getClient().getSuccess()+" Name changed from `"+oldname+"` to `"+event.getArgs()+"`");
|
||||
} catch(RateLimitedException e) {
|
||||
event.reply(event.getClient().getError()+" Name can only be changed twice per hour!");
|
||||
} catch(Exception e) {
|
||||
event.reply(event.getClient().getError()+" That name is not valid!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.owner;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.OwnerCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import net.dv8tion.jda.api.exceptions.RateLimitedException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetnameCmd extends OwnerCommand
|
||||
{
|
||||
public SetnameCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "setname";
|
||||
this.help = "sets the name of the bot";
|
||||
this.arguments = "<name>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
try
|
||||
{
|
||||
String oldname = event.getSelfUser().getName();
|
||||
event.getSelfUser().getManager().setName(event.getArgs()).complete(false);
|
||||
event.reply(event.getClient().getSuccess()+" Name changed from `"+oldname+"` to `"+event.getArgs()+"`");
|
||||
CommandLogContext.setMeta(new JSONObject().put("before", oldname).put("after", event.getArgs()));
|
||||
}
|
||||
catch(RateLimitedException e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Name can only be changed twice per hour!");
|
||||
CommandLogContext.setError("rate_limited");
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" That name is not valid!");
|
||||
CommandLogContext.setError("invalid_name");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2017 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.owner;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.OwnerCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import net.dv8tion.jda.api.OnlineStatus;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetstatusCmd extends OwnerCommand
|
||||
{
|
||||
public SetstatusCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "setstatus";
|
||||
this.help = "sets the status the bot displays";
|
||||
this.arguments = "<status>";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
try {
|
||||
OnlineStatus status = OnlineStatus.fromKey(event.getArgs());
|
||||
if(status==OnlineStatus.UNKNOWN)
|
||||
{
|
||||
event.replyError("Please include one of the following statuses: `ONLINE`, `IDLE`, `DND`, `INVISIBLE`");
|
||||
CommandLogContext.setError("invalid_status");
|
||||
}
|
||||
else
|
||||
{
|
||||
OnlineStatus before = event.getJDA().getPresence().getStatus();
|
||||
event.getJDA().getPresence().setStatus(status);
|
||||
event.replySuccess("Set the status to `"+status.getKey().toUpperCase()+"`");
|
||||
CommandLogContext.setMeta(new JSONObject()
|
||||
.put("before", before == null ? JSONObject.NULL : before.getKey())
|
||||
.put("after", status.getKey()));
|
||||
}
|
||||
} catch(Exception e) {
|
||||
event.reply(event.getClient().getError()+" The status could not be set!");
|
||||
CommandLogContext.setError("set_status_failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
-44
@@ -1,44 +1,46 @@
|
||||
/*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class ShutdownCmd extends Command {
|
||||
|
||||
private final Bot bot;
|
||||
public ShutdownCmd(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.name = "shutdown";
|
||||
this.help = "safely shuts down";
|
||||
this.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
event.reply(event.getClient().getWarning()+" Shutting down...");
|
||||
bot.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2017 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.owner;
|
||||
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.commands.OwnerCommand;
|
||||
import com.jagrosh.jmusicbot.datalog.CommandLogContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class ShutdownCmd extends OwnerCommand
|
||||
{
|
||||
public ShutdownCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "shutdown";
|
||||
this.help = "safely shuts down";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event)
|
||||
{
|
||||
CommandLogContext.setMeta(new JSONObject().put("invoked", true));
|
||||
event.replyWarning("Shutting down...");
|
||||
bot.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* 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.datalog;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public final class CommandLogContext
|
||||
{
|
||||
private static final ThreadLocal<CommandLogEntry> CONTEXT = new ThreadLocal<>();
|
||||
|
||||
private CommandLogContext()
|
||||
{
|
||||
}
|
||||
|
||||
public static void clear()
|
||||
{
|
||||
CONTEXT.remove();
|
||||
}
|
||||
|
||||
public static void setMeta(JSONObject meta)
|
||||
{
|
||||
getOrCreate().meta = meta;
|
||||
}
|
||||
|
||||
public static void setResult(String result)
|
||||
{
|
||||
getOrCreate().result = result;
|
||||
}
|
||||
|
||||
public static void setError(String reason)
|
||||
{
|
||||
CommandLogEntry entry = getOrCreate();
|
||||
entry.result = "ERROR";
|
||||
if(reason != null && !reason.isEmpty())
|
||||
{
|
||||
if(entry.meta == null)
|
||||
entry.meta = new JSONObject();
|
||||
entry.meta.put("error_reason", reason);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setManualLogging()
|
||||
{
|
||||
getOrCreate().manual = true;
|
||||
}
|
||||
|
||||
public static CommandLogEntry consume()
|
||||
{
|
||||
CommandLogEntry entry = CONTEXT.get();
|
||||
CONTEXT.remove();
|
||||
return entry;
|
||||
}
|
||||
|
||||
private static CommandLogEntry getOrCreate()
|
||||
{
|
||||
CommandLogEntry entry = CONTEXT.get();
|
||||
if(entry == null)
|
||||
{
|
||||
entry = new CommandLogEntry();
|
||||
CONTEXT.set(entry);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
public static final class CommandLogEntry
|
||||
{
|
||||
public JSONObject meta;
|
||||
public String result;
|
||||
public boolean manual;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.datalog;
|
||||
|
||||
public final class StatsAccess
|
||||
{
|
||||
private StatsAccess() {}
|
||||
|
||||
public static boolean canViewUserStats(long requesterId, long targetId, boolean canManageServer)
|
||||
{
|
||||
return requesterId == targetId || canManageServer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.datalog;
|
||||
|
||||
public final class StatsFilters
|
||||
{
|
||||
private StatsFilters() {}
|
||||
|
||||
public static String normalizeSource(String source)
|
||||
{
|
||||
if(source == null)
|
||||
return null;
|
||||
String normalized = source.trim().toLowerCase();
|
||||
if(normalized.isEmpty() || "all".equals(normalized) || "any".equals(normalized))
|
||||
return null;
|
||||
if("yt".equals(normalized) || "youtube.com".equals(normalized) || "youtu.be".equals(normalized))
|
||||
return "youtube";
|
||||
if("sc".equals(normalized) || "soundcloud.com".equals(normalized))
|
||||
return "soundcloud";
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.datalog;
|
||||
|
||||
import com.jagrosh.jmusicbot.utils.TimeUtil;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
|
||||
public final class StatsMessageFormatter
|
||||
{
|
||||
private static final int EMBED_FIELD_VALUE_LIMIT = 1024;
|
||||
private static final int SAFE_FIELD_VALUE_LIMIT = 1000;
|
||||
|
||||
private StatsMessageFormatter() {}
|
||||
|
||||
public static EmbedBuilder summaryEmbed(String title, StatsSummary summary, int color)
|
||||
{
|
||||
EmbedBuilder eb = new EmbedBuilder()
|
||||
.setTitle(title)
|
||||
.setColor(color)
|
||||
.setDescription("Range: **" + summary.getRange().getLabel() + "**"
|
||||
+ (summary.getSource() == null ? "" : "\nSource: **" + summary.getSource() + "**")
|
||||
+ "\nPlays: **" + summary.getTotalPlays() + "**"
|
||||
+ "\nListening time: **" + TimeUtil.formatTime(summary.getTotalListeningMillis()) + "**");
|
||||
addCountField(eb, "Top tracks", summary.getTopTracks(), "plays");
|
||||
addTimeField(eb, "Top listeners", summary.getTopListeners());
|
||||
addCountField(eb, "Top requesters", summary.getTopRequesters(), "plays");
|
||||
addCountField(eb, "Top sources", summary.getTopSources(), "plays");
|
||||
addCountField(eb, "Skip actions", summary.getSkipActions(), "skips");
|
||||
addCountField(eb, "Skip votes", summary.getSkipVotes(), "votes");
|
||||
return eb;
|
||||
}
|
||||
|
||||
public static EmbedBuilder personalEmbed(String title, StatsSummary summary, int color)
|
||||
{
|
||||
long skipActions = sumCounts(summary.getSkipActions());
|
||||
long skipVotes = sumCounts(summary.getSkipVotes());
|
||||
EmbedBuilder eb = new EmbedBuilder()
|
||||
.setTitle(title)
|
||||
.setColor(color)
|
||||
.setDescription("Range: **" + summary.getRange().getLabel() + "**"
|
||||
+ (summary.getSource() == null ? "" : "\nSource: **" + summary.getSource() + "**")
|
||||
+ "\nRequests: **" + summary.getTotalPlays() + "**"
|
||||
+ "\nListening time: **" + TimeUtil.formatTime(summary.getTotalListeningMillis()) + "**"
|
||||
+ "\nSkip actions: **" + skipActions + "**"
|
||||
+ "\nSkip votes: **" + skipVotes + "**");
|
||||
addCountField(eb, "Top tracks", summary.getTopTracks(), "plays");
|
||||
addCountField(eb, "Top sources", summary.getTopSources(), "plays");
|
||||
return eb;
|
||||
}
|
||||
|
||||
public static String countList(Iterable<StatsRow> rows, String unit)
|
||||
{
|
||||
return countList(rows, unit, 5);
|
||||
}
|
||||
|
||||
public static String countList(Iterable<StatsRow> rows, String unit, int limit)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int index = 1;
|
||||
for(StatsRow row : rows)
|
||||
{
|
||||
if(index > limit)
|
||||
break;
|
||||
builder.append(index++).append(". **").append(row.getLabel()).append("** - ")
|
||||
.append(row.getCount()).append(" ").append(unit);
|
||||
if(row.getDetail() != null && !row.getDetail().isEmpty())
|
||||
builder.append(" (").append(row.getDetail()).append(")");
|
||||
builder.append("\n");
|
||||
}
|
||||
return builder.length() == 0 ? "No data" : builder.toString();
|
||||
}
|
||||
|
||||
public static String timeList(Iterable<StatsRow> rows)
|
||||
{
|
||||
return timeList(rows, 5);
|
||||
}
|
||||
|
||||
public static String timeList(Iterable<StatsRow> rows, int limit)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int index = 1;
|
||||
for(StatsRow row : rows)
|
||||
{
|
||||
if(index > limit)
|
||||
break;
|
||||
builder.append(index++).append(". **").append(row.getLabel()).append("** - ")
|
||||
.append(TimeUtil.formatTime(row.getMillis())).append("\n");
|
||||
}
|
||||
return builder.length() == 0 ? "No data" : builder.toString();
|
||||
}
|
||||
|
||||
public static String trackListenList(Iterable<StatsRow> rows, int limit)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int index = 1;
|
||||
for(StatsRow row : rows)
|
||||
{
|
||||
if(index > limit)
|
||||
break;
|
||||
builder.append(index++).append(". **").append(row.getLabel()).append("** - ")
|
||||
.append(TimeUtil.formatTime(row.getMillis()));
|
||||
if(row.getCount() > 0L)
|
||||
builder.append(" (").append(row.getCount()).append(" listen").append(row.getCount() == 1L ? "" : "s").append(")");
|
||||
if(row.getDetail() != null && !row.getDetail().isEmpty())
|
||||
builder.append(" (").append(row.getDetail()).append(")");
|
||||
builder.append("\n");
|
||||
}
|
||||
return builder.length() == 0 ? "No data" : builder.toString();
|
||||
}
|
||||
|
||||
private static void addCountField(EmbedBuilder eb, String name, Iterable<StatsRow> rows, String unit)
|
||||
{
|
||||
addChunkedField(eb, name, countList(rows, unit), false);
|
||||
}
|
||||
|
||||
private static void addTimeField(EmbedBuilder eb, String name, Iterable<StatsRow> rows)
|
||||
{
|
||||
addChunkedField(eb, name, timeList(rows), false);
|
||||
}
|
||||
|
||||
public static void addChunkedField(EmbedBuilder eb, String name, String value, boolean inline)
|
||||
{
|
||||
if(value == null || value.isEmpty())
|
||||
{
|
||||
eb.addField(name, "No data", inline);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] lines = value.split("\\n");
|
||||
StringBuilder chunk = new StringBuilder();
|
||||
int part = 1;
|
||||
for(String rawLine : lines)
|
||||
{
|
||||
if(rawLine == null || rawLine.isEmpty())
|
||||
continue;
|
||||
String line = fitFieldLine(rawLine);
|
||||
int nextLength = chunk.length() + line.length() + 1;
|
||||
if(chunk.length() > 0 && nextLength > SAFE_FIELD_VALUE_LIMIT)
|
||||
{
|
||||
eb.addField(partName(name, part++), chunk.toString(), inline);
|
||||
chunk.setLength(0);
|
||||
}
|
||||
if(chunk.length() > 0)
|
||||
chunk.append("\n");
|
||||
chunk.append(line);
|
||||
}
|
||||
if(chunk.length() == 0)
|
||||
chunk.append("No data");
|
||||
eb.addField(partName(name, part), chunk.toString(), inline);
|
||||
}
|
||||
|
||||
private static String partName(String name, int part)
|
||||
{
|
||||
return part <= 1 ? name : name + " " + part;
|
||||
}
|
||||
|
||||
private static String fitFieldLine(String line)
|
||||
{
|
||||
if(line.length() <= EMBED_FIELD_VALUE_LIMIT)
|
||||
return line;
|
||||
return line.substring(0, SAFE_FIELD_VALUE_LIMIT - 3) + "...";
|
||||
}
|
||||
|
||||
private static long sumCounts(Iterable<StatsRow> rows)
|
||||
{
|
||||
long count = 0L;
|
||||
for(StatsRow row : rows)
|
||||
count += row.getCount();
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.datalog;
|
||||
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.settings.Settings;
|
||||
import com.jagrosh.jmusicbot.settings.StatsReportFrequency;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class StatsReportScheduler
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger("StatsReports");
|
||||
|
||||
private final Bot bot;
|
||||
private final Map<Long, String> sentPeriods = new ConcurrentHashMap<>();
|
||||
|
||||
public StatsReportScheduler(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
bot.getThreadpool().scheduleWithFixedDelay(() -> runScheduledReports(), 1, 1, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
private void runScheduledReports()
|
||||
{
|
||||
if(bot.getDataLogService() == null)
|
||||
return;
|
||||
JDA jda = bot.getJDA();
|
||||
if(jda == null)
|
||||
return;
|
||||
LocalDate today = LocalDate.now();
|
||||
for(Guild guild : jda.getGuilds())
|
||||
{
|
||||
Settings settings = bot.getSettingsManager().getSettings(guild);
|
||||
if(settings.getStatsReportFrequency() == StatsReportFrequency.OFF)
|
||||
continue;
|
||||
if(settings.getStatsReportFrequency() == StatsReportFrequency.MONTHLY && today.getDayOfMonth() != 1)
|
||||
continue;
|
||||
if(settings.getStatsReportFrequency() == StatsReportFrequency.YEARLY && (today.getDayOfMonth() != 1 || today.getMonthValue() != 1))
|
||||
continue;
|
||||
|
||||
TextChannel channel = settings.getStatsReportChannel(guild);
|
||||
if(channel == null)
|
||||
continue;
|
||||
StatsTimeRange range = settings.getStatsReportFrequency() == StatsReportFrequency.YEARLY
|
||||
? StatsTimeRange.previousYear()
|
||||
: StatsTimeRange.previousMonth();
|
||||
String periodKey = settings.getStatsReportFrequency().name() + ":" + range.getLabel();
|
||||
if(periodKey.equals(sentPeriods.get(guild.getIdLong())))
|
||||
continue;
|
||||
if(sendReport(guild, channel, range))
|
||||
sentPeriods.put(guild.getIdLong(), periodKey);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean sendReport(Guild guild, TextChannel channel, StatsTimeRange range)
|
||||
{
|
||||
if(bot.getDataLogService() == null || guild == null || channel == null)
|
||||
return false;
|
||||
StatsSummary summary = bot.getDataLogService().getStatsSummary(guild.getIdLong(), range, null, null);
|
||||
if(summary.isEmpty())
|
||||
return false;
|
||||
channel.sendMessageEmbeds(StatsMessageFormatter.summaryEmbed("Server rewind - " + range.getLabel(), summary,
|
||||
guild.getSelfMember().getColorRaw()).build()).queue(
|
||||
m -> {},
|
||||
t -> LOG.debug("Failed to send stats report for guild {}: {}", guild.getId(), t.getMessage()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.datalog;
|
||||
|
||||
public class StatsRow
|
||||
{
|
||||
private final long id;
|
||||
private final String label;
|
||||
private final String detail;
|
||||
private final long count;
|
||||
private final long millis;
|
||||
|
||||
public StatsRow(long id, String label, String detail, long count, long millis)
|
||||
{
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
this.detail = detail;
|
||||
this.count = count;
|
||||
this.millis = millis;
|
||||
}
|
||||
|
||||
public long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getDetail()
|
||||
{
|
||||
return detail;
|
||||
}
|
||||
|
||||
public long getCount()
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
public long getMillis()
|
||||
{
|
||||
return millis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.datalog;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class StatsSummary
|
||||
{
|
||||
private final StatsTimeRange range;
|
||||
private final String source;
|
||||
private final long totalPlays;
|
||||
private final long totalListeningMillis;
|
||||
private final List<StatsRow> topTracks;
|
||||
private final List<StatsRow> topRequesters;
|
||||
private final List<StatsRow> topListeners;
|
||||
private final List<StatsRow> topSources;
|
||||
private final List<StatsRow> skipVotes;
|
||||
private final List<StatsRow> skipActions;
|
||||
|
||||
public StatsSummary(StatsTimeRange range, String source, long totalPlays, long totalListeningMillis,
|
||||
List<StatsRow> topTracks, List<StatsRow> topRequesters, List<StatsRow> topListeners,
|
||||
List<StatsRow> topSources, List<StatsRow> skipVotes, List<StatsRow> skipActions)
|
||||
{
|
||||
this.range = range;
|
||||
this.source = source;
|
||||
this.totalPlays = totalPlays;
|
||||
this.totalListeningMillis = totalListeningMillis;
|
||||
this.topTracks = topTracks == null ? Collections.emptyList() : topTracks;
|
||||
this.topRequesters = topRequesters == null ? Collections.emptyList() : topRequesters;
|
||||
this.topListeners = topListeners == null ? Collections.emptyList() : topListeners;
|
||||
this.topSources = topSources == null ? Collections.emptyList() : topSources;
|
||||
this.skipVotes = skipVotes == null ? Collections.emptyList() : skipVotes;
|
||||
this.skipActions = skipActions == null ? Collections.emptyList() : skipActions;
|
||||
}
|
||||
|
||||
public StatsTimeRange getRange()
|
||||
{
|
||||
return range;
|
||||
}
|
||||
|
||||
public String getSource()
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
public long getTotalPlays()
|
||||
{
|
||||
return totalPlays;
|
||||
}
|
||||
|
||||
public long getTotalListeningMillis()
|
||||
{
|
||||
return totalListeningMillis;
|
||||
}
|
||||
|
||||
public List<StatsRow> getTopTracks()
|
||||
{
|
||||
return topTracks;
|
||||
}
|
||||
|
||||
public List<StatsRow> getTopRequesters()
|
||||
{
|
||||
return topRequesters;
|
||||
}
|
||||
|
||||
public List<StatsRow> getTopListeners()
|
||||
{
|
||||
return topListeners;
|
||||
}
|
||||
|
||||
public List<StatsRow> getTopSources()
|
||||
{
|
||||
return topSources;
|
||||
}
|
||||
|
||||
public List<StatsRow> getSkipVotes()
|
||||
{
|
||||
return skipVotes;
|
||||
}
|
||||
|
||||
public List<StatsRow> getSkipActions()
|
||||
{
|
||||
return skipActions;
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return totalPlays == 0L && totalListeningMillis == 0L
|
||||
&& topTracks.isEmpty() && topRequesters.isEmpty() && topListeners.isEmpty()
|
||||
&& topSources.isEmpty() && skipVotes.isEmpty() && skipActions.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2026
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.datalog;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
public class StatsTimeRange
|
||||
{
|
||||
private final String key;
|
||||
private final String label;
|
||||
private final Long startMillis;
|
||||
private final Long endMillis;
|
||||
|
||||
private StatsTimeRange(String key, String label, Long startMillis, Long endMillis)
|
||||
{
|
||||
this.key = key;
|
||||
this.label = label;
|
||||
this.startMillis = startMillis;
|
||||
this.endMillis = endMillis;
|
||||
}
|
||||
|
||||
public static StatsTimeRange all()
|
||||
{
|
||||
return new StatsTimeRange("all", "all time", null, null);
|
||||
}
|
||||
|
||||
public static StatsTimeRange parse(String token)
|
||||
{
|
||||
if(token == null || token.trim().isEmpty())
|
||||
return all();
|
||||
String normalized = token.trim().toLowerCase();
|
||||
ZonedDateTime now = ZonedDateTime.now();
|
||||
ZonedDateTime start;
|
||||
if(normalized.matches("\\d+[dwmy]"))
|
||||
{
|
||||
int amount = Integer.parseInt(normalized.substring(0, normalized.length() - 1));
|
||||
char unit = normalized.charAt(normalized.length() - 1);
|
||||
switch(unit)
|
||||
{
|
||||
case 'd':
|
||||
start = now.minusDays(amount);
|
||||
return new StatsTimeRange(normalized, "last " + amount + " day" + (amount == 1 ? "" : "s"), start.toInstant().toEpochMilli(), null);
|
||||
case 'w':
|
||||
start = now.minusWeeks(amount);
|
||||
return new StatsTimeRange(normalized, "last " + amount + " week" + (amount == 1 ? "" : "s"), start.toInstant().toEpochMilli(), null);
|
||||
case 'm':
|
||||
start = now.minusMonths(amount);
|
||||
return new StatsTimeRange(normalized, "last " + amount + " month" + (amount == 1 ? "" : "s"), start.toInstant().toEpochMilli(), null);
|
||||
case 'y':
|
||||
start = now.minusYears(amount);
|
||||
return new StatsTimeRange(normalized, "last " + amount + " year" + (amount == 1 ? "" : "s"), start.toInstant().toEpochMilli(), null);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch(normalized)
|
||||
{
|
||||
case "today":
|
||||
case "day":
|
||||
start = now.toLocalDate().atStartOfDay(now.getZone());
|
||||
return new StatsTimeRange("today", "today", start.toInstant().toEpochMilli(), null);
|
||||
case "week":
|
||||
case "thisweek":
|
||||
LocalDate monday = now.toLocalDate().with(DayOfWeek.MONDAY);
|
||||
start = monday.atStartOfDay(now.getZone());
|
||||
return new StatsTimeRange("week", "this week", start.toInstant().toEpochMilli(), null);
|
||||
case "month":
|
||||
case "thismonth":
|
||||
start = now.withDayOfMonth(1).toLocalDate().atStartOfDay(now.getZone());
|
||||
return new StatsTimeRange("month", "this month", start.toInstant().toEpochMilli(), null);
|
||||
case "year":
|
||||
case "thisyear":
|
||||
start = now.withDayOfYear(1).toLocalDate().atStartOfDay(now.getZone());
|
||||
return new StatsTimeRange("year", "this year", start.toInstant().toEpochMilli(), null);
|
||||
case "all":
|
||||
case "alltime":
|
||||
return all();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static StatsTimeRange previousMonth()
|
||||
{
|
||||
ZonedDateTime now = ZonedDateTime.now();
|
||||
ZonedDateTime start = now.minusMonths(1).withDayOfMonth(1).toLocalDate().atStartOfDay(now.getZone());
|
||||
ZonedDateTime end = start.plusMonths(1);
|
||||
return new StatsTimeRange("month", start.getMonth() + " " + start.getYear(), start.toInstant().toEpochMilli(), end.toInstant().toEpochMilli());
|
||||
}
|
||||
|
||||
public static StatsTimeRange month(int year, int month)
|
||||
{
|
||||
if(month < 1 || month > 12)
|
||||
return null;
|
||||
ZonedDateTime start = ZonedDateTime.of(year, month, 1, 0, 0, 0, 0, ZoneId.systemDefault());
|
||||
ZonedDateTime end = start.plusMonths(1);
|
||||
return new StatsTimeRange(String.format("%04d-%02d", year, month), Month.of(month) + " " + year,
|
||||
start.toInstant().toEpochMilli(), end.toInstant().toEpochMilli());
|
||||
}
|
||||
|
||||
public static StatsTimeRange previousYear()
|
||||
{
|
||||
ZonedDateTime now = ZonedDateTime.now();
|
||||
ZonedDateTime start = ZonedDateTime.of(now.getYear() - 1, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault());
|
||||
ZonedDateTime end = start.plusYears(1);
|
||||
return new StatsTimeRange("year", Integer.toString(start.getYear()), start.toInstant().toEpochMilli(), end.toInstant().toEpochMilli());
|
||||
}
|
||||
|
||||
public static StatsTimeRange year(int year)
|
||||
{
|
||||
ZonedDateTime start = ZonedDateTime.of(year, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault());
|
||||
ZonedDateTime end = start.plusYears(1);
|
||||
return new StatsTimeRange(Integer.toString(year), Integer.toString(year), start.toInstant().toEpochMilli(), end.toInstant().toEpochMilli());
|
||||
}
|
||||
|
||||
public String getKey()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
public Long getStartMillis()
|
||||
{
|
||||
return startMillis;
|
||||
}
|
||||
|
||||
public Long getEndMillis()
|
||||
{
|
||||
return endMillis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.entities;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
*/
|
||||
public class Pair<K,V>
|
||||
{
|
||||
private final K key;
|
||||
private final V value;
|
||||
|
||||
public Pair(K key, V value)
|
||||
{
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public K getKey()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
public V getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2018 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.entities;
|
||||
|
||||
import java.util.Scanner;
|
||||
import javax.swing.JOptionPane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class Prompt
|
||||
{
|
||||
private final String title;
|
||||
private final String noguiMessage;
|
||||
|
||||
private boolean nogui;
|
||||
private boolean noprompt;
|
||||
private Scanner scanner;
|
||||
|
||||
public Prompt(String title)
|
||||
{
|
||||
this(title, null);
|
||||
}
|
||||
|
||||
public Prompt(String title, String noguiMessage)
|
||||
{
|
||||
this(title, noguiMessage, "true".equalsIgnoreCase(System.getProperty("nogui")), "true".equalsIgnoreCase(System.getProperty("noprompt")));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
return nogui;
|
||||
}
|
||||
|
||||
public void alert(Level level, String context, String message)
|
||||
{
|
||||
if(nogui)
|
||||
{
|
||||
Logger log = LoggerFactory.getLogger(context);
|
||||
switch(level)
|
||||
{
|
||||
case INFO:
|
||||
log.info(message);
|
||||
break;
|
||||
case WARNING:
|
||||
log.warn(message);
|
||||
break;
|
||||
case ERROR:
|
||||
log.error(message);
|
||||
break;
|
||||
default:
|
||||
log.info(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
int option = 0;
|
||||
switch(level)
|
||||
{
|
||||
case INFO:
|
||||
option = JOptionPane.INFORMATION_MESSAGE;
|
||||
break;
|
||||
case WARNING:
|
||||
option = JOptionPane.WARNING_MESSAGE;
|
||||
break;
|
||||
case ERROR:
|
||||
option = JOptionPane.ERROR_MESSAGE;
|
||||
break;
|
||||
default:
|
||||
option = JOptionPane.PLAIN_MESSAGE;
|
||||
break;
|
||||
}
|
||||
JOptionPane.showMessageDialog(null, "<html><body><p style='width: 400px;'>"+message, title, option);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
nogui = true;
|
||||
alert(Level.WARNING, context, noguiMessage);
|
||||
alert(level, context, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String prompt(String content)
|
||||
{
|
||||
if(noprompt)
|
||||
return null;
|
||||
if(nogui)
|
||||
{
|
||||
if(scanner==null)
|
||||
scanner = new Scanner(System.in);
|
||||
try
|
||||
{
|
||||
System.out.println(content);
|
||||
if(scanner.hasNextLine())
|
||||
return scanner.nextLine();
|
||||
return null;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
alert(Level.ERROR, title, "Unable to read input from command line.");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return JOptionPane.showInputDialog(null, content, title, JOptionPane.QUESTION_MESSAGE);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
nogui = true;
|
||||
alert(Level.WARNING, title, noguiMessage);
|
||||
return prompt(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Level
|
||||
{
|
||||
INFO, WARNING, ERROR;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +1,73 @@
|
||||
/*
|
||||
* Copyright 2016 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.gui;
|
||||
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.WindowConstants;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class GUI extends JFrame {
|
||||
|
||||
private final ConsolePanel console;
|
||||
private final GuildsPanel guilds;
|
||||
private final Bot bot;
|
||||
|
||||
public GUI(Bot bot) {
|
||||
super();
|
||||
this.bot = bot;
|
||||
console = new ConsolePanel();
|
||||
guilds = new GuildsPanel(bot);
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
setTitle("JMusicBot");
|
||||
JTabbedPane tabs = new JTabbedPane();
|
||||
//tabs.add("Guilds", guilds);
|
||||
tabs.add("Console", console);
|
||||
getContentPane().add(tabs);
|
||||
pack();
|
||||
setLocationRelativeTo(null);
|
||||
setVisible(true);
|
||||
addWindowListener(new WindowListener() {
|
||||
@Override public void windowOpened(WindowEvent e) {}
|
||||
@Override public void windowClosing(WindowEvent e) {bot.shutdown();}
|
||||
@Override public void windowClosed(WindowEvent e) {}
|
||||
@Override public void windowIconified(WindowEvent e) {}
|
||||
@Override public void windowDeiconified(WindowEvent e) {}
|
||||
@Override public void windowActivated(WindowEvent e) {}
|
||||
@Override public void windowDeactivated(WindowEvent e) {}
|
||||
});
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2016 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.gui;
|
||||
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.WindowConstants;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class GUI extends JFrame
|
||||
{
|
||||
private final ConsolePanel console;
|
||||
private final Bot bot;
|
||||
|
||||
public GUI(Bot bot)
|
||||
{
|
||||
super();
|
||||
this.bot = bot;
|
||||
console = new ConsolePanel();
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
setTitle("JMusicBot");
|
||||
JTabbedPane tabs = new JTabbedPane();
|
||||
tabs.add("Console", console);
|
||||
getContentPane().add(tabs);
|
||||
pack();
|
||||
setLocationRelativeTo(null);
|
||||
setVisible(true);
|
||||
addWindowListener(new WindowListener()
|
||||
{
|
||||
@Override public void windowOpened(WindowEvent e) { /* unused */ }
|
||||
@Override public void windowClosing(WindowEvent e)
|
||||
{
|
||||
try
|
||||
{
|
||||
bot.shutdown();
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
@Override public void windowClosed(WindowEvent e) { /* unused */ }
|
||||
@Override public void windowIconified(WindowEvent e) { /* unused */ }
|
||||
@Override public void windowDeiconified(WindowEvent e) { /* unused */ }
|
||||
@Override public void windowActivated(WindowEvent e) { /* unused */ }
|
||||
@Override public void windowDeactivated(WindowEvent e) { /* unused */ }
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 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.gui;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.util.List;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.entities.Guild;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class GuildsPanel extends JPanel {
|
||||
|
||||
private final Bot bot;
|
||||
private final JList guildList;
|
||||
private final JTextArea guildQueue;
|
||||
private int index = -1;
|
||||
|
||||
public GuildsPanel(Bot bot)
|
||||
{
|
||||
super();
|
||||
super.setLayout(new GridBagLayout());
|
||||
this.bot = bot;
|
||||
|
||||
guildList = new JList();
|
||||
guildQueue = new JTextArea();
|
||||
guildList.setModel(new DefaultListModel());
|
||||
guildList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
guildList.setFixedCellHeight(20);
|
||||
guildList.setPreferredSize(new Dimension(100,300));
|
||||
guildQueue.setPreferredSize(new Dimension(300,300));
|
||||
guildQueue.setEditable(false);
|
||||
JScrollPane pane = new JScrollPane();
|
||||
JScrollPane pane2 = new JScrollPane();
|
||||
pane.setViewportView(guildList);
|
||||
pane2.setViewportView(guildQueue);
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.gridx = 0;
|
||||
c.gridwidth = 1;
|
||||
super.add(pane, c);
|
||||
c.gridx = 1;
|
||||
c.gridwidth = 3;
|
||||
super.add(pane2, c);
|
||||
//bot.registerPanel(this);
|
||||
guildList.addListSelectionListener((ListSelectionEvent e) -> {
|
||||
index = guildList.getSelectedIndex();
|
||||
//bot.updatePanel();
|
||||
});
|
||||
}
|
||||
|
||||
public void updateList(List<Guild> guilds)
|
||||
{
|
||||
String[] strs = new String[guilds.size()];
|
||||
for(int i=0; i<guilds.size(); i++)
|
||||
strs[i] = guilds.get(i).getName();
|
||||
guildList.setListData(strs);
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return guildList.getSelectedIndex();
|
||||
}
|
||||
|
||||
public void updatePanel(AudioHandler handler) {
|
||||
StringBuilder builder = new StringBuilder("Now Playing: ");
|
||||
if(handler==null || handler.getCurrentTrack()==null)
|
||||
{
|
||||
builder.append("nothing");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.append(handler.getCurrentTrack().getTrack().getInfo().title)
|
||||
.append(" [")
|
||||
.append(FormatUtil.formatTime(handler.getCurrentTrack().getTrack().getDuration()))
|
||||
.append("]\n");
|
||||
for(int i=0; i<handler.getQueue().size(); i++)
|
||||
builder.append("\n").append(i+1).append(". ").append(handler.getQueue().get(i).getTrack().getInfo().title);
|
||||
}
|
||||
guildQueue.setText(builder.toString());
|
||||
guildQueue.updateUI();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,147 +1,157 @@
|
||||
/*
|
||||
* Copyright 2017 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.gui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import javax.swing.*;
|
||||
/**
|
||||
*
|
||||
* @author Lawrence Dol
|
||||
*/
|
||||
public class TextAreaOutputStream extends OutputStream {
|
||||
|
||||
// *************************************************************************************************
|
||||
// INSTANCE MEMBERS
|
||||
// *************************************************************************************************
|
||||
|
||||
private byte[] oneByte; // array for write(int val);
|
||||
private Appender appender; // most recent action
|
||||
|
||||
public TextAreaOutputStream(JTextArea txtara) {
|
||||
this(txtara,1000);
|
||||
}
|
||||
|
||||
public TextAreaOutputStream(JTextArea txtara, int maxlin) {
|
||||
if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); }
|
||||
oneByte=new byte[1];
|
||||
appender=new Appender(txtara,maxlin);
|
||||
}
|
||||
|
||||
/** Clear the current console text area. */
|
||||
public synchronized void clear() {
|
||||
if(appender!=null) { appender.clear(); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
appender=null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int val) {
|
||||
oneByte[0]=(byte)val;
|
||||
write(oneByte,0,1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] ba) {
|
||||
write(ba,0,ba.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] ba,int str,int len) {
|
||||
if(appender!=null) { appender.append(bytesToString(ba,str,len)); }
|
||||
}
|
||||
|
||||
//@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING")
|
||||
static private String bytesToString(byte[] ba, int str, int len) {
|
||||
try { return new String(ba,str,len,"UTF-8"); } catch(UnsupportedEncodingException thr) { return new String(ba,str,len); } // all JVMs are required to support UTF-8
|
||||
}
|
||||
|
||||
// *************************************************************************************************
|
||||
// STATIC MEMBERS
|
||||
// *************************************************************************************************
|
||||
|
||||
static class Appender
|
||||
implements Runnable
|
||||
{
|
||||
private final JTextArea textArea;
|
||||
private final int maxLines; // maximum lines allowed in text area
|
||||
private final LinkedList<Integer> lengths; // length of lines within text area
|
||||
private final List<String> values; // values waiting to be appended
|
||||
|
||||
private int curLength; // length of current line
|
||||
private boolean clear;
|
||||
private boolean queue;
|
||||
|
||||
Appender(JTextArea txtara, int maxlin) {
|
||||
textArea =txtara;
|
||||
maxLines =maxlin;
|
||||
lengths =new LinkedList<>();
|
||||
values =new ArrayList<>();
|
||||
|
||||
curLength=0;
|
||||
clear =false;
|
||||
queue =true;
|
||||
}
|
||||
|
||||
synchronized void append(String val) {
|
||||
values.add(val);
|
||||
if(queue) { queue=false; EventQueue.invokeLater(this); }
|
||||
}
|
||||
|
||||
synchronized void clear() {
|
||||
clear=true;
|
||||
curLength=0;
|
||||
lengths.clear();
|
||||
values.clear();
|
||||
if(queue) { queue=false; EventQueue.invokeLater(this); }
|
||||
}
|
||||
|
||||
// MUST BE THE ONLY METHOD THAT TOUCHES textArea!
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
if(clear) { textArea.setText(""); }
|
||||
values.stream().map((val) -> {
|
||||
curLength+=val.length();
|
||||
return val;
|
||||
}).map((val) -> {
|
||||
if(val.endsWith(EOL1) || val.endsWith(EOL2)) {
|
||||
if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); }
|
||||
lengths.addLast(curLength);
|
||||
curLength=0;
|
||||
}
|
||||
return val;
|
||||
}).forEach((val) -> {
|
||||
textArea.append(val);
|
||||
});
|
||||
values.clear();
|
||||
clear =false;
|
||||
queue =true;
|
||||
}
|
||||
|
||||
static private final String EOL1="\n";
|
||||
static private final String EOL2=System.getProperty("line.separator",EOL1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright 2017 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.gui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import javax.swing.*;
|
||||
/**
|
||||
*
|
||||
* @author Lawrence Dol
|
||||
*/
|
||||
public class TextAreaOutputStream extends OutputStream {
|
||||
|
||||
// *************************************************************************************************
|
||||
// INSTANCE MEMBERS
|
||||
// *************************************************************************************************
|
||||
|
||||
private byte[] oneByte; // array for write(int val);
|
||||
private Appender appender; // most recent action
|
||||
|
||||
public TextAreaOutputStream(JTextArea txtara) {
|
||||
this(txtara,1000);
|
||||
}
|
||||
|
||||
public TextAreaOutputStream(JTextArea txtara, int maxlin) {
|
||||
if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); }
|
||||
oneByte=new byte[1];
|
||||
appender=new Appender(txtara,maxlin);
|
||||
}
|
||||
|
||||
/** Clear the current console text area. */
|
||||
public synchronized void clear() {
|
||||
if(appender!=null) { appender.clear(); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
appender=null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int val) {
|
||||
oneByte[0]=(byte)val;
|
||||
write(oneByte,0,1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] ba) {
|
||||
write(ba,0,ba.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] ba,int str,int len) {
|
||||
if(appender!=null) { appender.append(bytesToString(ba,str,len)); }
|
||||
}
|
||||
|
||||
//@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING")
|
||||
static private String bytesToString(byte[] ba, int str, int len) {
|
||||
try {
|
||||
return new String(ba,str,len,"UTF-8");
|
||||
} catch(UnsupportedEncodingException thr) {
|
||||
return new String(ba,str,len);
|
||||
} // all JVMs are required to support UTF-8
|
||||
}
|
||||
|
||||
// *************************************************************************************************
|
||||
// STATIC MEMBERS
|
||||
// *************************************************************************************************
|
||||
|
||||
static class Appender
|
||||
implements Runnable
|
||||
{
|
||||
static private final String EOL1="\n";
|
||||
static private final String EOL2=System.getProperty("line.separator",EOL1);
|
||||
|
||||
private final JTextArea textArea;
|
||||
private final int maxLines; // maximum lines allowed in text area
|
||||
private final LinkedList<Integer> lengths; // length of lines within text area
|
||||
private final List<String> values; // values waiting to be appended
|
||||
|
||||
private int curLength; // length of current line
|
||||
private boolean clear;
|
||||
private boolean queue;
|
||||
|
||||
Appender(JTextArea txtara, int maxlin) {
|
||||
textArea =txtara;
|
||||
maxLines =maxlin;
|
||||
lengths =new LinkedList<>();
|
||||
values =new ArrayList<>();
|
||||
|
||||
curLength=0;
|
||||
clear =false;
|
||||
queue =true;
|
||||
}
|
||||
|
||||
private synchronized void append(String val) {
|
||||
values.add(val);
|
||||
if(queue) {
|
||||
queue=false;
|
||||
EventQueue.invokeLater(this);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void clear() {
|
||||
clear=true;
|
||||
curLength=0;
|
||||
lengths.clear();
|
||||
values.clear();
|
||||
if(queue) {
|
||||
queue=false;
|
||||
EventQueue.invokeLater(this);
|
||||
}
|
||||
}
|
||||
|
||||
// MUST BE THE ONLY METHOD THAT TOUCHES textArea!
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
if(clear) { textArea.setText(""); }
|
||||
values.stream().map((val) -> {
|
||||
curLength+=val.length();
|
||||
return val;
|
||||
}).map((val) -> {
|
||||
if(val.endsWith(EOL1) || val.endsWith(EOL2)) {
|
||||
if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); }
|
||||
lengths.addLast(curLength);
|
||||
curLength=0;
|
||||
}
|
||||
return val;
|
||||
}).forEach((val) -> {
|
||||
textArea.append(val);
|
||||
});
|
||||
values.clear();
|
||||
clear =false;
|
||||
queue =true;
|
||||
}
|
||||
}
|
||||
} /* END PUBLIC CLASS */
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.playlist;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class Playlist {
|
||||
|
||||
private final String name;
|
||||
private final List<String> items;
|
||||
private List<AudioTrack> tracks;
|
||||
private List<PlaylistLoadError> errors;
|
||||
|
||||
private Playlist(String name, List<String> items)
|
||||
{
|
||||
this.name = name;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public void loadTracks(AudioPlayerManager manager, Runnable callback)
|
||||
{
|
||||
if(tracks==null)
|
||||
{
|
||||
tracks = new LinkedList<>();
|
||||
errors = new LinkedList<>();
|
||||
for(int i=0; i<items.size(); i++)
|
||||
{
|
||||
boolean last = i+1==items.size();
|
||||
int index = i;
|
||||
manager.loadItemOrdered(name, items.get(i), new AudioLoadResultHandler() {
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack at) {
|
||||
tracks.add(at);
|
||||
if(last && callback!=null)
|
||||
callback.run();
|
||||
}
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist ap) {
|
||||
if(ap.isSearchResult())
|
||||
tracks.add(ap.getTracks().get(0));
|
||||
else if(ap.getSelectedTrack()!=null)
|
||||
tracks.add(ap.getSelectedTrack());
|
||||
else
|
||||
tracks.addAll(ap.getTracks());
|
||||
if(last && callback!=null)
|
||||
callback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches() {
|
||||
errors.add(new PlaylistLoadError(index, items.get(index), "No matches found."));
|
||||
if(last && callback!=null)
|
||||
callback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException fe) {
|
||||
errors.add(new PlaylistLoadError(index, items.get(index), "Failed to load track: "+fe.getLocalizedMessage()));
|
||||
if(last && callback!=null)
|
||||
callback.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<String> getItems()
|
||||
{
|
||||
return items;
|
||||
}
|
||||
|
||||
public List<AudioTrack> getTracks()
|
||||
{
|
||||
return tracks;
|
||||
}
|
||||
|
||||
public List<PlaylistLoadError> getErrors()
|
||||
{
|
||||
return errors;
|
||||
}
|
||||
|
||||
public static void createFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.createDirectory(Paths.get("Playlists"));
|
||||
} catch (IOException ex)
|
||||
{}
|
||||
}
|
||||
|
||||
public static boolean folderExists()
|
||||
{
|
||||
return Files.exists(Paths.get("Playlists"));
|
||||
}
|
||||
|
||||
public static List<String> getPlaylists()
|
||||
{
|
||||
if(folderExists())
|
||||
{
|
||||
File folder = new File("Playlists");
|
||||
return Arrays.asList(folder.listFiles((pathname) -> pathname.getName().endsWith(".txt")))
|
||||
.stream().map(f -> f.getName().substring(0,f.getName().length()-4)).collect(Collectors.toList());
|
||||
}
|
||||
else
|
||||
{
|
||||
createFolder();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Playlist loadPlaylist(String name)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(folderExists())
|
||||
{
|
||||
return new Playlist(name, Files.readAllLines(Paths.get("Playlists"+File.separator+name+".txt"))
|
||||
.stream()
|
||||
.map((str) -> str.trim())
|
||||
.filter((s) -> (!s.isEmpty() && !s.startsWith("#") && !s.startsWith("//")))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
else
|
||||
{
|
||||
createFolder();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlaylistLoadError {
|
||||
private final int number;
|
||||
private final String item;
|
||||
private final String reason;
|
||||
|
||||
private PlaylistLoadError(int number, String item, String reason)
|
||||
{
|
||||
this.number = number;
|
||||
this.item = item;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return number;
|
||||
}
|
||||
|
||||
public String getItem()
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
public String getReason()
|
||||
{
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright 2018 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.playlist;
|
||||
|
||||
import com.jagrosh.jmusicbot.BotConfig;
|
||||
import com.jagrosh.jmusicbot.utils.OtherUtil;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (john.a.grosh@gmail.com)
|
||||
*/
|
||||
public class PlaylistLoader
|
||||
{
|
||||
private final BotConfig config;
|
||||
|
||||
public PlaylistLoader(BotConfig config)
|
||||
{
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public List<String> getPlaylistNames()
|
||||
{
|
||||
if(folderExists())
|
||||
{
|
||||
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());
|
||||
}
|
||||
else
|
||||
{
|
||||
createFolder();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public void createFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.createDirectory(OtherUtil.getPath(config.getPlaylistsFolder()));
|
||||
}
|
||||
catch (IOException ignore) {}
|
||||
}
|
||||
|
||||
public boolean folderExists()
|
||||
{
|
||||
return Files.exists(OtherUtil.getPath(config.getPlaylistsFolder()));
|
||||
}
|
||||
|
||||
public void createPlaylist(String name) throws IOException
|
||||
{
|
||||
Files.createFile(OtherUtil.getPath(config.getPlaylistsFolder()+File.separator+name+".txt"));
|
||||
}
|
||||
|
||||
public void deletePlaylist(String name) throws IOException
|
||||
{
|
||||
Files.delete(OtherUtil.getPath(config.getPlaylistsFolder()+File.separator+name+".txt"));
|
||||
}
|
||||
|
||||
public void writePlaylist(String name, String text) throws IOException
|
||||
{
|
||||
Files.write(OtherUtil.getPath(config.getPlaylistsFolder()+File.separator+name+".txt"), text.trim().getBytes());
|
||||
}
|
||||
|
||||
public Playlist getPlaylist(String name)
|
||||
{
|
||||
if(!getPlaylistNames().contains(name))
|
||||
return null;
|
||||
try
|
||||
{
|
||||
if(folderExists())
|
||||
{
|
||||
boolean[] shuffle = {false};
|
||||
List<String> list = new ArrayList<>();
|
||||
Files.readAllLines(OtherUtil.getPath(config.getPlaylistsFolder()+File.separator+name+".txt")).forEach(str ->
|
||||
{
|
||||
String s = str.trim();
|
||||
if(s.isEmpty())
|
||||
return;
|
||||
if(s.startsWith("#") || s.startsWith("//"))
|
||||
{
|
||||
s = s.replaceAll("\\s+", "");
|
||||
if(s.equalsIgnoreCase("#shuffle") || s.equalsIgnoreCase("//shuffle"))
|
||||
shuffle[0]=true;
|
||||
}
|
||||
else
|
||||
list.add(s);
|
||||
});
|
||||
if(shuffle[0])
|
||||
shuffle(list);
|
||||
return new Playlist(name, list, shuffle[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
createFolder();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static <T> void shuffle(List<T> list)
|
||||
{
|
||||
for(int first =0; first<list.size(); first++)
|
||||
{
|
||||
int second = (int)(Math.random()*list.size());
|
||||
T tmp = list.get(first);
|
||||
list.set(first, list.get(second));
|
||||
list.set(second, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Playlist
|
||||
{
|
||||
private final String name;
|
||||
private final List<String> items;
|
||||
private final boolean shuffle;
|
||||
private final List<AudioTrack> tracks = new LinkedList<>();
|
||||
private final List<PlaylistLoadError> errors = new LinkedList<>();
|
||||
private boolean loaded = false;
|
||||
|
||||
private Playlist(String name, List<String> items, boolean shuffle)
|
||||
{
|
||||
this.name = name;
|
||||
this.items = items;
|
||||
this.shuffle = shuffle;
|
||||
}
|
||||
|
||||
public void loadTracks(AudioPlayerManager manager, Consumer<AudioTrack> consumer, Runnable callback)
|
||||
{
|
||||
if(loaded)
|
||||
return;
|
||||
loaded = true;
|
||||
for(int i=0; i<items.size(); i++)
|
||||
{
|
||||
boolean last = i+1 == items.size();
|
||||
int index = i;
|
||||
manager.loadItemOrdered(name, items.get(i), new AudioLoadResultHandler()
|
||||
{
|
||||
private void done()
|
||||
{
|
||||
if(last)
|
||||
{
|
||||
if(shuffle)
|
||||
shuffleTracks();
|
||||
if(callback != null)
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack at)
|
||||
{
|
||||
if(config.isTooLong(at))
|
||||
errors.add(new PlaylistLoadError(index, items.get(index), "This track is longer than the allowed maximum"));
|
||||
else
|
||||
{
|
||||
at.setUserData(0L);
|
||||
tracks.add(at);
|
||||
consumer.accept(at);
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist ap)
|
||||
{
|
||||
if(ap.isSearchResult())
|
||||
{
|
||||
trackLoaded(ap.getTracks().get(0));
|
||||
}
|
||||
else if(ap.getSelectedTrack()!=null)
|
||||
{
|
||||
trackLoaded(ap.getSelectedTrack());
|
||||
}
|
||||
else
|
||||
{
|
||||
List<AudioTrack> loaded = new ArrayList<>(ap.getTracks());
|
||||
if(shuffle)
|
||||
for(int first =0; first<loaded.size(); first++)
|
||||
{
|
||||
int second = (int)(Math.random()*loaded.size());
|
||||
AudioTrack tmp = loaded.get(first);
|
||||
loaded.set(first, loaded.get(second));
|
||||
loaded.set(second, tmp);
|
||||
}
|
||||
loaded.removeIf(track -> config.isTooLong(track));
|
||||
loaded.forEach(at -> at.setUserData(0L));
|
||||
tracks.addAll(loaded);
|
||||
loaded.forEach(at -> consumer.accept(at));
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches()
|
||||
{
|
||||
errors.add(new PlaylistLoadError(index, items.get(index), "No matches found."));
|
||||
done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException fe)
|
||||
{
|
||||
errors.add(new PlaylistLoadError(index, items.get(index), "Failed to load track: "+fe.getLocalizedMessage()));
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void shuffleTracks()
|
||||
{
|
||||
shuffle(tracks);
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<String> getItems()
|
||||
{
|
||||
return items;
|
||||
}
|
||||
|
||||
public List<AudioTrack> getTracks()
|
||||
{
|
||||
return tracks;
|
||||
}
|
||||
|
||||
public List<PlaylistLoadError> getErrors()
|
||||
{
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlaylistLoadError
|
||||
{
|
||||
private final int number;
|
||||
private final String item;
|
||||
private final String reason;
|
||||
|
||||
private PlaylistLoadError(int number, String item, String reason)
|
||||
{
|
||||
this.number = number;
|
||||
this.item = item;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return number;
|
||||
}
|
||||
|
||||
public String getItem()
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
public String getReason()
|
||||
{
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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().equals(item.getIdentifier()))
|
||||
if(list.get(lastIndex).getIdentifier() == item.getIdentifier())
|
||||
break;
|
||||
lastIndex++;
|
||||
set.clear();
|
||||
@@ -46,78 +50,5 @@ public class FairQueue<T extends Queueable> {
|
||||
list.add(lastIndex, item);
|
||||
return lastIndex;
|
||||
}
|
||||
|
||||
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(String identifier)
|
||||
{
|
||||
int count = 0;
|
||||
for(int i=list.size()-1; i>=0; i--)
|
||||
{
|
||||
if(list.get(i).getIdentifier().equals(identifier))
|
||||
{
|
||||
list.remove(i);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
list.clear();
|
||||
}
|
||||
|
||||
public int shuffle(String identifier)
|
||||
{
|
||||
List<Integer> iset = new ArrayList<>();
|
||||
for(int i=0; i<list.size(); i++)
|
||||
{
|
||||
if(list.get(i).getIdentifier().equals(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);
|
||||
}
|
||||
|
||||
}
|
||||
Loaded 100 of 124 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user