summaryrefslogtreecommitdiff
path: root/crystal
diff options
context:
space:
mode:
authorJoe Rayhawk <jrayhawk+git@omgwallhack.org>2020-10-25 00:06:53 -0700
committerJoe Rayhawk <jrayhawk+git@omgwallhack.org>2020-10-25 00:06:53 -0700
commit805ddfbbdc357d3348e6f8a0abb9bb0be631d543 (patch)
tree4da5d32a454aab8005c95b6828b4e289b47359fd /crystal
parentc5a6071834ebc314361bbe1f849a288048e884b6 (diff)
downloadtwitchtools-805ddfbbdc357d3348e6f8a0abb9bb0be631d543.tar.gz
twitchtools-805ddfbbdc357d3348e6f8a0abb9bb0be631d543.zip
New: native twitch IRC client
Diffstat (limited to 'crystal')
-rw-r--r--crystal/irc.cr293
1 files changed, 293 insertions, 0 deletions
diff --git a/crystal/irc.cr b/crystal/irc.cr
new file mode 100644
index 0000000..96d4090
--- /dev/null
+++ b/crystal/irc.cr
@@ -0,0 +1,293 @@
+require "twitch/irc"
+require "http"
+require "uri"
+require "twitcr"
+require "json"
+
+STDOUT.sync = true
+
+settings = Hash(String, String).new
+
+settings["home"] = Path.home.to_s
+["access_token", "channel", "channel_id", "client_id", "client_id_twitch"].each do |key|
+ begin
+ settings[key] = File.read( settings["home"] + "/.config/twitch/" + key ).chomp
+ rescue IO::Error
+ STDERR.puts "Warning: Missing " + settings["home"] + "/.config/twitch/" + key
+ end
+end
+
+client = Twitcr::Client.new( settings )
+
+def ircmsg( home : String, channel : String, msg : String)
+ if msg !~ /(\r|\n)/
+ sock = Socket.unix
+# sock.connect Socket::UNIXAddress.new( home + "/.irssi/twitch-socket".to_s, type: Socket::Type::DGRAM )
+ sock.connect Socket::UNIXAddress.new( home + "/.irssi/twitch-socket".to_s )
+ sock.puts( "#" + channel + " " + msg )
+ sock.close
+ end
+end
+
+def t2smsg( msg : String)
+ sock = Socket.unix
+ sock.connect Socket::UNIXAddress.new( ENV["HOME"] + "/.t2s.sock" )
+ sock.puts( msg )
+ sock.close
+rescue ex
+ puts ex
+end
+
+def effectsmsg( msg : String )
+ sock = Socket.unix
+ sock.connect Socket::UNIXAddress.new( ENV["HOME"] + "/.effects.sock" )
+ sock.puts( msg )
+ sock.close
+ puts "attempted to use glmatrix"
+rescue ex
+ puts ex
+end
+
+def userlog( settings : Hash(String, String), message : FastIRC::Message )
+ unless ( ( prefix = message.prefix ) && ( chatuser = prefix.source ) && ( uid = message.tags["user-id"]? ) )
+ return nil
+ end
+ basedir = settings["home"] + "/.cache/twitchtools"
+ userdir = basedir + "/uids/" + uid
+ unless File.directory?( userdir )
+ Dir.mkdir( userdir )
+ end
+ unless File.directory?( userdir + "/names" )
+ Dir.mkdir( userdir + "/names" )
+ end
+ unless File.symlink?( userdir + "/names/" + chatuser )
+ namelatest = ""
+ datelatest = Time::UNIX_EPOCH
+ Dir.each_child( userdir + "/names/" ) do |name|
+ namedate = File.info( userdir + "/names/" + name ).modification_time
+ if namedate > datelatest
+ namelatest = name
+ datelatest = namedate
+ end
+ end
+ unless namelatest.empty?
+ ircmsg( settings["home"], settings["channel"], "Rename detected: #{uid}: #{namelatest} -> #{chatuser}" )
+ end
+ File.symlink( "../", userdir + "/names/" + chatuser )
+ end
+ unless File.symlink?( basedir + "/names/" + chatuser )
+ File.symlink( "../uids/" + uid, basedir + "/names/" + chatuser )
+ end
+ unless ( message.params[0] == "#bungmonkey" )
+ return nil
+ end
+ return ( [ chatuser, uid, userdir ] )
+rescue ex
+ puts ex
+end
+
+def getvoice( settings : Hash(String, String), userdir : String, chatuser : String )
+ if File.exists?( userdir + "/voice" )
+ voice_output = File.read( userdir + "/voice" ).chomp
+ voice_setting = voice_output
+ else
+ voice_output = File.read( settings["home"] + "/voicelist.txt" ).chomp.split( "\n" ).sample( 1 )[0].chomp
+ voice_setting = "random"
+ end
+ if File.exists?( userdir + "/voicesub" )
+ namesub = File.read( userdir + "/voicesub" ).chomp
+ else
+ namesub = chatuser
+ end
+ return( [namesub, voice_setting, voice_output ] )
+end
+
+def t2s( settings : Hash(String, String), userdir : String, chatuser : String, text : String )
+ if ( text !~ /^ *(!|\|)/ )
+ namesub, voice_setting, voice = getvoice( settings, userdir, chatuser )
+ t2stext = text.gsub( /http(s|):\/\/([a-z0-9.]+)\/[a-zA-Z0-9\/&=%-_]+/, "link to ${2}" )
+ t2stext = t2stext.gsub( /rrr.+/, "rr" )
+ t2smsg( "#{voice} #{namesub} #{t2stext}" )
+ return( voice )
+ else
+ return( nil )
+ end
+end
+
+voices = Hash(String, String).new
+File.each_line( settings["home"] + "/voicelist" ) do |line|
+ voices[ line.downcase ] = line
+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" ]
+
+# Create a handler to process incoming messages
+bot.on_message do |message|
+ next unless ( userlogreturn = userlog( settings, message ) )
+ chatuser, uid, userdir = userlogreturn
+ 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" ) && ( 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
+ 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( "#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
+ 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 == "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
+ bot.message( "#bungmonkey", "| Invalid voice. To see list, use !voices" )
+ end
+ end
+ when 3
+ else
+ end
+ elsif ( ( cmd =~ /^(status|title)$/ ) && ( mod || own ) )
+ begin
+ if match[2]?
+ puts "2 matches"
+ json = JSON.parse( client.put_channel!( settings["channel_id"].to_u64, status: match[2] ) )
+ ircmsg( settings["home"], settings["channel"], "Status is now \"#{ json["status"] }\"")
+ else
+ puts "1 matches"
+ json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) )
+ ircmsg( settings["home"], settings["channel"], "| Status is currently \"#{ json["status"] }\"")
+ 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"
+ json = JSON.parse( client.put_channel!( settings["channel_id"].to_u64, game: match[2] ) )
+ ircmsg( settings["home"], settings["channel"], "Game is now \"#{ json["game"] }\"")
+ else
+ puts "1 matches"
+ json = JSON.parse( client.get_channel( settings["channel_id"].to_u64 ) )
+ ircmsg( settings["home"], settings["channel"], "| Game is currently \"#{ json["game"] }\"")
+ end
+ rescue ex
+ ircmsg( settings["home"], settings["channel"], "| An error occurred! " + ex.message.to_s )
+ end
+ elsif ( ( cmd == "urban" ) && ( mod || own || sub || vip ) )
+
+ elsif ( cmd == "glmatrix" )
+ effectsmsg( "overlay glmatrix" )
+ 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
+ end
+
+rescue ex
+ pp ex
+end
+
+rooms = Array( String ).new
+rooms = [ "#bungmonkey", "kr3wzz" ]
+
+# Connect to Twitch
+bot.run( rooms.map{ | room | room.sub( /^#/, "") } )
+
+# FastIRC::Message.to_s
+# @badge-info=;badges=;color=;display-name=BungMonkey;emotes=;flags=;id=20fcc358-4fc3-4919-8229-f1034743d18f;mod=0;room-id=46694819;subscriber=0;tmi-sent-ts=1587876383907;turbo=0;user-id=59895482;user-type= :bungmonkey!bungmonkey@bungmonkey.tmi.twitch.tv PRIVMSG #kr3wzz test
+
+# FastIRC::Message.tags
+# {"badge-info" => "subscriber/34",
+# "badges" => "broadcaster/1,subscriber/12",
+# "color" => "",
+# "display-name" => "BungMonkey",
+# "emote-only" => "1",
+# "emotes" => "300780134:0-9",
+# "flags" => "",
+# "id" => "c5c08c05-6e39-483f-b426-488dfc477a6c",
+# "mod" => "0",
+# "room-id" => "59895482",
+# "subscriber" => "1",
+# "tmi-sent-ts" => "1587871386678",
+# "turbo" => "0",
+# "user-id" => "59895482",
+# "user-type" => ""}
+
+#command = "PRIVMSG"
+#prefix = Prefix(@source="bungmonkey", @user="bungmonkey", @host="bungmonkey.tmi.twitch.tv")
+#params = ["#bungmonkey", "test test test test"]