Initial commit: Telegram voice/video transcription bot in Zig
Long-polling bot that accepts voice messages and video notes, sends them to Whisper STT API, and replies with transcription text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
114
src/telegram.zig
Normal file
114
src/telegram.zig
Normal file
@@ -0,0 +1,114 @@
|
||||
const std = @import("std");
|
||||
const http = @import("http.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const Voice = struct {
|
||||
file_id: []const u8,
|
||||
duration: ?i64 = null,
|
||||
};
|
||||
|
||||
pub const VideoNote = struct {
|
||||
file_id: []const u8,
|
||||
duration: ?i64 = null,
|
||||
};
|
||||
|
||||
pub const Message = struct {
|
||||
message_id: i64,
|
||||
chat: struct { id: i64 },
|
||||
voice: ?Voice = null,
|
||||
video_note: ?VideoNote = null,
|
||||
};
|
||||
|
||||
pub const Update = struct {
|
||||
update_id: i64,
|
||||
message: ?Message = null,
|
||||
};
|
||||
|
||||
pub const GetUpdatesResponse = struct {
|
||||
ok: bool,
|
||||
result: []Update = &.{},
|
||||
};
|
||||
|
||||
pub const GetFileResponse = struct {
|
||||
ok: bool,
|
||||
result: ?struct {
|
||||
file_path: ?[]const u8 = null,
|
||||
} = null,
|
||||
};
|
||||
|
||||
pub const SendMessageBody = struct {
|
||||
chat_id: i64,
|
||||
text: []const u8,
|
||||
reply_to_message_id: ?i64 = null,
|
||||
};
|
||||
|
||||
pub const TelegramBot = struct {
|
||||
allocator: Allocator,
|
||||
token: []const u8,
|
||||
api_base: []const u8,
|
||||
|
||||
pub fn init(allocator: Allocator, token: []const u8) !TelegramBot {
|
||||
const api_base = try std.fmt.allocPrint(allocator, "https://api.telegram.org/bot{s}", .{token});
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.token = token,
|
||||
.api_base = api_base,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TelegramBot) void {
|
||||
self.allocator.free(self.api_base);
|
||||
}
|
||||
|
||||
pub fn getUpdates(self: *TelegramBot, offset: i64, timeout: u32) !std.json.Parsed(GetUpdatesResponse) {
|
||||
const url = try std.fmt.allocPrint(self.allocator, "{s}/getUpdates?offset={d}&timeout={d}&allowed_updates=[\"message\"]", .{ self.api_base, offset, timeout });
|
||||
defer self.allocator.free(url);
|
||||
|
||||
const body = try http.httpGet(self.allocator, url);
|
||||
defer self.allocator.free(body);
|
||||
|
||||
return std.json.parseFromSlice(GetUpdatesResponse, self.allocator, body, .{ .ignore_unknown_fields = true });
|
||||
}
|
||||
|
||||
pub fn getFilePath(self: *TelegramBot, file_id: []const u8) ![]u8 {
|
||||
const url = try std.fmt.allocPrint(self.allocator, "{s}/getFile?file_id={s}", .{ self.api_base, file_id });
|
||||
defer self.allocator.free(url);
|
||||
|
||||
const body = try http.httpGet(self.allocator, url);
|
||||
defer self.allocator.free(body);
|
||||
|
||||
const parsed = try std.json.parseFromSlice(GetFileResponse, self.allocator, body, .{ .ignore_unknown_fields = true });
|
||||
defer parsed.deinit();
|
||||
|
||||
if (parsed.value.result) |result| {
|
||||
if (result.file_path) |fp| {
|
||||
return self.allocator.dupe(u8, fp);
|
||||
}
|
||||
}
|
||||
return error.HttpRequestFailed;
|
||||
}
|
||||
|
||||
pub fn downloadFile(self: *TelegramBot, file_path: []const u8, dest: []const u8) !void {
|
||||
const url = try std.fmt.allocPrint(self.allocator, "https://api.telegram.org/file/bot{s}/{s}", .{ self.token, file_path });
|
||||
defer self.allocator.free(url);
|
||||
|
||||
try http.downloadToFile(self.allocator, url, dest);
|
||||
}
|
||||
|
||||
pub fn sendMessage(self: *TelegramBot, chat_id: i64, text: []const u8, reply_to: ?i64) !void {
|
||||
const url = try std.fmt.allocPrint(self.allocator, "{s}/sendMessage", .{self.api_base});
|
||||
defer self.allocator.free(url);
|
||||
|
||||
const msg = SendMessageBody{
|
||||
.chat_id = chat_id,
|
||||
.text = text,
|
||||
.reply_to_message_id = reply_to,
|
||||
};
|
||||
|
||||
const json_body = std.json.Stringify.valueAlloc(self.allocator, msg, .{}) catch return;
|
||||
defer self.allocator.free(json_body);
|
||||
|
||||
const resp = http.httpPostJson(self.allocator, url, json_body) catch return;
|
||||
self.allocator.free(resp);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user