new json parser testing

This commit is contained in:
rainfall 2024-11-02 17:31:14 -05:00
parent fa2243b097
commit 91ab72e963
6 changed files with 5225 additions and 5155 deletions

View File

@ -48,6 +48,13 @@ pub fn build(b: *std.Build) void {
.link_libc = true,
});
const zmpl = b.createModule(.{
//.name = "zlib",
.target = target,
.optimize = optimize,
.root_source_file = b.path("lib/zmpl/src/zmpl.zig"),
});
const srcs = &.{
"lib/zlib/adler32.c",
"lib/zlib/compress.c",
@ -77,6 +84,7 @@ pub fn build(b: *std.Build) void {
exe.root_module.addImport("ws", websocket);
exe.root_module.addImport("tls12", zig_tls_http);
exe.root_module.addImport("zlib", zlib_zig);
exe.root_module.addImport("zmpl", zmpl);
// test
test_comp.root_module.addImport("ws", websocket);

1
lib/zmpl Submodule

@ -0,0 +1 @@
Subproject commit a128d0a9c9cd70ca9f007405ae3d17e3a074ab34

View File

@ -10,13 +10,15 @@ const crypto = std.crypto;
const tls = std.crypto.tls;
// todo use this to read compressed messages
const zlib = @import("zlib");
const zmpl = @import("zmpl");
const Discord = @import("types.zig");
const Discord = @import("raw_types.zig");
const Self = @This();
const GatewayPayload = Discord.GatewayPayload;
const Opcode = Discord.GatewayOpcodes;
const Intents = Discord.Intents;
const ShardSocketCloseCodes = enum(u16) {
Shutdown = 3000,
@ -25,47 +27,6 @@ const ShardSocketCloseCodes = enum(u16) {
const BASE_URL = "https://discord.com/api/v10";
pub const Intents = packed struct {
guilds: bool = false,
guild_members: bool = false,
guild_bans: bool = false,
guild_emojis: bool = false,
guild_integrations: bool = false,
guild_webhooks: bool = false,
guild_invites: bool = false,
guild_voice_states: bool = false,
guild_presences: bool = false,
guild_messages: bool = false,
guild_message_reactions: bool = false,
guild_message_typing: bool = false,
direct_messages: bool = false,
direct_message_reactions: bool = false,
direct_message_typing: bool = false,
message_content: bool = false,
guild_scheduled_events: bool = false,
_pad: u3 = 0,
auto_moderation_configuration: bool = false,
auto_moderation_execution: bool = false,
_pad2: u2 = 0,
_pad3: u8 = 0,
pub fn toRaw(self: Intents) u32 {
return @as(u32, @bitCast(self));
}
pub fn fromRaw(raw: u32) Intents {
return @as(Intents, @bitCast(raw));
}
pub fn jsonStringify(self: Intents, options: std.json.StringifyOptions, writer: anytype) !void {
_ = options;
try writer.print("{}", .{self.toRaw()});
}
};
pub const GatewayDispatchEvent = struct {
// TODO: implement // application_command_permissions_update: null = null,
// TODO: implement // auto_moderation_rule_create: null = null,
@ -269,6 +230,12 @@ handler: GatewayDispatchEvent,
///useful for closing the conn
mutex: std.Thread.Mutex = .{},
fn parseJson(self: *Self, raw: []const u8) !zmpl.Data {
var data = zmpl.Data.init(self.allocator);
try data.fromJson(raw);
return data;
}
inline fn jitter() i1 {
return 0;
}
@ -300,6 +267,7 @@ inline fn gateway_url(self: ?*Self) []const u8 {
// identifies in order to connect to Discord and get the online status, this shall be done on hello perhaps
fn identify(self: *Self) !void {
std.debug.print("intents: {d}", .{self.intents.toRaw()});
const data = .{
.op = @intFromEnum(Opcode.Identify),
.d = .{
@ -383,29 +351,29 @@ pub fn readMessage(self: *Self, _: anytype) !void {
const raw = try json.parseFromSlice(struct {
/// opcode for the payload
op: Opcode,
op: isize,
/// Event data
d: ?json.Value,
d: json.Value,
/// Sequence isize, used for resuming sessions and heartbeats
s: ?isize,
s: ?i64,
/// The event name for this payload
t: ?[]const u8,
}, self.allocator, msg.data, .{});
const payload = raw.value;
std.debug.print("received: {?s}\n", .{payload.t});
//std.debug.print("received: {?s} with content {?s}\n", .{ payload.t, msg.data });
switch (payload.op) {
switch (@as(Opcode, @enumFromInt(payload.op))) {
Opcode.Dispatch => {
self.setSequence(payload.s orelse 0);
// maybe use threads and call it instead from there
if (payload.t) |name| try self.handleEvent(name, msg.data);
},
Opcode.Hello => {
if (payload.d) |d| {
{
const HelloPayload = struct { heartbeat_interval: u64, _trace: [][]const u8 };
const parsed = try json.parseFromValue(HelloPayload, self.allocator, d, .{});
const parsed = try json.parseFromValue(HelloPayload, self.allocator, payload.d, .{});
const helloPayload = parsed.value;
// PARSE NEW URL IN READY
@ -455,8 +423,8 @@ pub fn readMessage(self: *Self, _: anytype) !void {
session_id: []const u8,
seq: ?isize,
};
if (payload.d) |d| {
const parsed = try json.parseFromValue(WithSequence, self.allocator, d, .{});
{
const parsed = try json.parseFromValue(WithSequence, self.allocator, payload.d, .{});
const resume_payload = parsed.value;
self.setSequence(resume_payload.seq orelse 0);
@ -465,7 +433,7 @@ pub fn readMessage(self: *Self, _: anytype) !void {
},
Opcode.InvalidSession => {},
else => {
std.debug.print("Unhandled {} -- {s}", .{ payload.op, "none" });
std.debug.print("Unhandled {d} -- {s}", .{ payload.op, "none" });
},
}
}
@ -535,7 +503,7 @@ pub fn send(self: *Self, data: anytype) !void {
var string = std.ArrayList(u8).init(fba.allocator());
try std.json.stringify(data, .{}, string.writer());
std.debug.print("{s}\n", .{string.items});
//std.debug.print("{s}\n", .{string.items});
try self.client.write(string.items);
}
@ -552,9 +520,128 @@ pub inline fn setSequence(self: *Self, new: isize) void {
}
pub fn handleEvent(self: *Self, name: []const u8, payload: []const u8) !void {
if (std.ascii.eqlIgnoreCase(name, @tagName(.message_create))) {
const attempt = try std.json.parseFromSlice(Discord.Message, self.allocator, payload, .{});
defer attempt.deinit();
@call(.auto, self.handler.message_create, .{attempt.value});
const attempt = try self.parseJson(payload);
if (std.ascii.eqlIgnoreCase(name, "message_create")) {
const obj = attempt.getT(.object, "d").?;
const author_obj = obj.getT(.object, "author").?;
const member_obj = obj.getT(.object, "member").?;
const avatar_decoration_data_obj = author_obj.getT(.object, "avatar_decoration_data");
const avatar_decoration_data_member_obj = author_obj.getT(.object, "avatar_decoration_data");
const mentions_obj = obj.getT(.array, "mentions").?;
var mentions = std.ArrayList(Discord.User).init(self.allocator);
while (mentions_obj.iterator().next()) |m| {
const avatar_decoration_data_mention_obj = m.getT(.object, "avatar_decoration_data");
try mentions.append(Discord.User{
.id = m.getT(.string, "id").?,
.bot = m.getT(.boolean, "bot") orelse false,
.username = m.getT(.string, "username").?,
.accent_color = if (m.getT(.integer, "accent_color")) |ac| @as(isize, @intCast(ac)) else null,
// note: for selfbots this can be typed with an enu.?,
.flags = if (m.getT(.integer, "flags")) |fs| @as(isize, @intCast(fs)) else null,
// also for selfbot.?,
.email = m.getT(.string, "email"),
.avatar = m.getT(.string, "avatar"),
.locale = m.getT(.string, "locale"),
.system = m.getT(.boolean, "system"),
.banner = m.getT(.string, "banner"),
.verified = m.getT(.boolean, "verified"),
.global_name = m.getT(.string, "global_name"),
.mfa_enabled = m.getT(.boolean, "mfa_enabled"),
.public_flags = if (m.getT(.integer, "public_flags")) |pfs| @as(isize, @intCast(pfs)) else null,
.premium_type = if (m.getT(.integer, "premium_type")) |pfs| @as(Discord.PremiumTypes, @enumFromInt(pfs)) else null,
.discriminator = m.getT(.string, "discriminator").?,
.avatar_decoration_data = if (avatar_decoration_data_mention_obj) |addm| Discord.AvatarDecorationData{
.asset = addm.getT(.string, "asset").?,
.sku_id = addm.getT(.string, "sku_id").?,
} else null,
});
}
const member = Discord.Member{
.deaf = member_obj.getT(.boolean, "deaf"),
.mute = member_obj.getT(.boolean, "mute"),
.pending = member_obj.getT(.boolean, "pending"),
.user = null,
.nick = member_obj.getT(.string, "nick"),
.avatar = member_obj.getT(.string, "avatar"),
.roles = &[0][]const u8{},
.joined_at = member_obj.getT(.string, "joined_at").?,
.premium_since = member_obj.getT(.string, "premium_since"),
.permissions = member_obj.getT(.string, "permissions"),
.communication_disabled_until = member_obj.getT(.string, "communication_disabled_until"),
.flags = @as(isize, @intCast(member_obj.getT(.integer, "flags").?)),
.avatar_decoration_data = if (avatar_decoration_data_member_obj) |addm| Discord.AvatarDecorationData{
.asset = addm.getT(.string, "asset").?,
.sku_id = addm.getT(.string, "sku_id").?,
} else null,
};
const author = Discord.User{
.id = author_obj.getT(.string, "id").?,
.bot = author_obj.getT(.boolean, "bot") orelse false,
.username = author_obj.getT(.string, "username").?,
.accent_color = if (author_obj.getT(.integer, "accent_color")) |ac| @as(isize, @intCast(ac)) else null,
// note: for selfbots this can be typed with an enu.?,
.flags = if (author_obj.getT(.integer, "flags")) |fs| @as(isize, @intCast(fs)) else null,
// also for selfbot.?,
.email = author_obj.getT(.string, "email"),
.avatar = author_obj.getT(.string, "avatar"),
.locale = author_obj.getT(.string, "locale"),
.system = author_obj.getT(.boolean, "system"),
.banner = author_obj.getT(.string, "banner"),
.verified = author_obj.getT(.boolean, "verified"),
.global_name = author_obj.getT(.string, "global_name"),
.mfa_enabled = author_obj.getT(.boolean, "mfa_enabled"),
.public_flags = if (author_obj.getT(.integer, "public_flags")) |pfs| @as(isize, @intCast(pfs)) else null,
.premium_type = if (author_obj.getT(.integer, "premium_type")) |pfs| @as(Discord.PremiumTypes, @enumFromInt(pfs)) else null,
.discriminator = author_obj.getT(.string, "discriminator").?,
.avatar_decoration_data = if (avatar_decoration_data_obj) |add| Discord.AvatarDecorationData{
.asset = add.getT(.string, "asset").?,
.sku_id = add.getT(.string, "sku_id").?,
} else null,
};
const m = Discord.Message{
// the id
.id = obj.getT(.string, "id").?,
.tts = obj.getT(.boolean, "tts").?,
.mention_everyone = obj.getT(.boolean, "mention_everyone").?,
.pinned = obj.getT(.boolean, "pinned").?,
.type = @as(Discord.MessageTypes, @enumFromInt(obj.getT(.integer, "type").?)),
.channel_id = obj.getT(.string, "channel_id").?,
.author = author,
.member = member,
.content = obj.getT(.string, "content"),
.timestamp = obj.getT(.string, "timestamp").?,
.guild_id = obj.getT(.string, "guild_id"),
.attachments = &[0]Discord.Attachment{},
.edited_timestamp = null,
.mentions = mentions.items,
.mention_roles = &[0]?[]const u8{},
.mention_channels = &[0]?Discord.ChannelMention{},
.embeds = &[0]Discord.Embed{},
.reactions = &[0]?Discord.Reaction{},
.nonce = .{ .string = obj.getT(.string, "nonce").? },
.webhook_id = obj.getT(.string, "webhook_id"),
.activity = null,
.application = null,
.application_id = obj.getT(.string, "application_id"),
.message_reference = null,
.flags = if (obj.getT(.integer, "flags")) |fs| @as(Discord.MessageFlags, @bitCast(@as(u15, @intCast(fs)))) else null,
.stickers = &[0]?Discord.Sticker{},
.referenced_message = null,
.message_snapshots = &[0]?Discord.MessageSnapshot{},
.interaction_metadata = null,
.interaction = null,
.thread = null,
.components = null,
.sticker_items = &[0]?Discord.StickerItem{},
.position = if (obj.getT(.integer, "position")) |p| @as(isize, @intCast(p)) else null,
.poll = null,
.call = null,
};
@call(.auto, self.handler.message_create, .{m});
} else {}
}

View File

@ -1,23 +1,22 @@
const Session = @import("discord.zig");
const Message = @import("types.zig").Message;
const Intents = @import("raw_types.zig").Intents;
const Discord = @import("raw_types.zig");
const std = @import("std");
const TOKEN = "Bot MTI5ODgzOTgzMDY3OTEzMDE4OA.GNojts.iyblGKK0xTWU57QCG5n3hr2Be1whyylTGr44P0";
fn message_create(data: Message) void {
std.debug.print("Event:{s}", .{data.content});
fn message_create(message: Discord.Message) void {
std.debug.print("captured: {?s}\n", .{message.content});
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer if (gpa.deinit() == .leak) {
std.log.warn("Has leaked\n", .{});
};
const alloc = gpa.allocator();
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var handler = try Session.init(alloc, .{
var handler = try Session.init(allocator, .{
.token = TOKEN,
.intents = Session.Intents.fromRaw(513),
.intents = Intents.fromRaw(37379),
.run = Session.GatewayDispatchEvent{ .message_create = &message_create },
});
errdefer handler.deinit();

5063
src/raw_types.zig Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff