Unable to Subscribe to Channel Point Redemptions

Hi everyone!
I’ve been trying my best to figure out how to do this but have hit a brick wall.
I’m able to get the welcome message but I haven’t been able to successfully receive any messages from my twitch channel when someone redeems a channel point reward.

I’ve checked my OAuth token is valid and has the scope for channel:read:redemptions.

Please help, I’ve been at this for months and I bet it’s something really obvious x(

Here is my script and a screenshot of my log messages.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using TMPro;
using System.Text;
using System.Text.Json;
using System.Net.Http;
using System.Net.Http.Headers;

public class WebSocketClientTwitch : MonoBehaviour
{
    public TMP_Text chatBoxTMP;
    private string lastMessage;

    private ClientWebSocket webSocket;
    private string clientID = "MY_CLIENT_ID";
    private string accessToken = "MY_OAUTH_TOKEN";
    private string broadcasterId = "MY_BROADCASTER_ID";

    private string sessionIDString;

    public async void Start()
    {
        Uri uri = new Uri("wss://eventsub.wss.twitch.tv/ws");
        await ConnectAndReceiveAsync(uri);
    }

    private async Task ConnectAndReceiveAsync(Uri uri)
    {
        webSocket = new ClientWebSocket();

        try
        {
            await webSocket.ConnectAsync(uri, CancellationToken.None);
            Debug.Log("Connected to WebSocket!");

            // Receive data
            var receiveBuffer = new byte[1024];
            while (webSocket.State == WebSocketState.Open)
            {
                Debug.Log("WebSocket is open");

                WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                if (result.MessageType == WebSocketMessageType.Text)
                {
                    string receivedMessage = System.Text.Encoding.UTF8.GetString(receiveBuffer, 0, result.Count);
                    lastMessage = receivedMessage;

                    if (receivedMessage.Contains("pusher:pong"))
                    {
                        Debug.Log("Received: Pong");
                    }
                    else if (receivedMessage.Contains("ChatMessageEvent"))
                    {
                        Debug.Log("Received Chat Message: " + receivedMessage);
                    }
                    else if (receivedMessage.Contains("session_welcome"))
                    {
                        Debug.Log("We have been Welcomed: " + receivedMessage);
                        // Get the session ID from receivedMessage
                        sessionIDString = ExtractSessionID(receivedMessage);
                        //Debug.Log("Here is the session ID " + sessionIDString);
                        await SubscribeToChannelPointRedemptions(sessionIDString);
                        await StartPingLoop();
                    }
                    else if (receivedMessage.Contains("custom_reward_redemption"))
                    {
                        Debug.Log("Custom channel point redemption received: " + receivedMessage);
                    }
                    else
                        Debug.Log("Received: " + receivedMessage);
                }
                else
                {
                    Debug.Log("The Message Type is: " + result.MessageType);
                    Debug.Log("The Message Type is: " + result.EndOfMessage);
                    Debug.Log("The Message Type is: " + webSocket.State);
                }
            }
            Debug.Log("Websocket is closed...");
        }
        catch (Exception ex)
        {
            Debug.LogError("WebSocket connection error: " + ex.Message);
        }
    }

    static string ExtractSessionID(string welcomeMessage)
    {
        int startIndex = welcomeMessage.IndexOf("\"id\":\"") + 6;
        int endIndex = welcomeMessage.IndexOf("\"", startIndex);
        return welcomeMessage.Substring(startIndex, endIndex - startIndex);
    }

    private async Task StartPingLoop()
    {
        while (webSocket.State == WebSocketState.Open)
        {
            string pingMessage = "{\"type\":\"ping\"}";
            byte[] pingBuffer = Encoding.UTF8.GetBytes(pingMessage);
            await webSocket.SendAsync(new ArraySegment<byte>(pingBuffer), WebSocketMessageType.Text, true, CancellationToken.None);
            Debug.Log("Sent keep-alive ping to Twitch");
            await Task.Delay(10000); // Ping every 10 seconds

            if (UnityEditor.EditorApplication.isPlaying == false)
            {
                // If the app is closed within the Editor, stop the Ping loop
                Console.WriteLine("Close loop complete");
                break;
            }
        }
    }

    private async Task SubscribeToChannelPointRedemptions(string sessionID)
    {
        using (HttpClient client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("Client-ID", clientID);
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

            string subscribeMessage = @$"{{
                ""type"": ""subscribe"",
                ""version"": ""1"",
                ""condition"": {{
                    ""broadcaster_user_id"": ""{broadcasterId}""
                }},
                ""transport"": {{
                    ""method"": ""websocket"",
                    ""session_id"": ""{sessionID}""
                }},
                ""event_type"": ""channel.channel_points_custom_reward_redemption.add""
            }}";

            byte[] subscribeBuffer = Encoding.UTF8.GetBytes(subscribeMessage);
            await webSocket.SendAsync(new ArraySegment<byte>(subscribeBuffer), WebSocketMessageType.Text, true, CancellationToken.None);
            Debug.Log("Subscribed to custom channel point redemptions on Twitch EventSub");
        }
    }

    public async void OnApplicationQuit()
    {
        if (webSocket != null && webSocket.State == WebSocketState.Open)
        {
            // Close the WebSocket connection gracefully when the application quits
            await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Application quitting", CancellationToken.None);
            Debug.Log("WebSocket connection closed.");
        }
        else
            Debug.Log("WebSocket connection is closed already.");
    }
}

It looks like you’re trying to subscribe to a topic by sending a message on the WebSocket connection. That’s not correct, and if you log the websocket close status you should have received a 4001 for Client sent inbound traffic which would have pointed in as to where the error is.

To subscribe to a topic you need to use the Helix endpoint, as documented https://dev.twitch.tv/docs/eventsub/manage-subscriptions/#subscribing-to-events

Also as a side note it appears you’re hardcoding your access token? Keep in mind that they do expire, so that may cause an issue too.

Thank you thank you thank you!

I really appreciate you pointing me in the right direction and now I’ve finally got it working :smiley:

I was only hardcoding the access token until I could figure out how to get it working first - now I can look at automating this too ^^

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.