PHPackages                             clue/quassel-react - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Queues &amp; Workers](/categories/queues)
4. /
5. clue/quassel-react

ActiveLibrary[Queues &amp; Workers](/categories/queues)

clue/quassel-react
==================

Streaming, event-driven access to your Quassel IRC core, built on top of ReactPHP.

v0.7.0(5y ago)181254[3 issues](https://github.com/clue/reactphp-quassel/issues)MITPHPPHP &gt;=5.3

Since May 3Pushed 4y ago4 watchersCompare

[ Source](https://github.com/clue/reactphp-quassel)[ Packagist](https://packagist.org/packages/clue/quassel-react)[ Docs](https://github.com/clue/reactphp-quassel)[ Fund](https://clue.engineering/support)[ GitHub Sponsors](https://github.com/clue)[ RSS](/packages/clue-quassel-react/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (9)Dependencies (7)Versions (10)Used By (0)

clue/reactphp-quassel
=====================

[](#cluereactphp-quassel)

[![CI status](https://github.com/clue/reactphp-quassel/workflows/CI/badge.svg)](https://github.com/clue/reactphp-quassel/actions)[![installs on Packagist](https://camo.githubusercontent.com/9b3fbe0ff50d339ae229c3db6dda1af46ff07e7af359463210ea8354bca02bb8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f636c75652f7175617373656c2d72656163743f636f6c6f723d626c7565266c6162656c3d696e7374616c6c732532306f6e2532305061636b6167697374)](https://packagist.org/packages/clue/quassel-react)

Streaming, event-driven access to your [Quassel IRC](http://quassel-irc.org/) core, built on top of [ReactPHP](https://reactphp.org/).

This is a lightweight and low-level networking library which can be used to communicate with your Quassel IRC core. It allows you to react to incoming events (such as an incoming message) and to perform new requests (such as sending an outgoing reply message). This can be used to build chatbots, export your channel backlog, list online users, forward backend events as a message to a channel and much more. Unlike conventional IRC chatbots, Quassel IRC allows re-using your existing identity and sharing it with both a person and a chatbot, so that an outside person has no idea about this and only sees a single contact.

- **Async execution of requests** - Send any number of requests to your Quassel IRC core in parallel (automatic pipeline) and process their responses as soon as results come in.
- **Event-driven core** - Register your event handler callbacks to react to incoming events, such as an incoming chat message event.
- **Lightweight, SOLID design** - Provides a thin abstraction that is [*just good enough*](http://en.wikipedia.org/wiki/Principle_of_good_enough)and does not get in your way. Future or custom commands and events require little to no changes to be supported.
- **Good test coverage** - Comes with an automated tests suite and is regularly tested against actual Quassel IRC cores in the wild

**Table of contents**

- [Support us](#support-us)
- [Quickstart example](#quickstart-example)
- [Usage](#usage)
    - [Factory](#factory)
        - [createClient()](#createclient)
    - [Client](#client)
        - [Commands](#commands)
        - [Processing](#processing)
        - [on()](#on)
        - [close()](#close)
- [Install](#install)
- [Tests](#tests)
- [License](#license)

Support us
----------

[](#support-us)

We invest a lot of time developing, maintaining and updating our awesome open-source projects. You can help us sustain this high-quality of our work by [becoming a sponsor on GitHub](https://github.com/sponsors/clue). Sponsors get numerous benefits in return, see our [sponsoring page](https://github.com/sponsors/clue)for details.

Let's take these projects to the next level together! 🚀

Quickstart example
------------------

[](#quickstart-example)

The Quassel IRC protocol is not exactly trivial to explain and has some *interesting* message semantics. As such, it's highly recommended to check out the [examples](examples) to get started.

Usage
-----

[](#usage)

### Factory

[](#factory)

The `Factory` is responsible for creating your [`Client`](#client) instance.

```
$factory = new Clue\React\Quassel\Factory();
```

This class takes an optional `LoopInterface|null $loop` parameter that can be used to pass the event loop instance to use for this object. You can use a `null` value here in order to use the [default loop](https://github.com/reactphp/event-loop#loop). This value SHOULD NOT be given unless you're sure you want to explicitly use a given event loop instance.

If you need custom connector settings (DNS resolution, TLS parameters, timeouts, proxy servers etc.), you can explicitly pass a custom instance of the [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface):

```
$connector = new React\Socket\Connector(array(
    'dns' => '127.0.0.1',
    'tcp' => array(
        'bindto' => '192.168.10.1:0'
    ),
    'tls' => array(
        'verify_peer' => false,
        'verify_peer_name' => false
    )
));

$factory = new Clue\React\Quassel\Factory(null, $connector);
```

#### createClient()

[](#createclient)

The `createClient($uri)` method can be used to create a new [`Client`](#client). It helps with establishing a plain TCP/IP connection to your Quassel IRC core and probing for the correct protocol to use.

```
$factory->createClient('localhost')->then(
    function (Client $client) {
        // client connected (and authenticated)
    },
    function (Exception $e) {
        // an error occured while trying to connect (or authenticate) client
    }
);
```

The `$uri` parameter must be a valid URI which must contain a host part and can optionally be preceded by the `quassel://` URI scheme and may contain a port if your Quassel IRC core is not using the default TCP/IP port `4242`:

```
$factory->createClient('quassel://localhost:4242');
```

Quassel supports password-based authentication. If you want to create a "normal" client connection, you're recommended to pass the authentication details as part of the URI. You can pass the password `h@llo` URL-encoded (percent-encoded) as part of the URI like this:

```
$factory->createClient('quassel://user:h%40llo@localhost')->then(
    function (Client $client) {
        // client sucessfully connected and authenticated
        $client->on('data', function ($data) {
            // next message to follow would be "SessionInit"
        });
    }
);
```

Note that if you do not pass the authentication details as part of the URI, then this method will resolve with a "bare" `Client` right after connecting without sending any application messages. This can be useful if you need full control over the message flow, see below for more details.

Quassel uses "heartbeat" messages as a keep-alive mechanism to check the connection between Quassel core and Quassel client is still active. This project will automatically respond to each incoming "ping" (heartbeat request) with an appropriate "pong" (heartbeat response) message. If you do not want this and want to handle incoming heartbeat request messages yourself, you may pass the optional `?pong=0` parameter like this:

```
$factory->createClient('quassel://localhost?pong=0');
```

This automatic "pong" mechanism allows the Quassel core to detect the connection to the client is still active. However, it does not allow the client to detect if the connection to the Quassel core is still active. Because of this, this project will automatically send a "ping" (heartbeat request) message to the Quassel core if it did not receive any messages for 60s by default. If no message has been received after waiting for another period, the connection is assumed to be dead and will be closed. You can pass the `?ping=120.0` parameter to change this default interval. The Quassel core uses a configurable ping interval of 30s by default and also sends all IRC network state changes to the client, so this mechanism should only really kick in if the connection looks dead. If you do not want this and want to handle outgoing heartbeat request messages yourself, you may pass the optional `?ping=0` parameter like this:

```
$factory->createClient('quassel://localhost?ping=0');
```

> This method uses Quassel IRC's probing mechanism for the correct protocol to use (newer "datastream" protocol or original "legacy" protocol). Protocol handling will be abstracted away for you, so you don't have to worry about this (see also below for more details about protocol messages). Note that this project does not currently implement encryption and compression support.

### Client

[](#client)

The `Client` is responsible for exchanging messages with your Quassel IRC core and emitting incoming messages. It implements the [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface), i.e. it is both a normal readable and writable stream instance.

#### Commands

[](#commands)

The `Client` exposes several public methods which can be used to send outgoing commands to your Quassel IRC core:

```
$client->writeClientInit()
$client->writeClientLogin($user, $password);

$client->writeHeartBeatRequest($time);
$client->writeHeartBeatReply($time);

$client->writeBufferRequestBacklog($bufferId, $messageIdFirst, $messageIdLast, $maxAmount, $additional);
$client->writeBufferRequestBacklogAll($messageIdFirst, $messageIdLast, $maxAmount, $additional);
$client->writeBufferInput($bufferInfo, $input);

// many more…
```

Listing all available commands is out of scope here, please refer to the [class outline](src/Client.php).

#### Processing

[](#processing)

Sending commands is async (non-blocking), so you can actually send multiple commands in parallel. You can send multiple commands in parallel, pending commands will be pipelined automatically.

Quassel IRC has some *interesting* protocol semantics, which means that commands do not use request-response style. *Some* commands will trigger a message to be sent in response, see [on()](#on) for more details.

#### on()

[](#on)

The `on($eventName, $eventHandler)` method can be used to register a new event handler. Incoming events will be forwarded to registered event handler callbacks:

```
$client->on('data', function ($data) {
    // process an incoming message (raw message object or array)
    var_dump($data);
});

$client->on('end', function () {
    // connection ended, client will close
});

$client->on('error', function (Exception $e) {
    // an error occured, client will close
});

$client->on('close', function () {
    // the connection to Quassel IRC just closed
});
```

The `data` event will be forwarded with the PHP representation of whatever the remote Quassel IRC core sent to this client. From a consumer perspective this looks very similar to a parsed JSON structure, but this actually uses a binary wire format under the hood. This library exposes this parsed structure as-is and does usually not change anything about it.

There are only few noticable exceptions to this rule:

- Incoming buffers/channels and chat messages use complex data models, so they are represented by `BufferInfo` and `Message` respectively. All other data types use plain structured data, so you can access its object-based structure very similar to a JSON-like data structure.
- The legacy protocol uses plain times for heartbeat messages while the newer datastream protocol uses `DateTime` objects. This library always converts this to `DateTime` for consistency reasons.
- The initial `Network` synchronization uses different structures for the `IrcUsersAndChannels` format structures depending on which wire-protocol is used. This library always exposes this structure in its simpler "logic" form for consistency reasons. This means it always contains the keys `Users` and `Channels` which both contain a list of objects decribing each element.

This combined basically means that you should always get consistent `data`events for both the legacy protocol and the newer datastream protocol.

#### close()

[](#close)

The `close()` method can be used to force-close the Quassel connection immediately.

Install
-------

[](#install)

The recommended way to install this library is [through Composer](https://getcomposer.org). [New to Composer?](https://getcomposer.org/doc/00-intro.md)

This will install the latest supported version:

```
$ composer require clue/quassel-react:^0.7
```

See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.

This project aims to run on any platform and thus does not require any PHP extensions and supports running on legacy PHP 5.3 through current PHP 8+ and HHVM. It's *highly recommended to use PHP 7+* for this project.

Internally, it will use the `ext-mbstring` for converting between different character encodings for message strings. If this extension is missing, then this library will use a slighty slower Regex work-around that should otherwise work equally well. Installing `ext-mbstring` is highly recommended.

Tests
-----

[](#tests)

To run the test suite, you first need to clone this repo and then install all dependencies [through Composer](https://getcomposer.org):

```
$ composer install
```

To run the test suite, go to the project root and run:

```
$ php vendor/bin/phpunit
```

The test suite contains both unit tests and functional integration tests. The functional tests require access to a running Quassel core server instance and will be skipped by default.

Note that the functional test suite contains tests that set up your Quassel core (i.e. register your initial user if not already present). This test will be skipped if your core is already set up. You can use a [Docker container](https://github.com/clue/docker-quassel-core)if you want to test this against a fresh Quassel core:

```
$ docker run -it --rm -p 4242:4242 clue/quassel-core -d

```

If you want to run the functional tests, you need to supply *your* Quassel login details in environment variables like this:

```
$ QUASSEL_HOST=127.0.0.1 QUASSEL_USER=quassel QUASSEL_PASS=secret phpunit
```

License
-------

[](#license)

This project is released under the permissive [MIT license](LICENSE).

> Did you know that I offer custom development services and issuing invoices for sponsorships of releases and for contributions? Contact me (@clue) for details.

This library took some inspiration from other existing tools and libraries. As such, a huge shoutout to the authors of the following repositories!

- [Quassel](https://github.com/quassel/quassel)
- [QuasselDroid](https://github.com/sandsmark/QuasselDroid)
- [node-libquassel](https://github.com/magne4000/node-libquassel)
- [node-qtdatastream](https://github.com/magne4000/node-qtdatastream)

###  Health Score

29

—

LowBetter than 59% of packages

Maintenance17

Infrequent updates — may be unmaintained

Popularity20

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 93.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~265 days

Recently: every ~404 days

Total

9

Last Release

1907d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/776829?v=4)[Christian Lück](/maintainers/clue)[@clue](https://github.com/clue)

---

Top Contributors

[![clue](https://avatars.githubusercontent.com/u/776829?v=4)](https://github.com/clue "clue (151 commits)")[![SimonFrings](https://avatars.githubusercontent.com/u/44357440?v=4)](https://github.com/SimonFrings "SimonFrings (8 commits)")[![carusogabriel](https://avatars.githubusercontent.com/u/16328050?v=4)](https://github.com/carusogabriel "carusogabriel (1 commits)")[![PaulRotmann](https://avatars.githubusercontent.com/u/85174210?v=4)](https://github.com/PaulRotmann "PaulRotmann (1 commits)")

---

Tags

asyncreactphpircQuassel

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/clue-quassel-react/health.svg)

```
[![Health](https://phpackages.com/badges/clue-quassel-react/health.svg)](https://phpackages.com/packages/clue-quassel-react)
```

###  Alternatives

[react/socket

Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP

1.3k116.9M402](/packages/react-socket)[clue/docker-react

Async, event-driven access to the Docker Engine API, built on top of ReactPHP.

113154.9k1](/packages/clue-docker-react)[react/dns

Async DNS resolver for ReactPHP

536114.1M100](/packages/react-dns)[league/geotools

Geo-related tools PHP 7.3+ library

1.4k5.3M26](/packages/league-geotools)[react/promise-timer

A trivial implementation of timeouts for Promises, built on top of ReactPHP.

34141.9M96](/packages/react-promise-timer)[react/async

Async utilities and fibers for ReactPHP

2228.8M171](/packages/react-async)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
