bunch of changes

This commit is contained in:
Yuzu 2024-12-09 15:59:58 -05:00
parent 0412d6b404
commit 2ecad41c52
8 changed files with 241 additions and 20 deletions

View File

@ -5,7 +5,6 @@ A high-performance bleeding edge Discord library in Zig, featuring full API cove
* 100% API Coverage & Fully Typed: Offers complete access to Discord's API with strict typing for reliable and safe code. * 100% API Coverage & Fully Typed: Offers complete access to Discord's API with strict typing for reliable and safe code.
* High Performance: Faster than whichever library you can name (WIP) * High Performance: Faster than whichever library you can name (WIP)
* Flexible Payload Parsing: Supports payload parsing through both zlib and zstd*. * Flexible Payload Parsing: Supports payload parsing through both zlib and zstd*.
* Language Agnostic: Primarily in Zig, but also compatible with JavaScript. (PERHAPS?)
```zig ```zig
const Client = @import("discord.zig").Client; const Client = @import("discord.zig").Client;
@ -75,23 +74,13 @@ Contributions are welcome! Please open an issue or pull request if you'd like to
## http methods missing ## http methods missing
| Endpoint | Support | | Endpoint | Support |
|----------------------------------------|---------| |----------------------------------------|---------|
| Application related | ❌ |
| Audit log | ❌ | | Audit log | ❌ |
| Automod | ❌ | | Automod | ❌ |
| Channel related | ✅ |
| Emoji related | ✅ |
| Entitlement related | ❌ |
| Guild related | ✅ |
| Guild Scheduled Event related | ❌ | | Guild Scheduled Event related | ❌ |
| Guild template related | ❌ | | Guild template related | ❌ |
| Invite related | ✅ |
| Message related | ✅ |
| Poll related | ✅ |
| SKU related | ❌ |
| Soundboard related | ❌ | | Soundboard related | ❌ |
| Stage Instance related | ❌ | | Stage Instance related | ❌ |
| Sticker related | ❌ | | Sticker related | ❌ |
| Subscription related | ❌ | | Subscription related | ❌ |
| User related | ✅ |
| Voice related | ❌ | | Voice related | ❌ |
| Webhook related | ❌ | | Webhook related | ❌ |

View File

