minor refactor
This commit is contained in:
parent
195c5945eb
commit
b8c42bb22c
@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void {
|
||||
const zlib = b.dependency("zlib", .{});
|
||||
|
||||
const dzig = b.addModule("discord.zig", .{
|
||||
.root_source_file = b.path("src/discord.zig"),
|
||||
.root_source_file = b.path("src/lib.zig"),
|
||||
.link_libc = true,
|
||||
});
|
||||
|
||||
@ -47,7 +47,7 @@ pub fn build(b: *std.Build) void {
|
||||
|
||||
const lib = b.addStaticLibrary(.{
|
||||
.name = "discord.zig",
|
||||
.root_source_file = b.path("src/discord.zig"),
|
||||
.root_source_file = b.path("lib/discord.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
22
src/cache.zig → src/cache/cache.zig
vendored
22
src/cache.zig → src/cache/cache.zig
vendored
@ -4,23 +4,23 @@ const std = @import("std");
|
||||
/// otherwise, simply do TableTemplate{}
|
||||
/// this is a template for the cache tables
|
||||
pub const TableTemplate = struct {
|
||||
comptime User: type = @import("./config.zig").StoredUser,
|
||||
comptime Guild: type = @import("./config.zig").StoredGuild,
|
||||
comptime Channel: type = @import("./config.zig").StoredChannel,
|
||||
comptime Emoji: type = @import("./config.zig").StoredEmoji,
|
||||
comptime Message: type = @import("./config.zig").StoredMessage,
|
||||
comptime Role: type = @import("./config.zig").StoredRole,
|
||||
comptime Sticker: type = @import("./config.zig").StoredSticker,
|
||||
comptime Reaction: type = @import("./config.zig").StoredReaction,
|
||||
comptime Member: type = @import("./config.zig").StoredMember,
|
||||
comptime Thread: type = @import("./config.zig").StoredChannel,
|
||||
comptime User: type = @import("../config.zig").StoredUser,
|
||||
comptime Guild: type = @import("../config.zig").StoredGuild,
|
||||
comptime Channel: type = @import("../config.zig").StoredChannel,
|
||||
comptime Emoji: type = @import("../config.zig").StoredEmoji,
|
||||
comptime Message: type = @import("../config.zig").StoredMessage,
|
||||
comptime Role: type = @import("../config.zig").StoredRole,
|
||||
comptime Sticker: type = @import("../config.zig").StoredSticker,
|
||||
comptime Reaction: type = @import("../config.zig").StoredReaction,
|
||||
comptime Member: type = @import("../config.zig").StoredMember,
|
||||
comptime Thread: type = @import("../config.zig").StoredChannel,
|
||||
};
|
||||
|
||||
// by default this caches everything
|
||||
// therefore we'll allow custom cache tables
|
||||
pub fn CacheTables(comptime Table: TableTemplate) type {
|
||||
return struct {
|
||||
const Snowflake = @import("./structures/snowflake.zig").Snowflake;
|
||||
const Snowflake = @import("../structures/snowflake.zig").Snowflake;
|
||||
|
||||
const StoredUser: type = Table.User;
|
||||
const StoredGuild: type = Table.Guild;
|
@ -4,6 +4,7 @@ const PremiumTypes = @import("./structures/shared.zig").PremiumTypes;
|
||||
const Snowflake = @import("./structures/snowflake.zig").Snowflake;
|
||||
const AvatarDecorationData = @import("./structures/user.zig").AvatarDecorationData;
|
||||
|
||||
|
||||
/// https://discord.com/developers/docs/resources/user#user-object
|
||||
/// modify this to your liking
|
||||
pub const StoredUser = struct {
|
||||
|
@ -29,5 +29,5 @@ pub const DiscordError = struct {
|
||||
};
|
||||
|
||||
pub fn Result(comptime T: type) type {
|
||||
return @import("json-helper.zig").OwnedEither(DiscordError, T);
|
||||
return @import("./utils/json.zig").OwnedEither(DiscordError, T);
|
||||
}
|
||||
|
0
src/http/api.zig
Normal file
0
src/http/api.zig
Normal file
@ -18,10 +18,10 @@ const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const http = std.http;
|
||||
const json = std.json;
|
||||
const json_helpers = @import("json-helper.zig");
|
||||
const json_helpers = @import("../utils/json.zig");
|
||||
|
||||
pub const Result = @import("errors.zig").Result;
|
||||
pub const DiscordError = @import("errors.zig").DiscordError;
|
||||
pub const Result = @import("../errors.zig").Result;
|
||||
pub const DiscordError = @import("../errors.zig").DiscordError;
|
||||
|
||||
pub const BASE_URL = "https://discord.com/api/v10";
|
||||
|
@ -67,8 +67,6 @@ pub const PermissionStrings = @import("structures/types.zig").PermissionStrings;
|
||||
pub const GatewayCloseEventCodes = @import("structures/types.zig").GatewayCloseEventCodes;
|
||||
pub const GatewayOpcodes = @import("structures/types.zig").GatewayOpcodes;
|
||||
pub const GatewayDispatchEventNames = @import("structures/types.zig").GatewayDispatchEventNames;
|
||||
pub const GatewayIntents = @import("structures/types.zig").GatewayIntents;
|
||||
pub const Intents = @import("structures/types.zig").Intents;
|
||||
pub const InteractionResponseTypes = @import("structures/types.zig").InteractionResponseTypes;
|
||||
pub const SortOrderTypes = @import("structures/types.zig").SortOrderTypes;
|
||||
pub const ForumLayout = @import("structures/types.zig").ForumLayout;
|
||||
@ -307,26 +305,26 @@ pub const ApplicationWebhook = @import("structures/types.zig").ApplicationWebhoo
|
||||
pub const GatewayPayload = @import("structures/types.zig").GatewayPayload;
|
||||
// END USING NAMESPACE
|
||||
|
||||
pub const CacheTables = @import("cache.zig").CacheTables;
|
||||
pub const CacheLike = @import("cache.zig").CacheLike;
|
||||
pub const DefaultCache = @import("cache.zig").DefaultCache;
|
||||
pub const CacheTables = @import("cache/cache.zig").CacheTables;
|
||||
pub const CacheLike = @import("cache/cache.zig").CacheLike;
|
||||
pub const DefaultCache = @import("cache/cache.zig").DefaultCache;
|
||||
|
||||
pub const Permissions = @import("extra/permissions.zig").Permissions;
|
||||
pub const Shard = @import("shard.zig").Shard;
|
||||
pub const Permissions = @import("utils/permissions.zig").Permissions;
|
||||
pub const Shard = @import("shard/shard.zig").Shard;
|
||||
pub const zjson = @compileError("Deprecated.");
|
||||
|
||||
pub const Internal = @import("internal.zig");
|
||||
pub const Internal = @import("utils/core.zig");
|
||||
const GatewayDispatchEvent = Internal.GatewayDispatchEvent;
|
||||
const GatewayBotInfo = Internal.GatewayBotInfo;
|
||||
const GatewayBotInfo = @import("shard/util.zig").GatewayBotInfo;
|
||||
const Log = Internal.Log;
|
||||
|
||||
// sharder
|
||||
pub const Sharder = @import("sharder.zig").ShardManager;
|
||||
pub const Sharder = @import("shard/sharder.zig").ShardManager;
|
||||
|
||||
pub const cache = @import("cache.zig");
|
||||
pub const cache = @import("cache/cache.zig");
|
||||
|
||||
pub const FetchReq = @import("http.zig").FetchReq;
|
||||
pub const FileData = @import("http.zig").FileData;
|
||||
pub const FetchReq = @import("http/http.zig").FetchReq;
|
||||
pub const FileData = @import("http/http.zig").FileData;
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
@ -421,6 +419,9 @@ pub fn deinit(self: *Session) void {
|
||||
self.deinit();
|
||||
}
|
||||
|
||||
pub const GatewayIntents = @import("./shard/intents.zig").GatewayIntents;
|
||||
pub const Intents = @import("./shard/intents.zig").Intents;
|
||||
|
||||
pub fn start(self: *Session, settings: struct {
|
||||
token: []const u8,
|
||||
intents: Intents,
|
106
src/shard/bucket.zig
Normal file
106
src/shard/bucket.zig
Normal file
@ -0,0 +1,106 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
const time = std.time;
|
||||
const atomic = std.atomic;
|
||||
const Thread = std.Thread;
|
||||
const PriorityQueue = std.PriorityQueue;
|
||||
|
||||
pub const Bucket = struct {
|
||||
pub const RequestWithPrio = struct {
|
||||
callback: *const fn () void,
|
||||
priority: u32 = 1,
|
||||
};
|
||||
|
||||
fn lessthan(_: void, a: RequestWithPrio, b: RequestWithPrio) math.Order {
|
||||
return math.order(a.priority, b.priority);
|
||||
}
|
||||
|
||||
/// The queue of requests to acquire an available request.
|
||||
/// Mapped by (shardId, RequestWithPrio)
|
||||
queue: PriorityQueue(RequestWithPrio, void, lessthan),
|
||||
|
||||
limit: usize,
|
||||
refill_interval: u64,
|
||||
refill_amount: usize,
|
||||
|
||||
/// The amount of requests that have been used up already.
|
||||
used: usize = 0,
|
||||
|
||||
/// Whether or not the queue is already processing.
|
||||
processing: bool = false,
|
||||
|
||||
/// Whether the timeout should be killed because there is already one running
|
||||
should_stop: atomic.Value(bool) = .init(false),
|
||||
|
||||
/// The timestamp in milliseconds when the next refill is scheduled.
|
||||
refills_at: ?u64 = null,
|
||||
|
||||
pub fn init(allocator: mem.Allocator, limit: usize, refill_interval: u64, refill_amount: usize) Bucket {
|
||||
return .{
|
||||
.queue = PriorityQueue(RequestWithPrio, void, lessthan).init(allocator, {}),
|
||||
.limit = limit,
|
||||
.refill_interval = refill_interval,
|
||||
.refill_amount = refill_amount,
|
||||
};
|
||||
}
|
||||
|
||||
fn remaining(self: *Bucket) usize {
|
||||
if (self.limit < self.used) {
|
||||
return 0;
|
||||
} else {
|
||||
return self.limit - self.used;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refill(self: *Bucket) Thread.SpawnError!void {
|
||||
// Lower the used amount by the refill amount
|
||||
self.used = if (self.refill_amount > self.used) 0 else self.used - self.refill_amount;
|
||||
|
||||
// Reset the refills_at timestamp since it just got refilled
|
||||
self.refills_at = null;
|
||||
|
||||
if (self.used > 0) {
|
||||
if (self.should_stop.load(.monotonic) == true) {
|
||||
self.should_stop.store(false, .monotonic);
|
||||
}
|
||||
const thread = try Thread.spawn(.{}, Bucket.timeout, .{self});
|
||||
thread.detach;
|
||||
self.refills_at = time.milliTimestamp() + self.refill_interval;
|
||||
}
|
||||
}
|
||||
|
||||
fn timeout(self: *Bucket) void {
|
||||
while (!self.should_stop.load(.monotonic)) {
|
||||
self.refill();
|
||||
time.sleep(time.ns_per_ms * self.refill_interval);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn processQueue(self: *Bucket) std.Thread.SpawnError!void {
|
||||
if (self.processing) return;
|
||||
|
||||
while (self.queue.remove()) |first_element| {
|
||||
if (self.remaining() != 0) {
|
||||
first_element.callback();
|
||||
self.used += 1;
|
||||
|
||||
if (!self.should_stop.load(.monotonic)) {
|
||||
const thread = try Thread.spawn(.{}, Bucket.timeout, .{self});
|
||||
thread.detach;
|
||||
self.refills_at = time.milliTimestamp() + self.refill_interval;
|
||||
}
|
||||
} else if (self.refills_at) |ra| {
|
||||
const now = time.milliTimestamp();
|
||||
if (ra > now) time.sleep(time.ns_per_ms * (ra - now));
|
||||
}
|
||||
}
|
||||
|
||||
self.processing = false;
|
||||
}
|
||||
|
||||
pub fn acquire(self: *Bucket, rq: RequestWithPrio) !void {
|
||||
try self.queue.add(rq);
|
||||
try self.processQueue();
|
||||
}
|
||||
};
|
87
src/shard/connect_queue.zig
Normal file
87
src/shard/connect_queue.zig
Normal file
@ -0,0 +1,87 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
/// inspired from:
|
||||
/// https://github.com/tiramisulabs/seyfert/blob/main/src/websocket/structures/timeout.ts
|
||||
pub fn ConnectQueue(comptime T: type) type {
|
||||
return struct {
|
||||
pub const RequestWithShard = struct {
|
||||
callback: *const fn (self: *RequestWithShard) anyerror!void,
|
||||
shard: T,
|
||||
};
|
||||
|
||||
// ignore this function
|
||||
// so it becomes a regular dequeue
|
||||
fn eq(_: void, _: RequestWithShard, _: RequestWithShard) std.math.Order {
|
||||
return std.math.Order.eq;
|
||||
}
|
||||
|
||||
dequeue: std.PriorityDequeue(RequestWithShard, void, eq),
|
||||
allocator: mem.Allocator,
|
||||
remaining: usize,
|
||||
interval_time: u64 = 5000,
|
||||
running: bool = false,
|
||||
concurrency: usize = 1,
|
||||
|
||||
pub fn init(allocator: mem.Allocator, concurrency: usize, interval_time: u64) !ConnectQueue(T) {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.dequeue = std.PriorityDequeue(RequestWithShard, void, eq).init(allocator, {}),
|
||||
.remaining = concurrency,
|
||||
.interval_time = interval_time,
|
||||
.concurrency = concurrency,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ConnectQueue(T)) void {
|
||||
self.dequeue.deinit();
|
||||
}
|
||||
|
||||
pub fn push(self: *ConnectQueue(T), req: RequestWithShard) !void {
|
||||
if (self.remaining == 0) {
|
||||
return self.dequeue.add(req);
|
||||
}
|
||||
self.remaining -= 1;
|
||||
|
||||
if (!self.running) {
|
||||
try self.startInterval();
|
||||
self.running = true;
|
||||
}
|
||||
|
||||
if (self.dequeue.count() < self.concurrency) {
|
||||
// perhaps store this?
|
||||
const ptr = try self.allocator.create(RequestWithShard);
|
||||
ptr.* = req;
|
||||
try req.callback(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
return self.dequeue.add(req);
|
||||
}
|
||||
|
||||
fn startInterval(self: *ConnectQueue(T)) !void {
|
||||
while (self.running) {
|
||||
std.Thread.sleep(std.time.ns_per_ms * (self.interval_time / self.concurrency));
|
||||
const req: ?RequestWithShard = self.dequeue.removeMin(); // pop front
|
||||
|
||||
while (self.dequeue.count() == 0 and req == null) {}
|
||||
|
||||
if (req) |r| {
|
||||
const ptr = try self.allocator.create(RequestWithShard);
|
||||
ptr.* = r;
|
||||
try @call(.auto, r.callback, .{ptr});
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.remaining < self.concurrency) {
|
||||
self.remaining += 1;
|
||||
}
|
||||
|
||||
if (self.dequeue.count() == 0) {
|
||||
self.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
179
src/shard/intents.zig
Normal file
179
src/shard/intents.zig
Normal file
@ -0,0 +1,179 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const json = std.json;
|
||||
|
||||
/// https://discord.com/developers/docs/topics/gateway#list-of-intents
|
||||
pub const GatewayIntents = packed struct {
|
||||
pub fn toRaw(self: GatewayIntents) u32 {
|
||||
return @bitCast(self);
|
||||
}
|
||||
|
||||
pub fn fromRaw(raw: u32) GatewayIntents {
|
||||
return @bitCast(raw);
|
||||
}
|
||||
|
||||
pub fn jsonParse(allocator: mem.Allocator, src: anytype, _: json.ParseOptions) !@This() {
|
||||
const value = try json.innerParse(json.Value, allocator, src, .{
|
||||
.ignore_unknown_fields = true,
|
||||
.max_value_len = 0x1000,
|
||||
});
|
||||
if (value != .integer) @panic("Invalid value for bitfield");
|
||||
|
||||
return fromRaw(@intCast(value.integer));
|
||||
}
|
||||
|
||||
pub fn jsonParseFromValue(_: mem.Allocator, src: json.Value, _: json.ParseOptions) @This() {
|
||||
if (src != .integer) @panic("Invalid value for bitfield");
|
||||
return fromRaw(@intCast(src.integer));
|
||||
}
|
||||
|
||||
///
|
||||
/// - GUILD_CREATE
|
||||
/// - GUILD_UPDATE
|
||||
/// - GUILD_DELETE
|
||||
/// - GUILD_ROLE_CREATE
|
||||
/// - GUILD_ROLE_UPDATE
|
||||
/// - GUILD_ROLE_DELETE
|
||||
/// - CHANNEL_CREATE
|
||||
/// - CHANNEL_UPDATE
|
||||
/// - CHANNEL_DELETE
|
||||
/// - CHANNEL_PINS_UPDATE
|
||||
/// - THREAD_CREATE
|
||||
/// - THREAD_UPDATE
|
||||
/// - THREAD_DELETE
|
||||
/// - THREAD_LIST_SYNC
|
||||
/// - THREAD_MEMBER_UPDATE
|
||||
/// - THREAD_MEMBERS_UPDATE
|
||||
/// - STAGE_INSTANCE_CREATE
|
||||
/// - STAGE_INSTANCE_UPDATE
|
||||
/// - STAGE_INSTANCE_DELETE
|
||||
////
|
||||
Guilds: bool = false,
|
||||
///
|
||||
/// - GUILD_MEMBER_ADD
|
||||
/// - GUILD_MEMBER_UPDATE
|
||||
/// - GUILD_MEMBER_REMOVE
|
||||
/// - THREAD_MEMBERS_UPDATE
|
||||
///
|
||||
/// This is a privileged intent.
|
||||
////
|
||||
GuildMembers: bool = false,
|
||||
///
|
||||
/// - GUILD_AUDIT_LOG_ENTRY_CREATE
|
||||
/// - GUILD_BAN_ADD
|
||||
/// - GUILD_BAN_REMOVE
|
||||
////
|
||||
GuildModeration: bool = false,
|
||||
///
|
||||
/// - GUILD_EMOJIS_UPDATE
|
||||
/// - GUILD_STICKERS_UPDATE
|
||||
////
|
||||
GuildEmojisAndStickers: bool = false,
|
||||
///
|
||||
/// - GUILD_INTEGRATIONS_UPDATE
|
||||
/// - INTEGRATION_CREATE
|
||||
/// - INTEGRATION_UPDATE
|
||||
/// - INTEGRATION_DELETE
|
||||
////
|
||||
GuildIntegrations: bool = false,
|
||||
///
|
||||
/// - WEBHOOKS_UPDATE
|
||||
////
|
||||
GuildWebhooks: bool = false,
|
||||
///
|
||||
/// - INVITE_CREATE
|
||||
/// - INVITE_DELETE
|
||||
////
|
||||
GuildInvites: bool = false,
|
||||
///
|
||||
/// - VOICE_STATE_UPDATE
|
||||
/// - VOICE_CHANNEL_EFFECT_SEND
|
||||
////
|
||||
GuildVoiceStates: bool = false,
|
||||
///
|
||||
/// - PRESENCE_UPDATE
|
||||
///
|
||||
/// This is a privileged intent.
|
||||
////
|
||||
GuildPresences: bool = false,
|
||||
///
|
||||
/// - MESSAGE_CREATE
|
||||
/// - MESSAGE_UPDATE
|
||||
/// - MESSAGE_DELETE
|
||||
/// - MESSAGE_DELETE_BULK
|
||||
///
|
||||
/// The messages do not contain content by default.
|
||||
/// If you want to receive their content too, you need to turn on the privileged `MESSAGE_CONTENT` intent. */
|
||||
GuildMessages: bool = false,
|
||||
///
|
||||
/// - MESSAGE_REACTION_ADD
|
||||
/// - MESSAGE_REACTION_REMOVE
|
||||
/// - MESSAGE_REACTION_REMOVE_ALL
|
||||
/// - MESSAGE_REACTION_REMOVE_EMOJI
|
||||
////
|
||||
GuildMessageReactions: bool = false,
|
||||
///
|
||||
/// - TYPING_START
|
||||
////
|
||||
GuildMessageTyping: bool = false,
|
||||
///
|
||||
/// - CHANNEL_CREATE
|
||||
/// - MESSAGE_CREATE
|
||||
/// - MESSAGE_UPDATE
|
||||
/// - MESSAGE_DELETE
|
||||
/// - CHANNEL_PINS_UPDATE
|
||||
////
|
||||
DirectMessages: bool = false,
|
||||
///
|
||||
/// - MESSAGE_REACTION_ADD
|
||||
/// - MESSAGE_REACTION_REMOVE
|
||||
/// - MESSAGE_REACTION_REMOVE_ALL
|
||||
/// - MESSAGE_REACTION_REMOVE_EMOJI
|
||||
////
|
||||
DirectMessageReactions: bool = false,
|
||||
///
|
||||
/// - TYPING_START
|
||||
////
|
||||
DirectMessageTyping: bool = false,
|
||||
///
|
||||
/// This intent will add all content related values to message events.
|
||||
///
|
||||
/// This is a privileged intent.
|
||||
////
|
||||
MessageContent: bool = false,
|
||||
///
|
||||
/// - GUILD_SCHEDULED_EVENT_CREATE
|
||||
/// - GUILD_SCHEDULED_EVENT_UPDATE
|
||||
/// - GUILD_SCHEDULED_EVENT_DELETE
|
||||
/// - GUILD_SCHEDULED_EVENT_USER_ADD this is experimental and unstable.
|
||||
/// - GUILD_SCHEDULED_EVENT_USER_REMOVE this is experimental and unstable.
|
||||
////
|
||||
GuildScheduledEvents: bool = false,
|
||||
_pad: u4 = 0,
|
||||
///
|
||||
/// - AUTO_MODERATION_RULE_CREATE
|
||||
/// - AUTO_MODERATION_RULE_UPDATE
|
||||
/// - AUTO_MODERATION_RULE_DELETE
|
||||
////
|
||||
AutoModerationConfiguration: bool = false,
|
||||
///
|
||||
/// - AUTO_MODERATION_ACTION_EXECUTION
|
||||
////
|
||||
AutoModerationExecution: bool = false,
|
||||
_pad2: u3 = 0,
|
||||
///
|
||||
/// - MESSAGE_POLL_VOTE_ADD
|
||||
/// - MESSAGE_POLL_VOTE_REMOVE
|
||||
////
|
||||
GuildMessagePolls: bool = false,
|
||||
///
|
||||
/// - MESSAGE_POLL_VOTE_ADD
|
||||
/// - MESSAGE_POLL_VOTE_REMOVE
|
||||
////
|
||||
DirectMessagePolls: bool = false,
|
||||
_pad3: u4 = 0,
|
||||
};
|
||||
|
||||
/// https://discord.com/developers/docs/topics/gateway#list-of-intents
|
||||
/// alias
|
||||
pub const Intents = GatewayIntents;
|
@ -28,29 +28,31 @@ const http = std.http;
|
||||
const zlib = @import("zlib");
|
||||
const json = @import("std").json;
|
||||
|
||||
const Result = @import("errors.zig").Result;
|
||||
const Result = @import("../errors.zig").Result;
|
||||
|
||||
const IdentifyProperties = @import("internal.zig").IdentifyProperties;
|
||||
const GatewayInfo = @import("internal.zig").GatewayInfo;
|
||||
const GatewayBotInfo = @import("internal.zig").GatewayBotInfo;
|
||||
const GatewaySessionStartLimit = @import("internal.zig").GatewaySessionStartLimit;
|
||||
const ShardDetails = @import("internal.zig").ShardDetails;
|
||||
const internalLogif = @import("internal.zig").logif;
|
||||
const IdentifyProperties = @import("util.zig").IdentifyProperties;
|
||||
const GatewayInfo = @import("util.zig").GatewayInfo;
|
||||
const GatewayBotInfo = @import("util.zig").GatewayBotInfo;
|
||||
const GatewaySessionStartLimit = @import("util.zig").GatewaySessionStartLimit;
|
||||
const ShardDetails = @import("util.zig").ShardDetails;
|
||||
const internalLogif = @import("../utils/core.zig").logif;
|
||||
|
||||
const Log = @import("internal.zig").Log;
|
||||
const GatewayDispatchEvent = @import("internal.zig").GatewayDispatchEvent;
|
||||
const Bucket = @import("internal.zig").Bucket;
|
||||
const default_identify_properties = @import("internal.zig").default_identify_properties;
|
||||
const Log = @import("../utils/core.zig").Log;
|
||||
const GatewayDispatchEvent = @import("../utils/core.zig").GatewayDispatchEvent;
|
||||
const Bucket = @import("bucket.zig").Bucket;
|
||||
const default_identify_properties = @import("util.zig").default_identify_properties;
|
||||
|
||||
const Types = @import("./structures/types.zig");
|
||||
const Types = @import("../structures/types.zig");
|
||||
const Opcode = Types.GatewayOpcodes;
|
||||
const Intents = Types.Intents;
|
||||
const Intents = @import("intents.zig").Intents;
|
||||
|
||||
const Snowflake = Types.Snowflake;
|
||||
const FetchReq = @import("http.zig").FetchReq;
|
||||
const MakeRequestError = @import("http.zig").MakeRequestError;
|
||||
const FetchReq = @import("../http/http.zig").FetchReq;
|
||||
const MakeRequestError = @import("../http/http.zig").MakeRequestError;
|
||||
const Partial = Types.Partial;
|
||||
const TableTemplate = @import("cache.zig").TableTemplate;
|
||||
const TableTemplate = @import("../cache/cache.zig").TableTemplate;
|
||||
const CacheTables = @import("../cache/cache.zig").CacheTables;
|
||||
const FileData = @import("../http/http.zig").FileData;
|
||||
|
||||
pub fn Shard(comptime Table: TableTemplate) type {
|
||||
return struct {
|
||||
@ -106,7 +108,7 @@ pub fn Shard(comptime Table: TableTemplate) type {
|
||||
log: Log = .no,
|
||||
|
||||
/// actual cache
|
||||
cache_handler: *@import("cache.zig").CacheTables(Table),
|
||||
cache_handler: *CacheTables(Table),
|
||||
|
||||
pub fn resumable(self: *Self) bool {
|
||||
return self.resume_gateway_url != null and
|
||||
@ -160,7 +162,7 @@ pub fn Shard(comptime Table: TableTemplate) type {
|
||||
options: ShardOptions,
|
||||
run: GatewayDispatchEvent,
|
||||
log: Log,
|
||||
cache: *@import("cache.zig").CacheTables(Table),
|
||||
cache: *CacheTables(Table),
|
||||
sharder_pool: ?*std.Thread.Pool = null,
|
||||
}) zlib.Error!Self {
|
||||
return Self{
|
||||
@ -933,7 +935,7 @@ pub fn Shard(comptime Table: TableTemplate) type {
|
||||
/// the create message payload
|
||||
create_message: Partial(Types.CreateMessage),
|
||||
/// the files to send, must be one of FileData.type
|
||||
files: []@import("http.zig").FileData,
|
||||
files: []FileData,
|
||||
};
|
||||
|
||||
/// same as `sendMessage` but acceps a files field
|
||||
@ -1448,7 +1450,7 @@ pub fn Shard(comptime Table: TableTemplate) type {
|
||||
|
||||
pub const StartThreadInForumOrMediaChannelWithFiles = struct {
|
||||
start_thread: Types.StartThreadFromMessage,
|
||||
files: []@import("http.zig").FileData,
|
||||
files: []FileData,
|
||||
};
|
||||
|
||||
/// same as `startThreadInForumOrMediaChannel`
|
||||
@ -2862,7 +2864,7 @@ pub fn Shard(comptime Table: TableTemplate) type {
|
||||
self: *Self,
|
||||
guild_id: Snowflake,
|
||||
sticker: Types.CreateModifyGuildSticker,
|
||||
file: @import("http.zig").FileData,
|
||||
file: FileData,
|
||||
) RequestFailedError!Result(Types.Sticker) {
|
||||
var buf: [256]u8 = undefined;
|
||||
const path = try std.fmt.bufPrint(&buf, "/guilds/{d}/stickers", .{guild_id.into()});
|
@ -14,20 +14,21 @@
|
||||
//! OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
//! PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
const Intents = @import("./structures/types.zig").Intents;
|
||||
const Snowflake = @import("./structures/snowflake.zig").Snowflake;
|
||||
const GatewayBotInfo = @import("internal.zig").GatewayBotInfo;
|
||||
const IdentifyProperties = @import("internal.zig").IdentifyProperties;
|
||||
const internalLogif = @import("internal.zig").logif;
|
||||
const ShardDetails = @import("internal.zig").ShardDetails;
|
||||
const ConnectQueue = @import("internal.zig").ConnectQueue;
|
||||
const GatewayDispatchEvent = @import("internal.zig").GatewayDispatchEvent;
|
||||
const Log = @import("internal.zig").Log;
|
||||
const Intents = @import("intents.zig").Intents;
|
||||
const Snowflake = @import("../structures/snowflake.zig").Snowflake;
|
||||
const GatewayBotInfo = @import("util.zig").GatewayBotInfo;
|
||||
const IdentifyProperties = @import("util.zig").IdentifyProperties;
|
||||
const internalLogif = @import("../utils/core.zig").logif;
|
||||
const ShardDetails = @import("util.zig").ShardDetails;
|
||||
const ConnectQueue = @import("connect_queue.zig").ConnectQueue;
|
||||
const GatewayDispatchEvent = @import("../utils/core.zig").GatewayDispatchEvent;
|
||||
const Log = @import("../utils/core.zig").Log;
|
||||
const Shard = @import("shard.zig").Shard;
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const debug = @import("internal.zig").debug;
|
||||
const TableTemplate = @import("cache.zig").TableTemplate;
|
||||
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 {
|
||||
return struct {
|
||||
@ -50,7 +51,7 @@ pub fn ShardManager(comptime Table: TableTemplate) type {
|
||||
log: Log,
|
||||
|
||||
// must be initialised
|
||||
cache: *@import("cache.zig").CacheTables(Table),
|
||||
cache: *CacheTables(Table),
|
||||
|
||||
pub const ShardData = struct {
|
||||
/// resume seq to resume connections
|
||||
@ -87,11 +88,11 @@ pub fn ShardManager(comptime Table: TableTemplate) type {
|
||||
options: SessionOptions,
|
||||
run: GatewayDispatchEvent,
|
||||
log: Log,
|
||||
cache: @import("cache.zig").TableTemplate,
|
||||
cache: TableTemplate,
|
||||
}) mem.Allocator.Error!Self {
|
||||
const concurrency = settings.options.info.session_start_limit.?.max_concurrency;
|
||||
const cache = try allocator.create(@import("cache.zig").CacheTables(Table));
|
||||
cache.* = @import("cache.zig").CacheTables(Table).defaults(allocator);
|
||||
const cache = try allocator.create(CacheTables(Table));
|
||||
cache.* = CacheTables(Table).defaults(allocator);
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
59
src/shard/util.zig
Normal file
59
src/shard/util.zig
Normal file
@ -0,0 +1,59 @@
|
||||
pub const IdentifyProperties = struct {
|
||||
/// Operating system the shard runs on.
|
||||
os: []const u8,
|
||||
/// The "browser" where this shard is running on.
|
||||
browser: []const u8,
|
||||
/// The device on which the shard is running.
|
||||
device: []const u8,
|
||||
};
|
||||
|
||||
pub const default_identify_properties = IdentifyProperties{
|
||||
.os = @tagName(@import("builtin").os.tag),
|
||||
.browser = "discord.zig",
|
||||
.device = "discord.zig",
|
||||
};
|
||||
|
||||
/// https://discord.com/developers/docs/topics/gateway#get-gateway
|
||||
pub const GatewayInfo = struct {
|
||||
/// The WSS URL that can be used for connecting to the gateway
|
||||
url: []const u8,
|
||||
};
|
||||
|
||||
/// https://discord.com/developers/docs/events/gateway#session-start-limit-object
|
||||
pub const GatewaySessionStartLimit = struct {
|
||||
/// Total number of session starts the current user is allowed
|
||||
total: u32,
|
||||
/// Remaining number of session starts the current user is allowed
|
||||
remaining: u32,
|
||||
/// Number of milliseconds after which the limit resets
|
||||
reset_after: u32,
|
||||
/// Number of identify requests allowed per 5 seconds
|
||||
max_concurrency: u32,
|
||||
};
|
||||
|
||||
/// https://discord.com/developers/docs/topics/gateway#get-gateway-bot
|
||||
pub const GatewayBotInfo = struct {
|
||||
url: []const u8,
|
||||
/// The recommended number of shards to use when connecting
|
||||
///
|
||||
/// See https://discord.com/developers/docs/topics/gateway#sharding
|
||||
shards: u32,
|
||||
/// Information on the current session start limit
|
||||
///
|
||||
/// See https://discord.com/developers/docs/topics/gateway#session-start-limit-object
|
||||
session_start_limit: ?GatewaySessionStartLimit,
|
||||
};
|
||||
|
||||
pub const ShardDetails = struct {
|
||||
/// Bot token which is used to connect to Discord */
|
||||
token: []const u8,
|
||||
/// The URL of the gateway which should be connected to.
|
||||
url: []const u8 = "wss://gateway.discord.gg",
|
||||
/// The gateway version which should be used.
|
||||
version: ?usize = 10,
|
||||
/// The calculated intent value of the events which the shard should receive.
|
||||
intents: @import("./intents.zig").Intents,
|
||||
/// Identify properties to use
|
||||
properties: IdentifyProperties = default_identify_properties,
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ const Partial = @import("partial.zig").Partial;
|
||||
const User = @import("user.zig").User;
|
||||
const Team = @import("team.zig").Team;
|
||||
const Guild = @import("guild.zig").Guild;
|
||||
const AssociativeArray = @import("../json-helper.zig").AssociativeArray;
|
||||
const AssociativeArray = @import("../utils/json.zig").AssociativeArray;
|
||||
|
||||
/// https://discord.com/developers/docs/resources/application#application-object
|
||||
pub const Application = struct {
|
||||
|
@ -32,7 +32,7 @@
|
||||
const InteractionResponseTypes = @import("shared.zig").InteractionResponseTypes;
|
||||
const InteractionContextType = @import("command.zig").InteractionContextType;
|
||||
const Entitlement = @import("monetization.zig").Entitlement;
|
||||
const Record = @import("../json-helper.zig").Record;
|
||||
const Record = @import("../utils/json.zig").Record;
|
||||
|
||||
pub const Interaction = struct {
|
||||
/// Id of the interaction
|
||||
|
@ -15,12 +15,13 @@
|
||||
//! PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = std.builtin;
|
||||
|
||||
pub fn Partial(comptime T: type) type {
|
||||
const info = @typeInfo(T);
|
||||
switch (info) {
|
||||
.@"struct" => |s| {
|
||||
comptime var fields: []const std.builtin.Type.StructField = &[_]std.builtin.Type.StructField{};
|
||||
comptime var fields: []const builtin.Type.StructField = &[_]builtin.Type.StructField{};
|
||||
inline for (s.fields) |field| {
|
||||
if (field.is_comptime) {
|
||||
@compileError("Cannot make Partial of " ++ @typeName(T) ++ ", it has a comptime field " ++ field.name);
|
||||
@ -31,7 +32,7 @@ pub fn Partial(comptime T: type) type {
|
||||
};
|
||||
const default_value: optional_type = null;
|
||||
const aligned_ptr: *align(field.alignment) const anyopaque = @alignCast(@ptrCast(&default_value));
|
||||
const optional_field: [1]std.builtin.Type.StructField = [_]std.builtin.Type.StructField{.{
|
||||
const optional_field: [1]builtin.Type.StructField = [_]builtin.Type.StructField{.{
|
||||
.alignment = field.alignment,
|
||||
.default_value_ptr = aligned_ptr,
|
||||
.is_comptime = false,
|
||||
@ -40,9 +41,9 @@ pub fn Partial(comptime T: type) type {
|
||||
}};
|
||||
fields = fields ++ optional_field;
|
||||
}
|
||||
const partial_type_info: std.builtin.Type = .{ .@"struct" = .{
|
||||
const partial_type_info: builtin.Type = .{ .@"struct" = .{
|
||||
.backing_integer = s.backing_integer,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
.decls = &[_]builtin.Type.Declaration{},
|
||||
.fields = fields,
|
||||
.is_tuple = s.is_tuple,
|
||||
.layout = s.layout,
|
||||
|
@ -1430,181 +1430,6 @@ pub const GuildFeatures = enum {
|
||||
RESUMED,
|
||||
};
|
||||
|
||||
/// https://discord.com/developers/docs/topics/gateway#list-of-intents
|
||||
pub const GatewayIntents = packed struct {
|
||||
pub fn toRaw(self: GatewayIntents) u32 {
|
||||
return @bitCast(self);
|
||||
}
|
||||
|
||||
pub fn fromRaw(raw: u32) GatewayIntents {
|
||||
return @bitCast(raw);
|
||||
}
|
||||
|
||||
pub fn jsonParse(allocator: std.mem.Allocator, src: anytype, _: std.json.ParseOptions) !@This() {
|
||||
const value = try std.json.innerParse(std.json.Value, allocator, src, .{
|
||||
.ignore_unknown_fields = true,
|
||||
.max_value_len = 0x1000,
|
||||
});
|
||||
if (value != .integer) @panic("Invalid value for bitfield");
|
||||
|
||||
return fromRaw(@intCast(value.integer));
|
||||
}
|
||||
|
||||
pub fn jsonParseFromValue(_: std.mem.Allocator, src: std.json.Value, _: std.json.ParseOptions) @This() {
|
||||
if (src != .integer) @panic("Invalid value for bitfield");
|
||||
return fromRaw(@intCast(src.integer));
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// - GUILD_CREATE
|
||||
/// - GUILD_UPDATE
|
||||
/// - GUILD_DELETE
|
||||
/// - GUILD_ROLE_CREATE
|
||||
/// - GUILD_ROLE_UPDATE
|
||||
/// - GUILD_ROLE_DELETE
|
||||
/// - CHANNEL_CREATE
|
||||
/// - CHANNEL_UPDATE
|
||||
/// - CHANNEL_DELETE
|
||||
/// - CHANNEL_PINS_UPDATE
|
||||
/// - THREAD_CREATE
|
||||
/// - THREAD_UPDATE
|
||||
/// - THREAD_DELETE
|
||||
/// - THREAD_LIST_SYNC
|
||||
/// - THREAD_MEMBER_UPDATE
|
||||
/// - THREAD_MEMBERS_UPDATE
|
||||
/// - STAGE_INSTANCE_CREATE
|
||||
/// - STAGE_INSTANCE_UPDATE
|
||||
/// - STAGE_INSTANCE_DELETE
|
||||
////
|
||||
Guilds: bool = false,
|
||||
///
|
||||
/// - GUILD_MEMBER_ADD
|
||||
/// - GUILD_MEMBER_UPDATE
|
||||
/// - GUILD_MEMBER_REMOVE
|
||||
/// - THREAD_MEMBERS_UPDATE
|
||||
///
|
||||
/// This is a privileged intent.
|
||||
////
|
||||
GuildMembers: bool = false,
|
||||
///
|
||||
/// - GUILD_AUDIT_LOG_ENTRY_CREATE
|
||||
/// - GUILD_BAN_ADD
|
||||
/// - GUILD_BAN_REMOVE
|
||||
////
|
||||
GuildModeration: bool = false,
|
||||
///
|
||||
/// - GUILD_EMOJIS_UPDATE
|
||||
/// - GUILD_STICKERS_UPDATE
|
||||
////
|
||||
GuildEmojisAndStickers: bool = false,
|
||||
///
|
||||
/// - GUILD_INTEGRATIONS_UPDATE
|
||||
/// - INTEGRATION_CREATE
|
||||
/// - INTEGRATION_UPDATE
|
||||
/// - INTEGRATION_DELETE
|
||||
////
|
||||
GuildIntegrations: bool = false,
|
||||
///
|
||||
/// - WEBHOOKS_UPDATE
|
||||
////
|
||||
GuildWebhooks: bool = false,
|
||||
///
|
||||
/// - INVITE_CREATE
|
||||
/// - INVITE_DELETE
|
||||
////
|
||||
GuildInvites: bool = false,
|
||||
///
|
||||
/// - VOICE_STATE_UPDATE
|
||||
/// - VOICE_CHANNEL_EFFECT_SEND
|
||||
////
|
||||
GuildVoiceStates: bool = false,
|
||||
///
|
||||
/// - PRESENCE_UPDATE
|
||||
///
|
||||
/// This is a privileged intent.
|
||||
////
|
||||
GuildPresences: bool = false,
|
||||
///
|
||||
/// - MESSAGE_CREATE
|
||||
/// - MESSAGE_UPDATE
|
||||
/// - MESSAGE_DELETE
|
||||
/// - MESSAGE_DELETE_BULK
|
||||
///
|
||||
/// The messages do not contain content by default.
|
||||
/// If you want to receive their content too, you need to turn on the privileged `MESSAGE_CONTENT` intent. */
|
||||
GuildMessages: bool = false,
|
||||
///
|
||||
/// - MESSAGE_REACTION_ADD
|
||||
/// - MESSAGE_REACTION_REMOVE
|
||||
/// - MESSAGE_REACTION_REMOVE_ALL
|
||||
/// - MESSAGE_REACTION_REMOVE_EMOJI
|
||||
////
|
||||
GuildMessageReactions: bool = false,
|
||||
///
|
||||
/// - TYPING_START
|
||||
////
|
||||
GuildMessageTyping: bool = false,
|
||||
///
|
||||
/// - CHANNEL_CREATE
|
||||
/// - MESSAGE_CREATE
|
||||
/// - MESSAGE_UPDATE
|
||||
/// - MESSAGE_DELETE
|
||||
/// - CHANNEL_PINS_UPDATE
|
||||
////
|
||||
DirectMessages: bool = false,
|
||||
///
|
||||
/// - MESSAGE_REACTION_ADD
|
||||
/// - MESSAGE_REACTION_REMOVE
|
||||
/// - MESSAGE_REACTION_REMOVE_ALL
|
||||
/// - MESSAGE_REACTION_REMOVE_EMOJI
|
||||
////
|
||||
DirectMessageReactions: bool = false,
|
||||
///
|
||||
/// - TYPING_START
|
||||
////
|
||||
DirectMessageTyping: bool = false,
|
||||
///
|
||||
/// This intent will add all content related values to message events.
|
||||
///
|
||||
/// This is a privileged intent.
|
||||
////
|
||||
MessageContent: bool = false,
|
||||
///
|
||||
/// - GUILD_SCHEDULED_EVENT_CREATE
|
||||
/// - GUILD_SCHEDULED_EVENT_UPDATE
|
||||
/// - GUILD_SCHEDULED_EVENT_DELETE
|
||||
/// - GUILD_SCHEDULED_EVENT_USER_ADD this is experimental and unstable.
|
||||
/// - GUILD_SCHEDULED_EVENT_USER_REMOVE this is experimental and unstable.
|
||||
////
|
||||
GuildScheduledEvents: bool = false,
|
||||
_pad: u4 = 0,
|
||||
///
|
||||
/// - AUTO_MODERATION_RULE_CREATE
|
||||
/// - AUTO_MODERATION_RULE_UPDATE
|
||||
/// - AUTO_MODERATION_RULE_DELETE
|
||||
////
|
||||
AutoModerationConfiguration: bool = false,
|
||||
///
|
||||
/// - AUTO_MODERATION_ACTION_EXECUTION
|
||||
////
|
||||
AutoModerationExecution: bool = false,
|
||||
_pad2: u3 = 0,
|
||||
///
|
||||
/// - MESSAGE_POLL_VOTE_ADD
|
||||
/// - MESSAGE_POLL_VOTE_REMOVE
|
||||
////
|
||||
GuildMessagePolls: bool = false,
|
||||
///
|
||||
/// - MESSAGE_POLL_VOTE_ADD
|
||||
/// - MESSAGE_POLL_VOTE_REMOVE
|
||||
////
|
||||
DirectMessagePolls: bool = false,
|
||||
_pad3: u4 = 0,
|
||||
};
|
||||
|
||||
/// https://discord.com/developers/docs/topics/gateway#list-of-intents
|
||||
pub const Intents = GatewayIntents;
|
||||
|
||||
/// https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactionresponsetype
|
||||
pub const InteractionResponseTypes = enum(u4) {
|
||||
|
@ -66,8 +66,6 @@
|
||||
pub const GatewayCloseEventCodes = @import("shared.zig").GatewayCloseEventCodes;
|
||||
pub const GatewayOpcodes = @import("shared.zig").GatewayOpcodes;
|
||||
pub const GatewayDispatchEventNames = @import("shared.zig").GatewayDispatchEventNames;
|
||||
pub const GatewayIntents = @import("shared.zig").GatewayIntents;
|
||||
pub const Intents = @import("shared.zig").Intents;
|
||||
pub const InteractionResponseTypes = @import("shared.zig").InteractionResponseTypes;
|
||||
pub const SortOrderTypes = @import("shared.zig").SortOrderTypes;
|
||||
pub const ForumLayout = @import("shared.zig").ForumLayout;
|
||||
|
@ -20,7 +20,7 @@
|
||||
const OAuth2Scope = @import("shared.zig").OAuth2Scope;
|
||||
const Integration = @import("integration.zig").Integration;
|
||||
const Partial = @import("partial.zig").Partial;
|
||||
const Record = @import("../json-helper.zig").Record;
|
||||
const Record = @import("../utils/json.zig").Record;
|
||||
|
||||
/// https://discord.com/developers/docs/resources/user#user-object
|
||||
pub const User = struct {
|
||||
|
@ -15,74 +15,7 @@
|
||||
//! PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const builtin = @import("builtin");
|
||||
const Types = @import("./structures/types.zig");
|
||||
|
||||
pub const IdentifyProperties = struct {
|
||||
/// Operating system the shard runs on.
|
||||
os: []const u8,
|
||||
/// The "browser" where this shard is running on.
|
||||
browser: []const u8,
|
||||
/// The device on which the shard is running.
|
||||
device: []const u8,
|
||||
|
||||
system_locale: ?[]const u8 = null, // TODO parse this
|
||||
browser_user_agent: ?[]const u8 = null,
|
||||
browser_version: ?[]const u8 = null,
|
||||
os_version: ?[]const u8 = null,
|
||||
referrer: ?[]const u8 = null,
|
||||
referring_domain: ?[]const u8 = null,
|
||||
referrer_current: ?[]const u8 = null,
|
||||
referring_domain_current: ?[]const u8 = null,
|
||||
release_channel: ?[]const u8 = null,
|
||||
client_build_number: ?u64 = null,
|
||||
client_event_source: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
/// https://discord.com/developers/docs/topics/gateway#get-gateway
|
||||
pub const GatewayInfo = struct {
|
||||
/// The WSS URL that can be used for connecting to the gateway
|
||||
url: []const u8,
|
||||
};
|
||||
|
||||
/// https://discord.com/developers/docs/events/gateway#session-start-limit-object
|
||||
pub const GatewaySessionStartLimit = struct {
|
||||
/// Total number of session starts the current user is allowed
|
||||
total: u32,
|
||||
/// Remaining number of session starts the current user is allowed
|
||||
remaining: u32,
|
||||
/// Number of milliseconds after which the limit resets
|
||||
reset_after: u32,
|
||||
/// Number of identify requests allowed per 5 seconds
|
||||
max_concurrency: u32,
|
||||
};
|
||||
|
||||
/// https://discord.com/developers/docs/topics/gateway#get-gateway-bot
|
||||
pub const GatewayBotInfo = struct {
|
||||
url: []const u8,
|
||||
/// The recommended number of shards to use when connecting
|
||||
///
|
||||
/// See https://discord.com/developers/docs/topics/gateway#sharding
|
||||
shards: u32,
|
||||
/// Information on the current session start limit
|
||||
///
|
||||
/// See https://discord.com/developers/docs/topics/gateway#session-start-limit-object
|
||||
session_start_limit: ?GatewaySessionStartLimit,
|
||||
};
|
||||
|
||||
pub const ShardDetails = struct {
|
||||
/// Bot token which is used to connect to Discord */
|
||||
token: []const u8,
|
||||
/// The URL of the gateway which should be connected to.
|
||||
url: []const u8 = "wss://gateway.discord.gg",
|
||||
/// The gateway version which should be used.
|
||||
version: ?usize = 10,
|
||||
/// The calculated intent value of the events which the shard should receive.
|
||||
intents: Types.Intents,
|
||||
/// Identify properties to use
|
||||
properties: IdentifyProperties = default_identify_properties,
|
||||
};
|
||||
const Types = @import("../structures/types.zig");
|
||||
|
||||
pub const debug = std.log.scoped(.@"discord.zig");
|
||||
|
||||
@ -95,194 +28,6 @@ pub inline fn logif(log: Log, comptime format: []const u8, args: anytype) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub const default_identify_properties = IdentifyProperties{
|
||||
.os = @tagName(builtin.os.tag),
|
||||
.browser = "discord.zig",
|
||||
.device = "discord.zig",
|
||||
};
|
||||
|
||||
/// inspired from:
|
||||
/// https://github.com/tiramisulabs/seyfert/blob/main/src/websocket/structures/timeout.ts
|
||||
pub fn ConnectQueue(comptime T: type) type {
|
||||
return struct {
|
||||
pub const RequestWithShard = struct {
|
||||
callback: *const fn (self: *RequestWithShard) anyerror!void,
|
||||
shard: T,
|
||||
};
|
||||
|
||||
// ignore this function
|
||||
// so it becomes a regular dequeue
|
||||
fn eq(_: void, _: RequestWithShard, _: RequestWithShard) std.math.Order {
|
||||
return std.math.Order.eq;
|
||||
}
|
||||
|
||||
dequeue: std.PriorityDequeue(RequestWithShard, void, eq),
|
||||
allocator: mem.Allocator,
|
||||
remaining: usize,
|
||||
interval_time: u64 = 5000,
|
||||
running: bool = false,
|
||||
concurrency: usize = 1,
|
||||
|
||||
pub fn init(allocator: mem.Allocator, concurrency: usize, interval_time: u64) !ConnectQueue(T) {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.dequeue = std.PriorityDequeue(RequestWithShard, void, eq).init(allocator, {}),
|
||||
.remaining = concurrency,
|
||||
.interval_time = interval_time,
|
||||
.concurrency = concurrency,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ConnectQueue(T)) void {
|
||||
self.dequeue.deinit();
|
||||
}
|
||||
|
||||
pub fn push(self: *ConnectQueue(T), req: RequestWithShard) !void {
|
||||
if (self.remaining == 0) {
|
||||
return self.dequeue.add(req);
|
||||
}
|
||||
self.remaining -= 1;
|
||||
|
||||
if (!self.running) {
|
||||
try self.startInterval();
|
||||
self.running = true;
|
||||
}
|
||||
|
||||
if (self.dequeue.count() < self.concurrency) {
|
||||
// perhaps store this?
|
||||
const ptr = try self.allocator.create(RequestWithShard);
|
||||
ptr.* = req;
|
||||
try req.callback(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
return self.dequeue.add(req);
|
||||
}
|
||||
|
||||
fn startInterval(self: *ConnectQueue(T)) !void {
|
||||
while (self.running) {
|
||||
std.Thread.sleep(std.time.ns_per_ms * (self.interval_time / self.concurrency));
|
||||
const req: ?RequestWithShard = self.dequeue.removeMin(); // pop front
|
||||
|
||||
while (self.dequeue.count() == 0 and req == null) {}
|
||||
|
||||
if (req) |r| {
|
||||
const ptr = try self.allocator.create(RequestWithShard);
|
||||
ptr.* = r;
|
||||
try @call(.auto, r.callback, .{ptr});
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.remaining < self.concurrency) {
|
||||
self.remaining += 1;
|
||||
}
|
||||
|
||||
if (self.dequeue.count() == 0) {
|
||||
self.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Bucket = struct {
|
||||
/// The queue of requests to acquire an available request. Mapped by (shardId, RequestWithPrio)
|
||||
queue: std.PriorityQueue(RequestWithPrio, void, Bucket.lessthan),
|
||||
|
||||
limit: usize,
|
||||
refill_interval: u64,
|
||||
refill_amount: usize,
|
||||
|
||||
/// The amount of requests that have been used up already.
|
||||
used: usize = 0,
|
||||
|
||||
/// Whether or not the queue is already processing.
|
||||
processing: bool = false,
|
||||
|
||||
/// Whether the timeout should be killed because there is already one running
|
||||
should_stop: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
|
||||
|
||||
/// The timestamp in milliseconds when the next refill is scheduled.
|
||||
refills_at: ?u64 = null,
|
||||
|
||||
pub const RequestWithPrio = struct {
|
||||
callback: *const fn () void,
|
||||
priority: u32 = 1,
|
||||
};
|
||||
|
||||
fn lessthan(_: void, a: RequestWithPrio, b: RequestWithPrio) std.math.Order {
|
||||
return std.math.order(a.priority, b.priority);
|
||||
}
|
||||
|
||||
pub fn init(allocator: mem.Allocator, limit: usize, refill_interval: u64, refill_amount: usize) Bucket {
|
||||
return .{
|
||||
.queue = std.PriorityQueue(RequestWithPrio, void, lessthan).init(allocator, {}),
|
||||
.limit = limit,
|
||||
.refill_interval = refill_interval,
|
||||
.refill_amount = refill_amount,
|
||||
};
|
||||
}
|
||||
|
||||
fn remaining(self: *Bucket) usize {
|
||||
if (self.limit < self.used) {
|
||||
return 0;
|
||||
} else {
|
||||
return self.limit - self.used;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refill(self: *Bucket) std.Thread.SpawnError!void {
|
||||
// Lower the used amount by the refill amount
|
||||
self.used = if (self.refill_amount > self.used) 0 else self.used - self.refill_amount;
|
||||
|
||||
// Reset the refills_at timestamp since it just got refilled
|
||||
self.refills_at = null;
|
||||
|
||||
if (self.used > 0) {
|
||||
if (self.should_stop.load(.monotonic) == true) {
|
||||
self.should_stop.store(false, .monotonic);
|
||||
}
|
||||
const thread = try std.Thread.spawn(.{}, Bucket.timeout, .{self});
|
||||
thread.detach;
|
||||
self.refills_at = std.time.milliTimestamp() + self.refill_interval;
|
||||
}
|
||||
}
|
||||
|
||||
fn timeout(self: *Bucket) void {
|
||||
while (!self.should_stop.load(.monotonic)) {
|
||||
self.refill();
|
||||
std.time.sleep(std.time.ns_per_ms * self.refill_interval);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn processQueue(self: *Bucket) std.Thread.SpawnError!void {
|
||||
if (self.processing) return;
|
||||
|
||||
while (self.queue.remove()) |first_element| {
|
||||
if (self.remaining() != 0) {
|
||||
first_element.callback();
|
||||
self.used += 1;
|
||||
|
||||
if (!self.should_stop.load(.monotonic)) {
|
||||
const thread = try std.Thread.spawn(.{}, Bucket.timeout, .{self});
|
||||
thread.detach;
|
||||
self.refills_at = std.time.milliTimestamp() + self.refill_interval;
|
||||
}
|
||||
} else if (self.refills_at) |ra| {
|
||||
const now = std.time.milliTimestamp();
|
||||
if (ra > now) std.time.sleep(std.time.ns_per_ms * (ra - now));
|
||||
}
|
||||
}
|
||||
|
||||
self.processing = false;
|
||||
}
|
||||
|
||||
pub fn acquire(self: *Bucket, rq: RequestWithPrio) !void {
|
||||
try self.queue.add(rq);
|
||||
try self.processQueue();
|
||||
}
|
||||
};
|
||||
|
||||
pub const GatewayDispatchEvent = struct {
|
||||
application_command_permissions_update: ?*const fn (save: *anyopaque, application_command_permissions: Types.ApplicationCommandPermissions) anyerror!void = undefined,
|
||||
auto_moderation_rule_create: ?*const fn (save: *anyopaque, rule: Types.AutoModerationRule) anyerror!void = undefined,
|
Loading…
x
Reference in New Issue
Block a user