summaryrefslogtreecommitdiff
path: root/crystal
diff options
context:
space:
mode:
authorJoe Rayhawk <jrayhawk+git@omgwallhack.org>2024-02-24 14:02:20 -0800
committerJoe Rayhawk <jrayhawk@fairlystable.org>2024-02-27 03:14:33 -0800
commite04324d824a2e427c7229bd6b58006a26e16202f (patch)
tree33f5705fd9cbc9bb11f2df6076990f76e1236e71 /crystal
parent4c52796a73cfb981892a17eda3b570720d57794f (diff)
downloadtwitchtools-e04324d824a2e427c7229bd6b58006a26e16202f.tar.gz
twitchtools-e04324d824a2e427c7229bd6b58006a26e16202f.zip
crystal/tcpsocket: provide more command matching options in config
Diffstat (limited to 'crystal')
-rw-r--r--crystal/lib/bungmobott/src/bungmobott.cr13
-rw-r--r--crystal/tcpsocket.cr408
2 files changed, 214 insertions, 207 deletions
diff --git a/crystal/lib/bungmobott/src/bungmobott.cr b/crystal/lib/bungmobott/src/bungmobott.cr
index 4fdb9d2..3df034f 100644
--- a/crystal/lib/bungmobott/src/bungmobott.cr
+++ b/crystal/lib/bungmobott/src/bungmobott.cr
@@ -13,11 +13,15 @@
#chat_user:
# twitch: bungmonkey
# gamesurge: bungmoBott
+#match:
+# '^gamesurge|twitch$'
+# '^#bungmonkey$'
+# '^!help$': [ { perm: [ any ], func: say, arg: ['| https://bungmonkey.omgwallhack.org/txt/commands.txt'] } ]
module BungmoBott
EXE = "bungmobott"
-
+
class ChatUser
include YAML::Serializable
include YAML::Serializable::Unmapped
@@ -49,7 +53,7 @@ module BungmoBott
property twitch : Array(String)?
property gamesurge : Array(String)?
end
-
+
class Config
include YAML::Serializable
include YAML::Serializable::Unmapped
@@ -74,7 +78,10 @@ module BungmoBott
property obs_connect : String? = nil
property chat_user : ChatUser? = ChatUser.from_yaml("---")
property join_channels : JoinChannels? = JoinChannels.from_yaml("---")
- property commands : Hash( String, Array( Commands ) )? = nil
+ property match : Hash( String, # network regex
+ Hash( String, # channel regex
+ Hash( String, # text regex
+ Array( Commands ) ) ) )?
@[YAML::Field(emit_null: true)]
property twitch_user_id : UInt32? = nil
@[YAML::Field(emit_null: true)]
diff --git a/crystal/tcpsocket.cr b/crystal/tcpsocket.cr
index a7d1e8a..c9cdf84 100644
--- a/crystal/tcpsocket.cr
+++ b/crystal/tcpsocket.cr
@@ -297,7 +297,7 @@ fibers = Hash( String, Fiber ).new
evchan = Channel( JSON::Any ).new
obs : Nil | OBS::WebSocket = nil
if config.obs_connect
- obs = OBS::WebSocket.new( "ws://#{config.obs_connect}/" )
+ obs = OBS::WebSocket.new( "ws://#{config.obs_connect}/", secrets.obs_password )
# OBS event thread
spawn name: "obs_event_thread" do
fiberipc.send( Fiber.current )
@@ -592,7 +592,6 @@ rescue ex
return nil
end
-
# Currently only used in flag?(:unix)
def t2smsg( config : BungmoBott::Config, msg : String)
if File.exists?( config.rundir + "/.t2s.sock" )
@@ -741,235 +740,236 @@ spawn name: "command_dispatch" do
# end
# end
# end
- config.commands && config.commands.not_nil!.each do | commandregex, command | # Hash( String, Array )
- if Regex.new( commandregex ).match( message.params[1] )
- command.each do |exec| # Array
- if (
- ( 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 ) )
- unless oldname.is_a?( String )
- 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
- 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." )
+ config.match && config.match.not_nil!.each do | networkregex, channelhash |
+ Regex.new( networkregex ).match( service ) || next
+ channelhash.each do | channelregex, texthash |
+ Regex.new( channelregex ).match( ircchannel ) || next
+ texthash.each do | textregex, command | # Hash( String, Array )
+ Regex.new( textregex ).match( message.params[1] ) || next
+ command.each do |exec| # Array
+ if (
+ ( 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 ) )
+ unless oldname.is_a?( String )
+ say( service, config.chat_user.not_nil!.twitch.not_nil!, "Rename detected: #{uid}: #{oldname} -> #{chatuser}" )
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." )
+ if obs
+ # FIXME: better validate args
+ ( exec.func == "obs_random_source_enable" ) && obsrandommediaenable( obs, exec.arg.not_nil![0].not_nil! ) && next
+ 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
- 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." )
+ 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
- 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." )
+ if ( exec.func == "obs_scene_get" )
+ puts "Exec-ing obs_scene_get"
+ say( service, ircchannel, "| Current scene: #{obs.scenes.program.name}" )
+ next
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
+ 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
- sources.sample( count ).each do |path|
- obstemporarymediacreate( obs, exec.arg.not_nil![0].not_nil!, path.gsub(/[\/ ]/, '_').downcase, path )
+ 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
- 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.strip )
- lastvoice = lastvoice[0..4]
+ 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
- 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." )
+ 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
- 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." )
+ 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.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
- say( service, ircchannel, "| Voice for #{chatuser} is already random." )
+ pp ( match )
+ say( service, ircchannel, "| Invalid voice. To see list, use !voices" )
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" )
+ say( service, ircchannel, "| tts_voice_set failed generic string match ")
end
- else
- say( service, ircchannel, "| tts_voice_set failed generic string match ")
+ next
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." )
+ 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
- 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." )
+ 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
- say( service, ircchannel, "| Name substitution for #{chatuser} is already disabled." )
+ Dir.mkdir_p( userdir )
+ File.write( userdir + "/voicesub", voicesub )
+ say( service, ircchannel, "| Name substitution for #{chatuser} is now \"#{File.read( userdir + "/voicesub" ).strip}\"." )
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
+ next
end
+ end
+ if ( exec.func == "say" )
+ say( service, config.chat_user.not_nil!.twitch.not_nil!, exec.arg.not_nil![0].not_nil! )
next
end
- end
- if ( exec.func == "say" )
- say( service, config.chat_user.not_nil!.twitch.not_nil!, exec.arg.not_nil![0].not_nil! )
- next
- end
- if ( exec.func == "run" )
- if ( cmd = exec.arg[0]? ) && File.executable?( cmd )
- p = Process.new(
- cmd, exec.arg[1..]?, output: STDOUT, error: STDERR,
- env: {
- "TEXT" => message.params[1],
- "CHATUSER" => chatuser
- }
- )
- else
- STDERR.puts( "WARNING: exec.func \"run\" called without argument" )
+ if ( exec.func == "run" )
+ if ( cmd = exec.arg[0]? ) && File.executable?( cmd )
+ p = Process.new(
+ cmd, exec.arg[1..]?, output: STDOUT, error: STDERR,
+ env: {
+ "TEXT" => message.params[1],
+ "CHATUSER" => chatuser
+ }
+ )
+ else
+ STDERR.puts( "WARNING: exec.func \"run\" called without argument" )
+ end
end
- end
- if ( exec.func == "run_shell" )
- if ( cmd = exec.arg[0]? )
- p = Process.new(
- cmd, exec.arg[1..]?, output: STDOUT, error: STDERR, shell: true,
- env: {
- "TEXT" => message.params[1],
- "CHATUSER" => chatuser
- }
- )
- else
- STDERR.puts( "WARNING: exec.func \"run_shell\" called without argument" )
+ if ( exec.func == "run_shell" )
+ if ( cmd = exec.arg[0]? )
+ p = Process.new(
+ cmd, exec.arg[1..]?, output: STDOUT, error: STDERR, shell: true,
+ env: {
+ "TEXT" => message.params[1],
+ "CHATUSER" => chatuser
+ }
+ )
+ else
+ STDERR.puts( "WARNING: exec.func \"run_shell\" called without argument" )
+ end
+ next
end
- next
+ STDERR.puts "WARNING: unhandled function for /#{textregex}/: #{exec.func}"
+ else
+ STDOUT.print "DENIED: "
+ ppe command
end
- STDERR.puts "WARNING: unhandled function for /#{commandregex}/: #{exec.func}"
- else
- STDOUT.print "DENIED: "
- ppe command
end
end
end