fixed null ptr exception & added ready event

This commit is contained in:
rainfall 2024-11-04 13:06:16 -05:00
parent f4c8b3216f
commit 6689486660
6 changed files with 121 additions and 65 deletions

View File

@ -13,7 +13,12 @@ git clone https://github.com/jetzig-framework/zmpl.git ./lib/zmpl/
or simply run ./install.sh
# features
* idk man
* scalable
* 100% API coverage, fully typed
* faster than any other Discord library
* language-agnostic
* implemented from scratch
* parses payloads using zlib
```zig
// Sample code

View File

@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void {
// these are boiler plate code until you know what you are doing
// and you need to add additional options
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast });
// this is your own program
const exe = b.addExecutable(.{
@ -73,6 +73,8 @@ pub fn build(b: *std.Build) void {
"lib/zlib/zutil.c",
};
//const mode = b.standardReleaseOptions();
zlib_zig.addCSourceFiles(.{ .files = srcs, .flags = &.{"-std=c89"} });
zlib_zig.addIncludePath(b.path("lib/zlib/"));

View File

@ -13,6 +13,7 @@ const zlib = @import("zlib");
const zmpl = @import("zmpl");
const Discord = @import("raw_types.zig");
const Parser = @import("parser.zig");
const debug = std.log.scoped(.@"discord.zig");
const Self = @This();
@ -71,7 +72,7 @@ pub const GatewayDispatchEvent = struct {
// TODO: implement // interaction_create: null = null,
// TODO: implement // invite_create: null = null,
// TODO: implement // invite_delete: null = null,
message_create: *const fn (message: Discord.Message) void = undefined,
message_create: ?*const fn (message: Discord.Message) void = undefined,
// TODO: implement // message_update: null = null,
// TODO: implement // message_delete: null = null,
// TODO: implement // message_delete_bulk: null = null,
@ -95,9 +96,9 @@ pub const GatewayDispatchEvent = struct {
// TODO: implement // message_poll_vote_add: null = null,
// TODO: implement // message_poll_vote_remove: null = null,
// TODO: implement // ready: null = null,
ready: ?*const fn (data: Discord.Ready) void = undefined,
// TODO: implement // resumed: null = null,
any: *const fn (data: []u8) void = undefined,
any: ?*const fn (data: []u8) void = undefined,
};
const FetchReq = struct {
@ -536,50 +537,81 @@ pub inline fn setSequence(self: *Self, new: isize) void {
}
pub fn handleEvent(self: *Self, name: []const u8, payload: []const u8) !void {
const attempt = try self.parseJson(payload);
if (std.ascii.eqlIgnoreCase(name, "ready")) {
var attempt = try self.parseJson(payload);
defer attempt.deinit();
const obj = attempt.getT(.object, "d").?;
self.resume_gateway_url = obj.getT(.string, "resume_gateway_url");
self.logif("new gateway url: {s}", .{self.gatewayUrl()});
const application = obj.getT(.object, "application").?;
const user = Parser.parseUser(obj.getT(.object, "user").?);
var ready = Discord.Ready{
.v = @as(isize, @intCast(obj.getT(.integer, "v").?)),
.user = user,
.shard = null,
.session_id = obj.getT(.string, "session_id").?,
.guilds = &[0]Discord.UnavailableGuild{},
.resume_gateway_url = obj.getT(.string, "resume_gateway_url").?,
.application = .{
// todo
.name = null,
.description = null,
.rpc_origins = null,
.terms_of_service_url = null,
.privacy_policy_url = null,
.verify_key = null,
.primary_sku_id = null,
.slug = null,
.icon = null,
.bot_public = null,
.bot_require_code_grant = null,
.owner = null,
.team = null,
.guild_id = null,
.guild = null,
.cover_image = null,
.tags = null,
.install_params = null,
.integration_types_config = null,
.custom_install_url = null,
.role_connections_verification_url = null,
.approximate_guild_count = null,
.approximate_user_install_count = null,
.bot = null,
.redirect_uris = null,
.interactions_endpoint_url = null,
.flags = .{ .Embedded = true }, //@as(Discord.ApplicationFlags, @bitCast(@as(u25, @intCast(application.getT(.integer, "flags").?)))),
.id = application.getT(.string, "id").?,
},
};
const shard = obj.getT(.array, "shard");
if (shard) |s| {
for (&ready.shard.?, s.items()) |*rs, ss| rs.* = switch (ss.*) {
.integer => |v| @as(isize, @intCast(v.value)),
else => unreachable,
};
}
if (self.handler.ready) |event| @call(.auto, event, .{ready});
}
if (std.ascii.eqlIgnoreCase(name, "message_create")) {
const attempt = try self.parseJson(payload);
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 avatar_decoration_data_member_obj = member_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,
});
try mentions.append(Parser.parseUser(&m.object));
}
const member = Discord.Member{
@ -601,30 +633,7 @@ pub fn handleEvent(self: *Self, name: []const u8, payload: []const u8) !void {
} 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 author = Parser.parseUser(obj.getT(.object, "author").?);
const m = Discord.Message{
// the id
@ -666,7 +675,7 @@ pub fn handleEvent(self: *Self, name: []const u8, payload: []const u8) !void {
.call = null,
};
@call(.auto, self.handler.message_create, .{m});
if (self.handler.message_create) |event| @call(.auto, event, .{m});
} else {}
}

View File

@ -2,11 +2,14 @@ const Session = @import("discord.zig");
const Intents = @import("raw_types.zig").Intents;
const Discord = @import("raw_types.zig");
const std = @import("std");
const TOKEN = "Bot MTI5ODgzOTgzMDY3OTEzMDE4OA.GNojts.iyblGKK0xTWU57QCG5n3hr2Be1whyylTGr44P0";
fn ready(payload: Discord.Ready) void {
std.debug.print("logged in as {s}\n", .{payload.user.username});
}
fn message_create(message: Discord.Message) void {
std.debug.print("captured: {?s}\n", .{message.content});
std.debug.print("captured: {?s} send by {s}\n", .{ message.content, message.author.username });
}
pub fn main() !void {
@ -17,7 +20,10 @@ pub fn main() !void {
var handler = try Session.init(allocator, .{
.token = TOKEN,
.intents = Intents.fromRaw(37379),
.run = Session.GatewayDispatchEvent{ .message_create = &message_create },
.run = Session.GatewayDispatchEvent{
.message_create = &message_create,
.ready = &ready,
},
.log = .yes,
});
errdefer handler.deinit();

33
src/parser.zig Normal file
View File

@ -0,0 +1,33 @@
const zmpl = @import("zmpl");
const Discord = @import("raw_types.zig");
pub fn parseUser(obj: *zmpl.Data.Object) Discord.User {
const avatar_decoration_data_obj = obj.getT(.object, "avatar_decoration_data");
const user = Discord.User{
.clan = null,
.id = obj.getT(.string, "id").?,
.bot = obj.getT(.boolean, "bot") orelse false,
.username = obj.getT(.string, "username").?,
.accent_color = if (obj.getT(.integer, "accent_color")) |ac| @as(isize, @intCast(ac)) else null,
// note: for selfbots this can be typed with an enu.?,
.flags = if (obj.getT(.integer, "flags")) |fs| @as(isize, @intCast(fs)) else null,
// also for selfbot.?,
.email = obj.getT(.string, "email"),
.avatar = obj.getT(.string, "avatar"),
.locale = obj.getT(.string, "locale"),
.system = obj.getT(.boolean, "system"),
.banner = obj.getT(.string, "banner"),
.verified = obj.getT(.boolean, "verified"),
.global_name = obj.getT(.string, "global_name"),
.mfa_enabled = obj.getT(.boolean, "mfa_enabled"),
.public_flags = if (obj.getT(.integer, "public_flags")) |pfs| @as(isize, @intCast(pfs)) else null,
.premium_type = if (obj.getT(.integer, "premium_type")) |pfs| @as(Discord.PremiumTypes, @enumFromInt(pfs)) else null,
.discriminator = 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,
};
return user;
}

View File

@ -1345,6 +1345,7 @@ pub const User = struct {
banner: ?[]const u8,
/// data for the user's avatar decoration
avatar_decoration_data: ?AvatarDecorationData,
clan: ?[]const u8,
};
/// https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes
@ -4336,10 +4337,10 @@ pub const Ready = struct {
approximate_guild_count: ?isize,
approximate_user_install_count: ?isize,
bot: ?Partial(User),
redirect_uris: []?[]const u8,
redirect_uris: ?[][]const u8,
interactions_endpoint_url: ?[]const u8,
flags: ?ApplicationFlags,
flags: ApplicationFlags,
id: []const u8,
},
};