2024-01-25 07:36:05 +01:00
|
|
|
extends Websocket_Client
|
|
|
|
class_name Chat_Socket
|
2023-12-15 22:44:25 +01:00
|
|
|
|
|
|
|
## 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]+)!"
|
|
|
|
|
|
|
|
|
2024-01-25 07:36:05 +01:00
|
|
|
func _init(owner : Twitch_Connection):
|
|
|
|
|
|
|
|
chat_received_rich.connect(owner.check_chat_socket.bind(true))
|
|
|
|
chat_received.connect(owner.check_chat_socket)
|
2023-12-15 22:44:25 +01:00
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
|
2024-01-25 07:36:05 +01:00
|
|
|
|
|
|
|
|
2023-12-15 22:44:25 +01:00
|
|
|
|
|
|
|
## 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():
|
|
|
|
|
2024-02-26 12:36:19 +01:00
|
|
|
if channels.is_empty():
|
|
|
|
|
|
|
|
push_error("NoTwitch Error: No Twitch channels have been joined.")
|
|
|
|
return
|
|
|
|
|
|
|
|
|
2023-12-15 22:44:25 +01:00
|
|
|
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):
|
|
|
|
|
2024-02-26 12:36:19 +01:00
|
|
|
send_text("PART #" + channel)
|
2023-12-15 22:44:25 +01:00
|
|
|
channels.erase(channel)
|
|
|
|
|