mirror of
https://codeberg.org/StreamGraph/StreamGraph.git
synced 2024-11-13 19:49:55 +01:00
Twitch updates pt. 1 (#56)
- Add credential saving for Twitch - Sync NoTwitch with upstream to allow for API and EventSub access - Add Twitch Connected Account Info and Twitch Request User Info nodes Co-authored-by: Eroax <eroaxe.business@gmail.com> Reviewed-on: https://codeberg.org/StreamGraph/StreamGraph/pulls/56 Co-authored-by: Lera Elvoé <yagich@poto.cafe> Co-committed-by: Lera Elvoé <yagich@poto.cafe>
This commit is contained in:
parent
cb4a83a2f8
commit
d0313b9697
13 changed files with 499 additions and 20 deletions
|
@ -1,4 +1,5 @@
|
|||
extends "res://addons/no_twitch/websocket_client.gd"
|
||||
extends Websocket_Client
|
||||
class_name Chat_Socket
|
||||
|
||||
## Wrapper class around [Websocket_Client] that handles Twitch Chat
|
||||
|
||||
|
@ -15,7 +16,10 @@ var user_regex := RegEx.new()
|
|||
var user_pattern := r":([\w]+)!"
|
||||
|
||||
|
||||
func _init():
|
||||
func _init(owner : Twitch_Connection):
|
||||
|
||||
chat_received_rich.connect(owner.check_chat_socket.bind(true))
|
||||
chat_received.connect(owner.check_chat_socket)
|
||||
|
||||
packet_received.connect(data_received)
|
||||
|
||||
|
@ -37,6 +41,8 @@ func connect_to_chat(token, extra = false, nick = "terribletwitch"):
|
|||
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):
|
||||
|
@ -77,7 +83,6 @@ func parse_chat_msg(msg : String, tags : Dictionary):
|
|||
|
||||
return
|
||||
|
||||
|
||||
var msg_notice = msg.split(" ")[1]
|
||||
match msg_notice:
|
||||
|
||||
|
|
|
@ -29,5 +29,5 @@ func join_chat():
|
|||
|
||||
func send_chat(chat : String):
|
||||
|
||||
%Twitch_Connection.send_chat_to_channel(chat, %Channel.text)
|
||||
%Twitch_Connection.send_chat(chat, %Channel.text)
|
||||
|
||||
|
|
|
@ -1,8 +1,38 @@
|
|||
extends Control
|
||||
|
||||
var demo_events : Array[Twitch_Connection.EventSub_Subscription] = [
|
||||
|
||||
Twitch_Connection.EventSub_Subscription.new("channel.update", {"broadcaster_user_id" : ""}, "", "2")
|
||||
|
||||
]
|
||||
|
||||
func _ready():
|
||||
|
||||
$Twitch_Connection.token_received.connect(save_token)
|
||||
%Get_User.pressed.connect(func():
|
||||
|
||||
var resp = %Twitch_Connection.request_user_info()
|
||||
resp.response_received.connect(print_http_result)
|
||||
|
||||
)
|
||||
|
||||
%Get_Channel.pressed.connect(func():
|
||||
|
||||
var resp = %Twitch_Connection.request_channel_info()
|
||||
resp.response_received.connect(print_http_result)
|
||||
|
||||
)
|
||||
|
||||
%Connect_Channel_Points.pressed.connect(func():
|
||||
|
||||
await %Twitch_Connection.setup_eventsub_connection()
|
||||
print("Passed Await")
|
||||
%Twitch_Connection.eventsub_socket.notif_received.connect(eventsub_notif_received)
|
||||
|
||||
var resp = %Twitch_Connection.subscribe_to_channel_points(%Twitch_Connection.user_info.id)
|
||||
resp.response_received.connect(print_http_result)
|
||||
|
||||
)
|
||||
|
||||
load_token()
|
||||
|
||||
|
@ -23,5 +53,15 @@ func load_token():
|
|||
|
||||
var res = ResourceLoader.load("user://token.tres")
|
||||
$Twitch_Connection.token = res.token
|
||||
%Twitch_Connection.cache_user_data()
|
||||
|
||||
|
||||
func print_http_result(data):
|
||||
|
||||
print(data)
|
||||
|
||||
|
||||
func eventsub_notif_received(info):
|
||||
print(info)
|
||||
printt(info.payload.event.reward.id, info.payload.event.user_input, info.payload.event.reward.title)
|
||||
|
||||
|
|
|
@ -76,6 +76,21 @@ unique_name_in_owner = true
|
|||
layout_mode = 2
|
||||
text = "Send"
|
||||
|
||||
[node name="Get_User" type="Button" parent="VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Get User Info"
|
||||
|
||||
[node name="Get_Channel" type="Button" parent="VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Get Channel Info"
|
||||
|
||||
[node name="Connect_Channel_Points" type="Button" parent="VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Connect Channel Points"
|
||||
|
||||
[node name="Twitch_Connection" type="Node" parent="."]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("1_13a4v")
|
||||
|
|
|
@ -5,5 +5,5 @@ extends Button
|
|||
|
||||
func _pressed():
|
||||
|
||||
twitch_connection.authenticate_with_twitch()
|
||||
OS.shell_open(twitch_connection.authenticate_with_twitch(["channel:read:redemptions", "chat:read", "chat:edit"]))
|
||||
|
||||
|
|
4
addons/no_twitch/demo/token_saver.gd
Normal file
4
addons/no_twitch/demo/token_saver.gd
Normal file
|
@ -0,0 +1,4 @@
|
|||
extends Resource
|
||||
class_name TokenSaver
|
||||
|
||||
@export var token : String
|
70
addons/no_twitch/eventsub_socket.gd
Normal file
70
addons/no_twitch/eventsub_socket.gd
Normal file
|
@ -0,0 +1,70 @@
|
|||
extends Websocket_Client
|
||||
|
||||
const eventsub_url := "wss://eventsub.wss.twitch.tv/ws"
|
||||
|
||||
## Stores the "session id" for this EventSub connection.
|
||||
var session_id
|
||||
|
||||
var connection : Twitch_Connection
|
||||
|
||||
signal notif_received(data)
|
||||
|
||||
signal welcome_received()
|
||||
|
||||
|
||||
func _init(owner):
|
||||
|
||||
connection = owner
|
||||
|
||||
packet_received.connect(data_received)
|
||||
|
||||
|
||||
func connect_to_eventsub(events : Array[Twitch_Connection.EventSub_Subscription] = []):
|
||||
|
||||
connect_to_url(eventsub_url)
|
||||
await welcome_received
|
||||
|
||||
if events.is_empty():
|
||||
|
||||
return
|
||||
|
||||
|
||||
var responses : Array[Twitch_Connection.HTTPResponse]
|
||||
|
||||
for all in events:
|
||||
|
||||
responses.append(connection.add_eventsub_subscription(all))
|
||||
|
||||
|
||||
return responses
|
||||
|
||||
|
||||
func data_received(packet : PackedByteArray):
|
||||
|
||||
var info = JSON.parse_string(packet.get_string_from_utf8())
|
||||
|
||||
match info.metadata.message_type:
|
||||
|
||||
"session_welcome":
|
||||
|
||||
session_id = info.payload.session.id
|
||||
welcome_received.emit()
|
||||
|
||||
|
||||
"session_ping":
|
||||
|
||||
send_pong(info)
|
||||
|
||||
|
||||
"notification":
|
||||
|
||||
notif_received.emit(info)
|
||||
|
||||
|
||||
|
||||
|
||||
func send_pong(pong):
|
||||
|
||||
pong.metadata.message_type = "session_pong"
|
||||
send_text(str(pong))
|
||||
|
|
@ -7,50 +7,81 @@ class_name Twitch_Connection
|
|||
|
||||
signal token_received(token : String)
|
||||
|
||||
signal chat_received(chat_dict)
|
||||
signal chat_received_rich(chat_dict)
|
||||
|
||||
@export var client_id := "qyjg1mtby1ycs5scm1pvctos7yvyc1"
|
||||
@export var redirect_uri := "http://localhost"
|
||||
## Port that the redirect_uri will head to on your local system. Defaults to 80 for most cases. Linux tends to prefer 8000 or possibly 1338
|
||||
@export var redirect_port := "8000"
|
||||
|
||||
var twitch_url := "https://id.twitch.tv/oauth2/authorize?response_type=token&"
|
||||
var auth_url := "https://id.twitch.tv/oauth2/authorize?response_type=token&"
|
||||
|
||||
var auth_server : TCPServer
|
||||
## Websocket used for handling the chat connection.
|
||||
var chat_socket = preload("res://addons/no_twitch/chat_socket.gd").new()
|
||||
|
||||
const chat_socket_class = preload("res://addons/no_twitch/chat_socket.gd")
|
||||
const eventsub_socket_class = preload("res://addons/no_twitch/eventsub_socket.gd")
|
||||
## [Websocket_Client] used for receiving all data from Twitch pertaining to chat (Ex. IRC Chat Messages)
|
||||
var chat_socket : Websocket_Client
|
||||
## [Websocket_Client] handles all the data from Twitch pertaining to EventSub (Ex. General Alerts)
|
||||
var eventsub_socket : Websocket_Client
|
||||
|
||||
var state := make_state()
|
||||
var token : String
|
||||
|
||||
## The User data of the account that the authorized [member token] is connected to.
|
||||
var user_info : Dictionary
|
||||
|
||||
var responses : Array
|
||||
|
||||
func _ready():
|
||||
|
||||
chat_socket.chat_received.connect(check_chat_socket)
|
||||
token_received.connect(cache_user_data.unbind(1))
|
||||
|
||||
|
||||
## Function that handles Caching the data of the Account tied to the Connections Token
|
||||
func cache_user_data():
|
||||
|
||||
if token.is_empty():
|
||||
|
||||
return
|
||||
|
||||
|
||||
var resp = request_user_info()
|
||||
resp.response_received.connect(func(data):
|
||||
|
||||
user_info = data
|
||||
print("User Info Cached")
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
## Handles the basic Twitch Authentication process to request and then later receive a Token (using [method check_auth_peer]).
|
||||
## Returns the authentication URL.
|
||||
func authenticate_with_twitch(client_id = client_id) -> String:
|
||||
func authenticate_with_twitch(client_id = client_id, scopes : Array[String] = ["chat:read", "chat:edit"]) -> String:
|
||||
auth_server = TCPServer.new()
|
||||
var url := create_auth_url()
|
||||
var url := create_auth_url(scopes)
|
||||
auth_server.listen(int(redirect_port))
|
||||
return url
|
||||
|
||||
|
||||
## Sets up a single chat connection. Joining 1 room with [param token] as it's "PASS" specifying what account it's on. And the optional [param default_chat] specifying the default room to join. While [param nick] specifies the "nickname" (Not the username on Twitch)
|
||||
## Sets up the chat connection. Joining 1 room with [param token] as it's "PASS" specifying what account it's on. And the optional [param default_chat] specifying the default room to join. While [param nick] specifies the "nickname" (Not the username on Twitch)
|
||||
func setup_chat_connection(default_chat : String = "", token : String = token, request_twitch_info = true, nick = "terribletwitch"):
|
||||
|
||||
# Temporary, closes the socket to allow connecting multiple times without needing to reset.
|
||||
chat_socket.close()
|
||||
chat_socket = chat_socket_class.new(self)
|
||||
|
||||
# Connects to the Twitch IRC server.
|
||||
chat_socket.connect_to_chat(token, request_twitch_info)
|
||||
await chat_socket.socket_open
|
||||
|
||||
if !default_chat.is_empty():
|
||||
|
||||
Connections.save_credentials({"token" : token, "channel" : default_chat}, "twitch")
|
||||
chat_socket.join_chat(default_chat)
|
||||
|
||||
|
||||
|
||||
|
||||
## Joins the given [param channel] over IRC. Essentially just sending JOIN #[param channel]
|
||||
func join_channel(channel : String):
|
||||
|
||||
|
@ -62,6 +93,30 @@ func send_chat(msg : String, channel : String = ""):
|
|||
chat_socket.send_chat(msg, channel)
|
||||
|
||||
|
||||
## Sets up an EventSub connection to allow subscribing to EventSub events. Ex. Alerts, Channel Point Redemptions etc.
|
||||
func setup_eventsub_connection(events : Array[EventSub_Subscription] = [], timeout_duration : int = 20):
|
||||
|
||||
eventsub_socket = eventsub_socket_class.new(self)
|
||||
var ret = await eventsub_socket.connect_to_eventsub(events)
|
||||
return ret
|
||||
|
||||
|
||||
func add_eventsub_subscription(sub_type : EventSub_Subscription):
|
||||
|
||||
return twitch_request("https://api.twitch.tv/helix/eventsub/subscriptions", HTTPClient.METHOD_POST, str(sub_type.return_request_dictionary()))
|
||||
|
||||
|
||||
func subscribe_to_channel_points(user_id : String):
|
||||
|
||||
if !eventsub_socket.socket_open:
|
||||
|
||||
push_error("NoTwitch Error: No EventSub Connection, please use setup_eventsub_connection first")
|
||||
return
|
||||
|
||||
|
||||
return add_eventsub_subscription(EventSub_Subscription.new("channel.channel_points_custom_reward_redemption.add", {"broadcaster_user_id" : user_id}, eventsub_socket.session_id))
|
||||
|
||||
|
||||
func _process(delta):
|
||||
|
||||
if auth_server and auth_server.is_listening() and auth_server.is_connection_available():
|
||||
|
@ -74,12 +129,17 @@ func _process(delta):
|
|||
chat_socket.poll_socket()
|
||||
|
||||
|
||||
if eventsub_socket:
|
||||
|
||||
eventsub_socket.poll_socket()
|
||||
|
||||
|
||||
|
||||
## Utility function for creating a Twitch Authentication URL with the given
|
||||
## [param scopes], Twitch Client ID ([param id]) and [param redirect_uri].
|
||||
## [param id] defaults to [member client_id] and both [param redirect] and
|
||||
## [param redirect_port]
|
||||
func create_auth_url(scopes : Array[String] = ["chat:read", "chat:edit"], port := redirect_port, id : String = client_id, redirect : String = redirect_uri) -> String:
|
||||
func create_auth_url(scopes : Array[String], port := redirect_port, id : String = client_id, redirect : String = redirect_uri) -> String:
|
||||
|
||||
var str_scopes : String
|
||||
|
||||
|
@ -92,10 +152,78 @@ func create_auth_url(scopes : Array[String] = ["chat:read", "chat:edit"], port :
|
|||
if !port.is_empty():
|
||||
full_redirect_uri += ":" + port
|
||||
|
||||
var url := twitch_url + "client_id=" + id + "&redirect_uri=" + full_redirect_uri + "&scope=" + str_scopes + "&state=" + str(state)
|
||||
var url := auth_url + "client_id=" + id + "&redirect_uri=" + full_redirect_uri + "&scope=" + str_scopes + "&state=" + str(state)
|
||||
|
||||
return url
|
||||
|
||||
## Utility Function for making a generic HTTP Request to Twitch
|
||||
func twitch_request(url : String, method : HTTPClient.Method = HTTPClient.METHOD_GET, body := ""):
|
||||
|
||||
var headers : Array = ["Authorization: Bearer " + token, "Client-Id: " + client_id]
|
||||
|
||||
if !body.is_empty():
|
||||
|
||||
headers.append("Content-Type: application/json")
|
||||
|
||||
|
||||
# Adds the Content type to the headers if we're actually sending some data along
|
||||
if method != HTTPClient.METHOD_GET:
|
||||
|
||||
headers.append("Content-Type: application/json")
|
||||
|
||||
|
||||
var http := HTTPRequest.new()
|
||||
|
||||
var resp = HTTPResponse.new(http, responses)
|
||||
add_child(http)
|
||||
http.request(url, headers, method, body)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
## Wrapper function around [method twitch_request] to handle doing a Twitch "Get Users" request.
|
||||
func request_user_info(users : Array[String] = []):
|
||||
|
||||
var req_url := "https://api.twitch.tv/helix/users"
|
||||
|
||||
if users.is_empty():
|
||||
|
||||
return twitch_request(req_url)
|
||||
|
||||
|
||||
var user_list : String = "?login=" + users[0]
|
||||
for all in users:
|
||||
|
||||
user_list += "&login=" + all
|
||||
|
||||
|
||||
return twitch_request(req_url + user_list)
|
||||
|
||||
|
||||
## Wrapper function around [method twitch_request] to grab information about a set of channels based off an Array of Channel IDs Ex. stream title, current category, current tags, etc.
|
||||
func request_channel_info(channels : Array[String] = []):
|
||||
|
||||
var req_url := "https://api.twitch.tv/helix/channels?broadcaster_id="
|
||||
|
||||
if channels.is_empty():
|
||||
|
||||
if user_info.is_empty():
|
||||
|
||||
return
|
||||
|
||||
|
||||
channels.append(user_info.id)
|
||||
|
||||
|
||||
var id_string := channels[0]
|
||||
for all in channels:
|
||||
|
||||
id_string += "&broadcaster_id=" + all
|
||||
|
||||
|
||||
return twitch_request(req_url + id_string)
|
||||
|
||||
|
||||
## Utility function for creating a "state" used for different requests for some extra security as a semi password.
|
||||
func make_state(len : int = 16) -> String:
|
||||
|
||||
|
@ -134,9 +262,79 @@ func check_auth_peer(peer : StreamPeerTCP):
|
|||
token_received.emit(token)
|
||||
|
||||
|
||||
func check_chat_socket(dict):
|
||||
func check_chat_socket(dict, rich = false):
|
||||
|
||||
prints(dict.user, dict.message)
|
||||
|
||||
if rich:
|
||||
|
||||
chat_received_rich.emit(dict)
|
||||
return
|
||||
|
||||
chat_received.emit(dict)
|
||||
|
||||
|
||||
## "Promise" Class used for relaying the info received from the supplied [HTTPRequest] node.
|
||||
class HTTPResponse:
|
||||
|
||||
signal response_received
|
||||
|
||||
## The infuriatingly needed data variable. Because you can't await AND get signal data.
|
||||
var inf_data
|
||||
|
||||
func _init(http : HTTPRequest, storage : Array):
|
||||
|
||||
storage.append(self)
|
||||
http.request_completed.connect(request_complete.bind(http, storage))
|
||||
|
||||
|
||||
func request_complete(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http, storage):
|
||||
|
||||
var info = JSON.parse_string(body.get_string_from_utf8())
|
||||
|
||||
if info.has("error"):
|
||||
|
||||
push_error("NoTwitch Twitch API Error: " + info.error + " " + info.message)
|
||||
return
|
||||
|
||||
info = info.data[0]
|
||||
|
||||
print("Response Received")
|
||||
inf_data = info
|
||||
response_received.emit(info)
|
||||
|
||||
http.queue_free()
|
||||
storage.erase(self)
|
||||
|
||||
|
||||
|
||||
## Data handler class for making it easier to send the data used for EventSub Subscriptions over [member eventsub_socket]
|
||||
class EventSub_Subscription:
|
||||
|
||||
## Specifies the type of subscription this is representing (Ex. "Channel Update" is channel.update)
|
||||
var subscription_type : String
|
||||
## Used for setting the "version" the subscription that we're currently using.
|
||||
var version : String
|
||||
## Stores the "condition", AKA the condition for being able to subscribe to this Event. (Ex. The User ID of the channel you're listening to)
|
||||
var condition : Dictionary
|
||||
## Holds the "session" ID for this EventSub Subscription. Which is held in [member eventsub_socket.session_id]
|
||||
var session_id : String
|
||||
|
||||
var method = "websocket"
|
||||
|
||||
func _init(sub_type : String, cond : Dictionary, sess_id : String, vers : String = "1"):
|
||||
|
||||
subscription_type = sub_type
|
||||
version = vers
|
||||
condition = cond
|
||||
session_id = sess_id
|
||||
|
||||
|
||||
## Returns back the information pertaining to the Subscription in the format needed for twitch.
|
||||
func return_request_dictionary():
|
||||
|
||||
var dict = {"type" : subscription_type, "version" : version, "condition" : condition, "transport" : {"method" : method, "session_id" : session_id}}
|
||||
|
||||
return dict
|
||||
|
||||
|
||||
|
|
|
@ -319,6 +319,15 @@ func resolve_input_port_value(input_port: int) -> Variant:
|
|||
else:
|
||||
return null
|
||||
|
||||
func resolve_input_port_value_async(input_port: int) -> Variant:
|
||||
if await request_value_async(input_port) != null:
|
||||
return await request_value_async(input_port)
|
||||
elif get_input_ports()[input_port].value_callback.get_object() && get_input_ports()[input_port].value_callback.call() != null:
|
||||
return get_input_ports()[input_port].value_callback.call()
|
||||
elif get_input_ports()[input_port].value != null:
|
||||
return get_input_ports()[input_port].value
|
||||
else:
|
||||
return null
|
||||
|
||||
## Returns a [Dictionary] representation of this node.
|
||||
func to_dict(with_meta: bool = true) -> Dictionary:
|
||||
|
|
71
classes/deck/nodes/twitch/twitch_account_info.gd
Normal file
71
classes/deck/nodes/twitch/twitch_account_info.gd
Normal file
|
@ -0,0 +1,71 @@
|
|||
# (c) 2023-present Eroax
|
||||
# (c) 2023-present Yagich
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
extends DeckNode
|
||||
|
||||
|
||||
func _init():
|
||||
name = "Twitch Connected Account Info"
|
||||
node_type = name.to_snake_case()
|
||||
description = "Accesses the Cached data for the Connected Account"
|
||||
|
||||
props_to_serialize = []
|
||||
|
||||
add_output_port(DeckType.Types.STRING, "Login")
|
||||
add_output_port(DeckType.Types.STRING, "Display Name")
|
||||
add_output_port(DeckType.Types.STRING, "Profile Picture URL")
|
||||
add_output_port(DeckType.Types.DICTIONARY, "User Dictionary")
|
||||
|
||||
#{
|
||||
#"id": "141981764",
|
||||
#"login": "twitchdev",
|
||||
#"display_name": "TwitchDev",
|
||||
#"type": "",
|
||||
#"broadcaster_type": "partner",
|
||||
#"description": "Supporting third-party developers building Twitch integrations from chatbots to game integrations.",
|
||||
#"profile_image_url": "https://static-cdn.jtvnw.net/jtv_user_pictures/8a6381c7-d0c0-4576-b179-38bd5ce1d6af-profile_image-300x300.png",
|
||||
#"offline_image_url": "https://static-cdn.jtvnw.net/jtv_user_pictures/3f13ab61-ec78-4fe6-8481-8682cb3b0ac2-channel_offline_image-1920x1080.png",
|
||||
#"view_count": 5980557,
|
||||
#"email": "not-real@email.com",
|
||||
#"created_at": "2016-12-14T20:32:28Z"
|
||||
#}
|
||||
|
||||
|
||||
func _value_request(from_port):
|
||||
|
||||
var user_dict = Connections.twitch.user_info
|
||||
|
||||
if user_dict.is_empty():
|
||||
|
||||
DeckHolder.logger.log_node("Twitch Connection: No Data Cached for the connected Twitch Account, please try Connecting again", Logger.LogType.ERROR)
|
||||
return null
|
||||
|
||||
|
||||
match from_port:
|
||||
|
||||
0: # Login Name
|
||||
|
||||
return user_dict.login
|
||||
|
||||
|
||||
1: # Display Name
|
||||
|
||||
return user_dict.display_name
|
||||
|
||||
|
||||
2: # Profile Picture URL
|
||||
|
||||
return user_dict.profile_image_url
|
||||
|
||||
|
||||
_: # Dictionary
|
||||
|
||||
return user_dict
|
||||
|
||||
|
||||
|
||||
|
||||
return Connections.twitch.user_info
|
||||
|
||||
|
||||
|
65
classes/deck/nodes/twitch/twitch_request_user_info.gd
Normal file
65
classes/deck/nodes/twitch/twitch_request_user_info.gd
Normal file
|
@ -0,0 +1,65 @@
|
|||
# (c) 2023-present Eroax
|
||||
# (c) 2023-present Yagich
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
extends DeckNode
|
||||
|
||||
|
||||
func _init():
|
||||
name = "Twitch Request User Info"
|
||||
node_type = name.to_snake_case()
|
||||
description = "Requests to the Twitch API for the User info for the supplied Login or ID"
|
||||
|
||||
props_to_serialize = []
|
||||
|
||||
# Adds the User input.
|
||||
add_input_port(DeckType.Types.STRING, "User Login", "field")
|
||||
|
||||
# Adds the Checkbox for using IDs vs Logins and sets it to true.
|
||||
add_input_port(DeckType.Types.BOOL, "User Input as ID", "checkbox")
|
||||
get_input_ports()[1].set_value(true)
|
||||
|
||||
get_input_ports()[1].value_updated.connect(switch_user_input_type)
|
||||
|
||||
add_output_port(DeckType.Types.ANY, "User Info")
|
||||
|
||||
|
||||
func switch_user_input_type(value):
|
||||
|
||||
var user_port = get_input_ports()[0]
|
||||
|
||||
if value:
|
||||
|
||||
user_port.label = "User ID"
|
||||
|
||||
else:
|
||||
|
||||
user_port.label = "User Login"
|
||||
|
||||
|
||||
ports_updated.emit()
|
||||
|
||||
|
||||
|
||||
func _value_request(from_port):
|
||||
|
||||
var user_input = resolve_input_port_value(0)
|
||||
var as_id = resolve_input_port_value(1)
|
||||
|
||||
var req_url := "https://api.twitch.tv/helix/users?"
|
||||
if as_id:
|
||||
|
||||
req_url += "id=" + user_input
|
||||
|
||||
else:
|
||||
|
||||
req_url += "login=" + user_input
|
||||
|
||||
|
||||
var resp = Connections.twitch.twitch_request(req_url)
|
||||
|
||||
await resp.response_received
|
||||
var data = resp.inf_data
|
||||
|
||||
return data
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ func _ready() -> void:
|
|||
Connections.obs_websocket = no_obsws
|
||||
Connections.twitch = %Twitch_Connection
|
||||
|
||||
Connections.twitch.chat_socket.chat_received_rich.connect(Connections._twitch_chat_received)
|
||||
Connections.twitch.chat_received_rich.connect(Connections._twitch_chat_received)
|
||||
|
||||
file_popup_menu.set_item_shortcut(FileMenuId.NEW, new_deck_shortcut)
|
||||
file_popup_menu.set_item_shortcut(FileMenuId.OPEN, open_deck_shortcut)
|
||||
|
|
|
@ -21,6 +21,7 @@ func authenticate_twitch():
|
|||
Connections.twitch.token = loaded_creds.data.token
|
||||
if loaded_creds.data.has("channel"):
|
||||
%Default_Chat.text = loaded_creds.data.channel
|
||||
Connections.twitch.cache_user_data()
|
||||
connect_to_chat()
|
||||
return
|
||||
|
||||
|
@ -42,8 +43,9 @@ func copy_auth_link():
|
|||
|
||||
func connect_to_chat():
|
||||
|
||||
if Connections.twitch.chat_socket.get_ready_state() != WebSocketPeer.STATE_OPEN:
|
||||
if !Connections.twitch.get("chat_socket") or Connections.twitch.chat_socket.get_ready_state() != WebSocketPeer.STATE_OPEN:
|
||||
Connections.twitch.setup_chat_connection(%Default_Chat.text)
|
||||
return
|
||||
|
||||
|
||||
Connections.twitch.join_channel(%Default_Chat.text)
|
||||
|
|
Loading…
Reference in a new issue