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_raw
|
||||
signal chat_received_rich
|
||||
signal irc_received
|
||||
|
||||
signal chat_connected
|
||||
|
||||
|
@ -16,7 +17,7 @@ var user_regex := RegEx.new()
|
|||
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.connect(owner.check_chat_socket)
|
||||
|
@ -96,7 +97,7 @@ func parse_chat_msg(msg : String, tags : Dictionary):
|
|||
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)
|
||||
#(__username_regex.search(split[0]).get_string(1), split[3].right(1), split[2], tags)
|
||||
|
||||
if !tags.is_empty():
|
||||
|
||||
|
@ -112,18 +113,14 @@ func parse_chat_msg(msg : String, tags : Dictionary):
|
|||
prints("Connection Established", msg)
|
||||
chat_connected.emit()
|
||||
|
||||
# Chat Joining Message
|
||||
"JOIN":
|
||||
|
||||
pass
|
||||
|
||||
# Chat Leaving Message
|
||||
"PART":
|
||||
|
||||
pass
|
||||
|
||||
# General IRC Messages, Notices etc.
|
||||
_:
|
||||
|
||||
irc_received.emit(msg)
|
||||
|
||||
|
||||
|
||||
#@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.
|
||||
|
@ -150,7 +147,7 @@ func send_chat(msg : String, channel : String = ""):
|
|||
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
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():
|
||||
|
||||
$Twitch_Connection.token_received.connect(save_token)
|
||||
twitch.token_received.connect(save_token)
|
||||
%Get_User.pressed.connect(func():
|
||||
|
||||
var resp = %Twitch_Connection.request_user_info()
|
||||
|
@ -25,13 +26,20 @@ func _ready():
|
|||
|
||||
%Connect_Channel_Points.pressed.connect(func():
|
||||
|
||||
await %Twitch_Connection.setup_eventsub_connection()
|
||||
await twitch.setup_eventsub_connection()
|
||||
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)
|
||||
|
||||
)
|
||||
%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()
|
||||
|
|
|
@ -91,6 +91,11 @@ unique_name_in_owner = true
|
|||
layout_mode = 2
|
||||
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="."]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("1_13a4v")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
extends Button
|
||||
|
||||
|
||||
@onready var twitch_connection : Twitch_Connection = $"../../Twitch_Connection"
|
||||
@onready var twitch_connection : No_Twitch = $"../../Twitch_Connection"
|
||||
|
||||
func _pressed():
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ const eventsub_url := "wss://eventsub.wss.twitch.tv/ws"
|
|||
## Stores the "session id" for this EventSub connection.
|
||||
var session_id
|
||||
|
||||
var connection : Twitch_Connection
|
||||
var connection : No_Twitch
|
||||
|
||||
signal notif_received(data)
|
||||
|
||||
|
@ -14,7 +14,7 @@ signal welcome_received()
|
|||
var keepalive_timer := 0
|
||||
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
|
||||
|
||||
|
||||
|
@ -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.
|
||||
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)
|
||||
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.
|
||||
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:
|
||||
|
||||
|
@ -72,7 +72,7 @@ func subscribe_to_events(events : Array[Twitch_Connection.EventSub_Subscription]
|
|||
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:
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
extends Node
|
||||
class_name Twitch_Connection
|
||||
class_name No_Twitch
|
||||
|
||||
## Handler for a Connection to a Single Twitch Account
|
||||
##
|
||||
|
@ -113,7 +113,7 @@ func setup_eventsub_connection(events : Array[EventSub_Subscription] = [], timeo
|
|||
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.
|
||||
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:
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -181,15 +181,10 @@ func create_auth_url(port := redirect_port, id : String = client_id, redirect :
|
|||
return url
|
||||
|
||||
## 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]
|
||||
|
||||
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:
|
||||
|
||||
|
@ -326,19 +321,28 @@ class HTTPResponse:
|
|||
info["headers"] = headers
|
||||
|
||||
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
|
||||
|
||||
|
||||
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")
|
||||
emit_response(info, http, storage)
|
||||
|
||||
func emit_response(info, http, storage):
|
||||
|
||||
inf_data = info
|
||||
response_received.emit(info)
|
||||
|
||||
|
||||
http.queue_free()
|
||||
storage.erase(self)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ func _init():
|
|||
|
||||
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, "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)
|
||||
|
||||
#{
|
||||
|
@ -55,7 +55,7 @@ func _value_request(from_port):
|
|||
|
||||
2: # Profile Picture URL
|
||||
|
||||
return user_dict.profile_image_url
|
||||
return user_dict.id
|
||||
|
||||
|
||||
_: # Dictionary
|
||||
|
|
|
@ -12,7 +12,7 @@ enum inputs {
|
|||
|
||||
}
|
||||
|
||||
var subscription_data : Twitch_Connection.EventSub_Subscription
|
||||
var subscription_data : No_Twitch.EventSub_Subscription
|
||||
|
||||
func _init():
|
||||
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.
|
||||
subscription_data = Twitch_Connection.EventSub_Subscription.new(event, sub_data)
|
||||
# Creates an instance of No_Twitch.EventSub_Subscription to store the data with all the given inputs.
|
||||
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
|
||||
if sub_data.has("version"):
|
||||
|
||||
|
@ -81,7 +81,7 @@ func _receive(to_input_port, data: Variant) -> void:
|
|||
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):
|
||||
|
||||
match data.code:
|
||||
|
|
|
@ -14,15 +14,17 @@ func _init():
|
|||
node_type = "twitch_chat_received"
|
||||
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(
|
||||
DeckType.Types.BOOL,
|
||||
"On receive"
|
||||
DeckType.Types.ANY,
|
||||
"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
|
||||
|
||||
var id = UUID.v4()
|
||||
send(0, username, id)
|
||||
send(1, message, id)
|
||||
send(2, channel, id)
|
||||
send(3, tags, id)
|
||||
send(4, true, id)
|
||||
|
||||
send(0, null, id)
|
||||
send(1, username, id)
|
||||
send(2, message, id)
|
||||
send(3, channel, id)
|
||||
send(4, tags, id)
|
||||
|
||||
|
||||
func _value_request(on_port: int) -> Variant:
|
||||
match on_port:
|
||||
0:
|
||||
return username
|
||||
1:
|
||||
return message
|
||||
return username
|
||||
2:
|
||||
return channel
|
||||
return message
|
||||
3:
|
||||
return channel
|
||||
4:
|
||||
return tags
|
||||
_:
|
||||
0, _:
|
||||
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 := {
|
||||
TYPE_BOOL: Types.BOOL,
|
||||
TYPE_FLOAT: Types.NUMERIC,
|
||||
TYPE_INT: Types.NUMERIC,
|
||||
TYPE_STRING: Types.STRING,
|
||||
TYPE_ARRAY: Types.ARRAY,
|
||||
TYPE_DICTIONARY: Types.DICTIONARY,
|
||||
|
|
Loading…
Reference in a new issue