Using TwitchService
TwitchService is the central hub for interacting with Twitch. It acts as a high-level interface, simplifying common tasks by coordinating the underlying components like TwitchAPI, TwitchIRC, TwitchEventsub, and TwitchAuth.
Prerequisites
Before using TwitchService, you need to:
- Add the Node: Add a
TwitchServicenode to your scene tree. It is recommended to add it to your main scene. While it can be an autoload (Singleton), adding it as a regular node helps to avoid polluting the Autoloads and allows for better testing of individual scenes. Because it uses a.instancepattern it is still globally accessible as long as the node is in the scene tree. - Add Child Components: Ensure the necessary child nodes are added under
TwitchServicein the scene tree. Common children include:TwitchAPI(for API calls)TwitchAuth(for handling authentication)TwitchIRC(for chat read/write via IRC)TwitchEventsub(for subscribing to events like follows, subs)TwitchMediaLoader(for emotes, badges, etc.)
- Configure in Inspector: Select the
TwitchServicenode in the Godot editor. In the Inspector panel, you must assign the required resources:Oauth Setting: AOAuthSettingresource containing your Client ID, Client Secret, and Redirect URI.Scopes: AnOAuthScopesresource defining the permissions your application needs.Token: AnOAuthTokenresource where the user's access token will be stored after authentication.
C# note
In C#, TwitchService.instance from GDScript becomes the static property TwitchService.Instance. It returns the existing node if one is already in the scene, or null if none exists yet: call TwitchService.CreateInstance() to add one at the scene root from code instead of placing it in the editor.
Initial Setup and Authentication
The most crucial first step after getting the instance is to run the setup process, which includes authentication.
extends Node
# Assume you have created a TwitchService node in your scene
func _ready():
# Start the setup process (handles authentication)
# Returns true on success, false on failure (e.g., login run in timeout)
var setup_successful: bool = await TwitchService.instance.setup()
if setup_successful:
print("Twitch Service successfully set up and authenticated!")
# Now you can proceed with other Twitch interactions
await get_self_info()
else:
printerr("Twitch Service setup failed. Check authentication.")
# Example function called after successful setup
func get_self_info():
var current_user: TwitchUser = await TwitchService.instance.get_current_user()
if current_user:
print("Authenticated as: %s (ID: %s)" % [current_user.display_name, current_user.id])
else:
printerr("Could not get current user info.")using Godot;
using System.Threading.Tasks;
using TwitcherSharp;
public partial class YourNode : Node
{
// Assume you have created a TwitchService node in your scene
public override async void _Ready()
{
// Start the setup process (handles authentication)
// Returns true on success, false on failure (e.g., login run in timeout)
bool setupSuccessful = await TwitchService.Instance.Setup();
if (setupSuccessful)
{
GD.Print("Twitch Service successfully set up and authenticated!");
// Now you can proceed with other Twitch interactions
await GetSelfInfo();
}
else
{
GD.PrintErr("Twitch Service setup failed. Check authentication.");
}
}
// Example function called after successful setup
private async Task GetSelfInfo()
{
var currentUser = await TwitchService.Instance.GetCurrentUser();
if (currentUser != null)
{
GD.Print($"Authenticated as: {currentUser.DisplayName} (ID: {currentUser.Id})");
}
else
{
GD.PrintErr("Could not get current user info.");
}
}
}Common Usage Examples
Here are simple examples for common tasks, assuming setup() has completed successfully
Getting User Information
func _get_user_details(username: String) -> void:
# Get user info by login name
var user: TwitchUser = await TwitchService.instance.get_user(username)
if user:
print("User found: %s, ID: %s, Profile Image URL: %s" % [user.display_name, user.id, user.profile_image_url])
# You can then load the profile image (requires TwitchMediaLoader child)
var profile_texture: ImageTexture = await TwitchService.instance.load_profile_image(user)
if profile_texture:
$YourSprite2D.texture = profile_texture # Assign to a Sprite2D, TextureRect etc.
else:
print("User '%s' not found." % username)[Export] private Sprite2D _yourSprite2D;
private async Task GetUserDetails(string username)
{
// Get user info by login name
var user = await TwitchService.Instance.GetUser(username);
if (user != null)
{
GD.Print($"User found: {user.DisplayName}, ID: {user.Id}, Profile Image URL: {user.ProfileImageUrl}");
// You can then load the profile image (requires TwitchMediaLoader child)
var profileTexture = await TwitchService.Instance.GetProfileImage(user);
if (profileTexture != null)
{
_yourSprite2D.Texture = profileTexture; // Assign to a Sprite2D, TextureRect etc.
}
}
else
{
GD.Print($"User '{username}' not found.");
}
}Sending Chat Messages
func send_hello_message():
var me = await TwitchService.instance.get_current_user()
# Requires 'chat:read' and 'chat:edit' scopes
print("Sending 'Hello from Godot!' to channel ID: %s" % me.id)
TwitchService.chat("Hello from Godot!")private async Task SendHelloMessage()
{
var me = await TwitchService.Instance.GetCurrentUser();
// Requires 'chat:read' and 'chat:edit' scopes
GD.Print($"Sending 'Hello from Godot!' to channel ID: {me.Id}");
TwitchService.Instance.Chat("Hello from Godot!");
}Adding Chat Commands
func _ready():
# Wait for setup first...
var setup_successful = await TwitchService.instance.setup()
if setup_successful:
# Add a command handler for "!hello"
TwitchService.instance.add_command("hello", _on_hello_command)
print("Registered !hello command.")
else:
printerr("Setup failed, cannot add command.")
# Callback function for the command
func _on_hello_command(from_username: String, info: TwitchCommandInfo, args: PackedStringArray):
print("Received !hello command from %s" % info.username)
TwitchService.chat("Hi there, %s!" % info.username)using TwitcherSharp.Chat;
private TwitchCommand _helloCommand;
public override async void _Ready()
{
// Wait for setup first...
bool setupSuccessful = await TwitchService.Instance.Setup();
if (setupSuccessful)
{
// Add a command handler for "!hello"
_helloCommand = TwitchService.Instance.AddCommand(new TwitchCommand { Command = "hello" });
_helloCommand.CommandReceived += OnHelloCommand;
GD.Print("Registered !hello command.");
}
else
{
GD.PrintErr("Setup failed, cannot add command.");
}
}
// Callback function for the command
private void OnHelloCommand(string fromUsername, TwitchCommandInfo info, string[] args)
{
GD.Print($"Received !hello command from {info.Username}");
TwitchService.Instance.Chat($"Hi there, {info.Username}!");
}C# note
In C#, use the AddCommand(TwitchCommand) overload and subscribe to the returned command's CommandReceived event, as shown above. The AddCommand(string, Callable) overload (mirroring the GDScript add_command call) passes the callable straight through to GDScript and cannot marshal the native TwitchCommandInfo into the C# wrapper type, so it will fail at runtime for C# consumers — avoid it. See the Commands section for more details.
Subscribing to Events (EventSub)
(Requires TwitchEventsub child node)
# Example: Subscribe to follows on your channel
func subscribe_to_follows():
# Ensure EventSub is connected first
await TwitchService.instance.wait_for_eventsub_connection()
var me = await TwitchService.instance.get_current_user()
TwitchService.instance.subscribe_event(TwitchEventsubDefinition.CHANNEL_FOLLOW, {
&"broadcaster_user_id": me.id,
&"moderator_user_id": me.id
})
# Now, connect to the signal on the TwitchEventsub node itself
# to receive the actual event notifications.
TwitchService.instance.eventsub.event.connect(_on_eventsub_event)
func _on_eventsub_event(type: StringName, data: Dictionary):
if type == "channel.follow":
var follower_name = data.user_name
print("New follower: %s!" % follower_name)using System.Threading.Tasks;
using TwitcherSharp.EventSub;
using TwitcherSharp.EventSub.Generated.ChannelFollow;
// Example: Subscribe to follows on your channel
private async Task SubscribeToFollows()
{
// Ensure EventSub is connected first
await TwitchService.Instance.WaitForEventSubConnection();
var me = await TwitchService.Instance.GetCurrentUser();
var condition = new TwitchChannelFollowCondition(broadcasterUserId: me.Id, moderatorUserId: me.Id);
TwitchService.Instance.SubscribeEvent(TwitchEventSubDefinition.ChannelFollow, condition);
// Now, connect to the signal on the TwitchEventSub node itself
// to receive the actual event notifications.
TwitchEventSub.Instance.Event += OnEventSubEvent;
}
private void OnEventSubEvent(string type, Godot.Collections.Dictionary data)
{
if (type == "channel.follow")
{
var followerName = data["user_name"].AsString();
GD.Print($"New follower: {followerName}!");
}
}C# note
Unlike GDScript, SubscribeEvent takes a strongly-typed condition object (here TwitchChannelFollowCondition) instead of a raw Dictionary, so mismatched or missing condition keys are caught at compile time. Handling the raw Event signal like above works, but for most cases the typed TwitchEventListener<T> is easier: it gives you a TwitchChannelFollowEvent object instead of a raw dictionary. See TwitchEventListener for that pattern.