Skip to content

Commit 83d6036

Browse files
committed
[add] GitHub action of Lark notification
[optimize] upgrade to MobX-GitHub 0.4, Git-utility 0.2 & other latest Upstream packages [fix] too many Lark API requests
1 parent e6bcede commit 83d6036

File tree

6 files changed

+773
-767
lines changed

6 files changed

+773
-767
lines changed

.github/scripts/deno.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"nodeModulesDir": "none"
3+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { components } from 'npm:@octokit/openapi-types';
2+
import { stdin } from 'npm:zx';
3+
4+
type GitHubSchema = components['schemas'];
5+
6+
type GitHubUser = GitHubSchema['simple-user'];
7+
8+
interface GitHubAction
9+
extends Record<'event_name' | 'actor' | 'server_url' | 'repository', string> {
10+
action?: string;
11+
ref?: string;
12+
ref_name?: string;
13+
event: {
14+
head_commit?: GitHubSchema['git-commit'];
15+
issue?: GitHubSchema['webhook-issues-opened']['issue'];
16+
pull_request?: GitHubSchema['pull-request'];
17+
discussion?: GitHubSchema['discussion'];
18+
comment?: GitHubSchema['issue-comment'];
19+
release?: GitHubSchema['release'];
20+
};
21+
}
22+
23+
// Helper functions
24+
const getActionText = (action?: string) =>
25+
action === 'closed' ? '关闭' : action?.includes('open') ? '打开' : '编辑';
26+
27+
const createLink = (href: string, text = href) => ({ tag: 'a', href, text });
28+
29+
const createText = (text: string) => ({ tag: 'text', text });
30+
31+
// create user link
32+
const createUserLink = (user: GitHubUser) =>
33+
user ? createLink(user.html_url, user.login) : createText('无');
34+
35+
const createContentItem = (label: string, value?: string | { tag: string; text: string }) =>
36+
[
37+
createText(label),
38+
typeof value === 'string' ? createText(value || '无') : value || createText('无'),
39+
] as [object, object];
40+
41+
type EventHandler = (
42+
event: GitHubAction,
43+
actionText: string,
44+
) => {
45+
title: string;
46+
content: [object, object][];
47+
};
48+
49+
// Event handlers
50+
const eventHandlers: Record<string, EventHandler> = {
51+
push: ({ event: { head_commit }, ref, ref_name, server_url, repository, actor }) => ({
52+
title: 'GitHub 代码提交',
53+
content: [
54+
[createText('提交链接:'), createLink(head_commit!.url)],
55+
[createText('代码分支:'), createLink(`${server_url}/${repository}/tree/${ref_name}`, ref)],
56+
[createText('提交作者:'), createLink(`${server_url}/${actor}`, actor)],
57+
[createText('提交信息:'), createText(head_commit!.message)],
58+
],
59+
}),
60+
61+
issues: ({ event: { issue } }, actionText) => ({
62+
title: `GitHub issue ${actionText}${issue?.title}`,
63+
content: [
64+
[createText('链接:'), createLink(issue!.html_url)],
65+
[createText('作者:'), createLink(issue!.user!.html_url!, issue!.user!.login)],
66+
[
67+
createText('指派:'),
68+
issue?.assignee
69+
? createLink(issue.assignee.html_url!, issue.assignee.login)
70+
: createText('无'),
71+
],
72+
[createText('标签:'), createText(issue?.labels?.map(({ name }) => name).join(', ') || '无')],
73+
[createText('里程碑:'), createText(issue?.milestone?.title || '无')],
74+
[createText('描述:'), createText(issue?.body || '无')],
75+
],
76+
}),
77+
78+
pull_request: ({ event: { pull_request } }, actionText) => ({
79+
title: `GitHub PR ${actionText}${pull_request?.title}`,
80+
content: [
81+
[createText('链接:'), createLink(pull_request!.html_url)],
82+
[createText('作者:'), createLink(pull_request!.user.html_url, pull_request!.user.login)],
83+
[
84+
createText('指派:'),
85+
pull_request?.assignee
86+
? createLink(pull_request.assignee.html_url, pull_request.assignee.login)
87+
: createText('无'),
88+
],
89+
[
90+
createText('标签:'),
91+
createText(pull_request?.labels?.map(({ name }) => name).join(', ') || '无'),
92+
],
93+
[createText('里程碑:'), createText(pull_request?.milestone?.title || '无')],
94+
[createText('描述:'), createText(pull_request?.body || '无')],
95+
],
96+
}),
97+
98+
discussion: ({ event: { discussion } }, actionText) => ({
99+
title: `GitHub 讨论 ${actionText}${discussion?.title || '无'}`,
100+
content: [
101+
createContentItem('链接:', discussion?.html_url),
102+
createContentItem('作者:', createUserLink(discussion!.user as GitHubUser)),
103+
createContentItem('描述:', discussion?.body || '无'),
104+
],
105+
}),
106+
107+
issue_comment: ({ event: { comment, issue } }) => ({
108+
title: `GitHub issue 评论:${issue?.title || '未知 issue'}`,
109+
content: [
110+
createContentItem('链接:', comment?.html_url),
111+
createContentItem('作者:', createUserLink(comment!.user!)),
112+
createContentItem('描述:', comment?.body || '无'),
113+
],
114+
}),
115+
116+
discussion_comment: ({ event: { comment, discussion } }) => ({
117+
title: `GitHub 讨论评论:${discussion?.title || '无'}`,
118+
content: [
119+
createContentItem('链接:', comment?.html_url),
120+
createContentItem('作者:', createUserLink(comment!.user!)),
121+
createContentItem('描述:', comment?.body || '无'),
122+
],
123+
}),
124+
125+
release: ({ event: { release } }) => ({
126+
title: `GitHub Release 发布:${release!.name || release!.tag_name}`,
127+
content: [
128+
createContentItem('链接:', release!.html_url),
129+
createContentItem('作者:', createUserLink(release!.author)),
130+
createContentItem('描述:', release!.body!),
131+
],
132+
}),
133+
};
134+
135+
// Main processor
136+
const processEvent = (event: GitHubAction) => {
137+
const { event_name, action } = event;
138+
const actionText = getActionText(action);
139+
const handler = eventHandlers[event_name];
140+
141+
if (!handler) throw new Error(`No handler found for event: ${event_name}`);
142+
143+
try {
144+
return handler(event, actionText);
145+
} catch (cause) {
146+
throw new Error(`Error processing ${event_name} event: ${(cause as Error).message}`, { cause });
147+
}
148+
};
149+
150+
// Main execution:Processing GitHub Events and Outputting Results
151+
const event = JSON.parse((await stdin()) || '{}') as GitHubAction;
152+
const zh_cn = processEvent(event);
153+
154+
if (zh_cn) console.log(JSON.stringify({ post: { zh_cn } }));
155+
else throw new Error(`Unsupported ${event.event_name} event & ${event.action} action`);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Lark notification
2+
3+
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows
4+
on:
5+
push:
6+
issues:
7+
pull_request:
8+
discussion:
9+
issue_comment:
10+
discussion_comment:
11+
release:
12+
types:
13+
- published
14+
15+
jobs:
16+
send-Lark-message:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- uses: denoland/setup-deno@v2
22+
with:
23+
deno-version: v2.x
24+
25+
- name: Event Message serialization
26+
id: message
27+
run: |
28+
YAML=$(echo '${{ toJSON(github) }}' | deno --allow-all .github/scripts/transform-message.ts)
29+
{
30+
echo 'content<<EOF'
31+
echo $YAML
32+
echo 'EOF'
33+
} >> $GITHUB_OUTPUT
34+
35+
- name: Send message to Lark
36+
if: ${{ contains(steps.message.outputs.content, ':') }}
37+
uses: foxundermoon/feishu-action@v2
38+
with:
39+
url: ${{ secrets.LARK_CHATBOT_HOOK_URL }}
40+
msg_type: post
41+
content: |
42+
${{ steps.message.outputs.content }}

