From 805ddfbbdc357d3348e6f8a0abb9bb0be631d543 Mon Sep 17 00:00:00 2001 From: Joe Rayhawk Date: Sun, 25 Oct 2020 00:06:53 -0700 Subject: New: native twitch IRC client --- crystal/irc.cr | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 crystal/irc.cr (limited to 'crystal/irc.cr') 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"] -- cgit v1.2.3