diff --git a/src/client.zig b/src/client.zig index 5f43934..c3e5f6b 100644 --- a/src/client.zig +++ b/src/client.zig @@ -35,6 +35,7 @@ pub const Client = struct { } pub fn getJSON(self: *@This(), comptime T: type, url: [*:0]const u8, headers: ?*std.StringHashMap([]const u8)) anyerror!T { + var response_buffer = std.ArrayList(u8).init(self.allocator.*); if (cURL.curl_easy_setopt(self.ptr, cURL.CURLOPT_URL, url) != cURL.CURLE_OK) return error.CURLPerformFailed; if (cURL.curl_easy_setopt(self.ptr, cURL.CURLOPT_HTTPGET, @as(c_long, 1)) != cURL.CURLE_OK) @@ -48,8 +49,8 @@ pub const Client = struct { var header_slist: [*c]cURL.curl_slist = null; - if (headers) |h| { - var iterator = h.iterator(); + if (headers) |header| { + var iterator = header.iterator(); while (iterator.next()) |entry| { var buf = try self.allocator.alloc(u8, entry.key_ptr.*.len + 3 + entry.value_ptr.*.len); @@ -68,7 +69,6 @@ pub const Client = struct { return error.CURLSetOptFailed; } - var response_buffer = std.ArrayList(u8).init(self.allocator.*); if (cURL.curl_easy_setopt(self.ptr, cURL.CURLOPT_WRITEFUNCTION, writeToArrayListCallback) != cURL.CURLE_OK) return error.CURLSetOptFailed; @@ -81,7 +81,7 @@ pub const Client = struct { if (header_slist != null) cURL.curl_slist_free_all(header_slist); - var stream = json.TokenStream.init(response_buffer.items); + var stream = json.TokenStream.init(response_buffer.toOwnedSlice()); @setEvalBranchQuota(10_000); const res = json.parse(T, &stream, .{ .allocator = self.allocator.*, .ignore_unknown_fields = true }); @@ -92,9 +92,13 @@ pub const Client = struct { } pub fn postJSON(self: *@This(), url: []const u8, data: anytype, headers: ?std.StringHashMap([]const u8)) anyerror![]const u8 { + var post_buffer = std.ArrayList(u8).init(self.allocator.*); + var response_buffer = std.ArrayList(u8).init(self.allocator.*); + var rawUrl = try self.allocator.allocSentinel(u8, url.len, 0); std.mem.copy(u8, rawUrl, url); - if (cURL.curl_easy_setopt(self.ptr, cURL.CURLOPT_URL, url.ptr) != cURL.CURLE_OK) + + if (cURL.curl_easy_setopt(self.ptr, cURL.CURLOPT_URL, rawUrl.ptr) != cURL.CURLE_OK) return error.CURLPerformFailed; if (cURL.curl_easy_setopt(self.ptr, cURL.CURLOPT_NOPROGRESS, @as(c_long, 1)) != cURL.CURLE_OK) return error.CURLPerformFailed; @@ -121,8 +125,6 @@ pub const Client = struct { header_slist = cURL.curl_slist_append(header_slist, "Content-Type: application/json"); - var post_buffer = std.ArrayList(u8).init(self.allocator.*); - try json.stringify(data, .{}, post_buffer.writer()); if (cURL.curl_easy_setopt(self.ptr, cURL.CURLOPT_POST, @as(c_long, 1)) != cURL.CURLE_OK) @@ -145,7 +147,6 @@ pub const Client = struct { return error.CURLSetOptFailed; } - var response_buffer = std.ArrayList(u8).init(self.allocator.*); if (cURL.curl_easy_setopt(self.ptr, cURL.CURLOPT_WRITEFUNCTION, writeToArrayListCallback) != cURL.CURLE_OK) return error.CURLSetOptFailed; @@ -159,9 +160,9 @@ pub const Client = struct { cURL.curl_slist_free_all(header_slist); self.allocator.free(rawUrl); + post_buffer.deinit(); var res = response_buffer.toOwnedSlice(); - response_buffer.deinit(); return res; diff --git a/src/main.zig b/src/main.zig index eabb674..74e30ed 100644 --- a/src/main.zig +++ b/src/main.zig @@ -94,15 +94,39 @@ const Config = struct { var stat = try file.stat(); const file_buffer = try allocator.alloc(u8, stat.size); _ = try file.readAll(file_buffer); + file.close(); var stream = json.TokenStream.init(file_buffer); - return json.parse(@This(), &stream, .{ .allocator = allocator }); + const res = json.parse(@This(), &stream, .{ .allocator = allocator }); + allocator.free(file_buffer); + + return res; } }; const User = struct { user_login: []u8 }; +var wait_event = std.Thread.StaticResetEvent{}; + +fn handler_fn(_: c_int) callconv(.C) void { + wait_event.set(); +} + +fn setupSigIntHandler() void { + const handler = std.os.Sigaction{ + .handler = .{ + .handler = handler_fn, + }, + .mask = std.os.empty_sigset, + .flags = std.os.SA.RESETHAND, + }; + + if (std.os.linux.sigaction(std.os.SIG.INT, &handler, null) == -1) { + std.os.linux.perror("Failed to set signal"); + } +} + pub fn main() anyerror!void { var db = try sqlite.Database.open("data.db"); defer { @@ -111,9 +135,10 @@ pub fn main() anyerror!void { }; } + setupSigIntHandler(); + try createTables(&db); - //var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; var allocator = general_purpose_allocator.allocator(); @@ -130,15 +155,20 @@ pub fn main() anyerror!void { try insertOrReplaceStreamers(allocator, &db, &client, &config, &headers); - while (true) { + var loop = true; + while (loop) { var alertAllocator = std.heap.ArenaAllocator.init(allocator); try updateAlert(alertAllocator.allocator(), &client, &config, &db, &headers); alertAllocator.deinit(); - std.log.info("waiting for {} ns", .{config.refresh_rate * std.time.ns_per_ms}); - std.time.sleep(config.refresh_rate * std.time.ns_per_ms); + std.log.info("sleeping for {} ns", .{config.refresh_rate * std.time.ns_per_ms}); + const res = wait_event.timedWait(config.refresh_rate * std.time.ns_per_ms); + loop = res == .timed_out; } + std.log.info("stopping ...", .{}); + + headers.deinit(); client.deinit(); try Client.cleanup(); @@ -210,22 +240,24 @@ pub fn updateAlert( for (streams.data) |s| { if (try appendEmbed(allocator, &s, database)) |e| { + std.log.info("{s}", .{s.title}); try embeds.append(e); } } if (embeds.items.len > 0) { - _ = try client.postJSON(config.webhook_url, webhook.Webhook{ + var res = try client.postJSON(config.webhook_url, webhook.Webhook{ .username = "Twitch", .content = "Live alert", .embeds = embeds.items, }, null); - embeds.deinit(); + + client.allocator.free(res); } + embeds.deinit(); } } -const VIEWER_COUNT_NAME = "Viewer count"; fn appendEmbed(allocator: std.mem.Allocator, stream: *const twitch.Stream, db: *sqlite.Database) anyerror!?webhook.Embed { if (!try streamExist(db, stream.id)) { @@ -237,7 +269,7 @@ fn appendEmbed(allocator: std.mem.Allocator, stream: *const twitch.Stream, db: * try std.fmt.format(viewer.writer(), "{}", .{stream.viewer_count}); try fields.append(.{ - .name = VIEWER_COUNT_NAME, + .name = "Viewer count", .value = viewer.toOwnedSlice(), .@"inline" = true, }); @@ -322,7 +354,7 @@ pub fn insertMetadatas(db: *sqlite.Database, stream: *const twitch.Stream) anyer stm.finalize(); if (try mustInsertName(db, stream)) { - std.log.debug("inserting name", .{}); + std.log.debug("inserting name : {s} ({s})", .{stream.title, stream.id}); stm = try db.prepare( "INSERT INTO NAME_STREAM(nameStream, dateNameStream, idStream) VALUES(?, datetime(\"now\"), ?)", );