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