windgram is a Zig library for Telegram bots.
The project is still early, but the core is real: HTTPS requests to Telegram, long polling, a multithreaded dispatcher, and owned response parsing so data does not outlive its buffers by accident.
The point of windgram right now is not to look complete. The point is to be a small base that is easy to understand, easy to extend, and already useful for simple bots.
What already works:
- real Telegram Bot API requests
getUpdateslong polling- multithreaded update processing
getMegetUpdatessendMessageRouterContextctx.reply()- a runnable demo bot in
example_bot/
What is still missing:
- most Bot API methods
- webhook support
- routers, filters, middleware
- FSM and higher-level bot ergonomics
const std = @import("std");
const windgram = @import("windgram");
const Bot = struct {
pub fn handle(_: *anyopaque, client: *windgram.Client, update: *const windgram.types.Update) !void {
const message = update.effectiveMessage() orelse return;
const text = message.text orelse return;
const result = try client.sendMessage(message.chat.id, text);
defer result.deinit();
switch (result) {
.ok => {},
.api_error => |api_err| std.log.err("{s}", .{api_err.description}),
}
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
var client = windgram.Client.init(gpa.allocator(), "PUT_BOT_TOKEN_HERE");
defer client.deinit();
var bot = Bot{};
var poller = try windgram.Poller.init(gpa.allocator(), &client, .{
.context = &bot,
.handle_fn = Bot.handle,
}, 4, 20);
defer poller.deinit();
try poller.start();
defer poller.stop();
while (true) std.time.sleep(1 * std.time.ns_per_s);
}The demo lives in example_bot/.
- Put a bot token into
example_bot/token.zig - Run
zig build run-example
The demo handles:
/start/help/ping/id/echo <text>
Anything else gets a normal text reply, so it is easy to verify that updates are coming in and being processed.
The project currently implements 3 Telegram Bot API methods:
getMegetUpdatessendMessage
The full method-by-method checklist lives in API_COVERAGE.md.
That checklist is based on the official Telegram Bot API page as of 2026-03-27:
zig build test
zig buildThe best contributions right now are small and concrete:
- add one missing method cleanly
- add missing types around already-supported methods
- add regression tests around parsing, ownership, or polling
- tighten docs where the current behavior is unclear
See CONTRIBUTING.md and ROADMAP.md.
There is now a small high-level layer on top of the core client:
RouteronCommand()onText()Contextctx.reply()
The low-level Client and Poller are still there. The higher-level API is just a convenience layer on top, not a replacement for the core pieces.
MIT