Initial commit
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Normalize EOL for all files that Git considers text files.
|
||||||
|
* text=auto eol=lf
|
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Godot 4+ specific ignores
|
||||||
|
.godot/
|
9
Main.tscn
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[gd_scene load_steps=3 format=3 uid="uid://fh070t2wv8xi"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://main.gd" id="1_2diot"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dsp5r4i4h08v2" path="res://src/Emotes.tscn" id="2_s32fb"]
|
||||||
|
|
||||||
|
[node name="Main" type="Node2D"]
|
||||||
|
script = ExtResource("1_2diot")
|
||||||
|
|
||||||
|
[node name="Emotes" parent="." instance=ExtResource("2_s32fb")]
|
22
addons/TwitchGod/TwitchGod.tscn
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
[gd_scene load_steps=7 format=3 uid="uid://bsved37ft3klq"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/twitch_god.gd" id="1_41vp4"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dsev0vyt8vkuf" path="res://addons/TwitchGod/http/HttpClient.tscn" id="2_04lmt"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dxaclxfi6m2gk" path="res://addons/TwitchGod/http/WebsocketClient.tscn" id="2_tgrgj"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bn2omqaosdoqu" path="res://addons/TwitchGod/http/TwitchSetting.tscn" id="2_v0pcb"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://d7mhkh8sua4x" path="res://addons/TwitchGod/http/HttpServer.tscn" id="4_fhjvp"]
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/auth.gd" id="6_dmql6"]
|
||||||
|
|
||||||
|
[node name="TwitchGod" type="Node"]
|
||||||
|
script = ExtResource("1_41vp4")
|
||||||
|
|
||||||
|
[node name="TwitchSetting" parent="." instance=ExtResource("2_v0pcb")]
|
||||||
|
|
||||||
|
[node name="HttpServer" parent="." instance=ExtResource("4_fhjvp")]
|
||||||
|
|
||||||
|
[node name="WebsocketClient" parent="." instance=ExtResource("2_tgrgj")]
|
||||||
|
|
||||||
|
[node name="HttpClient" parent="." instance=ExtResource("2_04lmt")]
|
||||||
|
|
||||||
|
[node name="Auth" type="Node" parent="."]
|
||||||
|
script = ExtResource("6_dmql6")
|
19
addons/TwitchGod/auth.gd
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
const BROADCASTER_ID = "108501499"
|
||||||
|
const USER_ID = "108501499"
|
||||||
|
const CLIENT_SECRET = "ttvw03u4bel912zexo1u9njv790s53"
|
||||||
|
const CLIENT_ID = "vj5ibe8zguys0agxy52big86cbdjxs"
|
||||||
|
const PORT = 7178
|
||||||
|
const SCOPE = "moderator%3Amanage%3Ashoutouts+channel%3Aread%3Apolls+channel%3Amanage%3Apolls+user%3Aread%3Achat+user%3Awrite%3Achat+channel%3Aread%3Aredemptions+channel%3Amanage%3Aredemptions+channel%3Aread%3Apolls"
|
||||||
|
const SUBS = [
|
||||||
|
TwitchEvents.Event.CHANNEL_CHAT_MESSAGE,
|
||||||
|
TwitchEvents.Event.CHANNEL_POLL_BEGIN,
|
||||||
|
TwitchEvents.Event.CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_REDEMPTION_ADD,
|
||||||
|
TwitchEvents.Event.CHANNEL_POLL_BEGIN,
|
||||||
|
TwitchEvents.Event.CHANNEL_POLL_PROGRESS,
|
||||||
|
TwitchEvents.Event.CHANNEL_POLL_END
|
||||||
|
]
|
||||||
|
const SCOPES = [
|
||||||
|
""
|
||||||
|
]
|
6
addons/TwitchGod/http/HttpClient.tscn
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://dsev0vyt8vkuf"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/http/http_client.gd" id="1_prokr"]
|
||||||
|
|
||||||
|
[node name="HttpClient" type="Node"]
|
||||||
|
script = ExtResource("1_prokr")
|
6
addons/TwitchGod/http/HttpServer.tscn
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://d7mhkh8sua4x"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/http/http_server.gd" id="1_im1bn"]
|
||||||
|
|
||||||
|
[node name="HttpServer" type="Node"]
|
||||||
|
script = ExtResource("1_im1bn")
|
6
addons/TwitchGod/http/TwitchSetting.tscn
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bn2omqaosdoqu"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/http/twitch_setting.gd" id="1_qpw3i"]
|
||||||
|
|
||||||
|
[node name="TwitchSetting" type="Node"]
|
||||||
|
script = ExtResource("1_qpw3i")
|
6
addons/TwitchGod/http/WebsocketClient.tscn
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://dxaclxfi6m2gk"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/http/websocket_client.gd" id="1_2ut2u"]
|
||||||
|
|
||||||
|
[node name="WebsocketClient" type="Node"]
|
||||||
|
script = ExtResource("1_2ut2u")
|
118
addons/TwitchGod/http/http_client.gd
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
extends Node
|
||||||
|
class_name HttpClient
|
||||||
|
|
||||||
|
signal request_completed(type: API_TYPE, data: Dictionary)
|
||||||
|
|
||||||
|
const API_URL = "https://api.twitch.tv/helix"
|
||||||
|
|
||||||
|
enum API_TYPE {
|
||||||
|
AUTH,
|
||||||
|
CREATE_EVENTSUB,
|
||||||
|
VERIFY,
|
||||||
|
SEND_CHAT_MESSAGE,
|
||||||
|
SHOUTOUT,
|
||||||
|
USERS,
|
||||||
|
EMOTE
|
||||||
|
}
|
||||||
|
|
||||||
|
var auth
|
||||||
|
|
||||||
|
func send_message(message: String):
|
||||||
|
var parent = get_parent()
|
||||||
|
|
||||||
|
request(API_TYPE.SEND_CHAT_MESSAGE, {}, {
|
||||||
|
"broadcaster_id": parent.auth.BROADCASTER_ID,
|
||||||
|
"sender_id": parent.auth.USER_ID,
|
||||||
|
"message": message
|
||||||
|
})
|
||||||
|
|
||||||
|
func request(type: API_TYPE, arguments: Dictionary = {}, data: Dictionary = {}):
|
||||||
|
var path = _get_path_from_type(type, arguments)
|
||||||
|
var headers = _get_headers_from_type(type)
|
||||||
|
var method = HTTPClient.METHOD_POST
|
||||||
|
|
||||||
|
if type == API_TYPE.VERIFY:
|
||||||
|
headers.push_back("Authorization: OAuth %s" % arguments.token)
|
||||||
|
auth = arguments.token
|
||||||
|
method = HTTPClient.METHOD_GET
|
||||||
|
if type == API_TYPE.USERS:
|
||||||
|
method = HTTPClient.METHOD_GET
|
||||||
|
if type == API_TYPE.EMOTE:
|
||||||
|
method = HTTPClient.METHOD_GET
|
||||||
|
|
||||||
|
var request = HTTPRequest.new()
|
||||||
|
add_child(request)
|
||||||
|
#prints(path, headers, method, JSON.stringify(data))
|
||||||
|
if method == HTTPClient.METHOD_GET:
|
||||||
|
request.request(path, headers, method)
|
||||||
|
else:
|
||||||
|
request.request(path, headers, method, JSON.stringify(data))
|
||||||
|
request.request_completed.connect(_on_request_completed.bind(type))
|
||||||
|
|
||||||
|
var response = await request.request_completed
|
||||||
|
|
||||||
|
if response[2].has("Content-Type: image/png"):
|
||||||
|
return response[3]
|
||||||
|
|
||||||
|
var string = ""
|
||||||
|
for character in response[3]:
|
||||||
|
string += char(character)
|
||||||
|
|
||||||
|
var json = JSON.parse_string(string)
|
||||||
|
|
||||||
|
return json
|
||||||
|
|
||||||
|
|
||||||
|
func _on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, type: API_TYPE):
|
||||||
|
var string = ""
|
||||||
|
for character in body:
|
||||||
|
string += char(character)
|
||||||
|
|
||||||
|
if headers.has("Content-Type: application/json"):
|
||||||
|
var json = JSON.parse_string(string)
|
||||||
|
|
||||||
|
request_completed.emit(type, json)
|
||||||
|
|
||||||
|
func _get_path_from_type(type: API_TYPE, arguments: Dictionary):
|
||||||
|
var comp_args = ""
|
||||||
|
|
||||||
|
for argument in arguments:
|
||||||
|
if comp_args == "":
|
||||||
|
comp_args += "?%s=%s" % [argument, arguments[argument]]
|
||||||
|
else:
|
||||||
|
comp_args += "&%s=%s" % [argument, arguments[argument]]
|
||||||
|
|
||||||
|
match type:
|
||||||
|
API_TYPE.AUTH:
|
||||||
|
return "https://id.twitch.tv/oauth2/token" + comp_args
|
||||||
|
API_TYPE.CREATE_EVENTSUB:
|
||||||
|
return "%s/eventsub/subscriptions" % [API_URL]
|
||||||
|
API_TYPE.VERIFY:
|
||||||
|
return "https://id.twitch.tv/oauth2/validate"
|
||||||
|
API_TYPE.SEND_CHAT_MESSAGE:
|
||||||
|
return "%s/chat/messages" % [API_URL] + comp_args
|
||||||
|
API_TYPE.USERS:
|
||||||
|
return "%s/users" % [API_URL] + comp_args
|
||||||
|
API_TYPE.SHOUTOUT:
|
||||||
|
return "%s/chat/shoutouts" % [API_URL] + comp_args
|
||||||
|
API_TYPE.EMOTE:
|
||||||
|
return "https://static-cdn.jtvnw.net/emoticons/v2/%s/static/dark/1.0" % [arguments.id]
|
||||||
|
|
||||||
|
|
||||||
|
func _get_headers_from_type(type: API_TYPE):
|
||||||
|
match type:
|
||||||
|
API_TYPE.AUTH:
|
||||||
|
return []
|
||||||
|
API_TYPE.VERIFY:
|
||||||
|
return []
|
||||||
|
_:
|
||||||
|
return [_get_bearer(), _get_client_id(), "Content-Type: application/json"]
|
||||||
|
|
||||||
|
|
||||||
|
func _get_bearer():
|
||||||
|
return "Authorization: Bearer %s" % [auth]
|
||||||
|
|
||||||
|
|
||||||
|
func _get_client_id():
|
||||||
|
var parent = get_parent()
|
||||||
|
return "Client-Id: %s" % [parent.auth.CLIENT_ID]
|
78
addons/TwitchGod/http/http_server.gd
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
var server: TCPServer
|
||||||
|
|
||||||
|
signal started
|
||||||
|
signal received(auth)
|
||||||
|
|
||||||
|
var clients: Array[StreamPeerTCP] = []
|
||||||
|
|
||||||
|
var listening = false
|
||||||
|
|
||||||
|
func start():
|
||||||
|
var port = get_parent().auth.PORT
|
||||||
|
print(port)
|
||||||
|
server = TCPServer.new()
|
||||||
|
server.listen(port)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
if !server: return
|
||||||
|
if(!server.is_listening()): return;
|
||||||
|
|
||||||
|
if not listening:
|
||||||
|
print("SERVER STARTED")
|
||||||
|
started.emit()
|
||||||
|
listening = true
|
||||||
|
|
||||||
|
if(server.is_connection_available()):
|
||||||
|
_handle_connect();
|
||||||
|
|
||||||
|
for client in clients:
|
||||||
|
_process_request(client);
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_connect():
|
||||||
|
var client := server.take_connection()
|
||||||
|
clients.push_back(client)
|
||||||
|
print("CONNECT")
|
||||||
|
|
||||||
|
|
||||||
|
func _process_request(client: StreamPeerTCP):
|
||||||
|
match client.get_status():
|
||||||
|
StreamPeerTCP.STATUS_CONNECTED:
|
||||||
|
client.poll()
|
||||||
|
if client.get_available_bytes() > 0:
|
||||||
|
var string = client.get_utf8_string(client.get_available_bytes())
|
||||||
|
var split = string.split("\n")
|
||||||
|
var first = split[0]
|
||||||
|
var regex = RegEx.new()
|
||||||
|
regex.compile("code=(.*)&")
|
||||||
|
var result = regex.search(first)
|
||||||
|
if result:
|
||||||
|
var auth = result.get_string(1)
|
||||||
|
_send_response(client, "200 OK", "<html><head><title>Login</title><script>window.close()</script></head><body>Success!</body></html>".to_utf8_buffer());
|
||||||
|
received.emit(auth)
|
||||||
|
|
||||||
|
func auth(arguments: Dictionary):
|
||||||
|
var comp_args = ""
|
||||||
|
|
||||||
|
for argument in arguments:
|
||||||
|
if comp_args == "":
|
||||||
|
comp_args += "?%s=%s" % [argument, arguments[argument]]
|
||||||
|
else:
|
||||||
|
comp_args += "&%s=%s" % [argument, arguments[argument]]
|
||||||
|
|
||||||
|
OS.shell_open("https://id.twitch.tv/oauth2/authorize%s" % [comp_args])
|
||||||
|
|
||||||
|
|
||||||
|
func _send_response(client, response_code : String, body : PackedByteArray) -> void:
|
||||||
|
client.put_data(("HTTP/1.1 %s\r\n" % response_code).to_utf8_buffer())
|
||||||
|
client.put_data("Server: Godot Engine\r\n".to_utf8_buffer())
|
||||||
|
client.put_data(("Content-Length: %d\r\n"% body.size()).to_utf8_buffer())
|
||||||
|
client.put_data("Connection: close\r\n".to_utf8_buffer())
|
||||||
|
client.put_data("Content-Type: text/html; charset=UTF-8\r\n".to_utf8_buffer())
|
||||||
|
client.put_data("\r\n".to_utf8_buffer())
|
||||||
|
client.put_data(body)
|
||||||
|
client.disconnect_from_host();
|
24
addons/TwitchGod/http/twitch_setting.gd
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
@tool
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
add_custom_project_setting("twitch_god/client_id", "Test", TYPE_STRING)
|
||||||
|
print("Test")
|
||||||
|
|
||||||
|
func add_custom_project_setting(name: String, default_value, type: int, hint: int = PROPERTY_HINT_NONE, hint_string: String = ""):
|
||||||
|
#if ProjectSettings.has_setting(name): return
|
||||||
|
|
||||||
|
var setting_info: Dictionary = {
|
||||||
|
"name": name,
|
||||||
|
"type": type,
|
||||||
|
"hint": hint,
|
||||||
|
"hint_string": hint_string
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectSettings.set_setting(name, default_value)
|
||||||
|
ProjectSettings.add_property_info(setting_info)
|
||||||
|
ProjectSettings.set_initial_value(name, default_value)
|
71
addons/TwitchGod/http/websocket_client.gd
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
extends Node
|
||||||
|
class_name WebsocketClient
|
||||||
|
|
||||||
|
signal opened
|
||||||
|
signal received(type: TwitchEvents.Event, data: Dictionary)
|
||||||
|
|
||||||
|
const URL = "wss://eventsub.wss.twitch.tv/ws"
|
||||||
|
|
||||||
|
var socket := WebSocketPeer.new()
|
||||||
|
var connection_state := WebSocketPeer.STATE_CLOSED
|
||||||
|
var id := ""
|
||||||
|
|
||||||
|
|
||||||
|
# -- Built in Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
socket.poll()
|
||||||
|
|
||||||
|
var new_connection_state := socket.get_ready_state()
|
||||||
|
if new_connection_state != connection_state: _change_state(new_connection_state)
|
||||||
|
|
||||||
|
match connection_state:
|
||||||
|
WebSocketPeer.STATE_OPEN:
|
||||||
|
_read_data()
|
||||||
|
|
||||||
|
|
||||||
|
# -- Public Methods
|
||||||
|
|
||||||
|
|
||||||
|
## Open up the websocket to start listening
|
||||||
|
func open():
|
||||||
|
socket.connect_to_url(URL)
|
||||||
|
|
||||||
|
|
||||||
|
# -- Private Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _change_state(new_state: WebSocketPeer.State):
|
||||||
|
match new_state:
|
||||||
|
WebSocketPeer.STATE_OPEN:
|
||||||
|
opened.emit()
|
||||||
|
|
||||||
|
connection_state = new_state
|
||||||
|
|
||||||
|
|
||||||
|
func _read_data():
|
||||||
|
while (socket.get_available_packet_count()):
|
||||||
|
var packet := socket.get_packet()
|
||||||
|
var data = _read_packet(packet)
|
||||||
|
var payload = data.payload
|
||||||
|
|
||||||
|
match data.metadata.message_type:
|
||||||
|
"session_welcome":
|
||||||
|
id = payload.session.id
|
||||||
|
"session_keepalive":
|
||||||
|
pass
|
||||||
|
"session_reconnect":
|
||||||
|
pass
|
||||||
|
_:
|
||||||
|
var event_type = TwitchEvents.get_event_type_from_name(payload.subscription.type)
|
||||||
|
received.emit(event_type, payload.event)
|
||||||
|
|
||||||
|
|
||||||
|
func _read_packet(packet: PackedByteArray):
|
||||||
|
var string = ""
|
||||||
|
for chunk in packet:
|
||||||
|
string += char(chunk)
|
||||||
|
var data = JSON.parse_string(string)
|
||||||
|
|
||||||
|
return data
|
1
addons/TwitchGod/icons/bell.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bell"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>
|
After Width: | Height: | Size: 302 B |
37
addons/TwitchGod/icons/bell.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dbbslha8yjaga"
|
||||||
|
path="res://.godot/imported/bell.svg-b8e0c96cd4e3b89ad8e1d4d55e418910.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/bell.svg"
|
||||||
|
dest_files=["res://.godot/imported/bell.svg-b8e0c96cd4e3b89ad8e1d4d55e418910.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/TwitchGod/icons/bot-message-square.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bot-message-square"><path d="M12 6V2H8"/><path d="m8 18-4 4V8a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2Z"/><path d="M2 12h2"/><path d="M9 11v2"/><path d="M15 11v2"/><path d="M20 12h2"/></svg>
|
After Width: | Height: | Size: 391 B |
37
addons/TwitchGod/icons/bot-message-square.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dgxn1mb2i8ttj"
|
||||||
|
path="res://.godot/imported/bot-message-square.svg-786cdae9f8058aaa187e28cad3dbee61.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/bot-message-square.svg"
|
||||||
|
dest_files=["res://.godot/imported/bot-message-square.svg-786cdae9f8058aaa187e28cad3dbee61.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/TwitchGod/icons/cog.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-cog"><path d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z"/><path d="M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z"/><path d="M12 2v2"/><path d="M12 22v-2"/><path d="m17 20.66-1-1.73"/><path d="M11 10.27 7 3.34"/><path d="m20.66 17-1.73-1"/><path d="m3.34 7 1.73 1"/><path d="M14 12h8"/><path d="M2 12h2"/><path d="m20.66 7-1.73 1"/><path d="m3.34 17 1.73-1"/><path d="m17 3.34-1 1.73"/><path d="m11 13.73-4 6.93"/></svg>
|
After Width: | Height: | Size: 607 B |
37
addons/TwitchGod/icons/cog.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dsr1447417xrq"
|
||||||
|
path="res://.godot/imported/cog.svg-6bc9ddbf8b718bdb9823647c50ba7c4c.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/cog.svg"
|
||||||
|
dest_files=["res://.godot/imported/cog.svg-6bc9ddbf8b718bdb9823647c50ba7c4c.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/TwitchGod/icons/key-round.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-key-round"><path d="M2 18v3c0 .6.4 1 1 1h4v-3h3v-3h2l1.4-1.4a6.5 6.5 0 1 0-4-4Z"/><circle cx="16.5" cy="7.5" r=".5" fill="currentColor"/></svg>
|
After Width: | Height: | Size: 345 B |
37
addons/TwitchGod/icons/key-round.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c1dp2foe4qm3v"
|
||||||
|
path="res://.godot/imported/key-round.svg-09fd537499c75dbded8a700396a22502.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/key-round.svg"
|
||||||
|
dest_files=["res://.godot/imported/key-round.svg-09fd537499c75dbded8a700396a22502.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/TwitchGod/icons/message-square.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-square"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
After Width: | Height: | Size: 290 B |
37
addons/TwitchGod/icons/message-square.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dyg54d6jepjgh"
|
||||||
|
path="res://.godot/imported/message-square.svg-2af3a5bb9409f01fb3cbfc5b7a3dcbd7.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/message-square.svg"
|
||||||
|
dest_files=["res://.godot/imported/message-square.svg-2af3a5bb9409f01fb3cbfc5b7a3dcbd7.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/TwitchGod/icons/pyramid.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pyramid"><path d="M2.5 16.88a1 1 0 0 1-.32-1.43l9-13.02a1 1 0 0 1 1.64 0l9 13.01a1 1 0 0 1-.32 1.44l-8.51 4.86a2 2 0 0 1-1.98 0Z"/><path d="M12 2v20"/></svg>
|
After Width: | Height: | Size: 359 B |
37
addons/TwitchGod/icons/pyramid.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://d1738kasi15th"
|
||||||
|
path="res://.godot/imported/pyramid.svg-b581da31e5fb1851b7edda64e9bc57e8.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/pyramid.svg"
|
||||||
|
dest_files=["res://.godot/imported/pyramid.svg-b581da31e5fb1851b7edda64e9bc57e8.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/TwitchGod/icons/regex.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-regex"><path d="M17 3v10"/><path d="m12.67 5.5 8.66 5"/><path d="m12.67 10.5 8.66-5"/><path d="M9 17a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-2z"/></svg>
|
After Width: | Height: | Size: 370 B |
37
addons/TwitchGod/icons/regex.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dinqgmvqeen8t"
|
||||||
|
path="res://.godot/imported/regex.svg-35497d236d1bbc6045dd332d794afc45.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/regex.svg"
|
||||||
|
dest_files=["res://.godot/imported/regex.svg-35497d236d1bbc6045dd332d794afc45.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/TwitchGod/icons/server.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-server"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"/><rect width="20" height="8" x="2" y="14" rx="2" ry="2"/><line x1="6" x2="6.01" y1="6" y2="6"/><line x1="6" x2="6.01" y1="18" y2="18"/></svg>
|
After Width: | Height: | Size: 405 B |
37
addons/TwitchGod/icons/server.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cix7tp6kf6k44"
|
||||||
|
path="res://.godot/imported/server.svg-b484a4103196fbad974efece4ea210c6.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/server.svg"
|
||||||
|
dest_files=["res://.godot/imported/server.svg-b484a4103196fbad974efece4ea210c6.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/TwitchGod/icons/twitch.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-twitch"><path d="M21 2H3v16h5v4l4-4h5l4-4V2zm-10 9V7m5 4V7"/></svg>
|
After Width: | Height: | Size: 269 B |
37
addons/TwitchGod/icons/twitch.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cupvtgpmau5tl"
|
||||||
|
path="res://.godot/imported/twitch.svg-caa6e01720fb38b32b0fe8194c462f21.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/twitch.svg"
|
||||||
|
dest_files=["res://.godot/imported/twitch.svg-caa6e01720fb38b32b0fe8194c462f21.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/TwitchGod/icons/unplug.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-unplug"><path d="m19 5 3-3"/><path d="m2 22 3-3"/><path d="M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z"/><path d="M7.5 13.5 10 11"/><path d="M10.5 16.5 13 14"/><path d="m12 6 6 6 2.3-2.3a2.4 2.4 0 0 0 0-3.4l-2.6-2.6a2.4 2.4 0 0 0-3.4 0Z"/></svg>
|
After Width: | Height: | Size: 473 B |
37
addons/TwitchGod/icons/unplug.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://donvetfqnw6o4"
|
||||||
|
path="res://.godot/imported/unplug.svg-36958912a145d5a5ae67d5324e3af911.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/TwitchGod/icons/unplug.svg"
|
||||||
|
dest_files=["res://.godot/imported/unplug.svg-36958912a145d5a5ae67d5324e3af911.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
12
addons/TwitchGod/nodes/TwitchChatCommandListener.tscn
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[gd_scene load_steps=3 format=3 uid="uid://cunhrde7dsed6"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/nodes/twitch_chat_command_listener.gd" id="1_y088d"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b2585bqiywbwv" path="res://addons/TwitchGod/nodes/TwitchEventListener.tscn" id="2_p4sf8"]
|
||||||
|
|
||||||
|
[node name="TwitchChatCommandListener" type="Node"]
|
||||||
|
script = ExtResource("1_y088d")
|
||||||
|
|
||||||
|
[node name="TwitchEventListener" parent="." instance=ExtResource("2_p4sf8")]
|
||||||
|
event = 9
|
||||||
|
|
||||||
|
[connection signal="received" from="TwitchEventListener" to="." method="_on_twitch_event_listener_received"]
|
12
addons/TwitchGod/nodes/TwitchChatMessageListener.tscn
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[gd_scene load_steps=3 format=3 uid="uid://ceqp0eqllp4ai"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/nodes/twitch_chat_message_listener.gd" id="1_8blbc"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b2585bqiywbwv" path="res://addons/TwitchGod/nodes/TwitchEventListener.tscn" id="2_23mvm"]
|
||||||
|
|
||||||
|
[node name="TwitchChatMessageListener" type="Node"]
|
||||||
|
script = ExtResource("1_8blbc")
|
||||||
|
|
||||||
|
[node name="TwitchEventListener" parent="." instance=ExtResource("2_23mvm")]
|
||||||
|
event = 9
|
||||||
|
|
||||||
|
[connection signal="received" from="TwitchEventListener" to="." method="_on_twitch_event_listener_received"]
|
12
addons/TwitchGod/nodes/TwitchChatRegexListener.tscn
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[gd_scene load_steps=3 format=3 uid="uid://bln2snjbqkg42"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/nodes/twitch_chat_regex_listener.gd" id="1_snfls"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b2585bqiywbwv" path="res://addons/TwitchGod/nodes/TwitchEventListener.tscn" id="2_lqtl8"]
|
||||||
|
|
||||||
|
[node name="TwitchChatRegexListener" type="Node"]
|
||||||
|
script = ExtResource("1_snfls")
|
||||||
|
|
||||||
|
[node name="TwitchEventListener" parent="." instance=ExtResource("2_lqtl8")]
|
||||||
|
event = 9
|
||||||
|
|
||||||
|
[connection signal="received" from="TwitchEventListener" to="." method="_on_twitch_event_listener_received"]
|
6
addons/TwitchGod/nodes/TwitchEventListener.tscn
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://b2585bqiywbwv"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/nodes/twitch_event_listener.gd" id="1_siryd"]
|
||||||
|
|
||||||
|
[node name="TwitchEventListener" type="Node"]
|
||||||
|
script = ExtResource("1_siryd")
|
12
addons/TwitchGod/nodes/TwitchPointRedemptionListener.tscn
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[gd_scene load_steps=3 format=3 uid="uid://vghgdn7lns0x"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/TwitchGod/nodes/twitch_point_redemption_listener.gd" id="1_l7obv"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b2585bqiywbwv" path="res://addons/TwitchGod/nodes/TwitchEventListener.tscn" id="2_86a3u"]
|
||||||
|
|
||||||
|
[node name="TwitchPointRedemptionListener" type="Node"]
|
||||||
|
script = ExtResource("1_l7obv")
|
||||||
|
|
||||||
|
[node name="TwitchEventListener" parent="." instance=ExtResource("2_86a3u")]
|
||||||
|
event = 36
|
||||||
|
|
||||||
|
[connection signal="received" from="TwitchEventListener" to="." method="_on_twitch_event_listener_received"]
|
26
addons/TwitchGod/nodes/twitch_chat_command_listener.gd
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
@icon("../icons/bot-message-square.svg")
|
||||||
|
class_name TwitchChatCommandListener
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
signal received(data: Dictionary)
|
||||||
|
|
||||||
|
@export var names: Array[String] = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Private Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _on_twitch_event_listener_received(data):
|
||||||
|
var message = data.message.text
|
||||||
|
|
||||||
|
for name in names:
|
||||||
|
if message.begins_with("!%s " % [name]) or message == ("!%s" % [name]):
|
||||||
|
var args = data.message.text.split(" ")
|
||||||
|
|
||||||
|
args.remove_at(0)
|
||||||
|
|
||||||
|
data.args = args
|
||||||
|
data.argcount = args.size()
|
||||||
|
|
||||||
|
received.emit(data)
|
||||||
|
return
|
17
addons/TwitchGod/nodes/twitch_chat_message_listener.gd
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
@icon("../icons/message-square.svg")
|
||||||
|
class_name TwitchChatMessageListener
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
signal received(data: Dictionary)
|
||||||
|
|
||||||
|
@export var messages: Array[String] = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Private Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _on_twitch_event_listener_received(data):
|
||||||
|
var message = data.message.text
|
||||||
|
|
||||||
|
if messages.has(message):
|
||||||
|
received.emit(data)
|
24
addons/TwitchGod/nodes/twitch_chat_regex_listener.gd
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
@icon("../icons/regex.svg")
|
||||||
|
class_name TwitchChatRegexListener
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
signal received(data: Dictionary)
|
||||||
|
|
||||||
|
@export var regexes: Array[String] = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Private Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _on_twitch_event_listener_received(data):
|
||||||
|
var message = data.message.text
|
||||||
|
|
||||||
|
for regex in regexes:
|
||||||
|
var pattern = RegEx.new()
|
||||||
|
pattern.compile(regex)
|
||||||
|
|
||||||
|
var result = pattern.search(message)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
received.emit(data)
|
||||||
|
return
|
24
addons/TwitchGod/nodes/twitch_event_listener.gd
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
@icon("../icons/bell.svg")
|
||||||
|
class_name TwitchEventListener
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
signal received(data: Dictionary)
|
||||||
|
|
||||||
|
@export var event: TwitchEvents.Event
|
||||||
|
|
||||||
|
|
||||||
|
# -- Built in Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
assert(event != null)
|
||||||
|
|
||||||
|
TwitchGod.websocket_client.received.connect(_on_received)
|
||||||
|
|
||||||
|
|
||||||
|
# -- Private Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _on_received(type: TwitchEvents.Event, data: Dictionary):
|
||||||
|
if type == event:
|
||||||
|
received.emit(data);
|
18
addons/TwitchGod/nodes/twitch_point_redemption_listener.gd
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
class_name TwitchPointRedemptionListener
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
signal received(data: Dictionary)
|
||||||
|
|
||||||
|
@export var titles: Array[String] = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Private Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _on_twitch_event_listener_received(data):
|
||||||
|
var reward_title = data.reward.title
|
||||||
|
|
||||||
|
print(reward_title)
|
||||||
|
|
||||||
|
if titles.has(reward_title):
|
||||||
|
received.emit(data)
|
215
addons/TwitchGod/twitch_events.gd
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
@tool
|
||||||
|
class_name TwitchEvents
|
||||||
|
extends Object
|
||||||
|
|
||||||
|
enum Event {
|
||||||
|
AUTOMOD_MESSAGE_HOLD,
|
||||||
|
AUTOMOD_MESSAGE_UPDATE,
|
||||||
|
AUTOMOD_SETTINGS_UPDATE,
|
||||||
|
AUTOMOD_TERMS_UPDATE,
|
||||||
|
CHANNEL_UPDATE,
|
||||||
|
CHANNEL_FOLLOW,
|
||||||
|
CHANNEL_AD_BREAK_BEGIN,
|
||||||
|
CHANNEL_CHAT_CLEAR,
|
||||||
|
CHANNEL_CHAT_CLEAR_USER_MESSAGES,
|
||||||
|
CHANNEL_CHAT_MESSAGE,
|
||||||
|
CHANNEL_CHAT_MESSAGE_DELETE,
|
||||||
|
CHANNEL_CHAT_NOTIFICATION,
|
||||||
|
CHANNEL_CHAT_SETTINGS_UPDATE,
|
||||||
|
CHANNEL_CHAT_USER_MESSAGE_HOLD,
|
||||||
|
CHANNEL_CHAT_USER_MESSAGE_UPDATE,
|
||||||
|
CHANNEL_SUBSCRIBE,
|
||||||
|
CHANNEL_SUBSCRIPTION_END,
|
||||||
|
CHANNEL_SUBSCRIPTION_GIFT,
|
||||||
|
CHANNEL_SUBSCRIPTION_MESSAGE,
|
||||||
|
CHANNEL_CHEER,
|
||||||
|
CHANNEL_RAID,
|
||||||
|
CHANNEL_BAN,
|
||||||
|
CHANNEL_UNBAN,
|
||||||
|
CHANNEL_UNBAN_REQUEST_CREATE,
|
||||||
|
CHANNEL_UNBAN_REQUEST_RESOLVE,
|
||||||
|
CHANNEL_MODERATE,
|
||||||
|
CHANNEL_MODERATOR_ADD,
|
||||||
|
CHANNEL_MODERATOR_REMOVE,
|
||||||
|
CHANNEL_GUEST_STAR_SESSION_BEGIN,
|
||||||
|
CHANNEL_GUEST_STAR_SESSION_END,
|
||||||
|
CHANNEL_GUEST_STAR_GUEST_UPDATE,
|
||||||
|
CHANNEL_GUEST_STAR_SETTINGS_UPDATE,
|
||||||
|
CHANNEL_CHANNEL_POINTS_AUTOMATIC_REWARD_REDEMPTION_ADD,
|
||||||
|
CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_ADD,
|
||||||
|
CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_UPDATE,
|
||||||
|
CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_REMOVE,
|
||||||
|
CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_REDEMPTION_ADD,
|
||||||
|
CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_REDEMPTION_UPDATE,
|
||||||
|
CHANNEL_POLL_BEGIN,
|
||||||
|
CHANNEL_POLL_PROGRESS,
|
||||||
|
CHANNEL_POLL_END,
|
||||||
|
CHANNEL_PREDICTION_BEGIN,
|
||||||
|
CHANNEL_PREDICTION_PROGRESS,
|
||||||
|
CHANNEL_PREDICTION_LOCK,
|
||||||
|
CHANNEL_PREDICTION_END,
|
||||||
|
CHANNEL_SUSPICIOUS_USER_MESSAGE,
|
||||||
|
CHANNEL_SUSPICIOUS_USER_UPDATE,
|
||||||
|
CHANNEL_VIP_ADD,
|
||||||
|
CHANNEL_VIP_REMOVE,
|
||||||
|
CHANNEL_CHARITY_CAMPAIGN_DONATE,
|
||||||
|
CHANNEL_CHARITY_CAMPAIGN_START,
|
||||||
|
CHANNEL_CHARITY_CAMPAIGN_PROGRESS,
|
||||||
|
CHANNEL_CHARITY_CAMPAIGN_STOP,
|
||||||
|
CONDUIT_SHARD_DISABLED,
|
||||||
|
DROP_ENTITLEMENT_GRANT,
|
||||||
|
EXTENSION_BITS_TRANSACTION_CREATE,
|
||||||
|
CHANNEL_GOAL_BEGIN,
|
||||||
|
CHANNEL_GOAL_PROGRESS,
|
||||||
|
CHANNEL_GOAL_END,
|
||||||
|
CHANNEL_HYPE_TRAIN_BEGIN,
|
||||||
|
CHANNEL_HYPE_TRAIN_PROGRESS,
|
||||||
|
CHANNEL_HYPE_TRAIN_END,
|
||||||
|
CHANNEL_SHIELD_MODE_BEGIN,
|
||||||
|
CHANNEL_SHIELD_MODE_END,
|
||||||
|
CHANNEL_SHOUTOUT_CREATE,
|
||||||
|
CHANNEL_SHOUTOUT_RECEIVE,
|
||||||
|
STREAM_ONLINE,
|
||||||
|
STREAM_OFFLINE,
|
||||||
|
USER_AUTHORIZATION_GRANT,
|
||||||
|
USER_AUTHORIZATION_REVOKE,
|
||||||
|
USER_UPDATE,
|
||||||
|
USER_WHISPER_RECEIVED
|
||||||
|
}
|
||||||
|
|
||||||
|
static var _events = [
|
||||||
|
EventInfo.new(Event.AUTOMOD_MESSAGE_HOLD, "automod.message.hold", "1"),
|
||||||
|
EventInfo.new(Event.AUTOMOD_MESSAGE_UPDATE, "automod.message.update", "1"),
|
||||||
|
EventInfo.new(Event.AUTOMOD_SETTINGS_UPDATE, "automod.settings.update", "1"),
|
||||||
|
EventInfo.new(Event.AUTOMOD_TERMS_UPDATE, "automod.terms.update", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_UPDATE, "channel.update", "2"),
|
||||||
|
EventInfo.new(Event.CHANNEL_FOLLOW, "channel.follow", "2"),
|
||||||
|
EventInfo.new(Event.CHANNEL_AD_BREAK_BEGIN, "channel.ad_break.begin", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHAT_CLEAR, "channel.chat.clear", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHAT_CLEAR_USER_MESSAGES, "channel.chat.clear_user_messages", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHAT_MESSAGE, "channel.chat.message", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHAT_MESSAGE_DELETE, "channel.chat.message_delete", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHAT_NOTIFICATION, "channel.chat.notification", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHAT_SETTINGS_UPDATE, "channel.chat_settings.update", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHAT_USER_MESSAGE_HOLD, "channel.chat.user_message_hold", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHAT_USER_MESSAGE_UPDATE, "channel.chat.user_message_update", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SUBSCRIBE, "channel.subscribe", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SUBSCRIPTION_END, "channel.subscription.end", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SUBSCRIPTION_GIFT, "channel.subscription.gift", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SUBSCRIPTION_MESSAGE, "channel.subscription.message", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHEER, "channel.cheer", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_RAID, "channel.raid", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_BAN, "channel.ban", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_UNBAN, "channel.unban", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_UNBAN_REQUEST_CREATE, "channel.unban_request.create", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_UNBAN_REQUEST_RESOLVE, "channel.unban_request.resolve", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_MODERATE, "channel.moderate", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_MODERATOR_ADD, "channel.moderator.add", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_MODERATOR_REMOVE, "channel.moderator.remove", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_GUEST_STAR_SESSION_BEGIN, "channel.guest_star_session.begin", "beta"),
|
||||||
|
EventInfo.new(Event.CHANNEL_GUEST_STAR_SESSION_END, "channel.guest_star_session.end", "beta"),
|
||||||
|
EventInfo.new(Event.CHANNEL_GUEST_STAR_GUEST_UPDATE, "channel.guest_star_guest.update", "beta"),
|
||||||
|
EventInfo.new(Event.CHANNEL_GUEST_STAR_SETTINGS_UPDATE, "channel.guest_star_settings.update", "beta"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHANNEL_POINTS_AUTOMATIC_REWARD_REDEMPTION_ADD, "channel.channel_points_automatic_reward_redemption.add", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_ADD, "channel.channel_points_custom_reward.add", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_UPDATE, "channel.channel_points_custom_reward.update", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_REMOVE, "channel.channel_points_custom_reward.remove", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_REDEMPTION_ADD, "channel.channel_points_custom_reward_redemption.add", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHANNEL_POINTS_CUSTOM_REWARD_REDEMPTION_UPDATE, "channel.channel_points_custom_reward_redemption.update", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_POLL_BEGIN, "channel.poll.begin", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_POLL_PROGRESS, "channel.poll.progress", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_POLL_END, "channel.poll.end", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_PREDICTION_BEGIN, "channel.prediction.begin", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_PREDICTION_PROGRESS, "channel.prediction.progress", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_PREDICTION_LOCK, "channel.prediction.lock", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_PREDICTION_END, "channel.prediction.end", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SUSPICIOUS_USER_MESSAGE, "channel.suspicious_user.message", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SUSPICIOUS_USER_UPDATE, "channel.suspicious_user.update", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_VIP_ADD, "channel.vip.add", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_VIP_REMOVE, "channel.vip.remove", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHARITY_CAMPAIGN_DONATE, "channel.charity_campaign.donate", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHARITY_CAMPAIGN_START, "channel.charity_campaign.start", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHARITY_CAMPAIGN_PROGRESS, "channel.charity_campaign.progress", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_CHARITY_CAMPAIGN_STOP, "channel.charity_campaign.stop", "1"),
|
||||||
|
EventInfo.new(Event.CONDUIT_SHARD_DISABLED, "conduit.shard.disabled", "1"),
|
||||||
|
EventInfo.new(Event.DROP_ENTITLEMENT_GRANT, "drop.entitlement.grant", "1"),
|
||||||
|
EventInfo.new(Event.EXTENSION_BITS_TRANSACTION_CREATE, "extension.bits_transaction.create", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_GOAL_BEGIN, "channel.goal.begin", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_GOAL_PROGRESS, "channel.goal.progress", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_GOAL_END, "channel.goal.end", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_HYPE_TRAIN_BEGIN, "channel.hype_train.begin", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_HYPE_TRAIN_PROGRESS, "channel.hype_train.progress", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_HYPE_TRAIN_END, "channel.hype_train.end", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SHIELD_MODE_BEGIN, "channel.shield_mode.begin", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SHIELD_MODE_END, "channel.shield_mode.end", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SHOUTOUT_CREATE, "channel.shoutout.create", "1"),
|
||||||
|
EventInfo.new(Event.CHANNEL_SHOUTOUT_RECEIVE, "channel.shoutout.receive", "1"),
|
||||||
|
EventInfo.new(Event.STREAM_ONLINE, "stream.online", "1"),
|
||||||
|
EventInfo.new(Event.STREAM_OFFLINE, "stream.offline", "1"),
|
||||||
|
EventInfo.new(Event.USER_AUTHORIZATION_GRANT, "user.authorization.grant", "1"),
|
||||||
|
EventInfo.new(Event.USER_AUTHORIZATION_REVOKE, "user.authorization.revoke", "1"),
|
||||||
|
EventInfo.new(Event.USER_UPDATE, "user.update", "1"),
|
||||||
|
EventInfo.new(Event.USER_WHISPER_RECEIVED, "user.whisper.message", "1")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Public Methods
|
||||||
|
|
||||||
|
|
||||||
|
static func get_event_from_type(type: Event):
|
||||||
|
var result = _events.filter(func(event): return event.type == type)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return result[0]
|
||||||
|
else:
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
static func get_event_from_name(name: String):
|
||||||
|
var result = _events.filter(func(event): return event.name == name)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return result[0]
|
||||||
|
else:
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
static func get_event_type_from_name(name: String):
|
||||||
|
var result = _events.filter(func(event): return event.name == name)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return result[0].type
|
||||||
|
else:
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
# -- Classes
|
||||||
|
|
||||||
|
|
||||||
|
class EventInfo:
|
||||||
|
var type: Event
|
||||||
|
var name: String
|
||||||
|
var version: String
|
||||||
|
var conditions: Array[String]
|
||||||
|
|
||||||
|
|
||||||
|
func _init(type: Event, name: String, version: String, conditions: Array[String] = []):
|
||||||
|
self.type = type
|
||||||
|
self.name = name
|
||||||
|
self.version = version
|
||||||
|
self.conditions = conditions
|
||||||
|
|
||||||
|
|
||||||
|
func generate_sub(arguments: Dictionary):
|
||||||
|
return {
|
||||||
|
"type": self.name,
|
||||||
|
"version": self.version,
|
||||||
|
"condition": { # Change to generalize for all types
|
||||||
|
"broadcaster_user_id": arguments.broadcaster,
|
||||||
|
"user_id": arguments.user
|
||||||
|
},
|
||||||
|
"transport": {
|
||||||
|
"method": "websocket",
|
||||||
|
"session_id": arguments.websocket
|
||||||
|
}
|
||||||
|
}
|
82
addons/TwitchGod/twitch_god.gd
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
@onready var http_client = $"HttpClient"
|
||||||
|
@onready var websocket_client = $"WebsocketClient"
|
||||||
|
@onready var http_server = $"HttpServer"
|
||||||
|
@onready var auth = $"Auth"
|
||||||
|
|
||||||
|
const API_URL = "https://api.twitch.tv/helix"
|
||||||
|
var access_token = null
|
||||||
|
|
||||||
|
|
||||||
|
# -- Built-in Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
print("READY")
|
||||||
|
http_server.started.connect(_on_server_started)
|
||||||
|
http_server.received.connect(_on_server_received)
|
||||||
|
websocket_client.opened.connect(_on_websocket_opened)
|
||||||
|
http_client.request_completed.connect(_on_request_completed)
|
||||||
|
|
||||||
|
http_server.start()
|
||||||
|
|
||||||
|
|
||||||
|
# -- Private Methods
|
||||||
|
|
||||||
|
|
||||||
|
func _on_server_started():
|
||||||
|
websocket_client.open()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_websocket_opened():
|
||||||
|
http_server.auth(
|
||||||
|
{
|
||||||
|
"response_type": "code",
|
||||||
|
"client_id": auth.CLIENT_ID,
|
||||||
|
"redirect_uri": "http://localhost:%d" % [auth.PORT],
|
||||||
|
"scope": auth.SCOPE
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_server_received(auth2):
|
||||||
|
http_client.request(
|
||||||
|
HttpClient.API_TYPE.AUTH,
|
||||||
|
{
|
||||||
|
"grant_type": "authorization_code",
|
||||||
|
"client_id": auth.CLIENT_ID,
|
||||||
|
"redirect_uri": "http://localhost:%d" % [auth.PORT],
|
||||||
|
"code": auth2,
|
||||||
|
"client_secret": auth.CLIENT_SECRET
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_request_completed(type: HttpClient.API_TYPE, data: Dictionary):
|
||||||
|
match type:
|
||||||
|
HttpClient.API_TYPE.AUTH:
|
||||||
|
http_client.request(
|
||||||
|
HttpClient.API_TYPE.VERIFY,
|
||||||
|
{
|
||||||
|
"token": data.access_token
|
||||||
|
}
|
||||||
|
)
|
||||||
|
HttpClient.API_TYPE.VERIFY:
|
||||||
|
if not websocket_client.id:
|
||||||
|
push_error("Tried to use nonexistent websocket id. Please report to Ategon.")
|
||||||
|
return
|
||||||
|
|
||||||
|
for sub in auth.SUBS:
|
||||||
|
var event = TwitchEvents.get_event_from_type(sub)
|
||||||
|
var sub_data = event.generate_sub({
|
||||||
|
"broadcaster": auth.BROADCASTER_ID,
|
||||||
|
"user": auth.USER_ID,
|
||||||
|
"websocket": websocket_client.id
|
||||||
|
})
|
||||||
|
|
||||||
|
http_client.request(
|
||||||
|
HttpClient.API_TYPE.CREATE_EVENTSUB,
|
||||||
|
{},
|
||||||
|
sub_data
|
||||||
|
)
|
12
addons/TwitchGod/twitch_scopes.gd
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
@tool
|
||||||
|
class_name TwitchScopes
|
||||||
|
extends Object
|
||||||
|
|
||||||
|
enum Scope {
|
||||||
|
ANALYTICS_READ_EXTENSIONS,
|
||||||
|
ANALYTICS_READ_GAMES,
|
||||||
|
BITS_READ,
|
||||||
|
CHANNEL_MANAGE_ADS,
|
||||||
|
CHANNEL_READ_ADS,
|
||||||
|
CHANNEL_MANAGE_BROADCAST
|
||||||
|
}
|
1
icon.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="124" height="124" rx="14" fill="#363d52" stroke="#212532" stroke-width="4"/><g transform="scale(.101) translate(122 122)"><g fill="#fff"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 813 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H447l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c3 34 55 34 58 0v-86c-3-34-55-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></g></svg>
|
After Width: | Height: | Size: 949 B |
37
icon.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dyu3h6ohevyaf"
|
||||||
|
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://icon.svg"
|
||||||
|
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
5
main.gd
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
extends Node2D
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
get_viewport().transparent_bg = true
|
43
project.godot
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
; Engine configuration file.
|
||||||
|
; It's best edited using the editor UI and not directly,
|
||||||
|
; since the parameters that go here are not all obvious.
|
||||||
|
;
|
||||||
|
; Format:
|
||||||
|
; [section] ; section goes between []
|
||||||
|
; param=value ; assign values to parameters
|
||||||
|
|
||||||
|
config_version=5
|
||||||
|
|
||||||
|
[application]
|
||||||
|
|
||||||
|
config/name="EmoteWall"
|
||||||
|
run/main_scene="res://Main.tscn"
|
||||||
|
config/features=PackedStringArray("4.3")
|
||||||
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
[autoload]
|
||||||
|
|
||||||
|
TwitchGod="*res://addons/TwitchGod/TwitchGod.tscn"
|
||||||
|
|
||||||
|
[display]
|
||||||
|
|
||||||
|
window/size/transparent=true
|
||||||
|
window/per_pixel_transparency/allowed=true
|
||||||
|
|
||||||
|
[input]
|
||||||
|
|
||||||
|
show_whitelist_popup={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
hide_whitelist_popup={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
enter_text={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
18
src/AddedChannel.tscn
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://dwsnd5vbnrup"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://src/added_channel.gd" id="1_gtue1"]
|
||||||
|
|
||||||
|
[node name="AddedChannel" type="HBoxContainer"]
|
||||||
|
script = ExtResource("1_gtue1")
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="."]
|
||||||
|
custom_minimum_size = Vector2(220, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Test"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "X"
|
||||||
|
|
||||||
|
[connection signal="pressed" from="Button" to="." method="_on_button_pressed"]
|
9
src/Emote.tscn
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://b40miugn8pf1c"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://src/emote.gd" id="1_6boyd"]
|
||||||
|
|
||||||
|
[node name="Emote" type="TextureRect"]
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 40.0
|
||||||
|
pivot_offset = Vector2(20, 20)
|
||||||
|
script = ExtResource("1_6boyd")
|
122
src/Emotes.tscn
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
[gd_scene load_steps=5 format=3 uid="uid://dsp5r4i4h08v2"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://src/emotes.gd" id="1_fhqqu"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b2585bqiywbwv" path="res://addons/TwitchGod/nodes/TwitchEventListener.tscn" id="2_iwst4"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b40miugn8pf1c" path="res://src/Emote.tscn" id="3_52qj6"]
|
||||||
|
[ext_resource type="Script" path="res://src/add_to_whitelist.gd" id="4_quosf"]
|
||||||
|
|
||||||
|
[node name="Emotes" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 0
|
||||||
|
offset_right = 1152.0
|
||||||
|
offset_bottom = 648.0
|
||||||
|
script = ExtResource("1_fhqqu")
|
||||||
|
|
||||||
|
[node name="TwitchEventListener" parent="." instance=ExtResource("2_iwst4")]
|
||||||
|
event = 9
|
||||||
|
|
||||||
|
[node name="Emote" parent="." instance=ExtResource("3_52qj6")]
|
||||||
|
layout_mode = 0
|
||||||
|
|
||||||
|
[node name="AddToWhitelist" type="Control" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -264.0
|
||||||
|
offset_top = -119.0
|
||||||
|
offset_right = 264.0
|
||||||
|
offset_bottom = 119.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
pivot_offset = Vector2(268, 120)
|
||||||
|
script = ExtResource("4_quosf")
|
||||||
|
|
||||||
|
[node name="InsertIdLineEdit" type="LineEdit" parent="AddToWhitelist"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -158.0
|
||||||
|
offset_top = -35.0
|
||||||
|
offset_right = 158.0
|
||||||
|
offset_bottom = -4.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
placeholder_text = "Insert Channel Name"
|
||||||
|
|
||||||
|
[node name="SubmitButton" type="Button" parent="AddToWhitelist"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -140.0
|
||||||
|
offset_top = 9.0
|
||||||
|
offset_right = -58.0
|
||||||
|
offset_bottom = 26.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
text = "Submit"
|
||||||
|
|
||||||
|
[node name="CloseButton" type="Button" parent="AddToWhitelist"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -26.0
|
||||||
|
offset_bottom = 31.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
text = "X"
|
||||||
|
|
||||||
|
[node name="AddLabel" type="Label" parent="AddToWhitelist"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -134.0
|
||||||
|
offset_top = -79.0
|
||||||
|
offset_right = 134.0
|
||||||
|
offset_bottom = -36.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_font_sizes/font_size = 24
|
||||||
|
text = "Add to Whitelist"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="AddedChannels" type="HBoxContainer" parent="AddToWhitelist"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -361.0
|
||||||
|
offset_top = 71.0
|
||||||
|
offset_right = 307.0
|
||||||
|
offset_bottom = 291.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="AddedChannels1" type="VBoxContainer" parent="AddToWhitelist/AddedChannels"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="AddedChannels2" type="VBoxContainer" parent="AddToWhitelist/AddedChannels"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="AddedChannels3" type="VBoxContainer" parent="AddToWhitelist/AddedChannels"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[connection signal="received" from="TwitchEventListener" to="." method="_on_twitch_event_listener_received"]
|
||||||
|
[connection signal="new_whitelist_id" from="AddToWhitelist" to="." method="_on_add_to_whitelist_new_whitelist_id"]
|
||||||
|
[connection signal="focus_entered" from="AddToWhitelist/InsertIdLineEdit" to="AddToWhitelist" method="_on_insert_id_line_edit_focus_entered"]
|
||||||
|
[connection signal="focus_exited" from="AddToWhitelist/InsertIdLineEdit" to="AddToWhitelist" method="_on_insert_id_line_edit_focus_exited"]
|
||||||
|
[connection signal="pressed" from="AddToWhitelist/SubmitButton" to="AddToWhitelist" method="_on_submit_button_pressed"]
|
||||||
|
[connection signal="pressed" from="AddToWhitelist/CloseButton" to="AddToWhitelist" method="_on_close_button_pressed"]
|
83
src/add_to_whitelist.gd
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
signal new_whitelist_id(id: String, name: String)
|
||||||
|
|
||||||
|
@onready var insert_id_line_edit: LineEdit = $InsertIdLineEdit
|
||||||
|
|
||||||
|
var size_tween
|
||||||
|
var line_focused = false
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
scale = Vector2.ZERO
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
if Input.is_action_just_pressed("show_whitelist_popup"):
|
||||||
|
show_popup()
|
||||||
|
elif Input.is_action_just_pressed("hide_whitelist_popup") and not line_focused:
|
||||||
|
hide_popup()
|
||||||
|
elif Input.is_action_just_pressed("enter_text") and line_focused:
|
||||||
|
_on_submit_button_pressed()
|
||||||
|
|
||||||
|
|
||||||
|
func show_popup():
|
||||||
|
if size_tween:
|
||||||
|
size_tween.kill()
|
||||||
|
|
||||||
|
visible = true
|
||||||
|
size_tween = create_tween()
|
||||||
|
size_tween.set_ease(Tween.EASE_OUT)
|
||||||
|
size_tween.set_trans(Tween.TRANS_BACK)
|
||||||
|
size_tween.tween_property(self, "scale", Vector2.ONE, 0.5)
|
||||||
|
|
||||||
|
|
||||||
|
func hide_popup():
|
||||||
|
if size_tween:
|
||||||
|
size_tween.kill()
|
||||||
|
|
||||||
|
size_tween = create_tween()
|
||||||
|
size_tween.set_ease(Tween.EASE_OUT)
|
||||||
|
size_tween.set_trans(Tween.TRANS_QUAD)
|
||||||
|
size_tween.tween_property(self, "scale", Vector2.ZERO, 0.25)
|
||||||
|
size_tween.tween_callback(func():
|
||||||
|
visible = false
|
||||||
|
insert_id_line_edit.clear()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_close_button_pressed() -> void:
|
||||||
|
hide_popup()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_lookup_button_pressed() -> void:
|
||||||
|
OS.shell_open("https://www.streamweasels.com/tools/convert-twitch-username-%20to-user-id/")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_submit_button_pressed() -> void:
|
||||||
|
var value = insert_id_line_edit.text
|
||||||
|
insert_id_line_edit.clear()
|
||||||
|
|
||||||
|
if value.is_valid_int():
|
||||||
|
var data = await TwitchGod.http_client.request(TwitchGod.http_client.API_TYPE.USERS, {
|
||||||
|
"id": value
|
||||||
|
})
|
||||||
|
|
||||||
|
if not data.data.size() == 0:
|
||||||
|
new_whitelist_id.emit(data.data[0].id, data.data[0].display_name)
|
||||||
|
else:
|
||||||
|
var data = await TwitchGod.http_client.request(TwitchGod.http_client.API_TYPE.USERS, {
|
||||||
|
"login": value
|
||||||
|
})
|
||||||
|
|
||||||
|
if not data.data.size() == 0:
|
||||||
|
new_whitelist_id.emit(data.data[0].id, data.data[0].display_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func _on_insert_id_line_edit_focus_entered() -> void:
|
||||||
|
line_focused = true
|
||||||
|
|
||||||
|
|
||||||
|
func _on_insert_id_line_edit_focus_exited() -> void:
|
||||||
|
line_focused = false
|
9
src/added_channel.gd
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
extends HBoxContainer
|
||||||
|
|
||||||
|
signal remove_channel
|
||||||
|
|
||||||
|
@onready var label: Label = $Label
|
||||||
|
|
||||||
|
|
||||||
|
func _on_button_pressed() -> void:
|
||||||
|
remove_channel.emit()
|
16
src/emote.gd
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
extends TextureRect
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
position.y += 100
|
||||||
|
|
||||||
|
var tween = create_tween()
|
||||||
|
tween.set_ease(Tween.EASE_OUT)
|
||||||
|
tween.set_trans(Tween.TRANS_BACK)
|
||||||
|
tween.tween_property(self, "position:y", -100, 1.5).as_relative().from_current()
|
||||||
|
|
||||||
|
var tween2 = create_tween()
|
||||||
|
tween.set_ease(Tween.EASE_IN)
|
||||||
|
tween2.set_trans(Tween.TRANS_BACK)
|
||||||
|
tween2.set_parallel()
|
||||||
|
tween2.tween_property(self, "modulate", Color.TRANSPARENT, 0.5).set_delay(1.25)
|
||||||
|
tween2.tween_property(self, "scale", Vector2(0, 0), 0.5).set_delay(1.25)
|
103
src/emotes.gd
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
@onready var texture_rect = $Emote
|
||||||
|
@onready var added_channels_1: VBoxContainer = $AddToWhitelist/AddedChannels/AddedChannels1
|
||||||
|
@onready var added_channels_2: VBoxContainer = $AddToWhitelist/AddedChannels/AddedChannels2
|
||||||
|
@onready var added_channels_3: VBoxContainer = $AddToWhitelist/AddedChannels/AddedChannels3
|
||||||
|
|
||||||
|
const ADDED_CHANNEL = preload("res://src/AddedChannel.tscn")
|
||||||
|
const EMOTE = preload("res://src/Emote.tscn")
|
||||||
|
var whitelist = []
|
||||||
|
var emote_cache = {}
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_load_from_whitelist()
|
||||||
|
|
||||||
|
|
||||||
|
func _save_to_whitelist():
|
||||||
|
var file = FileAccess.open("user://whitelist.txt", FileAccess.WRITE)
|
||||||
|
|
||||||
|
for channel in whitelist:
|
||||||
|
file.store_line("%s#%s" % [channel.id, channel.name])
|
||||||
|
|
||||||
|
|
||||||
|
func _update_labels():
|
||||||
|
for child in added_channels_1.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
for child in added_channels_2.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
for child in added_channels_3.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
for i in range(0, whitelist.size()):
|
||||||
|
if i < 21:
|
||||||
|
var new_label = ADDED_CHANNEL.instantiate()
|
||||||
|
new_label.remove_channel.connect(_remove_from_whitelist.bind(whitelist[i].id))
|
||||||
|
if i < 7:
|
||||||
|
added_channels_1.add_child(new_label)
|
||||||
|
elif i < 14:
|
||||||
|
added_channels_2.add_child(new_label)
|
||||||
|
elif i < 21:
|
||||||
|
added_channels_3.add_child(new_label)
|
||||||
|
new_label.label.text = whitelist[i].name
|
||||||
|
|
||||||
|
|
||||||
|
func _load_from_whitelist():
|
||||||
|
if FileAccess.file_exists("user://whitelist.txt"):
|
||||||
|
var file = FileAccess.open("user://whitelist.txt", FileAccess.READ)
|
||||||
|
var content = file.get_as_text()
|
||||||
|
var filecontent = content.split("\n")
|
||||||
|
for i in range(0, filecontent.size()):
|
||||||
|
var idsplit = filecontent[i].split("#")
|
||||||
|
if idsplit.size() > 1:
|
||||||
|
whitelist.push_back({
|
||||||
|
"id": idsplit[0],
|
||||||
|
"name": idsplit[1]
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
whitelist = []
|
||||||
|
|
||||||
|
_update_labels()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_twitch_event_listener_received(data):
|
||||||
|
for fragment in data.message.fragments:
|
||||||
|
if fragment.type != "emote":
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not whitelist.has(fragment.emote.owner_id):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not emote_cache.has(fragment.emote.id):
|
||||||
|
var image_buffer = await TwitchGod.http_client.request(HttpClient.API_TYPE.EMOTE, { "id": fragment.emote.id })
|
||||||
|
var image = Image.new()
|
||||||
|
image.load_png_from_buffer(image_buffer)
|
||||||
|
var texture = ImageTexture.create_from_image(image)
|
||||||
|
emote_cache[fragment.emote.id] = texture
|
||||||
|
|
||||||
|
var new_emote = EMOTE.instantiate()
|
||||||
|
new_emote.position.x = randi_range(50, 900)
|
||||||
|
new_emote.position.y = 580
|
||||||
|
new_emote.texture = emote_cache[fragment.emote.id]
|
||||||
|
add_child(new_emote)
|
||||||
|
|
||||||
|
|
||||||
|
func _remove_from_whitelist(id: String):
|
||||||
|
whitelist = whitelist.filter(func (x): return x.id != id)
|
||||||
|
|
||||||
|
_update_labels()
|
||||||
|
_save_to_whitelist()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_add_to_whitelist_new_whitelist_id(id: String, name: String) -> void:
|
||||||
|
if whitelist.filter(func (x): return x.id == id).size():
|
||||||
|
return
|
||||||
|
|
||||||
|
whitelist.push_back({
|
||||||
|
"id": id,
|
||||||
|
"name": name
|
||||||
|
})
|
||||||
|
|
||||||
|
_update_labels()
|
||||||
|
_save_to_whitelist()
|