extends IrcClientSecure class_name TwiCIL signal raw_response_recieved(response) signal user_appeared(user) signal user_disappeared(user) signal message_recieved(sender, text, emotes) signal emote_recieved(user, emote_reference) signal texture_recieved(texture) enum IRCCommands{PING, PONG, PRIVMSG, JOIN, PART, NAMES} const TWITCH_IRC_CHAT_HOST = "wss://irc-ws.chat.twitch.tv" const TWITCH_IRC_CHAT_PORT = 443 onready var tools = HelperTools.new() onready var commands = InteractiveCommands.new() onready var chat_list = ChatList.new() var twitch_emotes_cache:TwitchEmotesCache var bttv_emotes_cache:BttvEmotesCache var ffz_emotes_cache:FfzEmotesCache var twitch_api_wrapper:TwitchApiWrapper var irc_commands = { IRCCommands.PING:"PING", IRCCommands.PONG:"PONG", IRCCommands.PRIVMSG:"PRIVMSG", IRCCommands.JOIN:"JOIN", IRCCommands.PART:"PART", IRCCommands.NAMES:"/NAMES" } var curr_channel = "" var user_emotes_queue: = Dictionary() func connect_to_twitch_chat()->bool: return .connect_to_host(TWITCH_IRC_CHAT_HOST, TWITCH_IRC_CHAT_PORT) func connect_to_channel(channel, client_id, password, nickname, realname = ""): _connect_to( channel, nickname, nickname if realname == "" else realname, password, client_id ) twitch_api_wrapper.set_credentials(client_id, password) func _connect_to(channel, nickname, realname, password, client_id): .send_command("PASS %s" % password) .send_command("NICK " + nickname) .send_command(str("USER ", client_id, " ", _host, " bla:", realname)) .send_command("JOIN #" + channel) .send_command("CAP REQ :twitch.tv/tags twitch.tv/commands twitch.tv/membership") curr_channel = channel func Disconnect(): .Disconnect(); func IsConnected()->bool: return .IsConnected(); func send_message(text): .send_command(str("PRIVMSG #", curr_channel, " :", text)) func send_whisper(recepient, text): send_message(str("/w ", recepient, " ", text)) func request_twitch_emote(user_name:String, id:int)->void : if not user_emotes_queue.has(id): user_emotes_queue[id] = [] user_emotes_queue[id].append(user_name) twitch_emotes_cache.get_emote(id) func request_bttv_emote(user_name:String, code:String)->void : var id:String = bttv_emotes_cache.available_emotes.get(code) if not user_emotes_queue.has(id): user_emotes_queue[id] = [] user_emotes_queue[id].append(user_name) bttv_emotes_cache.get_emote(code) func request_ffz_emote(user_name:String, code:String)->void : var id:String = ffz_emotes_cache.available_emotes.get(code, {}).get("id", "") if not user_emotes_queue.has(id): user_emotes_queue[id] = [] user_emotes_queue[id].append(user_name) ffz_emotes_cache.get_emote(code) func request_emote_from(emotes:Array, user_name:String, index:int)->void : if emotes.empty(): return var emote = emotes[index] if emote.get("type") == TwitchMessage.EmoteType.TWITCH: var emote_id: = int(emote.get("id")) request_twitch_emote(user_name, emote_id) elif emote.get("type") == TwitchMessage.EmoteType.BTTV: var emote_code: = emote.get("code") as String request_bttv_emote(user_name, emote_code) elif emote.get("type") == TwitchMessage.EmoteType.FFZ: var emote_code: = emote.get("code") as String request_ffz_emote(user_name, emote_code) func __init_emotes_caches()->void : twitch_emotes_cache = TwitchEmotesCache.new() add_child(twitch_emotes_cache) bttv_emotes_cache = BttvEmotesCache.new() add_child(bttv_emotes_cache) ffz_emotes_cache = FfzEmotesCache.new() add_child(ffz_emotes_cache) func __init_twitch_api()->void : twitch_api_wrapper = TwitchApiWrapper.new(http_request_queue, "") func __connect_signals(): connect("message_recieved", commands, "_on_message_recieved") connect("response_recieved", self, "_on_response_recieved") connect("http_response_recieved", self, "_on_http_response_recieved") twitch_emotes_cache.connect("emote_retrieved", self, "_on_emote_retrieved") bttv_emotes_cache.connect("emote_retrieved", self, "_on_emote_retrieved") ffz_emotes_cache.connect("emote_retrieved", self, "_on_emote_retrieved") twitch_api_wrapper.connect("api_user_info", self, "_on_twitch_api_api_user_info") func __parse(string:String)->TwitchIrcServerMessage: var args = [] var twitch_prefix = "" var prefix = "" var trailing = [] var command if string == null: return TwitchIrcServerMessage.new("", "", "", []) if string.substr(0, 1) == "@": var temp = tools.split_string(string.substr(1, string.length() - 1), " ", 1) twitch_prefix = temp[0] string = temp[1] if string.substr(0, 1) == ":": var temp = tools.split_string(string.substr(1, string.length() - 1), " ", 1) prefix = temp[0] string = temp[1] if string.find(" :") != - 1: var temp = tools.split_string(string, " :", 1) string = temp[0] trailing = temp[1] args = tools.split_string(string, [" ", " ", "\n"]) args.append(trailing) else : args = tools.split_string(string, [" ", " ", "\n"]) command = args[0] args.pop_front() return TwitchIrcServerMessage.new(twitch_prefix, prefix, command, args) func _ready(): __init_twitch_api() __init_emotes_caches() __connect_signals() func _on_response_recieved(response): emit_signal("raw_response_recieved", response) for single_response in response.split("\n", false): single_response = __parse(single_response.strip_edges(false)) if single_response.command == irc_commands[IRCCommands.PING]: .send_command(str(irc_commands[IRCCommands.PONG], " ", single_response.params[0])) elif single_response.command == irc_commands[IRCCommands.PRIVMSG]: var twitch_message:TwitchMessage = TwitchMessage.new( single_response, bttv_emotes_cache.available_emotes, ffz_emotes_cache.available_emotes ) emit_signal( "message_recieved", twitch_message.chat_message.name, twitch_message.chat_message.text, twitch_message.emotes ) elif single_response.command == irc_commands[IRCCommands.JOIN]: var user_name = MessageWrapper.get_sender_name(single_response) chat_list.add_user(user_name) ._log(str(user_name, " has joined chat")) emit_signal("user_appeared", user_name) elif single_response.command == irc_commands[IRCCommands.PART]: var user_name = MessageWrapper.get_sender_name(single_response) chat_list.remove_user(user_name) ._log(str(user_name, " has left chat")) emit_signal("user_disappeared", user_name) func _on_emote_retrieved(emote_reference:Reference)->void : var emote_id:String = emote_reference.id var user:String = (user_emotes_queue.get(emote_id, []) as Array).pop_front() emit_signal("emote_recieved", user, emote_reference) func _on_twitch_api_api_user_info(data): var user_id: = str(data.get("data", [{}])[0].get("id", 0)) ffz_emotes_cache.init_emotes(user_id)