summaryrefslogtreecommitdiff
path: root/crystal
diff options
context:
space:
mode:
authorJoe Rayhawk <jrayhawk+git@omgwallhack.org>2022-09-13 18:52:27 -0700
committerJoe Rayhawk <jrayhawk+git@omgwallhack.org>2022-09-13 18:52:27 -0700
commit89431aa34a8c01099dda72ca1f46b8c7ebcbb74d (patch)
tree922e858b40a93626bef15ddee568601e35feac89 /crystal
parent281e3eb8d79205b39b6cd8c6697c020587f5391f (diff)
downloadtwitchtools-89431aa34a8c01099dda72ca1f46b8c7ebcbb74d.tar.gz
twitchtools-89431aa34a8c01099dda72ca1f46b8c7ebcbb74d.zip
bungmobott: add transient OBS media source logic
Diffstat (limited to 'crystal')
-rwxr-xr-xcrystal/bungmobott.cr208
1 files changed, 159 insertions, 49 deletions
diff --git a/crystal/bungmobott.cr b/crystal/bungmobott.cr
index bbaac50..124d983 100755
--- a/crystal/bungmobott.cr
+++ b/crystal/bungmobott.cr
@@ -66,6 +66,14 @@ else
aws = true
end
+# enable explosions?
+if File.exists?( settings["configdir"] + "/explosionlist.txt" )
+ explosions = Hash(String, String).new
+ File.each_line( settings["configdir"] + "/explosionlist.txt" ) do |line|
+ explosions[ line.downcase ] = line
+ end
+end
+
client = Twitcr::Client.new( settings )
# derive channel_id from channel or vice versa
@@ -137,7 +145,7 @@ snifflast = Int64.new(0)
def urbandef( term : String )
#http://api.urbandictionary.com/v0/define?term=waifu
client = HTTP::Client.new( "api.urbandictionary.com" )
- response = client.exec( "GET", "/v0/define?term=" + URI.encode_www_form( term ) )
+ response = client.exec( "GET", "/v0/define?term=pawg" + URI.encode_www_form( term ) )
puts response.status_code
json = JSON.parse( response.body )
return json["list"][0]["definition"].to_s.gsub( /[\[\]]/, "" ).gsub( /\n/, "" )
@@ -384,25 +392,6 @@ macro genrefuser2uid( path, uid, depth )
{% end %}
end
-macro temporaryduplicate( item )
- request = Hash( String, String | Bool ){
- "request-type" => "SetSceneItemProperties",
- "message-id" => "SetSceneItemProperties",
- "scene-name" => "meta",
- "item" => {{item}},
- "visible" => true,
- }
- obsipc.send( request.to_json )
- request = Hash( String, String | Hash( String, String ) ){
- "request-type" => "DuplicateSceneItem",
- "message-id" => "DuplicateSceneItem",
- "item" => { "name" => {{item}} },
- "fromScene" => "meta"
- }
- ircipc.send( { "##{settings["channel"]}", "Duplicating source #{match[2]}" } )
- obsipc.send( request.to_json )
-end
-
voices = Hash(String, String).new
if File.exists?( settings["configdir"] + "/voicelist.txt" )
File.each_line( settings["configdir"] + "/voicelist.txt" ) do |line|
@@ -500,28 +489,29 @@ spawn do
# Outgoing
# state
currentscene = "unknown"
+ canvas = { 1280, 720 }
# scenename => { sourcename => { property => value }
scenes = Hash( String, Hash( String, Hash( String, Int64 | Float64 | Bool | String ) ) ).new
spawn do
while msg = obsipc.receive
pp msg
- if ( match = msg.match( /^\x10source ([a-z0-9-_]+)/ ) )
- source = match[1]
+ if ( match = msg.match( /^\x10source(-notransition|) ([a-z0-9-_]+)/ ) )
+ transition = ( match[1] != "-notransition" )
+ source = match[2]
sources = reversesource( scenes, currentscene )
pp sources
request = Hash( String, String | Bool ).new
request["request-type"] = "SetSceneItemProperties"
request["message-id"] = "SetSceneItemProperties"
- #request["request-type"] = "GetSceneItemProperties"
unless ( scene = sources[source]? )
ircipc.send( { "#" + settings["channel"], "| obs: source not found: #{source}" } )
next
end
request["scene-name"] = scene
request["item"] = source
- if ( match = msg.match( /^\x10source ([a-z0-9-_]+) (true|false)/ ) )
+ if ( match = msg.match( /^\x10source[-a-zA-Z0-9_]* ([a-z0-9-_]+) (true|false)/ ) )
request["visible"] = ( match[2] == "true" )
- elsif ( match = msg.match( /^\x10source ([a-z0-9-_]+)/ ) )
+ elsif ( match = msg.match( /^\x10source[-a-zA-Z0-9_]* ([a-z0-9-_]+)/ ) )
pp scenes[scene][source]["render"]
request["visible"] = ( scenes[scene][source]["render"]? != true )
# use a random similarly named source instead?
@@ -537,24 +527,61 @@ spawn do
end
end
obs_pubsub.send( request.to_json )
- #if scene == currentscene
+ puts "x10source: currentscene is " + currentscene
+ if transition
request = Hash( String, String | Bool ).new
request["request-type"] = "SetPreviewScene"
request["message-id"] = "SetPreviewScene"
request["scene-name"] = currentscene
obs_pubsub.send( request.to_json )
+ sleep 0.1 # somehow these get out-of-order otherwise? >:(
request = Hash( String, String ).new
request["request-type"] = "TransitionToProgram"
request["message-id"] = "TransitionToProgram"
obs_pubsub.send( request.to_json )
-
- #end
+ end
+ elsif ( match = msg.match( /^\x10sceneitem ([a-z0-9-_]+)/ ) )
+ source = match[1]
+ sources = reversesource( scenes, currentscene )
+ pp sources
+ request = Hash( String, String | Bool ).new
+ request["request-type"] = "GetSceneItemProperties"
+ request["message-id"] = "GetSceneItemProperties"
+ unless ( scene = sources[source]? )
+ ircipc.send( { "#" + settings["channel"], "| obs: source not found: #{source}" } )
+ next
+ end
+ request["scene-name"] = scene
+ request["item"] = source
+ obs_pubsub.send( request.to_json )
elsif ( match = msg.match( /^\x10source/ ) )
request = Hash( String, String ){
"request-type" => "GetCurrentScene",
"message-id" => "GetCurrentScene",
}
obs_pubsub.send( request.to_json )
+ elsif ( match = msg.match( /^\x10newsource(-temporary|) ([a-zA-Z0-9-_]+)/ ) )
+ temporary = ( match[1]? )
+
+ sourcename = "media#{temporary}-#{match[2]}"
+
+ request = Hash( String, String | Hash( String, String | Bool ) ){
+ "request-type" => "CreateSource",
+ "message-id" => "CreateSource",
+ "sourceName" => sourcename,
+ "sourceKind" => "ffmpeg_source",
+ "sceneName" => "meta-foreground",
+ #"setVisible" => "false", # this somehow triggers one or more MediaEnded events?
+ "sourceSettings" => {
+ "clear_on_media_end"=> true,
+ "close_when_inactive"=> false,
+ "is_local_file"=> true,
+ "local_file"=> "c:/cygwin64/home/user/effects/explosions/#{match[2]}.webm",
+ "looping"=> false
+ },
+ }
+ ircipc.send( { "##{settings["channel"]}", "Creating #{sourcename}" } )
+ obs_pubsub.send( request.to_json )
else
obs_pubsub.send( msg )
end
@@ -574,14 +601,15 @@ spawn do
next # so skip them
when "SwitchScenes"
ircipc.send( { "#" + settings["channel"], "| obs: switched scene to " + ( json["scene-name"]?.as_s? || "unknown" ) } )
+ puts "SwitchScenes: currentscene is " + currentscene
currentscene = json["scene-name"].as_s
- puts "currentscene is " + currentscene
+ puts "SwitchScenes: currentscene is " + currentscene
when "MediaEnded"
pp json
source = json["sourceName"].as_s
sources = reversesource( scenes, currentscene )
if scene = sources[source]?
- if ( scenes[scene][source]["render"] == true ) && ( source =~ /^media-/ )
+ if ( scenes[scene][source]["render"] == true ) && ( source =~ /^media-/ ) && ( source !~ /media-temporary/ )
request = Hash( String, String | Bool ){
"request-type" => "SetSceneItemProperties",
"message-id" => "SetSceneItemProperties",
@@ -593,18 +621,39 @@ spawn do
obsipc.send( request.to_json )
end
end
-# If we want to start deleting temporary media sources?
-# if ( ( source =~ /^media-/ ) && ( scene !~ /^meta/ ) )
-# request = Hash( String, String | Hash( String, String ) ){
-# "request-type" => "DeleteSceneItem",
-# "message-id" => "DeleteSceneItem",
-# "item" => { "name" => json["sourceName"].as_s },
-# }
-# ircipc.send( { "##{settings["channel"]}", "Deleting #{source}" } )
-# obsipc.send( request.to_json )
-# end
+ if ( source =~ /^media-temporary-/ )
+ request = Hash( String, String | Hash( String, String ) ){
+ "scene" => "meta-foreground",
+ "request-type" => "DeleteSceneItem",
+ "message-id" => "DeleteSceneItem",
+ "item" => { "name" => json["sourceName"].as_s },
+ }
+ ircipc.send( { "##{settings["channel"]}", "Deleting #{source}" } )
+ obsipc.send( request.to_json )
+ end
when "SourceFilterVisibilityChanged"
ircipc.send( { "##{settings["channel"]}", "| obs: source #{json["sourceName"]} filter #{json["filterName"]} visibility is currently #{json["filterEnabled"]}" } )
+ when "SceneItemAdded"
+ request = Hash( String, String ){
+ "request-type" => "GetCurrentScene",
+ "message-id" => "GetCurrentScene Quietly",
+ }
+ obsipc.send( request.to_json)
+ request = Hash( String, String ){
+ "request-type" => "GetSceneList",
+ "message-id" => "GetSceneList Quietly",
+ }
+ obsipc.send( request.to_json )
+
+ if json["item-name"].as_s =~ /media-temporary/
+ request = Hash( String, String | Bool ).new
+ request["request-type"] = "GetSceneItemProperties"
+ request["message-id"] = "GetSceneItemProperties"
+ request["scene-name"] = json["scene-name"].as_s
+ request["item"] = json["item-name"].as_s
+ obs_pubsub.send( request.to_json )
+ end
+ #scenes[json["scene-name"].as_s][json["item-name"].as_s]
when "SourceRenamed"
sourceprev = json["previousName"].as_s
sourcenew = json["newName"].as_s
@@ -617,15 +666,41 @@ spawn do
end
}
when "SceneItemVisibilityChanged"
- scenes[json["scene-name"].as_s][json["item-name"].as_s]["render"] = json["item-visible"].as_bool
- ircipc.send( { "##{settings["channel"]}", "| obs: source #{json["item-name"]} visibility is now #{json["item-visible"].as_bool}" } )
+ print( json.to_pretty_json )
+ if json["item-name"].as_s !~ /media-temporary/
+ scenes[json["scene-name"].as_s][json["item-name"].as_s]["render"] = json["item-visible"].as_bool
+ ircipc.send( { "##{settings["channel"]}", "| obs: source #{json["item-name"]} visibility is now #{json["item-visible"].as_bool}" } )
+ end
end
print( "RECEIVED: ")
print( json.to_pretty_json )
print( "\n")
case json["message-id"]?.as_s?
+ when "GetSceneItemProperties"
+ if json["name"].as_s =~ /media-temporary/
+ size = { json["sourceWidth"].as_i, json["sourceHeight"].as_i }
+ request = Hash( String, String | Bool | Hash( String, Float64) ).new
+ request["request-type"] = "SetSceneItemProperties"
+ request["message-id"] = "SetSceneItemProperties"
+ request["scene-name"] = "meta-foreground" # why isn't this returned?
+ request["item"] = json["name"].as_s
+ # FIXME we're not getting real values for sourceWidth and sourceHeight until playback begins, but dropping frames would be horrible.
+ # We should probably just preprocess these values into explosionslist :/
+ # ffprobe -show_entries stream=width,height big_explosion_1.webm
+ request["position"] = {
+ "x" => (rand(canvas[0]) - ( size[0] / 2 ) ).to_f ,
+ "y" => (rand(canvas[1]) - ( size[1] / 2 ) ).to_f,
+ }
+ obsipc.send( request.to_json )
+ #ircipc.send( { "##{settings["channel"]}", "| obs: #{request.to_json}" } )
+
+ end
+ when /^GetVideoInfo/
+ canvas = { json["baseWidth"].as_i64, json["baseHeight"].as_i64 }
+ pp canvas
when /^GetCurrentScene/
currentscene = json["name"].as_s
+ puts "GetCurrentScene: currentscene is " + currentscene
if json["message-id"].as_s? !~ /Quietly/
sources = reversesource( scenes, currentscene )
ircipc.send( { "#" + settings["channel"], "| obs: " + sources.keys.join(", ") } )
@@ -644,10 +719,18 @@ spawn do
source["type"].as_s? && ( scenes[scene["name"].as_s][source["name"].as_s]["type"] = source["type"].as_s )
source["x"].as_f? && ( scenes[scene["name"].as_s][source["name"].as_s]["x"] = source["x"].as_f )
source["y"].as_f? && ( scenes[scene["name"].as_s][source["name"].as_s]["y"] = source["y"].as_f )
- source["cx"].as_i64? && ( scenes[scene["name"].as_s][source["name"].as_s]["cx"] = source["cx"].as_i64 )
- source["cy"].as_i64? && ( scenes[scene["name"].as_s][source["name"].as_s]["cy"] = source["cy"].as_i64 )
source["source_cx"].as_f? && ( scenes[scene["name"].as_s][source["name"].as_s]["source_cx"] = source["source_cx"].as_f )
source["source_cy"].as_f? && ( scenes[scene["name"].as_s][source["name"].as_s]["source_cy"] = source["source_cy"].as_f )
+ if source["cx"].as_i64?
+ scenes[scene["name"].as_s][source["name"].as_s]["cx"] = source["cx"].as_i64
+ else
+ scenes[scene["name"].as_s][source["name"].as_s]["cx"] = 0.to_i64
+ end
+ if source["cy"].as_i64?
+ scenes[scene["name"].as_s][source["name"].as_s]["cy"] = source["cy"].as_i64
+ else
+ scenes[scene["name"].as_s][source["name"].as_s]["cy"] = 0.to_i64
+ end
}
}
pp scenes
@@ -666,6 +749,12 @@ spawn do
"message-id" => "GetSceneList Quietly",
}
obsipc.send( request.to_json)
+ sleep 1
+ request = Hash( String, String ){
+ "request-type" => "GetVideoInfo",
+ "message-id" => "GetVideoInfo Quietly",
+ }
+ obsipc.send( request.to_json)
end
obs_pubsub.run
rescue ex : Socket::ConnectError
@@ -777,7 +866,7 @@ loop do
elsif ( ( cmd == "regeneratevoicelist" ) && ( own ) )
voices = regeneratevoicelist( settings, aws, gcloud )
ircipc.send( { "##{settings["channel"]}", "| Regenerated voicelist." } )
- elsif ( ( cmd =~ /^(voices|voicelist)$/ ) && ( mod || sub ) )
+ elsif ( ( cmd =~ /^(voices|voice(s|)list|listvoice(s|))$/ ) && ( mod || sub ) )
bot.message( "##{settings["channel"]}", "| https://bungmonkey.omgwallhack.org/voicelist.txt" )
elsif ( ( cmd =~ /^(setvoice|voice)$/ ) && ( mod || sub ) )
case message.params[1].split( " " ).size
@@ -823,6 +912,12 @@ loop do
else
obsipc.send( "{ \"request-type\": \"GetSceneList\", \"message-id\": \"GetSceneList\" }" )
end
+ elsif ( ( cmd =~ /^sceneitem$/ ) && ( mod || own || vip ) )
+ if ( match[2]? && match[2] =~ /^[a-zA-Z0-9-_]+$/ )
+ obsipc.send( "\x10sceneitem #{match[2]}" )
+ else
+ ircipc.send( { "##{settings["channel"]}", "Must provide provide a scene name as argument." } )
+ end
elsif ( ( cmd =~ /^source$/ ) && ( mod || own || vip ) )
request = Hash( String, String | Bool ).new
if ( match[2]? ) && ( sourceargs = match[2].match( /^([a-zA-Z0-9-_]+) +(true|false)$/ ) )
@@ -854,14 +949,22 @@ loop do
else
ircipc.send( { "##{settings["channel"]}", "Must provide at least one source name as argument, and optionally one filter name and a value of true or false." } )
end
- elsif ( cmd == "duplicate" && ( mod || own || vip ) )
- if ( match[2]? ) && ( filterargs = match[2].match( /^([a-zA-Z0-9-_]+)/ ) )
- temporaryduplicate( match[2] )
+ elsif ( cmd == "create" && ( mod || own || vip ) )
+ if ( match[2]? ) && ( match[2] =~ /^([a-zA-Z0-9-_]+)/ )
+ obsipc.send( "\x10newsource-temporary #{match[2]}" )
else
ircipc.send( { "##{settings["channel"]}", "Must provide at least one source name as argument." } )
end
elsif ( cmd =~ /^(metaminute|youdied)$/ )
- obsipc.send( "\x10source media-#{cmd}" )
+ obsipc.send( "\x10source-notransition media-#{cmd}" )
+ elsif ( cmd =~ /^explosion$/ )
+ if explosions
+ explosion = explosions.sample( 1 )[0][1]
+ puts explosion
+ obsipc.send( "\x10newsource-temporary #{explosion}" )
+ else
+ puts "explosions undefined"
+ end
# FIXME: This is only half-implemented
elsif ( ( cmd =~ /^(user)$/ ) && ( mod || own ) )
if match[2]? && match[2] =~ /[0-9]+/
@@ -961,6 +1064,13 @@ loop do
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]}"} )
+ effectsmsg( settings, "overlay gltext 5 Go follow #{match[2]}" )
+ 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" )