diff --git a/connectors/odyseeconnector.gd b/connectors/odyseeconnector.gd new file mode 100644 index 0000000..f072849 --- /dev/null +++ b/connectors/odyseeconnector.gd @@ -0,0 +1,8 @@ +extends "res://connectors/baseconnector.gd" + +var display_name : String = "Odysee" +var tooltip : String = "Connect to odysee chat using websockets" +var icon : StreamTexture = preload("res://widgets/odyseechat.png") +var type : String = "odysee" + +var scene : String = "res://connectors/odyseeconnector_ui.tscn" diff --git a/connectors/odyseeconnector_ui.gd b/connectors/odyseeconnector_ui.gd new file mode 100644 index 0000000..e73e632 --- /dev/null +++ b/connectors/odyseeconnector_ui.gd @@ -0,0 +1,60 @@ +extends Control + +var receiver : Node = null +var info : Dictionary = {} + +func set_connection_info(info : Dictionary) -> void: + self.info = info + if info.has("claimid"): + $VBoxContainer/MarginContainerClientId/ClaimId.text = info["claimid"] + +func verify_connection(receiver : Node) -> void: + self.receiver = receiver + var publisher_id = $VBoxContainer/MarginContainerClientId/ClaimId.text + print("verify connection") + $HTTPRequest.connect("request_completed", self, "_on_odysee_verify", [], CONNECT_DEFERRED) + var err = $HTTPRequest.request("https://chainquery.lbry.com/api/sql?query=SELECT%20*%20FROM%20claim%20WHERE%20publisher_id=%22" + publisher_id + "%22%20AND%20bid_state%3C%3E%22Spent%22%20AND%20claim_type=1%20AND%20source_hash%20IS%20NULL%20ORDER%20BY%20id%20DESC%20LIMIT%201") + assert(err==OK) + +func _on_odysee_verify(result, response_code, headers, body) -> void: + $HTTPRequest.disconnect("request_completed", self, "_on_odysee_verify") + var userResponse : JSONParseResult = JSON.parse(body.get_string_from_utf8()) + + if response_code == 200: + var response = { + "error": false, + "name": $VBoxContainer/MarginContainerClientId/ClaimId.text, + "icon": load("res://widgets/odyseechat.png"), + "valid": true, + "type": "odysee", + "claimid": $VBoxContainer/MarginContainerClientId/ClaimId.text + } + + if info: + response["uuid"] = info["uuid"] + + receiver.connection_verified(response) + else: + var response : Dictionary + + if userResponse: + response = { + "error": true, + "message": userResponse.result["message"], + "valid": false + } + else: + response = { + "error": true, + "message": "Temporary connection problem. Please verify your connection.", + "valid": true + } + + if info: + response["uuid"] = info["uuid"] + + + receiver.connection_verified(response) + +func _on_RichTextLabel_meta_clicked(meta): + OS.shell_open(meta) diff --git a/connectors/odyseeconnector_ui.tscn b/connectors/odyseeconnector_ui.tscn new file mode 100644 index 0000000..bc05965 --- /dev/null +++ b/connectors/odyseeconnector_ui.tscn @@ -0,0 +1,46 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://connectors/odyseeconnector_ui.gd" type="Script" id=1] + +[node name="OdyseeConnectorUI" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -1.0 +margin_right = -1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="RichTextLabel" type="RichTextLabel" parent="VBoxContainer"] +margin_right = 1280.0 +margin_bottom = 692.0 +focus_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +bbcode_enabled = true +bbcode_text = "To connect to Odysee. Get your channels claim id from its about page and paste it into the Channel Claim id box." +text = "To connect to Odysee. Get your channels claim id from its about page and paste it into the Channel Claim id box." +selection_enabled = true + +[node name="MarginContainerClientId" type="MarginContainer" parent="VBoxContainer"] +margin_top = 696.0 +margin_right = 1280.0 +margin_bottom = 720.0 + +[node name="ClaimId" type="LineEdit" parent="VBoxContainer/MarginContainerClientId"] +margin_right = 1280.0 +margin_bottom = 24.0 +clear_button_enabled = true +placeholder_text = "Channel Claim ID" + +[node name="HTTPRequest" type="HTTPRequest" parent="."] + +[connection signal="meta_clicked" from="VBoxContainer/RichTextLabel" to="." method="_on_RichTextLabel_meta_clicked"] diff --git a/widgets/odysee/odysee_options.gd b/widgets/odysee/odysee_options.gd index 1341fee..aa46740 100644 --- a/widgets/odysee/odysee_options.gd +++ b/widgets/odysee/odysee_options.gd @@ -6,8 +6,8 @@ var receiver : Node = null func add_config_options(widget_id : String, config : Dictionary) -> void: self.config = config - var claim_id_value = "Place Stream Claim Id Here" - if config.has("claim_id"): claim_id_value = config["claim_id"] + var connection_value = null + if config.has("connection"): connection_value = config["connection"] var text_color_value = "#FFFFFF" if config.has("text_color"): text_color_value = config["text_color"] @@ -30,14 +30,21 @@ func add_config_options(widget_id : String, config : Dictionary) -> void: var tip_color_value = "#FFFFFF" if config.has("tip_color"): tip_color_value = config["tip_color"] + var connections = PasswordStorage.get_secret("connections") + if !connections: connections = [] var items = [] - var claim_id = preload("res://ui/elements/options/lineeditoption.tscn").instance() - $VBoxContainer.add_child(claim_id) - claim_id.set_label("stream claim id:") - claim_id.set_option_name("claim_id") - claim_id.set_value(claim_id_value) - claim_id.set_widget_node(self) + for i in connections: + if i["type"] == "odysee": + items.append({"name": "Odysee: " + i["name"], "value": i["uuid"]}) + + var connection = preload("res://ui/elements/options/dropdownoption.tscn").instance() + $VBoxContainer.add_child(connection) + connection.set_label("Connection:") + connection.set_values(items) + connection.set_option_name("connection") + if connection_value: connection.set_value(connection_value) + connection.set_widget_node(self) var text_color = preload("res://ui/elements/options/coloroption.tscn").instance() $VBoxContainer.add_child(text_color) diff --git a/widgets/odysee/odysee_widget.gd b/widgets/odysee/odysee_widget.gd index c2702ac..4d69dd5 100644 --- a/widgets/odysee/odysee_widget.gd +++ b/widgets/odysee/odysee_widget.gd @@ -13,18 +13,20 @@ var username = null var chat_token = null var compact_mode = false -var claim_id = "8d942740a95b8f212d5e6627a73a6e2943207eb9" +var claim_id = "edf63b41a82a93bad1d53b159af2e7e1fe119a5e" var text_color = Color(255, 255, 255) var tip_color = Color(255, 255, 255) var timestamp_color = Color(255, 255, 255) var username_color = Color(255, 255, 255) var show_timestamps = false +var stream_claim = "" # Apply config given by widget manager func apply_config(widget_id, config): + var connections = PasswordStorage.get_secret("connections") + if !connections: connections = [] if config.has("ratio"): size_flags_stretch_ratio = config["ratio"] - if config.has("claim_id"): claim_id = config["claim_id"] if config.has("text_color"): text_color = config["text_color"] if config.has("tip_color"): tip_color = config["tip_color"] if config.has("username_color"): username_color = config["username_color"] @@ -39,11 +41,51 @@ func apply_config(widget_id, config): dynamic_font.add_fallback(load("res://ui/font/TwitterColorEmoji-SVGinOT.ttf")) set("custom_fonts/normal_font", dynamic_font) - connect_to_odysee() + if config.has("connection"): + for i in connections: + if config["connection"] == i["uuid"]: + claim_id = i["claimid"] + # Called when the node enters the scene tree for the first time. +# Get stream claim id from channel claim id + +func _on_sql_request_completed(result, response_code, headers, body): + print("requesting claim id") + streamClaim.disconnect("request_completed", self, "_on_sql_request_completed") + _client.disconnect("connection_closed", self, "_closed") + var query = JSON.parse(body.get_string_from_utf8()) + + if query.result["data"].size() == 0: + print("Cannot find stream on channel, assuming claim id is for stream ") + stream_claim = claim_id + else: + print("Found stream claim_id " + query.result["data"][0]["claim_id"]) + stream_claim = query.result["data"][0]["claim_id"] + + # Prepare our chat lines array + for i in range(0, 200): + chat_lines.append("\n") + # Connect base signals to get notified of connection open, close, and errors. + _client.connect("connection_closed", self, "_closed") + _client.connect("connection_error", self, "_closed") + _client.connect("connection_established", self, "_connected") + # This signal is emitted when not using the Multiplayer API every time + # a full packet is received. + # Alternatively, you could check get_peer(1).get_available_packets() in a loop. + _client.connect("data_received", self, "_on_data") + + # Initiate connection to the given URL. + var err = _client.connect_to_url("wss://comments.lbry.com/api/v2/live-chat/subscribe?subscription_id=" + stream_claim) + if err != OK: + append_message("Unable to connect to odysee", "#B22222", "info", 0) + print("Unable to connect") + set_process(false) + else: + print("Connected to Odysee using " + stream_claim) + # Connect to Odysee via websockets func connect_to_odysee(): return @@ -71,40 +113,31 @@ func append_message(message, color, sender, tip_amount): chat_lines.append(message) redraw_chat() - + + # Our WebSocketClient instance var _client = WebSocketClient.new() +onready var streamClaim = HTTPRequest.new() func _ready(): print("Odysee chat widget loading!") - - # Prepare our chat lines array - for i in range(0, 200): - chat_lines.append("\n") - # Connect base signals to get notified of connection open, close, and errors. - _client.connect("connection_closed", self, "_closed") - _client.connect("connection_error", self, "_closed") - _client.connect("connection_established", self, "_connected") - # This signal is emitted when not using the Multiplayer API every time - # a full packet is received. - # Alternatively, you could check get_peer(1).get_available_packets() in a loop. - _client.connect("data_received", self, "_on_data") - - # Initiate connection to the given URL. - var err = _client.connect_to_url("wss://comments.lbry.com/api/v2/live-chat/subscribe?subscription_id=" + claim_id) - if err != OK: - print("Unable to connect") - set_process(false) + add_child(streamClaim) + streamClaim.connect("request_completed", self, "_on_sql_request_completed", [], CONNECT_DEFERRED) + print("Using connector: " + claim_id) + var error = streamClaim.request("https://chainquery.lbry.com/api/sql?query=SELECT%20*%20FROM%20claim%20WHERE%20publisher_id=%22" + claim_id + "%22%20AND%20bid_state%3C%3E%22Spent%22%20AND%20claim_type=1%20AND%20source_hash%20IS%20NULL%20ORDER%20BY%20id%20DESC%20LIMIT%201") func _closed(was_clean = false): # was_clean will tell you if the disconnection was correctly notified # by the remote peer before closing the socket. + append_message("Disconnected from odysee,", "#B22222", "info", 0) print("Closed, clean: ", was_clean) set_process(false) + _client.poll() func _connected(proto = ""): # This is called on connection, "proto" will be the selected WebSocket # sub-protocol (which is optional) + append_message("Connected to Odysee", "#B22222", "info", 0) print("Connected with protocol: ", proto) # You MUST always use get_peer(1).put_packet to send data to server, # and not put_packet directly when not using the MultiplayerAPI.