@ -65,8 +65,11 @@ pub const FetchReq = struct {
try self.extra_headers.append(http.Header{ .name = name, .value = value }); try self.extra_headers.append(http.Header{ .name = name, .value = value });
} }
pub fn addQueryParam(self: *FetchReq, name: []const u8, value: []const u8) !void { pub fn addQueryParam(self: *FetchReq, name: []const u8, value: anytype) !void {
try self.query_params.put(name, value); if (value == null)
return;
var buf: [256]u8 = undefined;
try self.query_params.put(name, try std.fmt.bufPrint(&buf, "{any}", .{value}));
} }
fn formatQueryParams(self: *FetchReq) ![]const u8 { fn formatQueryParams(self: *FetchReq) ![]const u8 {
@ -171,6 +174,15 @@ pub const FetchReq = struct {
return error.FailedRequest; return error.FailedRequest;
} }
pub fn put4(self: *FetchReq, comptime T: type, path: []const u8) !zjson.Owned(T) {
const result = try self.makeRequest(.PUT, path, null);
if (result.status != .ok)
return error.FailedRequest;
return try zjson.parse(T, self.allocator, try self.body.toOwnedSlice());
}
pub fn post(self: *FetchReq, comptime T: type, path: []const u8, object: anytype) !zjson.Owned(T) { pub fn post(self: *FetchReq, comptime T: type, path: []const u8, object: anytype) !zjson.Owned(T) {
var buf: [4096]u8 = undefined; var buf: [4096]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf); var fba = std.heap.FixedBufferAllocator.init(&buf);

View File

@ -796,14 +796,18 @@ pub const RequestFailedError = zjson.ParserError || MakeRequestError || error{Fa
/// If operating on a guild channel, this endpoint requires the current user to have the `VIEW_CHANNEL` permission. /// If operating on a guild channel, this endpoint requires the current user to have the `VIEW_CHANNEL` permission.
/// If the channel is a voice channel, they must also have the `CONNECT` permission. /// If the channel is a voice channel, they must also have the `CONNECT` permission.
/// If the current user is missing the `READ_MESSAGE_HISTORY` permission in the channel, then no messages will be returned. /// If the current user is missing the `READ_MESSAGE_HISTORY` permission in the channel, then no messages will be returned.
/// TODO: add query params pub fn fetchMessages(self: *Self, channel_id: Snowflake, query: Types.GetMessagesQuery) RequestFailedError!zjson.Owned([]Types.Message) {
pub fn fetchMessages(self: *Self, channel_id: Snowflake) RequestFailedError!zjson.Owned([]Types.Message) {
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/channels/{d}/messages", .{channel_id.into()}); const path = try std.fmt.bufPrint(&buf, "/channels/{d}/messages", .{channel_id.into()});
var req = FetchReq.init(self.allocator, self.details.token); var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit(); defer req.deinit();
try req.addQueryParam("limit", query.limit);
try req.addQueryParam("around", query.around);
try req.addQueryParam("before", query.before);
try req.addQueryParam("after", query.after);
const messages = try req.get([]Types.Message, path); const messages = try req.get([]Types.Message, path);
return messages; return messages;
} }
@ -2423,7 +2427,8 @@ pub fn fetchAnswerVoters(
/// Immediately ends the poll. /// Immediately ends the poll.
/// You cannot end polls from other users. /// You cannot end polls from other users.
/// ///
/// Returns a message object. Fires a Message Update Gateway event. /// Returns a message object.
/// Fires a Message Update Gateway event.
pub fn endPoll( pub fn endPoll(
self: *Self, self: *Self,
channel_id: Snowflake, channel_id: Snowflake,
@ -2438,3 +2443,141 @@ pub fn endPoll(
const msg = try req.post(Types.Message, path); const msg = try req.post(Types.Message, path);
return msg; return msg;
} }
/// Returns the application object associated with the requesting bot user.
pub fn fetchMyApplication(self: *Self) RequestFailedError!zjson.Owned(Types.Application) {
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
const app = try req.get(Types.Application, "/applications/@me");
return app;
}
/// Edit properties of the app associated with the requesting bot user.
/// Only properties that are passed will be updated.
/// Returns the updated application object on success.
pub fn editMyApplication(self: *Self, params: Types.ModifyApplication) RequestFailedError!zjson.Owned(Types.Application) {
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
const app = try req.patch(Types.Application, "/applications/@me", params);
return app;
}
/// Returns a serialized activity instance, if it exists.
/// Useful for preventing unwanted activity sessions.
pub fn fetchActivityInstance(self: *Self, application_id: Snowflake, insance: []const u8) RequestFailedError!zjson.Owned(Types.ActivityInstance) {
var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/applications/{d}/activity-instances/{s}", .{ application_id.into(), insance });
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
const activity_instance = try req.get(Types.ActivityInstance, path);
return activity_instance;
}
/// Returns a list of application role connection metadata objects for the given application.
pub fn fetchApplicationRoleConnectionMetadataRecords(self: *Self, application_id: Snowflake) RequestFailedError!zjson.Owned([]Types.ApplicationRoleConnection) {
var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/applications/{d}/role-connection/metadata", .{application_id.into()});
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
return req.get([]Types.ApplicationRoleConnection, path);
}
/// Updates and returns a list of application role connection metadata objects for the given application.
pub fn updateApplicationRoleConnectionMetadataRecords(self: *Self, application_id: Snowflake) RequestFailedError!zjson.Owned([]Types.ApplicationRoleConnection) {
var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/applications/{d}/role-connection/metadata", .{application_id.into()});
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
return req.put4([]Types.ApplicationRoleConnection, path);
}
/// Returns all entitlements for a given app, active and expired.
pub fn fetchEntitlements(self: *Self, application_id: Snowflake) RequestFailedError!zjson.Owned([]Types.Entitlement) {
var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/applications/{d}/entitlements", .{application_id.into()});
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
const entitlements = try req.get([]Types.Entitlement, path);
return entitlements;
}
/// Returns an entitlement.
pub fn fetchEntitlement(self: *Self, application_id: Snowflake, entitlement_id: Snowflake) RequestFailedError!zjson.Owned(Types.Entitlement) {
var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/applications/{d}/entitlements/{d}", .{ application_id.into(), entitlement_id.into() });
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
const entitlement = try req.get(Types.Entitlement, path);
return entitlement;
}
/// For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed.
/// The entitlement will have consumed: true when using List Entitlements.
///
/// Returns a 204 No Content on success.
pub fn consumeEntitlement(self: *Self, application_id: Snowflake, entitlement_id: Snowflake) RequestFailedError!void {
var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/applications/{d}/entitlements/{d}/consume", .{ application_id.into(), entitlement_id.into() });
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
try req.post5(path);
}
/// Creates a test entitlement to a given SKU for a given guild or user.
/// Discord will act as though that user or guild has entitlement to your premium offering.
///
/// This endpoint returns a partial entitlement object. It will not contain `subscription_id`, `starts_at`, or `ends_at`, as it's valid in perpetuity.
///
/// After creating a test entitlement, you'll need to reload your Discord client. After doing so, you'll see that your server or user now has premium access.
pub fn createTestEntitlement(
self: *Self,
application_id: Snowflake,
params: Types.CreateTestEntitlement,
) RequestFailedError!zjson.Owned(Partial(Types.Entitlement)) {
var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/applications/{d}/entitlements", .{application_id.into()});
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
return req.post(Partial(Types.Entitlement), path, params);
}
/// Deletes a currently-active test entitlement. Discord will act as though that user or guild no longer has entitlement to your premium offering.
///
/// Returns 204 No Content on success.
pub fn deleteTestEntitlement(self: *Self, application_id: Snowflake) RequestFailedError!void {
var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/applications/{d}/entitlements", .{application_id.into()});
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
try req.delete(path);
}
/// Returns all SKUs for a given application.
pub fn fetchSkus(self: *Self, application_id: Snowflake) RequestFailedError!zjson.Owner([]Types.Sku) {
var buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "/applications/{d}/skus", .{application_id.into()});
var req = FetchReq.init(self.allocator, self.details.token);
defer req.deinit();
const skus = try req.get([]Types.Sku, path);
return skus;
}

View File

@ -107,3 +107,57 @@ pub const InstallParams = struct {
/// Permissions to request for the bot role /// Permissions to request for the bot role
permissions: []const u8, permissions: []const u8,
}; };
pub const ModifyApplication = struct {
/// Default custom authorization URL for the app, if enabled
custom_install_url: ?[]const u8,
/// Description of the app
description: ?[]const u8,
/// Role connection verification URL for the app
role_connections_verification_url: ?[]const u8,
/// Settings for the app's default in-app authorization link, if enabled
install_params: ?InstallParams,
/// Default scopes and permissions for each supported installation context.
integration_types_config: ?ApplicationIntegrationType,
/// App's public flags
/// @remarks
/// Only limited intent flags (`GATEWAY_PRESENCE_LIMITED`, `GATEWAY_GUILD_MEMBERS_LIMITED`, and `GATEWAY_MESSAGE_CONTENT_LIMITED`) can be updated via the API.
flags: ?ApplicationFlags,
/// Icon for the app
icon: ?[]const u8,
/// Default rich presence invite cover image for the app
cover_image: ?[]const u8,
/// Interactions endpoint URL for the app
/// @remarks
/// To update an Interactions endpoint URL via the API, the URL must be valid
interaction_endpoint_url: ?[]const u8,
/// List of tags describing the content and functionality of the app (max of 20 characters per tag)
/// @remarks
/// There can only be a max of 5 tags
tags: ?[][]const u8,
/// Event webhook URL for the app to receive webhook events
event_webhooks_url: ?[]const u8,
/// If webhook events are enabled for the app. 1 to disable, and 2 to enable.
event_webhooks_status: ?ApplicationEventWebhookStatus,
/// List of Webhook event types the app subscribes to
event_webhooks_types: ?[]WebhookEventType,
};
pub const ApplicationEventWebhookStatus = enum(u8) {
/// Webhook events are disabled by developer
Disabled = 1,
/// Webhook events are enabled by developer */
Enabled = 2,
/// Webhook events are disabled by Discord, usually due to inactivity */
DisabledByDiscord = 3,
};
/// https://discord.com/developers/docs/events/webhook-events#event-types
pub const WebhookEventType = union(enum) {
/// Sent when an app was authorized by a user to a server or their account
APPLICATION_AUTHORIZED,
/// Entitlement was created
ENTITLEMENT_CREATE,
/// User was added to a Quest (currently unavailable)
QUEST_USER_ENROLLMENT,
};

View File

@ -126,7 +126,7 @@ pub const ActivityLocation = struct {
}; };
/// https://discord.com/developers/docs/resources/application#get-application-activity-instance-activity-location-kind-enum /// https://discord.com/developers/docs/resources/application#get-application-activity-instance-activity-location-kind-enum
pub const ActivityLocationKind = enum { pub const ActivityLocationKind = union(enum) {
/// The Location is a Guild Channel /// The Location is a Guild Channel
gc, gc,
/// The Location is a Private Channel, such as a DM or GDM /// The Location is a Private Channel, such as a DM or GDM

View File

@ -370,3 +370,14 @@ pub const AllowedMentions = struct {
/// For replies, whether to mention the author of the message being replied to (default false) /// For replies, whether to mention the author of the message being replied to (default false)
replied_user: ?bool, replied_user: ?bool,
}; };
pub const GetMessagesQuery = struct {
/// Get messages around this message ID,
around: ?Snowflake,
/// Get messages before this message ID
before: ?Snowflake,
/// Get messages after this message ID
after: ?Snowflake,
/// Max number of messages to return (1-100),
limit: ?usize = 50,
};

View File

@ -88,3 +88,15 @@ pub const SkuType = enum(u4) {
/// System-generated group for each SUBSCRIPTION SKU created /// System-generated group for each SUBSCRIPTION SKU created
SubscriptionGroup = 6, SubscriptionGroup = 6,
}; };
pub const CreateTestEntitlement = struct {
/// ID of the SKU to grant the entitlement to
sku_id: []const u8,
/// ID of the guild or user to grant the entitlement top
owner_id: []const u8,
/// 1 for a guild subscription, 2 for a user subscription
owner_type: enum(u8) {
guild_subscription = 1,
user_subscription = 2,
},
};

View File

@ -1101,12 +1101,12 @@ pub fn parseInto(comptime T: type, allocator: mem.Allocator, value: JsonType) Er
} }
/// meant to handle a `JsonType` value and handling the deinitialization thereof /// meant to handle a `JsonType` value and handling the deinitialization thereof
pub fn Owned(comptime Struct: type) type { pub fn Owned(comptime T: type) type {
if (@typeInfo(Struct) != .@"struct") @compileError("expected a `struct` type"); // if (@typeInfo(Struct) != .@"struct") @compileError("expected a `struct` type");
return struct { return struct {
arena: *std.heap.ArenaAllocator, arena: *std.heap.ArenaAllocator,
value: Struct, value: T,
pub fn deinit(self: @This()) void { pub fn deinit(self: @This()) void {
const allocator = self.arena.child_allocator; const allocator = self.arena.child_allocator;