@icon("res://addons/twitcher/assets/chat-bot-icon.svg") @tool extends Node ## Helper to send messages with a second bot user and the corrosponding bot badge. ## Take care that setup is needed that it actually works! The right scopes for the target channel ## and the sender user has to be set. ## See also https://dev.twitch.tv/docs/api/reference/#send-chat-message class_name TwitchBot static var _log: TwitchLogger = TwitchLogger.new("TwitchBot") const TWITCH_BOT_SCOPES = preload("res://addons/twitcher/chat/twitch_bot_scopes.tres") ## The base settings of the game / overlay the bot will inherit from @export var oauth_setting: OAuthSetting: set = _update_setting ## A token used for the bot specificially @export var bot_token: OAuthToken ## Sender user that should be used. Requires user:bot scope from chatting user, ## and either channel:bot scope from broadcaster or moderator status. @export var sender: TwitchUser: set = _update_sender ## Receive user you will need the channel:bot scope from broadcaster or moderator status. Fallbacks to sender. @export var receiver: TwitchUser: set = _update_receiver var _bot_auth: TwitchAuth var _bot_api: TwitchAPI func _init() -> void: child_entered_tree.connect(_on_enter_child) func _ready() -> void: if bot_token == null: bot_token = OAuthToken.new() bot_token._identifier = "Bot-Token" _ensure_children() _update_setting(oauth_setting) func _on_enter_child(node: Node) -> void: if node is TwitchAuth: _bot_auth = node if node is TwitchAPI: _bot_api = node func _ensure_children() -> void: if _bot_api == null: _bot_api = TwitchAPI.new() _bot_api.name = "BotApi" add_child(_bot_api) _bot_api.owner = get_tree().edited_scene_root if Engine.is_editor_hint() else owner if _bot_auth == null: _bot_auth = TwitchAuth.new() _bot_auth.name = "BotAuth" _bot_auth.scopes = TWITCH_BOT_SCOPES add_child(_bot_auth) _bot_auth.owner = get_tree().edited_scene_root if Engine.is_editor_hint() else owner _bot_auth.token = bot_token _bot_api.token = bot_token # Reset the API. The default shouldn't be the Bot API. It has way to less scopes! if TwitchAPI.instance == _bot_api: TwitchAPI.instance = null ## Sends a message as the bot user the target broadcaster default to the sender user. ## for_source_only: see https://dev.twitch.tv/docs/api/reference/#send-chat-message func send_message(message: String, reply_parent_message_id: String = "", for_source_only = true, broadcaster: TwitchUser = null) -> void: if not _bot_auth.is_authenticated: await _bot_auth.authorize() if broadcaster == null: broadcaster = receiver if receiver != null else sender _log.d("Send message from %s to %s: %s" % [sender.display_name, broadcaster.display_name, message]) var body: TwitchSendChatMessage.Body = TwitchSendChatMessage.Body.create(broadcaster.id, sender.id, message) if reply_parent_message_id != "": body.reply_parent_message_id = reply_parent_message_id var response: TwitchSendChatMessage.Response = await _bot_api.send_chat_message(body) for data in response.data: if data.is_sent: _log.d("Message was sent %s" % [data.message_id]) else: _log.e("Message couldn't be send cause of [%s]: %s" % [data.drop_reason.code, data.drop_reason.message]) func _update_setting(val: OAuthSetting) -> void: oauth_setting = val update_configuration_warnings() if oauth_setting == null || not is_inside_tree(): return var bot_setting = oauth_setting.duplicate() bot_setting.authorization_flow = OAuth.AuthorizationFlow.CLIENT_CREDENTIALS _bot_auth.oauth_setting = bot_setting _bot_api.oauth_setting = bot_setting func _update_sender(val: TwitchUser) -> void: sender = val update_configuration_warnings() func _update_receiver(val: TwitchUser) -> void: receiver = val update_configuration_warnings() func _get_configuration_warnings() -> PackedStringArray: var result: PackedStringArray = [] if oauth_setting == null: result.append("Proper OAuth settings are needed.") elif oauth_setting.client_secret == "": result.append("Client Secret is needed for using the bot node.") if sender == null: result.append("Sender is missing.") elif receiver != null && sender.id == receiver.id: result.append("Sender and receiver are the same -> you won't see the bot badge.") return result