init
This commit is contained in:
commit
f6cf0c6dc6
7 changed files with 185 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
zig-*
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "deps/ws"]
|
||||||
|
path = deps/ws
|
||||||
|
url = https://github.com/nikneym/ws.git
|
43
build.zig
Normal file
43
build.zig
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "ntfylisten",
|
||||||
|
.root_source_file = .{ .path = "src/main.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const zuri_mod = b.createModule(.{
|
||||||
|
.source_file = .{ .path = "deps/ws/lib/zuri/src/zuri.zig" },
|
||||||
|
});
|
||||||
|
|
||||||
|
exe.addAnonymousModule("ws", .{
|
||||||
|
.source_file = .{ .path = "deps/ws/src/main.zig" },
|
||||||
|
.dependencies = &.{.{ .name = "zuri", .module = zuri_mod }},
|
||||||
|
});
|
||||||
|
|
||||||
|
// need libnotify, so might as well link glib and use glib logging
|
||||||
|
exe.linkLibC();
|
||||||
|
exe.linkSystemLibrary("glib-2.0");
|
||||||
|
|
||||||
|
// needed for libnotify
|
||||||
|
exe.linkSystemLibrary("gdk-pixbuf-2.0");
|
||||||
|
exe.linkSystemLibrary("notify");
|
||||||
|
|
||||||
|
exe.install();
|
||||||
|
|
||||||
|
const run_cmd = exe.run();
|
||||||
|
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
1
deps/ws
vendored
Submodule
1
deps/ws
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit d11513b70fa7b062bfa327af3377436a86d05b89
|
4
src/Message.zig
Normal file
4
src/Message.zig
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
event: []const u8,
|
||||||
|
message: ?[:0]const u8 = null,
|
||||||
|
title: ?[]const u8 = null,
|
||||||
|
priority: u8 = 4,
|
4
src/ffi.zig
Normal file
4
src/ffi.zig
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
pub const c = @cImport({
|
||||||
|
@cInclude("libnotify/notify.h");
|
||||||
|
@cInclude("glib.h");
|
||||||
|
});
|
129
src/main.zig
Normal file
129
src/main.zig
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const ws = @import("ws");
|
||||||
|
const c = @import("ffi.zig").c;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.ntfylisten);
|
||||||
|
|
||||||
|
const Message = @import("Message.zig");
|
||||||
|
|
||||||
|
pub const std_options = struct {
|
||||||
|
// level filtering handled by glib
|
||||||
|
pub const log_level = .debug;
|
||||||
|
|
||||||
|
pub fn logFn(
|
||||||
|
comptime message_level: std.log.Level,
|
||||||
|
comptime scope: @TypeOf(.enum_literal),
|
||||||
|
comptime format: []const u8,
|
||||||
|
args: anytype,
|
||||||
|
) void {
|
||||||
|
var printbuf: [1024 * 4]u8 = undefined;
|
||||||
|
const msg = std.fmt.bufPrint(&printbuf, format, args) catch return;
|
||||||
|
|
||||||
|
const level = switch (message_level) {
|
||||||
|
.debug => c.G_LOG_LEVEL_DEBUG,
|
||||||
|
.info => c.G_LOG_LEVEL_INFO,
|
||||||
|
.warn => c.G_LOG_LEVEL_WARNING,
|
||||||
|
.err => c.G_LOG_LEVEL_CRITICAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const domain = @tagName(scope);
|
||||||
|
|
||||||
|
const fields = [_]c.GLogField{
|
||||||
|
.{
|
||||||
|
.key = "GLIB_DOMAIN",
|
||||||
|
.value = domain,
|
||||||
|
.length = @intCast(c.gssize, domain.len),
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.key = "MESSAGE",
|
||||||
|
.value = msg.ptr,
|
||||||
|
.length = @intCast(c.gssize, msg.len),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
c.g_log_structured_array(level, &fields, fields.len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
if (std.os.argv.len != 4) {
|
||||||
|
log.err(
|
||||||
|
\\Expected 3 arguments!
|
||||||
|
\\Usage:
|
||||||
|
\\ {s} [server] [topic] [title_prefix]
|
||||||
|
,
|
||||||
|
.{std.os.argv[0]},
|
||||||
|
);
|
||||||
|
std.os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = std.mem.span(std.os.argv[1]);
|
||||||
|
const topic = std.mem.span(std.os.argv[2]);
|
||||||
|
const title_prefix = std.mem.span(std.os.argv[3]);
|
||||||
|
|
||||||
|
if (c.notify_init("ntfylisten") == 0)
|
||||||
|
return error.LibnotifyInit;
|
||||||
|
|
||||||
|
const addr = try std.fmt.allocPrint(std.heap.c_allocator, "ws://{s}/{s}/ws", .{ server, topic });
|
||||||
|
defer std.heap.c_allocator.free(addr);
|
||||||
|
|
||||||
|
log.info("connecting to WebSocket URL {s}", .{addr});
|
||||||
|
var ws_client = try ws.connect(std.heap.c_allocator, addr, &.{
|
||||||
|
.{ "Host", server },
|
||||||
|
});
|
||||||
|
defer ws_client.deinit(std.heap.c_allocator);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var msg = try ws_client.receive();
|
||||||
|
|
||||||
|
switch (msg.type) {
|
||||||
|
.text, .binary => {
|
||||||
|
var ts = std.json.TokenStream.init(msg.data);
|
||||||
|
const data = std.json.parse(
|
||||||
|
Message,
|
||||||
|
&ts,
|
||||||
|
.{ .allocator = std.heap.c_allocator, .ignore_unknown_fields = true },
|
||||||
|
) catch |e| {
|
||||||
|
log.warn("invalid data from server: {}", .{e});
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
defer std.json.parseFree(Message, data, .{ .allocator = std.heap.c_allocator });
|
||||||
|
|
||||||
|
if (!std.mem.eql(u8, data.event, "message"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const text = data.message orelse continue;
|
||||||
|
|
||||||
|
const title = if (data.title) |title|
|
||||||
|
try std.fmt.allocPrintZ(std.heap.c_allocator, "{s}: {s}", .{ title_prefix, title })
|
||||||
|
else
|
||||||
|
try std.heap.c_allocator.dupeZ(u8, title_prefix);
|
||||||
|
|
||||||
|
defer std.heap.c_allocator.free(title);
|
||||||
|
|
||||||
|
const urgency = switch (std.math.order(data.priority, 3)) {
|
||||||
|
.lt => c.NOTIFY_URGENCY_LOW,
|
||||||
|
.eq => c.NOTIFY_URGENCY_NORMAL,
|
||||||
|
.gt => c.NOTIFY_URGENCY_CRITICAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const notif = c.notify_notification_new(title, text, null);
|
||||||
|
c.notify_notification_set_urgency(notif, @intCast(c_uint, urgency));
|
||||||
|
_ = c.notify_notification_show(notif, null);
|
||||||
|
},
|
||||||
|
|
||||||
|
.ping => {
|
||||||
|
try ws_client.pong();
|
||||||
|
},
|
||||||
|
|
||||||
|
.close => {
|
||||||
|
log.warn("server sent close message, exiting", .{});
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try ws_client.close();
|
||||||
|
}
|
Loading…
Reference in a new issue