From 62d19faa4095e474fd07cb49d42d1cbcd02b5139 Mon Sep 17 00:00:00 2001 From: Isaac Parenteau Date: Thu, 3 Oct 2019 00:13:27 -0500 Subject: [PATCH] Initial Commit --- .gitignore | 7 + Jenkinsfile | 180 ++++++++++++++++++ README.md | 29 +++ pom.xml | 136 +++++++++++++ .../handlers/EightTrackAudioSendHandler.java | 32 ++++ .../discord/eighttrack/main/Entry.java | 154 +++++++++++++++ .../eighttrack/scheduler/TrackScheduler.java | 80 ++++++++ src/main/resources/log4j2.xml | 29 +++ 8 files changed, 647 insertions(+) create mode 100644 .gitignore create mode 100644 Jenkinsfile create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/net/locusworks/discord/eighttrack/handlers/EightTrackAudioSendHandler.java create mode 100644 src/main/java/net/locusworks/discord/eighttrack/main/Entry.java create mode 100644 src/main/java/net/locusworks/discord/eighttrack/scheduler/TrackScheduler.java create mode 100644 src/main/resources/log4j2.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2574f9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/target/ +*.log* +.settings/ +.classpath +.project +/pseudobotDB/ +/nbactions.xml diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..155a059 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,180 @@ +#!groovy + + +init() + +def branch_name +def branch_name_base +def build_number +def build_url +def git_commit +def job_name +def tag +def version +def build_type +def display_name + +def init() { + + // Keep the 5 most recent builds + properties([[$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', numToKeepStr: '5']]]) + + build_number = env.BUILD_NUMBER + build_url = env.BUILD_URL + job_name = "${env.JOB_NAME}" + branch_name = env.BRANCH_NAME + persist = branch_name + + // execute the branch type specific pipeline code + try { + + if (branch_name.indexOf('release/')==0) build_type='release' + if (branch_name.indexOf('feature/')==0) build_type='feature' + if (branch_name.indexOf('develop')==0) build_type='develop' + if (branch_name.indexOf('master')==0) build_type='master' + if (branch_name.indexOf('hotfix/')==0) build_type='hotfix' + if (branch_name.indexOf('bugfix/')==0) build_type='bugfix' + + switch(build_type) { + case ~/feature/: + case ~/hotfix/: + case ~/bugfix/: + case ~/master/: + case ~/develop/: + CommonBuild(); + break; + case ~/release/: + CommonBuild(); + Deploy(); + break; + default: + throw "unsupported branch type: ${branch_name}" + } + + node('master') { + set_result('SUCCESS') + } + + } catch (err) { + node() { + set_result('FAILURE') + } + throw err + } +} + +def CommonBuild() { + // common pipeline elements + node('master') { + Initialize() + SetVersion(build_type) + print_vars() // after SetVersion - all variables now defined + set_result('INPROGRESS') + Build() + } +} + +def Build() { + stage ('build') { + mvn_alt("install -DskipTests=true -Dbuild.revision=${git_commit}") + step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true]) + } +} + +def Initialize() { + stage ('initialize') { + + // get new code + checkout scm + + git_commit = getSha1() + } +} + +def Deploy() { + node('master') { + stage ('deploy') { + mvn_alt("deploy -DskipTests=true -Dbuild.number=${build_number} -Dbuild.revision=${git_commit}") + } + } +} + +def getSha1() { + sha1 = sh(script: 'git rev-parse HEAD', returnStdout: true).trim() + echo "sha1 is ${sha1}" + return sha1 +} + +def mvn_initial(args) { + mvn_alt(args) +} + +def mvn_alt(args) { + // add maven tools to path before calling maven + withMaven(maven: 'maven-3.6.1', jdk: 'jdk1.8.0_221', mavenSettingsConfig: 'maven_settings') { + writeFile file: '.skip-task-scanner', text: '' + writeFile file: '.skip-publish-junit-results', text: '' + sh "mvn ${args}" + } +} + +def set_result(status) { + if ( status == 'SUCCESS' ) { + currentBuild.result = status + notify_bitbucket('SUCCESSFUL') + } else if ( status == 'FAILURE' ) { + currentBuild.result = status + notify_bitbucket('FAILED') + } else if ( status == 'INPROGRESS' ) { + notify_bitbucket('INPROGRESS') + } else { + error ("unknown status") + } +} + +def notify_bitbucket(state) { + echo "notify bitbucket, state = $state, commit: ${git_commit}" +} + +def print_vars() { + echo "build_number = ${build_number}" + echo "build_url = ${build_url}" + echo "job_name = ${job_name}" + echo "branch_name = ${branch_name}" + echo "branch_name_base = ${branch_name_base}" + echo "build_type = ${build_type}" + echo "display_name = ${currentBuild.displayName}" + echo "version = ${version}" + echo "git_commit = ${git_commit}" +} + +def SetVersion( v ) { + stage ('set version') { + echo "set version ${v}" + branch_name_base = (branch_name =~ /([^\/]+$)/)[0][0] + + switch(build_type) { + case ~/develop/: + version = build_number + '-SNAPSHOT' + currentBuild.displayName = version + break; + case ~/release/: + // for release branches, where the branch is named "release/1.2.3", + // derive the version and display name derive from the numeric suffix and append the build number + // 3.2.1.100 + version = branch_name_base + '.' + build_number + '-RELEASE' + currentBuild.displayName = version + break; + default: + // for all other branches the version number is 0 with an appended build number + // and for the display name use the jenkins default #n and add the branch name + version = branch_name_base + "." + build_number + currentBuild.displayName = "#" + build_number + " - " + branch_name_base + } + + display_name = currentBuild.displayName + mvn_initial("versions:set -DnewVersion=${version}") + } +} + +return this diff --git a/README.md b/README.md new file mode 100644 index 0000000..39af52c --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# README # + +This README would normally document whatever steps are necessary to get your application up and running. + +### What is this repository for? ### + +* Quick summary +* Version +* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo) + +### How do I get set up? ### + +* Summary of set up +* Configuration +* Dependencies +* Database configuration +* How to run tests +* Deployment instructions + +### Contribution guidelines ### + +* Writing tests +* Code review +* Other guidelines + +### Who do I talk to? ### + +* Repo owner or admin +* Other community or team contact \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..209b375 --- /dev/null +++ b/pom.xml @@ -0,0 +1,136 @@ + + 4.0.0 + net.locusworks.discord + eight-track + 0.0.1-SNAPSHOT + Eight Track + + + 2.11.2 + 1.7.26 + http://nexus.locusworks.net + 5.2.2 + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M2 + + + + + + + + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + org.owasp + dependency-check-maven + ${dep.check.version} + + true + true + false + true + + + + + check + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + package + + shade + + + DeBOTchery-${project.version} + + + net.locusworks.discord.eighttrack.main.Entry + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + + + + net.dv8tion + JDA + 4.0.0_46 + + + + net.locusworks + applogger + 1.0.1-RELEASE + + + + com.sedmelluq + lavaplayer + 1.3.22 + + + + + + + nexus-proxy-public + ${nexus.repo}/repository/maven-public/ + + + + + nexus-proxy-public + ${nexus.repo}/repository/maven-public/ + + + + + + nexus-snapshot + ${nexus.repo}/repository/locusworks-snapshot/ + + + nexus-release + ${nexus.repo}/repository/locusworks-release/ + + + + \ No newline at end of file diff --git a/src/main/java/net/locusworks/discord/eighttrack/handlers/EightTrackAudioSendHandler.java b/src/main/java/net/locusworks/discord/eighttrack/handlers/EightTrackAudioSendHandler.java new file mode 100644 index 0000000..e1aec72 --- /dev/null +++ b/src/main/java/net/locusworks/discord/eighttrack/handlers/EightTrackAudioSendHandler.java @@ -0,0 +1,32 @@ +package net.locusworks.discord.eighttrack.handlers; + +import java.nio.ByteBuffer; + +import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; +import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame; + +import net.dv8tion.jda.api.audio.AudioSendHandler; + +public class EightTrackAudioSendHandler implements AudioSendHandler { + + private final AudioPlayer audioPlayer; + private AudioFrame lastFrame; + + public EightTrackAudioSendHandler(AudioPlayer audioPlayer) { + this.audioPlayer = audioPlayer; + } + + public boolean canProvide() { + lastFrame = audioPlayer.provide(); + return lastFrame != null; + } + + public ByteBuffer provide20MsAudio() { + return ByteBuffer.wrap(lastFrame.getData()); + } + + public boolean isOpus() { + return true; + } + +} diff --git a/src/main/java/net/locusworks/discord/eighttrack/main/Entry.java b/src/main/java/net/locusworks/discord/eighttrack/main/Entry.java new file mode 100644 index 0000000..dfc913e --- /dev/null +++ b/src/main/java/net/locusworks/discord/eighttrack/main/Entry.java @@ -0,0 +1,154 @@ +package net.locusworks.discord.eighttrack.main; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.OffsetDateTime; +import java.util.Iterator; + +import javax.security.auth.login.LoginException; + +import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; +import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; +import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager; +import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers; +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; +import net.dv8tion.jda.api.AccountType; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.VoiceChannel; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.managers.AudioManager; +import net.locusworks.discord.eighttrack.handlers.EightTrackAudioSendHandler; +import net.locusworks.discord.eighttrack.scheduler.TrackScheduler; +import net.locusworks.logger.ApplicationLogger; +import net.locusworks.logger.ApplicationLoggerFactory; + +public class Entry { + + public static void main(String[] args) throws LoginException, IOException { + if (args.length < 1) throw new RuntimeException("no token provided"); + + ApplicationLogger logger = ApplicationLoggerFactory.getLogger(Entry.class); + logger.info("Starting Eight-Track"); + + JDA client = new JDABuilder(AccountType.BOT).setToken(args[0]).build(); + + AudioPlayerManager apm = new DefaultAudioPlayerManager(); + AudioSourceManagers.registerLocalSource(apm); + + final AudioPlayer player = apm.createPlayer(); + + TrackScheduler ts = new TrackScheduler(); + + player.addListener(ts); + + for (Iterator iter = Files.list(Paths.get(args[1])).iterator(); iter.hasNext();) { + Path song = iter.next(); + if (!song.getFileName().toString().toLowerCase().endsWith(".mp3")) continue; + logger.info("Loading song: %s", song); + try { + apm.loadItem(song.toAbsolutePath().toString(), ts); + } catch (IllegalStateException ex) { + logger.warn("Unable to load song :%s -> %s", song.toAbsolutePath().toString(), ex.getMessage()); + } + } + + client.addEventListener(new ListenerAdapter() { + + @Override + public void onMessageReceived(final MessageReceivedEvent event) { + if (event.getAuthor().isBot()) return; + + String command = event.getMessage().getContentRaw().trim().toLowerCase(); + + switch (command) { + case "-play": + play(event, ts, player); + return; + case "-stop": + stop(event, ts, player); + return; + case "-next": + next(event, ts, player); + return; + case "-repeat": + repeat(event, ts, player); + return; + case "-whatsnext": + whatsNext(event, ts, player); + default: + return; + } + } + }); + } + + protected static void whatsNext(MessageReceivedEvent event, TrackScheduler ts, AudioPlayer player) { + AudioTrack track = ts.peek(); + MessageEmbed embed = new EmbedBuilder() + .setAuthor(event.getMember().getEffectiveName(), null, event.getAuthor().getAvatarUrl()) + .setTitle("Next Up:") + .setDescription(String.format("**%s** by __%s__", track.getInfo().title, track.getInfo().author)) + .setTimestamp(OffsetDateTime.now()) + .build(); + event.getTextChannel().sendMessage(embed).queue(); + } + + protected static void repeat(MessageReceivedEvent event, TrackScheduler ts, AudioPlayer player) { + next(event, ts, player); + ts.setFinishedCallback((ater) -> { + next(event, ts, player); + }); + + } + + protected static void next(MessageReceivedEvent event, TrackScheduler ts, AudioPlayer player) { + stop(event, ts, player); + play(event, ts, player); + } + + private static void stop(MessageReceivedEvent event, TrackScheduler ts, AudioPlayer player) { + player.stopTrack(); + } + + private static void play(MessageReceivedEvent event, TrackScheduler ts, AudioPlayer player) { + VoiceChannel vc = event.getMember().getVoiceState().getChannel(); + if (vc == null) { + event.getTextChannel().sendMessage(String.format("<@%s you are not in a voice channel to play music", event.getMember().getId())).queue(); + return; + } + + if (!event.getGuild().getSelfMember().hasPermission(vc, Permission.VOICE_CONNECT, Permission.VOICE_SPEAK)) { + event.getTextChannel().sendMessage(String.format("<@%s, I cannot play my music in channel %s as I dont have permission to", + event.getMember().getId(), vc.getName())).queue(); + return; + } + + AudioManager manager = event.getGuild().getAudioManager(); + manager.openAudioConnection(vc); + + manager.setSendingHandler(new EightTrackAudioSendHandler(player)); + + if (ts.hasTracks()) { + AudioTrack track = ts.getNextTrack(); + + MessageEmbed embed = new EmbedBuilder() + .setAuthor(event.getMember().getEffectiveName(), null, event.getAuthor().getAvatarUrl()) + .setTitle("Now Playing:") + .setDescription(String.format("**%s** by __%s__", track.getInfo().title, track.getInfo().author)) + .setTimestamp(OffsetDateTime.now()) + .build(); + + event.getTextChannel().sendMessage(embed).queue(); + player.playTrack(track); + ts.trackLoaded(track); + } + } + +} diff --git a/src/main/java/net/locusworks/discord/eighttrack/scheduler/TrackScheduler.java b/src/main/java/net/locusworks/discord/eighttrack/scheduler/TrackScheduler.java new file mode 100644 index 0000000..a6d2d79 --- /dev/null +++ b/src/main/java/net/locusworks/discord/eighttrack/scheduler/TrackScheduler.java @@ -0,0 +1,80 @@ +package net.locusworks.discord.eighttrack.scheduler; + +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.function.Consumer; + +import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; +import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; +import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter; +import com.sedmelluq.discord.lavaplayer.player.event.AudioEventListener; +import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; +import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; +import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason; + +public class TrackScheduler extends AudioEventAdapter implements AudioEventListener, AudioLoadResultHandler { + + private Queue trackQueue; + + private boolean started; + + private Consumer finished; + + public TrackScheduler() { + trackQueue = new LinkedBlockingQueue(); + } + + @Override + public void onTrackStart(AudioPlayer player, AudioTrack track) { + this.started = true; + super.onTrackStart(player, track); + } + + @Override + public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) { + this.started = false; + super.onTrackEnd(player, track, endReason); + if (finished != null) finished.accept(endReason); + } + + public void setFinishedCallback(Consumer callback) { + if (this.finished == null) this.finished = callback; + } + + public void trackLoaded(AudioTrack track) { + trackQueue.add(track); + } + + public void playlistLoaded(AudioPlaylist playlist) { + for (AudioTrack at : playlist.getTracks()) { + trackQueue.add(at); + } + } + + public void noMatches() { + // TODO Auto-generated method stub + } + + public void loadFailed(FriendlyException exception) { + // TODO Auto-generated method stub + } + + public AudioTrack peek() { + return trackQueue.peek(); + } + + public boolean hasTracks() { + return !trackQueue.isEmpty(); + } + + public AudioTrack getNextTrack() { + return trackQueue.poll(); + } + + public boolean playing() { + return started; + } + + +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..078aa7e --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + ${sys:LOG_LEVEL:-INFO} + %d{dd-MMM-yyyy HH:mm:ss.SSS} [%-5p] [%c{1}] %m%n + + + + + + + + + + + + + + + + + + + +