Skip to content

Instantly share code, notes, and snippets.

@NickHu
Created August 20, 2015 11:45
Show Gist options
  • Save NickHu/7d82d1316d62b7d11950 to your computer and use it in GitHub Desktop.
Save NickHu/7d82d1316d62b7d11950 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'mumble-ruby'
require 'ruby-mpd'
require 'sqlite3'
class Bot
attr_reader :queue
def initialize(client, mpd, quickmpd, karma_db)
@client = client
@mpd = mpd
@mpd_interrupt = false
@quickmpd = quickmpd
@queue = []
@mpd.clear
@mpd.on :playlistlength do |playlistlength|
if playlistlength < @queue.length
@queue.pop
end
end
@quickmpd.clear
@quickmpd.on :playlistlength do |pll|
if pll > 0 and @mpd.playing? == true
@mpd_interrupt=true
@mpd.pause=true
@quickmpd.play
elsif pll > 0
@quickmpd.play
else
if @mpd_interrupt
@mpd.play
end
end
end
@karma = karma_db
@soundbytes = populate_soundbytes("quickplay")
end
def command_handler(input)
input.gsub!(/(<[^>]*>)|\n|\t/s) {" "}
if (md = (/(\w+)\+\+(?:$|\s+)(.*)/).match(input))
#puts "Increment"
#puts md.inspect
if (md[2].empty? != true)
karma_modify(md[1], md[2], 1)
else
karma_modify(md[1], nil, 1)
end
return nil
elsif (md = (/(\w+)--(?:$|\s+)(.*)/).match(input))
#puts "Decrement"
#puts md.inspect
if (md[2].empty? != true)
karma_modify(md[1], md[2], 0)
else
karma_modify(md[1], nil, 0)
end
return nil
end
if @soundbytes.key?(input.downcase)
quickplay @soundbytes[input.downcase].sample
end
if (input.start_with?("!") != true)
return nil
end
input[0] = ''
argv = input.split
#puts "Argv #{argv}"
if (argv[0].eql? "help")
helptext = <<-EOF
<br />
!help - List commands <br />
!play - Play music <br />
!pause - Pause music <br />
!queue - List play queue <br />
!next - Next track <br />
!clear - Clear playlist <br />
!volume [up|down|0-100] - Set volume <br />
!youtube - Queue YouTube by URL or ID <br />
!soundcloud - Queue SoundCloud by URL or ID <br />
!url - Queue audio file by URL <br />
!8ball - Ask the Magic 8 Ball <br />
!coin - Flip a coin <br />
!dice - Roll the dice <br />
!karma - Show karma for something <br />
&lt;WORD&gt;++ &lt;REASON&gt; - Increment karma for something <br />
&lt;WORD&gt;-- &lt;REASON&gt; - Decrement karma for something <br />
!reload - Reload config
EOF
@client.text_channel(0, helptext)
elsif (argv[0].eql? "play")
while @quickmpd.status[:playlistlength] > 0
sleep 1
end
@mpd.play
@client.text_channel(0, "Playing...")
elsif (argv[0].eql? "pause")
@mpd.pause=true
@client.text_channel(0, "Pausing...")
elsif (argv[0].eql? "queue")
if @queue.empty?
@client.text_channel(0, "Queue empty")
else
@client.text_channel(0, "<br />* " + @queue.join("<br />"))
end
elsif (argv[0].eql? "next")
@mpd.next
@client.text_channel(0, "Skipping...")
elsif (argv[0].eql? "clear")
@mpd.clear
@client.text_channel(0, "Clearing...")
elsif (argv[0].eql? "volume" or argv[0].eql? "vol")
if argv[1].eql? "up"
if argv[2]
@mpd.volume=(@mpd.volume + argv[2])
@client.text_channel(0, "Increasing volume by #{argv[2]}")
else
@mpd.volume=(@mpd.volume + 5)
@client.text_channel(0, "Increasing volume by 5")
end
elsif argv[1].eql? "down"
if argv[2]
@mpd.volume=(@mpd.volume - argv[2])
@client.text_channel(0, "Decreasing volume by #{argv[2]}")
else
@mpd.volume=(@mpd.volume - 5)
@client.text_channel(0, "Decreasing volume by 5")
end
else
if argv[1].to_i.integer? and argv[1].to_i >= 0 and argv[1].to_i <= 100
@mpd.volume=argv[1]
@client.text_channel(0, "Set volume to #{argv[1]}")
end
end
elsif (argv[0].eql? "youtube" or argv[0].eql? "yt")
begin
youtube(argv[1])
rescue
@client.text_channel(0, "BEEP BEEP ERROR")
end
elsif (argv[0].eql? "soundcloud" or argv[0].eql? "sc")
queue(argv[1])
elsif (argv[0].eql? "url")
queue(argv[1], :url)
elsif (argv[0].eql? "8ball")
@client.text_channel(0, magic8ball)
elsif (argv[0].eql? "coin")
@client.text_channel(0, "It's " + coin)
elsif (argv[0].eql? "dice" or argv[0].eql? "rtd")
roll = 1
if argv[1]
roll = dice(argv[1].to_i)
else
roll = dice
end
@client.text_channel(0, "Rolled a #{roll}")
elsif (argv[0].eql? "karma")
karma_print(argv[1])
elsif (argv[0].eql? "reload")
@quickmpd.update
@soundbytes = populate_soundbytes("quickplay")
@client.text_channel(0, "Reloaded config")
end
end
def quickplay(file)
@quickmpd.add(file) unless @quickmpd.playing?
end
def populate_soundbytes(directory)
items = Dir.entries(directory)
items.delete(".")
items.delete("..")
soundbytes = Hash.new
items.each { |item| root = item.match(/([^\-]+)(-\d+)?\.ogg/); (soundbytes[root[1]] ||= []) << item }
return soundbytes
end
def karma_print(thing)
karma_str = ""
stm = @karma.prepare("SELECT Karma FROM Karma WHERE Item = ?")
stm.bind_params(thing.downcase)
rs = stm.execute
row = rs.next
if row
karma_str << "Karma of #{thing} is #{row[0]} <br />"
rstm = @karma.prepare("SELECT Reason, Quantity, Positive FROM Reason WHERE Item = ?")
rstm.bind_params(thing.downcase)
rrs = rstm.execute
while (rrow = rrs.next) do
if rrow[2] == 0
karma_str << "-#{rrow[1]} ~ #{rrow[0]} <br />"
elsif rrow[2] == 1
karma_str << "+#{rrow[1]} ~ #{rrow[0]} <br />"
end
end
rstm.close if rstm
else
karma_str = "No karma for #{thing} <br />"
end
stm.close if stm
@client.text_channel(0, karma_str)
end
def karma_modify(thing, reason, positive)
if reason
rstm = nil
if karma_reason_exists(thing, reason, positive)
rstm = @karma.prepare("UPDATE Reason SET Quantity = Quantity + 1 WHERE Reason = ?")
rstm.bind_params(reason.downcase)
else
rstm = @karma.prepare("INSERT INTO Reason VALUES(?, ?, 1, ?)")
rstm.bind_params(reason.downcase, thing.downcase, positive)
end
rstm.execute
rstm.close if rstm
end
stm = nil
feedback = ""
if karma_item_exists(thing)
if positive == 1
stm = @karma.prepare("UPDATE Karma SET Karma = Karma + 1 WHERE Item = ?")
feedback << "Karma gained for #{thing}"
elsif positive == 0
stm = @karma.prepare("UPDATE Karma SET Karma = Karma - 1 WHERE Item = ?")
feedback << "Karma lost for #{thing}"
end
else
if positive == 1
stm = @karma.prepare("INSERT INTO Karma VALUES(?, 1)")
feedback << "Karma gained for #{thing}"
elsif positive == 0
stm = @karma.prepare("INSERT INTO Karma VALUES(?, -1)")
feedback << "Karma lost for #{thing}"
end
end
feedback << " ~ #{reason}" if reason
stm.bind_params(thing.downcase)
stm.execute
@client.text_channel(0, feedback)
stm.close if stm
end
def karma_item_exists(item)
stm = @karma.prepare "SELECT * FROM Karma WHERE Item = ?"
stm.bind_params(item.downcase)
rs = stm.execute
if rs.next
stm.close if stm
return true
else
stm.close if stm
return false
end
end
def karma_reason_exists(thing, reason, positive)
stm = @karma.prepare "SELECT * FROM Reason WHERE Item = ? AND Reason = ? AND Positive = ?"
stm.bind_params(thing.downcase, reason.downcase, positive)
rs = stm.execute
if rs.next
stm.close if stm
return true
else
stm.close if stm
return false
end
end
def youtube(input)
matchdata = input.match(/watch\?v=(\w{11})/)
if matchdata
id = matchdata[1]
elsif input =~ /[\w\-]{11}/
id = input
end
if id.empty?
raise RuntimeError, "Failed to parse valid YouTube identifier"
end
queue(id, :youtube)
end
def get_title(id)
title = `./youtube-dl -e "#{id}"` or raise RuntimeError, "Failed to get Title"
return title
end
def get_url_generic(id)
url = `./youtube-dl -g "#{id}"`
return url.gsub(/https:\/\//, "http://")
end
def get_youtube(id)
system("./youtube-dl -f141 #{id} -o \"/var/lib/mpd/music/%\(id\)s.%\(ext\)s\"") or raise RuntimeError, "Failed to get URL"
return "#{id}.m4a"
end
def queue(id, source = :generic)
url = ""
if source == :youtube
title = get_title id
url = get_youtube(id)
elsif source == :url
title = id
url = id.gsub(/https:\/\//, "http://")
else
title = get_title id
url = get_url_generic(id)
end
begin
@mpd.update
@mpd.add(url.chomp)
@queue.push(title)
@client.text_channel(0, "Queued #{@queue.last}")
rescue
@client.text_channel(0, "BEEP BEEP ERROR")
end
end
def magic8ball
outcome = rand(21)
case outcome
when 0
return "It is certain"
when 1
return "It is decidedly so"
when 2
return "Without a doubt"
when 3
return "Yes definitely"
when 4
return "You may rely on it"
when 5
return "As I see it, yes"
when 6
return "Most likely"
when 7
return "Outlook good"
when 8
return "Yes"
when 9
return "Signs point to yes"
when 10
return "Reply hazy try again"
when 11
return "Ask again later"
when 12
return "Better not tell you now"
when 13
return "Cannot predict now"
when 14
return "Concentrate and ask again"
when 15
return "Don't count on it"
when 16
return "My reply is no"
when 17
return "My sources say no"
when 18
return "Outlook not so good"
when 19
return "Very doubtful"
end
end
def coin
flip = rand(2)
case flip
when 0
return "heads"
when 1
return "tails"
end
end
def dice(faces = 6)
return 1 + rand(faces)
end
end
#########################
#Mumble code starts here#
#########################
cli = Mumble::Client.new('localhost', 64738, 'phrik')
cli.connect
sleep(1)
mpd = MPD.new("localhost", 6600, {callbacks: true})
mpd.connect
quickmpd = MPD.new("/home/nick/mumble-client/quickmpd", 0, {callbacks: true})
quickmpd.connect
quickmpd.update
karma_db = SQLite3::Database.open "karma.db"
karma_db.execute "CREATE TABLE IF NOT EXISTS Karma(Item VARCHAR PRIMARY KEY, Karma INTEGER NOT NULL)"
karma_db.execute "CREATE TABLE IF NOT EXISTS Reason(Reason VARCHAR NOT NULL, Item VARCHAR NOT NULL, Quantity INTEGER NOT NULL, Positive BOOLEAN NOT NULL, PRIMARY KEY(Reason, Item, Positive), FOREIGN KEY(Item) REFERENCES Karma(Item))"
phrik = Bot.new(cli, mpd, quickmpd, karma_db)
cli.on_text_message do |msg|
phrik.command_handler(msg.message)
end
#cli.on_connected do
# cli.me.mute
# cli.me.deafen
#end
cli.player.stream_named_pipe('/tmp/mumble.fifo')
gets
karma_db.close if karma_db
mpd.disconnect
quickmpd.disconnect
cli.disconnect
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment