feat: implement audit log, core fetch functionality, guild management, message handling, and poll features
This commit is contained in:
parent
5b4832a2cb
commit
6272bb62ff
53
src/discord/audit-log.ts
Normal file
53
src/discord/audit-log.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { fetchDiscord } from "./core";
|
||||
|
||||
export interface AuditLog {
|
||||
application_commands: any[];
|
||||
audit_log_entries: AuditLogEntry[];
|
||||
auto_moderation_rules: any[];
|
||||
guild_scheduled_events: any[];
|
||||
integrations: any[];
|
||||
threads: any[];
|
||||
users: any[];
|
||||
webhooks: any[];
|
||||
}
|
||||
|
||||
export interface AuditLogEntry {
|
||||
target_id: string | null;
|
||||
changes?: AuditLogChange[];
|
||||
user_id: string | null;
|
||||
id: string;
|
||||
action_type: number;
|
||||
options?: any;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface AuditLogChange {
|
||||
new_value?: any;
|
||||
old_value?: any;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export const getAuditLog = async (options: {
|
||||
guildId: string;
|
||||
userId?: string;
|
||||
actionType?: number;
|
||||
before?: string;
|
||||
after?: string;
|
||||
limit?: number;
|
||||
}): Promise<AuditLog> => {
|
||||
const { guildId, userId, actionType, before, after, limit } = options;
|
||||
const query = new URLSearchParams();
|
||||
if (userId) query.append("user_id", userId);
|
||||
if (actionType) query.append("action_type", String(actionType));
|
||||
if (before) query.append("before", before);
|
||||
if (after) query.append("after", after);
|
||||
if (limit) query.append("limit", String(limit));
|
||||
|
||||
const queryString = query.toString();
|
||||
const path = `/guilds/${guildId}/audit-logs${
|
||||
queryString ? `?${queryString}` : ""
|
||||
}`;
|
||||
|
||||
const response = await fetchDiscord(path);
|
||||
return response.json();
|
||||
};
|
||||
21
src/discord/core.ts
Normal file
21
src/discord/core.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Config } from "../config";
|
||||
|
||||
export const fetchDiscord = async (
|
||||
path: string,
|
||||
init?: RequestInit,
|
||||
): Promise<Response> => {
|
||||
const response = await fetch(`${Config.DISCORD_API}${path}`, {
|
||||
...init,
|
||||
headers: {
|
||||
Authorization: `Bot ${Config.BOT_TOKEN}`,
|
||||
"Content-Type": "application/json",
|
||||
...(init?.headers || {}),
|
||||
},
|
||||
});
|
||||
if (response.status === 429) {
|
||||
const data = await response.json().catch(() => ({}) as any);
|
||||
const retry = (data?.retry_after ? Number(data.retry_after) : 1) * 1000;
|
||||
await new Promise((r) => setTimeout(r, retry));
|
||||
}
|
||||
return response;
|
||||
};
|
||||
252
src/discord/guild.ts
Normal file
252
src/discord/guild.ts
Normal file
@ -0,0 +1,252 @@
|
||||
import { fetchDiscord } from "./core";
|
||||
|
||||
/**
|
||||
* Represents a Discord guild.
|
||||
* This is a partial interface based on the Discord API documentation.
|
||||
*/
|
||||
export interface Guild {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string | null;
|
||||
owner_id: string;
|
||||
verification_level: number;
|
||||
roles: any[]; // Array of role objects
|
||||
emojis: any[]; // Array of emoji objects
|
||||
features: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a Guild Member.
|
||||
* This is a partial interface.
|
||||
*/
|
||||
export interface GuildMember {
|
||||
user?: any; // User object
|
||||
nick?: string | null;
|
||||
roles: string[];
|
||||
joined_at: string; // ISO8601 timestamp
|
||||
deaf: boolean;
|
||||
mute: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a Role.
|
||||
*/
|
||||
export interface Role {
|
||||
id: string;
|
||||
name: string;
|
||||
color: number;
|
||||
hoist: boolean;
|
||||
position: number;
|
||||
permissions: string;
|
||||
managed: boolean;
|
||||
mentionable: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a Ban.
|
||||
*/
|
||||
export interface Ban {
|
||||
reason: string | null;
|
||||
user: any; // User object
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a guild by ID.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param withCounts Whether to include approximate member and presence counts.
|
||||
* @returns A guild object.
|
||||
*/
|
||||
export const getGuild = async (
|
||||
guildId: string,
|
||||
withCounts?: boolean,
|
||||
): Promise<Guild> => {
|
||||
const path = `/guilds/${guildId}${withCounts ? "?with_counts=true" : ""}`;
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify a guild's settings.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param data The new guild data.
|
||||
* @returns The updated guild object.
|
||||
*/
|
||||
export const modifyGuild = async (
|
||||
guildId: string,
|
||||
data: any,
|
||||
): Promise<Guild> => {
|
||||
const path = `/guilds/${guildId}`;
|
||||
const res = await fetchDiscord(path, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of guild channel objects.
|
||||
* @param guildId The ID of the guild.
|
||||
* @returns A list of channel objects.
|
||||
*/
|
||||
export const getGuildChannels = async (guildId: string): Promise<any[]> => {
|
||||
const path = `/guilds/${guildId}/channels`;
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new channel object for the guild.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param data The channel data.
|
||||
* @returns The new channel object.
|
||||
*/
|
||||
export const createGuildChannel = async (
|
||||
guildId: string,
|
||||
data: any,
|
||||
): Promise<any> => {
|
||||
const path = `/guilds/${guildId}/channels`;
|
||||
const res = await fetchDiscord(path, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of guild member objects that are members of the guild.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param options Options for listing members.
|
||||
* @returns A list of guild member objects.
|
||||
*/
|
||||
export const listGuildMembers = async (
|
||||
guildId: string,
|
||||
options?: { limit?: number; after?: string },
|
||||
): Promise<GuildMember[]> => {
|
||||
const query = new URLSearchParams();
|
||||
if (options?.limit) query.append("limit", String(options.limit));
|
||||
if (options?.after) query.append("after", options.after);
|
||||
const queryString = query.toString();
|
||||
const path = `/guilds/${guildId}/members${queryString ? `?${queryString}` : ""}`;
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a guild member object for the specified user.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param userId The ID of the user.
|
||||
* @returns A guild member object.
|
||||
*/
|
||||
export const getGuildMember = async (
|
||||
guildId: string,
|
||||
userId: string,
|
||||
): Promise<GuildMember> => {
|
||||
const path = `/guilds/${guildId}/members/${userId}`;
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify attributes of a guild member.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param userId The ID of the user.
|
||||
* @param data The data to update.
|
||||
* @returns The updated guild member.
|
||||
*/
|
||||
export const modifyGuildMember = async (
|
||||
guildId: string,
|
||||
userId: string,
|
||||
data: any,
|
||||
): Promise<GuildMember> => {
|
||||
const path = `/guilds/${guildId}/members/${userId}`;
|
||||
const res = await fetchDiscord(path, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a member from a guild.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param userId The ID of the user to remove.
|
||||
*/
|
||||
export const removeGuildMember = async (
|
||||
guildId: string,
|
||||
userId: string,
|
||||
): Promise<void> => {
|
||||
const path = `/guilds/${guildId}/members/${userId}`;
|
||||
await fetchDiscord(path, {
|
||||
method: "DELETE",
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of role objects for the guild.
|
||||
* @param guildId The ID of the guild.
|
||||
* @returns A list of role objects.
|
||||
*/
|
||||
export const getGuildRoles = async (guildId: string): Promise<Role[]> => {
|
||||
const path = `/guilds/${guildId}/roles`;
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of ban objects for the users banned from this guild.
|
||||
* @param guildId The ID of the guild.
|
||||
* @returns A list of ban objects.
|
||||
*/
|
||||
export const getGuildBans = async (guildId: string): Promise<Ban[]> => {
|
||||
const path = `/guilds/${guildId}/bans`;
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a guild ban, and optionally delete previous messages sent by the banned user.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param userId The ID of the user to ban.
|
||||
* @param options Options for the ban.
|
||||
*/
|
||||
export const createGuildBan = async (
|
||||
guildId: string,
|
||||
userId: string,
|
||||
options?: { delete_message_seconds?: number },
|
||||
): Promise<void> => {
|
||||
const path = `/guilds/${guildId}/bans/${userId}`;
|
||||
await fetchDiscord(path, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(options),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the ban for a user.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param userId The ID of the user to unban.
|
||||
*/
|
||||
export const removeGuildBan = async (
|
||||
guildId: string,
|
||||
userId: string,
|
||||
): Promise<void> => {
|
||||
const path = `/guilds/${guildId}/bans/${userId}`;
|
||||
await fetchDiscord(path, {
|
||||
method: "DELETE",
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a role from a guild member.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param userId The ID of the user.
|
||||
* @param roleId The ID of the role to remove.
|
||||
*/
|
||||
export const removeGuildMemberRole = async (
|
||||
guildId: string,
|
||||
userId: string,
|
||||
roleId: string,
|
||||
): Promise<void> => {
|
||||
const path = `/guilds/${guildId}/members/${userId}/roles/${roleId}`;
|
||||
await fetchDiscord(path, { method: "DELETE" });
|
||||
};
|
||||
5
src/discord/index.ts
Normal file
5
src/discord/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from "./audit-log";
|
||||
export * from "./core";
|
||||
export * from "./guild";
|
||||
export * from "./message";
|
||||
export * from "./poll";
|
||||
219
src/discord/message.ts
Normal file
219
src/discord/message.ts
Normal file
@ -0,0 +1,219 @@
|
||||
import { fetchDiscord } from "./core";
|
||||
|
||||
/**
|
||||
* Represents a message sent in a channel within Discord.
|
||||
* This is a partial interface based on the Discord API documentation.
|
||||
*/
|
||||
export interface Message {
|
||||
id: string;
|
||||
channel_id: string;
|
||||
author: any; // User object
|
||||
content: string;
|
||||
timestamp: string; // ISO8601 timestamp
|
||||
edited_timestamp: string | null; // ISO8601 timestamp
|
||||
tts: boolean;
|
||||
mention_everyone: boolean;
|
||||
mentions: any[]; // array of user objects
|
||||
mention_roles: string[]; // array of role object ids
|
||||
mention_channels?: any[]; // array of channel mention objects
|
||||
attachments: any[]; // array of attachment objects
|
||||
embeds: any[]; // array of embed objects
|
||||
reactions?: any[]; // array of reaction objects
|
||||
nonce?: number | string;
|
||||
pinned: boolean;
|
||||
webhook_id?: string;
|
||||
type: number;
|
||||
activity?: any; // message activity object
|
||||
application?: any; // partial application object
|
||||
application_id?: string;
|
||||
flags?: number;
|
||||
message_reference?: any; // message reference object
|
||||
referenced_message?: Message | null;
|
||||
interaction?: any; // message interaction object
|
||||
thread?: any; // channel object
|
||||
components?: any[]; // array of message components
|
||||
sticker_items?: any[]; // array of message sticker item objects
|
||||
stickers?: any[]; // array of sticker objects
|
||||
position?: number;
|
||||
role_subscription_data?: any; // role subscription data object
|
||||
resolved?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the messages in a channel.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param options Options for fetching messages.
|
||||
* @returns An array of message objects.
|
||||
*/
|
||||
export const getChannelMessages = async (
|
||||
channelId: string,
|
||||
options?: {
|
||||
around?: string;
|
||||
before?: string;
|
||||
after?: string;
|
||||
limit?: number;
|
||||
},
|
||||
): Promise<Message[]> => {
|
||||
const query = new URLSearchParams();
|
||||
if (options?.around) query.append("around", options.around);
|
||||
if (options?.before) query.append("before", options.before);
|
||||
if (options?.after) query.append("after", options.after);
|
||||
if (options?.limit) query.append("limit", String(options.limit));
|
||||
|
||||
const queryString = query.toString();
|
||||
const path = `/channels/${channelId}/messages${queryString ? `?${queryString}` : ""}`;
|
||||
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves a specific message in the channel.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param messageId The ID of the message.
|
||||
* @returns A message object.
|
||||
*/
|
||||
export const getChannelMessage = async (
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
): Promise<Message> => {
|
||||
const path = `/channels/${channelId}/messages/${messageId}`;
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
export interface CreateMessageParams {
|
||||
content?: string;
|
||||
tts?: boolean;
|
||||
embeds?: any[];
|
||||
allowed_mentions?: any;
|
||||
message_reference?: any;
|
||||
components?: any[];
|
||||
sticker_ids?: string[];
|
||||
files?: any[];
|
||||
payload_json?: string;
|
||||
attachments?: any[];
|
||||
flags?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post a message to a guild text or DM channel.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param data The message data.
|
||||
* @returns The created message object.
|
||||
*/
|
||||
export const createMessage = async (
|
||||
channelId: string,
|
||||
data: CreateMessageParams,
|
||||
): Promise<Message> => {
|
||||
const path = `/channels/${channelId}/messages`;
|
||||
const res = await fetchDiscord(path, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return res.json();
|
||||
};
|
||||
|
||||
export interface EditMessageParams {
|
||||
content?: string;
|
||||
embeds?: any[];
|
||||
flags?: number;
|
||||
allowed_mentions?: any;
|
||||
components?: any[];
|
||||
files?: any[];
|
||||
payload_json?: string;
|
||||
attachments?: any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a previously sent message.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param messageId The ID of the message to edit.
|
||||
* @param data The new message data.
|
||||
* @returns The updated message object.
|
||||
*/
|
||||
export const editMessage = async (
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
data: EditMessageParams,
|
||||
): Promise<Message> => {
|
||||
const path = `/channels/${channelId}/messages/${messageId}`;
|
||||
const res = await fetchDiscord(path, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a message.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param messageId The ID of the message to delete.
|
||||
*/
|
||||
export const deleteMessage = async (
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
): Promise<void> => {
|
||||
const path = `/channels/${channelId}/messages/${messageId}`;
|
||||
await fetchDiscord(path, {
|
||||
method: "DELETE",
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete multiple messages in a single request.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param messageIds An array of message IDs to delete.
|
||||
*/
|
||||
export const bulkDeleteMessages = async (
|
||||
channelId: string,
|
||||
messageIds: string[],
|
||||
): Promise<void> => {
|
||||
const path = `/channels/${channelId}/messages/bulk-delete`;
|
||||
await fetchDiscord(path, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ messages: messageIds }),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a reaction for the message.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param messageId The ID of the message.
|
||||
* @param emoji The emoji to react with.
|
||||
*/
|
||||
export const addReaction = async (
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
emoji: string,
|
||||
): Promise<void> => {
|
||||
const path = `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(
|
||||
emoji,
|
||||
)}/@me`;
|
||||
await fetchDiscord(path, { method: "PUT" });
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of users that reacted with this emoji.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param messageId The ID of the message.
|
||||
* @param emoji The emoji.
|
||||
* @param options Options for fetching reactions.
|
||||
* @returns A list of user objects.
|
||||
*/
|
||||
export const getReactions = async (
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
emoji: string,
|
||||
options?: { after?: string; limit?: number },
|
||||
): Promise<any[]> => {
|
||||
const query = new URLSearchParams();
|
||||
if (options?.after) query.append("after", options.after);
|
||||
if (options?.limit) query.append("limit", String(options.limit ?? 100));
|
||||
const queryString = query.toString();
|
||||
const path = `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(
|
||||
emoji,
|
||||
)}${queryString ? `?${queryString}` : ""}`;
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
81
src/discord/poll.ts
Normal file
81
src/discord/poll.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { fetchDiscord } from "./core";
|
||||
import { Message } from "./message";
|
||||
|
||||
export interface PollMedia {
|
||||
text?: string;
|
||||
emoji?: any; // Partial Emoji
|
||||
}
|
||||
|
||||
export interface PollAnswer {
|
||||
answer_id: number;
|
||||
poll_media: PollMedia;
|
||||
}
|
||||
|
||||
export interface PollAnswerCount {
|
||||
id: number;
|
||||
count: number;
|
||||
me_voted: boolean;
|
||||
}
|
||||
|
||||
export interface PollResults {
|
||||
is_finalized: boolean;
|
||||
answer_counts: PollAnswerCount[];
|
||||
}
|
||||
|
||||
export interface Poll {
|
||||
question: PollMedia;
|
||||
answers: PollAnswer[];
|
||||
expiry: string | null; // ISO8601 timestamp
|
||||
allow_multiselect: boolean;
|
||||
layout_type: number;
|
||||
results?: PollResults;
|
||||
}
|
||||
|
||||
export interface PollCreateRequest {
|
||||
question: PollMedia;
|
||||
answers: PollAnswer[];
|
||||
duration?: number;
|
||||
allow_multiselect?: boolean;
|
||||
layout_type?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of users that voted for a specific answer.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param messageId The ID of the message with the poll.
|
||||
* @param answerId The ID of the answer.
|
||||
* @param options Options for fetching voters.
|
||||
* @returns A list of user objects.
|
||||
*/
|
||||
export const getAnswerVoters = async (
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
answerId: number,
|
||||
options?: { after?: string; limit?: number },
|
||||
): Promise<{ users: any[] }> => {
|
||||
const query = new URLSearchParams();
|
||||
if (options?.after) query.append("after", options.after);
|
||||
if (options?.limit) query.append("limit", String(options.limit));
|
||||
const queryString = query.toString();
|
||||
|
||||
const path = `/channels/${channelId}/polls/${messageId}/answers/${answerId}${queryString ? `?${queryString}` : ""}`;
|
||||
const res = await fetchDiscord(path);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Immediately ends a poll.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param messageId The ID of the message with the poll.
|
||||
* @returns The updated message object.
|
||||
*/
|
||||
export const endPoll = async (
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
): Promise<Message> => {
|
||||
const path = `/channels/${channelId}/polls/${messageId}/expire`;
|
||||
const res = await fetchDiscord(path, {
|
||||
method: "POST",
|
||||
});
|
||||
return res.json();
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user