diff options
Diffstat (limited to 'crystal')
-rw-r--r-- | crystal/tcpsocket.cr | 530 |
1 files changed, 280 insertions, 250 deletions
diff --git a/crystal/tcpsocket.cr b/crystal/tcpsocket.cr index a330aaf..a7d1e8a 100644 --- a/crystal/tcpsocket.cr +++ b/crystal/tcpsocket.cr @@ -40,6 +40,36 @@ class OpenSSL::SSL::Socket::Client end end +# Say() in all available primary channels +macro say_all_self_chan( text ) + if fibers["Twitch::IRC"]? + say( "twitch", config.chat_user.not_nil!.twitch.not_nil!, {{text}} ) + elsif fibers["BungmoBott::Socket client"]? && + say( "twitch_remote", config.chat_user.not_nil!.twitch.not_nil!, {{text}} ) + end + # FIXME: Maybe use config.join_channels.gamesurge[0]? think about this later + if fibers["GameSurge::IRC"]? + say( "gamesurge", config.chat_user.not_nil!.gamesurge.not_nil!, {{text}} ) + elsif fibers["BungmoBott::Socket client"]? + say( "gamesurge_remote", config.chat_user.not_nil!.gamesurge.not_nil!, {{text}} ) + end +end + +# IRC say +macro say( service, channel, text ) +#def say( ipc : Channel( Tuple( String, String ) ), channel : String, text : String ) + case {{service}} + when "twitch" + twitchircipc.send( { "#" + {{channel}}, {{text}} } ) + when "gamesurge" + ircipc.send( { "#" + {{channel}}, {{text}} } ) + when "twitch_remote" + bbscliipc.send( "say twitch " + {{text}} ) + when "gamesurge_remote" + bbscliipc.send( "say gamesurge " + {{text}} ) + end +end + macro testrefuser2uid( path ) {% if flag?(:windows) %} File.exists?( {{path}} ) && ( File.read( {{ path }} ) =~ /^[0-9]+$/ ) @@ -156,6 +186,7 @@ def obstemporarymediacreate( obs : OBS::WebSocket, sname : String, iname, path : } response = obs.scenes[sname].createinput( iname, "ffmpeg_source", isettings ) # Skip ORM stuff and configure the SceneItem as fast as we possibly can + # FIXME: start with ?sceneItemEnabled false to give us time to SetSceneItemTransform if ( rdata = response["responseData"]? ) && ( goodtransform = obs.sources.last_known_real_transform?( iname ) ) siid = rdata["sceneItemId"].as_i64 obs.send( OBS.req( "SetSceneItemTransform", JSON.parse( { "sceneName" => sname, "sceneItemId" => siid, "sceneItemTransform" => { "positionX" => goodtransform.to_h["positionX"], "positionY" => goodtransform.to_h["positionY"] } }.to_json ) ) ) @@ -224,8 +255,16 @@ mss : Bool mss = false {% end %} +voices = Hash( String, String ).new if ( mss || gcloud || aws ) && config.voice_list text2speech = true + if File.exists?( config.voice_list.not_nil! ) + File.read( config.voice_list.not_nil! ).strip.split( "\n" ).each do |voice| + voices[voice.downcase] = voice.strip + end + #else + #regeneratevoicelist() + end else text2speech = false end @@ -274,7 +313,7 @@ if config.obs_connect d = json # Copy *immediately* case d["eventType"].as_s when "CurrentProgramSceneChanged" - say( twitchircipc, config.chat_user.not_nil!.twitch.not_nil!, "| obs: switched scene to " + ( d["eventData"]["sceneName"]?.as_s? || "unknown" ) ) + say_all_self_chan( "| obs: switched scene to " + ( d["eventData"]["sceneName"]?.as_s? || "unknown" ) ) when "MediaInputPlaybackEnded" if d["eventData"]["inputName"].as_s =~ /^media-temporary-/ obs.send( OBS.req( "RemoveInput", JSON.parse({ "inputName" => d["eventData"]["inputName"].as_s }.to_json) ) ) @@ -285,7 +324,7 @@ if config.obs_connect edata = d["eventData"] name = obs.scenes[ edata["sceneName"].as_s ][ edata["sceneItemId"].as_i64 ].name if name !~ /media-temporary/ - say( twitchircipc, config.chat_user.not_nil!.twitch.not_nil!, "| obs: source #{name} visibility is now #{edata["sceneItemEnabled"].as_bool}" ) + say_all_self_chan( "| obs: source #{name} visibility is now #{edata["sceneItemEnabled"].as_bool}" ) end when "SceneItemTransformChanged" edata = d["eventData"] @@ -307,13 +346,13 @@ if config.obs_connect end when "SourceFilterEnableStateChanged" edata = d["eventData"] - say( twitchircipc, config.chat_user.not_nil!.twitch.not_nil!, "| obs: source #{edata["sourceName"].as_s} filter #{edata["filterName"].as_s} visibility is currently #{edata["filterEnabled"].as_bool}" ) + say_all_self_chan( "| obs: source #{edata["sourceName"].as_s} filter #{edata["filterName"].as_s} visibility is currently #{edata["filterEnabled"].as_bool}" ) end end Fiber.yield end rescue ex - puts ex + pp ex ensure waitgroup.send( Fiber.current.name.not_nil! ) end @@ -358,20 +397,21 @@ def urbandef( term : String ) return json["list"][0]["definition"].to_s.gsub( /[\[\]]/, "" ).gsub( /(\r|\n)/, " " ) end +# FIXME: maybe break this out into separate functions later def getvoice( voicelist : String, userdir : String, chatuser : String ) if File.exists?( userdir + "/voice" ) - voice_output = File.read( userdir + "/voice" ).chomp + voice_output = File.read( userdir + "/voice" ).strip voice_setting = voice_output else - voice_output = File.read( voicelist ).chomp.split( "\n" ).sample( 1 )[0].chomp + voice_output = File.read( voicelist ).strip.split( "\n" ).sample( 1 )[0].strip voice_setting = "random" end if File.exists?( userdir + "/voicesub" ) - namesub = File.read( userdir + "/voicesub" ).chomp + namesub = File.read( userdir + "/voicesub" ).strip else namesub = chatuser end - return( [namesub, voice_setting, voice_output ] ) + return( [ namesub, voice_setting, voice_output ] ) end def generatevoicelistaws( ) @@ -389,6 +429,7 @@ def generatevoicelistgcs( gcloud_token : String ) headers["Content-Type"] = "application/json; charset=utf-8" response = HTTP::Client.exec( "GET", "https://texttospeech.googleapis.com/v1/voices?key=#{gcloud_token}", headers, nil, tls: ssl_context ) JSON.parse( response.body )["voices"].as_a.each do | v | + STDERR.puts( "#{v["naturalSampleRateHertz"]} #{v["languageCodes"]} #{v["name"]}" ) voices.push( v["name"].as_s ) end return voices @@ -418,24 +459,43 @@ def generatevoicelistwin() end {% end %} -def regeneratevoicelist( defaultsettings : Hash( String, String ), aws : Bool, gcloud : Bool ) - voices = Hash(String, String).new +macro writevoices() + File.write( config.voice_list.not_nil!, voices.values.sort.join("\r\n") ) + say_all_self_chan( "| Wrote voice_list file." ) +end + +macro regeneratevoicelist() if aws - generatevoicelistaws() do | voice | - voices[ voice.downcase ] = voice + generatevoicelistaws().each do | voice | + voices[ voice.downcase ] = voice.strip end + elsif fibers["BungmoBott::Socket client"]? + bbscliipc.send( "awsvoicelist" ) end - if gcloud - generatevoicelistgcs( secrets.gcloud_token ).each do | voice | - voices[ voice.downcase ] = voice + if secrets.gcloud_token + generatevoicelistgcs( secrets.gcloud_token.not_nil! ).each do | voice | + voices[ voice.downcase ] = voice.strip end + elsif fibers["BungmoBott::Socket client"]? + bbscliipc.send( "gcsvoicelist" ) end {% if flag?(:windows) %} - generatevoicelistwin().each do + generatevoicelistwin().each do | voice | + voices[ voice.downcase ] = voice.strip + end {% end %} + writevoices() +end - File.write( config.configdir + "/voicelist.txt", voices.values.sort.join("\r\n") ) - return voices +def file_list( path : String ) : Array( String ) + if File.file?( path ) + return Array( String ){ File.realpath( path ) } + elsif File.directory?( path ) + dir = Dir.new( path ) + return dir.children.map{ | child | dir.path + child } + else + raise Exception.new("InvalidPath") + end end # TODO: add piping into mpv on POSIX @@ -479,9 +539,9 @@ def userlog( config : BungmoBott::Config, service : String, message : FastIRC::M unless ( ( prefix = message.prefix ) && ( chatuser = prefix.source ) ) return nil end - if service == "twitch" + if service =~ /^twitch/ if ( uid = message.tags["user-id"]? ) - basedir = config.statedir + "/" + service + basedir = config.statedir + "/twitch" userdir = basedir + "/uids/" + uid if File.directory?( userdir ) lastseen = File.info( userdir ).modification_time.to_unix @@ -511,8 +571,8 @@ def userlog( config : BungmoBott::Config, service : String, message : FastIRC::M STDERR.puts( "WARNING: userlog unexpectedly found message.prefix.source with no UID tag." ) return( nil ) end - elsif( service == "gamesurge" ) - basedir = config.statedir + "/" + service + elsif( service =~ /^gamesurge/ ) + basedir = config.statedir + "/gamesurge" userdir = basedir + "/names/" + chatuser if File.directory?( userdir ) lastseen = File.info( userdir ).modification_time.to_unix @@ -528,14 +588,10 @@ def userlog( config : BungmoBott::Config, service : String, message : FastIRC::M end return ( { chatuser, uid, userdir, lastseen, oldname } ) rescue ex - puts ex + pp ex + return nil end -# IRC say -# does this function have a reason to exist? -def say( ipc : Channel( Tuple( String, String ) ), channel : String, text : String ) - ipc.send( { "#" + channel, text } ) -end # Currently only used in flag?(:unix) def t2smsg( config : BungmoBott::Config, msg : String) @@ -546,7 +602,7 @@ def t2smsg( config : BungmoBott::Config, msg : String) sock.close end rescue ex - puts ex + pp ex end def t2s( t2sipc : Channel, config : BungmoBott::Config, userdir : String, chatuser : String, text : String ) @@ -635,7 +691,7 @@ spawn name: "command_dispatch" do chatuser, uid, userdir, lastseen, oldname = userlogreturn # Have we seen this user lately? if ( ( Time.utc.to_unix - lastseen ) >= 14400 ) - if service == "twitch" && uid.is_a?( UInt64 ) + if service =~ /^twitch/ && uid.is_a?( UInt64 ) twitchapiipc.send( { "get_user", uid.to_s } ) twitchapiipc.send( { "get_followers", uid.to_s } ) prevnames = Array( String ).new @@ -658,13 +714,13 @@ spawn name: "command_dispatch" do # channel owner # botowner chanowner = ( chatuser == ircchannel ) - if service == "twitch" + if service =~ /^twitch/ #chanowner = ( message.tags["room-id"] == message.tags["user-id"] ) botowner = ( chatuser == config.chat_user.not_nil!.twitch ) vip = ( message.tags["badges"].to_s.matches?( /(^|,)vip\// ) ) mod = ( message.tags["mod"] == "1" ) sub = ( message.tags["subscriber"] == "1" ) - elsif service == "gamesurge" + elsif service =~ /^gamesurge/ botowner = ( chatuser == config.chat_user.not_nil!.gamesurge ) vip = false # FIXME: Maybe +v? FastIRC lets us see MODE changes, but does not itself track them mod = false # FIXME: Probably +o @@ -689,44 +745,198 @@ spawn name: "command_dispatch" do if Regex.new( commandregex ).match( message.params[1] ) command.each do |exec| # Array if ( - ( chanowner || ( exec.perm && exec.perm.not_nil!.includes?("any") ) ) || - ( botowner && ( exec.perm && exec.perm.not_nil!.includes?("owner") ) ) || - ( mod && ( exec.perm && exec.perm.not_nil!.includes?("mod") ) ) || - ( sub && ( exec.perm && exec.perm.not_nil!.includes?("sub") ) ) || - ( vip && ( exec.perm && exec.perm.not_nil!.includes?("vip") ) ) + ( botowner || ( exec.perm && exec.perm.not_nil!.includes?("any") ) ) || + ( chanowner && ( exec.perm && exec.perm.not_nil!.includes?("owner") ) ) || + ( mod && ( exec.perm && exec.perm.not_nil!.includes?("mod") ) ) || + ( sub && ( exec.perm && exec.perm.not_nil!.includes?("sub") ) ) || + ( vip && ( exec.perm && exec.perm.not_nil!.includes?("vip") ) ) ) # As a matter of policy: # BungmoBott::Socket clients can only say things in their own authenticated channel # Direct IRC clients can say things whereever. # FIXME: this needs to be service-specific - if ( service == "twitch" && exec.func == "detect_rename" && uid.is_a?( UInt64 ) ) + if ( service =~ /^twitch/ && exec.func == "detect_rename" && uid.is_a?( UInt64 ) ) unless oldname.is_a?( String ) - ircipc.send( { "##{config.chat_user.not_nil!.twitch}", "Rename detected: #{uid}: #{oldname} -> #{chatuser}" } ) + say( service, config.chat_user.not_nil!.twitch.not_nil!, "Rename detected: #{uid}: #{oldname} -> #{chatuser}" ) end next end if obs # FIXME: better validate args ( exec.func == "obs_random_source_enable" ) && obsrandommediaenable( obs, exec.arg.not_nil![0].not_nil! ) && next - # ( exec.func == "obs_stats" ) && obs_stats( obs ) + if ( exec.func == "obs_stats_get" ) + puts "Exec-ing obs_stats_get" + stats = obs.stats.to_h + ostatus = obs.outputs["adv_stream"].status.to_h + say( service, ircchannel, "| #{ostatus["outputTimecode"].to_s[0..7]} #{stats["activeFps"].to_s[0,2]}fps Usage: #{stats["cpuUsage"].as(Float64).to_i64}% #{stats["memoryUsage"].as(Float64).to_i64}MiB Frame losses: #{stats["outputSkippedFrames"].as(Int64)} #{stats["renderSkippedFrames"].as(Int64)}" ) + next + end + if ( exec.func == "obs_scene_list_get" ) + puts "Exec-ing obs_scene_list_get" + say( service, ircchannel, "| scenes: #{obs.scenes.to_h.keys.join(" ")}" ) + next + end + if ( exec.func == "obs_scene_get" ) + puts "Exec-ing obs_scene_get" + say( service, ircchannel, "| Current scene: #{obs.scenes.program.name}" ) + next + end + if ( exec.func == "obs_scene_set" ) + puts "Exec-ing obs_scene_set" + + if ( match = / ([a-zA-Z0-9-_ ]+)/.match( message.params[1] ) ) + obs.scenes[match[1]].program! + else + say( service, ircchannel, "| Could not find scene name." ) + end + next + end + if ( exec.func == "obs_input_list_get" ) + puts "Exec-ing obs_input_list_get" + say( service, ircchannel, "| inputs: #{obs.inputs.to_h.keys.join(" ")}" ) + next + end + if ( exec.func == "obs_source_list_get" ) + puts "Exec-ing obs_source_list_get" + say( service, ircchannel, "| current sources: #{obs.scenes.current.metascene.keys.join(" ")}" ) + next + end + if ( exec.func == "obs_source_toggle" ) + puts "Exec-ing obs_source_toggle" + + if ( match = / ([a-zA-Z0-9-_ ]+)/.match( message.params[1] ) ) + obs.scenes.current.metascene[match[1]][0].toggle! + # in studio mode, direct Scene->SceneItem toggles require a transition + obs.scenes.current.preview! + obs.transition! + else + say( service, ircchannel, "| Could not find source name." ) + end + next + end + if ( exec.func == "obs_source_filters_list_get" ) + puts "Exec-ing obs_source_filters_list_get" + if ( match = / ([a-zA-Z0-9-_]+)/.match( message.params[1] ) ) + say( service, ircchannel, "| #{match[1]} filters: #{obs.sources.to_h[match[1]].filters.to_h.keys.join(" ")}" ) + else + say( service, ircchannel, "| Could not match source name." ) + end + next + end + if ( exec.func == "obs_source_filter_toggle" ) + puts "Exec-ing obs_source_filter_toggle" + if ( match = / ([a-zA-Z0-9-_]+) ([a-zA-Z0-9-_]+)/.match( message.params[1] ) ) + obs.sources.to_h[match[1]].filters[match[2]].toggle! + else + say( service, ircchannel, "| Could not match source and filter name to toggle." ) + end + next + end + if ( exec.func == "obs_create_ephemeral_media_sources_from_path" ) + puts "Exec-ing obs_create_ephemeral_media_sources_from_path" + count = 1 + sources = file_list( exec.arg.not_nil![1].not_nil! ) + if ( match = / ([0-9]+)/.match( message.params[1] ) ) + count = match[1].to_i + if ( ( count < 1 ) || ( count > sources.size ) ) + say( service, ircchannel, "| Ephemeral OBS source count must be between 1 and #{sources.size}" ) + next + end + end + sources.sample( count ).each do |path| + obstemporarymediacreate( obs, exec.arg.not_nil![0].not_nil!, path.gsub(/[\/ ]/, '_').downcase, path ) + end + next + end + + end if text2speech if ( exec.func == "text_to_speech" ) puts "Exec-ing text_to_speech" if ( t2sreturn = t2s( t2sipc, config, userdir, chatuser, message.params[1] ) ) - lastvoice.insert( 0, t2sreturn ) + lastvoice.insert( 0, t2sreturn.strip ) lastvoice = lastvoice[0..4] end next end + if ( exec.func == "tts_voice_list_generate" ) + puts "Exec-ing " + exec.func + regeneratevoicelist() + say( service, ircchannel, "| Regenerated voicelist." ) + next + end + if ( exec.func == "tts_last_voice" ) + unless lastvoice.empty? + say( service, ircchannel, "| Last voices were " + lastvoice.join( ", " ) ) + else + say( service, ircchannel, "| No voices used so far." ) + end + next + end + if ( exec.func == "tts_voice_get" ) + puts "Exec-ing tts_voice_get" + namesub, voice_setting, voice_output = getvoice( config.voice_list.not_nil!, userdir, chatuser ) + say( service, config.chat_user.not_nil!.twitch.not_nil!, "| Current voice is #{voice_setting}" ) + next + end + if ( exec.func == "tts_voice_set" ) + puts "Exec-ing tts_voice_set" + if ( match = / ([a-zA-Z0-9-_]+)/.match( message.params[1] ) ) + voice = match[1].downcase + if voice =~ /disabled|null|disable|none|random/ + if File.exists?( userdir + "/voice" ) + File.delete( userdir + "/voice" ) + say( service, ircchannel, "| Voice for #{chatuser} is now random." ) + else + say( service, ircchannel, "| Voice for #{chatuser} is already random." ) + end + elsif voices.has_key?( voice.downcase ) + csvoice = voices[voice] + Dir.mkdir_p( userdir ) + File.write( userdir + "/voice", csvoice ) + pp userdir + say( service, ircchannel, "| Voice for #{chatuser} is now #{File.read( userdir + "/voice" ).strip}." ) + else + pp ( match ) + say( service, ircchannel, "| Invalid voice. To see list, use !voices" ) + end + else + say( service, ircchannel, "| tts_voice_set failed generic string match ") + end + next + end + if ( exec.func == "tts_name_get" ) + puts "Exec-ing " + exec.func + if File.exists?( userdir + "/voicesub" ) + say( service, ircchannel, "| Current name substitution is \"#{File.read( userdir + "/voicesub" ).strip}\"." ) + else + say( service, ircchannel, "| Current name substitution is disabled." ) + end + end + if ( exec.func == "tts_name_set" ) + puts "Exec-ing tts_name_set" + if ( match = / ([\sa-zA-Z0-9-]+)$/.match( message.params[1] ) ) + pp match[1] + voicesub = match[1].downcase + if voicesub =~ /^(disabled|null|disable|none)$/ + if File.exists?( userdir + "/voicesub" ) + File.delete( userdir + "/voicesub" ) + say( service, ircchannel, "| Name substitution for #{chatuser} is now disabled." ) + else + say( service, ircchannel, "| Name substitution for #{chatuser} is already disabled." ) + end + else + Dir.mkdir_p( userdir ) + File.write( userdir + "/voicesub", voicesub ) + say( service, ircchannel, "| Name substitution for #{chatuser} is now \"#{File.read( userdir + "/voicesub" ).strip}\"." ) + end + end + next + end end - # FIXME: add gamesurge support if ( exec.func == "say" ) - if ( service == "twitch" ) - say( twitchircipc, config.chat_user.not_nil!.twitch.not_nil!, exec.arg.not_nil![0].not_nil! ) - elsif ( service == "gamesurge" ) - say( ircipc, config.chat_user.not_nil!.gamesurge.not_nil!, exec.arg.not_nil![0].not_nil! ) - end + say( service, config.chat_user.not_nil!.twitch.not_nil!, exec.arg.not_nil![0].not_nil! ) next end if ( exec.func == "run" ) @@ -754,6 +964,7 @@ spawn name: "command_dispatch" do else STDERR.puts( "WARNING: exec.func \"run_shell\" called without argument" ) end + next end STDERR.puts "WARNING: unhandled function for /#{commandregex}/: #{exec.func}" else @@ -772,139 +983,17 @@ spawn name: "command_dispatch" do 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] -# if ( ( cmd =~ /^(substitute|voicesub)$/ ) && ( mod || sub ) ) -# case message.params[1].split( " " ).size -# when 1 -# if File.exists?( userdir + "/voicesub" ) -# bot.message( "##{settings["channel"]}", "| Current name substitution is \"#{File.read( userdir + "/voicesub" )}\"." ) -# else -# bot.message( "##{settings["channel"]}", "| Current name substitution is disabled." ) -# end -# else -# if match[2]? -# voicesub = match[2].downcase -# if voicesub =~ /^(disabled|null|disable|none)$/ -# if File.exists?( userdir + "/voicesub" ) -# File.delete( userdir + "/voicesub" ) -# bot.message( "##{settings["channel"]}", "| Name substitution for #{chatuser} is now disabled." ) -# else -# bot.message( "##{settings["channel"]}", "| Name substitution for #{chatuser} is already disabled." ) -# end -# else -# Dir.mkdir_p( userdir ) -# File.write( userdir + "/voicesub", voicesub ) -# bot.message( "##{settings["channel"]}", "| Name substitution for #{chatuser} is now \"#{File.read( userdir + "/voicesub" )}\"." ) -# end -# end -# end -# elsif ( ( cmd == "lastvoice" ) && ( mod || sub ) ) -# unless lastvoice.empty? -# bot.message( "##{settings["channel"]}", "| Last voices were " + lastvoice.join( ", " ) ) -# else -# bot.message( "##{settings["channel"]}", "| No voices used so far." ) -# end -# elsif ( ( cmd == "regeneratevoicelist" ) && ( own ) ) -# voices = regeneratevoicelist( settings, aws, gcloud ) -# ircipc.send( { "##{settings["channel"]}", "| Regenerated voicelist." } ) -# elsif ( ( cmd =~ /^(setvoice|voice)$/ ) && ( mod || sub ) ) -# case message.params[1].split( " " ).size -# when 1 -# namesub, voice_setting, voice_output = getvoice( config.voice_list_file, userdir, chatuser ) -# bot.message( "##{settings["channel"]}", "| 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( "##{settings["channel"]}", "| Voice for #{chatuser} is now random." ) -# else -# bot.message( "##{settings["channel"]}", "| Voice for #{chatuser} is already random." ) -# end -# elsif voices.has_key?( voice ) -# csvoice = voices[voice] -# Dir.mkdir_p( userdir ) -# File.write( userdir + "/voice", csvoice ) -# pp userdir -# bot.message( "##{settings["channel"]}", "| Voice for #{chatuser} is now #{File.read( userdir + "/voice" )}." ) -# else -# pp ( match ) -# bot.message( "##{settings["channel"]}", "| Invalid voice. To see list, use !voices" ) -# end -# end -# when 3 -# else -# end -# elsif ( cmd =~ /^uptime$/ ) -# stats = obs.stats.to_h -# ostatus = obs.outputs["adv_stream"].status.to_h -# ircipc.send( { "##{settings["channel"]}", "| #{ostatus["outputTimecode"].to_s[0..7]} #{stats["activeFps"].to_s[0,2]}fps Usage: #{stats["cpuUsage"].as(Float64).to_i64}% #{stats["memoryUsage"].as(Float64).to_i64}MiB Frame losses: #{stats["outputSkippedFrames"].as(Int64)} #{stats["renderSkippedFrames"].as(Int64)}" } ) -# # !newimage [name] [url] (random|top|bottom|left|right|center) # # download url, verify mimetype with libmagic, duplicate existing medialoop source, change "file" SourceSetting # elsif ( ( cmd =~ /^inputsetting(|s)$/ ) && ( mod || own || vip ) ) # if ( match[2]? && match[2] =~ /^[a-zA-Z0-9-_]+$/ ) # ircipc.send( { "##{settings["channel"]}", "| #{obs.inputs[match[2]].settings.to_h.to_s} " } ) # end -# elsif ( ( cmd =~ /^input(|s)$/ ) && ( mod || own || vip ) ) -# ircipc.send( { "##{settings["channel"]}", "| inputs: #{obs.inputs.to_h.keys.join(" ")}" } ) -# elsif ( ( cmd =~ /^scene(|s)$/ ) && ( mod || own || vip ) ) -# if ( match[2]? && match[2] =~ /^[a-zA-Z0-9-_]+$/ ) -# obs.scenes[match[2]].program! -# else -# ircipc.send( { "##{settings["channel"]}", "| scenes: #{obs.scenes.to_h.keys.join(" ")}" } ) -# end -# elsif ( ( cmd =~ /^source(|s)$/ ) && ( mod || own || vip ) ) -# request = Hash( String, String | Bool ).new -# if ( match[2]? && match[2] =~ /^[a-zA-Z0-9-_]+$/ ) -# obs.scenes.current.metascene[match[2]][0].toggle! -# # in studio mode, direct Scene->SceneItem toggles require a transition -# obs.scenes.current.preview! -# obs.transition! -# else -# ircipc.send( { "##{settings["channel"]}", "| current sources: #{obs.scenes.current.metascene.keys.join(" ")}" } ) -# end -# elsif ( ( cmd =~ /^filter(|s)$/ ) && ( mod || own ) ) -# if ( match[2]? ) && ( filterargs = match[2].match( /^([a-zA-Z0-9-_]+) +([a-zA-Z0-9-_]+)/ ) ) -# obs.sources.to_h[filterargs[1]].filters[filterargs[2]].toggle! -# elsif ( match[2]? && match[2] =~ /^[a-zA-Z0-9-_]+$/ ) -# ircipc.send( { "##{settings["channel"]}", "| #{match[2]} filters: #{obs.sources.to_h[match[2]].filters.to_h.keys.join(" ")}" } ) -# else -# ircipc.send( { "##{settings["channel"]}", "Must provide at least one source name as argument, and optionally one filter name to toggle on or off." } ) -# end # elsif ( cmd == "create" && ( mod || own || vip ) ) # if ( match[2]? ) && ( match[2] =~ /^([\/a-zA-Z0-9-_]+)/ ) # obstemporarymediacreate( obs, "meta-foreground", match[2], "C:/cygwin64/home/user/effects/#{match[2]}.webm" ) # else # ircipc.send( { "##{settings["channel"]}", "Must provide at least one source name as argument." } ) # end -# elsif ( cmd =~ /^(metaminute|youdied)$/ ) -# obsrandommediaenable( obs, "media-#{cmd}" ) -# elsif ( cmd =~ /^fart(s|)$/ ) && ( sub || mod || own || vip ) -# if effects.values.select( /^farts/ ).size > 0 -# if ( match[2]? ) && ( match[2].match( /^([0-9]+)/ ) && ( match[2].to_i > 1 ) && ( match[2].to_i <= 32 ) ) -# count = match[2].to_i -# else -# count = 1 -# end -# effects.values.select( /^farts/ ).sample( count ).each do | effect | -# obstemporarymediacreate( obs, "meta-foreground", effect.gsub(/[\/ ]/, '_').downcase, "C:/cygwin64/home/user/effects/#{effect}" ) -# end -# else -# puts "farts undefined" -# end -# elsif ( cmd =~ /^explosion(s|)$/ ) && ( sub || mod || own || vip ) -# if effects.values.select( /^explosions/ ).size > 0 -# if ( match[2]? ) && ( match[2].match( /^([0-9]+)/ ) && ( match[2].to_i > 1 ) && ( match[2].to_i <= 32 ) ) -# count = match[2].to_i -# else -# count = 1 -# end -# effects.values.select( /^explosions/ ).sample( count ).each do | effect | -# obstemporarymediacreate( obs, "meta-foreground", effect.gsub(/[\/ ]/, '_').downcase, "C:/cygwin64/home/user/effects/#{effect}" ) -# end -# else -# puts "explosions undefined" -# end # # FIXME: This is only half-implemented # elsif ( ( cmd =~ /^(user)$/ ) && ( mod || own ) ) # if match[2]? && match[2] =~ /[a-z][a-z0-9_]+/ @@ -942,63 +1031,6 @@ spawn name: "command_dispatch" do # else # ircipc.send( { "##{settings["channel"]}", "| Urban Dictionary search term should consist of letters, numbers, spaces, and/or hyphens." } ) # end -# elsif ( cmd == "matrix" ) -# effectsmsg( settings, "overlay glmatrix" ) -# elsif ( cmd =~ /juggle|juggler/ ) -# effectsmsg( settings, "overlay juggler3d" ) -# elsif ( cmd =~ /fireworks|firework/ ) -# effectsmsg( settings, "overlay fireworkx" ) -# elsif ( cmd == "pipes" ) -# if match[2]? && match[2] =~ /^(fast|faster)$/ -# effectsmsg( settings, "overlay pipes " + match[2] ) -# else -# effectsmsg( settings, "overlay pipes" ) -# end -# elsif ( cmd == "jellyfish" ) -# effectsmsg( settings, "overlay hydrostat" ) -# elsif ( cmd == "gluten" ) -# effectsmsg( settings, "overlay flyingtoasters" ) -# elsif ( cmd =~ /^(glsnake|glmatrix|gibson|xmatrix|flyingtoasters|moebiusgears|fireworkx|hydrostat|hypertorus|jigsaw|juggler3d|kaleidocycle|kumppa|molecule|noof|polyhedra)$/ ) -# effectsmsg( settings, "overlay " + cmd ) -# elsif ( cmd =~ /gltext|cowsay|xcowsay|cowfuscious/ ) -# ( cmd == "cowfuscious" ) && ( cmd = "xcowsay" ) -# if ( match[2]? ) && ( gltextargs = match[2].match( /^([0-9]+) +(.+)$/ ) ) -# seconds=UInt64.new( 1 ) -# seconds=gltextargs[1].to_u64 -# if ( own || mod || vip ) -# effectsmsg( settings, "overlay #{cmd} #{match[2]}" ) -# puts "matched #{cmd} #{match[2]}" -# elsif ( sub ) -# if ( seconds > 20 ) -# seconds=20 -# end -# effectsmsg( settings, "overlay #{cmd} #{seconds} #{gltextargs[2]}" ) -# puts "matched #{cmd} #{seconds} #{gltextargs[2]}" -# else -# if ( seconds > 5 ) -# seconds=5 -# end -# effectsmsg( settings, "overlay #{cmd} #{seconds} #{gltextargs[2]}" ) -# puts "matched #{cmd} #{seconds} #{gltextargs[2]}" -# end -# elsif match[2]? && match[2] =~ /^.+$/ -# effectsmsg( settings, "overlay #{cmd} #{match[2]}" ) -# puts "matched #{cmd} #{match[2]}" -# else -# effectsmsg( settings, "overlay #{cmd}" ) -# puts "failed to match gltext" -# end -# elsif ( cmd =~ /^(cow(s|)|cowabunga|holycow$)/ ) -# if match[2]? && match[2] =~ /^([0-9])+$/ -# effectsmsg( settings, "overlay bouncingcow #{match[2]}" ) -# else -# effectsmsg( settings, "overlay bouncingcow" ) -# end -# elsif ( cmd == "overlay" ) -# if match[2]? && match[2] =~ /^[a-z]+$/ -# ircipc.send( { "##{settings["channel"]}", "| overlay requires an argument consisting wholly of lower case characters."} ) -# effectsmsg( settings, "overlay #{match[2]}" ) -# end # elsif ( cmd =~ /^(shout|shoutout)$/ ) # if match[2]? && match[2] =~ /^[a-z]+$/ # ircipc.send( { "##{settings["channel"]}", "| Go check out twitch.tv/#{match[2]}"} ) @@ -1006,10 +1038,6 @@ spawn name: "command_dispatch" do # else # ircipc.send( { "##{settings["channel"]}", "| Missing argument."} ) # end -# elsif ( cmd =~ /^(system|dxdiag|computer)$/ ) && ( Crystal::DESCRIPTION =~ /linux/ ) -# ircipc.send( { "##{settings["channel"]}", "| https://bungmonkey.omgwallhack.org/tmp/DxDiag.txt" } ) -# elsif ( cmd == "hackerman" ) -# ircipc.send( { "##{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=/ ) ) @@ -1086,17 +1114,10 @@ spawn name: "command_dispatch" do # end # end rescue ex - puts ex + pp ex puts ex.backtrace - if service == "twitch" - say( twitchircipc, config.chat_user.not_nil!.twitch.not_nil!, "An error occurred! " + ex.message.to_s ) - elsif service == "gamesurge" - say( ircipc, config.chat_user.not_nil!.gamesurge.not_nil!, "An error occurred! " + ex.message.to_s ) - else - puts "...also, the chat service is undefined." - end + say_all_self_chan( "An error occurred! " + ex.message.to_s ) end - end end ensure @@ -1119,7 +1140,6 @@ def ttsgcs( languagecode : String, voice : String, text : String, gcskey : Strin headers = HTTP::Headers.new headers["Content-Type"] = "application/json; charset=utf-8" - response = HTTP::Client.exec( "POST", "https://texttospeech.googleapis.com/v1/text:synthesize?key=#{gcskey}", headers, request.to_json, tls: ssl_context ) json=JSON.parse(response.body) @@ -1198,7 +1218,7 @@ spawn name: "text2speech" do end end rescue ex - puts ex + pp ex end end ensure @@ -1322,7 +1342,7 @@ if ( secrets.twitch_access_token && config.chat_user.not_nil!.twitch && config.j pp message pp message.params rescue ex - puts ex + pp ex #twitchircipc.send( { "##{config.channel}", "An error occurred! " + ex.message.to_s } ) # Maybe send all error messages out through the API? Have to do channel->client mappings, though. end @@ -1441,7 +1461,7 @@ if ( secrets.gamesurge_password && config.chat_user.not_nil!.gamesurge && config # ircipc.send( { "##{settings["channel"]}", "| https://bungmonkey.omgwallhack.org/tmp/DxDiag.txt" } ) # end rescue ex - puts ex + pp ex ircipc.send( { "##{config.chat_user.not_nil!.gamesurge.not_nil!}", "An error occurred! " + ex.message.to_s } ) end end @@ -1490,7 +1510,7 @@ if config.bungmobott_connect negotiated = true #ssl_socket.puts( "say twitch #{user} test" ) elsif ( match = message.match( /^msg (twitch|gamesurge)/ ) ) - commandircipc.send( { match[1], FastIRC.parse_line( message.split(" ")[2..].join(" ") ) } ) + commandircipc.send( { "#{match[1]}_remote", FastIRC.parse_line( message.split(" ")[2..].join(" ") ) } ) elsif ( match = message.match( /^awst2s ([0-9]+)/ ) ) datasize = match[1].to_u32 audiodata = Bytes.new( datasize ) @@ -1501,6 +1521,16 @@ if config.bungmobott_connect audiodata = Bytes.new( datasize ) ssl_socket.fill_read( audiodata ) playaudiodata( config.tempdir, audiodata ) + elsif ( match = message.match( /^awsvoicelist (.+)$/ ) ) + match[1].split(" ").each do | voice | + voices[voice.downcase] = voice + end + writevoices() + elsif ( match = message.match( /^gcsvoicelist (.+)$/ ) ) + match[1].split(" ").each do | voice | + voices[voice.downcase] = voice + end + writevoices() end end end @@ -1603,7 +1633,7 @@ if config.bungmobott_listen if ( gcloud_token = secrets.gcloud_token ).is_a?( String ) if ( match = message.match( /^gcsvoicelist$/i ) ) client.puts "gcsvoicelist " + generatevoicelistgcs( ( gcloud_token ) ).join(" ") - elsif ( match = message.match( /^gcst2s (([a-zA-Z]{2,3}-[a-zA-Z]{2})[a-zA-Z-]+) (.+)/i ) ) + elsif ( match = message.match( /^gcst2s (([a-zA-Z]{2,3}-[a-zA-Z]{2})[a-zA-Z0-9-]+) (.+)/i ) ) mp3data = ttsgcs( match[2], match[1], match[3], gcloud_token ) client.puts "gcst2s #{mp3data.size}" STDOUT.puts "SENT: gcst2s #{mp3data.size}" @@ -1613,8 +1643,8 @@ if config.bungmobott_listen else client.puts "ERROR: gcloud_token missing, gcs commands disabled." end - elsif ( match = message.match( /^say twitch (.+)/i ) ) - say( twitchircipc, connections[client]["user"], match[1] ) + elsif ( match = message.match( /^say (twitch|gamesurge) (.+)/i ) ) + say( match[1], connections[client]["user"], match[2] ) elsif ( message =~ /testchannelsubs/ ) #commandircipc.send( "testchannelsubs" ) end @@ -1646,10 +1676,10 @@ if config.bungmobott_listen end end rescue ex : IO::Error - puts ex + pp ex next rescue ex - puts ex + pp ex next ensure connections.delete( client ) @@ -1660,7 +1690,7 @@ if config.bungmobott_listen end end rescue ex - puts ex + pp ex ensure waitgroup.send( Fiber.current.name.not_nil! ) end |