diff --git a/infra/app.ts b/infra/app.ts index 1b2351ec8c..bb627f51ec 100644 --- a/infra/app.ts +++ b/infra/app.ts @@ -4,6 +4,10 @@ const GITHUB_APP_ID = new sst.Secret("GITHUB_APP_ID") const GITHUB_APP_PRIVATE_KEY = new sst.Secret("GITHUB_APP_PRIVATE_KEY") export const EMAILOCTOPUS_API_KEY = new sst.Secret("EMAILOCTOPUS_API_KEY") const ADMIN_SECRET = new sst.Secret("ADMIN_SECRET") +const DISCORD_SUPPORT_BOT_TOKEN = new sst.Secret("DISCORD_SUPPORT_BOT_TOKEN") +const DISCORD_SUPPORT_CHANNEL_ID = new sst.Secret("DISCORD_SUPPORT_CHANNEL_ID") +const FEISHU_APP_ID = new sst.Secret("FEISHU_APP_ID") +const FEISHU_APP_SECRET = new sst.Secret("FEISHU_APP_SECRET") const bucket = new sst.cloudflare.Bucket("Bucket") export const api = new sst.cloudflare.Worker("Api", { @@ -13,7 +17,16 @@ export const api = new sst.cloudflare.Worker("Api", { WEB_DOMAIN: domain, }, url: true, - link: [bucket, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, ADMIN_SECRET], + link: [ + bucket, + GITHUB_APP_ID, + GITHUB_APP_PRIVATE_KEY, + ADMIN_SECRET, + DISCORD_SUPPORT_BOT_TOKEN, + DISCORD_SUPPORT_CHANNEL_ID, + FEISHU_APP_ID, + FEISHU_APP_SECRET, + ], transform: { worker: (args) => { args.logpush = true diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts index 3710cb77f8..66c06a824a 100644 --- a/packages/console/core/sst-env.d.ts +++ b/packages/console/core/sst-env.d.ts @@ -34,6 +34,14 @@ declare module "sst" { "type": "sst.cloudflare.SolidStart" "url": string } + "DISCORD_SUPPORT_BOT_TOKEN": { + "type": "sst.sst.Secret" + "value": string + } + "DISCORD_SUPPORT_CHANNEL_ID": { + "type": "sst.sst.Secret" + "value": string + } "Database": { "database": string "host": string @@ -46,6 +54,14 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "FEISHU_APP_ID": { + "type": "sst.sst.Secret" + "value": string + } + "FEISHU_APP_SECRET": { + "type": "sst.sst.Secret" + "value": string + } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts index 3710cb77f8..66c06a824a 100644 --- a/packages/console/function/sst-env.d.ts +++ b/packages/console/function/sst-env.d.ts @@ -34,6 +34,14 @@ declare module "sst" { "type": "sst.cloudflare.SolidStart" "url": string } + "DISCORD_SUPPORT_BOT_TOKEN": { + "type": "sst.sst.Secret" + "value": string + } + "DISCORD_SUPPORT_CHANNEL_ID": { + "type": "sst.sst.Secret" + "value": string + } "Database": { "database": string "host": string @@ -46,6 +54,14 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "FEISHU_APP_ID": { + "type": "sst.sst.Secret" + "value": string + } + "FEISHU_APP_SECRET": { + "type": "sst.sst.Secret" + "value": string + } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts index 3710cb77f8..66c06a824a 100644 --- a/packages/console/resource/sst-env.d.ts +++ b/packages/console/resource/sst-env.d.ts @@ -34,6 +34,14 @@ declare module "sst" { "type": "sst.cloudflare.SolidStart" "url": string } + "DISCORD_SUPPORT_BOT_TOKEN": { + "type": "sst.sst.Secret" + "value": string + } + "DISCORD_SUPPORT_CHANNEL_ID": { + "type": "sst.sst.Secret" + "value": string + } "Database": { "database": string "host": string @@ -46,6 +54,14 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "FEISHU_APP_ID": { + "type": "sst.sst.Secret" + "value": string + } + "FEISHU_APP_SECRET": { + "type": "sst.sst.Secret" + "value": string + } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string diff --git a/packages/enterprise/sst-env.d.ts b/packages/enterprise/sst-env.d.ts index 3710cb77f8..66c06a824a 100644 --- a/packages/enterprise/sst-env.d.ts +++ b/packages/enterprise/sst-env.d.ts @@ -34,6 +34,14 @@ declare module "sst" { "type": "sst.cloudflare.SolidStart" "url": string } + "DISCORD_SUPPORT_BOT_TOKEN": { + "type": "sst.sst.Secret" + "value": string + } + "DISCORD_SUPPORT_CHANNEL_ID": { + "type": "sst.sst.Secret" + "value": string + } "Database": { "database": string "host": string @@ -46,6 +54,14 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "FEISHU_APP_ID": { + "type": "sst.sst.Secret" + "value": string + } + "FEISHU_APP_SECRET": { + "type": "sst.sst.Secret" + "value": string + } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string diff --git a/packages/function/src/api.ts b/packages/function/src/api.ts index 6f00dae9a2..d08e9e49ac 100644 --- a/packages/function/src/api.ts +++ b/packages/function/src/api.ts @@ -12,6 +12,20 @@ type Env = { WEB_DOMAIN: string } +async function getFeishuTenantToken(): Promise { + const response = await fetch("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + app_id: Resource.FEISHU_APP_ID.value, + app_secret: Resource.FEISHU_APP_SECRET.value, + }), + }) + const data = (await response.json()) as { tenant_access_token?: string } + if (!data.tenant_access_token) throw new Error("Failed to get Feishu tenant token") + return data.tenant_access_token +} + export class SyncServer extends DurableObject { constructor(ctx: DurableObjectState, env: Env) { super(ctx, env) @@ -200,6 +214,58 @@ export default new Hono<{ Bindings: Env }>() return c.json({ info, messages }) }) + .post("/feishu", async (c) => { + const body = (await c.req.json()) as { + challenge?: string + event?: { + message?: { + message_id?: string + root_id?: string + parent_id?: string + chat_id?: string + content?: string + } + } +} + console.log(JSON.stringify(body, null, 2)) + const challenge = body.challenge + if (challenge) return c.json({ challenge }) + + const content = body.event?.message?.content + const parsed = + typeof content === "string" && content.trim().startsWith("{") ? (JSON.parse(content) as { + text?: string +}) : undefined + const text = typeof parsed?.text === "string" ? parsed.text : typeof content === "string" ? content : "" + + let message = text.trim().replace(/^@_user_\d+\s*/, "") + message = message.replace(/^aiden,?\s*/i, "<@759257817772851260> ") + if (!message) return c.json({ ok: true }) + + const threadId = body.event?.message?.root_id || body.event?.message?.message_id + if (threadId) message = `${message} [${threadId}]` + + const response = await fetch( + `https://discord.com/api/v10/channels/${Resource.DISCORD_SUPPORT_CHANNEL_ID.value}/messages`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${Resource.DISCORD_SUPPORT_BOT_TOKEN.value}`, + }, + body: JSON.stringify({ + content: `${message}`, + }), + }, + ) + + if (!response.ok) { + console.error(await response.text()) + return c.json({ error: "Discord bot message failed" }, { status: 502 }) + } + + return c.json({ ok: true }) + }) /** * Used by the GitHub action to get GitHub installation access token given the OIDC token */ diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index 3710cb77f8..66c06a824a 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -34,6 +34,14 @@ declare module "sst" { "type": "sst.cloudflare.SolidStart" "url": string } + "DISCORD_SUPPORT_BOT_TOKEN": { + "type": "sst.sst.Secret" + "value": string + } + "DISCORD_SUPPORT_CHANNEL_ID": { + "type": "sst.sst.Secret" + "value": string + } "Database": { "database": string "host": string @@ -46,6 +54,14 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "FEISHU_APP_ID": { + "type": "sst.sst.Secret" + "value": string + } + "FEISHU_APP_SECRET": { + "type": "sst.sst.Secret" + "value": string + } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string diff --git a/sst-env.d.ts b/sst-env.d.ts index ad02b7bf1d..b767ef1b30 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -49,6 +49,14 @@ declare module "sst" { "type": "sst.cloudflare.SolidStart" "url": string } + "DISCORD_SUPPORT_BOT_TOKEN": { + "type": "sst.sst.Secret" + "value": string + } + "DISCORD_SUPPORT_CHANNEL_ID": { + "type": "sst.sst.Secret" + "value": string + } "Database": { "database": string "host": string @@ -65,6 +73,14 @@ declare module "sst" { "name": string "type": "sst.cloudflare.Bucket" } + "FEISHU_APP_ID": { + "type": "sst.sst.Secret" + "value": string + } + "FEISHU_APP_SECRET": { + "type": "sst.sst.Secret" + "value": string + } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string