From 264cc179044ccfee797c4121d19393f54b4dbbe8 Mon Sep 17 00:00:00 2001 From: choelzl Date: Tue, 12 Oct 2021 02:44:33 +0200 Subject: [PATCH] Init --- .drone.yml | 24 ++++++++ .gitingore | 2 + Dockerfile | 34 +++++++++++ docker-compose.yml | 14 +++++ main.js | 139 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 19 +++++++ run.sh | 13 +++++ 7 files changed, 245 insertions(+) create mode 100644 .drone.yml create mode 100644 .gitingore create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 main.js create mode 100644 package.json create mode 100644 run.sh diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..a0c821c --- /dev/null +++ b/.drone.yml @@ -0,0 +1,24 @@ +--- +kind: pipeline +type: docker +name: default + +steps: +- name: deploy + image: docker/compose + volumes: + - name: docksock + path: /var/run/docker.sock + commands: + - echo $DISCORD_BOT_TOKEN + - docker-compose -p discbot up -d --force-recreate --build + when: + branch: [master] + environment: + DISCORD_BOT_TOKEN: + from_secret: discord_api + +volumes: +- name: docksock + host: + path: /var/run/docker.sock diff --git a/.gitingore b/.gitingore new file mode 100644 index 0000000..3ddc903 --- /dev/null +++ b/.gitingore @@ -0,0 +1,2 @@ +./node_modules/ +./package-lock.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9e39b2a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM archlinux:latest + +ENV DEBIAN_FRONTEND=noninteractive +ENV UNAME user + +RUN useradd -u 1001 -m -d /home/user -s /bin/sh user && \ + usermod -aG audio user +RUN echo "user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers +RUN echo "root ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +WORKDIR /usr/src/app + + +RUN pacman -Syud --noconfirm \ + base-devel git \ + nodejs npm \ + pulseaudio \ + mpv rtmpdump aria2 + +RUN mkdir /opt/ytdl && chown user:user /opt/ytdl +USER user +RUN git clone https://aur.archlinux.org/yt-dlp.git /opt/ytdl && \ + cd /opt/ytdl && makepkg -sci --noconfirm && sudo ln -s /usr/bin/yt-dlp /usr/bin/youtube-dl +USER root + +COPY package*.json ./ +RUN npm install + +COPY . . +RUN chmod +x run.sh + + + +CMD [ "./run.sh" ] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c29455a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3.7" + +services: + discbot: + build: ./ + container_name: discbot + labels: + - "traefik.enable=false" + environment: + - DISCORD_BOT_TOKEN=$DISCORD_BOT_TOKEN + devices: + - /dev/dri/renderD128:/dev/dri/renderD128 + - /dev/dri/card0:/dev/dri/card0 + restart: always diff --git a/main.js b/main.js new file mode 100644 index 0000000..50f342c --- /dev/null +++ b/main.js @@ -0,0 +1,139 @@ +const mpv = require('node-mpv'); +const prism = require('prism-media'); +const { Client, Intents, VoiceChannel} = require('discord.js'); +const { joinVoiceChannel, + createAudioPlayer, + createAudioResource, + entersState, + NoSubscriberBehavior, + StreamType, + AudioPlayerStatus, + VoiceConnectionStatus, } = require('@discordjs/voice'); +const delay = ms => new Promise(res => setTimeout(res, ms)); + +global.client = new Client({ + intents: [ + Intents.FLAGS.GUILDS, + Intents.FLAGS.GUILD_MEMBERS, + Intents.FLAGS.GUILD_MESSAGES, + Intents.FLAGS.GUILD_VOICE_STATES + ], + disableMentions: 'everyone', +}); + +client.music = {title:undefined, duration: undefined, pos:-1, count:0, report:false}; +client.on("ready", () => { + console.log(`${client.user.username} ready!`); + client.user.setActivity(`H C L`, { type: 'WATCHING' }); +}); +client.on("warn", (info) => console.log(info)); +client.on("error", console.error); +client.login(process.env.DISCORD_BOT_TOKEN); +client.exec={}; +client.exec.format_url= function (url) { return url.includes("://") ? url : 'ytdl://ytsearch:"'+url+'"'}; + +const mpvPlayer = new mpv({audio_only:true, "verbose": false,},['--audio-device=pulse/MPVSink', '--ytdl-format=bestaudio']);//, '--demuxer-readahead-secs=2',' --demuxer-max-bytes=500M']); +var channel = null; +mpvPlayer.start().then(e=>console.log("MPV Started")).catch(e=>console.error(e)); +mpvPlayer.on('status', (status) => { + console.log(status); + if(status.property=='media-title'){ + if(client.music.duration) + client.music.title = status.value; + } + if(status.property=='duration'){ + client.music.duration = status.value; + } + if(status.property=='playlist-count'){ + client.music.count = status.value; + client.music.report = false; + }else if(status.property=='playlist-pos'){ + client.music.pos = status.value; + client.music.report = false + + }else if(client.music.title && client.music.duration){ + client.channels.fetch(channel).then(c=>{ + if(client.music.title && client.music.duration && client.music.report==false){ + c.send('Playing: '+client.music.title+" ("+Math.floor(client.music.duration/60)+":"+Math.floor(client.music.duration%60)+")"); + client.music.report = true; + } + }); + } +}); +mpvPlayer.on('stopped', (e) => { + console.log(e) + if(channel && client.music.pos == -1) + client.channels.fetch(channel).then(c=>c.send('Finished playing... Gime more music UwU')); +}); + +const player = createAudioPlayer({ + behaviors: { + noSubscriber: NoSubscriberBehavior.Play, + maxMissedFrames: 250, + }, +}); +const resource = createAudioResource(new prism.FFmpeg({ + args: ['-analyzeduration','0','-loglevel','0', + '-f', 'pulse','-i', 'MPVSink.monitor', + '-acodec','libopus','-f','opus', + '-ar','48000','-ac','2', + ], + }), + {inputType: StreamType.OggOpus}); +player.play(resource); + + +client.exec.play = async(message, url)=>{ + if(channel == null){ + const vchannel = message.member.voice.channel; + if (!vchannel) return message.reply("You are not currently in a voice channel"); + const permissions = vchannel.permissionsFor(message.client.user); + if (!permissions.has("CONNECT")) return message.reply("I am not allowed to join voice T_T"); + if (!permissions.has("SPEAK")) return message.reply("I am not allowed to speak T_T"); + + const vc = joinVoiceChannel({ + channelId: message.member.voice.channel.id, + guildId: message.guild.id, + adapterCreator: message.guild.voiceAdapterCreator + }) + vc.subscribe(player); + channel = message.channelId; + } + mpvPlayer.load(client.exec.format_url(url),'replace').catch(e=>{}); + + return entersState(player, AudioPlayerStatus.Playing, 10e5); +} + +client.exec.stop = (message)=>{ + mpvPlayer.stop().catch(e=>{}); + message.guild.me.voice.disconnect("Ended Playback"); + channel = null; +} + +client.exec.queue = (message)=>{ + let url = message.content.slice(6); + if(channel){ + mpvPlayer.load(client.exec.format_url(url),'append-play').catch(e=>{}); + }else{ + client.exec.play(message,url); + } +} +client.exec.skip = (message)=>{ + if(channel) + mpvPlayer.next().catch(e=>{}); +} + +client.on("message", async (message) => { + if (message.author.bot) return; + if (!message.guild) return; + + if (message.content.indexOf("stop")==0){ + client.exec.stop(message); + }else if (message.content.indexOf("play")==0){ + client.exec.play(message, message.content.slice(5)); + }else if (message.content.indexOf("queue")==0){ + client.exec.queue(message); + }else if (message.content.indexOf("skip")==0){ + client.exec.skip(message); + } +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..304fc23 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "discbot", + "version": "1.0.0", + "description": "", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@discordjs/opus": "^0.5.3", + "@discordjs/voice": "^0.6.0", + "discord.js": "^13.2.0", + "libsodium-wrappers": "^0.7.9", + "node-mpv": "git://github.com/j-holub/Node-MPV.git", + "prism-media": "^1.3.2" + } +} diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..0bcb4c6 --- /dev/null +++ b/run.sh @@ -0,0 +1,13 @@ +#!/bin/bash +su user + +if test -z "$DBUS_SESSION_BUS_ADDRESS" ; then + eval `dbus-launch --sh-syntax` +fi +sleep 1 +rm -rf /root/.config/pulse/ +pulseaudio -D --exit-idle-time=-1 --disallow-exit +sleep 2 +pacmd load-module module-null-sink sink_name=MPVSink +sleep 1 +node main.js