From ae73b4f37423294ecc6212a2a0aad29151636115 Mon Sep 17 00:00:00 2001 From: Matthieu Grieger Date: Mon, 8 Dec 2014 21:21:47 -0800 Subject: [PATCH] Restarting rewrite using Go instead of Ruby --- CHANGELOG.md | 3 + Gemfile | 6 -- Gemfile.lock | 42 ---------- README.md | 7 +- mumbledj/.gitignore | 1 - mumbledj/config.rb | 157 ------------------------------------ mumbledj/mumbledj.rb | 179 ----------------------------------------- mumbledj/run_bot.rb | 20 ----- mumbledj/song.rb | 57 ------------- mumbledj/song_queue.rb | 80 ------------------ 10 files changed, 7 insertions(+), 545 deletions(-) delete mode 100644 Gemfile delete mode 100644 Gemfile.lock delete mode 100644 mumbledj/.gitignore delete mode 100644 mumbledj/config.rb delete mode 100644 mumbledj/mumbledj.rb delete mode 100644 mumbledj/run_bot.rb delete mode 100644 mumbledj/song.rb delete mode 100644 mumbledj/song_queue.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index debef08..736d777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ MumbleDJ Changelog ================== +### December 8, 2014 +* Switched from Ruby to Go, using `gumble` instead of `mumble-ruby` now. + ### November 15, 2014 * Created "v2" branch for Ruby rewrite. diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 85d477f..0000000 --- a/Gemfile +++ /dev/null @@ -1,6 +0,0 @@ -# MumbleDJ Gemfile -source "https://rubygems.org" - -gem "mumble-ruby" -gem "spotify" -gem "mkfifo" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 6dfefe2..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,42 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - activesupport (4.1.8) - i18n (~> 0.6, >= 0.6.9) - json (~> 1.7, >= 1.7.7) - minitest (~> 5.1) - thread_safe (~> 0.1) - tzinfo (~> 1.1) - ffi (1.9.6) - hashie (3.3.1) - i18n (0.6.11) - json (1.8.1) - libspotify (12.1.51.4) - minitest (5.4.3) - mkfifo (0.0.1) - mumble-ruby (1.1.2) - activesupport - hashie - opus-ruby - ruby_protobuf - wavefile - opus-ruby (1.0.1) - ffi - performer (1.0.1) - ruby_protobuf (0.4.11) - spotify (12.6.0) - ffi (~> 1.0, >= 1.0.11) - libspotify (~> 12.1.51) - performer (~> 1.0) - thread_safe (0.3.4) - tzinfo (1.2.2) - thread_safe (~> 0.1) - wavefile (0.6.0) - -PLATFORMS - ruby - -DEPENDENCIES - mkfifo - mumble-ruby - spotify diff --git a/README.md b/README.md index a95eb63..bfccd56 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ MumbleDJ ======== -A Mumble bot that plays music fetched from YouTube videos. I have decided to experiment with rewriting the bot in Ruby using [mumble-ruby](https://github.com/perrym5/mumble-ruby). I am hoping this will cut down on the dependency list, make it easier to develop in the future, and allow for some extra functionality that wasn't previously possible. +A Mumble bot that plays music fetched from YouTube videos. I have decided to experiment with rewriting the bot in Go using [gumble](https://github.com/layeh/gumble). I am hoping this will cut down on the dependency list, make it easier to develop in the future, and allow for some extra functionality that wasn't previously possible. + +And yes, I know that technically this is v3. The Ruby implementation had problems with high CPU usage and choppy audio which I couldn't seem to figure out. ## Author [Matthieu Grieger](http://matthieugrieger.com) @@ -32,6 +34,5 @@ THE SOFTWARE. ## Thanks * All those who contribute to [Mumble](https://github.com/mumble-voip/mumble). -* [perrym5](https://github.com/perrym5) for [mumble-ruby](https://github.com/perrym5/mumble-ruby). -* [Kim Burgestrand](https://github.com/Burgestrand) for [libspotify Ruby bindings](https://github.com/Burgestrand/spotify). +* [Tim Cooper](https://github.com/bontibon) for [gumble](https://github.com/layeh/gumble). * [Ricardo Garcia](https://github.com/rg3) for [youtube-dl](https://github.com/rg3/youtube-dl). diff --git a/mumbledj/.gitignore b/mumbledj/.gitignore deleted file mode 100644 index b229014..0000000 --- a/mumbledj/.gitignore +++ /dev/null @@ -1 +0,0 @@ -certs diff --git a/mumbledj/config.rb b/mumbledj/config.rb deleted file mode 100644 index 10a4327..0000000 --- a/mumbledj/config.rb +++ /dev/null @@ -1,157 +0,0 @@ -# MumbleDJ -# By Matthieu Grieger -# config.rb - - -# ------------------------ -# CONNECTION CONFIGURATION -# ------------------------ - -# Bot username -# DEFAULT VALUE: "MumbleDJ" -BOT_USERNAME = "MumbleDJTest" - -# Password to join Mumble server -# DEFAULT VALUE: "" (leave it as this value if no password is required) -MUMBLE_PASSWORD = ENV['MUMBLE_PW'] - -# Server address -# DEFAULT VALUE: "localhost" -MUMBLE_SERVER_ADDRESS = "matthieugrieger.com" - -# Server port number -# DEFAULT VALUE: 64738 -MUMBLE_SERVER_PORT = 64738 - - -# --------------------- -# GENERAL CONFIGURATION -# --------------------- - -# Default channel -# DEFAULT VALUE: "Music" -DEFAULT_CHANNEL = "Bot Testing" - -# Command prefix -# DEFAULT VALUE: "!" -COMMAND_PREFIX = "!" - -# Show status output in console? -# DEFAULT VALUE: true -OUTPUT_ENABLED = true - -# Default volume -# DEFAULT VALUE: 0.2 -VOLUME = 0.2 - -# Lowest volume allowed -# DEFAULT VALUE: 0.01 -LOWEST_VOLUME = 0.01 - -# Highest volume allowed -# DEFAULT VALUE: 0.6 -HIGHEST_VOLUME = 0.6 - -# Ratio that must be met or exceeded to trigger a song skip -# DEFAULT VALUE: 0.5 -SKIP_RATIO = 0.5 - - -# --------------------- -# COMMAND CONFIGURATION -# --------------------- - -# Alias used for add command -# DEFAULT VALUE: "add" -ADD_ALIAS = "add" - -# Alias used for skip command -# DEFAULT VALUE: "skip" -SKIP_ALIAS = "skip" - -# Alias used for volume command -# DEFAULT VALUE: "volume" -VOLUME_ALIAS = "volume" - -# Alias used for move command -# DEFAULT VALUE: "move" -MOVE_ALIAS = "move" - -# Alias used for mute command -# DEFAULT VALUE: "mute" -MUTE_ALIAS = "mute" - -# Alias used for unmute command -# DEFAULT VALUE: "unmute" -UNMUTE_ALIAS = "unmute" - - -# ------------------- -# ADMIN CONFIGURATION -# ------------------- - -# Enable admins (true = on, false = off) -# DEFAULT VALUE: true -ENABLE_ADMINS = true - -# List of admins -# NOTE: I recommend only giving users admin privileges if they are -# registered on the server. Otherwise people can just take their username -# and issue admin commands. -ADMINS = ["DrumZ"] - -# Make add an admin command? -# DEFAULT VALUE: false -ADMIN_ADD = false - -# Make skip an admin command? -# DEFAULT VALUE: false -ADMIN_SKIP = false - -# Make volume an admin command? -# DEFAULT VALUE: true -ADMIN_VOLUME = true - -# Make move an admin command? -# DEFAULT VALUE: true -ADMIN_MOVE = true - -# Make mute an admin command? -# DEFAULT VALUE: true -ADMIN_MUTE = true - -# Make unmute an admin command? -# DEFAULT VALUE: true -ADMIN_UNMUTE = true - - -#---------------------- -# MESSAGE CONFIGURATION -#---------------------- - -# Message shown to users when they do not have permission to execute a command. -NO_PERMISSION_MSG = "You do not have permission to execute that command." - -# Message shown to users when they try to move the bot to a non-existant channel. -CHANNEL_DOES_NOT_EXIST_MSG = "The channel you specified does not exist." - -# Message shown to users when they attempt to add an invalid URL to the queue. -INVALID_URL_MSG = "The URL you submitted does not match the required format." - -# Message shown to users when they attempt to use the stop command when no music is playing. -NO_MUSIC_PLAYING_MSG = "There is no music playing at the moment." - -# Message shown to users when they issue a command that requires an argument and one was not supplied. -NO_ARGUMENT_MSG = "The command you issued requires an argument and you did not provide one. Make sure a space exists between the command and the argument." - -# Message shown to users when they try to change the volume to a value outside the volume range. -NOT_IN_VOLUME_RANGE_MSG = "The volume you tried to supply is not in the allowed volume range. The value must be between #{LOWEST_VOLUME} and #{HIGHEST_VOLUME}." - -# Message shown to users when they successfully change the volume. -VOLUME_SUCCESS_MSG = "You have successfully changed the volume to the following: %s." - -# Message shown to users when they try to skip a song they have already skipped. -ALREADY_SKIPPED_MSG = "You have already voted to skip this song." - -# Message shown to users when the required number of votes to trigger a skip has been met. -SKIP_SUCCESS_MSG = "Number of required skip votes has been met. Skipping song!" diff --git a/mumbledj/mumbledj.rb b/mumbledj/mumbledj.rb deleted file mode 100644 index 129c6f4..0000000 --- a/mumbledj/mumbledj.rb +++ /dev/null @@ -1,179 +0,0 @@ -# MumbleDJ v2 -# By Matthieu Grieger -# mumbledj.rb - -require "mumble-ruby" -require "mkfifo" -require_relative "config" -require_relative "song_queue" - -# Class that defines MumbleDJ behavior. -class MumbleDJ - - attr_reader :username, :server_address, :server_port, :default_channel - - # Initializes a new instance of MumbleDJ. The parameters are as follows: - # username: Desired username of the Mumble bot - # server_address: IP address/web address of Mumble server to connect to - # server_port: Port number of Mumble server (generally 64738) - # default_channel: The channel you would like the bot to connect to by - # default. If the channel does not exist, the bot will connect to - # the root channel of the server instead. - # password: Password to join a password-protected server - def initialize(username, server_address, server_port, default_channel, password) - @username = username - @password = password - @server_address = server_address - @server_port = server_port - @default_channel = default_channel - @song_queue = SongQueue.new - - Mumble.configure do |conf| - conf.sample_rate = 48000 - conf.bitrate = 32000 - conf.ssl_cert_opts[:cert_dir] = File.expand_path("certs") - end - end - - # Connects to the Mumble server with the credentials specified in - # initialize. - def connect - @client = Mumble::Client.new(@server_address) do |conf| - conf.username = @username - if @password != "" - conf.password = @password - end - end - - set_callbacks - - @client.connect - @client.on_connected do - if @default_channel != "" - @client.join_channel(@default_channel) - end - end - end - - # Safely disconnects the bot from the server. - def disconnect - @client.disconnect - end - - private - - # Parses messages looking for commands, and calls the appropriate - # methods to complete each requested command. - def parse_message(message) - @sender = @client.users[message.actor].name - if message.message[0] == COMMAND_PREFIX - if message.message.count(" ") != 0 - @command = message.message[1..(message.message.index(" ") - 1)] - @argument = message.message[(message.message.index(" ") + 1)..-1] - else - @command = message.message[1..-1] - end - - case @command - when ADD_ALIAS - if has_permission?(ADMIN_ADD, @sender) - add(@sender, @argument) - else - @client.text_user(@sender, NO_PERMISSION_MSG) - end - when SKIP_ALIAS - if has_permission?(ADMIN_SKIP, @sender) - skip(@sender) - else - @client.text_user(@sender, NO_PERMISSION_MSG) - end - when VOLUME_ALIAS - if has_permission?(ADMIN_VOLUME, @sender) - volume(@sender, @argument) - else - @client.text_user(@sender, NO_PERMISSION_MSG) - end - when MOVE_ALIAS - if has_permission?(ADMIN_MOVE, @sender) - move(@sender, @argument) - else - @client.text_user(@sender, NO_PERMISSION_MSG) - end - when MUTE_ALIAS - if has_permission?(ADMIN_MUTE, @sender) - @client.me.mute - else - @client.text_user(@sender, NO_PERMISSION_MSG) - end - when UNMUTE_ALIAS - if has_permission?(ADMIN_UNMUTE, @sender) - @client.me.mute(false) - else - @client.text_user(@sender, NO_PERMISSION_MSG) - end - when 'test' - File.mkfifo('/tmp/audio_stream.fifo') - `youtube-dl --output audio --write-info-json --quiet --format bestaudio https://www.youtube.com/watch?v=5xfEr2Oxdys` - spawn 'ffmpeg -y -i audio -f s16le -acodec pcm_s16le -ar 24000 -loglevel quiet /tmp/audio_stream.fifo' - @client.player.stream_named_pipe('/tmp/audio_stream.fifo') - else - @client.text_user(@sender, INVALID_COMMAND_MSG) - end - end - end - - # Sets various callbacks that can be triggered during the connection. - def set_callbacks - @client.on_text_message do |message| - parse_message(message) - end - end - - # Checks message sender against ADMINS array to verify if they have - # permission to use a specific command. - def has_permission?(admin_command, sender) - if ENABLE_ADMINS and admin_command - return ADMINS.include?(sender) - else - return true - end - end - - def add(sender, url) - if OUTPUT_ENABLED - puts("#{sender} has added a song to the queue.") - end - if @song_queue.add_song?(url, sender) - @client.text_channel(@client.me.current_channel.name, "#{sender} has added a song to the queue.") - else - @client.text_user(sender, INVALID_URL_MSG) - end - end - - def skip(sender) - if OUTPUT_ENABLED - puts("#{sender} has voted to skip the current song.") - end - if @song_queue.get_current_song.add_skip?(sender) - @client.text_channel(@client.me.current_channel.name, "#{sender} has voted to skip the current song.") - if @song_queue.get_current_song.skip_now?(@client.me.current_channel.users.count - 1) - @client.text_channel(@client.me.current_channel.name, SKIP_SUCCESS_MSG) - @song_queue.get_current_song.skip - end - else - @client.text_user(sender, ALREADY_SKIPPED_MSG) - end - end - - def volume(sender, vol) - - end - - def move(sender, channel) - begin - @client.join_channel(channel) - rescue Mumble::ChannelNotFound - @client.text_user(sender, CHANNEL_DOES_NOT_EXIST_MSG) - end - end -end diff --git a/mumbledj/run_bot.rb b/mumbledj/run_bot.rb deleted file mode 100644 index 0d85cd8..0000000 --- a/mumbledj/run_bot.rb +++ /dev/null @@ -1,20 +0,0 @@ -# MumbleDJ -# By Matthieu Grieger -# run_bot.rb - -require_relative "mumbledj" -require_relative "config" -require "thread" - -bot = MumbleDJ.new(username=BOT_USERNAME, server_address=MUMBLE_SERVER_ADDRESS, port=MUMBLE_SERVER_PORT, - default_channel=DEFAULT_CHANNEL, password=MUMBLE_PASSWORD) -bot.connect - -begin - t = Thread.new do - $stdin.gets - end - - t.join - rescue Interrupt => e -end diff --git a/mumbledj/song.rb b/mumbledj/song.rb deleted file mode 100644 index eca2a52..0000000 --- a/mumbledj/song.rb +++ /dev/null @@ -1,57 +0,0 @@ -# MumbleDJ v2 -# By Matthieu Grieger -# song.rb - -require_relative "config" - -# Base Song class that defines default behavior for any kind of song. -class Song - - # Starts the song. - def start - - end - - # Gets the name of the user who submitted the song. - def get_submitter - return @submitter - end - - # Adds a skipper to the skips array for the current song. - def add_skip?(username) - if not @skips.include?(username) - @skips << username - return true - else - return false - end - end - - # Determines if a skip should occur. Returns true if a skip is needed, - # false otherwise. - def skip_now?(total_users) - return (total_users / @skips.count) >= SKIP_RATIO - end -end - -class YouTubeSong < Song - - attr_reader :url, :submitter, :song_title, :song_duration, :song_thumbnail_url - - # Initializes the YouTubeSong object and retrieves the song title, - # duration, and thumbnail URL from the YouTube API. - def initialize(url, submitter) - @url = url - @submitter = submitter - @skips = [] - # TODO: Retrieve YouTube information - @song_title = "" - @song_duration = "" - @song_thumbnail_url = "" - end - - # Downloads the audio for the YouTube video and returns the filename. - def download_audio - - end -end diff --git a/mumbledj/song_queue.rb b/mumbledj/song_queue.rb deleted file mode 100644 index c7975ee..0000000 --- a/mumbledj/song_queue.rb +++ /dev/null @@ -1,80 +0,0 @@ -# MumbleDJ v2 -# By Matthieu Grieger -# song_queue.rb - -require_relative "song" - -# A specialized SongQueue class that handles queueing/unqueueing songs -# and other actions. -class SongQueue - - attr_reader :queue - - # Initializes a new song queue. - def initialize - @queue = [] - end - - # Checks if song already exists in the queue, and adds it if it doesn't - # already exist. - def add_song?(url, submitter) - youtube_regex = /(https?:\/\/www\.youtube\.com\/watch\?v=([\d\a_\-]+)) - |(https?:\/\/youtube\.com\/watch\?v=([\d\a_\-]+)) - |(https?:\/\/youtu\.be\/([\d\a_\-]+)) - |(https?:\/\/youtube\.com\/v\/([\d\a_\-]+)) - |(https?:\/\/www\.youtube\.com\/v\/([\d\a_\-]+))/x - - if youtube_regex.match(url) - audio_type = "youtube" - end - - if @queue.empty? - if audio_type == "youtube" - song = YouTubeSong.new(url, submitter) - end - @queue.push(song) - else - @queue.each do |song| - if song.url == url - return false - end - end - if audio_type == "youtube" - song = YouTubeSong.new(url, submitter) - end - @queue.push(song) - end - end - - # Processes a song delete request. Searches the queue for songs with - # titles containing the keyword. If found, the song is deleted if the - # username of the user who requested the deletion matches the - # username of who originally added the song. - def delete_song?(keyword, username) - if not @queue.empty? - @queue.each do |song| - if song.song_title.includes?(keyword) - if song.get_submitter == username - @queue.delete(song) - return true - end - end - end - return false - else - return false - end - - end - - # Returns a formatted string that contains information about the next - # song in the queue. - def peek_next - - end - - # Returns the current Song object from the queue. - def get_current_song - return @queue[0] - end -end