extends Websocket_Client class_name Chat_Socket ## Wrapper class around [Websocket_Client] that handles Twitch Chat signal chat_received signal chat_received_raw signal chat_received_rich signal chat_connected var channels : Array[String] var extra_info : bool var user_regex := RegEx.new() var user_pattern := r":([\w]+)!" func _init(): packet_received.connect(data_received) user_regex.compile(user_pattern) ## Connects to the Twitch IRC server. func connect_to_chat(token, extra = false, nick = "terribletwitch"): extra_info = extra connect_to_url("wss://irc-ws.chat.twitch.tv:443") await socket_open send_text("PASS oauth:" + token) send_text("NICK " + nick) if extra: send_text("CAP REQ :twitch.tv/commands twitch.tv/tags") ## Handles checking the received packet from [signal Websocket_Client.packet_received] func data_received(packet : PackedByteArray): # Gets the text from the packet, strips the end and splits the different lines var messages = packet.get_string_from_utf8().strip_edges(false).split("\r\n") for msg in messages: # Checks if this is a message that has tags enabled and if so, parses out the tags. var tags : Dictionary if msg.begins_with("@"): # Grabs the actual end of the string with the message in it. var real_msg = msg.split(" ", false, 1) msg = real_msg[1] # Loops through all the tags splitting them up by ; and then by = to get the keys and values. for tag in real_msg[0].split(";"): var key_value = tag.split("=") tags[key_value[0]] = key_value[1] parse_chat_msg(msg, tags) #@badge-info=subscriber/34;badges=broadcaster/1,subscriber/6,game-developer/1;client-nonce=b5009ae3ee034a7706d86fe221882925;color=#2E8B57;display-name=EroAxee;emotes=;first-msg=0;flags=;id=be05dae8-4067-4edf-83f2-e6be02974904;mod=0;returning-chatter=0;room-id=160349129;subscriber=1;tmi-sent-ts=1702009303249;turbo=0;user-id=160349129;user-type= :eroaxee!eroaxee@eroaxee.tmi.twitch.tv PRIVMSG #eroaxee :More ## Parses the given [param msg] [String] func parse_chat_msg(msg : String, tags : Dictionary): var msg_dict : Dictionary if msg == "PING :tmi.twitch.tv": send_text(msg.replace("PING", "PONG")) return var msg_notice = msg.split(" ")[1] match msg_notice: "PRIVMSG": var space_split = msg.split(" ", true, 3) msg_dict["username"] = user_regex.search(msg).get_string(1) msg_dict["message"] = space_split[3].trim_prefix(":") msg_dict["channel"] = space_split[2].trim_prefix("#") msg_dict.merge(parse_tags(tags)) prints(msg_dict.username, msg_dict.message, msg_dict.channel) #(__username_regex.search(split[0]).get_string(1), split[3].right(1), split[2], tags) if !tags.is_empty(): chat_received_rich.emit(msg_dict) return chat_received.emit(msg_dict) # Connection Message "001": prints("Connection Established", msg) chat_connected.emit() # Chat Joining Message "JOIN": pass # Chat Leaving Message "PART": pass #@badge-info=subscriber/34;badges=broadcaster/1,subscriber/6,game-developer/1;client-nonce=02d73777ab1fab1aee33ada1830d52b5;color=#2E8B57;display-name=EroAxee;emotes=;first-msg=0;flags=;id=4ff91a8c-b965-43f8-85a1-ddd541a2b438;mod=0;returning-chatter=0;room-id=160349129;subscriber=1;tmi-sent-ts=1701850826667;turbo=0;user-id=160349129;user-type= :eroaxee!eroaxee@eroaxee.tmi.twitch.tv PRIVMSG #eroaxee :Stuff ## Utility function that takes a Dictionary of tags from a Twitch message and parses them to be slightly more usable. func parse_tags(tags : Dictionary): var new_tags : Dictionary for all in tags.keys(): if all == "badges": tags[all] = tags[all].split(",") new_tags[all.replace("-", "_")] = tags[all] return new_tags #{ "@badge-info": "subscriber/34", "badges": "broadcaster/1,subscriber/6,game-developer/1", "client-nonce": "b2e3524806f51c94cadd61d338bc14ed", "color": "#2E8B57", "display-name": "EroAxee", "emotes": "", "first-msg": "0", "flags": "", "id": "494dc47e-0d9c-4407-83ec-309764e1adf3", "mod": "0", "returning-chatter": "0", "room-id": "160349129", "subscriber": "1", "tmi-sent-ts": "1701853794297", "turbo": "0", "user-id": "160349129", "user-type": "" } ## Wrapper function around [method WebSocketPeer.send_text] func send_chat(msg : String, channel : String = ""): if channel.is_empty(): channel = channels[0] channel = channel.strip_edges() send_text("PRIVMSG #" + channel + " :" + msg + "\r\n") ## Utility function that handles joining the supplied [param channel]'s chat. func join_chat(channel : String): send_text("JOIN #" + channel + "\r\n") channels.append(channel) ## Utility function that handles leaving the supplied [param channel]'s chat. func leave_chat(channel : String): send_chat("PART #" + channel) channels.erase(channel)