diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d1782e240..9161e12e8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,6 +7,6 @@ jobs: - uses: actions/checkout@v2 - uses: denolib/setup-deno@v2 - name: Run fmt check script - run: deno fmt --check + run: deno fmt --check --ignore=./src/types/util.ts - name: Run lint script run: deno lint src/** test/** --unstable diff --git a/src/api/controllers/misc.ts b/src/api/controllers/misc.ts index f3ff65254..ccd4e91b5 100644 --- a/src/api/controllers/misc.ts +++ b/src/api/controllers/misc.ts @@ -1,6 +1,8 @@ import { eventHandlers, setApplicationID, setBotID } from "../../bot.ts"; import { DiscordPayload, + IntegrationCreateUpdateEvent, + IntegrationDeleteEvent, PresenceUpdatePayload, ReadyPayload, TypingStartPayload, @@ -155,3 +157,73 @@ export function handleInternalWebhooksUpdate(data: DiscordPayload) { options.guild_id, ); } + +export function handleInternalIntegrationCreate( + data: DiscordPayload, +) { + if (data.t !== "INTEGRATION_CREATE") return; + + const { + guild_id: guildID, + enable_emoticons: enableEmoticons, + expire_behavior: expireBehavior, + expire_grace_period: expireGracePeriod, + subscriber_count: subscriberCount, + role_id: roleID, + synced_at: syncedAt, + ...rest + } = data.d as IntegrationCreateUpdateEvent; + + eventHandlers.integrationCreate?.({ + ...rest, + guildID, + enableEmoticons, + expireBehavior, + expireGracePeriod, + syncedAt, + subscriberCount, + roleID, + }); +} + +export function handleInternalIntegrationUpdate(data: DiscordPayload) { + if (data.t !== "INTEGRATION_UPDATE") return; + + const { + enable_emoticons: enableEmoticons, + expire_behavior: expireBehavior, + expire_grace_period: expireGracePeriod, + role_id: roleID, + subscriber_count: subscriberCount, + synced_at: syncedAt, + guild_id: guildID, + ...rest + } = data.d as IntegrationCreateUpdateEvent; + + eventHandlers.integrationUpdate?.({ + ...rest, + guildID, + subscriberCount, + enableEmoticons, + expireGracePeriod, + roleID, + expireBehavior, + syncedAt, + }); +} + +export function handleInternalIntegrationDelete(data: DiscordPayload) { + if (data.t !== "INTEGRATION_DELETE") return; + + const { + guild_id: guildID, + application_id: applicationID, + ...rest + } = data.d as IntegrationDeleteEvent; + + eventHandlers.integrationDelete?.({ + ...rest, + applicationID, + guildID, + }); +} diff --git a/src/api/controllers/mod.ts b/src/api/controllers/mod.ts index e6250f8b5..970702191 100644 --- a/src/api/controllers/mod.ts +++ b/src/api/controllers/mod.ts @@ -30,6 +30,9 @@ import { handleInternalMessageUpdate, } from "./messages.ts"; import { + handleInternalIntegrationCreate, + handleInternalIntegrationDelete, + handleInternalIntegrationUpdate, handleInternalPresenceUpdate, handleInternalReady, handleInternalTypingStart, @@ -82,6 +85,9 @@ export let controllers = { USER_UPDATE: handleInternalUserUpdate, VOICE_STATE_UPDATE: handleInternalVoiceStateUpdate, WEBHOOKS_UPDATE: handleInternalWebhooksUpdate, + INTEGRATION_CREATE: handleInternalIntegrationCreate, + INTEGRATION_UPDATE: handleInternalIntegrationUpdate, + INTEGRATION_DELETE: handleInternalIntegrationDelete, }; export type Controllers = typeof controllers; diff --git a/src/types/discord.ts b/src/types/discord.ts index 904dcb3fb..ddc9f13ee 100644 --- a/src/types/discord.ts +++ b/src/types/discord.ts @@ -1,4 +1,9 @@ -import { CreateGuildPayload, PartialUser, UserPayload } from "./guild.ts"; +import { + CreateGuildPayload, + Integration, + PartialUser, + UserPayload, +} from "./guild.ts"; import { MemberCreatePayload } from "./member.ts"; import { Activity, Application } from "./message.ts"; import { ClientStatusPayload } from "./presence.ts"; @@ -43,7 +48,10 @@ export interface DiscordPayload { | "TYPING_START" | "USER_UPDATE" | "VOICE_STATE_UPDATE" - | "WEBHOOKS_UPDATE"; + | "WEBHOOKS_UPDATE" + | "INTEGRATION_CREATE" + | "INTEGRATION_UPDATE" + | "INTEGRATION_DELETE"; } export interface DiscordBotGatewayData { @@ -301,3 +309,17 @@ export type UnavailableGuildPayload = Pick< CreateGuildPayload, "id" | "unavailable" >; + +export type IntegrationCreateUpdateEvent = Integration & { + /** id of the guild */ + guild_id: string; +}; + +export interface IntegrationDeleteEvent { + /** integration id */ + id: string; + /** id of the guild */ + guild_id: string; + /** id of the bot/OAuth2 application for this discord integration */ + application_id?: string; +} diff --git a/src/types/guild.ts b/src/types/guild.ts index a9278e6d2..63c173422 100644 --- a/src/types/guild.ts +++ b/src/types/guild.ts @@ -2,7 +2,7 @@ import { Guild } from "../api/structures/mod.ts"; import { ChannelCreatePayload, ChannelTypes } from "./channel.ts"; import { Emoji, StatusType } from "./discord.ts"; import { MemberCreatePayload } from "./member.ts"; -import { Activity } from "./message.ts"; +import { Activity, Application } from "./message.ts"; import { Permission } from "./permission.ts"; import { ClientStatusPayload } from "./presence.ts"; import { RoleData } from "./role.ts"; @@ -246,7 +246,7 @@ export interface EditIntegrationOptions { enable_emoticons: boolean; } -export interface GuildIntegration { +export interface Integration { /** The integrations unique id */ id: string; /** the integrations name */ @@ -256,19 +256,32 @@ export interface GuildIntegration { /** Is this integration enabled */ enabled: boolean; /** is this integration syncing */ - syncing: boolean; + syncing?: boolean; /** id that this integration uses for "subscribers" */ - role_id: string; + role_id?: string; + /** whether emoticons should be synced for this integration (twitch only currently) */ + enable_emoticons?: boolean; /** The behavior of expiring subscribers */ - expire_behavior: number; + expire_behavior?: IntegrationExpireBehaviors; /** The grace period before expiring subscribers */ - expire_grace_period: number; + expire_grace_period?: number; /** The user for this integration */ - user: UserPayload; + user?: UserPayload; /** The integration account information */ account: Account; /** When this integration was last synced */ - synced_at: string; + synced_at?: string; + /** how many subscribers this integration has */ + subscriber_count?: number; + /** has this integration been revoked */ + revoked?: boolean; + /** The bot/OAuth2 application for discord integrations */ + application?: Application; +} + +export enum IntegrationExpireBehaviors { + RemoveRole, + Kick, } export interface Account { diff --git a/src/types/message.ts b/src/types/message.ts index 606655b01..e957c51ef 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -188,6 +188,10 @@ export interface Application { icon: string | null; /** The name of the application */ name: string; + /** the description of the app */ + summary: string; + /** the bot associated with this application */ + bot?: UserPayload; } export interface Reference { diff --git a/src/types/options.ts b/src/types/options.ts index e5c566527..8ba6b7e47 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -8,6 +8,8 @@ import { import { DiscordPayload, Emoji, + IntegrationCreateUpdateEvent, + IntegrationDeleteEvent, PresenceUpdatePayload, TypingStartPayload, VoiceStateUpdatePayload, @@ -24,6 +26,7 @@ import { PartialMessage, ReactionPayload, } from "./message.ts"; +import { Camelize } from "./util.ts"; export interface BotConfig { token: string; @@ -208,6 +211,12 @@ export interface EventHandlers { ) => unknown; /** Sent when a guild channel's webhook is created, updated, or deleted. */ webhooksUpdate?: (channelID: string, guildID: string) => unknown; + /** Sent when an integration is created on a server such as twitch, youtube etc.. */ + integrationCreate?: (data: Camelize) => unknown; + /** Sent when an integration is updated. */ + integrationUpdate?: (data: Camelize) => unknown; + /** Sent when an integration is deleted. */ + integrationDelete?: (data: Camelize) => undefined; } /** https://discord.com/developers/docs/topics/gateway#list-of-intents */ @@ -241,6 +250,9 @@ export enum Intents { GUILD_EMOJIS = 1 << 3, /** Enables the following events: * - GUILD_INTEGRATIONS_UPDATE + * - INTEGRATION_CREATE + * - INTEGRATION_UPDATE + * - INTEGRATION_DELETE */ GUILD_INTEGRATIONS = 1 << 4, /** Enables the following events: @@ -297,5 +309,3 @@ export enum Intents { */ DIRECT_MESSAGE_TYPING = 1 << 14, } - -export type ValueOf = T[keyof T]; diff --git a/src/types/util.ts b/src/types/util.ts new file mode 100644 index 000000000..43af12643 --- /dev/null +++ b/src/types/util.ts @@ -0,0 +1,9 @@ +export type CamelizeString = T extends string + ? string extends T ? string + : T extends `${infer F}_${infer R}` + ? `${F}${T extends `${infer F}_id` ? Uppercase + : Capitalize>}` + : T + : T; + +export type Camelize = { [K in keyof T as CamelizeString]: T[K] }