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:
104
src/http.zig
Normal file
104
src/http.zig
Normal file
@@ -0,0 +1,104 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub fn httpGet(allocator: Allocator, url: []const u8) ![]u8 {
|
||||
var client: std.http.Client = .{ .allocator = allocator };
|
||||
defer client.deinit();
|
||||
|
||||
var aw: std.Io.Writer.Allocating = .init(allocator);
|
||||
defer aw.deinit();
|
||||
|
||||
const result = client.fetch(.{
|
||||
.location = .{ .url = url },
|
||||
.response_writer = &aw.writer,
|
||||
}) catch return error.HttpRequestFailed;
|
||||
|
||||
if (result.status != .ok) return error.HttpRequestFailed;
|
||||
|
||||
return aw.toOwnedSlice() catch return error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn httpPostJson(allocator: Allocator, url: []const u8, body: []const u8) ![]u8 {
|
||||
var client: std.http.Client = .{ .allocator = allocator };
|
||||
defer client.deinit();
|
||||
|
||||
var aw: std.Io.Writer.Allocating = .init(allocator);
|
||||
defer aw.deinit();
|
||||
|
||||
const result = client.fetch(.{
|
||||
.location = .{ .url = url },
|
||||
.method = .POST,
|
||||
.payload = body,
|
||||
.headers = .{ .content_type = .{ .override = "application/json" } },
|
||||
.response_writer = &aw.writer,
|
||||
}) catch return error.HttpRequestFailed;
|
||||
|
||||
if (result.status != .ok) return error.HttpRequestFailed;
|
||||
|
||||
return aw.toOwnedSlice() catch return error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn httpPostMultipart(
|
||||
allocator: Allocator,
|
||||
url: []const u8,
|
||||
file_field: []const u8,
|
||||
file_path: []const u8,
|
||||
file_name: []const u8,
|
||||
fields: []const [2][]const u8,
|
||||
) ![]u8 {
|
||||
const boundary = "----ZigMultipartBoundary9876543210";
|
||||
|
||||
// Build multipart body
|
||||
var body_writer: std.Io.Writer.Allocating = .init(allocator);
|
||||
defer body_writer.deinit();
|
||||
|
||||
// Add form fields
|
||||
for (fields) |field| {
|
||||
body_writer.writer.print("--{s}\r\nContent-Disposition: form-data; name=\"{s}\"\r\n\r\n{s}\r\n", .{ boundary, field[0], field[1] }) catch return error.OutOfMemory;
|
||||
}
|
||||
|
||||
// Add file field header
|
||||
body_writer.writer.print("--{s}\r\nContent-Disposition: form-data; name=\"{s}\"; filename=\"{s}\"\r\nContent-Type: application/octet-stream\r\n\r\n", .{ boundary, file_field, file_name }) catch return error.OutOfMemory;
|
||||
|
||||
// Read and append file content
|
||||
const file = std.fs.openFileAbsolute(file_path, .{}) catch return error.HttpRequestFailed;
|
||||
defer file.close();
|
||||
const file_content = file.readToEndAlloc(allocator, 100 * 1024 * 1024) catch return error.HttpRequestFailed;
|
||||
defer allocator.free(file_content);
|
||||
|
||||
body_writer.writer.writeAll(file_content) catch return error.OutOfMemory;
|
||||
body_writer.writer.print("\r\n--{s}--\r\n", .{boundary}) catch return error.OutOfMemory;
|
||||
|
||||
const body = body_writer.written();
|
||||
|
||||
// Send request
|
||||
var client: std.http.Client = .{ .allocator = allocator };
|
||||
defer client.deinit();
|
||||
|
||||
var response_aw: std.Io.Writer.Allocating = .init(allocator);
|
||||
defer response_aw.deinit();
|
||||
|
||||
const content_type = std.fmt.allocPrint(allocator, "multipart/form-data; boundary={s}", .{boundary}) catch return error.OutOfMemory;
|
||||
defer allocator.free(content_type);
|
||||
|
||||
const result = client.fetch(.{
|
||||
.location = .{ .url = url },
|
||||
.method = .POST,
|
||||
.payload = body,
|
||||
.headers = .{ .content_type = .{ .override = content_type } },
|
||||
.response_writer = &response_aw.writer,
|
||||
}) catch return error.HttpRequestFailed;
|
||||
|
||||
if (result.status != .ok) return error.HttpRequestFailed;
|
||||
|
||||
return response_aw.toOwnedSlice() catch return error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn downloadToFile(allocator: Allocator, url: []const u8, dest_path: []const u8) !void {
|
||||
const data = try httpGet(allocator, url);
|
||||
defer allocator.free(data);
|
||||
|
||||
const file = std.fs.createFileAbsolute(dest_path, .{}) catch return error.HttpRequestFailed;
|
||||
defer file.close();
|
||||
file.writeAll(data) catch return error.HttpRequestFailed;
|
||||
}
|
||||
Reference in New Issue
Block a user