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"]