feat(feishu): add Feishu inbound webhook, /gh commands and card actions

- add feishu_links D1 table and link store helpers
- implement X-Lark-Signature verification, url_verification, /gh login|logout|comment|merge|close and card.action.trigger Merge/Close
- render interactive cards with clickable title link, inline links and callback buttons (no whole-card card_link)
- bind Feishu account in OAuth callback
- document event subscription and required scopes
This commit is contained in:
RhenCloud 2026-08-27 00:04:49 +08:00
parent 3437ac5513
commit 4b99f6d33d
No known key found for this signature in database
GPG key ID: A574A617378C4E0B
38 changed files with 2449 additions and 1412 deletions

View file

@ -186,6 +186,7 @@
<option value="">{{ t("groupEditor.logDisabled") }}</option>
<option value="discord">Discord</option>
<option value="telegram">Telegram</option>
<option value="feishu">{{ t("groupEditor.logFeishu") }}</option>
</select>
<template v-if="form.logPlatform === 'discord'">
<input
@ -201,7 +202,7 @@
:placeholder="t('routeEditor.threadPlaceholder')"
/>
</template>
<template v-else-if="form.logPlatform === 'telegram'">
<template v-else-if="form.logPlatform === 'telegram' || form.logPlatform === 'feishu'">
<input
v-model="form.logChatId"
type="text"
@ -209,6 +210,7 @@
:placeholder="t('routeEditor.chatPlaceholder')"
/>
<input
v-if="form.logPlatform === 'telegram'"
v-model="form.logTopicId"
type="text"
class="input mt-2"
@ -263,7 +265,7 @@ const form = reactive({
emoji: true,
forgeSources: [] as ForgeSource[],
lang: "",
logPlatform: "" as "" | "discord" | "telegram",
logPlatform: "" as "" | "discord" | "telegram" | "feishu",
logChannelId: "",
logThreadId: "",
logChatId: "",
@ -323,11 +325,8 @@ function save(): void {
}
let logTarget:
| { platform: "discord"; channelId: string; threadId?: string }
| {
platform: "telegram";
chatId: string;
topicId?: string;
}
| { platform: "telegram"; chatId: string; topicId?: string }
| { platform: "feishu"; chatId: string }
| undefined;
if (form.logPlatform === "discord") {
const channelId = form.logChannelId.trim();
@ -343,6 +342,13 @@ function save(): void {
return;
}
logTarget = { platform: "telegram", chatId, topicId: form.logTopicId.trim() || undefined };
} else if (form.logPlatform === "feishu") {
const chatId = form.logChatId.trim();
if (!chatId) {
formError.value = t("groupEditor.errLogFeishuChat");
return;
}
logTarget = { platform: "feishu", chatId };
}
const installationText = form.installationId.trim();
let installationId: number | undefined;