fixed #20
This commit is contained in:
parent
1f1ad16e6f
commit
18450d661d
11
Gift.gd
11
Gift.gd
@ -3,7 +3,7 @@ extends Gift
|
|||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
cmd_no_permission.connect(no_permission)
|
cmd_no_permission.connect(no_permission)
|
||||||
chat_message.connect(on_chat)
|
chat_message.connect(on_chat)
|
||||||
channel_follow.connect(on_follow)
|
event.connect(on_event)
|
||||||
|
|
||||||
# I use a file in the working directory to store auth data
|
# I use a file in the working directory to store auth data
|
||||||
# so that I don't accidentally push it to the repository.
|
# so that I don't accidentally push it to the repository.
|
||||||
@ -24,8 +24,11 @@ func _ready() -> void:
|
|||||||
if (success):
|
if (success):
|
||||||
request_caps()
|
request_caps()
|
||||||
join_channel(initial_channel)
|
join_channel(initial_channel)
|
||||||
events.append("channel.follow")
|
|
||||||
await(connect_to_eventsub())
|
await(connect_to_eventsub())
|
||||||
|
# Refer to https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types/ for details on
|
||||||
|
# what events exist, which API versions are available and which conditions are required.
|
||||||
|
# Make sure your token has all required scopes for the event.
|
||||||
|
subscribe_event("channel.follow", 2, {"broadcaster_user_id": user_id, "moderator_user_id": user_id})
|
||||||
|
|
||||||
# Adds a command with a specified permission flag.
|
# Adds a command with a specified permission flag.
|
||||||
# All implementations must take at least one arg for the command info.
|
# All implementations must take at least one arg for the command info.
|
||||||
@ -75,7 +78,9 @@ func _ready() -> void:
|
|||||||
# Send a whisper to target user
|
# Send a whisper to target user
|
||||||
# whisper("TEST", initial_channel)
|
# whisper("TEST", initial_channel)
|
||||||
|
|
||||||
func on_follow(data : Dictionary) -> void:
|
func on_event(type : String, data : Dictionary) -> void:
|
||||||
|
match(type):
|
||||||
|
"channel.follow":
|
||||||
print("%s followed your channel!" % data["user_name"])
|
print("%s followed your channel!" % data["user_name"])
|
||||||
|
|
||||||
func on_chat(data : SenderData, msg : String) -> void:
|
func on_chat(data : SenderData, msg : String) -> void:
|
||||||
|
@ -15,6 +15,7 @@ grow_vertical = 2
|
|||||||
|
|
||||||
[node name="Gift" type="Node" parent="."]
|
[node name="Gift" type="Node" parent="."]
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
|
scopes = Array[String](["chat:edit", "chat:read", "moderator:read:followers"])
|
||||||
|
|
||||||
[node name="ChatContainer" type="VBoxContainer" parent="."]
|
[node name="ChatContainer" type="VBoxContainer" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
15
README.md
15
README.md
@ -27,7 +27,7 @@ extends Gift
|
|||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
cmd_no_permission.connect(no_permission)
|
cmd_no_permission.connect(no_permission)
|
||||||
chat_message.connect(on_chat)
|
chat_message.connect(on_chat)
|
||||||
channel_follow.connect(on_follow)
|
event.connect(on_event)
|
||||||
|
|
||||||
# I use a file in the working directory to store auth data
|
# I use a file in the working directory to store auth data
|
||||||
# so that I don't accidentally push it to the repository.
|
# so that I don't accidentally push it to the repository.
|
||||||
@ -48,8 +48,11 @@ func _ready() -> void:
|
|||||||
if (success):
|
if (success):
|
||||||
request_caps()
|
request_caps()
|
||||||
join_channel(initial_channel)
|
join_channel(initial_channel)
|
||||||
events.append("channel.follow")
|
|
||||||
await(connect_to_eventsub())
|
await(connect_to_eventsub())
|
||||||
|
# Refer to https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types/ for details on
|
||||||
|
# what events exist, which API versions are available and which conditions are required.
|
||||||
|
# Make sure your token has all required scopes for the event.
|
||||||
|
subscribe_event("channel.follow", 2, {"broadcaster_user_id": user_id, "moderator_user_id": user_id})
|
||||||
|
|
||||||
# Adds a command with a specified permission flag.
|
# Adds a command with a specified permission flag.
|
||||||
# All implementations must take at least one arg for the command info.
|
# All implementations must take at least one arg for the command info.
|
||||||
@ -99,7 +102,9 @@ func _ready() -> void:
|
|||||||
# Send a whisper to target user
|
# Send a whisper to target user
|
||||||
# whisper("TEST", initial_channel)
|
# whisper("TEST", initial_channel)
|
||||||
|
|
||||||
func on_follow(data : Dictionary) -> void:
|
func on_event(type : String, data : Dictionary) -> void:
|
||||||
|
match(type):
|
||||||
|
"channel.follow":
|
||||||
print("%s followed your channel!" % data["user_name"])
|
print("%s followed your channel!" % data["user_name"])
|
||||||
|
|
||||||
func on_chat(data : SenderData, msg : String) -> void:
|
func on_chat(data : SenderData, msg : String) -> void:
|
||||||
@ -174,9 +179,7 @@ func list(cmd_info : CommandInfo, arg_ary : PackedStringArray) -> void:
|
|||||||
|events_id(id)|id(String))|The id has been received from the welcome message.|
|
|events_id(id)|id(String))|The id has been received from the welcome message.|
|
||||||
|events_reconnect|-|Twitch directed the bot to reconnect to a different URL.|
|
|events_reconnect|-|Twitch directed the bot to reconnect to a different URL.|
|
||||||
|events_revoked|event(String), reason(String)|Twitch revoked a event subscription|
|
|events_revoked|event(String), reason(String)|Twitch revoked a event subscription|
|
||||||
|
|event|type(String), data(Dictionary)|A subscribed eventsub event omitted data.|
|
||||||
Events from EventSub are named just like their subscription name, with all '.' replaced by '_'.
|
|
||||||
Example: channel.follow emits the signal channel_follow(data(Dictionary))
|
|
||||||
***
|
***
|
||||||
|
|
||||||
### Functions:
|
### Functions:
|
||||||
|
@ -44,52 +44,8 @@ signal events_reconnect
|
|||||||
# Twitch revoked a event subscription
|
# Twitch revoked a event subscription
|
||||||
signal events_revoked(event, reason)
|
signal events_revoked(event, reason)
|
||||||
|
|
||||||
# Currently supported Twitch events. Refer to https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types/ for details.
|
# Refer to https://dev.twitch.tv/docs/eventsub/eventsub-reference/ data contained in the data dictionary.
|
||||||
#signal channel_update
|
signal event(type, data)
|
||||||
signal channel_follow(event_dict) # Beta, Twitch might change the API.
|
|
||||||
signal channel_subscribe(event_dict)
|
|
||||||
#signal channel_subscription_end
|
|
||||||
signal channel_subscription_gift(event_dict)
|
|
||||||
signal channel_subscription_message(event_dict)
|
|
||||||
signal channel_cheer(event_dict)
|
|
||||||
#signal channel_raid
|
|
||||||
#signal channel_ban
|
|
||||||
#signal channel_unban
|
|
||||||
#signal channel_moderator_add
|
|
||||||
#signal channel_moderator_remove
|
|
||||||
#signal channel_points_custom_reward_add
|
|
||||||
#signal channel_points_custom_reward_update
|
|
||||||
#signal channel_points_custom_reward_remove
|
|
||||||
signal channel_points_custom_reward_redemption_add(event_dict)
|
|
||||||
signal channel_points_custom_reward_redemption_update(event_dict)
|
|
||||||
#signal channel_poll_begin
|
|
||||||
#signal channel_poll_progress
|
|
||||||
#signal channel_poll_end
|
|
||||||
#signal channel_prediction_begin
|
|
||||||
#signal channel_prediction_progress
|
|
||||||
#signal channel_prediction_lock
|
|
||||||
#signal channel_prediction_end
|
|
||||||
signal channel_charity_campaign_donate(event_dict)
|
|
||||||
#signal channel_charity_campaign_start
|
|
||||||
#signal channel_charity_campaign_progress
|
|
||||||
#signal channel_charity_campaign_stop
|
|
||||||
#signal drop_entitlement_grant
|
|
||||||
signal extension_bits_transaction_create(event_dict)
|
|
||||||
#signal channel_goal_begin
|
|
||||||
#signal channel_goal_progress
|
|
||||||
#signal channel_goal_end
|
|
||||||
#signal channel_hype_train_begin
|
|
||||||
#signal channel_hype_train_progress
|
|
||||||
#signal channel_hype_train_end
|
|
||||||
#signal channel_shield_mode_begin
|
|
||||||
#signal channel_shield_mode_end
|
|
||||||
#signal channel_shoutout_create
|
|
||||||
#signal channel_shoutout_receive
|
|
||||||
#signal stream_online
|
|
||||||
#signal stream_offline
|
|
||||||
#signal user_authorization_grant
|
|
||||||
#signal user_authorization_revoke
|
|
||||||
#signal user_update
|
|
||||||
|
|
||||||
@export_category("IRC")
|
@export_category("IRC")
|
||||||
|
|
||||||
@ -102,10 +58,6 @@ signal extension_bits_transaction_create(event_dict)
|
|||||||
## Scopes to request for the token. Look at https://dev.twitch.tv/docs/authentication/scopes/ for a list of all available scopes.
|
## Scopes to request for the token. Look at https://dev.twitch.tv/docs/authentication/scopes/ for a list of all available scopes.
|
||||||
@export var scopes : Array[String] = ["chat:edit", "chat:read"]
|
@export var scopes : Array[String] = ["chat:edit", "chat:read"]
|
||||||
|
|
||||||
@export_category("EventSub")
|
|
||||||
## Events to subscribe to. Make sure you have requested the required scope. Full list available at https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types/
|
|
||||||
@export var events : Array[String] = []
|
|
||||||
|
|
||||||
@export_category("Emotes/Badges")
|
@export_category("Emotes/Badges")
|
||||||
|
|
||||||
## If true, caches emotes/badges to disk, so that they don't have to be redownloaded on every restart.
|
## If true, caches emotes/badges to disk, so that they don't have to be redownloaded on every restart.
|
||||||
@ -148,6 +100,8 @@ var connected : bool = false
|
|||||||
var user_regex : RegEx = RegEx.new()
|
var user_regex : RegEx = RegEx.new()
|
||||||
var twitch_restarting : bool = false
|
var twitch_restarting : bool = false
|
||||||
|
|
||||||
|
const USER_AGENT = "User-Agent: GIFT/4.0.0 (Godot Engine)"
|
||||||
|
|
||||||
enum RequestType {
|
enum RequestType {
|
||||||
EMOTE,
|
EMOTE,
|
||||||
BADGE,
|
BADGE,
|
||||||
@ -243,6 +197,9 @@ func get_token() -> void:
|
|||||||
peer.poll()
|
peer.poll()
|
||||||
if (peer.get_available_bytes() > 0):
|
if (peer.get_available_bytes() > 0):
|
||||||
var response = peer.get_utf8_string(peer.get_available_bytes())
|
var response = peer.get_utf8_string(peer.get_available_bytes())
|
||||||
|
if (response == ""):
|
||||||
|
print("Empty response. Check if your redirect URL is set to http://localhost:18297.")
|
||||||
|
return
|
||||||
var start : int = response.find("?")
|
var start : int = response.find("?")
|
||||||
response = response.substr(start + 1, response.find(" ", start) - start)
|
response = response.substr(start + 1, response.find(" ", start) - start)
|
||||||
var data : Dictionary = {}
|
var data : Dictionary = {}
|
||||||
@ -269,7 +226,7 @@ func get_token() -> void:
|
|||||||
peer.disconnect_from_host()
|
peer.disconnect_from_host()
|
||||||
var request : HTTPRequest = HTTPRequest.new()
|
var request : HTTPRequest = HTTPRequest.new()
|
||||||
add_child(request)
|
add_child(request)
|
||||||
request.request("https://id.twitch.tv/oauth2/token", ["User-Agent: GIFT/3.0.0 (Godot Engine)", "Content-Type: application/x-www-form-urlencoded"], HTTPClient.METHOD_POST, "client_id=" + client_id + "&client_secret=" + client_secret + "&code=" + data["code"] + "&grant_type=authorization_code&redirect_uri=http://localhost:18297")
|
request.request("https://id.twitch.tv/oauth2/token", [USER_AGENT, "Content-Type: application/x-www-form-urlencoded"], HTTPClient.METHOD_POST, "client_id=" + client_id + "&client_secret=" + client_secret + "&code=" + data["code"] + "&grant_type=authorization_code&redirect_uri=http://localhost:18297")
|
||||||
var answer = await(request.request_completed)
|
var answer = await(request.request_completed)
|
||||||
if (!DirAccess.dir_exists_absolute("user://gift/auth")):
|
if (!DirAccess.dir_exists_absolute("user://gift/auth")):
|
||||||
DirAccess.make_dir_recursive_absolute("user://gift/auth")
|
DirAccess.make_dir_recursive_absolute("user://gift/auth")
|
||||||
@ -285,7 +242,7 @@ func get_token() -> void:
|
|||||||
func is_token_valid(token : String) -> String:
|
func is_token_valid(token : String) -> String:
|
||||||
var request : HTTPRequest = HTTPRequest.new()
|
var request : HTTPRequest = HTTPRequest.new()
|
||||||
add_child(request)
|
add_child(request)
|
||||||
request.request("https://id.twitch.tv/oauth2/validate", ["User-Agent: GIFT/3.0.0 (Godot Engine)", "Authorization: OAuth " + token])
|
request.request("https://id.twitch.tv/oauth2/validate", [USER_AGENT, "Authorization: OAuth " + token])
|
||||||
var data = await(request.request_completed)
|
var data = await(request.request_completed)
|
||||||
if (data[1] == 200):
|
if (data[1] == 200):
|
||||||
var payload : Dictionary = JSON.parse_string(data[3].get_string_from_utf8())
|
var payload : Dictionary = JSON.parse_string(data[3].get_string_from_utf8())
|
||||||
@ -392,26 +349,8 @@ func process_event(data : PackedByteArray) -> void:
|
|||||||
"revocation":
|
"revocation":
|
||||||
events_revoked.emit(payload["subscription"]["type"], payload["subscription"]["status"])
|
events_revoked.emit(payload["subscription"]["type"], payload["subscription"]["status"])
|
||||||
"notification":
|
"notification":
|
||||||
var event : Dictionary = payload["event"]
|
var event_data : Dictionary = payload["event"]
|
||||||
match payload["subscription"]["type"]:
|
event.emit(payload["subscription"]["type"], event_data)
|
||||||
"channel.follow":
|
|
||||||
channel_follow.emit(event)
|
|
||||||
"channel.subscribe":
|
|
||||||
channel_subscribe.emit(event)
|
|
||||||
"channel.subscription.gift":
|
|
||||||
channel_subscription_gift.emit(event)
|
|
||||||
"channel.subscription.message":
|
|
||||||
channel_subscription_message.emit(event)
|
|
||||||
"channel.cheer":
|
|
||||||
channel_cheer.emit(event)
|
|
||||||
"channel.charity_campaign.donate":
|
|
||||||
channel_charity_campaign_donate.emit(event)
|
|
||||||
"channel.channel_points_custom_reward_redemption.add":
|
|
||||||
channel_points_custom_reward_redemption_add.emit(event)
|
|
||||||
"channel.channel_points_custom_reward_redemption.update":
|
|
||||||
channel_points_custom_reward_redemption_update.emit(event)
|
|
||||||
"extensions.bits.transaction.create":
|
|
||||||
extension_bits_transaction_create.emit(event)
|
|
||||||
|
|
||||||
# Connect to Twitch IRC. Make sure to authenticate first.
|
# Connect to Twitch IRC. Make sure to authenticate first.
|
||||||
func connect_to_irc() -> bool:
|
func connect_to_irc() -> bool:
|
||||||
@ -432,27 +371,28 @@ func connect_to_eventsub(url : String = "wss://eventsub-beta.wss.twitch.tv/ws")
|
|||||||
eventsub.connect_to_url(url)
|
eventsub.connect_to_url(url)
|
||||||
print("Connecting to Twitch EventSub.")
|
print("Connecting to Twitch EventSub.")
|
||||||
await(events_id)
|
await(events_id)
|
||||||
for event in events:
|
events_connected.emit()
|
||||||
var request : HTTPRequest = HTTPRequest.new()
|
|
||||||
var version
|
# Refer to https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types/ for details on
|
||||||
if (event == "channel_follow"):
|
# which API versions are available and which conditions are required.
|
||||||
version = "beta"
|
func subscribe_event(event_name : String, version : int, conditions : Dictionary) -> void:
|
||||||
else:
|
|
||||||
version = "1"
|
|
||||||
var data : Dictionary = {}
|
var data : Dictionary = {}
|
||||||
data["type"] = event
|
data["type"] = event_name
|
||||||
data["version"] = version
|
data["version"] = str(version)
|
||||||
data["condition"] = {"broadcaster_user_id":user_id}
|
data["condition"] = conditions
|
||||||
data["transport"] = {
|
data["transport"] = {
|
||||||
"method":"websocket",
|
"method":"websocket",
|
||||||
"session_id":session_id
|
"session_id":session_id
|
||||||
}
|
}
|
||||||
|
var request : HTTPRequest = HTTPRequest.new()
|
||||||
add_child(request)
|
add_child(request)
|
||||||
request.request("https://api.twitch.tv/helix/eventsub/subscriptions", ["User-Agent: GIFT/3.0.0 (Godot Engine)", "Authorization: Bearer " + token["access_token"], "Client-Id:" + client_id, "Content-Type: application/json"], HTTPClient.METHOD_POST, JSON.stringify(data))
|
request.request("https://api.twitch.tv/helix/eventsub/subscriptions", [USER_AGENT, "Authorization: Bearer " + token["access_token"], "Client-Id:" + client_id, "Content-Type: application/json"], HTTPClient.METHOD_POST, JSON.stringify(data))
|
||||||
var reply : Array = await(request.request_completed)
|
var reply : Array = await(request.request_completed)
|
||||||
var response : Dictionary = JSON.parse_string(reply[3].get_string_from_utf8())
|
var response : Dictionary = JSON.parse_string(reply[3].get_string_from_utf8())
|
||||||
print("Now listening to %s events for broadcaster_id %s." % [response["data"][0]["type"], response["data"][0]["condition"]["broadcaster_user_id"]])
|
if (response.has("error")):
|
||||||
events_connected.emit()
|
print("Subscription failed for event '%s'. Error %s (%s): %s" % [event_name, response["status"], response["error"], response["message"]])
|
||||||
|
return
|
||||||
|
print("Now listening to '%s' events." % event_name)
|
||||||
|
|
||||||
# Request capabilities from twitch.
|
# Request capabilities from twitch.
|
||||||
func request_caps(caps : String = "twitch.tv/commands twitch.tv/tags twitch.tv/membership") -> void:
|
func request_caps(caps : String = "twitch.tv/commands twitch.tv/tags twitch.tv/membership") -> void:
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
name="Godot IRC For Twitch"
|
name="Godot IRC For Twitch"
|
||||||
description="Godot websocket implementation for Twitch IRC."
|
description="Godot websocket implementation for Twitch IRC."
|
||||||
author="issork"
|
author="issork"
|
||||||
version="3.0.1"
|
version="4.0.0"
|
||||||
script="gift.gd"
|
script="gift.gd"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user