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>
105 lines
3.7 KiB
Zig
105 lines
3.7 KiB
Zig
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;
|
|
}
|