package.json

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,28 @@
1717
"@mui/lab": "^7.0.0-beta.16",
1818
"@mui/material": "^7.3.1",
1919
"@mui/material-nextjs": "^7.3.0",
20-
"@sentry/nextjs": "^10.5.0",
20+
"@sentry/nextjs": "^10.8.0",
2121
"file-type": "^21.0.0",
2222
"jsonwebtoken": "^9.0.2",
2323
"koa": "^3.0.1",
2424
"koa-jwt": "^4.0.4",
2525
"koajax": "^3.1.2",
2626
"lodash": "^4.17.21",
27-
"marked": "^16.2.0",
27+
"marked": "^16.2.1",
2828
"mime": "^4.0.7",
2929
"mobx": "^6.13.7",
30-
"mobx-github": "^0.3.11",
30+
"mobx-github": "^0.4.0",
3131
"mobx-i18n": "^0.7.1",
3232
"mobx-lark": "^2.4.0",
3333
"mobx-react": "^9.2.0",
3434
"mobx-react-helper": "^0.5.1",
3535
"mobx-restful": "^2.1.0",
36-
"next": "^15.5.0",
36+
"next": "^15.5.2",
3737
"next-pwa": "~5.6.0",
3838
"next-ssr-middleware": "^1.0.2",
3939
"react": "^19.1.1",
4040
"react-dom": "^19.1.1",
41-
"web-utility": "^4.5.1",
41+
"web-utility": "^4.5.3",
4242
"webpack": "^5.101.3"
4343
},
4444
"devDependencies": {
@@ -47,8 +47,8 @@
4747
"@babel/preset-react": "^7.27.1",
4848
"@cspell/eslint-plugin": "^9.2.0",
4949
"@eslint/compat": "^1.3.2",
50-
"@eslint/js": "^9.33.0",
51-
"@next/eslint-plugin-next": "^15.5.0",
50+
"@eslint/js": "^9.34.0",
51+
"@next/eslint-plugin-next": "^15.5.2",
5252
"@stylistic/eslint-plugin": "^5.2.3",
5353
"@tailwindcss/postcss": "^4.1.12",
5454
"@tailwindcss/typography": "^0.5.16",
@@ -58,14 +58,14 @@
5858
"@types/koa__router": "^12.0.4",
5959
"@types/lodash": "^4.17.20",
6060
"@types/next-pwa": "^5.6.9",
61-
"@types/node": "^22.17.2",
62-
"@types/react": "^19.1.11",
63-
"eslint": "^9.33.0",
64-
"eslint-config-next": "^15.5.0",
61+
"@types/node": "^22.18.0",
62+
"@types/react": "^19.1.12",
63+
"eslint": "^9.34.0",
64+
"eslint-config-next": "^15.5.2",
6565
"eslint-config-prettier": "^10.1.8",
6666
"eslint-plugin-react": "^7.37.5",
6767
"eslint-plugin-simple-import-sort": "^12.1.1",
68-
"get-git-folder": "^0.1.2",
68+
"git-utility": "^0.2.0",
6969
"globals": "^16.3.0",
7070
"husky": "^9.1.7",
7171
"jiti": "^2.5.1",
@@ -76,7 +76,7 @@
7676
"prettier-plugin-tailwindcss": "^0.6.14",
7777
"tailwindcss": "^4.1.12",
7878
"typescript": "~5.9.2",
79-
"typescript-eslint": "^8.40.0"
79+
"typescript-eslint": "^8.41.0"
8080
},
8181
"resolutions": {
8282
"next": "$next"
@@ -105,7 +105,7 @@
105105
},
106106
"scripts": {
107107
"prepare": "husky || true",
108-
"install": "get-git-folder https://github.com/idea2app/key-vault main idea2app.github.io || true",
108+
"install": "xgit download https://github.com/idea2app/key-vault main idea2app.github.io || true",
109109
"dev": "next dev",
110110
"build": "next build",
111111
"export": "next build && next export",

pages/wiki/[node_token].tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Block, renderBlocks, WikiNode } from 'mobx-lark';
22
import { GetStaticPaths, GetStaticProps } from 'next';
33
import { FC } from 'react';
4+
import { Minute, Second } from 'web-utility';
45

56
import { PageHead } from '../../components/PageHead';
67
import documentStore from '../../models/Document';
@@ -25,12 +26,18 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
2526

2627
if (node?.obj_type !== 'docx') return { notFound: true };
2728

28-
const blocks = await documentStore.getOneBlocks(
29-
node.obj_token,
30-
token => `/api/Lark/file/${token}`,
31-
);
29+
try {
30+
const blocks = await documentStore.getOneBlocks(
31+
node.obj_token,
32+
token => `/api/Lark/file/${token}`,
33+
);
3234

33-
return { props: { node, blocks } };
35+
return { props: { node, blocks } };
36+
} catch (error) {
37+
console.error(error);
38+
39+
return { notFound: true, revalidate: Minute / Second };
40+
}
3441
};
3542

3643
interface WikiDocumentPageProps {

0 commit comments

Comments
 (0)