Compare commits

...
84 Commits
Author SHA1 Message Date
John Grosh 27fd75aa67 deps update and small code cleanup 2020-07-04 17:02:19 -04:00
John Grosh c5a583b96f update deps and deal with discords new ratelimits 2020-05-30 13:26:06 -04:00
John Grosh f1e301b48e fix windows path loading bug
fix empty game bug
2020-04-09 16:13:48 -04:00
John Grosh 61d4c51276 update deps 2020-03-23 17:05:02 -04:00
John Grosh cbdbe7bf4b update lavaplayer 2020-02-02 20:42:00 -05:00
John Grosh baa3a60b05 Merge origin/master
Conflicts:
	pom.xml
2020-01-28 21:02:36 -05:00
John Grosh 338dafca0c deps update 2020-01-28 21:00:57 -05:00
John Grosh b1dddb649b Merge pull request #342 from SplitPixl/patch-1
Add the letter "s"
2020-01-17 10:13:39 -05:00
SplitPixl 3eb284da55 Add the letter "s"
As of January 2020, JCenter rejects all non-https connections with a 403 Forbidden. 

https://jfrog.com/blog/secure-jcenter-with-https/
2020-01-16 20:02:26 -08:00
John Grosh 5fb969283d Update bug-report.md 2019-11-10 00:54:29 -05:00
John Grosh ae8b5cfe90 first pass of per-server prefixes 2019-11-09 20:22:53 -05:00
John Grosh b6fa7de7e2 Merge origin/master 2019-11-09 18:16:13 -05:00
John Grosh a4f13ca2de Merge pull request #308 from MichailiK/pr/feature/config/aliases
Add configurable aliases for all commands
2019-11-09 18:15:38 -05:00
John Grosh 5172deb12d dep update and fix playlist shuffling 2019-11-07 22:07:31 -05:00
MichailiK 179f447a70 Catch ConfigException.Missing in BotConfig#getAliases(string) 2019-10-26 09:39:56 +02:00
MichailiK f4a937cc6b Remove unused import from SCSearchCmd 2019-10-25 20:03:45 +02:00
MichailiK 0b4c6c9af1 Use Bot#getConfig() instead of using a public static config & Add missing newline 2019-10-25 19:48:41 +02:00
MichailiK f17058a344 Put aliases inside an "aliases"-block 2019-10-25 19:25:05 +02:00
MichailiK 4fcf86a5d6 Fix alias of volume 2019-10-25 18:14:23 +02:00
MichailiK 94362d640b Change Example 2 2019-10-25 17:56:55 +02:00
MichailiK 7192d55732 Add configurable aliases for all commands 2019-10-25 17:51:56 +02:00
John Grosh 11c9b00510 fix DJ permissions in PlayCmd
general cleanup
2019-10-21 01:02:48 -04:00
John Grosh 4579d6ca09 update deps and bump version 2019-10-21 00:27:19 -04:00
John Grosh c3a9937c90 first pass of debug command 2019-10-21 00:25:45 -04:00
MichailiandM R S E 8650f82210 Fix bot joining in AFK voice channels (#292)
* Fix bot joining in AFK voice channels

Since Discord automatically mutes users in AFK channels, bots shouldn't
play music inside AFK channels. This commit also fixes #147.

* Apply requested changes

* Fix potential NPE

Co-Authored-By: M R S E <Maurice.Craft@t-online.de>

* Fix indention
2019-10-19 23:02:28 -04:00
John Grosh 3ba13d2daa Merge pull request #296 from MichailiK/rp/fix/nowplayinghandler/refreshes-even-with-npimages-enabled
Fix nowplaying being updated even when NPImages is true.
2019-10-16 18:34:29 -04:00
MichailiK dace101c30 Fix nowplaying being updated even when NPImages is true. 2019-10-17 00:27:07 +02:00
John Grosh 05e8ec6814 Merge pull request #290 from MichailiK/pr/fix/pausebug
Fix player being paused when it is paused, then the queue being cleared
2019-10-15 13:19:26 -04:00
John Grosh 646ed6e9b1 Merge pull request #294 from MichailiK/pr/feature/command/queue/wrap-page-ends
Add wrapping of page ends in queue command
2019-10-15 13:15:41 -04:00
MichailiK a501b00ab7 Add wrapping of page ends in queue command
This feature wraps both page ends, meaning that users can jump from the
first page to the last page, using the previous page reaction,
while using the next page button on the last page brings you to the
first page.

Resolves #233.
2019-10-15 18:33:32 +02:00
MichailiK 6ae683b749 Fix player being paused when it is paused, then the queue being cleared
This commit fixes #75, where the player doesn't start playing when
it's been previously paused.
This may happen when a user pauses the player, clears the queue or skips
all songs and another user tries to use the player later.
2019-10-15 10:48:04 +02:00
John Grosh 235af8c8c6 Merge pull request #287 from MichailiK/pr-forceremovecmd-copyright
Add copyright and author to ForceRemoveCmd.java
2019-10-12 16:44:02 -04:00
MichailiK 33e4e8e5ce Add copyright and author to ForceRemoveCmd.java 2019-10-12 22:33:53 +02:00
John Grosh 20b23a3b0d Merge pull request #243 from MichailiK/pr-forceremovecmd
Added ForceRemoveCmd
2019-10-07 09:52:23 -04:00
MichailiK d616c1e386 Apply requested changes (Pass 3) 2019-10-05 19:37:19 +02:00
MichailiK e6f2b23ca9 Fix minor oversight 2019-10-05 10:30:59 +02:00
MichailiK fbfea7ab1f Apply requested changes (Pass 2) 2019-10-04 17:19:11 +02:00
MichailiK bc85fb9597 Apply requested changes 2019-09-12 14:44:13 +02:00
John Grosh a34f06669d removed build files 2019-09-11 19:31:15 -04:00
John Grosh 6508c2b62c dependency and version bump 2019-09-11 19:04:13 -04:00
John Grosh fb53b9e8ea deps updates 2019-07-31 23:35:50 -04:00
Michaili K 75abab2832 Removed redundant code. 2019-06-30 19:09:43 +02:00
Michaili K 13d9fa87d8 Added ForceRemoveCmd 2019-06-30 14:54:42 +02:00
John Grosh c0d40b2594 Merge origin/master 2019-06-10 18:35:49 -04:00
John Grosh 327cfe7d21 Update README.md 2019-06-07 14:48:26 -04:00
John Grosh b4e83fd3f0 Merge origin/master
also minor edits
Conflicts:
	pom.xml
2019-05-29 22:26:51 -04:00
John Grosh c902c1eace Merge pull request #225 from Sanduhr32/patch-1
Fixing news/store channels issue
2019-05-22 14:05:55 -04:00
M R S E 39a787c3c9 Dont go to insane on JDAs version
Also bump version
2019-05-05 16:55:18 +02:00
M R S E cdc20ce500 Fixing news/store channels issue 2019-05-05 16:52:51 +02:00
John Grosh 7dcd7badb3 Merge pull request #218 from SlapGaming/pullreqs
Move track command
2019-04-09 15:34:59 -04:00
Rick Fontein 1a0a73ac7c PR feedback pass 2 2019-04-09 20:56:16 +02:00
Rick Fontein eb1fb776a5 PR feedback pass 2019-04-08 20:38:59 +02:00
Rick Fontein 82349d380b Move track command
Adds support for moving a track in queue.
2019-04-07 00:12:21 +02:00
John Grosh 3c5deca55e improved config flow and autowrite full config 2019-03-16 16:29:09 -05:00
John Grosh e1b9e0165a Move pull req template 2019-01-30 11:58:55 -05:00
John Grosh 2e701d0d23 Move code of conduct 2019-01-30 11:58:21 -05:00
John Grosh 259e3d716b Update README.md 2019-01-13 15:53:19 -05:00
John Grosh 43398449c7 more cleanup and update readme 2019-01-13 15:51:26 -05:00
John Grosh e7f00fe088 Merge origin/master 2019-01-13 15:06:49 -05:00
John Grosh 1a9364acae code cleanup and deps update 2019-01-13 15:06:26 -05:00
John Grosh 19234998f1 Merge pull request #193 from jagrosh/add-code-of-conduct-1
Create CODE_OF_CONDUCT.md
2018-12-07 10:46:41 -05:00
John Grosh 31037775d1 Create CODE_OF_CONDUCT.md 2018-12-07 10:42:34 -05:00
John Grosh e0b1bb0d06 Create PULL_REQUEST_TEMPLATE.md 2018-12-07 10:34:42 -05:00
John Grosh 1f810aa36f Delete ISSUE_TEMPLATE.md 2018-12-07 10:22:20 -05:00
John Grosh e7a8cec0b4 Update issue templates 2018-12-07 10:22:04 -05:00
John Grosh ae07ac2598 lyrics second pass 2018-12-05 02:16:39 -05:00
John Grosh d75098fdd9 Update config.yml 2018-12-05 00:47:25 -05:00
John Grosh ac87cc13d7 fix pom 2018-12-05 00:43:13 -05:00
John Grosh 34bffe0148 first pass of lyrics command
version bump
more code cleanup
2018-12-05 00:30:28 -05:00
John Grosh 15f3cf07a8 Merge origin/master 2018-12-04 23:53:24 -05:00
John Grosh c0f0d45138 code cleanup 2018-12-04 23:37:45 -05:00
John Grosh 2eddaccb86 Update README.md 2018-11-27 15:04:21 -05:00
John Grosh 87b2f4f93a Update README.md 2018-11-27 15:02:41 -05:00
John Grosh 88970314ad Update README.md 2018-11-27 10:01:04 -05:00
John Grosh 0de4e2afcc Update .appveyor.yml 2018-11-27 09:58:09 -05:00
John Grosh 2671ac18c8 Update .appveyor.yml 2018-11-27 09:50:50 -05:00
John Grosh 68a992c79e Create .appveyor.yml 2018-11-27 09:40:46 -05:00
John Grosh 9bf233ec79 Update README.md 2018-11-27 09:25:50 -05:00
John Grosh b41dcded98 Update README.md 2018-11-27 09:25:30 -05:00
John Grosh b6239ed608 Update .travis.yml 2018-11-27 09:25:14 -05:00
John Grosh 9dfe613921 Update .travis.yml 2018-11-27 09:21:10 -05:00
John Grosh 865119e2ae Create .travis.settings.xml 2018-11-27 09:20:53 -05:00
John Grosh c616f9c92a Create .travis.yml 2018-11-27 09:01:09 -05:00
John Grosh 2737a85a61 Create config.yml 2018-11-27 08:41:38 -05:00
61 changed files with 1407 additions and 660 deletions

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
# 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: circleci/openjdk:8u171-jdk
# 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
+76
View File
@@ -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
-24
View File
@@ -1,24 +0,0 @@
[download]: https://github.com/jagrosh/MusicBot/releases/latest
[release]: https://img.shields.io/github/release/jagrosh/MusicBot.svg
[setup]: https://github.com/jagrosh/MusicBot/wiki/Setup
[rpi]: https://github.com/jagrosh/MusicBot/wiki/JMusicBot-on-Raspberry-Pi
[features]: https://github.com/jagrosh/MusicBot/projects/1
## General Troubleshooting
Please check off these steps before suggesting a feature or reporting a bug:
- [ ] I am running the [latest version][download]: [![Release][release]][download]
- [ ] I have followed all instructions on the [setup page][setup] (and if applicable, [Raspberry Pi][rpi])
- [ ] I have read through the [planned and suggested features][features]
## Issue
### Issue Type
- [ ] Bug Report
- [ ] Feature Request
### Description
Replace this with a description of your bug or feature request. Please provide screenshots and logs as applicable.
+29
View File
@@ -0,0 +1,29 @@
---
name: Bug Report
about: Report a bug or unexpected behavior
title: "[Bug Report] Title Here"
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Run with '...'
2. Do a command '....'
3. Some other step '....'
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Debug Info**
Please run the `debug` command and paste it here or upload it as an attachment. If you cannot run the command, please provide information about your system such as operating system version, JMusicBot version, etc.
**Additional context**
Add any other context about the problem here.
+27
View File
@@ -0,0 +1,27 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature Request] Title Here"
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe the basic flow/steps of using this feature**
How this feature would be used:
1. Set something '...'
2. Run some command '...'
3. Other steps or related info '...'
4. ...
**Additional context**
Add any other context or screenshots about the feature request here.
**Have you confirmed that this idea is not already described here?**
https://github.com/jagrosh/MusicBot/projects/1
+14
View File
@@ -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 #...
+3 -1
View File
@@ -1,5 +1,7 @@
/nbproject/
/target/
/Playlists/
/test/
*.json
*.txt
/nbproject/
nb*.xml
+27 -22
View File
@@ -1,36 +1,41 @@
<img align="right" src="https://i.imgur.com/zrE80HY.png" height="200" width="200">
# JMusicBot
[![Downloads](https://img.shields.io/github/downloads/jagrosh/MusicBot/total.svg)](https://github.com/jagrosh/MusicBot/releases/latest)
[![Stars](https://img.shields.io/github/stars/jagrosh/MusicBot.svg)](https://github.com/jagrosh/MusicBot/stargazers)
[![Release](https://img.shields.io/github/release/jagrosh/MusicBot.svg)](https://github.com/jagrosh/MusicBot/releases/latest)
[![License](https://img.shields.io/github/license/jagrosh/MusicBot.svg)](https://github.com/jagrosh/MusicBot/blob/master/LICENSE)
[![Discord](https://discordapp.com/api/guilds/147698382092238848/widget.png)](https://discord.gg/0p9LSGoRLu6Pet0k)
[![Discord](https://discordapp.com/api/guilds/147698382092238848/widget.png)](https://discord.gg/0p9LSGoRLu6Pet0k)<br>
[![CircleCI](https://img.shields.io/circleci/project/github/jagrosh/MusicBot/master.svg)](https://circleci.com/gh/jagrosh/MusicBot)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/gdu6nyte5psj6xfk/branch/master?svg=true)](https://ci.appveyor.com/project/jagrosh/musicbot/branch/master)
[![CodeFactor](https://www.codefactor.io/repository/github/jagrosh/musicbot/badge)](https://www.codefactor.io/repository/github/jagrosh/musicbot)
# JMusicBot
A Discord music bot with a clean interface, and that is easy to set up and run yourself
A cross-platform Discord music bot with a clean interface, and that is easy to set up and run yourself!
[![Setup](http://i.imgur.com/VvXYp5j.png)](https://github.com/jagrosh/MusicBot/wiki/Setup)
# 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)
## 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)
# Setup
## Example
![Loading Example...](https://i.imgur.com/kVtTKvS.gif)
## Setup
Please see the [Setup Page](https://github.com/jagrosh/MusicBot/wiki/Setup) in the wiki to run this bot yourself!
# Questions/Suggestions/Bug Reports
## Questions/Suggestions/Bug Reports
**Please read the [Suggested/Planned Features List](https://github.com/jagrosh/MusicBot/projects/1) before suggesting a feature**. If you'd like to suggest changes to how the bot functions, recommend more customization options, or report bugs, feel free to either open an [Issue](https://github.com/jagrosh/MusicBot/issues) on this repository, or join [my Discord server](https://discord.gg/0p9LSGoRLu6Pet0k). (Note: I will not accept any feature requests that will require additional API keys, nor any non-music features). If you like this bot, be sure to add a star to the libraries that make this possible: [**JDA**](https://github.com/DV8FromTheWorld/JDA) and [**lavaplayer**](https://github.com/sedmelluq/lavaplayer)
# Example
![Example](https://i.imgur.com/tevrtKt.png)
# 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.
## Editing
This bot (and the source code here) might not be easy to edit for inexperienced programmers. The main purpose of having the source public is to show the capabilities of the libraries, to allow others to understand how the bot works, and to allow those knowledgeable about java, JDA, and Discord bot development to contribute. There are many requirements and dependencies required to edit and compile it, and there will not be support provided for people looking to make changes on their own. Instead, consider making a feature request (see the above section). If you choose to make edits, please do so in accordance with the Apache 2.0 License.
+56 -28
View File
@@ -3,14 +3,19 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.jagrosh</groupId>
<artifactId>JMusicBot</artifactId>
<version>0.2.1</version>
<version>Snapshot</version>
<packaging>jar</packaging>
<repositories>
<repository>
<id>central</id>
<name>bintray</name>
<url>http://jcenter.bintray.com</url>
<url>https://jcenter.bintray.com</url>
</repository>
<repository>
<id>bintray-sedmelluq-com.sedmelluq</id>
<name>bintray</name>
<url>https://dl.bintray.com/sedmelluq/com.sedmelluq</url>
</repository>
</repositories>
@@ -18,19 +23,29 @@
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>3.8.1_447</version>
<version>3.8.3_464</version>
</dependency>
<dependency>
<groupId>com.sedmelluq</groupId>
<artifactId>lavaplayer</artifactId>
<version>1.3.10</version>
<version>1.3.50</version>
</dependency>
<dependency>
<groupId>com.sedmelluq</groupId>
<artifactId>lavaplayer-natives-extra</artifactId>
<version>1.3.13</version>
</dependency>
<dependency>
<groupId>com.jagrosh</groupId>
<artifactId>jda-utilities</artifactId>
<version>2.1.4</version>
<version>2.1.5</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.jagrosh</groupId>
<artifactId>JLyrics</artifactId>
<version>0.4</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
@@ -58,28 +73,41 @@
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.jagrosh.jmusicbot.JMusicBot</mainClass>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<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>
@@ -89,4 +117,4 @@
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>
</project>
@@ -23,16 +23,10 @@ import com.typesafe.config.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import net.dv8tion.jda.core.OnlineStatus;
import net.dv8tion.jda.core.entities.Game;
/**
* @author Your Name
* Changes from original source:
* - Changed "searching" to "finding"
*
*
* @author John Grosh (jagrosh)
@@ -41,6 +35,8 @@ 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,
@@ -49,7 +45,9 @@ public class BotConfig
private long owner, maxSeconds;
private OnlineStatus status;
private Game game;
private Config aliases;
private boolean valid = false;
public BotConfig(Prompt prompt)
@@ -65,7 +63,7 @@ public class BotConfig
try
{
// get the path to the config, default config.txt
path = Paths.get(System.getProperty("config.file", System.getProperty("config", "config.txt")));
path = OtherUtil.getPath(System.getProperty("config.file", System.getProperty("config", "config.txt")));
if(path.toFile().exists())
{
if(System.getProperty("config.file") == null)
@@ -77,9 +75,6 @@ public class BotConfig
//Config config = ConfigFactory.parseFile(path.toFile()).withFallback(ConfigFactory.load());
Config config = ConfigFactory.load();
// info on where the config is being loaded from
prompt.alert(Prompt.Level.INFO, CONTEXT, "Loading config from "+path.toAbsolutePath().toString());
// set values
token = config.getString("token");
prefix = config.getString("prefix");
@@ -100,10 +95,11 @@ public class BotConfig
useEval = config.getBoolean("eval");
maxSeconds = config.getLong("maxtime");
playlistsFolder = config.getString("playlistsfolder");
aliases = config.getConfig("aliases");
dbots = owner == 113156185389092864L;
// we may need to get some additional data and write a new config file
List<String> lines = new LinkedList<>();
// 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"))
@@ -114,12 +110,12 @@ public class BotConfig
+ "\nBot Token: ");
if(token==null)
{
prompt.alert(Prompt.Level.WARNING, CONTEXT, "No token provided! Exiting.");
prompt.alert(Prompt.Level.WARNING, CONTEXT, "No token provided! Exiting.\n\nConfig Location: " + path.toAbsolutePath().toString());
return;
}
else
{
lines.add("token="+token);
write = true;
}
}
@@ -140,36 +136,51 @@ public class BotConfig
}
if(owner<=0)
{
prompt.alert(Prompt.Level.ERROR, CONTEXT, "Invalid User ID! Exiting.");
System.exit(0);
prompt.alert(Prompt.Level.ERROR, CONTEXT, "Invalid User ID! Exiting.\n\nConfig Location: " + path.toAbsolutePath().toString());
return;
}
else
{
lines.add("owner="+owner);
write = true;
}
}
if(!lines.isEmpty())
{
StringBuilder builder = new StringBuilder();
lines.stream().forEach(s -> builder.append(s).append("\r\n"));
try
{
Files.write(path, builder.toString().trim().getBytes());
}
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.");
}
}
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());
prompt.alert(Prompt.Level.ERROR, CONTEXT, ex + ": " + ex.getMessage() + "\n\nConfig Location: " + path.toAbsolutePath().toString());
}
}
private void writeToFile()
{
String original = OtherUtil.loadResource(this, "/reference.conf");
byte[] bytes;
if(original==null)
{
bytes = ("token = "+token+"\r\nowner = "+owner).getBytes();
}
else
{
bytes = original.substring(original.indexOf(START_TOKEN)+START_TOKEN.length(), original.indexOf(END_TOKEN))
.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());
}
}
@@ -294,4 +305,16 @@ public class BotConfig
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];
}
}
}
@@ -15,7 +15,6 @@
*/
package com.jagrosh.jmusicbot;
import com.jagrosh.jdautilities.command.CommandClient;
import com.jagrosh.jdautilities.command.CommandClientBuilder;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import com.jagrosh.jdautilities.examples.command.*;
@@ -41,9 +40,9 @@ import org.slf4j.LoggerFactory;
*/
public class JMusicBot
{
public final static String PLAY_EMOJI = "\u25B6";
public final static String PAUSE_EMOJI = "\u23F8";
public final static String STOP_EMOJI = "\u23F9";
public final static String PLAY_EMOJI = "\u25B6"; // ▶
public final static String PAUSE_EMOJI = "\u23F8"; // ⏸
public final static String STOP_EMOJI = "\u23F9"; // ⏹
public final static Permission[] RECOMMENDED_PERMS = new Permission[]{Permission.MESSAGE_READ, Permission.MESSAGE_WRITE, Permission.MESSAGE_HISTORY, Permission.MESSAGE_ADD_REACTION,
Permission.MESSAGE_EMBED_LINKS, Permission.MESSAGE_ATTACH_FILES, Permission.MESSAGE_MANAGE, Permission.MESSAGE_EXT_EMOJI,
Permission.MANAGE_CHANNEL, Permission.VOICE_CONNECT, Permission.VOICE_SPEAK, Permission.NICKNAME_CHANGE};
@@ -59,15 +58,6 @@ public class JMusicBot
Prompt prompt = new Prompt("JMusicBot", "Switching to nogui mode. You can manually start in nogui mode by including the -Dnogui=true flag.",
"true".equalsIgnoreCase(System.getProperty("nogui", "false")));
// check deprecated nogui mode (new way of setting it is -Dnogui=true)
for(String arg: args)
if("-nogui".equalsIgnoreCase(arg))
{
prompt.alert(Prompt.Level.WARNING, "GUI", "The -nogui flag has been deprecated. "
+ "Please use the -Dnogui=true flag before the name of the jar. Example: java -jar -Dnogui=true JMusicBot.jar");
break;
}
// get and check latest version
String version = OtherUtil.checkVersion(prompt);
@@ -87,7 +77,7 @@ public class JMusicBot
new String[]{"High-quality music playback", "FairQueue™ Technology", "Easy to host yourself"},
RECOMMENDED_PERMS);
aboutCommand.setIsAuthor(false);
aboutCommand.setReplacementCharacter("\uD83C\uDFB6");
aboutCommand.setReplacementCharacter("\uD83C\uDFB6"); // 🎶
// set up the command client
CommandClientBuilder cb = new CommandClientBuilder()
@@ -100,36 +90,41 @@ public class JMusicBot
.setGuildSettingsManager(settings)
.addCommands(aboutCommand,
new PingCommand(),
new SettingsCmd(),
new SettingsCmd(bot),
new LyricsCmd(bot),
new NowplayingCmd(bot),
new PlayCmd(bot, config.getLoading()),
new PlayCmd(bot),
new PlaylistsCmd(bot),
new QueueCmd(bot),
new RemoveCmd(bot),
new SearchCmd(bot, config.getSearching()),
new SCSearchCmd(bot, config.getSearching()),
new SearchCmd(bot),
new SCSearchCmd(bot),
new ShuffleCmd(bot),
new SkipCmd(bot),
new ForceRemoveCmd(bot),
new ForceskipCmd(bot),
new MoveTrackCmd(bot),
new PauseCmd(bot),
new PlaynextCmd(bot, config.getLoading()),
new PlaynextCmd(bot),
new RepeatCmd(bot),
new SkiptoCmd(bot),
new StopCmd(bot),
new VolumeCmd(bot),
new SetdjCmd(),
new SettcCmd(),
new SetvcCmd(),
new PrefixCmd(bot),
new SetdjCmd(bot),
new SettcCmd(bot),
new SetvcCmd(bot),
new AutoplaylistCmd(bot),
new DebugCmd(bot),
new PlaylistCmd(bot),
new SetavatarCmd(),
new SetgameCmd(),
new SetavatarCmd(bot),
new SetgameCmd(bot),
new SetnameCmd(bot),
new SetstatusCmd(),
new SetstatusCmd(bot),
new ShutdownCmd(bot)
);
if(config.useEval())
@@ -146,7 +141,6 @@ public class JMusicBot
}
else
cb.setGame(config.getGame());
CommandClient client = cb.build();
if(!prompt.isNoGUI())
{
@@ -164,7 +158,7 @@ public class JMusicBot
}
}
log.info("Loaded config from "+config.getConfigLocation());
log.info("Loaded config from " + config.getConfigLocation());
// attempt to log in and start
try
@@ -173,22 +167,24 @@ public class JMusicBot
.setToken(config.getToken())
.setAudioEnabled(true)
.setGame(nogame ? null : Game.playing("loading..."))
.setStatus(config.getStatus()==OnlineStatus.INVISIBLE||config.getStatus()==OnlineStatus.OFFLINE ? OnlineStatus.INVISIBLE : OnlineStatus.DO_NOT_DISTURB)
.addEventListener(client, waiter, new Listener(bot))
.setStatus(config.getStatus()==OnlineStatus.INVISIBLE || config.getStatus()==OnlineStatus.OFFLINE
? OnlineStatus.INVISIBLE : OnlineStatus.DO_NOT_DISTURB)
.addEventListener(cb.build(), waiter, new Listener(bot))
.setBulkDeleteSplittingEnabled(true)
.build();
bot.setJDA(jda);
}
catch (LoginException ex)
{
log.error(ex+"\nPlease make sure you are "
prompt.alert(Prompt.Level.ERROR, "JMusicBot", ex + "\nPlease make sure you are "
+ "editing the correct config.txt file, and that you have used the "
+ "correct token (not the 'secret'!)");
+ "correct token (not the 'secret'!)\nConfig Location: " + config.getConfigLocation());
System.exit(1);
}
catch(IllegalArgumentException ex)
{
log.error("Some aspect of the configuration is invalid: "+ex);
prompt.alert(Prompt.Level.ERROR, "JMusicBot", "Some aspect of the configuration is "
+ "invalid: " + ex + "\nConfig Location: " + config.getConfigLocation());
System.exit(1);
}
}
@@ -58,13 +58,12 @@ public class Listener extends ListenerAdapter
{
String defpl = bot.getSettingsManager().getSettings(guild).getDefaultPlaylist();
VoiceChannel vc = bot.getSettingsManager().getSettings(guild).getVoiceChannel(guild);
if(defpl!=null && vc!=null)
if(defpl!=null && vc!=null && bot.getPlayerManager().setUpHandler(guild).playFromDefault())
{
if(bot.getPlayerManager().setUpHandler(guild).playFromDefault())
guild.getAudioManager().openAudioConnection(vc);
guild.getAudioManager().openAudioConnection(vc);
}
}
catch(Exception ex) {}
catch(Exception ignore) {}
});
if(bot.getConfig().useUpdateAlerts())
{
@@ -29,8 +29,6 @@ import java.util.Set;
import com.jagrosh.jmusicbot.queue.FairQueue;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import static com.jagrosh.jmusicbot.utils.FormatUtil.formatTime;
import static com.jagrosh.jmusicbot.utils.FormatUtil.volumeIcon;
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioTrack;
import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.JDA;
@@ -168,6 +166,9 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
manager.getBot().getNowplayingHandler().onTrackUpdate(guildId, null, this);
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
@@ -255,11 +256,11 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
if(title==null || title.equals("Unknown Title"))
title = track.getInfo().uri;
return "**"+title+"** ["+(userid==0 ? "autoplay" : "<@"+userid+">")+"]"
+ "\n"+(audioPlayer.isPaused()?"\u23F8":"\u25B6")+" "
+"["+formatTime(track.getDuration())+"] "
+volumeIcon(audioPlayer.getVolume());
+ "\n" + (audioPlayer.isPaused() ? JMusicBot.PAUSE_EMOJI : JMusicBot.PLAY_EMOJI) + " "
+ "[" + FormatUtil.formatTime(track.getDuration()) + "] "
+ FormatUtil.volumeIcon(audioPlayer.getVolume());
}
else return "No music playing \u23F9 " + volumeIcon(audioPlayer.getVolume());
else return "No music playing " + JMusicBot.STOP_EMOJI + " " + FormatUtil.volumeIcon(audioPlayer.getVolume());
}
// Audio Send Handler methods
@@ -18,19 +18,18 @@ package com.jagrosh.jmusicbot.audio;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.entities.Pair;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
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.core.MessageBuilder;
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.TextChannel;
import net.dv8tion.jda.core.exceptions.PermissionException;
import net.dv8tion.jda.core.exceptions.RateLimitedException;
/**
*
@@ -49,7 +48,8 @@ public class NowplayingHandler
public void init()
{
bot.getThreadpool().scheduleWithFixedDelay(() -> updateAll(), 0, 5, TimeUnit.SECONDS);
if(!bot.getConfig().useNPImages())
bot.getThreadpool().scheduleWithFixedDelay(() -> updateAll(), 0, 5, TimeUnit.SECONDS);
}
public void setLastNPMessage(Message m)
@@ -120,12 +120,13 @@ public class NowplayingHandler
{
try
{
if(wait)
tchan.getManager().setTopic(text).complete();
else
tchan.getManager().setTopic(text).queue();
// normally here if 'wait' was false, we'd want to queue, however,
// new discord ratelimits specifically limiting changing channel topics
// mean we don't want a backlog of changes piling up, so if we hit a
// ratelimit, we just won't change the topic this time
tchan.getManager().setTopic(text).complete(wait);
}
catch(PermissionException e) {}
catch(PermissionException | RateLimitedException ignore) {}
}
}
}
@@ -15,6 +15,7 @@
*/
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.core.Permission;
@@ -29,17 +30,19 @@ public abstract class DJCommand extends MusicCommand
public DJCommand(Bot bot)
{
super(bot);
this.category = 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;
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());
});
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());
}
}
@@ -52,7 +52,7 @@ public abstract class MusicCommand extends Command
try
{
event.getMessage().delete().queue();
} catch(PermissionException e){}
} catch(PermissionException ignore){}
event.replyInDm(event.getClient().getError()+" You can only use that command in "+tchannel.getAsMention()+"!");
return;
}
@@ -73,6 +73,14 @@ public abstract class MusicCommand extends Command
event.replyError("You must be listening in "+(current==null ? "a voice channel" : "**"+current.getName()+"**")+" to use that!");
return;
}
VoiceChannel afkChannel = userState.getGuild().getAfkChannel();
if(afkChannel != null && afkChannel.equals(userState.getChannel()))
{
event.replyError("You cannot use that command in an AFK channel!");
return;
}
if(!event.getGuild().getSelfMember().getVoiceState().inVoiceChannel())
{
try
@@ -0,0 +1,58 @@
/*
* Copyright 2018 John Grosh <john.a.grosh@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.commands.admin;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.commands.AdminCommand;
import com.jagrosh.jmusicbot.settings.Settings;
/**
*
* @author John Grosh (john.a.grosh@gmail.com)
*/
public class PrefixCmd extends AdminCommand
{
public PrefixCmd(Bot bot)
{
this.name = "prefix";
this.help = "sets a server-specific prefix";
this.arguments = "<prefix|NONE>";
this.aliases = bot.getConfig().getAliases(this.name);
}
@Override
protected void execute(CommandEvent event)
{
if(event.getArgs().isEmpty())
{
event.replyError("Please include a prefix or NONE");
return;
}
Settings s = event.getClient().getSettingsFor(event.getGuild());
if(event.getArgs().equalsIgnoreCase("none"))
{
s.setPrefix(null);
event.replySuccess("Prefix cleared.");
}
else
{
s.setPrefix(event.getArgs());
event.replySuccess("Custom prefix set to `" + event.getArgs() + "` on *" + event.getGuild().getName() + "*");
}
}
}
@@ -18,6 +18,7 @@ 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.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
@@ -29,11 +30,12 @@ import net.dv8tion.jda.core.entities.Role;
*/
public class SetdjCmd extends AdminCommand
{
public SetdjCmd()
public SetdjCmd(Bot bot)
{
this.name = "setdj";
this.help = "sets the DJ role for certain music commands";
this.arguments = "<rolename|NONE>";
this.aliases = bot.getConfig().getAliases(this.name);
}
@Override
@@ -18,6 +18,7 @@ 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.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
@@ -29,11 +30,12 @@ import net.dv8tion.jda.core.entities.TextChannel;
*/
public class SettcCmd extends AdminCommand
{
public SettcCmd()
public SettcCmd(Bot 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
@@ -18,6 +18,7 @@ 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.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
@@ -29,11 +30,12 @@ import net.dv8tion.jda.core.entities.VoiceChannel;
*/
public class SetvcCmd extends AdminCommand
{
public SetvcCmd()
public SetvcCmd(Bot 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
@@ -0,0 +1,119 @@
/*
* 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 net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.User;
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!");
return;
}
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
if (handler.getQueue().isEmpty())
{
event.replyError("There is nothing in the queue!");
return;
}
User target;
List<Member> found = FinderUtil.findMembers(event.getArgs(), event.getGuild());
if(found.isEmpty())
{
event.replyError("Unable to find the user!");
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)
{
int count = ((AudioHandler) event.getGuild().getAudioManager().getSendingHandler()).getQueue().removeAll(target.getIdLong());
if (count == 0)
{
event.replyWarning("**"+target.getName()+"** doesn't have any songs in the queue!");
}
else
{
event.replySuccess("Successfully removed `"+count+"` entries from **"+target.getName()+"**#"+target.getDiscriminator()+".");
}
}
}
@@ -32,7 +32,7 @@ public class ForceskipCmd extends DJCommand
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;
}
@@ -0,0 +1,85 @@
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.queue.FairQueue;
/**
* 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.");
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.");
return;
}
if (from == to)
{
event.replyError("Can't move a track to the same position.");
return;
}
// Validate that from and to are available
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
FairQueue<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);
return;
}
if (isUnavailablePosition(queue, to))
{
String reply = String.format("`%d` is not a valid position in the queue!", to);
event.replyError(reply);
return;
}
// Move the track
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);
}
private static boolean isUnavailablePosition(FairQueue<QueuedTrack> queue, int position)
{
return (position < 1 || position > queue.size());
}
}
@@ -31,6 +31,7 @@ public class PauseCmd extends DJCommand
super(bot);
this.name = "pause";
this.help = "pauses the current song";
this.aliases = bot.getConfig().getAliases(this.name);
this.bePlaying = true;
}
@@ -16,23 +16,16 @@
package com.jagrosh.jmusicbot.commands.dj;
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.music.PlayCmd;
import static com.jagrosh.jmusicbot.commands.music.PlayCmd.CANCEL;
import static com.jagrosh.jmusicbot.commands.music.PlayCmd.LOAD;
import com.jagrosh.jmusicbot.utils.FormatUtil;
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 java.util.concurrent.TimeUnit;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.exceptions.PermissionException;
/**
*
@@ -42,13 +35,14 @@ public class PlaynextCmd extends DJCommand
{
private final String loadingEmoji;
public PlaynextCmd(Bot bot, String loadingEmoji)
public PlaynextCmd(Bot bot)
{
super(bot);
this.loadingEmoji = loadingEmoji;
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;
}
@@ -69,9 +63,9 @@ public class PlaynextCmd extends DJCommand
private class ResultHandler implements AudioLoadResultHandler
{
final Message m;
final CommandEvent event;
final boolean ytsearch;
private final Message m;
private final CommandEvent event;
private final boolean ytsearch;
private ResultHandler(Message m, CommandEvent event, boolean ytsearch)
{
@@ -32,6 +32,7 @@ public class RepeatCmd extends DJCommand
this.name = "repeat";
this.help = "re-adds music to the queue when finished";
this.arguments = "[on|off]";
this.aliases = bot.getConfig().getAliases(this.name);
this.guildOnly = true;
}
@@ -63,5 +64,5 @@ public class RepeatCmd extends DJCommand
}
@Override
public void doCommand(CommandEvent event) {}
public void doCommand(CommandEvent event) { /* Intentionally Empty */ }
}
@@ -32,7 +32,7 @@ public class SkiptoCmd extends DJCommand
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;
}
@@ -31,6 +31,7 @@ public class StopCmd extends DJCommand
super(bot);
this.name = "stop";
this.help = "stops the current song and clears the queue";
this.aliases = bot.getConfig().getAliases(this.name);
this.bePlaying = false;
}
@@ -32,7 +32,7 @@ public class VolumeCmd extends DJCommand
{
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]";
}
@@ -19,6 +19,7 @@ import com.jagrosh.jdautilities.command.Command;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.MessageBuilder;
import net.dv8tion.jda.core.entities.Role;
@@ -31,11 +32,13 @@ import net.dv8tion.jda.core.entities.VoiceChannel;
*/
public class SettingsCmd extends Command
{
public SettingsCmd()
private final static String EMOJI = "\uD83C\uDFA7"; // 🎧
public SettingsCmd(Bot bot)
{
this.name = "settings";
this.help = "shows the bots settings";
this.aliases = new String[]{"status"};
this.aliases = bot.getConfig().getAliases(this.name);
this.guildOnly = true;
}
@@ -44,23 +47,24 @@ public class SettingsCmd extends Command
{
Settings s = event.getClient().getSettingsFor(event.getGuild());
MessageBuilder builder = new MessageBuilder()
.append("\uD83C\uDFA7 **")
.append(event.getSelfUser().getName())
.append(EMOJI + " **")
.append(FormatUtil.filter(event.getSelfUser().getName()))
.append("** settings:");
TextChannel tchan = s.getTextChannel(event.getGuild());
VoiceChannel vchan = s.getVoiceChannel(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.getName()+"**")
+ "\nDJ Role: "+(role==null ? "None" : "**"+role.getName()+"**")
+ "\nRepeat Mode: **"+(s.getRepeatMode() ? "On" : "Off")+"**"
+ "\nDefault Playlist: "+(s.getDefaultPlaylist()==null ? "None" : "**"+s.getDefaultPlaylist()+"**")
.setDescription("Text Channel: " + (tchan == null ? "Any" : "**#" + tchan.getName() + "**")
+ "\nVoice Channel: " + (vchan == null ? "Any" : "**" + vchan.getName() + "**")
+ "\nDJ Role: " + (role == null ? "None" : "**" + role.getName() + "**")
+ "\nCustom Prefix: " + (s.getPrefix() == null ? "None" : "`" + s.getPrefix() + "`")
+ "\nRepeat Mode: **" + (s.getRepeatMode() ? "On" : "Off") + "**"
+ "\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);
.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();
}
@@ -0,0 +1,92 @@
/*
* 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 net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.Permission;
/**
*
* @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 to the currently-playing song";
this.aliases = bot.getConfig().getAliases(this.name);
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
this.bePlaying = true;
}
@Override
public void doCommand(CommandEvent event)
{
event.getChannel().sendTyping().queue();
String title;
if(event.getArgs().isEmpty())
title = ((AudioHandler)event.getGuild().getAudioManager().getSendingHandler()).getPlayer().getPlayingTrack().getInfo().title;
else
title = event.getArgs();
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]`)" : ""));
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());
});
}
}
@@ -33,7 +33,7 @@ public class NowplayingCmd extends MusicCommand
super(bot);
this.name = "nowplaying";
this.help = "shows the song that is currently playing";
this.aliases = new String[]{"np","current"};
this.aliases = bot.getConfig().getAliases(this.name);
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
}
@@ -26,14 +26,13 @@ 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.playlist.PlaylistLoader.Playlist;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import java.util.concurrent.TimeUnit;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.exceptions.PermissionException;
/**
@@ -42,18 +41,19 @@ import net.dv8tion.jda.core.exceptions.PermissionException;
*/
public class PlayCmd extends MusicCommand
{
public final static String LOAD = "\uD83D\uDCE5";
public final static String CANCEL = "\uD83D\uDEAB";
private final static String LOAD = "\uD83D\uDCE5"; // 📥
private final static String CANCEL = "\uD83D\uDEAB"; // 🚫
private final String loadingEmoji;
public PlayCmd(Bot bot, String loadingEmoji)
public PlayCmd(Bot bot)
{
super(bot);
this.loadingEmoji = loadingEmoji;
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)};
@@ -67,20 +67,13 @@ public class PlayCmd extends MusicCommand
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
if(handler.getPlayer().getPlayingTrack()!=null && handler.getPlayer().isPaused())
{
boolean isDJ = event.getMember().hasPermission(Permission.MANAGE_SERVER);
if(!isDJ)
isDJ = event.isOwner();
Settings settings = event.getClient().getSettingsFor(event.getGuild());
Role dj = settings.getRole(event.getGuild());
if(!isDJ && dj!=null)
isDJ = event.getMember().getRoles().contains(dj);
if(!isDJ)
event.replyError("Only DJs can unpause the player!");
else
if(DJCommand.checkDJPermission(event))
{
handler.getPlayer().setPaused(false);
event.replySuccess("Resumed **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**.");
}
else
event.replyError("Only DJs can unpause the player!");
return;
}
StringBuilder builder = new StringBuilder(event.getClient().getWarning()+" Play Commands:\n");
@@ -99,9 +92,9 @@ public class PlayCmd extends MusicCommand
private class ResultHandler implements AudioLoadResultHandler
{
final Message m;
final CommandEvent event;
final boolean ytsearch;
private final Message m;
private final CommandEvent event;
private final boolean ytsearch;
private ResultHandler(Message m, CommandEvent event, boolean ytsearch)
{
@@ -139,7 +132,7 @@ public class PlayCmd extends MusicCommand
m.editMessage(addMsg).queue();
}).setFinalAction(m ->
{
try{m.clearReactions().queue();}catch(PermissionException ex){}
try{ m.clearReactions().queue(); }catch(PermissionException ignore) {}
}).build().display(m);
}
}
@@ -31,7 +31,7 @@ public class PlaylistsCmd extends MusicCommand
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;
@@ -37,23 +37,27 @@ import net.dv8tion.jda.core.exceptions.PermissionException;
*/
public class QueueCmd extends MusicCommand
{
private final static String REPEAT = "\uD83D\uDD01"; // 🔁
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 = new String[]{"list"};
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 e){}})
.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);
}
@@ -66,7 +70,7 @@ public class QueueCmd extends MusicCommand
{
pagenum = Integer.parseInt(event.getArgs());
}
catch(NumberFormatException e){}
catch(NumberFormatException ignore){}
AudioHandler ah = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
List<QueuedTrack> list = ah.getQueue().getList();
if(list.isEmpty())
@@ -110,6 +114,6 @@ public class QueueCmd extends MusicCommand
}
return FormatUtil.filter(sb.append(success).append(" Current Queue | ").append(songslength)
.append(" entries | `").append(FormatUtil.formatTime(total)).append("` ")
.append(repeatmode ? "| \uD83D\uDD01" : "").toString());
.append(repeatmode ? "| " + REPEAT : "").toString());
}
}
@@ -36,7 +36,7 @@ public class RemoveCmd extends MusicCommand
this.name = "remove";
this.help = "removes a song from the queue";
this.arguments = "<position|ALL>";
this.aliases = new String[]{"delete"};
this.aliases = bot.getConfig().getAliases(this.name);
this.beListening = true;
this.bePlaying = true;
}
@@ -23,12 +23,12 @@ import com.jagrosh.jmusicbot.Bot;
*/
public class SCSearchCmd extends SearchCmd
{
public SCSearchCmd(Bot bot, String searchingEmoji)
public SCSearchCmd(Bot bot)
{
super(bot, searchingEmoji);
super(bot);
this.searchPrefix = "scsearch:";
this.name = "scsearch";
this.help = "searches Soundcloud for a provided query";
this.aliases = new String[]{};
this.aliases = bot.getConfig().getAliases(this.name);
}
}
@@ -41,12 +41,12 @@ public class SearchCmd extends MusicCommand
private final OrderedMenu.Builder builder;
private final String searchingEmoji;
public SearchCmd(Bot bot, String searchingEmoji)
public SearchCmd(Bot bot)
{
super(bot);
this.searchingEmoji = searchingEmoji;
this.searchingEmoji = bot.getConfig().getSearching();
this.name = "search";
this.aliases = new String[]{"ytsearch"};
this.aliases = bot.getConfig().getAliases(this.name);
this.arguments = "<query>";
this.help = "searches Youtube for a provided query";
this.beListening = true;
@@ -73,8 +73,9 @@ public class SearchCmd extends MusicCommand
private class ResultHandler implements AudioLoadResultHandler
{
final Message m;
final CommandEvent event;
private final Message m;
private final CommandEvent event;
private ResultHandler(Message m, CommandEvent event)
{
this.m = m;
@@ -114,8 +115,8 @@ public class SearchCmd extends MusicCommand
}
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1;
event.replySuccess("Added **"+track.getInfo().title
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
event.replySuccess("Added **" + FormatUtil.filter(track.getInfo().title)
+ "** (`" + FormatUtil.formatTime(track.getDuration()) + "`) " + (pos==0 ? "to begin playing"
: " to the queue at position "+pos));
})
.setCancel((msg) -> {})
@@ -31,6 +31,7 @@ public class ShuffleCmd extends MusicCommand
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;
}
@@ -32,7 +32,7 @@ public class SkipCmd extends MusicCommand
super(bot);
this.name = "skip";
this.help = "votes to skip the current song";
this.aliases = new String[]{"voteskip"};
this.aliases = bot.getConfig().getAliases(this.name);
this.beListening = true;
this.bePlaying = true;
}
@@ -35,6 +35,7 @@ public class AutoplaylistCmd extends OwnerCommand
this.name = "autoplaylist";
this.arguments = "<name|NONE>";
this.help = "sets the default playlist for the server";
this.aliases = bot.getConfig().getAliases(this.name);
}
@Override
@@ -0,0 +1,87 @@
/*
* 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.jdautilities.commons.JDAUtilitiesInfo;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.commands.OwnerCommand;
import com.jagrosh.jmusicbot.utils.OtherUtil;
import com.sedmelluq.discord.lavaplayer.tools.PlayerLibrary;
import net.dv8tion.jda.core.JDAInfo;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.ChannelType;
/**
*
* @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"};
private final Bot bot;
public DebugCmd(Bot bot)
{
this.bot = bot;
this.name = "debug";
this.help = "shows debug info";
this.aliases = bot.getConfig().getAliases(this.name);
this.guildOnly = false;
}
@Override
protected void execute(CommandEvent event)
{
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(JDAUtilitiesInfo.VERSION)
.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.getTextChannel(), Permission.MESSAGE_ATTACH_FILES))
event.getChannel().sendFile(sb.toString().getBytes(), "debug_information.txt").queue();
else
event.reply("Debug Information: " + sb.toString());
}
}
@@ -27,10 +27,14 @@ import com.jagrosh.jmusicbot.commands.OwnerCommand;
*/
public class EvalCmd extends OwnerCommand
{
private final Bot bot;
public EvalCmd(Bot bot)
{
this.bot = bot;
this.name = "eval";
this.help = "evaluates nashorn code";
this.aliases = bot.getConfig().getAliases(this.name);
this.guildOnly = false;
}
@@ -38,6 +42,7 @@ public class EvalCmd extends OwnerCommand
protected void execute(CommandEvent event)
{
ScriptEngine se = new ScriptEngineManager().getEngineByName("Nashorn");
se.put("bot", bot);
se.put("event", event);
se.put("jda", event.getJDA());
se.put("guild", event.getGuild());
@@ -21,7 +21,6 @@ import com.jagrosh.jdautilities.command.Command;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.commands.OwnerCommand;
import com.jagrosh.jmusicbot.commands.owner.AutoplaylistCmd;
import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist;
/**
@@ -38,6 +37,7 @@ public class PlaylistCmd extends OwnerCommand
this.name = "playlist";
this.arguments = "<append|delete|make|setdefault>";
this.help = "playlist management";
this.aliases = bot.getConfig().getAliases(this.name);
this.children = new OwnerCommand[]{
new ListCmd(),
new AppendlistCmd(),
@@ -18,6 +18,7 @@ package com.jagrosh.jmusicbot.commands.owner;
import java.io.IOException;
import java.io.InputStream;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.commands.OwnerCommand;
import com.jagrosh.jmusicbot.utils.OtherUtil;
import net.dv8tion.jda.core.entities.Icon;
@@ -28,11 +29,12 @@ import net.dv8tion.jda.core.entities.Icon;
*/
public class SetavatarCmd extends OwnerCommand
{
public SetavatarCmd()
public SetavatarCmd(Bot bot)
{
this.name = "setavatar";
this.help = "sets the avatar of the bot";
this.arguments = "<url>";
this.aliases = bot.getConfig().getAliases(this.name);
this.guildOnly = false;
}
@@ -16,6 +16,7 @@
package com.jagrosh.jmusicbot.commands.owner;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.commands.OwnerCommand;
import net.dv8tion.jda.core.entities.Game;
@@ -25,11 +26,12 @@ import net.dv8tion.jda.core.entities.Game;
*/
public class SetgameCmd extends OwnerCommand
{
public SetgameCmd()
public SetgameCmd(Bot 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(),
@@ -31,19 +31,25 @@ public class SetnameCmd extends OwnerCommand
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
protected void execute(CommandEvent event)
{
try {
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) {
}
catch(RateLimitedException e)
{
event.reply(event.getClient().getError()+" Name can only be changed twice per hour!");
} catch(Exception e) {
}
catch(Exception e)
{
event.reply(event.getClient().getError()+" That name is not valid!");
}
}
@@ -16,6 +16,7 @@
package com.jagrosh.jmusicbot.commands.owner;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.commands.OwnerCommand;
import net.dv8tion.jda.core.OnlineStatus;
@@ -25,11 +26,12 @@ import net.dv8tion.jda.core.OnlineStatus;
*/
public class SetstatusCmd extends OwnerCommand
{
public SetstatusCmd()
public SetstatusCmd(Bot bot)
{
this.name = "setstatus";
this.help = "sets the status the bot displays";
this.arguments = "<status>";
this.aliases = bot.getConfig().getAliases(this.name);
this.guildOnly = false;
}
@@ -32,6 +32,7 @@ public class ShutdownCmd extends OwnerCommand
this.bot = bot;
this.name = "shutdown";
this.help = "safely shuts down";
this.aliases = bot.getConfig().getAliases(this.name);
this.guildOnly = false;
}
@@ -61,9 +61,18 @@ public class Prompt
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;
case INFO:
log.info(message);
break;
case WARNING:
log.warn(message);
break;
case ERROR:
log.error(message);
break;
default:
log.info(message);
break;
}
}
else
@@ -73,9 +82,18 @@ public class Prompt
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;
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);
}
@@ -51,7 +51,7 @@ public class GUI extends JFrame
setVisible(true);
addWindowListener(new WindowListener()
{
@Override public void windowOpened(WindowEvent e) {}
@Override public void windowOpened(WindowEvent e) { /* unused */ }
@Override public void windowClosing(WindowEvent e)
{
try
@@ -63,11 +63,11 @@ public class GUI extends JFrame
System.exit(0);
}
}
@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) {}
@Override public void windowClosed(WindowEvent e) { /* unused */ }
@Override public void windowIconified(WindowEvent e) { /* unused */ }
@Override public void windowDeiconified(WindowEvent e) { /* unused */ }
@Override public void windowActivated(WindowEvent e) { /* unused */ }
@Override public void windowDeactivated(WindowEvent e) { /* unused */ }
});
}
}
@@ -1,111 +0,0 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.gui;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.entities.Guild;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class GuildsPanel extends JPanel
{
private final Bot bot;
private final JList guildList;
private final JTextArea guildQueue;
private int index = -1;
public GuildsPanel(Bot bot)
{
super();
super.setLayout(new GridBagLayout());
this.bot = bot;
guildList = new JList();
guildQueue = new JTextArea();
guildList.setModel(new DefaultListModel());
guildList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
guildList.setFixedCellHeight(20);
guildList.setPreferredSize(new Dimension(100,300));
guildQueue.setPreferredSize(new Dimension(300,300));
guildQueue.setEditable(false);
JScrollPane pane = new JScrollPane();
JScrollPane pane2 = new JScrollPane();
pane.setViewportView(guildList);
pane2.setViewportView(guildQueue);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.LINE_START;
c.gridx = 0;
c.gridwidth = 1;
super.add(pane, c);
c.gridx = 1;
c.gridwidth = 3;
super.add(pane2, c);
//bot.registerPanel(this);
guildList.addListSelectionListener((ListSelectionEvent e) -> {
index = guildList.getSelectedIndex();
//bot.updatePanel();
});
}
public void updateList(List<Guild> guilds)
{
String[] strs = new String[guilds.size()];
for(int i=0; i<guilds.size(); i++)
strs[i] = guilds.get(i).getName();
guildList.setListData(strs);
}
public int getIndex()
{
return guildList.getSelectedIndex();
}
/*public void updatePanel(AudioHandler handler) {
StringBuilder builder = new StringBuilder("Now Playing: ");
if(handler==null || handler.getCurrentTrack()==null)
{
builder.append("nothing");
}
else
{
builder.append(handler.getCurrentTrack().getTrack().getInfo().title)
.append(" [")
.append(FormatUtil.formatTime(handler.getCurrentTrack().getTrack().getDuration()))
.append("]\n");
for(int i=0; i<handler.getQueue().size(); i++)
builder.append("\n").append(i+1).append(". ").append(handler.getQueue().get(i).getTrack().getInfo().title);
}
guildQueue.setText(builder.toString());
guildQueue.updateUI();
}*/
}
@@ -1,147 +1,157 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.gui;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
/**
*
* @author Lawrence Dol
*/
public class TextAreaOutputStream extends OutputStream {
// *************************************************************************************************
// INSTANCE MEMBERS
// *************************************************************************************************
private byte[] oneByte; // array for write(int val);
private Appender appender; // most recent action
public TextAreaOutputStream(JTextArea txtara) {
this(txtara,1000);
}
public TextAreaOutputStream(JTextArea txtara, int maxlin) {
if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); }
oneByte=new byte[1];
appender=new Appender(txtara,maxlin);
}
/** Clear the current console text area. */
public synchronized void clear() {
if(appender!=null) { appender.clear(); }
}
@Override
public synchronized void close() {
appender=null;
}
@Override
public synchronized void flush() {
}
@Override
public synchronized void write(int val) {
oneByte[0]=(byte)val;
write(oneByte,0,1);
}
@Override
public synchronized void write(byte[] ba) {
write(ba,0,ba.length);
}
@Override
public synchronized void write(byte[] ba,int str,int len) {
if(appender!=null) { appender.append(bytesToString(ba,str,len)); }
}
//@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING")
static private String bytesToString(byte[] ba, int str, int len) {
try { return new String(ba,str,len,"UTF-8"); } catch(UnsupportedEncodingException thr) { return new String(ba,str,len); } // all JVMs are required to support UTF-8
}
// *************************************************************************************************
// STATIC MEMBERS
// *************************************************************************************************
static class Appender
implements Runnable
{
private final JTextArea textArea;
private final int maxLines; // maximum lines allowed in text area
private final LinkedList<Integer> lengths; // length of lines within text area
private final List<String> values; // values waiting to be appended
private int curLength; // length of current line
private boolean clear;
private boolean queue;
Appender(JTextArea txtara, int maxlin) {
textArea =txtara;
maxLines =maxlin;
lengths =new LinkedList<>();
values =new ArrayList<>();
curLength=0;
clear =false;
queue =true;
}
synchronized void append(String val) {
values.add(val);
if(queue) { queue=false; EventQueue.invokeLater(this); }
}
synchronized void clear() {
clear=true;
curLength=0;
lengths.clear();
values.clear();
if(queue) { queue=false; EventQueue.invokeLater(this); }
}
// MUST BE THE ONLY METHOD THAT TOUCHES textArea!
@Override
public synchronized void run() {
if(clear) { textArea.setText(""); }
values.stream().map((val) -> {
curLength+=val.length();
return val;
}).map((val) -> {
if(val.endsWith(EOL1) || val.endsWith(EOL2)) {
if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); }
lengths.addLast(curLength);
curLength=0;
}
return val;
}).forEach((val) -> {
textArea.append(val);
});
values.clear();
clear =false;
queue =true;
}
static private final String EOL1="\n";
static private final String EOL2=System.getProperty("line.separator",EOL1);
}
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jagrosh.jmusicbot.gui;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
/**
*
* @author Lawrence Dol
*/
public class TextAreaOutputStream extends OutputStream {
// *************************************************************************************************
// INSTANCE MEMBERS
// *************************************************************************************************
private byte[] oneByte; // array for write(int val);
private Appender appender; // most recent action
public TextAreaOutputStream(JTextArea txtara) {
this(txtara,1000);
}
public TextAreaOutputStream(JTextArea txtara, int maxlin) {
if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); }
oneByte=new byte[1];
appender=new Appender(txtara,maxlin);
}
/** Clear the current console text area. */
public synchronized void clear() {
if(appender!=null) { appender.clear(); }
}
@Override
public synchronized void close() {
appender=null;
}
@Override
public synchronized void flush() {
/* empty */
}
@Override
public synchronized void write(int val) {
oneByte[0]=(byte)val;
write(oneByte,0,1);
}
@Override
public synchronized void write(byte[] ba) {
write(ba,0,ba.length);
}
@Override
public synchronized void write(byte[] ba,int str,int len) {
if(appender!=null) { appender.append(bytesToString(ba,str,len)); }
}
//@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING")
static private String bytesToString(byte[] ba, int str, int len) {
try {
return new String(ba,str,len,"UTF-8");
} catch(UnsupportedEncodingException thr) {
return new String(ba,str,len);
} // all JVMs are required to support UTF-8
}
// *************************************************************************************************
// STATIC MEMBERS
// *************************************************************************************************
static class Appender
implements Runnable
{
static private final String EOL1="\n";
static private final String EOL2=System.getProperty("line.separator",EOL1);
private final JTextArea textArea;
private final int maxLines; // maximum lines allowed in text area
private final LinkedList<Integer> lengths; // length of lines within text area
private final List<String> values; // values waiting to be appended
private int curLength; // length of current line
private boolean clear;
private boolean queue;
Appender(JTextArea txtara, int maxlin) {
textArea =txtara;
maxLines =maxlin;
lengths =new LinkedList<>();
values =new ArrayList<>();
curLength=0;
clear =false;
queue =true;
}
private synchronized void append(String val) {
values.add(val);
if(queue) {
queue=false;
EventQueue.invokeLater(this);
}
}
private synchronized void clear() {
clear=true;
curLength=0;
lengths.clear();
values.clear();
if(queue) {
queue=false;
EventQueue.invokeLater(this);
}
}
// MUST BE THE ONLY METHOD THAT TOUCHES textArea!
@Override
public synchronized void run() {
if(clear) { textArea.setText(""); }
values.stream().map((val) -> {
curLength+=val.length();
return val;
}).map((val) -> {
if(val.endsWith(EOL1) || val.endsWith(EOL2)) {
if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); }
lengths.addLast(curLength);
curLength=0;
}
return val;
}).forEach((val) -> {
textArea.append(val);
});
values.clear();
clear =false;
queue =true;
}
}
} /* END PUBLIC CLASS */
@@ -16,6 +16,7 @@
package com.jagrosh.jmusicbot.playlist;
import com.jagrosh.jmusicbot.BotConfig;
import com.jagrosh.jmusicbot.utils.OtherUtil;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
@@ -24,7 +25,6 @@ import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -61,29 +61,29 @@ public class PlaylistLoader
{
try
{
Files.createDirectory(Paths.get(config.getPlaylistsFolder()));
Files.createDirectory(OtherUtil.getPath(config.getPlaylistsFolder()));
}
catch (IOException ex) {}
catch (IOException ignore) {}
}
public boolean folderExists()
{
return Files.exists(Paths.get(config.getPlaylistsFolder()));
return Files.exists(OtherUtil.getPath(config.getPlaylistsFolder()));
}
public void createPlaylist(String name) throws IOException
{
Files.createFile(Paths.get(config.getPlaylistsFolder()+File.separator+name+".txt"));
Files.createFile(OtherUtil.getPath(config.getPlaylistsFolder()+File.separator+name+".txt"));
}
public void deletePlaylist(String name) throws IOException
{
Files.delete(Paths.get(config.getPlaylistsFolder()+File.separator+name+".txt"));
Files.delete(OtherUtil.getPath(config.getPlaylistsFolder()+File.separator+name+".txt"));
}
public void writePlaylist(String name, String text) throws IOException
{
Files.write(Paths.get(config.getPlaylistsFolder()+File.separator+name+".txt"), text.trim().getBytes());
Files.write(OtherUtil.getPath(config.getPlaylistsFolder()+File.separator+name+".txt"), text.trim().getBytes());
}
public Playlist getPlaylist(String name)
@@ -96,7 +96,7 @@ public class PlaylistLoader
{
boolean[] shuffle = {false};
List<String> list = new ArrayList<>();
Files.readAllLines(Paths.get(config.getPlaylistsFolder()+File.separator+name+".txt")).forEach(str ->
Files.readAllLines(OtherUtil.getPath(config.getPlaylistsFolder()+File.separator+name+".txt")).forEach(str ->
{
String s = str.trim();
if(s.isEmpty())
@@ -157,101 +157,90 @@ public class PlaylistLoader
public void loadTracks(AudioPlayerManager manager, Consumer<AudioTrack> consumer, Runnable callback)
{
if(!loaded)
if(loaded)
return;
loaded = true;
for(int i=0; i<items.size(); i++)
{
loaded = true;
for(int i=0; i<items.size(); i++)
boolean last = i+1 == items.size();
int index = i;
manager.loadItemOrdered(name, items.get(i), new AudioLoadResultHandler()
{
boolean last = i+1==items.size();
int index = i;
manager.loadItemOrdered(name, items.get(i), new AudioLoadResultHandler()
private void done()
{
@Override
public void trackLoaded(AudioTrack at)
if(last)
{
if(config.isTooLong(at))
errors.add(new PlaylistLoadError(index, items.get(index), "This track is longer than the allowed maximum"));
else
{
at.setUserData(0L);
tracks.add(at);
consumer.accept(at);
}
if(last && callback!=null)
callback.run();
}
@Override
public void playlistLoaded(AudioPlaylist ap)
{
if(ap.isSearchResult())
{
if(config.isTooLong(ap.getTracks().get(0)))
errors.add(new PlaylistLoadError(index, items.get(index), "This track is longer than the allowed maximum"));
else
{
ap.getTracks().get(0).setUserData(0L);
tracks.add(ap.getTracks().get(0));
consumer.accept(ap.getTracks().get(0));
}
}
else if(ap.getSelectedTrack()!=null)
{
if(config.isTooLong(ap.getSelectedTrack()))
errors.add(new PlaylistLoadError(index, items.get(index), "This track is longer than the allowed maximum"));
else
{
ap.getSelectedTrack().setUserData(0L);
tracks.add(ap.getSelectedTrack());
consumer.accept(ap.getSelectedTrack());
}
}
else
{
List<AudioTrack> loaded = new ArrayList<>(ap.getTracks());
if(shuffle)
for(int first =0; first<loaded.size(); first++)
{
int second = (int)(Math.random()*loaded.size());
AudioTrack tmp = loaded.get(first);
loaded.set(first, loaded.get(second));
loaded.set(second, tmp);
}
loaded.removeIf(track -> config.isTooLong(track));
loaded.forEach(at -> at.setUserData(0L));
tracks.addAll(loaded);
loaded.forEach(at -> consumer.accept(at));
}
if(last && callback!=null)
if(shuffle)
shuffleTracks();
if(callback != null)
callback.run();
}
}
@Override
public void noMatches()
@Override
public void trackLoaded(AudioTrack at)
{
if(config.isTooLong(at))
errors.add(new PlaylistLoadError(index, items.get(index), "This track is longer than the allowed maximum"));
else
{
errors.add(new PlaylistLoadError(index, items.get(index), "No matches found."));
if(last && callback!=null)
callback.run();
at.setUserData(0L);
tracks.add(at);
consumer.accept(at);
}
done();
}
@Override
public void loadFailed(FriendlyException fe)
@Override
public void playlistLoaded(AudioPlaylist ap)
{
if(ap.isSearchResult())
{
errors.add(new PlaylistLoadError(index, items.get(index), "Failed to load track: "+fe.getLocalizedMessage()));
if(last && callback!=null)
callback.run();
trackLoaded(ap.getTracks().get(0));
}
});
}
else if(ap.getSelectedTrack()!=null)
{
trackLoaded(ap.getSelectedTrack());
}
else
{
List<AudioTrack> loaded = new ArrayList<>(ap.getTracks());
if(shuffle)
for(int first =0; first<loaded.size(); first++)
{
int second = (int)(Math.random()*loaded.size());
AudioTrack tmp = loaded.get(first);
loaded.set(first, loaded.get(second));
loaded.set(second, tmp);
}
loaded.removeIf(track -> config.isTooLong(track));
loaded.forEach(at -> at.setUserData(0L));
tracks.addAll(loaded);
loaded.forEach(at -> consumer.accept(at));
}
done();
}
@Override
public void noMatches()
{
errors.add(new PlaylistLoadError(index, items.get(index), "No matches found."));
done();
}
@Override
public void loadFailed(FriendlyException fe)
{
errors.add(new PlaylistLoadError(index, items.get(index), "Failed to load track: "+fe.getLocalizedMessage()));
done();
}
});
}
if(shuffle)
shuffleTracks();
}
public void shuffleTracks()
{
if(tracks!=null)
shuffle(tracks);
shuffle(tracks);
}
public String getName()
@@ -128,4 +128,17 @@ public class FairQueue<T extends Queueable> {
for(int i=0; i<number; i++)
list.remove(0);
}
/**
* Move an item to a different position in the list
* @param from The position of the item
* @param to The new position of the item
* @return the moved item
*/
public T moveItem(int from, int to)
{
T item = list.remove(from);
list.add(to, item);
return item;
}
}
@@ -16,6 +16,8 @@
package com.jagrosh.jmusicbot.settings;
import com.jagrosh.jdautilities.command.GuildSettingsProvider;
import java.util.Collection;
import java.util.Collections;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.entities.TextChannel;
@@ -34,8 +36,9 @@ public class Settings implements GuildSettingsProvider
private int volume;
private String defaultPlaylist;
private boolean repeatMode;
private String prefix;
public Settings(SettingsManager manager, String textId, String voiceId, String roleId, int volume, String defaultPlaylist, boolean repeatMode)
public Settings(SettingsManager manager, String textId, String voiceId, String roleId, int volume, String defaultPlaylist, boolean repeatMode, String prefix)
{
this.manager = manager;
try
@@ -65,9 +68,10 @@ public class Settings implements GuildSettingsProvider
this.volume = volume;
this.defaultPlaylist = defaultPlaylist;
this.repeatMode = repeatMode;
this.prefix = prefix;
}
public Settings(SettingsManager manager, long textId, long voiceId, long roleId, int volume, String defaultPlaylist, boolean repeatMode)
public Settings(SettingsManager manager, long textId, long voiceId, long roleId, int volume, String defaultPlaylist, boolean repeatMode, String prefix)
{
this.manager = manager;
this.textId = textId;
@@ -76,6 +80,7 @@ public class Settings implements GuildSettingsProvider
this.volume = volume;
this.defaultPlaylist = defaultPlaylist;
this.repeatMode = repeatMode;
this.prefix = prefix;
}
// Getters
@@ -109,6 +114,17 @@ public class Settings implements GuildSettingsProvider
return repeatMode;
}
public String getPrefix()
{
return prefix;
}
@Override
public Collection<String> getPrefixes()
{
return prefix == null ? Collections.EMPTY_SET : Collections.singleton(prefix);
}
// Setters
public void setTextChannel(TextChannel tc)
{
@@ -145,4 +161,10 @@ public class Settings implements GuildSettingsProvider
this.repeatMode = mode;
this.manager.writeSettings();
}
public void setPrefix(String prefix)
{
this.prefix = prefix;
this.manager.writeSettings();
}
}
@@ -16,6 +16,7 @@
package com.jagrosh.jmusicbot.settings;
import com.jagrosh.jdautilities.command.GuildSettingsManager;
import com.jagrosh.jmusicbot.utils.OtherUtil;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
@@ -37,7 +38,7 @@ public class SettingsManager implements GuildSettingsManager
{
this.settings = new HashMap<>();
try {
JSONObject loadedSettings = new JSONObject(new String(Files.readAllBytes(Paths.get("serversettings.json"))));
JSONObject loadedSettings = new JSONObject(new String(Files.readAllBytes(OtherUtil.getPath("serversettings.json"))));
loadedSettings.keySet().forEach((id) -> {
JSONObject o = loadedSettings.getJSONObject(id);
settings.put(Long.parseLong(id), new Settings(this,
@@ -46,7 +47,8 @@ public class SettingsManager implements GuildSettingsManager
o.has("dj_role_id") ? o.getString("dj_role_id") : null,
o.has("volume") ? o.getInt("volume") : 100,
o.has("default_playlist")? o.getString("default_playlist"): null,
o.has("repeat") ? o.getBoolean("repeat") : false));
o.has("repeat") ? o.getBoolean("repeat") : false,
o.has("prefix") ? o.getString("prefix") : null));
});
} catch(IOException | JSONException e) {
LoggerFactory.getLogger("Settings").warn("Failed to load server settings (this is normal if no settings have been set yet): "+e);
@@ -72,7 +74,7 @@ public class SettingsManager implements GuildSettingsManager
private Settings createDefaultSettings()
{
return new Settings(this, 0, 0, 0, 100, null, false);
return new Settings(this, 0, 0, 0, 100, null, false, null);
}
protected void writeSettings()
@@ -89,14 +91,16 @@ public class SettingsManager implements GuildSettingsManager
o.put("dj_role_id", Long.toString(s.roleId));
if(s.getVolume()!=100)
o.put("volume",s.getVolume());
if(s.getDefaultPlaylist()!=null)
if(s.getDefaultPlaylist() != null)
o.put("default_playlist", s.getDefaultPlaylist());
if(s.getRepeatMode())
o.put("repeat", true);
if(s.getPrefix() != null)
o.put("prefix", s.getPrefix());
obj.put(Long.toString(key), o);
});
try {
Files.write(Paths.get("serversettings.json"), obj.toString(4).getBytes());
Files.write(OtherUtil.getPath("serversettings.json"), obj.toString(4).getBytes());
} catch(IOException ex){
LoggerFactory.getLogger("Settings").warn("Failed to write to file: "+ex);
}
@@ -37,30 +37,13 @@ public class FormatUtil {
seconds %= 60;
return (hours>0 ? hours+":" : "") + (minutes<10 ? "0"+minutes : minutes) + ":" + (seconds<10 ? "0"+seconds : seconds);
}
/*public static String embedFormat(AudioHandler handler)
{
if(handler==null)
return "No music playing\n\u23F9 "+progressBar(-1)+" "+volumeIcon(100);
else if (!handler.isMusicPlaying())
return "No music playing\n\u23F9 "+progressBar(-1)+" "+volumeIcon(handler.getPlayer().getVolume());
else
{
AudioTrack track = handler.getPlayer().getPlayingTrack();
double progress = (double)track.getPosition()/track.getDuration();
return (handler.getPlayer().isPaused()?"\u23F8":"\u25B6")
+" "+progressBar(progress)
+" `["+formatTime(track.getPosition()) + "/" + formatTime(track.getDuration()) +"]` "
+volumeIcon(handler.getPlayer().getVolume());
}
}//*/
public static String progressBar(double percent)
{
String str = "";
for(int i=0; i<12; i++)
if(i == (int)(percent*12))
str+="\uD83D\uDD18";
str+="\uD83D\uDD18"; // 🔘
else
str+="▬";
return str;
@@ -69,12 +52,12 @@ public class FormatUtil {
public static String volumeIcon(int volume)
{
if(volume == 0)
return "\uD83D\uDD07";
return "\uD83D\uDD07"; // 🔇
if(volume < 30)
return "\uD83D\uDD08";
return "\uD83D\uDD08"; // 🔈
if(volume < 70)
return "\uD83D\uDD09";
return "\uD83D\uDD0A";
return "\uD83D\uDD09"; // 🔉
return "\uD83D\uDD0A"; // 🔊
}
public static String listOfTChannels(List<TextChannel> list, String query)
@@ -109,6 +92,9 @@ public class FormatUtil {
public static String filter(String input)
{
return input.replace("@everyone", "@\u0435veryone").replace("@here", "@h\u0435re").trim();
return input.replace("\u202E","")
.replace("@everyone", "@\u0435veryone") // cyrillic letter e
.replace("@here", "@h\u0435re") // cyrillic letter e
.trim();
}
}
@@ -17,11 +17,12 @@ package com.jagrosh.jmusicbot.utils;
import com.jagrosh.jmusicbot.JMusicBot;
import com.jagrosh.jmusicbot.entities.Prompt;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Path;
import java.nio.file.Paths;
import net.dv8tion.jda.core.OnlineStatus;
import net.dv8tion.jda.core.entities.Game;
import okhttp3.*;
@@ -39,7 +40,58 @@ public class OtherUtil
+ "Current version: %s\n"
+ "New Version: %s\n\n"
+ "Please visit https://github.com/jagrosh/MusicBot/releases/latest to get the latest release.";
private final static String WINDOWS_INVALID_PATH = "c:\\windows\\system32\\";
/**
* gets a Path from a String
* also fixes the windows tendency to try to start in system32
* any time the bot tries to access this path, it will instead start in the location of the jar file
*
* @param path the string path
* @return the Path object
*/
public static Path getPath(String path)
{
// special logic to prevent trying to access system32
if(path.toLowerCase().startsWith(WINDOWS_INVALID_PATH))
{
String filename = path.substring(WINDOWS_INVALID_PATH.length());
try
{
path = new File(JMusicBot.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile().getPath() + File.separator + filename;
}
catch(URISyntaxException ex) {}
}
return Paths.get(path);
}
/**
* Loads a resource from the jar as a string
*
* @param clazz class base object
* @param name name of resource
* @return string containing the contents of the resource
*/
public static String loadResource(Object clazz, String name)
{
try(BufferedReader reader = new BufferedReader(new InputStreamReader(clazz.getClass().getResourceAsStream(name))))
{
StringBuilder sb = new StringBuilder();
reader.lines().forEach(line -> sb.append("\r\n").append(line));
return sb.toString().trim();
}
catch(IOException ex)
{
return null;
}
}
/**
* Loads image data from a URL
*
* @param url url of image
* @return inputstream of url
*/
public static InputStream imageFromUrl(String url)
{
if(url==null)
@@ -51,33 +103,44 @@ public class OtherUtil
urlConnection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36");
return urlConnection.getInputStream();
}
catch(IOException|IllegalArgumentException e) {}
catch(IOException | IllegalArgumentException ignore) {}
return null;
}
/**
* Parses an activity from a string
*
* @param game the game, including the action such as 'playing' or 'watching'
* @return the parsed activity
*/
public static Game parseGame(String game)
{
if(game==null || game.trim().isEmpty() || game.trim().equalsIgnoreCase("default"))
return null;
String lower = game.toLowerCase();
if(lower.startsWith("playing"))
return Game.playing(game.substring(7).trim());
return Game.playing(makeNonEmpty(game.substring(7).trim()));
if(lower.startsWith("listening to"))
return Game.listening(game.substring(12).trim());
return Game.listening(makeNonEmpty(game.substring(12).trim()));
if(lower.startsWith("listening"))
return Game.listening(game.substring(9).trim());
return Game.listening(makeNonEmpty(game.substring(9).trim()));
if(lower.startsWith("watching"))
return Game.watching(game.substring(8).trim());
return Game.watching(makeNonEmpty(game.substring(8).trim()));
if(lower.startsWith("streaming"))
{
String[] parts = game.substring(9).trim().split("\\s+", 2);
if(parts.length == 2)
{
return Game.streaming(parts[1], "https://twitch.tv/"+parts[0]);
return Game.streaming(makeNonEmpty(parts[1]), "https://twitch.tv/"+parts[0]);
}
}
return Game.playing(game);
}
public static String makeNonEmpty(String str)
{
return str == null || str.isEmpty() ? "\u200B" : str;
}
public static OnlineStatus parseStatus(String status)
{
+75 -17
View File
@@ -1,3 +1,4 @@
/// START OF JMUSICBOT CONFIG ///
/////////////////////////////////////////////////////////
// Config for the JMusicBot //
/////////////////////////////////////////////////////////
@@ -13,14 +14,14 @@
// If you don't know how to get a bot token, please see the guide here:
// https://github.com/jagrosh/MusicBot/wiki/Getting-a-Bot-Token
token=BOT_TOKEN_HERE
token = BOT_TOKEN_HERE
// This sets the owner of the bot
// This needs to be the owner's ID (a 17-18 digit number)
// https://github.com/jagrosh/MusicBot/wiki/Finding-Your-User-ID
owner=0
owner = 0 // OWNER ID
// This sets the prefix for the bot
@@ -29,7 +30,7 @@ owner=0
// If you do not set this, the prefix will be a mention of the bot (@Botname play)
// If you make this blank, the bot will not use a prefix
prefix="@mention"
prefix = "@mention"
// If you set this, it modifies the default game of the bot
@@ -39,13 +40,13 @@ prefix="@mention"
// where X is the title. If you don't include an action, it will use the
// default of "Playing"
game="DEFAULT"
game = "DEFAULT"
// If you set this, it will modify the default status of bot
// Valid values: ONLINE IDLE DND INVISIBLE
status=ONLINE
status = ONLINE
// If you set this to true, the bot will list the title of the song it is currently playing in its
@@ -58,23 +59,23 @@ songinstatus=false
// If you set this, the bot will also use this prefix in addition to
// the one provided above
altprefix="NONE"
altprefix = "NONE"
// If you set these, it will change the various emojis
success="🎶"
warning="💡"
error="🚫"
loading="⌚"
searching="🔎"
success = "🎶"
warning = "💡"
error = "🚫"
loading = "⌚"
searching = "🔎"
// If you set this, you change the word used to view the help.
// For example, if you set the prefix to !! and the help to cmds, you would type
// !!cmds to see the help text
help=help
help = help
// If you set this, the "nowplaying" command will show youtube thumbnails
@@ -82,27 +83,27 @@ help=help
// This is because refreshing the boxes causes the image to be reloaded
// every time it refreshes.
npimages=false
npimages = false
// If you set this, the bot will not leave a voice channel after it finishes a queue.
// Keep in mind that being connected to a voice channel uses additional bandwith,
// so this option is not recommended if bandwidth is a concern.
stayinchannel=false
stayinchannel = false
// This sets the maximum amount of seconds any track loaded can be. If not set or set
// to any number less than or equal to zero, there is no maximum time length. This time
// restriction applies to songs loaded from any source.
maxtime=0
maxtime = 0
// This sets an alternative folder to be used as the Playlists folder
// This can be a relative or absolute path
playlistsfolder=Playlists
playlistsfolder = "Playlists"
// By default, the bot will DM the owner if the bot is running and a new version of the bot
@@ -111,6 +112,60 @@ playlistsfolder=Playlists
updatealerts=true
// Changing this changes the lyrics provider
// Currently available providers: "A-Z Lyrics", "Genius", "MusicMatch"
// At the time of writing, I would recommend sticking with A-Z Lyrics or MusicMatch,
// as Genius tends to have a lot of non-song results and you might get something
// completely unrelated to what you want.
// If you are interested in contributing a provider, please see
// https://github.com/jagrosh/JLyrics
lyrics.default = "A-Z Lyrics"
// These settings allow you to configure custom aliases for all commands.
// Multiple aliases may be given, separated by commas.
//
// Example 1: Giving command "play" the alias "p":
// play = [ p ]
//
// Example 2: Giving command "search" the aliases "yts" and "find":
// search = [ yts, find ]
aliases {
// General commands
settings = [ status ]
// Music commands
lyrics = []
nowplaying = [ np, current ]
play = []
playlists = [ pls ]
queue = [ list ]
remove = [ delete ]
scsearch = []
search = [ ytsearch ]
shuffle = []
skip = [ voteskip ]
// Admin commands
prefix = [ setprefix ]
setdj = []
settc = []
setvc = []
// DJ Commands
forceremove = [ forcedelete, modremove, moddelete, modelete ]
forceskip = [ modskip ]
movetrack = [ move ]
pause = []
playnext = []
repeat = []
skipto = [ jumpto ]
stop = []
volume = [ vol ]
}
// If you set this to true, it will enable the eval command for the bot owner. This command
// allows the bot owner to run arbitrary code from the bot's account.
//
@@ -122,4 +177,7 @@ updatealerts=true
// DO NOT ENABLE THIS IF YOU DON'T KNOW WHAT THIS DOES OR HOW TO USE IT
// IF SOMEONE ASKS YOU TO ENABLE THIS, THERE IS AN 11/10 CHANCE THEY ARE TRYING TO SCAM YOU
eval=false
eval=false
/// END OF JMUSICBOT CONFIG ///
@@ -24,7 +24,7 @@ import static org.junit.Assert.*;
*
* @author John Grosh (john.a.grosh@gmail.com)
*/
public class QueueTests
public class FairQueueTest
{
@Test
public void differentIdentifierSize()