mirror of
https://github.com/chrisb09/MusicBot.git
synced 2026-11-05 17:02:35 -05:00
Compare commits
309
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at j@gro.sh. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: jagrosh
|
||||
patreon: jagrosh
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Bug Report
|
||||
description: Report a bug or unexpected behavior
|
||||
title: "[Bug Report] Title Here "
|
||||
labels: []
|
||||
assignees: []
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Bug Description
|
||||
description: Please provide a clear and concise description of what the bug is.
|
||||
placeholder: The bug is...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: Provide steps you can take to reliably reproduce this bug
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected Result
|
||||
description: What did you expect to happen when this bug occurred?
|
||||
placeholder: I expected...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: debug
|
||||
attributes:
|
||||
label: Debug Output
|
||||
description: Please run the `debug` command and paste it here or upload it as an attachment. If you cannot run the command, please provide information about your system such as operating system version, JMusicBot version, etc.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-info
|
||||
attributes:
|
||||
label: Additional Info
|
||||
description: Any additional info that you think might be useful or relevant to this bug
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Before submitting your issue, please affirm the following
|
||||
options:
|
||||
- label: I have looked for information about this [within the documentation](https://jmusicbot.com/)
|
||||
required: true
|
||||
- label: I have searched for similar issues [on the issues page](https://github.com/jagrosh/MusicBot/issues?q=is%3Aissue)
|
||||
required: true
|
||||
- label: I am using an official release from [the releases page](https://github.com/jagrosh/MusicBot/releases)
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: JMusicBot Version
|
||||
description: Which version of JMusicBot are you running?
|
||||
validations:
|
||||
required: true
|
||||
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Question
|
||||
url: https://github.com/jagrosh/MusicBot/discussions/categories/questions
|
||||
about: For general questions, please check the Discussions
|
||||
- name: Troubleshooting
|
||||
url: https://github.com/jagrosh/MusicBot/discussions/categories/troubleshooting
|
||||
about: For troubleshooting help, please check the Discussions
|
||||
@@ -0,0 +1,52 @@
|
||||
name: Feature Request
|
||||
description: Suggest an idea for this project
|
||||
title: "[Feature Request] Title Here"
|
||||
labels: []
|
||||
assignees: []
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
description: Please provide a clear and concise description of what the problem is.
|
||||
placeholder: A common problem users face is...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: What is your ideal solution to the problem?
|
||||
description: Describe a clear and concise description of what you want to happen.
|
||||
placeholder: My solution to the above problem is...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: procedure
|
||||
attributes:
|
||||
label: How would this feature be used?
|
||||
description: Describe the basic flow/steps of using this feature.
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-info
|
||||
attributes:
|
||||
label: Additional Info
|
||||
description: Any additional info that you think might be useful or relevant to this feature request
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Before submitting your issue, please affirm the following
|
||||
options:
|
||||
- label: I have checked [the documentation](https://jmusicbot.com/) to make sure this feature doesn't already exist
|
||||
required: true
|
||||
- label: I have searched for [similar feature requests](https://github.com/jagrosh/MusicBot/labels/enhancement)
|
||||
required: true
|
||||
- label: I have confirmed that my requests is **not** on the [list of things that will not be added](https://jmusicbot.com/things-that-wont-be-added)
|
||||
required: true
|
||||
- label: "I am running the latest version of the bot: "
|
||||
required: true
|
||||
@@ -0,0 +1,14 @@
|
||||
### This pull request...
|
||||
- [ ] Fixes a bug
|
||||
- [ ] Introduces a new feature
|
||||
- [ ] Improves an existing feature
|
||||
- [ ] Boosts code quality or performance
|
||||
|
||||
### Description
|
||||
Replace this with a description of the pull request
|
||||
|
||||
### Purpose
|
||||
Replace this with a description of the problem or use-case
|
||||
|
||||
### Relevant Issue(s)
|
||||
This PR closes issue #...
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Build and Test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Build and Test with Maven
|
||||
run: mvn --batch-mode --update-snapshots verify
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Make Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_number:
|
||||
description: 'Version Number'
|
||||
required: true
|
||||
type: string
|
||||
info:
|
||||
description: 'Description of this Release'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build_jar:
|
||||
name: Build Jar
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Set Version
|
||||
uses: datamonsters/replace-action@v2
|
||||
with:
|
||||
files: 'pom.xml'
|
||||
replacements: 'Snapshot=${{ github.event.inputs.version_number }}'
|
||||
- name: Build with Maven
|
||||
run: mvn --batch-mode --update-snapshots verify
|
||||
- name: Rename jar
|
||||
run: mv target/*-All.jar JMusicBot-${{ github.event.inputs.version_number }}.jar
|
||||
- name: Upload jar
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: jar
|
||||
path: JMusicBot-${{ github.event.inputs.version_number }}.jar
|
||||
if-no-files-found: error
|
||||
create_release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: build_jar
|
||||
steps:
|
||||
- name: Download a Build Artifact
|
||||
uses: actions/download-artifact@v3.0.0
|
||||
with:
|
||||
name: jar
|
||||
path: .
|
||||
- name: Show Artifacts
|
||||
run: ls -R
|
||||
- name: Create Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.event.inputs.version_number }}
|
||||
name: Version ${{ github.event.inputs.version_number }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
artifacts: "*.jar"
|
||||
body: |
|
||||
${{ github.event.inputs.info }}
|
||||
|
||||
---
|
||||
### Setup
|
||||
https://jmusicbot.com/setup
|
||||
https://jmusicbot.com/config
|
||||
# Download: [JMusicBot-${{ github.event.inputs.version_number }}.jar](https://github.com/jagrosh/MusicBot/releases/download/${{ github.event.inputs.version_number }}/JMusicBot-${{ github.event.inputs.version_number }}.jar)
|
||||
@@ -0,0 +1,9 @@
|
||||
/nbproject/
|
||||
/target/
|
||||
/Playlists/
|
||||
/test/
|
||||
*.json
|
||||
*.txt
|
||||
nb*.xml
|
||||
dockerfile
|
||||
.*
|
||||
@@ -1,29 +1,122 @@
|
||||
# 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 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 fork based on JMusicBot, updated to keep working on modern Discord and JDA versions.
|
||||
|
||||
# Setup
|
||||
Please see the [Setup Page](https://github.com/jagrosh/MusicBot/wiki/Setup) in the wiki to run this bot yourself!
|
||||
This repository keeps the bot practical to run today, while also adding fork-specific features such as status messages and playback analytics logging. Possibly more.
|
||||
|
||||
# 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)
|
||||
## Demo
|
||||
|
||||
# Editing
|
||||
This bot (and the source code here) is not meant to be edited. The main purpose of having the source public is to show the capabilities of the libraries, and to allow others to understand how the bot works. There are many requirements and dependencies required to edit and compile it, and there will not be support provided for people looking to make changes on their own. Instead, consider making a feature request (see the above section). If you still choose to make edits, please do so in accordance with the Apache 2.0 License.
|
||||
Temporary invite: [discord.gg/4cp9CvjwaW](https://discord.gg/4cp9CvjwaW)
|
||||
Permanent invite: [discord.gg/hK95396uB3](https://discord.gg/hK95396uB3)
|
||||
|
||||
# Example
|
||||

|
||||
## What This Fork Adds
|
||||
- Make it work again, through:
|
||||
- JDA 6 support and current dependency updates
|
||||
- DAVE voice encryption support for current Discord voice requirements
|
||||
- Optional YouTube account login support
|
||||
- Optional YouTube PO token and visitor-data support
|
||||
- New niche features, such as:
|
||||
- Optional per-channel playback status message
|
||||
- Optional JDBC/SQLite playback analytics logging
|
||||
- Basic QoL improvements, such as:
|
||||
- Docker image and container-friendly deployment path
|
||||
|
||||
## Quick Links
|
||||
|
||||
- Fork changes and version notes: [docs/fork-changelog.md](/git/MusicBot/docs/fork-changelog.md)
|
||||
- Setup guide: [docs/setup.md](/git/MusicBot/docs/setup.md)
|
||||
- Docker usage: [docs/docker.md](/git/MusicBot/docs/docker.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
|
||||
|
||||
## Setup Notes
|
||||
|
||||
If you only need the essentials:
|
||||
|
||||
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 directly or use Docker.
|
||||
|
||||
For the detailed version, see [docs/setup.md](/git/MusicBot/docs/setup.md).
|
||||
|
||||
## Docker
|
||||
|
||||
A Docker image is published here: [docker.io/chrisb09/jmusicbot](https://hub.docker.com/r/chrisb09/jmusicbot)
|
||||
|
||||
For compose examples and deployment notes, see [docs/docker.md](/git/MusicBot/docs/docker.md).
|
||||
|
||||
## YouTube Login
|
||||
|
||||
This fork supports Google account login for YouTube playback workarounds.
|
||||
|
||||
Enable:
|
||||
|
||||
```conf
|
||||
youtubeoauth2=true
|
||||
```
|
||||
|
||||
Do not use your main Google account. Use a burner or otherwise disposable account instead.
|
||||
|
||||
More details are in [docs/setup.md](/git/MusicBot/docs/setup.md).
|
||||
|
||||
If you use PO tokens or visitor data for YouTube, those details are also covered in [docs/setup.md](/git/MusicBot/docs/setup.md).
|
||||
|
||||
## DAVE / Voice Encryption
|
||||
|
||||
Discord currently requires DAVE for voice on modern API versions. This fork 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](/git/MusicBot/pom.xml). You probably need to build the library from source for your architecture, so have fun with that.
|
||||
|
||||
## 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)
|
||||
|
||||
If this fork does not fit your needs, another fork you may want to inspect is [SeVile/MusicBot](https://github.com/SeVile/MusicBot).
|
||||
|
||||
## Questions And Contributions
|
||||
|
||||
If you run into issues, please include:
|
||||
|
||||
- the version you are running
|
||||
- whether you are using Docker or the jar directly
|
||||
- relevant config changes
|
||||
- relevant logs
|
||||
|
||||
Fork-specific version notes live in [docs/fork-changelog.md](/git/MusicBot/docs/fork-changelog.md), which should help with troubleshooting and migration as the fork evolves.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Docker Guide
|
||||
|
||||
This fork supports running the bot in Docker, which is usually the easiest deployment path for a server.
|
||||
|
||||
## Image
|
||||
|
||||
Published image:
|
||||
|
||||
- [docker.io/chrisb09/jmusicbot](https://hub.docker.com/r/chrisb09/jmusicbot)
|
||||
|
||||
Container build sources:
|
||||
|
||||
- [github.com/chrisb09/jmb-container](https://github.com/chrisb09/jmb-container)
|
||||
|
||||
## Minimal Compose Example
|
||||
|
||||
```yaml
|
||||
version: "2.2"
|
||||
|
||||
services:
|
||||
jmusicbot:
|
||||
image: chrisb09/jmusicbot:latest
|
||||
container_name: jmusicbot
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/jmb/config
|
||||
```
|
||||
|
||||
Replace `./config` with the host path you actually want to use.
|
||||
|
||||
## Config Files
|
||||
|
||||
Your persistent bot config should live in the mounted config directory. That lets you redeploy containers without losing your bot settings.
|
||||
|
||||
Typical things to configure there:
|
||||
|
||||
- bot token
|
||||
- owner ID
|
||||
- prefix
|
||||
- YouTube login toggle
|
||||
- YouTube PO token and visitor-data values if needed
|
||||
- status message toggle
|
||||
- analytics logging settings
|
||||
|
||||
## Deploy Flow
|
||||
|
||||
A typical update flow is:
|
||||
|
||||
1. Pull or rebuild the image.
|
||||
2. Restart the container.
|
||||
3. Check logs for startup errors.
|
||||
|
||||
Useful commands:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
docker-compose logs -f jmusicbot
|
||||
```
|
||||
|
||||
## Alpine / musl Note
|
||||
|
||||
This fork bundles native support for Alpine-compatible musl environments as part of its DAVE setup, so Alpine-based container deployments are supported.
|
||||
|
||||
## SQLite Logging In Docker
|
||||
|
||||
If you enable analytics logging with SQLite:
|
||||
|
||||
```conf
|
||||
dblog.enabled = true
|
||||
dblog.jdbc_url = "jdbc:sqlite:musicbot.db"
|
||||
```
|
||||
|
||||
make sure the database file lives somewhere inside your mounted persistent config or data path if you want it to survive container recreation.
|
||||
|
||||
The `stats` command and scheduled server rewind embeds read from this database, so reports only include activity logged after analytics were enabled.
|
||||
|
||||
## YouTube Token Notes In Docker
|
||||
|
||||
If you use YouTube login or PO token based configuration, keep those values in your persistent mounted config rather than baking them into the image.
|
||||
|
||||
Relevant config values include:
|
||||
|
||||
```conf
|
||||
youtubeoauth2 = true
|
||||
ytpotoken = "PO_TOKEN_HERE"
|
||||
ytvisitordata = "VISITOR_DATA_HERE"
|
||||
```
|
||||
|
||||
## Recommendation
|
||||
|
||||
For production-style use, Docker is the cleanest path if you want:
|
||||
|
||||
- reproducible runtime behavior
|
||||
- simple upgrades
|
||||
- easier separation between bot code and persistent config
|
||||
@@ -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 non-Docker 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
|
||||
mvn 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 directly on Windows rather than in Docker, 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).
|
||||
@@ -3,81 +3,302 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.jagrosh</groupId>
|
||||
<artifactId>JMusicBot</artifactId>
|
||||
<version>0.0.9</version>
|
||||
<version>0.6.6</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.2.0_258</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.43</version>
|
||||
<version>2.2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jagrosh</groupId>
|
||||
<artifactId>JDA-Utilities</artifactId>
|
||||
<version>1.6</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>1.18.1</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.27.0</version>
|
||||
<!-- https://github.com/SeleniumHQ/selenium/issues/13791 -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-devtools-v85</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-devtools-v129</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-devtools-v130</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.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,113 +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 com.jagrosh.jmusicbot.playlist.PlaylistLoader;
|
||||
import com.jagrosh.jmusicbot.settings.SettingsManager;
|
||||
import java.util.Objects;
|
||||
import javafx.util.Pair;
|
||||
import net.dv8tion.jda.core.JDA;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
import net.dv8tion.jda.core.entities.Game;
|
||||
import net.dv8tion.jda.core.entities.Guild;
|
||||
import net.dv8tion.jda.core.entities.Message;
|
||||
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.events.guild.GuildJoinEvent;
|
||||
import net.dv8tion.jda.core.events.message.MessageDeleteEvent;
|
||||
import net.dv8tion.jda.core.exceptions.PermissionException;
|
||||
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.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 HashMap<Long,Pair<Long,Long>> lastNP; // guild -> channel,message
|
||||
private final AudioPlayerManager manager;
|
||||
public class Bot
|
||||
{
|
||||
private final EventWaiter waiter;
|
||||
private final ScheduledExecutorService threadpool;
|
||||
private final Config config;
|
||||
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 ->
|
||||
public Bot(EventWaiter waiter, BotConfig config, SettingsManager settings)
|
||||
{
|
||||
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, Config config)
|
||||
{
|
||||
this.config = config;
|
||||
this.waiter = waiter;
|
||||
this.settings = new HashMap<>();
|
||||
this.lastNP = 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 JDA getJDA()
|
||||
public BotConfig getConfig()
|
||||
{
|
||||
return jda;
|
||||
return config;
|
||||
}
|
||||
|
||||
public SettingsManager getSettingsManager()
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
|
||||
public EventWaiter getWaiter()
|
||||
@@ -129,347 +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);
|
||||
if(AudioHandler.USE_NP_REFRESH)
|
||||
threadpool.scheduleWithFixedDelay(() -> updateLastNP(guild.getIdLong()), 0, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
else
|
||||
handler = (AudioHandler)guild.getAudioManager().getSendingHandler();
|
||||
return handler;
|
||||
return nowplaying;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Game game = config.getGame()==null || config.getGame().equalsIgnoreCase("none") ? null : Game.of(config.getGame());
|
||||
if(!Objects.equals(jda.getPresence().getGame(), game))
|
||||
jda.getPresence().setGame(game);
|
||||
}
|
||||
|
||||
public void setLastNP(Message m)
|
||||
{
|
||||
lastNP.put(m.getGuild().getIdLong(), new Pair<>(m.getTextChannel().getIdLong(), m.getIdLong()));
|
||||
Activity game = config.getGame()==null || config.getGame().getName().equalsIgnoreCase("none") ? null : config.getGame();
|
||||
if(!Objects.equals(jda.getPresence().getActivity(), game))
|
||||
jda.getPresence().setActivity(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageDelete(MessageDeleteEvent event) {
|
||||
if(lastNP.containsKey(event.getGuild().getIdLong()))
|
||||
{
|
||||
Pair<Long,Long> pair = lastNP.get(event.getGuild().getIdLong());
|
||||
if(pair.getValue()==event.getMessageIdLong())
|
||||
lastNP.remove(event.getGuild().getIdLong());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLastNP(long guildId)
|
||||
public void shutdown()
|
||||
{
|
||||
Guild guild = jda.getGuildById(guildId);
|
||||
if(guild==null)
|
||||
if(shuttingDown)
|
||||
return;
|
||||
if(!lastNP.containsKey(guildId))
|
||||
return;
|
||||
Pair<Long,Long> pair = lastNP.get(guildId);
|
||||
if(pair==null)
|
||||
return;
|
||||
TextChannel tc = guild.getTextChannelById(pair.getKey());
|
||||
if(tc==null)
|
||||
{
|
||||
lastNP.remove(guildId);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
tc.editMessageById(pair.getValue(), FormatUtil.nowPlayingMessage(guild, config.getSuccess())).queue(m->{}, t -> lastNP.remove(guildId));
|
||||
} catch(Exception e) {
|
||||
lastNP.remove(guildId);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTopic(long guildId, AudioHandler handler)
|
||||
{
|
||||
Guild guild = jda.getGuildById(guildId);
|
||||
if(guild==null)
|
||||
return;
|
||||
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().lastIndexOf("\u200B"));
|
||||
else
|
||||
otherText = "\u200B\n "+tchan.getTopic();
|
||||
String text = FormatUtil.topicFormat(handler, guild.getJDA())+otherText;
|
||||
if(!text.equals(tchan.getTopic()))
|
||||
try {
|
||||
tchan.getManager().setTopic(text).queue();
|
||||
} catch(PermissionException e){}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown(){
|
||||
manager.shutdown();
|
||||
shuttingDown = true;
|
||||
threadpool.shutdownNow();
|
||||
jda.getGuilds().stream().forEach(g -> {
|
||||
g.getAudioManager().closeAudioConnection();
|
||||
AudioHandler ah = (AudioHandler)g.getAudioManager().getSendingHandler();
|
||||
if(ah!=null)
|
||||
if(jda.getStatus()!=JDA.Status.SHUTTING_DOWN)
|
||||
{
|
||||
jda.getGuilds().stream().forEach(g ->
|
||||
{
|
||||
ah.getQueue().clear();
|
||||
ah.getPlayer().destroy();
|
||||
updateTopic(g.getIdLong(), 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));
|
||||
}
|
||||
credit(event.getJDA());
|
||||
jda.getGuilds().forEach((guild) -> {
|
||||
try
|
||||
{
|
||||
String defpl = getSettings(guild).getDefaultPlaylist();
|
||||
VoiceChannel vc = guild.getVoiceChannelById(getSettings(guild).getVoiceId());
|
||||
if(defpl!=null && vc!=null)
|
||||
{
|
||||
if(setUpHandler(guild).playFromDefault())
|
||||
guild.getAudioManager().openAudioConnection(vc);
|
||||
}
|
||||
}
|
||||
catch(Exception ex) {System.err.println(ex);}
|
||||
});
|
||||
}
|
||||
|
||||
@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(config.getDBots())
|
||||
return;
|
||||
jda.getTextChannelById(119222314964353025L)
|
||||
.sendMessage("<@113156185389092864>: This account is running JMusicBot. Please do not list bot clones on this server, <@"+config.getOwnerId()+">.").complete();
|
||||
dbots.leave().queue();
|
||||
}
|
||||
|
||||
// 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,290 +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 java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
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;
|
||||
import net.dv8tion.jda.core.OnlineStatus;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (jagrosh)
|
||||
*/
|
||||
public class Config {
|
||||
private boolean nogui;
|
||||
private String prefix;
|
||||
private String altprefix;
|
||||
private String token;
|
||||
private String owner;
|
||||
private String success;
|
||||
private String warning;
|
||||
private String error;
|
||||
private String game;
|
||||
private String help;
|
||||
private boolean stay;
|
||||
private boolean dbots;
|
||||
private boolean songingame;
|
||||
private boolean useEval;
|
||||
private boolean npimages;
|
||||
private long maxSeconds;
|
||||
private OnlineStatus status = OnlineStatus.UNKNOWN;
|
||||
|
||||
public Config(boolean nogui)
|
||||
{
|
||||
this.nogui = nogui;
|
||||
List<String> lines;
|
||||
try {
|
||||
lines = Files.readAllLines(Paths.get("config.txt"), Charset.forName("ISO-8859-1"));
|
||||
System.out.println("[INFO] Loading config: "+Paths.get("config.txt").toFile().getAbsolutePath());
|
||||
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 "altprefix":
|
||||
altprefix = 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 "songinstatus":
|
||||
songingame = "true".equalsIgnoreCase(value);
|
||||
break;
|
||||
case "stayinchannel":
|
||||
stay = "true".equalsIgnoreCase(value);
|
||||
break;
|
||||
case "eval":
|
||||
useEval = "true".equalsIgnoreCase(value);
|
||||
break;
|
||||
case "dbots":
|
||||
dbots = "110373943822540800".equals(value);
|
||||
break;
|
||||
case "npimages":
|
||||
npimages = "true".equalsIgnoreCase(value);
|
||||
break;
|
||||
case "maxtime":
|
||||
try{
|
||||
maxSeconds = Long.parseLong(value);
|
||||
}catch(NumberFormatException e){}
|
||||
break;
|
||||
case "status":
|
||||
status = OnlineStatus.fromKey(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 getAltPrefix()
|
||||
{
|
||||
return altprefix;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean getStay()
|
||||
{
|
||||
return stay;
|
||||
}
|
||||
|
||||
public boolean getSongInStatus()
|
||||
{
|
||||
return songingame;
|
||||
}
|
||||
|
||||
public boolean getDBots()
|
||||
{
|
||||
return dbots;
|
||||
}
|
||||
|
||||
public boolean useEval()
|
||||
{
|
||||
return useEval;
|
||||
}
|
||||
|
||||
public boolean useNPImages()
|
||||
{
|
||||
return npimages;
|
||||
}
|
||||
|
||||
public long getMaxSeconds()
|
||||
{
|
||||
return maxSeconds;
|
||||
}
|
||||
|
||||
public OnlineStatus getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
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,73 +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.audio.AudioHandler;
|
||||
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, config);
|
||||
SettingsManager settings = new SettingsManager();
|
||||
Bot bot = new Bot(waiter, config, settings);
|
||||
CommandClient client = createCommandClient(config, settings, bot);
|
||||
|
||||
AboutCommand ab = new AboutCommand(Color.BLUE.brighter(),
|
||||
"a music bot that is [easy to host yourself!](https://github.com/jagrosh/MusicBot) (v0.0.9)",
|
||||
|
||||
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);
|
||||
ab.setIsAuthor(false);
|
||||
ab.setReplacementCharacter("\uD83C\uDFB6");
|
||||
AudioHandler.STAY_IN_CHANNEL = config.getStay();
|
||||
AudioHandler.SONG_IN_STATUS = config.getSongInStatus();
|
||||
AudioHandler.MAX_SECONDS = config.getMaxSeconds();
|
||||
AudioHandler.USE_NP_REFRESH = !config.useNPImages();
|
||||
aboutCommand.setIsAuthor(false);
|
||||
aboutCommand.setReplacementCharacter("\uD83C\uDFB6"); // 🎶
|
||||
|
||||
// set up the command client
|
||||
|
||||
CommandClientBuilder cb = new CommandClientBuilder()
|
||||
.setPrefix(config.getPrefix())
|
||||
.setAlternativePrefix(config.getAltPrefix())
|
||||
.setOwnerId(config.getOwnerId())
|
||||
.setOwnerId(Long.toString(config.getOwnerId()))
|
||||
.setEmojis(config.getSuccess(), config.getWarning(), config.getError())
|
||||
.setHelpWord(config.getHelp())
|
||||
.addCommands(
|
||||
ab,
|
||||
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),
|
||||
@@ -89,21 +224,29 @@ 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),
|
||||
@@ -111,48 +254,23 @@ public class JMusicBot {
|
||||
new SetstatusCmd(bot),
|
||||
new ShutdownCmd(bot)
|
||||
);
|
||||
|
||||
// enable eval if applicable
|
||||
if(config.useEval())
|
||||
cb.addCommand(new EvalCmd(bot));
|
||||
boolean nogame = false;
|
||||
if(config.getStatus()!=OnlineStatus.UNKNOWN)
|
||||
|
||||
// set status if set in config
|
||||
if(config.getStatus() != OnlineStatus.UNKNOWN)
|
||||
cb.setStatus(config.getStatus());
|
||||
if(config.getGame()==null)
|
||||
|
||||
// set game
|
||||
if(config.getGame() == null)
|
||||
cb.useDefaultGame();
|
||||
else if(config.getGame().equalsIgnoreCase("none"))
|
||||
{
|
||||
cb.setGame(null);
|
||||
nogame = true;
|
||||
}
|
||||
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(nogame ? null : Game.of("loading..."))
|
||||
.setStatus(config.getStatus()==OnlineStatus.INVISIBLE||config.getStatus()==OnlineStatus.OFFLINE ? OnlineStatus.INVISIBLE : 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,54 +30,84 @@ 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.Game;
|
||||
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 {
|
||||
public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
|
||||
{
|
||||
public final static String PLAY_EMOJI = "\u25B6"; // ▶
|
||||
public final static String PAUSE_EMOJI = "\u23F8"; // ⏸
|
||||
public final static String STOP_EMOJI = "\u23F9"; // ⏹
|
||||
|
||||
private final 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 final FairQueue<QueuedTrack> queue;
|
||||
private final Set<String> votes;
|
||||
private final List<AudioTrack> defaultQueue;
|
||||
private final Bot bot;
|
||||
|
||||
private AudioFrame lastFrame;
|
||||
private long requester;
|
||||
public static boolean STAY_IN_CHANNEL;
|
||||
public static boolean SONG_IN_STATUS;
|
||||
public static long MAX_SECONDS = -1;
|
||||
public static boolean USE_NP_REFRESH;
|
||||
private AbstractQueue<QueuedTrack> queue;
|
||||
private volatile String playCountTrackIdentifier;
|
||||
private volatile long currentTrackServerPlayCount = -1L;
|
||||
|
||||
public AudioHandler(AudioPlayer audioPlayer, Guild guild, Bot bot) {
|
||||
this.audioPlayer = audioPlayer;
|
||||
this.guildId = guild.getIdLong();
|
||||
this.bot = bot;
|
||||
queue = new FairQueue<>();
|
||||
votes = new HashSet<>();
|
||||
defaultQueue = new LinkedList<>();
|
||||
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)
|
||||
{
|
||||
queue = type.createInstance(queue);
|
||||
}
|
||||
|
||||
public int addTrackToFront(QueuedTrack qtrack)
|
||||
{
|
||||
if(audioPlayer.getPlayingTrack()==null)
|
||||
{
|
||||
requester = user.getIdLong();
|
||||
audioPlayer.playTrack(track);
|
||||
audioPlayer.playTrack(qtrack.getTrack());
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
return queue.add(new QueuedTrack(track, user.getIdLong()));
|
||||
{
|
||||
queue.addAt(0, qtrack);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public FairQueue<QueuedTrack> getQueue()
|
||||
public int addTrack(QueuedTrack qtrack)
|
||||
{
|
||||
if(audioPlayer.getPlayingTrack()==null)
|
||||
{
|
||||
audioPlayer.playTrack(qtrack.getTrack());
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
return queue.add(qtrack);
|
||||
}
|
||||
|
||||
public AbstractQueue<QueuedTrack> getQueue()
|
||||
{
|
||||
return queue;
|
||||
}
|
||||
@@ -84,9 +120,9 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
|
||||
//current = null;
|
||||
}
|
||||
|
||||
public boolean isMusicPlaying()
|
||||
public boolean isMusicPlaying(JDA jda)
|
||||
{
|
||||
return bot.getJDA().getGuildById(guildId).getSelfMember().getVoiceState().inVoiceChannel() && audioPlayer.getPlayingTrack()!=null;
|
||||
return guild(jda).getSelfMember().getVoiceState().inAudioChannel() && audioPlayer.getPlayingTrack()!=null;
|
||||
}
|
||||
|
||||
public Set<String> getVotes()
|
||||
@@ -99,9 +135,12 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
|
||||
return audioPlayer;
|
||||
}
|
||||
|
||||
public long getRequester()
|
||||
public RequestMetadata getRequestMetadata()
|
||||
{
|
||||
return requester;
|
||||
if(audioPlayer.getPlayingTrack() == null)
|
||||
return RequestMetadata.EMPTY;
|
||||
RequestMetadata rm = audioPlayer.getPlayingTrack().getUserData(RequestMetadata.class);
|
||||
return rm == null ? RequestMetadata.EMPTY : rm;
|
||||
}
|
||||
|
||||
public boolean playFromDefault()
|
||||
@@ -111,79 +150,302 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
|
||||
audioPlayer.playTrack(defaultQueue.remove(0));
|
||||
return true;
|
||||
}
|
||||
Guild guild = bot.getJDA().getGuildById(guildId);
|
||||
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(), (at) -> {
|
||||
pl.loadTracks(manager, (at) ->
|
||||
{
|
||||
at.setUserData(RequestMetadata.fromDefaultPlaylist(settings.getDefaultPlaylist()));
|
||||
if(audioPlayer.getPlayingTrack()==null)
|
||||
audioPlayer.playTrack(at);
|
||||
else
|
||||
defaultQueue.add(at);
|
||||
}, () -> {
|
||||
if(pl.getTracks().isEmpty() && !STAY_IN_CHANNEL)
|
||||
guild.getAudioManager().closeAudioConnection();
|
||||
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) {
|
||||
requester = 0;
|
||||
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())
|
||||
{
|
||||
if(SONG_IN_STATUS)
|
||||
bot.resetGame();
|
||||
if(!STAY_IN_CHANNEL)
|
||||
bot.getJDA().getGuildById(guildId).getAudioManager().closeAudioConnection();
|
||||
bot.updateTopic(guildId, this);
|
||||
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
|
||||
{
|
||||
QueuedTrack qt = queue.pull();
|
||||
requester = qt.getIdentifier();
|
||||
player.playTrack(qt.getTrack());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackStart(AudioPlayer player, AudioTrack track) {
|
||||
votes.clear();
|
||||
if(SONG_IN_STATUS)
|
||||
{
|
||||
if(bot.getJDA().getGuilds().stream().filter(g -> g.getSelfMember().getVoiceState().inVoiceChannel()).count()<=1)
|
||||
bot.getJDA().getPresence().setGame(Game.of(track.getInfo().title));
|
||||
else
|
||||
bot.resetGame();
|
||||
}
|
||||
bot.updateTopic(guildId, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProvide() {
|
||||
lastFrame = audioPlayer.provide();
|
||||
return lastFrame != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] provide20MsAudio() {
|
||||
return lastFrame.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpus() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isTooLong(AudioTrack track)
|
||||
public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception)
|
||||
{
|
||||
if(MAX_SECONDS<=0)
|
||||
return false;
|
||||
return Math.round(track.getDuration()/1000.0) > MAX_SECONDS;
|
||||
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;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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 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,159 @@
|
||||
/*
|
||||
* 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.Web;
|
||||
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 = new YoutubeAudioSourceManager(true);
|
||||
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());
|
||||
registerSourceManager(new TwitchStreamAudioSourceManager());
|
||||
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()
|
||||
{
|
||||
YoutubeAudioSourceManager yt = new YoutubeAudioSourceManager(true);
|
||||
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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
|
||||
* Copyright 2021 John Grosh <john.a.grosh@gmail.com>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,28 +15,35 @@
|
||||
*/
|
||||
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 com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class QueuedTrack implements Queueable {
|
||||
|
||||
public class QueuedTrack implements Queueable
|
||||
{
|
||||
private final AudioTrack track;
|
||||
private final long owner;
|
||||
|
||||
public QueuedTrack(AudioTrack track, long owner)
|
||||
private final RequestMetadata requestMetadata;
|
||||
|
||||
public QueuedTrack(AudioTrack track, RequestMetadata rm)
|
||||
{
|
||||
this.track = track;
|
||||
this.owner = owner;
|
||||
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 owner;
|
||||
public long getIdentifier()
|
||||
{
|
||||
return requestMetadata.getOwner();
|
||||
}
|
||||
|
||||
public AudioTrack getTrack()
|
||||
@@ -44,9 +51,17 @@ public class QueuedTrack implements Queueable {
|
||||
return track;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "`["+FormatUtil.formatTime(track.getDuration())+"]` **" + track.getInfo().title +"** - <@"+owner+">";
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,56 +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.commands;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh (jagrosh)
|
||||
*/
|
||||
public class EvalCmd extends Command {
|
||||
|
||||
public EvalCmd(Bot bot)
|
||||
{
|
||||
this.name = "eval";
|
||||
this.help = "evaluates nashorn code";
|
||||
this.ownerCommand = true;
|
||||
this.guildOnly = false;
|
||||
this.category = bot.OWNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
ScriptEngine se = new ScriptEngineManager().getEngineByName("Nashorn");
|
||||
se.put("event", event);
|
||||
se.put("jda", event.getJDA());
|
||||
se.put("guild", event.getGuild());
|
||||
se.put("channel", event.getChannel());
|
||||
try
|
||||
{
|
||||
event.reply(event.getClient().getSuccess()+" Evaluated Successfully:\n```\n"+se.eval(event.getArgs())+" ```");
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" An exception was thrown:\n```\n"+e+" ```");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()).isMusicPlaying()))
|
||||
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,212 +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.audio.AudioHandler;
|
||||
import com.jagrosh.jmusicbot.playlist.Playlist;
|
||||
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 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())
|
||||
{
|
||||
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
if(handler!=null && handler.getPlayer().getPlayingTrack()!=null && handler.getPlayer().isPaused())
|
||||
{
|
||||
boolean isDJ = event.getMember().hasPermission(Permission.MANAGE_SERVER);
|
||||
if(!isDJ)
|
||||
isDJ = event.isOwner() || event.isCoOwner();
|
||||
if(!isDJ)
|
||||
isDJ = event.getMember().getRoles().contains(event.getGuild().getRoleById(bot.getSettings(event.getGuild()).getRoleId()));
|
||||
if(!isDJ)
|
||||
event.replyError("Only DJs can unpause the player!");
|
||||
else
|
||||
{
|
||||
handler.getPlayer().setPaused(false);
|
||||
event.replySuccess("Resumed **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**.");
|
||||
}
|
||||
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());
|
||||
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) {
|
||||
if(AudioHandler.isTooLong(track))
|
||||
{
|
||||
m.editMessage(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+FormatUtil.formatTime(track.getDuration())+"` > `"+FormatUtil.formatTime(AudioHandler.MAX_SECONDS*1000)+"`").queue();
|
||||
return;
|
||||
}
|
||||
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();
|
||||
if(AudioHandler.isTooLong(single))
|
||||
{
|
||||
m.editMessage(event.getClient().getWarning()+" This track (**"+single.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+FormatUtil.formatTime(single.getDuration())+"` > `"+FormatUtil.formatTime(AudioHandler.MAX_SECONDS*1000)+"`").queue();
|
||||
return;
|
||||
}
|
||||
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
|
||||
{
|
||||
int[] count = {0};
|
||||
playlist.getTracks().stream().forEach((track) -> {
|
||||
if(!AudioHandler.isTooLong(track))
|
||||
{
|
||||
bot.queueTrack(event, track);
|
||||
count[0]++;
|
||||
}
|
||||
});
|
||||
if(count[0]==0)
|
||||
{
|
||||
m.editMessage(event.getClient().getWarning()+" All entries in this playlist "+(playlist.getName()==null ? "" : "(**"+playlist.getName()
|
||||
+"**) ")+"were longer than the allowed maximum (`"+FormatUtil.formatTime(AudioHandler.MAX_SECONDS*1000)+"`)").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!"
|
||||
+ (count[0]<playlist.getTracks().size() ? "\n"+event.getClient().getWarning()+" Tracks longer than the allowed maximum (`"
|
||||
+ FormatUtil.formatTime(AudioHandler.MAX_SECONDS*1000)+"`) have been omitted." : "")).queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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()+"**... ("+playlist.getItems().size()+" items)").queue(m -> {
|
||||
playlist.loadTracks(bot.getAudioManager(), (at)->bot.queueTrack(event, at), () -> {
|
||||
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();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +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){}
|
||||
AudioHandler ah = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
|
||||
List<QueuedTrack> list = ah.getQueue().getList();
|
||||
if(list.isEmpty())
|
||||
{
|
||||
event.replyWarning("There is no music in the queue!"
|
||||
+(!ah.isMusicPlaying() ? "" : " Now playing:\n\n**"+ah.getPlayer().getPlayingTrack().getInfo().title+"**\n"+FormatUtil.embedFormat(ah)));
|
||||
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();
|
||||
}
|
||||
long fintotal = total;
|
||||
builder.setText((i1,i2) -> getQueueTitle(ah, event.getClient().getSuccess(), songs.length, fintotal))
|
||||
.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)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(ah.getPlayer().getPlayingTrack()!=null)
|
||||
sb.append("**").append(ah.getPlayer().getPlayingTrack().getInfo().title).append("**\n").append(FormatUtil.embedFormat(ah)).append("\n\n");
|
||||
return sb.append(success).append(" Current Queue | ").append(songslength).append(" entries | `").append(FormatUtil.formatTime(total)).append("` ").toString();
|
||||
}
|
||||
}
|
||||
@@ -1,97 +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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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().getIdLong());
|
||||
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()==event.getAuthor().getIdLong())
|
||||
{
|
||||
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,134 +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.audio.AudioHandler;
|
||||
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) {
|
||||
if(AudioHandler.isTooLong(track))
|
||||
{
|
||||
m.editMessage(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+FormatUtil.formatTime(track.getDuration())+"` > `"+FormatUtil.formatTime(AudioHandler.MAX_SECONDS*1000)+"`").queue();
|
||||
return;
|
||||
}
|
||||
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);
|
||||
if(AudioHandler.isTooLong(track))
|
||||
{
|
||||
event.getChannel().sendMessage(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+FormatUtil.formatTime(track.getDuration())+"` > `"+FormatUtil.formatTime(AudioHandler.MAX_SECONDS*1000)+"`").queue();
|
||||
return;
|
||||
}
|
||||
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,135 +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.audio.AudioHandler;
|
||||
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) {
|
||||
if(AudioHandler.isTooLong(track))
|
||||
{
|
||||
m.editMessage(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+FormatUtil.formatTime(track.getDuration())+"` > `"+FormatUtil.formatTime(AudioHandler.MAX_SECONDS*1000)+"`").queue();
|
||||
return;
|
||||
}
|
||||
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);
|
||||
if(AudioHandler.isTooLong(track))
|
||||
{
|
||||
event.getChannel().sendMessage(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
|
||||
+FormatUtil.formatTime(track.getDuration())+"` > `"+FormatUtil.formatTime(AudioHandler.MAX_SECONDS*1000)+"`").queue();
|
||||
return;
|
||||
}
|
||||
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,79 +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] OR stream <username> <game>";
|
||||
this.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
this.children = new Command[]{new SetstreamCmd(bot)};
|
||||
}
|
||||
|
||||
@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!");
|
||||
}
|
||||
}
|
||||
|
||||
private class SetstreamCmd extends Command {
|
||||
|
||||
private SetstreamCmd(Bot 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.ownerCommand = true;
|
||||
this.category = bot.OWNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(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'");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
event.getJDA().getPresence().setGame(Game.of(parts[1], "https://twitch.tv/"+parts[0]));
|
||||
event.replySuccess("**"+event.getSelfUser().getName()
|
||||
+"** is now streaming `"+parts[1]+"`");
|
||||
} 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().getIdLong());
|
||||
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,74 +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().getIdLong()==handler.getRequester())
|
||||
{
|
||||
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().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 = event.getJDA().getUserById(handler.getRequester());
|
||||
msg+="\n"+event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title
|
||||
+"**"+(handler.getRequester()==0 ? "" : " (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
|
||||
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()));
|
||||
}
|
||||
}
|
||||
+31
-17
@@ -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.
|
||||
@@ -13,55 +13,69 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jagrosh.jmusicbot.commands;
|
||||
package com.jagrosh.jmusicbot.commands.admin;
|
||||
|
||||
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.jdautilities.commons.utils.FinderUtil;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.utils.FinderUtil;
|
||||
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.core.entities.Role;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class SetdjCmd extends Command {
|
||||
|
||||
private final Bot bot;
|
||||
public class SetdjCmd extends AdminCommand
|
||||
{
|
||||
public SetdjCmd(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
super(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;
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(CommandEvent event) {
|
||||
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;
|
||||
}
|
||||
else if(event.getArgs().equalsIgnoreCase("none"))
|
||||
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"))
|
||||
{
|
||||
bot.clearRole(event.getGuild());
|
||||
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.findRole(event.getArgs(), event.getGuild());
|
||||
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
|
||||
{
|
||||
bot.setRole(list.get(0));
|
||||
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
-11
@@ -13,36 +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 = event.getJDA().getUserById(handler.getRequester());
|
||||
RequestMetadata rm = handler.getRequestMetadata();
|
||||
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title
|
||||
+"** (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
|
||||
+"** "+(rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + 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);
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
+18
-9
@@ -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.
|
||||
@@ -13,38 +13,47 @@
|
||||
* 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 PauseCmd extends MusicCommand {
|
||||
|
||||
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;
|
||||
this.category = bot.DJ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
+22
-9
@@ -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.
|
||||
@@ -13,33 +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 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 MusicCommand {
|
||||
|
||||
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.bePlaying = true;
|
||||
this.category = bot.DJ;
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
this.bePlaying = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-17
@@ -13,31 +13,22 @@
|
||||
* 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 com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import com.jagrosh.jmusicbot.utils.FormatUtil;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class NowplayingCmd extends MusicCommand {
|
||||
|
||||
public NowplayingCmd(Bot bot)
|
||||
public class SCSearchCmd extends SearchCmd
|
||||
{
|
||||
public SCSearchCmd(Bot bot)
|
||||
{
|
||||
super(bot);
|
||||
this.name = "nowplaying";
|
||||
this.help = "shows the song that is currently playing";
|
||||
this.aliases = new String[]{"np","current"};
|
||||
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
|
||||
this.searchPrefix = "scsearch:";
|
||||
this.name = "scsearch";
|
||||
this.help = "searches Soundcloud for a provided query";
|
||||
this.aliases = bot.getConfig().getAliases(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCommand(CommandEvent event) {
|
||||
event.reply(FormatUtil.nowPlayingMessage(event.getGuild(), event.getClient().getSuccess()), m->bot.setLastNP(m));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+21
-19
@@ -13,58 +13,60 @@
|
||||
* 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.settings.Settings;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author John Grosh <john.a.grosh@gmail.com>
|
||||
*/
|
||||
public class AutoplaylistCmd extends Command {
|
||||
|
||||
private final Bot bot;
|
||||
public class AutoplaylistCmd extends OwnerCommand
|
||||
{
|
||||
public AutoplaylistCmd(Bot bot)
|
||||
{
|
||||
this.bot = bot;
|
||||
this.category = bot.OWNER;
|
||||
this.ownerCommand = true;
|
||||
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 execute(CommandEvent event) {
|
||||
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"))
|
||||
{
|
||||
bot.setDefaultPlaylist(event.getGuild(), null);
|
||||
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(Playlist.loadPlaylist(pname)==null)
|
||||
if(bot.getPlaylistLoader().getPlaylist(pname)==null)
|
||||
{
|
||||
event.reply(event.getClient().getError()+" Could not find `"+pname+".txt`!");
|
||||
CommandLogContext.setError("playlist_not_found");
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.setDefaultPlaylist(event.getGuild(), pname);
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-10
@@ -13,44 +13,53 @@
|
||||
* 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 com.jagrosh.jdautilities.commandclient.Command;
|
||||
import com.jagrosh.jdautilities.commandclient.CommandEvent;
|
||||
import com.jagrosh.jdautilities.command.CommandEvent;
|
||||
import com.jagrosh.jmusicbot.Bot;
|
||||
import net.dv8tion.jda.core.OnlineStatus;
|
||||
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 Command {
|
||||
|
||||
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.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)
|
||||
{
|
||||
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();
|
||||
}*/
|
||||
|
||||
}
|
||||
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user