This is the companion app to "Matrix Reloaded" LED matrix display. Written in Python with full asyncio concurrency, the "Matrix Display Controller" connects your Twitch chat to the LED matrix display.
Emotes and emojis posted in chosen channel(s) are fed to the LED matrix as an interactive element of streamers' set. The app provides a rudimentary remote control interface allowing integration in a workflow.
Necessary assets are downloaded and cached in host system's temporary directory. Any logic is done before sending an image to the matrix which is a simple queue.
Purposely rendered Twemoji emojis are used.
Python 3.11+
Libraries used: aiohttp, aiofile, emoji, loguru
Download or clone the repo. Alternatively, files you really need for running the app are matrix_display.py
and requirements.txt
.
All dependencies are available from PyPI. You probably should create a Python virtual environment to install them:
python -m venv matrix_env
Then switch to the virtual environment and install packages using pip:
. matrix_env/bin/activate
for POSIX shell
matrix_env\Scripts\activate.bat
for cmd
matrix_env\Scripts\Activate.ps1
for PowerShell
pip install -r requirements.txt
matrix_display.py has a command line interface. To run, first activate the venv as above then use Python from the local environment.
Try python matrix_display.py --help
to get the usage prompt below.
Two convenience tools that cover most uses are provided in the windows/
directory.
Download or clone the repo. Python interpreter must be available in your environment (select add to PATH when you install Python). Try python --version
in cmd.
make.bat performs installation steps and creates windows/matrix_controller/
containing everything needed and which you can move elsewhere on the same machine. You need to regenerate when matrix_display.py
or Python is updated: delete subdirectories of windows/
and re-run the script.
Use matrix_controller\run.bat
in a terminal to launch the app as you would be running matrix_display.py directly.
generate_exe.bat uses PyInstaller to produce a standalone executable you'll find in windows/exe/dist/
.
You can remove subdirectories of windows/
once you have copied matrix_display.exe
.
PyInstaller supports using UPX in order to reduce executable size. Pass the path to the directory containing upx.exe
as first argument of generate_exe.bat if you'd like to use UPX.
The matrix display is a simple image queue which preserves upload order. The buffer of available memory is divided into fixed size slots, each holding one image. A slot is freed once the image it contains has been displayed. No logic besides consuming queue items happens at the receiving end. The display firmware directly decodes PNG and GIF and supports resizing. matrix_display.py
listens to Twitch chat, collects emotes and emojis, then uploads them to the display over HTTP while ensuring maximum use of the queue.
This app connects to Twitch Messaging Interface (TMI) as the anonymous user (read only), no credentials are required. You can join multiple channels at the same time (as described in Twitch Developers documentation). Letter case doesn't matter.
Example: python matrix_display.py "#ioodyme,#ElisaK_,#Rancune_,#Yorzian,#SarahCoponat"
Note
Joining a channel is an asynchronous action. It only succeeded if you see the "Successfully joined #… as justinfan…!" message.
Twitch emotes and emojis from incoming messages are collected and may be ranked by popularity according to the following logic: if matrix display buffer is not full each image is sent right away, otherwise a counter of occurences is incremented for each image not yet sent. The ranking resulting from this process is used to decide upload priority order when one or more image slot becomes available in the matrix display. Higher number of occurences equals higher priority. When an image is sent to the display, its counter is reset.
Important
By default multiple occurences of the same emote/emoji in a message are counted. This behaviour can be disabled using --no-summation
in order to prevent emote priority war by flooding with very long messages. On the other hand, default function better reflects viewer excitement if they don't abuse it. When this argument is used, any amount of the same image in a message counts for 1.
Twitch emote files are obtained from Twitch static CDN, emojis are Twemoji purposely rendered at 128x128 served by jsDelivr. Image files are downloaded once and cached forever in python_matrix_reloaded_cache
directory located at user's temporary files path as returned by tempfile Python module.
Under Windows it yields <User Directory>/AppData/Local/Temp/python_matrix_reloaded_cache/
.
Note
This cache can be removed for cleanup purposes or when an emoji is updated using --purge
.
By default the target display is matrix-reloaded.local
, this can be changed by specifying --matrix-hostname
. While the display is unreachable, emote/emoji collection, ranking and download remain running. The backlog is uploaded to the matrix display as soon as it is available.
The command interface is intended to help handing over control of the matrix display to another software in an automation system. It doesn't provide remote dynamic full functionality.
Text over a TCP connection is used to issue commands. The command interface can be enabled by providing the port you'd like to use: --command-port <port>
. Concurrent connections are not supported, which greatly simplified implementation. When a new connection is established, the existing one (if any) is closed. You will receive a banner when the connection is established. All commands have a response message.
Available commands are explained in the following table. I've reused the IRC Protocol syntax. Charset is UTF-8, end of line character is LF.
Command | Description | Explanation |
---|---|---|
ON | Connects to TMI and starts operation. |
Enables or disables this software. When "OFF", TMI is disconnected and no contact with the matrix display is attempted. Only the command interface remains active. Important If one or more channel is provided in the command line, it is joined during the startup sequence. |
OFF | Disconnects from TMI and stops operation. | |
CLEAR | Clears all queues and the matrix display. | Empties local and remote queue and blacks out the display. |
PAUSE | Stops sending images to the matrix display, emotes and emoji collection remaining active. |
These commands are intended for handing over control of the display to a third party client. HTTP requests can then be issued to the display without interference when paused.
Example workflow:
|
RESUME | Resumes sending images to the matrix display. | |
JOIN :<#chan>{,<#chan>{,…}} | Joins <#chan>. |
Example: |
Although the command interface is meant for machine control, terminal-friendly features are provided for debug and exploration purposes.
First connect the TCP socket to the port you specified via command line input with your favourite client. telnet or PuTTY can be used. There is no protocol.
Immediately send the command TELNET
after receiving the first message. This will replace line endings you receive by CR LF and interpret BS (backspace) so you can type normally in your terminal. You will receive the welcome banner again, with CR LF line endings. The "telnet mode" lasts until you close the connection.
Send the command ?
or h
or help
to get help.
Example with the software running on the same machine at port 6666:
Microsoft Telnet> o ::1 6666 ⏎
> Matrix Display Controller v1.0
> Type '?' to obtain available commands.
> Hello ::1!
< telnet
> CR LF line breaks
> BS is interpreted
> Matrix Display Controller v1.0
> Type '?' to obtain available commands.
> Hello ::1!
< ?
> ** Command list **
> | ? - Shows this message.
> | ON - Starts operation.
> | OFF - Stops operation.
> | CLEAR - Clears all queues and the matrix display.
> | PAUSE - Stops sending images to the matrix display, emotes and emoji collection remaining active.
> | RESUME - Resumes sending images to the matrix display. The backlog is sent.
> | TELNET - All line breaks (LF) are converted to CR LF for the lifetime of the connection.
> | JOIN :<#chan>{,<#chan>{,...}} - Joins <#chan>.
The default behaviour of this program is to connect to TMI and join specified channels autonomously upon startup. The user providing a channel string (the one positional argument) is required.
When --interactive
argument is used, no action is automatically performed. This mode is intended to be used with the command interface, as an always-running service. Thus --command-port
becomes required, you must enable the command interface in order to do anything.
The channel string provided by command line input becomes optional. If one is given, joining will be attempted automatically after a successful connection to TMI.
-
Logging: by default there are good and bad warnings output to stderr (level SUCCESS and WARNING). If you only want warnings when something's wrong, for automated log processing: set
--log-level warning
explicitely. -
--forbidden-users
allows you to ignore your bots so that emojis in notifications and responses to stats queries are not shown. Multiple usernames can be passed separated by a comma, letter case doesn't matter.
Example:--forbidden-users WizeBot,StreamElements
-
--forbidden-emotes
allows you to ignore specific Twitch emotes. Multiple emote ids can be passed separated by a comma.
This argument takes an emote id. You can get the identifier of an emote using Twitch API or by browsing twitchemotes.com.
Example:--forbidden-emotes emotesv2_bf2ee530e5a04b5bb305847719998dc7,emotesv2_c9108ca6f1c344e287e1a565ce4dbd57
Tip
It is advised that you use --log-level debug
while setting up in order to see all messages.
The basic scenario is running the program when you need it, and terminate it when you don't. The operation with a simple command line is easy:
matrix_display.py "#ioodyme"
Maybe you have a chat services bot you want to ignore because it uses emojis in its responses to commands:
matrix_display.py --forbidden-users WizeBot "#ioodyme"
While you are experimenting, it is good to set logging level to DEBUG in order to see everything happening:
matrix_display.py --log-level debug --forbidden-users WizeBot "#ioodyme"
In a streaming setup you'll want to integrate the display to your automation system. A direct TCP connection from your automation bot to the command interface is meant to control an always running instance of this software. In this use case it is up to the system the software is running on to maintain it up, reachable and connected to LAN and WAN as any service. On the other hand, the automation bot (Node-RED as an example) sends purely functional commands.
The most basic command line to launch with the remote control interface enabled is:
matrix_display.py --command-port 6666
You have to provide an available port for the software to listen on. As described in Command Interface, specifying a channel to join is optional.
Upon startup the software connects to TMI and does nothing or joins provided channel(s) to start usual operation. This is equivalent to issuing the ON
command.
You will most likely want to use --interactive
in order to prevent any action that wasn't explicitely commanded and obtain a service-like operation. With the --interactive
switch activated, no action happens upon startup. The automation will ask the service to connect (ON
) or disconnect (OFF
). If a channel is specified in the command line, it will be automatically joined after the ON
command is processed. Usually you'll specify a channel you always want to join in the command line (your own). A realistic start command would be:
matrix_display.py --command-port 6666 --interactive --forbidden-users WizeBot "#ioodyme"
Note
No deamonization is implemented, the operating system has to handle running the Python application in the background.
explanation about joining channels
Important
A channel cannot be PARTed, you will have to disconnect (OFF
) in order to reset joined channels.
use case about pause and clear
Matrix Display Controller v1.0 [-h] [-q] [-s] [-u] [-i]
[--matrix-hostname MATRIX_HOSTNAME]
[--log-level {TRACE,DEBUG,INFO,SUCCESS,WARNING,ERROR,CRITICAL}]
[--forbidden-emotes FORBIDDEN_EMOTES]
[--forbidden-users FORBIDDEN_USERS]
[--command-port COMMAND_PORT]
[--purge]
[--version]
[--license]
[chan]
positional arguments:
chan Required if standalone. Twitch Messaging Interface channel(s) to join.
Format: <#chan>{,<#chan>{,...}}
options:
-h, --help show this help message and exit
--matrix-hostname MATRIX_HOSTNAME
Defaults to 'matrix-reloaded.local'.
Matrix display hostname or IP address to connect to.
--log-level {TRACE,DEBUG,INFO,SUCCESS,WARNING,ERROR,CRITICAL}
Defaults to INFO. Messages level SUCCESS and higher are output to stderr.
Level SUCCESS corresponds to successful events that are important
to the user (good warnings), select WARNING if you want to only be notified
of failure warnings.
Setting log level to DEBUG is suggested while experimenting.
TRACE level prints IRC communications, which will expose credentials!
-q, --quiet No ouput to stdout. All messages are ouput to stderr.
Log level can still be set using --log-level. Defaults to SUCCESS then.
-s, --silent No output.
--forbidden-emotes FORBIDDEN_EMOTES
Comma-separated list of forbidden Twitch emote ids.
--forbidden-users FORBIDDEN_USERS
Comma-separated list of Twitch users to be ignored.
Use this to ignore your bots.
-u, --no-summation Don't count repetitions of the same emote/emoji in A message.
-i, --interactive Don't do anything. Wait for commands on the command interface.
--command-port is mandatory.
--command-port COMMAND_PORT
TCP port for the command interface.
The command interface is disabled if this argument is not specified.
--purge Cleans the local cache and exits. Sometimes emojis get corrections.
--version Shows version and exits.
--license Shows license prompt and exits.
Built-in forbidden Twitch emotes: MercyWing1, MercyWing2, PowerUpL, PowerUpR, Squid1, Squid2,
Squid4, DinoDance
-
At least on Windows, when command interface is enabled, sometimes an uncaught exception of unknown origin prevents the app from shutting down upon a SIGTERM. The workaround is issuing SIGTERM twice in order to ensure shutdown if it aborted the first time.
This issue is under investigation. -
Extremely rarely, error "Display: Cache miss. This isn't supposed to happen!" appears. Please report it, accompanied by a description of what you were doing.
The one-file Python app and associated scripted tools are licensed under GNU Affero General Public License version 3.0 and comes with absolutely no warranty. Full license text is available as the LICENSE file, which is a mandatory part of this distribution.
This program is free software: you can redistribute it and/or modify it under specified terms. More information here.
The Windows icon ioodymDeni.ico
is provided by Alain Gervasi following terms of CC0 1.0 Universal.