+6
build.zig
+6
build.zig
···
14
14
.optimize = optimize,
15
15
});
16
16
17
+
const zat = b.dependency("zat", .{
18
+
.target = target,
19
+
.optimize = optimize,
20
+
});
21
+
17
22
const exe = b.addExecutable(.{
18
23
.name = "zig-bsky-feed",
19
24
.root_module = b.createModule(.{
···
23
28
.imports = &.{
24
29
.{ .name = "websocket", .module = websocket.module("websocket") },
25
30
.{ .name = "zqlite", .module = zqlite.module("zqlite") },
31
+
.{ .name = "zat", .module = zat.module("zat") },
26
32
},
27
33
}),
28
34
});
+4
build.zig.zon
+4
build.zig.zon
···
12
12
.url = "https://github.com/karlseguin/zqlite.zig/archive/refs/heads/master.tar.gz",
13
13
.hash = "zqlite-0.0.0-RWLaY_y_mADh2LdbDrG_2HT2dBAcsAR8Jig_7-dOJd0B",
14
14
},
15
+
.zat = .{
16
+
.url = "https://github.com/zzstoatzz/zat/archive/refs/heads/main.tar.gz",
17
+
.hash = "zat-0.0.1-alpha-5PuC7liTAAAHY5R9yFiTz2Q51azmq4gLHpJLrzlA6-ms",
18
+
},
15
19
},
16
20
.paths = .{
17
21
"build.zig",
+1
src/atproto.zig
+1
src/atproto.zig
+8
-20
src/db.zig
+8
-20
src/db.zig
···
1
1
const std = @import("std");
2
2
const posix = std.posix;
3
3
const zqlite = @import("zqlite");
4
+
const zat = @import("zat");
4
5
const config = @import("config.zig");
5
6
const atproto = @import("atproto.zig");
6
7
···
106
107
};
107
108
}
108
109
109
-
/// parse TID (base32-sortable) to get timestamp in milliseconds
110
-
/// TID is 13 chars, first 53 bits are microseconds since epoch
110
+
/// parse TID to get timestamp in milliseconds
111
111
pub fn parseTidTimestamp(rkey: []const u8) i64 {
112
-
if (rkey.len != 13) return 0;
113
-
114
-
const charset = "234567abcdefghijklmnopqrstuvwxyz";
115
-
var value: u64 = 0;
116
-
for (rkey) |c| {
117
-
const idx = std.mem.indexOf(u8, charset, &[_]u8{c}) orelse return 0;
118
-
value = value * 32 + idx;
119
-
}
120
-
121
-
// top 53 bits are microseconds, convert to milliseconds
122
-
const micros = value >> 10;
123
-
return @intCast(micros / 1000);
112
+
const tid = zat.internal.Tid.parse(rkey) orelse return 0;
113
+
return @intCast(tid.timestamp() / 1000); // microseconds -> milliseconds
124
114
}
125
115
126
116
/// parse TID from AT URI to get post timestamp
127
117
fn parseTimestampFromUri(uri: []const u8) ?i64 {
128
-
const last_slash = std.mem.lastIndexOf(u8, uri, "/") orelse return null;
129
-
const rkey = uri[last_slash + 1 ..];
118
+
const at_uri = zat.internal.AtUri.parse(uri) orelse return null;
119
+
const rkey = at_uri.rkey() orelse return null;
130
120
const ts = parseTidTimestamp(rkey);
131
121
return if (ts > 0) ts else null;
132
122
}
···
278
268
279
269
/// extract DID from post URI (at://did:plc:xxx/app.bsky.feed.post/yyy)
280
270
fn extractDidFromUri(uri: []const u8) ?[]const u8 {
281
-
if (!std.mem.startsWith(u8, uri, "at://")) return null;
282
-
const rest = uri[5..];
283
-
const end = std.mem.indexOf(u8, rest, "/") orelse return null;
284
-
return rest[0..end];
271
+
const at_uri = zat.internal.AtUri.parse(uri) orelse return null;
272
+
return at_uri.authority();
285
273
}
286
274
287
275
/// get follows as newline-separated string (caller owns returned memory)