mirror of
https://codeberg.org/StreamGraph/StreamGraph.git
synced 2024-11-13 19:49:55 +01:00
Updates NoTwitch + Implements Twitch Get Request (#124)
Patch the Array/Dictionary/Array problem with NoTwitch Plus tweaks the existing Connected Account Info + Chat Received Nodes. Reviewed-on: https://codeberg.org/StreamGraph/StreamGraph/pulls/124 Co-authored-by: Eroax <eroaxebusiness@duck.com> Co-committed-by: Eroax <eroaxebusiness@duck.com>
This commit is contained in:
parent
edeb8e22dc
commit
b94c3702cc
11 changed files with 167 additions and 63 deletions
|
@ -6,6 +6,7 @@ class_name Chat_Socket
|
||||||
signal chat_received
|
signal chat_received
|
||||||
signal chat_received_raw
|
signal chat_received_raw
|
||||||
signal chat_received_rich
|
signal chat_received_rich
|
||||||
|
signal irc_received
|
||||||
|
|
||||||
signal chat_connected
|
signal chat_connected
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ var user_regex := RegEx.new()
|
||||||
var user_pattern := r":([\w]+)!"
|
var user_pattern := r":([\w]+)!"
|
||||||
|
|
||||||
|
|
||||||
func _init(owner : Twitch_Connection):
|
func _init(owner : No_Twitch):
|
||||||
|
|
||||||
chat_received_rich.connect(owner.check_chat_socket.bind(true))
|
chat_received_rich.connect(owner.check_chat_socket.bind(true))
|
||||||
chat_received.connect(owner.check_chat_socket)
|
chat_received.connect(owner.check_chat_socket)
|
||||||
|
@ -112,15 +113,11 @@ func parse_chat_msg(msg : String, tags : Dictionary):
|
||||||
prints("Connection Established", msg)
|
prints("Connection Established", msg)
|
||||||
chat_connected.emit()
|
chat_connected.emit()
|
||||||
|
|
||||||
# Chat Joining Message
|
|
||||||
"JOIN":
|
|
||||||
|
|
||||||
pass
|
# General IRC Messages, Notices etc.
|
||||||
|
_:
|
||||||
|
|
||||||
# Chat Leaving Message
|
irc_received.emit(msg)
|
||||||
"PART":
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,7 +147,7 @@ func send_chat(msg : String, channel : String = ""):
|
||||||
|
|
||||||
if channels.is_empty():
|
if channels.is_empty():
|
||||||
|
|
||||||
push_error("NoTwitch Error: No Twitch channels have been joined.")
|
push_error("No_Twitch Error: No Twitch channels have been joined.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
var demo_events : Array[Twitch_Connection.EventSub_Subscription] = [
|
var demo_events : Array[No_Twitch.EventSub_Subscription] = [
|
||||||
|
|
||||||
Twitch_Connection.EventSub_Subscription.new("channel.update", {"broadcaster_user_id" : ""}, "", "2")
|
No_Twitch.EventSub_Subscription.new("channel.update", {"broadcaster_user_id" : ""}, "", "2")
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@onready var twitch : No_Twitch = %Twitch_Connection
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
|
||||||
$Twitch_Connection.token_received.connect(save_token)
|
twitch.token_received.connect(save_token)
|
||||||
%Get_User.pressed.connect(func():
|
%Get_User.pressed.connect(func():
|
||||||
|
|
||||||
var resp = %Twitch_Connection.request_user_info()
|
var resp = %Twitch_Connection.request_user_info()
|
||||||
|
@ -25,13 +26,20 @@ func _ready():
|
||||||
|
|
||||||
%Connect_Channel_Points.pressed.connect(func():
|
%Connect_Channel_Points.pressed.connect(func():
|
||||||
|
|
||||||
await %Twitch_Connection.setup_eventsub_connection()
|
await twitch.setup_eventsub_connection()
|
||||||
print("Passed Await")
|
print("Passed Await")
|
||||||
%Twitch_Connection.eventsub_socket.notif_received.connect(eventsub_notif_received)
|
twitch.eventsub_socket.notif_received.connect(eventsub_notif_received)
|
||||||
|
|
||||||
var resp = %Twitch_Connection.subscribe_to_channel_points(%Twitch_Connection.user_info.id)
|
var resp = await twitch.subscribe_to_channel_points(twitch.user_info.id)
|
||||||
resp.response_received.connect(print_http_result)
|
resp.response_received.connect(print_http_result)
|
||||||
|
|
||||||
|
)
|
||||||
|
%Get_Followers.pressed.connect(func():
|
||||||
|
|
||||||
|
var resp = twitch.twitch_request("https://api.twitch.tv/helix/channels/followers?broadcaster_id=" + twitch.user_info.id)
|
||||||
|
await resp.response_received
|
||||||
|
print(resp.inf_data)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
load_token()
|
load_token()
|
||||||
|
|
|
@ -91,6 +91,11 @@ unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Connect Channel Points"
|
text = "Connect Channel Points"
|
||||||
|
|
||||||
|
[node name="Get_Followers" type="Button" parent="VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Get Followers"
|
||||||
|
|
||||||
[node name="Twitch_Connection" type="Node" parent="."]
|
[node name="Twitch_Connection" type="Node" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
script = ExtResource("1_13a4v")
|
script = ExtResource("1_13a4v")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
extends Button
|
extends Button
|
||||||
|
|
||||||
|
|
||||||
@onready var twitch_connection : Twitch_Connection = $"../../Twitch_Connection"
|
@onready var twitch_connection : No_Twitch = $"../../Twitch_Connection"
|
||||||
|
|
||||||
func _pressed():
|
func _pressed():
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ const eventsub_url := "wss://eventsub.wss.twitch.tv/ws"
|
||||||
## Stores the "session id" for this EventSub connection.
|
## Stores the "session id" for this EventSub connection.
|
||||||
var session_id
|
var session_id
|
||||||
|
|
||||||
var connection : Twitch_Connection
|
var connection : No_Twitch
|
||||||
|
|
||||||
signal notif_received(data)
|
signal notif_received(data)
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ signal welcome_received()
|
||||||
var keepalive_timer := 0
|
var keepalive_timer := 0
|
||||||
var timeout_time : int
|
var timeout_time : int
|
||||||
|
|
||||||
## Dictionary used for storing all the subscribed events in the format "subscription_type : Twitch_Connection.EventSub_Subscription"
|
## Dictionary used for storing all the subscribed events in the format "subscription_type : No_Twitch.EventSub_Subscription"
|
||||||
var subscribed_events : Dictionary
|
var subscribed_events : Dictionary
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ func poll_socket():
|
||||||
|
|
||||||
|
|
||||||
## Handles setting up the connection to EventSub with an Array of the Events that should be subscribed to.
|
## Handles setting up the connection to EventSub with an Array of the Events that should be subscribed to.
|
||||||
func connect_to_eventsub(events : Array[Twitch_Connection.EventSub_Subscription]):
|
func connect_to_eventsub(events : Array[No_Twitch.EventSub_Subscription]):
|
||||||
|
|
||||||
connect_to_url(eventsub_url)
|
connect_to_url(eventsub_url)
|
||||||
await welcome_received
|
await welcome_received
|
||||||
|
@ -55,9 +55,9 @@ func connect_to_eventsub(events : Array[Twitch_Connection.EventSub_Subscription]
|
||||||
|
|
||||||
|
|
||||||
## Utility function for subscribing to multiple Twitch EventSub events at once.
|
## Utility function for subscribing to multiple Twitch EventSub events at once.
|
||||||
func subscribe_to_events(events : Array[Twitch_Connection.EventSub_Subscription]):
|
func subscribe_to_events(events : Array[No_Twitch.EventSub_Subscription]):
|
||||||
|
|
||||||
var responses : Array[Twitch_Connection.HTTPResponse]
|
var responses : Array[No_Twitch.HTTPResponse]
|
||||||
|
|
||||||
for all in events:
|
for all in events:
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ func subscribe_to_events(events : Array[Twitch_Connection.EventSub_Subscription]
|
||||||
return responses
|
return responses
|
||||||
|
|
||||||
|
|
||||||
func new_eventsub_subscription(info, event_subscription : Twitch_Connection.EventSub_Subscription):
|
func new_eventsub_subscription(info, event_subscription : No_Twitch.EventSub_Subscription):
|
||||||
|
|
||||||
if !event_subscription in subscribed_events:
|
if !event_subscription in subscribed_events:
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
extends Node
|
extends Node
|
||||||
class_name Twitch_Connection
|
class_name No_Twitch
|
||||||
|
|
||||||
## Handler for a Connection to a Single Twitch Account
|
## Handler for a Connection to a Single Twitch Account
|
||||||
##
|
##
|
||||||
|
@ -113,7 +113,7 @@ func setup_eventsub_connection(events : Array[EventSub_Subscription] = [], timeo
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
## Adds a new subscription to a given EventSub Event using a [Twitch_Connection.EventSub_Subscription]
|
## Adds a new subscription to a given EventSub Event using a [No_Twitch.EventSub_Subscription]
|
||||||
## to handle the data.
|
## to handle the data.
|
||||||
func add_eventsub_subscription(sub_type : EventSub_Subscription):
|
func add_eventsub_subscription(sub_type : EventSub_Subscription):
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ func subscribe_to_channel_points(user_id : String):
|
||||||
|
|
||||||
if !eventsub_socket.socket_open:
|
if !eventsub_socket.socket_open:
|
||||||
|
|
||||||
push_error("NoTwitch Error: No EventSub Connection, please use setup_eventsub_connection first")
|
push_error("No_Twitch Error: No EventSub Connection, please use setup_eventsub_connection first")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@ -181,15 +181,10 @@ func create_auth_url(port := redirect_port, id : String = client_id, redirect :
|
||||||
return url
|
return url
|
||||||
|
|
||||||
## Utility Function for making a generic HTTP Request to Twitch
|
## Utility Function for making a generic HTTP Request to Twitch
|
||||||
func twitch_request(url : String, method : HTTPClient.Method = HTTPClient.METHOD_GET, body := ""):
|
func twitch_request(url : String, method : HTTPClient.Method = HTTPClient.METHOD_GET, body := "") -> HTTPResponse:
|
||||||
|
|
||||||
var headers : Array = ["Authorization: Bearer " + token, "Client-Id: " + client_id]
|
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
|
# Adds the Content type to the headers if we're actually sending some data along
|
||||||
if method != HTTPClient.METHOD_GET:
|
if method != HTTPClient.METHOD_GET:
|
||||||
|
|
||||||
|
@ -327,13 +322,21 @@ class HTTPResponse:
|
||||||
|
|
||||||
if info.has("error"):
|
if info.has("error"):
|
||||||
|
|
||||||
push_error("NoTwitch Twitch API Error: " + str(info))
|
push_error("No_Twitch Twitch API Error: " + str(info))
|
||||||
|
emit_response(info, http, storage)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
info["data"] = info.get("data", [[]])[0]
|
if info.data is Array and info.data.size() == 1:
|
||||||
|
info.data = info.data[0]
|
||||||
|
# Revisit later cause Twitch weirdness
|
||||||
|
#info["data"] = info.get("data", [[]])[0]
|
||||||
|
|
||||||
print("Response Received")
|
print("Response Received")
|
||||||
|
emit_response(info, http, storage)
|
||||||
|
|
||||||
|
func emit_response(info, http, storage):
|
||||||
|
|
||||||
inf_data = info
|
inf_data = info
|
||||||
response_received.emit(info)
|
response_received.emit(info)
|
||||||
|
|
||||||
|
@ -342,6 +345,7 @@ class HTTPResponse:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Data handler class for making it easier to send the data used for EventSub Subscriptions over
|
## Data handler class for making it easier to send the data used for EventSub Subscriptions over
|
||||||
## [member eventsub_socket]
|
## [member eventsub_socket]
|
||||||
class EventSub_Subscription:
|
class EventSub_Subscription:
|
||||||
|
|
|
@ -13,7 +13,7 @@ func _init():
|
||||||
|
|
||||||
add_output_port(DeckType.Types.STRING, "Login", "", Port.UsageType.VALUE_REQUEST)
|
add_output_port(DeckType.Types.STRING, "Login", "", Port.UsageType.VALUE_REQUEST)
|
||||||
add_output_port(DeckType.Types.STRING, "Display Name", "", Port.UsageType.VALUE_REQUEST)
|
add_output_port(DeckType.Types.STRING, "Display Name", "", Port.UsageType.VALUE_REQUEST)
|
||||||
add_output_port(DeckType.Types.STRING, "Profile Picture URL", "", Port.UsageType.VALUE_REQUEST)
|
add_output_port(DeckType.Types.STRING, "User ID", "", Port.UsageType.VALUE_REQUEST)
|
||||||
add_output_port(DeckType.Types.DICTIONARY, "User Dictionary", "", Port.UsageType.VALUE_REQUEST)
|
add_output_port(DeckType.Types.DICTIONARY, "User Dictionary", "", Port.UsageType.VALUE_REQUEST)
|
||||||
|
|
||||||
#{
|
#{
|
||||||
|
@ -55,7 +55,7 @@ func _value_request(from_port):
|
||||||
|
|
||||||
2: # Profile Picture URL
|
2: # Profile Picture URL
|
||||||
|
|
||||||
return user_dict.profile_image_url
|
return user_dict.id
|
||||||
|
|
||||||
|
|
||||||
_: # Dictionary
|
_: # Dictionary
|
||||||
|
|
|
@ -12,7 +12,7 @@ enum inputs {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscription_data : Twitch_Connection.EventSub_Subscription
|
var subscription_data : No_Twitch.EventSub_Subscription
|
||||||
|
|
||||||
func _init():
|
func _init():
|
||||||
name = "Twitch Add EventSub Subscription"
|
name = "Twitch Add EventSub Subscription"
|
||||||
|
@ -68,8 +68,8 @@ func _receive(to_input_port, data: Variant) -> void:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Creates an instance of Twitch_Connection.EventSub_Subscription to store the data with all the given inputs.
|
# Creates an instance of No_Twitch.EventSub_Subscription to store the data with all the given inputs.
|
||||||
subscription_data = Twitch_Connection.EventSub_Subscription.new(event, sub_data)
|
subscription_data = No_Twitch.EventSub_Subscription.new(event, sub_data)
|
||||||
# Checks if the data has a version field, if so sets it on the EventSub_Subscription
|
# Checks if the data has a version field, if so sets it on the EventSub_Subscription
|
||||||
if sub_data.has("version"):
|
if sub_data.has("version"):
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ func _receive(to_input_port, data: Variant) -> void:
|
||||||
req.response_received.connect(eventsub_subscription_response)
|
req.response_received.connect(eventsub_subscription_response)
|
||||||
|
|
||||||
|
|
||||||
## Handles checking the [Twitch_Connection.HTTPResponse] returned by [method Twitch_Connection.add_eventsub_subscription] to ensure that it succeeded.
|
## Handles checking the [No_Twitch.HTTPResponse] returned by [method No_Twitch.add_eventsub_subscription] to ensure that it succeeded.
|
||||||
func eventsub_subscription_response(data):
|
func eventsub_subscription_response(data):
|
||||||
|
|
||||||
match data.code:
|
match data.code:
|
||||||
|
|
|
@ -14,15 +14,17 @@ func _init():
|
||||||
node_type = "twitch_chat_received"
|
node_type = "twitch_chat_received"
|
||||||
description = "Receives Twitch chat events from a Twitch connection."
|
description = "Receives Twitch chat events from a Twitch connection."
|
||||||
|
|
||||||
add_output_port(DeckType.Types.STRING, "Username", "", Port.UsageType.TRIGGER)
|
|
||||||
add_output_port(DeckType.Types.STRING, "Message", "", Port.UsageType.TRIGGER)
|
|
||||||
add_output_port(DeckType.Types.STRING, "Channel", "", Port.UsageType.TRIGGER)
|
|
||||||
add_output_port(DeckType.Types.DICTIONARY, "Tags", "", Port.UsageType.TRIGGER)
|
|
||||||
|
|
||||||
add_output_port(
|
add_output_port(
|
||||||
DeckType.Types.BOOL,
|
DeckType.Types.ANY,
|
||||||
"On receive"
|
"On receive",
|
||||||
|
"",
|
||||||
|
Port.UsageType.TRIGGER,
|
||||||
)
|
)
|
||||||
|
add_output_port(DeckType.Types.STRING, "Username")
|
||||||
|
add_output_port(DeckType.Types.STRING, "Message")
|
||||||
|
add_output_port(DeckType.Types.STRING, "Channel")
|
||||||
|
add_output_port(DeckType.Types.DICTIONARY, "Tags")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,22 +40,24 @@ func _event_received(event_name : StringName, event_data : Dictionary = {}):
|
||||||
tags = event_data
|
tags = event_data
|
||||||
|
|
||||||
var id = UUID.v4()
|
var id = UUID.v4()
|
||||||
send(0, username, id)
|
|
||||||
send(1, message, id)
|
send(0, null, id)
|
||||||
send(2, channel, id)
|
send(1, username, id)
|
||||||
send(3, tags, id)
|
send(2, message, id)
|
||||||
send(4, true, id)
|
send(3, channel, id)
|
||||||
|
send(4, tags, id)
|
||||||
|
|
||||||
|
|
||||||
func _value_request(on_port: int) -> Variant:
|
func _value_request(on_port: int) -> Variant:
|
||||||
match on_port:
|
match on_port:
|
||||||
0:
|
|
||||||
return username
|
|
||||||
1:
|
1:
|
||||||
return message
|
return username
|
||||||
2:
|
2:
|
||||||
return channel
|
return message
|
||||||
3:
|
3:
|
||||||
|
return channel
|
||||||
|
4:
|
||||||
return tags
|
return tags
|
||||||
_:
|
0, _:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
85
classes/deck/nodes/twitch/twitch_get_request.gd
Normal file
85
classes/deck/nodes/twitch/twitch_get_request.gd
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
# (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
|
||||||
|
|
||||||
|
|
||||||
|
enum inputs {
|
||||||
|
url,
|
||||||
|
params,
|
||||||
|
trigger
|
||||||
|
}
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
name = "Twitch Get Request"
|
||||||
|
node_type = name.to_snake_case()
|
||||||
|
description = "A simple node for doing an HTTP Request to Twitch using the Authorized Account Information"
|
||||||
|
|
||||||
|
add_input_port(DeckType.Types.STRING, "Request URL", "field")
|
||||||
|
add_input_port(DeckType.Types.DICTIONARY, "Parameters")
|
||||||
|
add_input_port(DeckType.Types.ANY, "Trigger")
|
||||||
|
|
||||||
|
add_output_port(DeckType.Types.DICTIONARY, "Response", "", Port.UsageType.TRIGGER)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func _receive(to_input_port, data: Variant):
|
||||||
|
|
||||||
|
if (to_input_port == 0 or to_input_port == 1) and data == null:
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
var params : Dictionary
|
||||||
|
var url : String
|
||||||
|
|
||||||
|
match to_input_port:
|
||||||
|
|
||||||
|
inputs.url:
|
||||||
|
|
||||||
|
url = str(data)
|
||||||
|
params = await resolve_input_port_value_async(inputs.params)
|
||||||
|
|
||||||
|
inputs.params:
|
||||||
|
|
||||||
|
if not data is Dictionary:
|
||||||
|
return
|
||||||
|
|
||||||
|
params = data
|
||||||
|
url = await resolve_input_port_value_async(inputs.url)
|
||||||
|
|
||||||
|
inputs.trigger:
|
||||||
|
|
||||||
|
url = await resolve_input_port_value_async(inputs.url)
|
||||||
|
params = await resolve_input_port_value_async(inputs.params)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if url == null or not url.begins_with("https://api.twitch.tv/helix/"):
|
||||||
|
return
|
||||||
|
|
||||||
|
var twitch := Connections.twitch
|
||||||
|
var url_string := url
|
||||||
|
|
||||||
|
if not params.is_empty() and not url_string.ends_with("?"):
|
||||||
|
|
||||||
|
url_string += "?"
|
||||||
|
|
||||||
|
|
||||||
|
for key in params:
|
||||||
|
|
||||||
|
var val = params[key]
|
||||||
|
|
||||||
|
url_string += key + "=" + val + "&"
|
||||||
|
|
||||||
|
|
||||||
|
url_string = url_string.trim_suffix("&")
|
||||||
|
|
||||||
|
var resp := twitch.twitch_request(url_string)
|
||||||
|
|
||||||
|
resp.response_received.connect(func(info):
|
||||||
|
|
||||||
|
info.erase("headers")
|
||||||
|
send(0, info)
|
||||||
|
)
|
||||||
|
|
|
@ -32,6 +32,7 @@ const GODOT_TYPES_MAP := {
|
||||||
const INVERSE_GODOT_TYPES_MAP := {
|
const INVERSE_GODOT_TYPES_MAP := {
|
||||||
TYPE_BOOL: Types.BOOL,
|
TYPE_BOOL: Types.BOOL,
|
||||||
TYPE_FLOAT: Types.NUMERIC,
|
TYPE_FLOAT: Types.NUMERIC,
|
||||||
|
TYPE_INT: Types.NUMERIC,
|
||||||
TYPE_STRING: Types.STRING,
|
TYPE_STRING: Types.STRING,
|
||||||
TYPE_ARRAY: Types.ARRAY,
|
TYPE_ARRAY: Types.ARRAY,
|
||||||
TYPE_DICTIONARY: Types.DICTIONARY,
|
TYPE_DICTIONARY: Types.DICTIONARY,
|
||||||
|
|
Loading…
Reference in a new issue