- C 98%
- CMake 2%
Added new !yt command (cmd_yt.h) that fetches the title and channel name of a YouTube video using the YouTube oEmbed API. The video URL is percent-encoded via a new yt_url_encode() helper before being passed as a query parameter. The JSON response is parsed for 'title' and 'author_name' fields using the existing json_find() helper, and the result is formatted as 'Title - Channel'. - New file: cmd_yt.h with yt_url_encode() and cmd_yt() command handler - CMakeLists.txt: added WITH_YT feature toggle (ON by default) - zynk.c: added conditional #include for cmd_yt.h - irc.h: added cmd_yt() dispatch in handle_privmsg() - cmd_help.h: added !yt <url> to the help listing - zynk.c: added doc comments to json_find() function - cmd_yt.h: added doc comments to yt_url_encode() and cmd_yt() - CHANGELOG.md: added 0.41.0 entry documenting all changes - README.md: updated feature summary, features list, feature toggles table, minimal build command, and IRC commands utilities table |
||
|---|---|---|
| .gitignore | ||
| AGENTS.md | ||
| ai.h | ||
| CHANGELOG.md | ||
| CMakeLists.txt | ||
| cmd_ai.h | ||
| cmd_calc.h | ||
| cmd_changelog.h | ||
| cmd_code.h | ||
| cmd_forecast.h | ||
| cmd_gitlog.h | ||
| cmd_greeting_or_chat.h | ||
| cmd_help.h | ||
| cmd_ping.h | ||
| cmd_quit.h | ||
| cmd_rebuild.h | ||
| cmd_reload.h | ||
| cmd_restart.h | ||
| cmd_seen.h | ||
| cmd_stock.h | ||
| cmd_tell.h | ||
| cmd_time.h | ||
| cmd_uptime.h | ||
| cmd_version.h | ||
| cmd_weather.h | ||
| cmd_yt.h | ||
| cmd_zynk.h | ||
| db.h | ||
| irc.h | ||
| LICENSE | ||
| net.h | ||
| README.md | ||
| tls.h | ||
| zynk.c | ||
| zynk.h | ||
zynk - An IRC bot
An IRC bot in C. Features a key-value store backed by SQLite3, weather lookups via wttr.in, stock quotes, calculator, time, user tracking, YouTube title lookups, and AI-powered replies.
This is a fork!
This is a fork from my good friend Armin's calc bot. He started the project, but he is working on a lot of awesome other things like plugins (VST and AU) for your DAW right now. So, I forked this awesome code, and I am working on this now.
I want to thank Armin for his work and for sharing his code with me. Take a look at his profile here to see his awesome work.
Features
- Key-Value Store: Persist facts, notes, or any data with
!zynk key = value; Overwrite existing keys with!zynk! key = value - Weather: Current conditions and 3-day forecasts via
wttr.in - Stock Quotes: Real-time stock prices via Yahoo Finance
- Calculator: Evaluate math expressions with
!calc - Time: Show current time in any timezone with
!time - User Tracking: Track when users were last seen with
!seen - Offline Messages: Leave messages for offline users with
!tell - AI Queries: Ask questions via
!aior by addressing the bot; powered byopencode - Code Changes: AI-powered code editing with auto-recompile and hot reload
- YouTube Titles: Look up video titles with
!yt <url>(via YouTube oEmbed API) - Chat Greetings: Responds to
hi,hello,hey,bye, and more - Hot Reload: Reload or restart without leaving channels, losing your nick, or dropping voice mode
- TLS: Secure connections via OpenSSL
- Rate Limiting: Per-nick protection against spam
- Reconnection: Auto-reconnect with exponential backoff
- Single File: Easy to deploy and modify
Requirements
gcc(or any C99 compiler)libssl-dev(OpenSSL)libdl(dynamic linker)libsqlite3(loaded at runtime viadlopen)curl(for weather, forecast, and stock lookups)bc(for the calculator command)git(for the gitlog command)date(for timezone-aware time lookups)tzdata(optional — without it,!timefalls back to 45 built-in timezones)opencode(for AI queries)
Build
Quick build
cmake -B build && cmake --build build
The binary is placed in build/zynk.
Install
cmake --build build --target install
Feature toggles
All commands are enabled by default. Disable any command at build time with -DWITH_<FEATURE>=OFF:
cmake -B build -DWITH_WEATHER=OFF -DWITH_AI=OFF
cmake --build build
Available options (the !command shown in parentheses):
| Option | Command | Description | Default |
|---|---|---|---|
WITH_ZYNK |
!zynk |
Key-value store | ON |
WITH_PING |
!ping |
Ping/pong | ON |
WITH_VERSION |
!version |
Version info | ON |
WITH_HELP |
!help |
Help listing | ON |
WITH_QUIT |
!quit / !die |
Shut down the bot | ON |
WITH_WEATHER |
!weather |
Weather lookup (requires curl) | ON |
WITH_FORECAST |
!forecast |
3-day forecast (requires curl) | ON |
WITH_STOCK |
!stock |
Stock quotes (requires curl) | ON |
WITH_CALC |
!calc |
Calculator (requires bc) | ON |
WITH_TIME |
!time |
Time/timezone lookup | ON |
WITH_SEEN |
!seen |
User tracking | ON |
WITH_TELL |
!tell |
Offline messages | ON |
WITH_GITLOG |
!gitlog |
Recent git commits | ON |
WITH_CHANGELOG |
!changelog |
Changelog entries | ON |
WITH_AI |
!ai |
AI questions (requires opencode) | ON (can not be disabled) |
WITH_CODE |
!code |
AI code changes (ops only) | OFF |
WITH_REBUILD |
!rebuild |
Rebuild & restart (ops only) | OFF |
WITH_UPTIME |
!uptime |
Uptime display | ON |
WITH_RELOAD |
!reload |
Hot reload (ops only) | ON |
WITH_RESTART |
!restart |
Restart (ops only) | ON |
WITH_YT |
!yt |
YouTube title lookup (requires curl) | ON |
WITH_GREETING_OR_CHAT |
<nick>: <text> |
Greeting/chat with AI fallback | ON (can not be disabled) |
Minimal build
Disable everything except the core store, quit, and help:
cmake -B build \
-DWITH_PING=OFF -DWITH_VERSION=OFF -DWITH_WEATHER=OFF -DWITH_FORECAST=OFF \
-DWITH_STOCK=OFF -DWITH_CALC=OFF -DWITH_TIME=OFF -DWITH_SEEN=OFF \
-DWITH_TELL=OFF -DWITH_GITLOG=OFF -DWITH_CHANGELOG=OFF -DWITH_AI=OFF \
-DWITH_CODE=OFF -DWITH_REBUILD=OFF -DWITH_UPTIME=OFF -DWITH_RELOAD=OFF \
-DWITH_RESTART=OFF -DWITH_YT=OFF -DWITH_GREETING_OR_CHAT=OFF
cmake --build build
Clean
rm -rf build
Usage
./zynk [options]
Options
| Option | Description |
|---|---|
-ssl |
Enable TLS (auto-enabled on ports 6697/7000) |
-noverify |
Skip TLS certificate verification |
-server <host> |
IRC server (default: irc.libera.chat) |
-port <p> |
Port (default: 6697) |
-pass <p> |
Server password |
-nick <n> |
Nickname (default: zynk) |
-join <c> |
Auto-join channels, comma-separated (default: #zynk) |
-opencode <p> |
Path to opencode binary (default: /usr/bin/opencode) |
-h |
Show help |
Example
./zynk -ssl -nick mybot -join "#mychan,#myotherchan"
IRC Commands
General
| Command | Description |
|---|---|
!help |
List all available commands |
!version |
Show the bot version |
!ping |
Responds with pong |
!uptime |
Show bot uptime |
!quit / !die |
Shut down the bot (ops only) |
Key-Value Store
| Command | Description |
|---|---|
!zynk <key> |
Look up a stored value |
!zynk <key> = <value> |
Store a value (key is lowercased) |
!zynk! <key> = <value> |
Overwrite an existing value (ops in channels) |
!zynk |
Get a random stored entry |
Weather & Finance
| Command | Description |
|---|---|
!weather [city] |
Current weather (default: Berlin) |
!forecast [city] [full] |
3-day forecast; full adds weather description, sunrise/sunset, moonrise/moonset (default: Berlin) |
!stock <SYMBOL>[,SYMBOL...] |
Stock quotes, up to 3 symbols (e.g. !stock AAPL,MSFT) |
Utilities
| Command | Description |
|---|---|
!calc <expression> |
Evaluate math (see below) |
!time [timezone] |
Show current time (see below) |
!seen <nick> |
Check when a user was last seen |
!tell <nick> <message> |
Leave a message for an offline user |
!yt <url> |
Show YouTube video title and channel name |
!gitlog [count] |
Show recent git commits with links (default: 1, max: 10) |
!changelog [count] |
Show changelog entries (default: 1, max: 10) |
Time (!time)
Shows the current date and time. Without arguments, displays the bot's local time. With a timezone argument, displays the time in that timezone using POSIX TZ format.
Output format: YYYY-MM-DD HH:MM:SS
Examples:
| Command | Description |
|---|---|
!time |
Bot's local time |
!time UTC |
Current UTC time |
!time US/Eastern |
US Eastern time |
!time US/Pacific |
US Pacific time |
!time Europe/Berlin |
Central European time |
!time Europe/London |
UK time |
!time Asia/Tokyo |
Japan time |
!time Australia/Sydney |
Australian Eastern time |
If the system has tzdata installed, any IANA/Olson timezone name works. Otherwise, the bot falls back to a built-in table with the following timezones:
Built-in timezones (no tzdata required):
| Timezone | Region | Offset | DST |
|---|---|---|---|
UTC / GMT |
Global | +0 | No |
EST / CST / MST / PST / HST |
US (fixed) | -5 to -10 | No |
US/Eastern |
US Eastern | -5 / -4 | Yes |
US/Central |
US Central | -6 / -5 | Yes |
US/Mountain |
US Mountain | -7 / -6 | Yes |
US/Pacific |
US Pacific | -8 / -7 | Yes |
US/Alaska |
US Alaska | -9 / -8 | Yes |
US/Honolulu |
US Hawaii | -10 | No |
Canada/Toronto |
Canada Eastern | -5 / -4 | Yes |
Canada/Vancouver |
Canada Pacific | -8 / -7 | Yes |
America/Sao_Paulo |
Brazil | -3 / -2 | Yes |
America/Mexico_City |
Mexico | -6 / -5 | Yes |
America/Argentina/Buenos_Aires |
Argentina | -3 | No |
Europe/London |
UK | 0 / +1 | Yes |
Europe/Berlin |
Central Europe | +1 / +2 | Yes |
Europe/Paris |
France | +1 / +2 | Yes |
Europe/Moscow |
Russia | +3 | No |
Europe/Istanbul |
Turkey | +3 | No |
Asia/Tokyo |
Japan | +9 | No |
Asia/Shanghai |
China | +8 | No |
Asia/Kolkata |
India | +5:30 | No |
Asia/Dubai |
UAE | +4 | No |
Asia/Singapore |
Singapore | +8 | No |
Asia/Seoul |
South Korea | +9 | No |
Asia/Hong_Kong |
Hong Kong | +8 | No |
Asia/Taipei |
Taiwan | +8 | No |
Australia/Sydney |
Australia Eastern | +10 / +11 | Yes |
Australia/Melbourne |
Australia Eastern | +10 / +11 | Yes |
Australia/Brisbane |
Australia Eastern | +10 | No |
Australia/Perth |
Australia Western | +8 | No |
Australia/Adelaide |
Australia Central | +9:30 / +10:30 | Yes |
Australia/Darwin |
Australia Central | +9:30 | No |
Pacific/Auckland |
New Zealand | +12 / +13 | Yes |
Pacific/Honolulu |
Hawaii | -10 | No |
Pacific/Fiji |
Fiji | +12 / +13 | Yes |
Africa/Cairo |
Egypt | +2 | No |
Africa/Johannesburg |
South Africa | +2 | No |
Africa/Lagos |
West Africa | +1 | No |
Africa/Nairobi |
East Africa | +3 | No |
Atlantic/Reykjavik |
Iceland | +0 | No |
Calculator (!calc)
The calculator uses bc -l (POSIX arbitrary-precision calculator with the math library). Any valid bc expression works.
Basic arithmetic:
| Expression | Result |
|---|---|
!calc 2+2 |
4 |
!calc 100-37 |
63 |
!calc 6*7 |
42 |
!calc 100/3 |
33.3333333333333333333 |
!calc 10%3 |
1 |
Parentheses for grouping:
| Expression | Result |
|---|---|
!calc (3+7)*2 |
20 |
!calc (100-50)/5 |
10 |
Powers:
| Expression | Result |
|---|---|
!calc 2^10 |
1024 |
!calc 3^3 |
27 |
Constants:
| Expression | Description |
|---|---|
!calc pi() |
Pi (3.14159...) |
!calc pi()*2 |
Circumference of a unit circle |
!calc pi()^2 |
Pi squared |
Math library functions (enabled by -l):
| Expression | Description |
|---|---|
!calc s(1) |
Sine of 1 (radians) |
!calc c(0) |
Cosine of 0 |
!calc a(1) |
Arctangent of 1 |
!calc l(100) |
Natural logarithm of 100 |
!calc e(1) |
e^1 |
Scale (decimal places):
| Expression | Description |
|---|---|
!calc scale=4; 1/3 |
0.3333 |
Variables and multi-statement:
| Expression | Description |
|---|---|
!calc x=5; x*2 |
10 |
!calc 2+2; 3*3 |
4 (first line of output) |
AI
| Command | Description |
|---|---|
!ai <question> |
Ask the AI a question (plan mode, read-only) |
!code <instruction> |
AI code change with auto-recompile (ops only, in channels) |
<nick>: <question> |
Address the bot by nick to ask the AI |
Admin (Ops Only)
| Command | Description |
|---|---|
!reload |
Hot reload (preserve IRC session, fork-in-place) |
!restart |
Restart the bot (preserves session, same as !reload) |
!rebuild [pull] |
Rebuild and restart (ops only, in channels; pull runs Git pull before rebuilding) |
!code <instruction> |
Make AI-powered code changes |
Chat
| Command | Description |
|---|---|
<nick>: hi/hello/hey |
Greeting response |
<nick>: bye |
Farewell response |
<nick>: <anything> |
AI-powered conversation |
Database
Data is stored in zynk.db (SQLite3) in the working directory. The bot creates four tables:
zynk- key-value pairs with nick and timestamp metadataai_pending- single-row table for managing in-flight AI queriesseen- tracks when users were last seen (nick, timestamp, channel, action)tell- stores pending offline messages for delivery
Architecture
zynk uses a single poll() loop with a 100 ms timeout. AI queries fork a child process that runs the opencode CLI; the parent picks up the result on the next poll iteration. SQLite3 is loaded at runtime via dlopen to avoid a compile-time dependency. The bot supports hot reload: !reload and !restart fork without exec, so the child inherits the live socket and SSL session — the bot stays connected, keeps its nick, and remains voiced in channels. !rebuild/!code do a clean QUIT+exec since the binary changed on disk.
License
MIT - see LICENSE file.