Note: Twitch PubSub was fully decommissioned on April 14, 2025. This compatibility layer uses EventSub under the hood, which is Twitch’s current real-time event system.
Quick Start
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"github.com/Its-donkey/kappopher/helix"
)
func main() {
// Create auth client and get token
authClient := helix.NewAuthClient(helix.AuthConfig{
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
})
token, _ := authClient.GetAppAccessToken(context.Background())
authClient.SetToken(token)
// Create Helix client
helixClient := helix.NewClient("your-client-id", authClient)
// Create PubSub client
pubsub := helix.NewPubSubClient(helixClient,
helix.WithPubSubMessageHandler(func(topic string, message json.RawMessage) {
fmt.Printf("Received on %s: %s\n", topic, string(message))
}),
helix.WithPubSubErrorHandler(func(err error) {
log.Printf("Error: %v\n", err)
}),
)
// Connect
ctx := context.Background()
if err := pubsub.Connect(ctx); err != nil {
log.Fatal(err)
}
defer pubsub.Close(ctx)
// Listen to topics (familiar PubSub-style!)
pubsub.Listen(ctx, "channel-points-channel-v1.12345")
pubsub.Listen(ctx, "channel-subscribe-events-v1.12345")
// Block forever (in real app, use proper signal handling)
select {}
}
Supported Topics
The following PubSub topics are supported and automatically mapped to their EventSub equivalents:
| PubSub Topic | EventSub Type(s) |
|---|---|
channel-bits-events-v1.<channel_id> |
channel.cheer |
channel-bits-events-v2.<channel_id> |
channel.cheer |
channel-bits-badge-unlocks.<channel_id> |
channel.chat.notification |
channel-points-channel-v1.<channel_id> |
channel.channel_points_custom_reward_redemption.add |
channel-subscribe-events-v1.<channel_id> |
channel.subscribe, channel.subscription.gift, channel.subscription.message |
automod-queue.<moderator_id>.<channel_id> |
automod.message.hold |
chat_moderator_actions.<user_id>.<channel_id> |
channel.moderate |
whispers.<user_id> |
user.whisper.message |
API Reference
NewPubSubClient
Creates a new PubSub compatibility client.
pubsub := helix.NewPubSubClient(helixClient, opts...)
Parameters:
helixClient(*Client): The Helix API client for creating subscriptionsopts(…PubSubOption): Optional configuration functions
Options
// Set message handler for all topics
helix.WithPubSubMessageHandler(func(topic string, message json.RawMessage) {
// Handle messages
})
// Set error handler
helix.WithPubSubErrorHandler(func(err error) {
// Handle errors
})
// Set connection handler
helix.WithPubSubConnectHandler(func() {
// Called when connected
})
// Set reconnection handler
helix.WithPubSubReconnectHandler(func() {
// Called after successful reconnection
})
// Set custom WebSocket URL (for testing)
helix.WithPubSubWSURL("wss://custom.example.com/ws")
Connect
Establishes the WebSocket connection to EventSub.
err := pubsub.Connect(ctx)
Listen
Subscribes to a PubSub topic. The topic is automatically translated to the equivalent EventSub subscription(s).
err := pubsub.Listen(ctx, "channel-points-channel-v1.12345")
Notes:
- Some topics map to multiple EventSub subscriptions (e.g.,
channel-subscribe-events-v1creates 3 subscriptions) - Listening to the same topic twice is idempotent (no error, no duplicate subscriptions)
- Returns
ErrPubSubNotConnectedif not connected - Returns
ErrPubSubInvalidTopicfor unrecognized topic formats - Returns
ErrPubSubUnsupportedTopicfor valid but unsupported topic types
Unlisten
Unsubscribes from a PubSub topic.
err := pubsub.Unlisten(ctx, "channel-points-channel-v1.12345")
Close
Closes the connection and cleans up all subscriptions.
err := pubsub.Close(ctx)
IsConnected
Returns whether the client is currently connected.
if pubsub.IsConnected() {
// Connected
}
Topics
Returns the list of topics currently being listened to.
topics := pubsub.Topics()
for _, topic := range topics {
fmt.Println(topic)
}
SessionID
Returns the EventSub session ID.
sessionID := pubsub.SessionID()
Helper Functions
ParseTopic
Parses a PubSub topic string into its components.
parsed, err := helix.ParseTopic("channel-points-channel-v1.12345")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Type: %s, ChannelID: %s\n", parsed.Type, parsed.ChannelID)
BuildTopic
Constructs a PubSub topic string from components.
topic := helix.BuildTopic("channel-points-channel-v1", "12345")
// Returns: "channel-points-channel-v1.12345"
topic = helix.BuildTopic("automod-queue", "mod_id", "channel_id")
// Returns: "automod-queue.mod_id.channel_id"
TopicEventSubTypes
Returns the EventSub types that a PubSub topic maps to.
types := helix.TopicEventSubTypes("channel-subscribe-events-v1.12345")
// Returns: ["channel.subscribe", "channel.subscription.gift", "channel.subscription.message"]
SupportedTopics
Returns a list of all supported topic patterns.
patterns := helix.SupportedTopics()
for _, pattern := range patterns {
fmt.Println(pattern)
}
Message Format
Messages are delivered to your handler wrapped in a PubSubMessage envelope:
type PubSubMessage struct {
Type string `json:"type"` // EventSub subscription type
Data json.RawMessage `json:"data"` // EventSub event payload
}
Example handler that parses the message:
helix.WithPubSubMessageHandler(func(topic string, message json.RawMessage) {
var envelope helix.PubSubMessage
json.Unmarshal(message, &envelope)
switch envelope.Type {
case helix.EventSubTypeChannelPointsRedemptionAdd:
var event helix.ChannelPointsRedemptionAddEvent
json.Unmarshal(envelope.Data, &event)
fmt.Printf("Redemption: %s redeemed %s\n",
event.UserName, event.Reward.Title)
case helix.EventSubTypeChannelCheer:
var event helix.ChannelCheerEvent
json.Unmarshal(envelope.Data, &event)
fmt.Printf("Cheer: %s cheered %d bits\n",
event.UserName, event.Bits)
}
})
Error Handling
The client handles several error scenarios:
helix.WithPubSubErrorHandler(func(err error) {
switch {
case errors.Is(err, helix.ErrPubSubNotConnected):
// Not connected to WebSocket
case errors.Is(err, helix.ErrPubSubInvalidTopic):
// Topic format not recognized
case errors.Is(err, helix.ErrPubSubUnsupportedTopic):
// Topic type not supported
default:
// Other errors (subscription revocation, reconnection failures, etc.)
log.Printf("PubSub error: %v", err)
}
})
Reconnection
The client automatically handles EventSub reconnection requests. When the server requests a reconnection:
- The client connects to the new URL provided by Twitch
- A new session is established
- Your
onReconnecthandler is called - All existing topic subscriptions remain active (EventSub handles this automatically)
helix.WithPubSubReconnectHandler(func() {
log.Println("Successfully reconnected to EventSub")
})
Migration from PubSub
If you’re migrating from the old PubSub system, the API is intentionally similar:
Old PubSub code:
// Old: Direct PubSub (no longer works)
pubsub.Listen("channel-points-channel-v1.12345", token)
New Kappopher code:
// New: PubSub compatibility layer (uses EventSub)
pubsub := helix.NewPubSubClient(helixClient, ...)
pubsub.Connect(ctx)
pubsub.Listen(ctx, "channel-points-channel-v1.12345")
Key differences:
- Requires a Helix client (for creating EventSub subscriptions via API)
- Uses
context.Contextfor all operations - Messages are wrapped in a
PubSubMessageenvelope with the EventSub type - Event payloads use EventSub format (not old PubSub format)