Skip to content

Add Twitch EventSub support with OAuth flow and command system#15

Open
ghostzero wants to merge 1 commit into
masterfrom
eventsub
Open

Add Twitch EventSub support with OAuth flow and command system#15
ghostzero wants to merge 1 commit into
masterfrom
eventsub

Conversation

@ghostzero

@ghostzero ghostzero commented Jun 27, 2026

Copy link
Copy Markdown
Owner

AI disclosure: The use of Claude AI has been added to this branch to provide support for EventSub.

What's Changed

  • EventSubClient: WebSocket client for Twitch EventSub (chat.message, chat.notification) using ratchet/pawl and react/http
  • EventSubOptions: configuration for access token, client ID, subscriptions, and bot user ID resolution
  • ChatMessageEvent / ChatNotificationEvent: typed event classes for EventSub notifications
  • OAuthHelper: Authorization Code OAuth flow with local callback server, token persistence, auto-refresh, and automatic re-auth on scope change
  • CommandContext: command handler context with reply() and say() helpers that post via POST /helix/chat/messages

Screenshots

Example in CLI:
image

Example in Twitch Chat:
image

Example Usage

<?php

require __DIR__ . '/vendor/autoload.php';

use GhostZero\Tmi\CommandContext;
use GhostZero\Tmi\EventSubClient;
use GhostZero\Tmi\EventSubOptions;
use GhostZero\Tmi\Events\EventSub\ChatMessageEvent;
use GhostZero\Tmi\Events\EventSub\ChatNotificationEvent;
use GhostZero\Tmi\OAuthHelper;

$oauth = new OAuthHelper(
    clientId: 'XXXXXXXXXXXXXXX',
    clientSecret: 'XXXXXXXXXXXXXXX',
);

// First run: opens Twitch OAuth in browser and stores the token in .twitch_token.json
// Subsequent runs: reuses the stored token, auto-refreshes when expired
// user:bot lets the bot read chat in channels where the broadcaster has added it
$accessToken = $oauth->getAccessToken(['user:read:chat', 'user:write:chat', 'user:bot']);
$botUserId = $oauth->getUserId();

$broadcasterUserId = '280506223';

$client = new EventSubClient(new EventSubOptions([
    'access_token' => $accessToken,
    'client_id'    => 'XXXXXXXXXXXXXXX',
    'subscriptions' => [
        [
            'type'      => 'channel.chat.message',
            'condition' => [
                'broadcaster_user_id' => $broadcasterUserId,
                'user_id'             => $botUserId,
            ],
        ],
        [
            'type'      => 'channel.chat.notification',
            'condition' => [
                'broadcaster_user_id' => $broadcasterUserId,
                'user_id'             => $botUserId,
            ],
        ],
    ],
    'options' => ['debug' => false],
]));

$client->on(ChatMessageEvent::class, function (ChatMessageEvent $event) {
    echo "[{$event->broadcasterUserLogin}] {$event->chatterUserLogin}: {$event->messageText}" . PHP_EOL;
});

$client->on(ChatNotificationEvent::class, function (ChatNotificationEvent $event) {
    echo "[notice:{$event->noticeType}] {$event->systemMessage}" . PHP_EOL;
});

$client->command('!hello', function (CommandContext $ctx) {
    $ctx->reply("Hey {$ctx->event->chatterUserName}! 👋");
});

$client->command('!ping', function (CommandContext $ctx) {
    $ctx->reply('Pong!');
});

$client->connect();

- EventSubClient: WebSocket client for Twitch EventSub (chat.message,
  chat.notification) using ratchet/pawl and react/http
- EventSubOptions: configuration for access token, client ID,
  subscriptions, and bot user ID resolution
- ChatMessageEvent / ChatNotificationEvent: typed event classes for
  EventSub notifications
- OAuthHelper: Authorization Code OAuth flow with local callback server,
  token persistence, auto-refresh, and automatic re-auth on scope change
- CommandContext: command handler context with reply() and say() helpers
  that post via POST /helix/chat/messages
- gitignore .twitch_token.json to keep credentials out of the repo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant