From 8a7e4ec6a0e737058251ae4b8d1c1eaf4b712d8d Mon Sep 17 00:00:00 2001 From: Joe Rayhawk Date: Wed, 16 Mar 2022 19:05:20 -0700 Subject: crystal/irc.cr: add obs integration and parallelize everything --- crystal/irc.cr | 664 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 412 insertions(+), 252 deletions(-) (limited to 'crystal') diff --git a/crystal/irc.cr b/crystal/irc.cr index ceddd56..391f2c8 100644 --- a/crystal/irc.cr +++ b/crystal/irc.cr @@ -7,6 +7,12 @@ require "crystal_mpd" STDOUT.sync = true +struct Nil + def as_s? + self + end +end + settings = Hash(String, String).new settings["home"] = Path.home.to_s @@ -18,6 +24,9 @@ settings["home"] = Path.home.to_s end end +obsipc = Channel( String ).new +ircipc = Channel( Tuple( String, String ) ).new + snifflast = Int64.new(0) client = Twitcr::Client.new( settings ) @@ -113,6 +122,7 @@ def getvoice( settings : Hash(String, String), userdir : String, chatuser : Stri return( [namesub, voice_setting, voice_output ] ) end +# text2speech def t2s( settings : Hash(String, String), userdir : String, chatuser : String, text : String ) if ( text !~ /^ *(!|\|)/ ) namesub, voice_setting, voice = getvoice( settings, userdir, chatuser ) @@ -132,281 +142,431 @@ end lastvoice = Array(String).new -bot = Twitch::IRC::Client.new( nick: "bungmonkey", token: "oauth:" + settings["access_token"], log_mode: true ) -bot.tags = [ "membership", "tags", "commands" ] - -# Reconnect on IO::Error? +# OBS +spawn do + loop do + begin -# Create a handler to process incoming messages -bot.on_message do |message| - spawn do - next unless ( userlogreturn = userlog( settings, message ) ) - chatuser, uid, userdir = userlogreturn - if ( chatuser == "pipne" ) && ( ( Time.utc.to_unix - snifflast ) >= 14400 ) - snifflast = Time.utc.to_unix - t2smsg( chatuser + " sniff null" ) - end - if ( t2sreturn = t2s( settings, userdir, chatuser, message.params[1] ) ) - lastvoice.insert( 0, t2sreturn ) - lastvoice = lastvoice[0..4] + obs_pubsub = HTTP::WebSocket.new( URI.parse( "ws://127.0.0.1:4444/" ), HTTP::Headers{"Cookie" => "SESSIONID=1234"} ) + # Outgoing + spawn do + while msg = obsipc.receive + pp msg + obs_pubsub.send( msg ) + end + end + # Incoming + obs_pubsub.on_message do | message | + json = JSON.parse( message ) + if json["error"]? + puts json["error"] + ircipc.send( { "#" + settings["channel"], "| obs: #{json["error"]}" } ) + next + end + case json["update-type"]? + when "StreamStatus" # these are a bit noisy, + next # so skip them + when "SwitchScenes" + ircipc.send( { "#" + settings["channel"], "| obs: switched scene to " + ( json["scene-name"]?.as_s? || "unknown" ) } ) + when "MediaEnded" + request = Hash( String, String | Bool ){ + "request-type" => "SetSceneItemProperties", + "message-id" => "SetSceneItemProperties", + "item" => json["sourceName"].as_s, + "visible" => false + } + #ircipc.send( { "##{settings["channel"]}", "Disabling #{json["sourceName"].as_s}" } ) + obsipc.send( request.to_json ) + when "SourceFilterVisibilityChanged" + ircipc.send( { "##{settings["channel"]}", "| obs: source #{json["sourceName"]} filter #{json["filterName"]} visibility is now #{json["filterEnabled"]}" } ) + end + print( "RECEIVED: ") + print( json.to_pretty_json ) + print( "\n") + case json["message-id"]? + when "GetCurrentScene" + ircipc.send( { "#" + settings["channel"], "| obs: " + json["sources"].as_a.map{ |source| source["name"] }.join(", ") } ) + when "GetSourceFilters" + ircipc.send( { "#" + settings["channel"], "| obs: " + json["filters"].as_a.map{ |filter| filter["name"] }.join(", ") } ) + when "GetSceneList" + ircipc.send( { "#" + settings["channel"], "| obs: " + json["scenes"].as_a.map{ |scene| scene["name"] }.join(", ") } ) + when "toggle-filter" + name = json["filters"][0]["name"].as_s + visible = ( json["filters"][0]["enabled"].as_bool == false ) + ircipc.send( { "#" + settings["channel"], "| obs: Setting visibility of filter #{name} to #{visible}" } ) + obsipc.send( "{ \"request-type\": \"SetSourceFilterVisibility\", \"message-id\": \"SetSourceFilterVisibility\", \"sourceName\": \"#{name}\", \"visible\": #{visible} }" ) + #This is a dumb hack to toggle SetSceneItemProperty visibility + when "toggle-source" + name = json["name"].as_s + visible = ( json["visible"].as_bool == false ) + ircipc.send( { "#" + settings["channel"], "| obs: Setting visibility of source #{name} to #{visible}" } ) + obsipc.send( "{ \"request-type\": \"SetSceneItemProperties\", \"message-id\": \"SetSceneItemProperties\", \"item\": \"#{name}\", \"visible\": #{visible} }" ) + end + end + obs_pubsub.run + rescue ex : Socket::ConnectError + # these are a bit noisy + pp ex + obs_pubsub && obs_pubsub.close + rescue ex + ircmsg( settings["home"], settings["channel"], "irc.cr: " + ex.to_s.gsub(/\r|\n/, ' ') ) + puts ex + obs_pubsub && obs_pubsub.close + end + sleep 10 + next end - next unless ( ( match = message.params[1].match(/^ *!([A-Za-z]+) (([a-zA-Z0-9= _\:,.&'\/?;\\\(\)\[\]+\-]|!)+)/) || message.params[1].match(/^ *!([A-Za-z]+)/) ) ) - cmd = match[1] - own = ( message.tags["room-id"] == message.tags["user-id"] ) - vip = ( message.tags["badges"].to_s.matches?( /(^|,)vip\// ) ) - mod = ( message.tags["mod"] == "1" ) - sub = ( message.tags["subscriber"] == "1" ) - if ( ( cmd =~ /^(substitute|voicesub)$/ ) && ( mod || sub ) ) - case message.params[1].split( " " ).size - when 1 - if File.exists?( userdir + "/voicesub" ) - bot.message( "#bungmonkey", "| Current name substitution is \"#{File.read( userdir + "/voicesub" )}\"." ) - else - bot.message( "#bungmonkey", "| Current name substitution is disabled." ) +end + +# IRC +loop do + begin + + bot = Twitch::IRC::Client.new( nick: settings["channel"], token: "oauth:" + settings["access_token"], log_mode: true ) + bot.tags = [ "membership", "tags", "commands" ] + + # Outgoing + spawn do + while msgtuple = ircipc.receive +bot.message( msgtuple[0], msgtuple[1][0..480] ) end - else - if match[2]? - voicesub = match[2].downcase - if voicesub =~ /^(disabled|null|disable|none)$/ + end + + # Create a handler to process incoming messages + bot.on_message do |message| + spawn do + next unless ( userlogreturn = userlog( settings, message ) ) + chatuser, uid, userdir = userlogreturn + if ( chatuser == "pipne" ) && ( ( Time.utc.to_unix - snifflast ) >= 14400 ) + snifflast = Time.utc.to_unix + t2smsg( chatuser + " sniff null" ) + end + if ( t2sreturn = t2s( settings, userdir, chatuser, message.params[1] ) ) + lastvoice.insert( 0, t2sreturn ) + lastvoice = lastvoice[0..4] + end + next unless ( ( match = message.params[1].match(/^ *!([A-Za-z]+) (([a-zA-Z0-9= _\:,.&'\/?;\\\(\)\[\]+\-]|!)+)/) || message.params[1].match(/^ *!([A-Za-z]+)/) ) ) + cmd = match[1] + own = ( message.tags["room-id"] == message.tags["user-id"] ) + vip = ( message.tags["badges"].to_s.matches?( /(^|,)vip\// ) ) + mod = ( message.tags["mod"] == "1" ) + sub = ( message.tags["subscriber"] == "1" ) + if ( ( cmd =~ /^(substitute|voicesub)$/ ) && ( mod || sub ) ) + case message.params[1].split( " " ).size + when 1 if File.exists?( userdir + "/voicesub" ) - File.delete( userdir + "/voicesub" ) - bot.message( "#bungmonkey", "| Name substitution for #{chatuser} is now disabled." ) + bot.message( "#bungmonkey", "| Current name substitution is \"#{File.read( userdir + "/voicesub" )}\"." ) else - bot.message( "#bungmonkey", "| Name substitution for #{chatuser} is already disabled." ) + bot.message( "#bungmonkey", "| Current name substitution is disabled." ) end else - File.directory?( userdir ) || Dir.mkdir( userdir ) - File.write( userdir + "/voicesub", voicesub ) - bot.message( "#bungmonkey", "| Name substitution for #{chatuser} is now \"#{File.read( userdir + "/voicesub" )}\"." ) + if match[2]? + voicesub = match[2].downcase + if voicesub =~ /^(disabled|null|disable|none)$/ + if File.exists?( userdir + "/voicesub" ) + File.delete( userdir + "/voicesub" ) + bot.message( "#bungmonkey", "| Name substitution for #{chatuser} is now disabled." ) + else + bot.message( "#bungmonkey", "| Name substitution for #{chatuser} is already disabled." ) + end + else + File.directory?( userdir ) || Dir.mkdir( userdir ) + File.write( userdir + "/voicesub", voicesub ) + bot.message( "#bungmonkey", "| Name substitution for #{chatuser} is now \"#{File.read( userdir + "/voicesub" )}\"." ) + end + end end - end - end - elsif ( cmd =~ /^(commands|help)$/ ) - bot.message( "#bungmonkey", "| https://bungmonkey.omgwallhack.org/txt/commands.txt" ) - elsif ( ( cmd == "lastvoice" ) && ( mod || sub ) ) - unless lastvoice.empty? - bot.message( "#bungmonkey", "| Last voices were " + lastvoice.join( ", " ) ) - else - bot.message( "#bungmonkey", "| No voices used so far." ) - end - elsif ( ( cmd =~ /^(voices|voicelist)$/ ) && ( mod || sub ) ) - bot.message( "#bungmonkey", "| https://bungmonkey.omgwallhack.org/voicelist.txt" ) - elsif ( ( cmd =~ /^(setvoice|voice)$/ ) && ( mod || sub ) ) - case message.params[1].split( " " ).size - when 1 - namesub, voice_setting, voice_output = getvoice( settings, userdir, chatuser ) - bot.message( "#bungmonkey", "| Current voice is #{voice_setting}" ) - when 2 - if match[2]? - voice = match[2].downcase - if voice =~ /disabled|null|disable|none|random/ - if File.exists?( userdir + "/voice" ) - File.delete( userdir + "/voice" ) - bot.message( "#bungmonkey", "| Voice for #{chatuser} is now random." ) + elsif ( cmd =~ /^(commands|help)$/ ) + bot.message( "#bungmonkey", "| https://bungmonkey.omgwallhack.org/txt/commands.txt" ) + elsif ( cmd =~ /^(dexem)$/ ) + ircipc.send( { "#bungmonkey", "| You're doing great work, Dexem!" } ) + elsif ( ( cmd == "lastvoice" ) && ( mod || sub ) ) + unless lastvoice.empty? + bot.message( "#bungmonkey", "| Last voices were " + lastvoice.join( ", " ) ) + else + bot.message( "#bungmonkey", "| No voices used so far." ) + end + elsif ( ( cmd =~ /^(voices|voicelist)$/ ) && ( mod || sub ) ) + bot.message( "#bungmonkey", "| https://bungmonkey.omgwallhack.org/voicelist.txt" ) + elsif ( ( cmd =~ /^(setvoice|voice)$/ ) && ( mod || sub ) ) + case message.params[1].split( " " ).size + when 1 + namesub, voice_setting, voice_output = getvoice( settings, userdir, chatuser ) + bot.message( "#bungmonkey", "| Current voice is #{voice_setting}" ) + when 2 + if match[2]? + voice = match[2].downcase + if voice =~ /disabled|null|disable|none|random/ + if File.exists?( userdir + "/voice" ) + File.delete( userdir + "/voice" ) + bot.message( "#bungmonkey", "| Voice for #{chatuser} is now random." ) + else + bot.message( "#bungmonkey", "| Voice for #{chatuser} is already random." ) + end + elsif voices.has_key?( voice ) + csvoice = voices[voice] + File.directory?( userdir ) || Dir.mkdir( userdir ) + File.write( userdir + "/voice", csvoice ) + pp userdir + bot.message( "#bungmonkey", "| Voice for #{chatuser} is now #{File.read( userdir + "/voice" )}." ) + # TODO: make separate script to print streamelements URLs to client machine + else + pp ( match ) + bot.message( "#bungmonkey", "| Invalid voice. To see list, use !voices" ) + end + end + when 3 + else + end + elsif ( ( cmd =~ /^scene$/ ) && ( mod || own ) ) + if ( match[2]? && match[2] =~ /^[a-zA-Z0-9-_]+$/ ) + obsipc.send( "{ \"request-type\": \"SetCurrentScene\", \"message-id\": \"SetCurrentScene\", \"scene-name\": \"" + match[2] + "\" }" ) + else + obsipc.send( "{ \"request-type\": \"GetSceneList\", \"message-id\": \"GetSceneList\" }" ) + end + elsif ( ( cmd =~ /^source$/ ) && ( mod || own ) ) + request = Hash( String, String | Bool ).new + if ( match[2]? ) && ( sourceargs = match[2].match( /^([a-zA-Z0-9-_]+) +(true|false)/ ) ) + request["request-type"] = "SetSceneItemProperties" + request["message-id"] = "SetSceneItemProperties" + request["item"] = sourceargs[1] + sourceargs[2] == "true" && ( request["visible"] = true ) + sourceargs[2] == "false" && ( request["visible"] = false ) + + ircipc.send( { "##{settings["channel"]}", "Setting source #{sourceargs[1]} visibility to #{sourceargs[2]}" } ) + obsipc.send( request.to_json ) + elsif ( match[2]? && match[2] =~ /^[a-zA-Z0-9-_]+$/ ) + request["request-type"] = "GetSceneItemProperties" + request["message-id"] = "toggle-source" + request["item"] = match[2] + obsipc.send( request.to_json ) + else + request["request-type"] = "GetCurrentScene" + request["message-id"] = "GetCurrentScene" + obsipc.send( request.to_json ) + end + elsif ( ( cmd =~ /^filter$/ ) && ( mod || own ) ) + request = Hash( String, String | Bool ){ + "request-type" => "GetSourceFilters" + } + if ( match[2]? ) && ( filterargs = match[2].match( /^([a-zA-Z0-9-_]+) +([a-zA-Z0-9-_]+) (true|false)/ ) ) + request["request-type"] = "SetSourceFilterVisibility" + request["message-id"] = "SetSourceFilterVisibility" + request["sourceName"] = filterargs[1] + request["filterName"] = filterargs[2] + #request["filterEnabled"] = filterargs[3] + filterargs[3] == "true" && ( request["filterEnabled"] = true ) + filterargs[3] == "false" && ( request["filterEnabled"] = false ) + obsipc.send( request.to_json ) + elsif ( match[2]? ) && ( filterargs = match[2].match( /^([a-zA-Z0-9-_]+) +([a-zA-Z0-9-_]+)/ ) ) + request["message-id"] = "toggle-filter" + request["sourceName"] = filterargs[1] + request["filterName"] = filterargs[2] + obsipc.send( request.to_json ) + elsif ( match[2]? && match[2] =~ /^[a-zA-Z0-9-_]+$/ ) + request["sourceName"] = match[2] + request["message-id"] = "GetSourceFilters" + obsipc.send( request.to_json ) + else + ircipc.send( { "##{settings["channel"]}", "Must provide at least one source name as argument, and optionally one filter name." } ) + end + elsif ( cmd =~ /^(metaminute|youdied)$/ ) + request = Hash( String, String | Bool ).new + request["request-type"] = "SetSceneItemProperties" + request["message-id"] = "SetSceneItemProperties" + request["item"] = "media-" + cmd + # This breaks playback (???) + #request["visible"] = false + #obsipc.send( request.to_json ) + #sleep 0.5 + # I should probably set this up to add the source, and then delete the source at the end of playback. + request["visible"] = true + ircipc.send( { "##{settings["channel"]}", "Playing media-#{cmd}" } ) + obsipc.send( request.to_json ) + elsif ( ( cmd =~ /^(status|title)$/ ) && ( mod || own ) ) + begin + if match[2]? + client.put_channel!( settings["channel_id"].to_u64, title: match[2] ) + json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) ) + ircmsg( settings["home"], settings["channel"], "Title is now \"#{ json["data"][0]["title"] }\"") + else + json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) ) + ircmsg( settings["home"], settings["channel"], "| Title is currently \"#{ json["data"][0]["title"] }\"") + end + rescue ex + ircmsg( settings["home"], settings["channel"], "| An error occurred! " + ex.message.to_s ) + end + elsif ( ( cmd =~ /^(game|category)$/ ) && ( mod || own ) ) + begin + if match[2]? + puts "2 matches" + client.put_channel!( settings["channel_id"].to_u64, game: match[2] ) + json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) ) + ircmsg( settings["home"], settings["channel"], "Game is now \"#{ json["data"][0]["game_name"] }\"") else - bot.message( "#bungmonkey", "| Voice for #{chatuser} is already random." ) + puts "1 matches" + json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) ) + ircmsg( settings["home"], settings["channel"], "| Game is currently \"#{ json["data"][0]["game_name"] }\"") end - elsif voices.has_key?( voice ) - csvoice = voices[voice] - File.directory?( userdir ) || Dir.mkdir( userdir ) - File.write( userdir + "/voice", csvoice ) - pp userdir - bot.message( "#bungmonkey", "| Voice for #{chatuser} is now #{File.read( userdir + "/voice" )}." ) - # TODO: make separate script to print streamelements URLs to client machine + rescue ex + ircmsg( settings["home"], settings["channel"], "| An error occurred! " + ex.message.to_s ) + end + elsif ( ( cmd == "urban" ) && ( mod || own || sub || vip ) ) + if match[2]? && match[2] =~ /^([a-zA-Z0-9 -])+$/ + definition = urbandef( match[2] ) + ircmsg( settings["home"], settings["channel"], definition[0,400] ) else - pp ( match ) - bot.message( "#bungmonkey", "| Invalid voice. To see list, use !voices" ) + ircmsg( settings["home"], settings["channel"], "| Urban Dictionary search term should consist of letters, numbers, spaces, and/or hyphens." ) end - end - when 3 - else - end - elsif ( ( cmd =~ /^(status|title)$/ ) && ( mod || own ) ) - begin - if match[2]? - client.put_channel!( settings["channel_id"].to_u64, title: match[2] ) - json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) ) - ircmsg( settings["home"], settings["channel"], "Title is now \"#{ json["data"][0]["title"] }\"") - else - json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) ) - ircmsg( settings["home"], settings["channel"], "| Title is currently \"#{ json["data"][0]["title"] }\"") - end - rescue ex - ircmsg( settings["home"], settings["channel"], "| An error occurred! " + ex.message.to_s ) - end - elsif ( ( cmd =~ /^(game|category)$/ ) && ( mod || own ) ) - begin - if match[2]? - puts "2 matches" - client.put_channel!( settings["channel_id"].to_u64, game: match[2] ) - json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) ) - ircmsg( settings["home"], settings["channel"], "Game is now \"#{ json["data"][0]["game_name"] }\"") - else - puts "1 matches" - json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) ) - ircmsg( settings["home"], settings["channel"], "| Game is currently \"#{ json["data"][0]["game_name"] }\"") - end - rescue ex - ircmsg( settings["home"], settings["channel"], "| An error occurred! " + ex.message.to_s ) - end - elsif ( ( cmd == "urban" ) && ( mod || own || sub || vip ) ) - if match[2]? && match[2] =~ /^([a-zA-Z0-9 -])+$/ - definition = urbandef( match[2] ) - ircmsg( settings["home"], settings["channel"], definition[0,400] ) - else - ircmsg( settings["home"], settings["channel"], "| Urban Dictionary search term should consist of letters, numbers, spaces, and/or hyphens." ) - end - elsif ( cmd == "matrix" ) - effectsmsg( "overlay glmatrix" ) - elsif ( cmd =~ /juggle|juggler/ ) - effectsmsg( "overlay juggler3d" ) - elsif ( cmd =~ /fireworks|firework/ ) - effectsmsg( "overlay fireworkx" ) - elsif ( cmd == "pipes" ) - if match[2]? && match[2] =~ /^(fast|faster)$/ - effectsmsg( "overlay pipes " + match[2] ) - else - effectsmsg( "overlay pipes" ) - end - elsif ( cmd == "jellyfish" ) - effectsmsg( "overlay hydrostat" ) - elsif ( cmd == "gluten" ) - effectsmsg( "overlay flyingtoasters" ) - elsif ( cmd =~ /^(glsnake|glmatrix|gibson|xmatrix|flyingtoasters|moebiusgears|fireworkx|hydrostat|hypertorus|jigsaw|juggler3d|kaleidocycle|kumppa|molecule|noof|polyhedra)$/ ) - effectsmsg( "overlay " + cmd ) - elsif ( cmd =~ /gltext|cowsay|xcowsay/ ) - if ( match[2]? ) && ( gltextargs = match[2].match( /^([0-9]+) +(.+)$/ ) ) - seconds=UInt64.new( 1 ) - seconds=gltextargs[1].to_u64 - if ( own || mod || vip ) - effectsmsg( "overlay #{cmd} #{match[2]}" ) - puts "matched #{cmd} #{match[2]}" - elsif ( sub ) - if ( seconds > 20 ) - seconds=20 + elsif ( cmd == "matrix" ) + effectsmsg( "overlay glmatrix" ) + elsif ( cmd =~ /juggle|juggler/ ) + effectsmsg( "overlay juggler3d" ) + elsif ( cmd =~ /fireworks|firework/ ) + effectsmsg( "overlay fireworkx" ) + elsif ( cmd == "pipes" ) + if match[2]? && match[2] =~ /^(fast|faster)$/ + effectsmsg( "overlay pipes " + match[2] ) + else + effectsmsg( "overlay pipes" ) end - effectsmsg( "overlay #{cmd} #{seconds} #{gltextargs[2]}" ) - puts "matched #{cmd} #{seconds} #{gltextargs[2]}" - else - if ( seconds > 5 ) - seconds=5 + elsif ( cmd == "jellyfish" ) + effectsmsg( "overlay hydrostat" ) + elsif ( cmd == "gluten" ) + effectsmsg( "overlay flyingtoasters" ) + elsif ( cmd =~ /^(glsnake|glmatrix|gibson|xmatrix|flyingtoasters|moebiusgears|fireworkx|hydrostat|hypertorus|jigsaw|juggler3d|kaleidocycle|kumppa|molecule|noof|polyhedra)$/ ) + effectsmsg( "overlay " + cmd ) + elsif ( cmd =~ /gltext|cowsay|xcowsay/ ) + if ( match[2]? ) && ( gltextargs = match[2].match( /^([0-9]+) +(.+)$/ ) ) + seconds=UInt64.new( 1 ) + seconds=gltextargs[1].to_u64 + if ( own || mod || vip ) + effectsmsg( "overlay #{cmd} #{match[2]}" ) + puts "matched #{cmd} #{match[2]}" + elsif ( sub ) + if ( seconds > 20 ) + seconds=20 + end + effectsmsg( "overlay #{cmd} #{seconds} #{gltextargs[2]}" ) + puts "matched #{cmd} #{seconds} #{gltextargs[2]}" + else + if ( seconds > 5 ) + seconds=5 + end + effectsmsg( "overlay #{cmd} #{seconds} #{gltextargs[2]}" ) + puts "matched #{cmd} #{seconds} #{gltextargs[2]}" + end + elsif match[2]? && match[2] =~ /^.+$/ + effectsmsg( "overlay #{cmd} #{match[2]}" ) + puts "matched #{cmd} #{match[2]}" + else + effectsmsg( "overlay #{cmd}" ) + puts "failed to match gltext" end - effectsmsg( "overlay #{cmd} #{seconds} #{gltextargs[2]}" ) - puts "matched #{cmd} #{seconds} #{gltextargs[2]}" - end - elsif match[2]? && match[2] =~ /^.+$/ - effectsmsg( "overlay #{cmd} #{match[2]}" ) - puts "matched #{cmd} #{match[2]}" - else - effectsmsg( "overlay #{cmd}" ) - puts "failed to match gltext" - end - elsif ( cmd =~ /cow|cowabunga|holycow/ ) - if match[2]? && match[2] =~ /^([0-9])+$/ - effectsmsg( "overlay bouncingcow #{match[2]}" ) - else - effectsmsg( "overlay bouncingcow" ) - end - elsif ( cmd == "overlay" ) - if match[2]? && match[2] =~ /^[a-z]+$/ - ircmsg( settings["home"], settings["channel"], "| overlay requires an argument consisting wholly of lower case characters.") - effectsmsg( "overlay #{match[2]}" ) - else - end - elsif ( cmd == "hackerman" ) - ircmsg( settings["home"], settings["channel"], "| https://bungmonkey.omgwallhack.org/img/hackerman.jpg" ) - elsif ( cmd =~ /^(songrequest|sr)$/ ) && match[2]? - puts ("song detected: #{match[2]}") - if ( ( match[2] =~ /list=/ ) && ( match[2] !~ /v=/ ) ) - ircmsg( settings["home"], settings["channel"], "| Lists are not accepted.\n" ) - elsif Process.run( "sraddsong.sh", {match[2]} ) - m = MPD::Client.new - currentsong = m.currentsong - if ( currentsong ) && ( currentsong["file"].to_s == "http://music/music.ogg" ) + elsif ( cmd =~ /cow|cowabunga|holycow/ ) + if match[2]? && match[2] =~ /^([0-9])+$/ + effectsmsg( "overlay bouncingcow #{match[2]}" ) + else + effectsmsg( "overlay bouncingcow" ) + end + elsif ( cmd == "overlay" ) + if match[2]? && match[2] =~ /^[a-z]+$/ + ircmsg( settings["home"], settings["channel"], "| overlay requires an argument consisting wholly of lower case characters.") + effectsmsg( "overlay #{match[2]}" ) + else + end + elsif ( cmd == "hackerman" ) + ircmsg( settings["home"], settings["channel"], "| https://bungmonkey.omgwallhack.org/img/hackerman.jpg" ) + elsif ( cmd =~ /^(songrequest|sr)$/ ) && match[2]? + puts ("song detected: #{match[2]}") + if ( ( match[2] =~ /list=/ ) && ( match[2] !~ /v=/ ) ) + ircmsg( settings["home"], settings["channel"], "| Lists are not accepted.\n" ) + elsif Process.run( "sraddsong.sh", {match[2]} ) + m = MPD::Client.new + currentsong = m.currentsong + if ( currentsong ) && ( currentsong["file"].to_s == "http://music/music.ogg" ) + m.next + end + if ( status = m.status ) && ( playlistinfo = m.playlistinfo ) + ircmsg( settings["home"], settings["channel"], "| " + playlistinfo[ status["playlistlength"].to_i - 1 ]["file"].to_s + " added in position " + status["playlistlength"].to_s ) + else + ircmsg( settings["home"], settings["channel"], "| A failure occured." ) + end + m.disconnect + else + end + elsif ( cmd =~ /^current(|song)$/ ) + m = MPD::Client.new + if ( currentsong = m.currentsong ) + ircmsg( settings["home"], settings["channel"], "| Currently playing: " + currentsong["file"].to_s ) + else + ircmsg( settings["home"], settings["channel"], "| A failure occured." ) + end + m.disconnect + elsif ( cmd =~ /^seek$/ ) + if ( ( match[2] ) && ( match[2] =~ /([+-]|)[0-9]/ ) ) + m = MPD::Client.new + m.seekcur( match[2] ) + if ( ( musicstatus = m.status ) && ( pos = musicstatus["elapsed"].to_f.to_u64 ) ) + min=( pos / 60 ).to_u64 + sec=( pos % 60 ).to_u64 + ircmsg( settings["home"], settings["channel"], "| " + sprintf( "2%d:2%d", min, sec ) ) + else + ircmsg( settings["home"], settings["channel"], "| An error occurred. " ) + end + m.disconnect + else + ircmsg( settings["home"], settings["channel"], "| Seek requires an argument of an absolute position in integer number of seconds, or a relative position in in signed integer number of seconds such as +60" ) + end + elsif ( cmd =~ /^next(|song)$/ ) + m = MPD::Client.new m.next - end - if ( status = m.status ) && ( playlistinfo = m.playlistinfo ) - ircmsg( settings["home"], settings["channel"], "| " + playlistinfo[ status["playlistlength"].to_i - 1 ]["file"].to_s + " added in position " + status["playlistlength"].to_s ) - else - ircmsg( settings["home"], settings["channel"], "| A failure occured." ) - end - m.disconnect - else - end - elsif ( cmd =~ /^current(|song)$/ ) - m = MPD::Client.new - if ( currentsong = m.currentsong ) - ircmsg( settings["home"], settings["channel"], "| Currently playing: " + currentsong["file"].to_s ) - else - ircmsg( settings["home"], settings["channel"], "| A failure occured." ) - end - m.disconnect - elsif ( cmd =~ /^seek$/ ) - if ( ( match[2] ) && ( match[2] =~ /([+-]|)[0-9]/ ) ) - m = MPD::Client.new - m.seekcur( match[2] ) - if ( ( musicstatus = m.status ) && ( pos = musicstatus["elapsed"].to_f.to_u64 ) ) - min=( pos / 60 ).to_u64 - sec=( pos % 60 ).to_u64 - ircmsg( settings["home"], settings["channel"], "| " + sprintf( "2%d:2%d", min, sec ) ) - else - ircmsg( settings["home"], settings["channel"], "| An error occurred. " ) - end - m.disconnect - else - ircmsg( settings["home"], settings["channel"], "| Seek requires an argument of an absolute position in integer number of seconds, or a relative position in in signed integer number of seconds such as +60" ) - end - elsif ( cmd =~ /^next(|song)$/ ) - m = MPD::Client.new - m.next - if ( status = m.status ) && ( status["playlistlength"].to_i > 0 ) - if ( currentsong = m.nextsong ) - ircmsg( settings["home"], settings["channel"], "| Currently playing: " + currentsong["file"].to_s ) - else - ircmsg( settings["home"], settings["channel"], "| A failure occured." ) - end - else - ircmsg( settings["home"], settings["channel"], "| Playlist is now empty." ) - end - m.disconnect - elsif ( cmd == "followage" ) - begin - if match[2]? - args = match[2].split(/\s/) - if args[1]? - json = JSON.parse( client.get_user_follows( from: client.user_id( args[0] ).to_u64 , to: client.user_id( args[1] ).to_u64 ) ) - puts client.user_id( args[0] ).to_s - puts client.user_id( args[1] ).to_s - puts json - ircmsg( settings["home"], settings["channel"], "| " + json["data"][0]["followed_at"].to_s ) - elsif args[0]? - json = JSON.parse( client.get_user_follows( from: client.user_id( args[0] ).to_u64 , to: settings["channel_id"].to_u64 ) ) - puts client.user_id( args[0] ).to_s - puts json - ircmsg( settings["home"], settings["channel"], "| " + json["data"][0]["followed_at"].to_s ) + if ( status = m.status ) && ( status["playlistlength"].to_i > 0 ) + if ( currentsong = m.nextsong ) + ircmsg( settings["home"], settings["channel"], "| Currently playing: " + currentsong["file"].to_s ) + else + ircmsg( settings["home"], settings["channel"], "| A failure occured." ) + end + else + ircmsg( settings["home"], settings["channel"], "| Playlist is now empty." ) + end + m.disconnect + elsif ( cmd == "followage" ) + begin + if match[2]? + args = match[2].split(/\s/) + if args[1]? + json = JSON.parse( client.get_user_follows( from: client.user_id( args[0] ).to_u64 , to: client.user_id( args[1] ).to_u64 ) ) + puts client.user_id( args[0] ).to_s + puts client.user_id( args[1] ).to_s + puts json + ircmsg( settings["home"], settings["channel"], "| " + json["data"][0]["followed_at"].to_s ) + elsif args[0]? + json = JSON.parse( client.get_user_follows( from: client.user_id( args[0] ).to_u64 , to: settings["channel_id"].to_u64 ) ) + puts client.user_id( args[0] ).to_s + puts json + ircmsg( settings["home"], settings["channel"], "| " + json["data"][0]["followed_at"].to_s ) + end + else + json = JSON.parse( client.get_user_follows( from: uid.to_u64 , to: settings["channel_id"].to_u64 ) ) + puts json + ircmsg( settings["home"], settings["channel"], "| " + json["data"][0]["followed_at"].to_s ) + end + rescue ex + ircmsg( settings["home"], settings["channel"], "An error occurred! " + ex.message.to_s ) end - else - json = JSON.parse( client.get_user_follows( from: uid.to_u64 , to: settings["channel_id"].to_u64 ) ) - puts json - ircmsg( settings["home"], settings["channel"], "| " + json["data"][0]["followed_at"].to_s ) end - rescue ex - ircmsg( settings["home"], settings["channel"], "An error occurred! " + ex.message.to_s ) + end end - end - end -end - -rooms = Array( String ).new -rooms = [ "#bungmonkey", "kr3wzz" ] + rooms = Array( String ).new + rooms = [ "#bungmonkey", "kr3wzz" ] -while true - begin -# Connect to Twitch + # Connect to Twitch bot.run( rooms.map{ | room | room.sub( /^#/, "") } ) rescue ex : IO::Error pp ex + sleep 1 # loop to reconnect rescue ex pp ex -- cgit v1.2.3