Skip to content

danilopolani/twitch-pub-sub

Repository files navigation

(Laravel) Twitch PubSub

Latest Version on Packagist Build Status


Laravel Twitch PubSub

Connect to Twitch PubSub (Web Sockets) in a Laravel application, dispatching Events whenever a message for a topic is received.

Built with Amphp with Web Socket.

Table of Contents

  1. Getting Started
  2. Usage
  3. Changelog
  4. Contributing
  5. Testing
  6. Security
  7. Credits
  8. License

Getting Started

The package supports Laravel 8.x and PHP >= 7.4.

Prerequisites

The PHP extension ext-pcntl is required.

Installation

You can install the package via composer:

composer require danilopolani/twitch-pub-sub

Usage

The package relies on one main function:

TwitchPubSub::run(string|array $twitchAuthToken, array $topics = [])
Argument Description
array|string $twitchAuthToken if string, it must be a valid Auth Token, otherwise it can be an associative array of authToken => topics[]
array $topics an array of valid topics, needed only if $twitchAuthToken is a string

Usually you would put the main function of the package inside an Artisan Command.

use \Danilopolani\TwitchPubSub\Facades\TwitchPubSub;

/**
 * Execute the console command.
 *
 * @return mixed
 */
public function handle()
{
    TwitchPubSub::run('a1b2c3d4e5', ['whispers.44322889']);

    // Or the array syntax that support multiple users too
    TwitchPubSub::run([
        'a1b2c3d4e5' => ['whispers.44322889'],
        'f6g7h8j9k0' => ['channel-bits-events-v1.123456', 'channel-points-channel-v1.123456'],
    ]);
}

Now you can run your command from your terminal or a worker.

You should definitely setup Supervisor or a similar tool to keep your command alive and restart it if something goes wrong.

Finally, create a Listener to handle the incoming events.

// App\Providers\EventServiceProvider.php

/**
 * The event listener mappings for the application.
 *
 * @var array
 */
protected $listen = [
    \Danilopolani\TwitchPubSub\Events\WhisperReceived::class => [
        TrackMessages::class,
    ],
];

// Or with a closure
Event::listen(function (\Danilopolani\TwitchPubSub\Events\WhisperReceived $event) {
    dd($event->data);
});

Topics & Events

Topic Event
channel-bits-events-v1.<channel_id> \Danilopolani\TwitchPubSub\Events\BitsDonated
channel-bits-events-v2.<channel_id> \Danilopolani\TwitchPubSub\Events\BitsDonated
channel-bits-badge-unlocks.<channel_id> \Danilopolani\TwitchPubSub\Events\BitsBadgeUnlocked
channel-points-channel-v1.<channel_id> \Danilopolani\TwitchPubSub\Events\RewardRedeemed
channel-subscribe-events-v1.<channel_id> \Danilopolani\TwitchPubSub\Events\SubscriptionReceived
chat_moderator_actions.<user_id>.<channel_id> \Danilopolani\TwitchPubSub\Events\ModeratorActionSent
whispers.<user_id> \Danilopolani\TwitchPubSub\Events\WhisperReceived

Reconnection

When the connection is closed, the package itself will try to attempt a reconnection, but this would need a fresh access token, furthermore we strongly suggest you to handle the onClose callback and exit your script. This, with a correct configuration of Supervisor, will restart the worker automatically reconnecting with a fresh token, if your code is written in that way. Below a simple example with a correct flow to demonstrate how it should work:

// App/Console/Commands/PubSub.php

public function handle()
{
    // A fresh Twitch Access Token
    $token = $user->getFreshAccessToken();
    
    TwitchPubSub::onClose(function (\Amp\Websocket\ClosedException $e) {
        exit(0);
    });
    
    TwitchPubSub::run($token, ['my-topic']);
}

When exit(0) will be executed, the script will stop, Supervisor will restart it - invoking handle again - and refreshing the token reconnecting correctly.
Please see below for more information about callbacks.

Callbacks

The package provides several callbacks fired when something occurs. These callbacks must be put before the ::run() method to let them work correctly.

// A message (anything, PING/PONG too for example) is received
TwitchPubSub::onMessage(function (array $payload) {
    dump('received message:', $payload);
});

// A generic error occurs
TwitchPubSub::onError(function (\Exception $e) {
    dump('generic error:', $e->getMessage());
});

// The connection has been closed
// This could triggered from a SIGINT or SIGTERM too (stopping the script, restarting the worker etc.)
TwitchPubSub::onClose(function (\Amp\Websocket\ClosedException $e) {
    dump('connection closed, reason:', $e->getMessage());
});

// An error occurred in a Listener after the event has been dispatched
TwitchPubSub::onDispatchError(function (string $event, array $payload, Throwable $e) {
    dump('error for event', $event, $payload, $e->getMessage());
});

// Runner
TwitchPubSub::run('a1b2c3d4e5', ['whispers.44322889']);

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Testing

Clone the repository and just run

composer test

With Docker (Windows):

docker run --rm -v %cd%:/app composer:2 bash -c "cd /app && composer install --ignore-platform-reqs && ./vendor/bin/phpunit"

With Docker (Linux/OSX):

docker run --rm -v $(pwd):/app composer:2 bash -c "cd /app && composer install --ignore-platform-reqs && ./vendor/bin/phpunit"

Security

If you discover any security related issues, please email danilo.polani@gmail.com instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

Laravel Package Boilerplate

This package was generated using the Laravel Package Boilerplate.