A Python-based Kick.com chat logger. Collects chat messages, user subscriptions, timeouts, bans, unbans, message deletions, pinned message events, etc. asynchronously from multiple channels and stores them in a database backend (SQLite or PostgreSQL).
The tool connects to Kick.com's WebSocket to log live chat events in real time. The system records almost all events, including chat messages, user subscriptions, bans and unbans, message deletions, pinned messages, stream hosting, and chatroom setting changes. All data is stored in a database backend (SQLite or PostgreSQL), with each channel having its own table. There is a command-line interface for adding, pausing, resuming, and managing channels, as well as viewing statistics. The program is designed to handle errors automatically, with built-in reconnection and retry logic to keep it running smoothly. It also uses curl-cffi to get around Cloudflare protection and avoid detection.
- Everything runs asynchronously with
asyncio, so you can scrape a lot of channels at once without blocking (soak tested with 100 channels for 30+ hours without issues). - All channels are multiplexed over a single Pusher WebSocket connection. Each channel gets its own queue and consumer, so one busy channel can't stall the others.
- The code is split up into logical pieces: API handling, database operations, websocket and event handling, and the CLI.
- Each type of Kick event (chat, subs, bans, etc.) gets its own parsing logic, so one can change the way events are handled.
- WebSockets: Uses the
websocketslibrary to listen to Kick's live chat stream. - API Calls:
curl-cffiis used for HTTP requests, mostly because Kick uses Cloudflare for protection and this gets around it. - Database: Storage is pluggable. Use SQLite (
aiosqlite) or PostgreSQL (asyncpg). Switch inconfig.py. - CLI: The command-line interface is built with
prompt_toolkitandrich.
- Channel names get cleaned up before being used as table names, so unique names are guaranteed, this handling might not cover all edge cases, but is sufficient.
- If the connection drops, it'll keep retrying forever with capped exponential backoff and jitter instead of spamming the server. Channel-level failures (banned or renamed channels) are marked as errored instead and can be retried with
resume <channel>. - The WebSocket connection self-pings (see
PING_INTERVAL_SECONDSinconfig.py) to stay alive and notice dead connections quickly, and answers Pusher's application-level pings. - Chatroom IDs are cached in the database, so restarts don't hit the Kick API for channels it has already seen. Fresh lookups are capped by
CHANNEL_LOOKUP_CONCURRENCYinconfig.py, andresume <channel>always looks the ID up fresh in case the cached one went stale. - Configs are stored in
config.py, so one can change the websocket URL if Kick changes it, ping interval, messages, etc. - Raw payloads are stored for debugging, though this increases storage requirements.
- Each channel gets its own database table for better query performance and easier data management.
-
Clone the repository:
git clone https://github.com/iberkayC/kick-chat-logger cd kick-chat-logger -
Use a virtual environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate -
Install dependencies:
pip install -r requirements.txt
cd src
python cli.py
add <channel_name>- Add and start monitoring a channelbulkadd <file>- Add and start monitoring every channel listed in a file (one name per line, blank lines and#comments ignored)pause <channel_name>- Temporarily stop monitoring a channelresume <channel_name>- Resume monitoring a paused channellist- Show all channels and their statusstats <channel_name>- Display statistics for a specific channelresume- Restart all active channelshelp- Show available commandsexit- Shutdown the scraper
The system uses the following two main table types (schema identical for SQLite and PostgreSQL):
Tracks all monitored channels and their status:
id- Primary keyname- Normalized channel nameadded_at- UTC timestamp when channel was addedpaused- Whether monitoring is pausedpaused_at- UTC timestamp when channel was paused
Each channel gets its own table (prefixed with kickchat_ on default) containing:
id- Primary keyevent_type- Type of event (chat message, subscription, ban, etc.)event_id- Kick's event identifierchatroom_id- Kick's chatroom identifiertimestamp- Event timestamp from Kickuser_id- User's Kick IDusername- User's display namecontent- Processed message content or event descriptionsender_data- User metadatametadata- Event-specific metadataraw_payload- Original JSON payload (for debugging, can be removed if wanted)created_at- UTC timestamp when event was stored locally
- If a channel gets banned or renamed while being logged, Pusher may still accept the subscription, so
listcan show it as running while it logs nothing. There is no automatic staleness detection, checkstatsonce in a while andresume <channel>anything suspicious to refresh it. - Channels that fail to subscribe (bans, renames, lookup errors) show as errored in
listand stay that way until manually retried withresume <channel>.
You may contact me at ceylaniberkay@gmail.com
The default storage type is SQLite which gets database locked errors on high traffic.
- Open
src/config.py. - Set
STORAGE_TYPE = "sqlite" # or "postgresql" - For PostgreSQL, copy
.env.exampleto.envand fill in your connection details:KICK_PG_HOST=localhost KICK_PG_PORT=5432 KICK_PG_DB=kick_chat_logger KICK_PG_USER=kickscraper KICK_PG_PASSWORD=changeme