breaking changes

This commit is contained in:
yuzu 2025-05-15 15:13:50 -05:00
parent b8c42bb22c
commit 32d9688234
8 changed files with 3119 additions and 3122 deletions

View File

@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void {
const zlib = b.dependency("zlib", .{}); const zlib = b.dependency("zlib", .{});
const dzig = b.addModule("discord.zig", .{ const dzig = b.addModule("discord.zig", .{
.root_source_file = b.path("src/lib.zig"), .root_source_file = b.path("src/root.zig"),
.link_libc = true, .link_libc = true,
}); });

View File

@ -11,7 +11,7 @@
// This is a [Semantic Version](https://semver.org/). // This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication. // In a future version of Zig it will be used for package deduplication.
.version = "0.0.0", .version = "1.0.0",
// This field is optional. // This field is optional.
// This is currently advisory only; Zig does not yet do anything // This is currently advisory only; Zig does not yet do anything
@ -25,8 +25,8 @@
// internet connectivity. // internet connectivity.
.dependencies = .{ .dependencies = .{
.zlib = .{ .zlib = .{
.url = "https://github.com/yuzudev/zig-zlib/archive/refs/heads/main.zip", .url = "https://git.yuzucchii.xyz/yuzucchii/zlib/archive/master.tar.gz",
.hash = "zlib-0.1.0-AAAAAKm6QADNBB6NBPHanW9G0EOPTmgJsRO6NFT__arp", .hash = "zlib-0.1.0-AAAAALW6QABbti7dQfKuK0IQb-xdcp3SI8zdTvs5ouUD",
}, },
.websocket = .{ .url = "https://github.com/karlseguin/websocket.zig/archive/refs/heads/master.zip", .hash = "websocket-0.1.0-ZPISdYBIAwB1yO6AFDHRHLaZSmpdh4Bz4dCmaQUqNNWh" }, .websocket = .{ .url = "https://github.com/karlseguin/websocket.zig/archive/refs/heads/master.zip", .hash = "websocket-0.1.0-ZPISdYBIAwB1yO6AFDHRHLaZSmpdh4Bz4dCmaQUqNNWh" },
}, },

File diff suppressed because it is too large Load Diff

View File

@ -310,7 +310,7 @@ pub const CacheLike = @import("cache/cache.zig").CacheLike;
pub const DefaultCache = @import("cache/cache.zig").DefaultCache; pub const DefaultCache = @import("cache/cache.zig").DefaultCache;
pub const Permissions = @import("utils/permissions.zig").Permissions; pub const Permissions = @import("utils/permissions.zig").Permissions;
pub const Shard = @import("shard/shard.zig").Shard; pub const Shard = @import("shard/shard.zig");
pub const zjson = @compileError("Deprecated."); pub const zjson = @compileError("Deprecated.");
pub const Internal = @import("utils/core.zig"); pub const Internal = @import("utils/core.zig");
@ -319,12 +319,13 @@ const GatewayBotInfo = @import("shard/util.zig").GatewayBotInfo;
const Log = Internal.Log; const Log = Internal.Log;
// sharder // sharder
pub const Sharder = @import("shard/sharder.zig").ShardManager; pub const Sharder = @import("shard/sharder.zig");
pub const cache = @import("cache/cache.zig"); pub const cache = @import("cache/cache.zig");
pub const FetchReq = @import("http/http.zig").FetchReq; pub const FetchReq = @import("http/http.zig").FetchReq;
pub const FileData = @import("http/http.zig").FileData; pub const FileData = @import("http/http.zig").FileData;
pub const API = @import("http/api.zig");
const std = @import("std"); const std = @import("std");
const mem = std.mem; const mem = std.mem;
@ -336,14 +337,20 @@ pub fn CustomisedSession(comptime Table: cache.TableTemplate) type {
const Self = @This(); const Self = @This();
allocator: mem.Allocator, allocator: mem.Allocator,
sharder: Sharder(Table), sharder: Sharder,
token: []const u8, authorization: []const u8,
cache: cache.CacheTables(Table),
// there is only 1 api, therefore we don't need pointers
api: API,
pub fn init(allocator: mem.Allocator) Self { pub fn init(allocator: mem.Allocator) Self {
return .{ return .{
.allocator = allocator, .allocator = allocator,
.sharder = undefined, .sharder = undefined,
.token = undefined, .authorization = undefined,
.api = undefined,
.cache = .defaults(allocator),
}; };
} }
@ -352,7 +359,7 @@ pub fn CustomisedSession(comptime Table: cache.TableTemplate) type {
} }
pub fn start(self: *Self, settings: struct { pub fn start(self: *Self, settings: struct {
token: []const u8, authorization: []const u8,
intents: Intents, intents: Intents,
options: struct { options: struct {
spawn_shard_delay: u64 = 5300, spawn_shard_delay: u64 = 5300,
@ -362,17 +369,20 @@ pub fn CustomisedSession(comptime Table: cache.TableTemplate) type {
}, },
run: GatewayDispatchEvent, run: GatewayDispatchEvent,
log: Log, log: Log,
cache: cache.TableTemplate, cache: ?cache.CacheTables(Table),
}) !void { }) !void {
if (!std.mem.startsWith(u8, settings.token, "Bot")) { if (!std.mem.startsWith(u8, settings.authorization, "Bot")) {
var buffer = [_]u8{undefined} ** 128; var buffer = [_]u8{undefined} ** 128;
const printed = try std.fmt.bufPrint(&buffer, "Bot {s}", .{settings.token}); const printed = try std.fmt.bufPrint(&buffer, "Bot {s}", .{settings.authorization});
self.token = printed; self.authorization = printed;
} else { } else {
self.token = settings.token; self.authorization = settings.authorization;
} }
var req = FetchReq.init(self.allocator, self.token); self.api = API.init(self.allocator, self.authorization);
self.cache = settings.cache orelse .defaults(self.allocator);
var req = FetchReq.init(self.allocator, self.authorization);
defer req.deinit(); defer req.deinit();
const res = try req.makeRequest(.GET, "/gateway/bot", null); const res = try req.makeRequest(.GET, "/gateway/bot", null);
@ -387,11 +397,11 @@ pub fn CustomisedSession(comptime Table: cache.TableTemplate) type {
const parsed = try json.parseFromSlice(GatewayBotInfo, self.allocator, body, .{}); const parsed = try json.parseFromSlice(GatewayBotInfo, self.allocator, body, .{});
defer parsed.deinit(); defer parsed.deinit();
self.sharder = try Sharder(Table).init(self.allocator, .{ self.sharder = try Sharder.init(self.allocator, .{
.token = self.token, .authorization = self.authorization,
.intents = settings.intents, .intents = settings.intents,
.run = settings.run, .run = settings.run,
.options = Sharder(Table).SessionOptions{ .options = Sharder.SessionOptions{
.info = parsed.value, .info = parsed.value,
.shard_start = settings.options.shard_start, .shard_start = settings.options.shard_start,
.shard_end = @intCast(parsed.value.shards), .shard_end = @intCast(parsed.value.shards),
@ -399,7 +409,6 @@ pub fn CustomisedSession(comptime Table: cache.TableTemplate) type {
.spawn_shard_delay = settings.options.spawn_shard_delay, .spawn_shard_delay = settings.options.spawn_shard_delay,
}, },
.log = settings.log, .log = settings.log,
.cache = settings.cache,
}); });
try self.sharder.spawnShards(); try self.sharder.spawnShards();
@ -423,7 +432,7 @@ pub const GatewayIntents = @import("./shard/intents.zig").GatewayIntents;
pub const Intents = @import("./shard/intents.zig").Intents; pub const Intents = @import("./shard/intents.zig").Intents;
pub fn start(self: *Session, settings: struct { pub fn start(self: *Session, settings: struct {
token: []const u8, authorization: []const u8,
intents: Intents, intents: Intents,
options: struct { options: struct {
spawn_shard_delay: u64 = 5300, spawn_shard_delay: u64 = 5300,
@ -433,7 +442,7 @@ pub fn start(self: *Session, settings: struct {
}, },
run: GatewayDispatchEvent, run: GatewayDispatchEvent,
log: Log, log: Log,
cache: cache.TableTemplate, cache: cache.CacheTables(DefaultTable),
}) !void { }) !void {
return self.start(settings); return self.start(settings);
} }

File diff suppressed because it is too large Load Diff

View File

@ -23,240 +23,226 @@ const ShardDetails = @import("util.zig").ShardDetails;
const ConnectQueue = @import("connect_queue.zig").ConnectQueue; const ConnectQueue = @import("connect_queue.zig").ConnectQueue;
const GatewayDispatchEvent = @import("../utils/core.zig").GatewayDispatchEvent; const GatewayDispatchEvent = @import("../utils/core.zig").GatewayDispatchEvent;
const Log = @import("../utils/core.zig").Log; const Log = @import("../utils/core.zig").Log;
const Shard = @import("shard.zig").Shard; const Shard = @import("shard.zig");
const std = @import("std"); const std = @import("std");
const mem = std.mem; const mem = std.mem;
const debug = @import("../utils/core.zig").debug; const debug = @import("../utils/core.zig").debug;
const TableTemplate = @import("../cache/cache.zig").TableTemplate;
const CacheTables = @import("../cache/cache.zig").CacheTables;
pub fn ShardManager(comptime Table: TableTemplate) type { const Self = @This();
return struct {
const Self = @This();
shard_details: ShardDetails, shard_details: ShardDetails,
allocator: mem.Allocator, allocator: mem.Allocator,
/// Queue for managing shard connections /// Queue for managing shard connections
connect_queue: ConnectQueue(Shard(Table)), connect_queue: ConnectQueue(Shard),
shards: std.AutoArrayHashMap(usize, Shard(Table)), shards: std.AutoArrayHashMap(usize, Shard),
handler: GatewayDispatchEvent, handler: GatewayDispatchEvent,
/// where we dispatch work for every thread, threads must be spawned upon shard creation /// where we dispatch work for every thread, threads must be spawned upon shard creation
/// make sure the address of workers is stable /// make sure the address of workers is stable
workers: std.Thread.Pool = undefined, workers: std.Thread.Pool = undefined,
/// configuration settings /// configuration settings
options: SessionOptions, options: SessionOptions,
log: Log, log: Log,
// must be initialised pub const ShardData = struct {
cache: *CacheTables(Table), /// resume seq to resume connections
resume_seq: ?usize,
pub const ShardData = struct { /// resume_gateway_url is the url to resume the connection
/// resume seq to resume connections /// https://discord.com/developers/docs/topics/gateway#ready-event
resume_seq: ?usize, resume_gateway_url: ?[]const u8,
/// resume_gateway_url is the url to resume the connection /// session_id is the unique session id of the gateway
/// https://discord.com/developers/docs/topics/gateway#ready-event session_id: ?[]const u8,
resume_gateway_url: ?[]const u8, };
/// session_id is the unique session id of the gateway pub const SessionOptions = struct {
session_id: ?[]const u8, /// Important data which is used by the manager to connect shards to the gateway. */
}; info: GatewayBotInfo,
/// Delay in milliseconds to wait before spawning next shard. OPTIMAL IS ABOVE 5100. YOU DON'T WANT TO HIT THE RATE LIMIT!!!
spawn_shard_delay: ?u64 = 5300,
/// Total amount of shards your bot uses. Useful for zero-downtime updates or resharding.
total_shards: usize = 1,
shard_start: usize = 0,
shard_end: usize = 1,
/// The payload handlers for messages on the shard.
resharding: ?struct { interval: u64, percentage: usize } = null,
/// worker threads
workers_per_shard: usize = 1,
/// The shard lifespan in milliseconds. If a shard is not connected within this time, it will be closed.
shard_lifespan: ?u64 = null,
};
pub const SessionOptions = struct { pub fn init(allocator: mem.Allocator, settings: struct {
/// Important data which is used by the manager to connect shards to the gateway. */ authorization: []const u8,
info: GatewayBotInfo, intents: Intents,
/// Delay in milliseconds to wait before spawning next shard. OPTIMAL IS ABOVE 5100. YOU DON'T WANT TO HIT THE RATE LIMIT!!! options: SessionOptions,
spawn_shard_delay: ?u64 = 5300, run: GatewayDispatchEvent,
/// Total amount of shards your bot uses. Useful for zero-downtime updates or resharding. log: Log,
total_shards: usize = 1, }) mem.Allocator.Error!Self {
shard_start: usize = 0, const concurrency = settings.options.info.session_start_limit.?.max_concurrency;
shard_end: usize = 1,
/// The payload handlers for messages on the shard.
resharding: ?struct { interval: u64, percentage: usize } = null,
/// worker threads
workers_per_shard: usize = 1,
/// The shard lifespan in milliseconds. If a shard is not connected within this time, it will be closed.
shard_lifespan: ?u64 = null,
};
pub fn init(allocator: mem.Allocator, settings: struct { return .{
token: []const u8, .allocator = allocator,
intents: Intents, .connect_queue = try ConnectQueue(Shard).init(allocator, concurrency, 5000),
options: SessionOptions, .shards = .init(allocator),
run: GatewayDispatchEvent, .workers = undefined,
log: Log, .shard_details = ShardDetails{
cache: TableTemplate, .token = settings.authorization,
}) mem.Allocator.Error!Self { .intents = settings.intents,
const concurrency = settings.options.info.session_start_limit.?.max_concurrency; },
const cache = try allocator.create(CacheTables(Table)); .handler = settings.run,
cache.* = CacheTables(Table).defaults(allocator); .options = .{
.info = .{
.url = settings.options.info.url,
.shards = settings.options.info.shards,
.session_start_limit = settings.options.info.session_start_limit,
},
.total_shards = settings.options.total_shards,
.shard_start = settings.options.shard_start,
.shard_end = settings.options.shard_end,
.workers_per_shard = settings.options.workers_per_shard,
},
.log = settings.log,
};
}
return .{ pub fn deinit(self: *Self) void {
.allocator = allocator, self.connect_queue.deinit();
.connect_queue = try ConnectQueue(Shard(Table)).init(allocator, concurrency, 5000), self.shards.deinit();
.shards = .init(allocator), }
.workers = undefined,
.shard_details = ShardDetails{ pub fn forceIdentify(self: *Self, shard_id: usize) !void {
.token = settings.token, self.logif("#{d} force identify", .{shard_id});
.intents = settings.intents, const shard = try self.create(shard_id);
},
.handler = settings.run, return shard.identify(null);
.options = .{ }
.info = .{
.url = settings.options.info.url, pub fn disconnect(self: *Self, shard_id: usize) Shard.CloseError!void {
.shards = settings.options.info.shards, return if (self.shards.get(shard_id)) |shard| shard.disconnect();
.session_start_limit = settings.options.info.session_start_limit, }
},
.total_shards = settings.options.total_shards, pub fn disconnectAll(self: *Self) Shard.CloseError!void {
.shard_start = settings.options.shard_start, while (self.shards.iterator().next()) |shard| shard.value_ptr.disconnect();
.shard_end = settings.options.shard_end, }
.workers_per_shard = settings.options.workers_per_shard,
}, /// spawn buckets in order
.log = settings.log, /// Log bucket preparation
.cache = cache, /// Divide shards into chunks based on concurrency
}; /// Assign each shard to a bucket
/// Return list of buckets
/// https://discord.com/developers/docs/events/gateway#sharding-max-concurrency
fn spawnBuckets(self: *Self) ![][]Shard {
const concurrency = self.options.info.session_start_limit.?.max_concurrency;
self.logif("{d}-{d}", .{ self.options.shard_start, self.options.shard_end });
const range = std.math.sub(usize, self.options.shard_start, self.options.shard_end) catch 1;
const bucket_count = (range + concurrency - 1) / concurrency;
self.logif("#0 preparing buckets", .{});
const buckets = try self.allocator.alloc([]Shard, bucket_count);
for (buckets, 0..) |*bucket, i| {
const bucket_size = if ((i + 1) * concurrency > range) range - (i * concurrency) else concurrency;
bucket.* = try self.allocator.alloc(Shard, bucket_size);
for (bucket.*, 0..) |*shard, j| {
shard.* = try self.create(self.options.shard_start + i * concurrency + j);
} }
}
pub fn deinit(self: *Self) void { self.logif("{d} buckets created", .{bucket_count});
self.connect_queue.deinit();
self.shards.deinit();
}
pub fn forceIdentify(self: *Self, shard_id: usize) !void { // finally defihne threads
self.logif("#{d} force identify", .{shard_id});
const shard = try self.create(shard_id);
return shard.identify(null); try self.workers.init(.{
} .allocator = self.allocator,
.n_jobs = self.options.workers_per_shard * self.options.total_shards,
});
pub fn disconnect(self: *Self, shard_id: usize) Shard(Table).CloseError!void { return buckets;
return if (self.shards.get(shard_id)) |shard| shard.disconnect(); }
}
pub fn disconnectAll(self: *Self) Shard(Table).CloseError!void { /// creates a shard and stores it
while (self.shards.iterator().next()) |shard| shard.value_ptr.disconnect(); fn create(self: *Self, shard_id: usize) !Shard {
} if (self.shards.get(shard_id)) |s| return s;
/// spawn buckets in order const shard: Shard = try .init(self.allocator, shard_id, self.options.total_shards, .{
/// Log bucket preparation .token = self.shard_details.token,
/// Divide shards into chunks based on concurrency .intents = self.shard_details.intents,
/// Assign each shard to a bucket .options = Shard.ShardOptions{
/// Return list of buckets .info = self.options.info,
/// https://discord.com/developers/docs/events/gateway#sharding-max-concurrency .ratelimit_options = .{},
fn spawnBuckets(self: *Self) ![][]Shard(Table) { },
const concurrency = self.options.info.session_start_limit.?.max_concurrency; .run = self.handler,
.log = self.log,
.sharder_pool = &self.workers,
});
self.logif("{d}-{d}", .{ self.options.shard_start, self.options.shard_end }); try self.shards.put(shard_id, shard);
const range = std.math.sub(usize, self.options.shard_start, self.options.shard_end) catch 1; return shard;
const bucket_count = (range + concurrency - 1) / concurrency; }
self.logif("#0 preparing buckets", .{}); pub fn resume_(self: *Self, shard_id: usize, shard_data: ShardData) void {
if (self.shards.contains(shard_id)) return error.CannotOverrideExistingShard;
const buckets = try self.allocator.alloc([]Shard(Table), bucket_count); const shard = self.create(shard_id);
for (buckets, 0..) |*bucket, i| { shard.data = shard_data;
const bucket_size = if ((i + 1) * concurrency > range) range - (i * concurrency) else concurrency;
bucket.* = try self.allocator.alloc(Shard(Table), bucket_size); return self.connect_queue.push(.{
.shard = shard,
.callback = &callback,
});
}
for (bucket.*, 0..) |*shard, j| { fn callback(self: *ConnectQueue(Shard).RequestWithShard) anyerror!void {
shard.* = try self.create(self.options.shard_start + i * concurrency + j); try self.shard.connect();
} }
}
self.logif("{d} buckets created", .{bucket_count}); pub fn spawnShards(self: *Self) !void {
const buckets = try self.spawnBuckets();
// finally defihne threads self.logif("Spawning shards", .{});
try self.workers.init(.{ for (buckets) |bucket| {
.allocator = self.allocator, for (bucket) |shard| {
.n_jobs = self.options.workers_per_shard * self.options.total_shards, self.logif("adding {d} to connect queue", .{shard.id});
}); try self.connect_queue.push(.{
return buckets;
}
/// creates a shard and stores it
fn create(self: *Self, shard_id: usize) !Shard(Table) {
if (self.shards.get(shard_id)) |s| return s;
const shard: Shard(Table) = try .init(self.allocator, shard_id, self.options.total_shards, .{
.token = self.shard_details.token,
.intents = self.shard_details.intents,
.options = Shard(Table).ShardOptions{
.info = self.options.info,
.ratelimit_options = .{},
},
.run = self.handler,
.log = self.log,
.cache = self.cache,
.sharder_pool = &self.workers,
});
try self.shards.put(shard_id, shard);
return shard;
}
pub fn resume_(self: *Self, shard_id: usize, shard_data: ShardData) void {
if (self.shards.contains(shard_id)) return error.CannotOverrideExistingShard;
const shard = self.create(shard_id);
shard.data = shard_data;
return self.connect_queue.push(.{
.shard = shard, .shard = shard,
.callback = &callback, .callback = &callback,
}); });
} }
}
fn callback(self: *ConnectQueue(Shard(Table)).RequestWithShard) anyerror!void { //self.startResharder();
try self.shard.connect(); }
}
pub fn send(self: *Self, shard_id: usize, data: anytype) Shard.SendError!void {
pub fn spawnShards(self: *Self) !void { if (self.shards.get(shard_id)) |shard| try shard.send(data);
const buckets = try self.spawnBuckets(); }
self.logif("Spawning shards", .{}); // SPEC OF THE RESHARDER:
// Class Self
for (buckets) |bucket| { //
for (bucket) |shard| { // Method startResharder():
self.logif("adding {d} to connect queue", .{shard.id}); // If resharding interval is not set or shard bounds are not valid:
try self.connect_queue.push(.{ // Exit
.shard = shard, // Set up periodic check for resharding:
.callback = &callback, // If new shards are required:
}); // Log resharding process
} // Update options with new shard settings
} // Disconnect old shards and clear them from manager
// Spawn shards again with updated configuration
//self.startResharder(); //
}
inline fn logif(self: *Self, comptime format: []const u8, args: anytype) void {
pub fn send(self: *Self, shard_id: usize, data: anytype) Shard(Table).SendError!void { internalLogif(self.log, format, args);
if (self.shards.get(shard_id)) |shard| try shard.send(data);
}
// SPEC OF THE RESHARDER:
// Class Self
//
// Method startResharder():
// If resharding interval is not set or shard bounds are not valid:
// Exit
// Set up periodic check for resharding:
// If new shards are required:
// Log resharding process
// Update options with new shard settings
// Disconnect old shards and clear them from manager
// Spawn shards again with updated configuration
//
inline fn logif(self: *Self, comptime format: []const u8, args: anytype) void {
internalLogif(self.log, format, args);
}
};
} }

View File

@ -19,6 +19,7 @@ const Types = @import("../structures/types.zig");
pub const debug = std.log.scoped(.@"discord.zig"); pub const debug = std.log.scoped(.@"discord.zig");
const Shard = @import("../shard/shard.zig");
pub const Log = union(enum) { yes, no }; pub const Log = union(enum) { yes, no };
pub inline fn logif(log: Log, comptime format: []const u8, args: anytype) void { pub inline fn logif(log: Log, comptime format: []const u8, args: anytype) void {
@ -29,82 +30,82 @@ pub inline fn logif(log: Log, comptime format: []const u8, args: anytype) void {
} }
pub const GatewayDispatchEvent = struct { pub const GatewayDispatchEvent = struct {
application_command_permissions_update: ?*const fn (save: *anyopaque, application_command_permissions: Types.ApplicationCommandPermissions) anyerror!void = undefined, application_command_permissions_update: ?*const fn (shard: *Shard, application_command_permissions: Types.ApplicationCommandPermissions) anyerror!void = undefined,
auto_moderation_rule_create: ?*const fn (save: *anyopaque, rule: Types.AutoModerationRule) anyerror!void = undefined, auto_moderation_rule_create: ?*const fn (shard: *Shard, rule: Types.AutoModerationRule) anyerror!void = undefined,
auto_moderation_rule_update: ?*const fn (save: *anyopaque, rule: Types.AutoModerationRule) anyerror!void = undefined, auto_moderation_rule_update: ?*const fn (shard: *Shard, rule: Types.AutoModerationRule) anyerror!void = undefined,
auto_moderation_rule_delete: ?*const fn (save: *anyopaque, rule: Types.AutoModerationRule) anyerror!void = undefined, auto_moderation_rule_delete: ?*const fn (shard: *Shard, rule: Types.AutoModerationRule) anyerror!void = undefined,
auto_moderation_action_execution: ?*const fn (save: *anyopaque, action_execution: Types.AutoModerationActionExecution) anyerror!void = undefined, auto_moderation_action_execution: ?*const fn (shard: *Shard, action_execution: Types.AutoModerationActionExecution) anyerror!void = undefined,
channel_create: ?*const fn (save: *anyopaque, chan: Types.Channel) anyerror!void = undefined, channel_create: ?*const fn (shard: *Shard, chan: Types.Channel) anyerror!void = undefined,
channel_update: ?*const fn (save: *anyopaque, chan: Types.Channel) anyerror!void = undefined, channel_update: ?*const fn (shard: *Shard, chan: Types.Channel) anyerror!void = undefined,
/// this isn't send when the channel is not relevant to you /// this isn't send when the channel is not relevant to you
channel_delete: ?*const fn (save: *anyopaque, chan: Types.Channel) anyerror!void = undefined, channel_delete: ?*const fn (shard: *Shard, chan: Types.Channel) anyerror!void = undefined,
channel_pins_update: ?*const fn (save: *anyopaque, chan_pins_update: Types.ChannelPinsUpdate) anyerror!void = undefined, channel_pins_update: ?*const fn (shard: *Shard, chan_pins_update: Types.ChannelPinsUpdate) anyerror!void = undefined,
thread_create: ?*const fn (save: *anyopaque, thread: Types.Channel) anyerror!void = undefined, thread_create: ?*const fn (shard: *Shard, thread: Types.Channel) anyerror!void = undefined,
thread_update: ?*const fn (save: *anyopaque, thread: Types.Channel) anyerror!void = undefined, thread_update: ?*const fn (shard: *Shard, thread: Types.Channel) anyerror!void = undefined,
/// has `id`, `guild_id`, `parent_id`, and `type` fields. /// has `id`, `guild_id`, `parent_id`, and `type` fields.
thread_delete: ?*const fn (save: *anyopaque, thread: Types.Partial(Types.Channel)) anyerror!void = undefined, thread_delete: ?*const fn (shard: *Shard, thread: Types.Partial(Types.Channel)) anyerror!void = undefined,
thread_list_sync: ?*const fn (save: *anyopaque, data: Types.ThreadListSync) anyerror!void = undefined, thread_list_sync: ?*const fn (shard: *Shard, data: Types.ThreadListSync) anyerror!void = undefined,
thread_member_update: ?*const fn (save: *anyopaque, guild_id: Types.ThreadMemberUpdate) anyerror!void = undefined, thread_member_update: ?*const fn (shard: *Shard, guild_id: Types.ThreadMemberUpdate) anyerror!void = undefined,
thread_members_update: ?*const fn (save: *anyopaque, thread_data: Types.ThreadMembersUpdate) anyerror!void = undefined, thread_members_update: ?*const fn (shard: *Shard, thread_data: Types.ThreadMembersUpdate) anyerror!void = undefined,
// TODO: implement // guild_audit_log_entry_create: null = null, // TODO: implement // guild_audit_log_entry_create: null = null,
guild_create: ?*const fn (save: *anyopaque, guild: Types.Guild) anyerror!void = undefined, guild_create: ?*const fn (shard: *Shard, guild: Types.Guild) anyerror!void = undefined,
guild_create_unavailable: ?*const fn (save: *anyopaque, guild: Types.UnavailableGuild) anyerror!void = undefined, guild_create_unavailable: ?*const fn (shard: *Shard, guild: Types.UnavailableGuild) anyerror!void = undefined,
guild_update: ?*const fn (save: *anyopaque, guild: Types.Guild) anyerror!void = undefined, guild_update: ?*const fn (shard: *Shard, guild: Types.Guild) anyerror!void = undefined,
/// this is not necessarily sent upon deletion of a guild /// this is not necessarily sent upon deletion of a guild
/// but from when a user is *removed* therefrom /// but from when a user is *removed* therefrom
guild_delete: ?*const fn (save: *anyopaque, guild: Types.UnavailableGuild) anyerror!void = undefined, guild_delete: ?*const fn (shard: *Shard, guild: Types.UnavailableGuild) anyerror!void = undefined,
guild_ban_add: ?*const fn (save: *anyopaque, gba: Types.GuildBanAddRemove) anyerror!void = undefined, guild_ban_add: ?*const fn (shard: *Shard, gba: Types.GuildBanAddRemove) anyerror!void = undefined,
guild_ban_remove: ?*const fn (save: *anyopaque, gbr: Types.GuildBanAddRemove) anyerror!void = undefined, guild_ban_remove: ?*const fn (shard: *Shard, gbr: Types.GuildBanAddRemove) anyerror!void = undefined,
guild_emojis_update: ?*const fn (save: *anyopaque, fields: Types.GuildEmojisUpdate) anyerror!void = undefined, guild_emojis_update: ?*const fn (shard: *Shard, fields: Types.GuildEmojisUpdate) anyerror!void = undefined,
guild_stickers_update: ?*const fn (save: *anyopaque, fields: Types.GuildStickersUpdate) anyerror!void = undefined, guild_stickers_update: ?*const fn (shard: *Shard, fields: Types.GuildStickersUpdate) anyerror!void = undefined,
guild_integrations_update: ?*const fn (save: *anyopaque, fields: Types.GuildIntegrationsUpdate) anyerror!void = undefined, guild_integrations_update: ?*const fn (shard: *Shard, fields: Types.GuildIntegrationsUpdate) anyerror!void = undefined,
guild_member_add: ?*const fn (save: *anyopaque, guild_id: Types.GuildMemberAdd) anyerror!void = undefined, guild_member_add: ?*const fn (shard: *Shard, guild_id: Types.GuildMemberAdd) anyerror!void = undefined,
guild_member_update: ?*const fn (save: *anyopaque, fields: Types.GuildMemberUpdate) anyerror!void = undefined, guild_member_update: ?*const fn (shard: *Shard, fields: Types.GuildMemberUpdate) anyerror!void = undefined,
guild_member_remove: ?*const fn (save: *anyopaque, user: Types.GuildMemberRemove) anyerror!void = undefined, guild_member_remove: ?*const fn (shard: *Shard, user: Types.GuildMemberRemove) anyerror!void = undefined,
guild_members_chunk: ?*const fn (save: *anyopaque, data: Types.GuildMembersChunk) anyerror!void = undefined, guild_members_chunk: ?*const fn (shard: *Shard, data: Types.GuildMembersChunk) anyerror!void = undefined,
guild_role_create: ?*const fn (save: *anyopaque, role: Types.GuildRoleCreate) anyerror!void = undefined, guild_role_create: ?*const fn (shard: *Shard, role: Types.GuildRoleCreate) anyerror!void = undefined,
guild_role_delete: ?*const fn (save: *anyopaque, role: Types.GuildRoleDelete) anyerror!void = undefined, guild_role_delete: ?*const fn (shard: *Shard, role: Types.GuildRoleDelete) anyerror!void = undefined,
guild_role_update: ?*const fn (save: *anyopaque, role: Types.GuildRoleUpdate) anyerror!void = undefined, guild_role_update: ?*const fn (shard: *Shard, role: Types.GuildRoleUpdate) anyerror!void = undefined,
guild_scheduled_event_create: ?*const fn (save: *anyopaque, s_event: Types.ScheduledEvent) anyerror!void = undefined, guild_scheduled_event_create: ?*const fn (shard: *Shard, s_event: Types.ScheduledEvent) anyerror!void = undefined,
guild_scheduled_event_update: ?*const fn (save: *anyopaque, s_event: Types.ScheduledEvent) anyerror!void = undefined, guild_scheduled_event_update: ?*const fn (shard: *Shard, s_event: Types.ScheduledEvent) anyerror!void = undefined,
guild_scheduled_event_delete: ?*const fn (save: *anyopaque, s_event: Types.ScheduledEvent) anyerror!void = undefined, guild_scheduled_event_delete: ?*const fn (shard: *Shard, s_event: Types.ScheduledEvent) anyerror!void = undefined,
guild_scheduled_event_user_add: ?*const fn (save: *anyopaque, data: Types.ScheduledEventUserAdd) anyerror!void = undefined, guild_scheduled_event_user_add: ?*const fn (shard: *Shard, data: Types.ScheduledEventUserAdd) anyerror!void = undefined,
guild_scheduled_event_user_remove: ?*const fn (save: *anyopaque, data: Types.ScheduledEventUserRemove) anyerror!void = undefined, guild_scheduled_event_user_remove: ?*const fn (shard: *Shard, data: Types.ScheduledEventUserRemove) anyerror!void = undefined,
integration_create: ?*const fn (save: *anyopaque, guild_id: Types.IntegrationCreateUpdate) anyerror!void = undefined, integration_create: ?*const fn (shard: *Shard, guild_id: Types.IntegrationCreateUpdate) anyerror!void = undefined,
integration_update: ?*const fn (save: *anyopaque, guild_id: Types.IntegrationCreateUpdate) anyerror!void = undefined, integration_update: ?*const fn (shard: *Shard, guild_id: Types.IntegrationCreateUpdate) anyerror!void = undefined,
integration_delete: ?*const fn (save: *anyopaque, guild_id: Types.IntegrationDelete) anyerror!void = undefined, integration_delete: ?*const fn (shard: *Shard, guild_id: Types.IntegrationDelete) anyerror!void = undefined,
interaction_create: ?*const fn (save: *anyopaque, interaction: Types.MessageInteraction) anyerror!void = undefined, interaction_create: ?*const fn (shard: *Shard, interaction: Types.MessageInteraction) anyerror!void = undefined,
invite_create: ?*const fn (save: *anyopaque, data: Types.InviteCreate) anyerror!void = undefined, invite_create: ?*const fn (shard: *Shard, data: Types.InviteCreate) anyerror!void = undefined,
invite_delete: ?*const fn (save: *anyopaque, data: Types.InviteDelete) anyerror!void = undefined, invite_delete: ?*const fn (shard: *Shard, data: Types.InviteDelete) anyerror!void = undefined,
message_create: ?*const fn (save: *anyopaque, message: Types.Message) anyerror!void = undefined, message_create: ?*const fn (shard: *Shard, message: Types.Message) anyerror!void = undefined,
message_update: ?*const fn (save: *anyopaque, message: Types.Message) anyerror!void = undefined, message_update: ?*const fn (shard: *Shard, message: Types.Message) anyerror!void = undefined,
message_delete: ?*const fn (save: *anyopaque, log: Types.MessageDelete) anyerror!void = undefined, message_delete: ?*const fn (shard: *Shard, log: Types.MessageDelete) anyerror!void = undefined,
message_delete_bulk: ?*const fn (save: *anyopaque, log: Types.MessageDeleteBulk) anyerror!void = undefined, message_delete_bulk: ?*const fn (shard: *Shard, log: Types.MessageDeleteBulk) anyerror!void = undefined,
message_reaction_add: ?*const fn (save: *anyopaque, log: Types.MessageReactionAdd) anyerror!void = undefined, message_reaction_add: ?*const fn (shard: *Shard, log: Types.MessageReactionAdd) anyerror!void = undefined,
message_reaction_remove_all: ?*const fn (save: *anyopaque, data: Types.MessageReactionRemoveAll) anyerror!void = undefined, message_reaction_remove_all: ?*const fn (shard: *Shard, data: Types.MessageReactionRemoveAll) anyerror!void = undefined,
message_reaction_remove: ?*const fn (save: *anyopaque, data: Types.MessageReactionRemove) anyerror!void = undefined, message_reaction_remove: ?*const fn (shard: *Shard, data: Types.MessageReactionRemove) anyerror!void = undefined,
message_reaction_remove_emoji: ?*const fn (save: *anyopaque, data: Types.MessageReactionRemoveEmoji) anyerror!void = undefined, message_reaction_remove_emoji: ?*const fn (shard: *Shard, data: Types.MessageReactionRemoveEmoji) anyerror!void = undefined,
presence_update: ?*const fn (save: *anyopaque, presence: Types.PresenceUpdate) anyerror!void = undefined, presence_update: ?*const fn (shard: *Shard, presence: Types.PresenceUpdate) anyerror!void = undefined,
stage_instance_create: ?*const fn (save: *anyopaque, stage_instance: Types.StageInstance) anyerror!void = undefined, stage_instance_create: ?*const fn (shard: *Shard, stage_instance: Types.StageInstance) anyerror!void = undefined,
stage_instance_update: ?*const fn (save: *anyopaque, stage_instance: Types.StageInstance) anyerror!void = undefined, stage_instance_update: ?*const fn (shard: *Shard, stage_instance: Types.StageInstance) anyerror!void = undefined,
stage_instance_delete: ?*const fn (save: *anyopaque, stage_instance: Types.StageInstance) anyerror!void = undefined, stage_instance_delete: ?*const fn (shard: *Shard, stage_instance: Types.StageInstance) anyerror!void = undefined,
typing_start: ?*const fn (save: *anyopaque, data: Types.TypingStart) anyerror!void = undefined, typing_start: ?*const fn (shard: *Shard, data: Types.TypingStart) anyerror!void = undefined,
/// remember this is only sent when you change your profile yourself/your bot does /// remember this is only sent when you change your profile yourself/your bot does
user_update: ?*const fn (save: *anyopaque, user: Types.User) anyerror!void = undefined, user_update: ?*const fn (shard: *Shard, user: Types.User) anyerror!void = undefined,
// will do these someday, music is rather pointless at this point in time // will do these someday, music is rather pointless at this point in time
// TODO: implement // voice_channel_effect_send: null = null, // TODO: implement // voice_channel_effect_send: null = null,
// TODO: implement // voice_state_update: null = null, // TODO: implement // voice_state_update: null = null,
// TODO: implement // voice_server_update: null = null, // TODO: implement // voice_server_update: null = null,
webhooks_update: ?*const fn (save: *anyopaque, fields: Types.WebhookUpdate) anyerror!void = undefined, webhooks_update: ?*const fn (shard: *Shard, fields: Types.WebhookUpdate) anyerror!void = undefined,
entitlement_create: ?*const fn (save: *anyopaque, entitlement: Types.Entitlement) anyerror!void = undefined, entitlement_create: ?*const fn (shard: *Shard, entitlement: Types.Entitlement) anyerror!void = undefined,
entitlement_update: ?*const fn (save: *anyopaque, entitlement: Types.Entitlement) anyerror!void = undefined, entitlement_update: ?*const fn (shard: *Shard, entitlement: Types.Entitlement) anyerror!void = undefined,
/// discord claims this is infrequent, therefore not throughoutly tested - Yuzu /// discord claims this is infrequent, therefore not throughoutly tested - Yuzu
entitlement_delete: ?*const fn (save: *anyopaque, entitlement: Types.Entitlement) anyerror!void = undefined, entitlement_delete: ?*const fn (shard: *Shard, entitlement: Types.Entitlement) anyerror!void = undefined,
message_poll_vote_add: ?*const fn (save: *anyopaque, poll: Types.PollVoteAdd) anyerror!void = undefined, message_poll_vote_add: ?*const fn (shard: *Shard, poll: Types.PollVoteAdd) anyerror!void = undefined,
message_poll_vote_remove: ?*const fn (save: *anyopaque, poll: Types.PollVoteRemove) anyerror!void = undefined, message_poll_vote_remove: ?*const fn (shard: *Shard, poll: Types.PollVoteRemove) anyerror!void = undefined,
ready: ?*const fn (save: *anyopaque, data: Types.Ready) anyerror!void = undefined, ready: ?*const fn (shard: *Shard, data: Types.Ready) anyerror!void = undefined,
// TODO: implement // resumed: null = null, // TODO: implement // resumed: null = null,
any: ?*const fn (save: *anyopaque, data: std.json.Value) anyerror!void = undefined, any: ?*const fn (shard: *Shard, data: std.json.Value) anyerror!void = undefined,
}; };

View File

@ -20,18 +20,17 @@ const Shard = Discord.Shard;
const Intents = Discord.Intents; const Intents = Discord.Intents;
const INTENTS = 53608447; const INTENTS = 53608447;
const Cache = Discord.cache.TableTemplate{};
fn ready(_: *anyopaque, payload: Discord.Ready) !void { var session: *Discord.Session = undefined;
fn ready(_: *Shard, payload: Discord.Ready) !void {
std.debug.print("logged in as {s}\n", .{payload.user.username}); std.debug.print("logged in as {s}\n", .{payload.user.username});
// cache demonstration TODO
} }
fn message_create(shard_ptr: *anyopaque, message: Discord.Message) !void { fn message_create(_: *Shard, message: Discord.Message) !void {
// set custom cache
const session: *Shard(Cache) = @ptrCast(@alignCast(shard_ptr));
if (message.content != null and std.ascii.eqlIgnoreCase(message.content.?, "!hi")) { if (message.content != null and std.ascii.eqlIgnoreCase(message.content.?, "!hi")) {
var result = try session.sendMessage(message.channel_id, .{ .content = "hi :)" }); var result = try session.api.sendMessage(message.channel_id, .{ .content = "hi :)" });
defer result.deinit(); defer result.deinit();
const m = result.value.unwrap(); const m = result.value.unwrap();
@ -43,8 +42,9 @@ pub fn main() !void {
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var handler = Discord.init(allocator); session = try allocator.create(Discord.Session);
defer handler.deinit(); session.* = Discord.init(allocator);
defer session.deinit();
const env_map = try allocator.create(std.process.EnvMap); const env_map = try allocator.create(std.process.EnvMap);
env_map.* = try std.process.getEnvMap(allocator); env_map.* = try std.process.getEnvMap(allocator);
@ -54,12 +54,12 @@ pub fn main() !void {
@panic("DISCORD_TOKEN not found in environment variables"); @panic("DISCORD_TOKEN not found in environment variables");
}; };
try handler.start(.{ try session.start(.{
.token = token, .authorization = token,
.intents = Intents.fromRaw(INTENTS), .intents = Intents.fromRaw(INTENTS),
.run = .{ .message_create = &message_create, .ready = &ready }, .run = .{ .message_create = &message_create, .ready = &ready },
.log = .yes, .log = .yes,
.options = .{}, .options = .{},
.cache = Cache, .cache = .defaults(allocator),
}); });
} }