Skip to content

Commit e8078fb

Browse files
committed
[migrate] upgrade Lark notification to Interactive Card
1 parent 2e28a72 commit e8078fb

File tree

2 files changed

+186
-81
lines changed

2 files changed

+186
-81
lines changed

.github/scripts/transform-message.ts

Lines changed: 175 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ type GitHubSchema = components['schemas'];
55

66
type GitHubUser = GitHubSchema['simple-user'];
77

8-
interface GitHubAction
9-
extends Record<'event_name' | 'actor' | 'server_url' | 'repository', string> {
8+
interface GitHubAction extends Record<
9+
'event_name' | 'actor' | 'server_url' | 'repository',
10+
string
11+
> {
1012
action?: string;
1113
ref?: string;
1214
ref_name?: string;
@@ -21,113 +23,201 @@ interface GitHubAction
2123
}
2224

2325
// 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('无');
26+
const ACTION_TEXT_MAP: Record<string, string> = {
27+
created: '创建',
28+
opened: '创建',
29+
submitted: '创建',
30+
closed: '关闭',
31+
reopened: '重新打开',
32+
labeled: '添加标签',
33+
unlabeled: '移除标签',
34+
assigned: '指派',
35+
unassigned: '取消指派',
36+
edited: '编辑',
37+
deleted: '删除',
38+
synchronize: '更新',
39+
review_requested: '请求审核',
40+
};
3441

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];
42+
const getActionText = (action?: string) => (action ? ACTION_TEXT_MAP[action] || action : '编辑');
43+
44+
const createLink = (href: string, text = href) => `[${text}](${href})`;
45+
46+
const createUserLink = (user: any) => (user ? createLink(user.html_url, user.login) : '无');
47+
48+
// Convert GitHub markdown to Lark card supported format
49+
const sanitizeMarkdown = (text: string): string =>
50+
text
51+
// Remove code blocks
52+
.replace(/```[\s\S]*?```/g, '[代码块]')
53+
// Remove inline code
54+
.replace(/`[^`]+`/g, match => match.slice(1, -1))
55+
// Convert images to link text
56+
.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '🖼️ [$1]($2)')
57+
// Convert ### headers to bold
58+
.replace(/^###\s+(.+)$/gm, '**$1**')
59+
// Convert ## headers to bold
60+
.replace(/^##\s+(.+)$/gm, '**$1**')
61+
// Convert # headers to bold
62+
.replace(/^#\s+(.+)$/gm, '**$1**')
63+
// Remove HTML comments
64+
.replace(/<!--[\s\S]*?-->/g, '')
65+
// Remove HTML tags (keep content)
66+
.replace(/<[^>]+>/g, '')
67+
// Remove excess empty lines
68+
.replace(/\n{3,}/g, '\n\n')
69+
// Truncate long content
70+
.slice(0, 800) + (text.length > 800 ? '\n...' : '');
71+
72+
const createContentItem = (label: string, value?: string) =>
73+
`**${label}** ${value ? sanitizeMarkdown(value) : '无'}`;
74+
75+
interface LarkMessageElement {
76+
tag: string;
77+
content: string | [object, object][];
78+
}
4079

4180
type EventHandler = (
4281
event: GitHubAction,
4382
actionText: string,
4483
) => {
4584
title: string;
46-
content: [object, object][];
85+
elements: LarkMessageElement[];
4786
};
4887

4988
// Event handlers
5089
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-
}),
90+
push: ({ event: { head_commit }, ref, ref_name, server_url, repository, actor }) => {
91+
const commitUrl = head_commit?.url || `${server_url}/${repository}/tree/${ref_name}`;
92+
const commitMessage = head_commit?.message || 'Create/Delete/Update Branch (No head commit)';
93+
94+
return {
95+
title: 'GitHub 代码提交',
96+
elements: [
97+
{
98+
tag: 'markdown',
99+
content: [
100+
createContentItem('提交链接:', createLink(commitUrl)),
101+
createContentItem(
102+
'代码分支:',
103+
createLink(`${server_url}/${repository}/tree/${ref_name}`, ref_name),
104+
),
105+
createContentItem('提交作者:', createLink(`${server_url}/${actor}`, actor)),
106+
createContentItem('提交信息:', commitMessage),
107+
].join('\n'),
108+
},
109+
],
110+
};
111+
},
60112

61113
issues: ({ event: { issue } }, actionText) => ({
62114
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 || '无')],
115+
elements: [
116+
{
117+
tag: 'markdown',
118+
content: [
119+
createContentItem('链接:', createLink(issue!.html_url)),
120+
createContentItem('作者:', createUserLink(issue!.user!)),
121+
createContentItem('指派:', issue?.assignee ? createUserLink(issue.assignee) : '无'),
122+
createContentItem('标签:', issue?.labels?.map(({ name }) => name).join(', ') || '无'),
123+
createContentItem('里程碑:', issue?.milestone?.title || '无'),
124+
createContentItem('描述:', issue?.body || '无'),
125+
].join('\n'),
126+
},
75127
],
76128
}),
77129

78130
pull_request: ({ event: { pull_request } }, actionText) => ({
79131
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 || '无')],
132+
elements: [
133+
{
134+
tag: 'markdown',
135+
content: [
136+
createContentItem('链接:', createLink(pull_request!.html_url)),
137+
createContentItem('作者:', createUserLink(pull_request!.user)),
138+
createContentItem(
139+
'指派:',
140+
pull_request?.assignee ? createUserLink(pull_request.assignee) : '无',
141+
),
142+
createContentItem(
143+
'标签:',
144+
pull_request?.labels?.map(({ name }) => name).join(', ') || '无',
145+
),
146+
createContentItem('里程碑:', pull_request?.milestone?.title || '无'),
147+
createContentItem('描述:', pull_request?.body || '无'),
148+
].join('\n'),
149+
},
95150
],
96151
}),
97152

98153
discussion: ({ event: { discussion } }, actionText) => ({
99154
title: `GitHub 讨论 ${actionText}${discussion?.title || '无'}`,
100-
content: [
101-
createContentItem('链接:', discussion?.html_url),
102-
createContentItem('作者:', createUserLink(discussion!.user as GitHubUser)),
103-
createContentItem('描述:', discussion?.body || '无'),
155+
elements: [
156+
{
157+
tag: 'markdown',
158+
content: [
159+
createContentItem('链接:', createLink(discussion!.html_url)),
160+
createContentItem('作者:', createUserLink(discussion!.user as GitHubUser)),
161+
createContentItem('描述:', discussion?.body || '无'),
162+
].join('\n'),
163+
},
104164
],
105165
}),
106166

107167
issue_comment: ({ event: { comment, issue } }) => ({
108168
title: `GitHub issue 评论:${issue?.title || '未知 issue'}`,
109-
content: [
110-
createContentItem('链接:', comment?.html_url),
111-
createContentItem('作者:', createUserLink(comment!.user!)),
112-
createContentItem('描述:', comment?.body || '无'),
169+
elements: [
170+
{
171+
tag: 'markdown',
172+
content: [
173+
createContentItem('链接:', createLink(comment!.html_url)),
174+
createContentItem('作者:', createUserLink(comment!.user!)),
175+
createContentItem('描述:', comment?.body || '无'),
176+
].join('\n'),
177+
},
113178
],
114179
}),
115180

116181
discussion_comment: ({ event: { comment, discussion } }) => ({
117182
title: `GitHub 讨论评论:${discussion?.title || '无'}`,
118-
content: [
119-
createContentItem('链接:', comment?.html_url),
120-
createContentItem('作者:', createUserLink(comment!.user!)),
121-
createContentItem('描述:', comment?.body || '无'),
183+
elements: [
184+
{
185+
tag: 'markdown',
186+
content: [
187+
createContentItem('链接:', createLink(comment!.html_url)),
188+
createContentItem('作者:', createUserLink(comment!.user!)),
189+
createContentItem('描述:', comment?.body || '无'),
190+
].join('\n'),
191+
},
122192
],
123193
}),
124194

125195
release: ({ event: { release } }) => ({
126196
title: `GitHub Release 发布:${release!.name || release!.tag_name}`,
127-
content: [
128-
createContentItem('链接:', release!.html_url),
129-
createContentItem('作者:', createUserLink(release!.author)),
130-
createContentItem('描述:', release!.body!),
197+
elements: [
198+
{
199+
tag: 'markdown',
200+
content: [
201+
createContentItem('链接:', createLink(release!.html_url)),
202+
createContentItem('作者:', createUserLink(release!.author)),
203+
createContentItem('描述:', release?.body || '无'),
204+
].join('\n'),
205+
},
206+
],
207+
}),
208+
209+
pull_request_review_comment: ({ event: { comment, pull_request } }) => ({
210+
title: `GitHub PR 代码评论:${pull_request?.title || '未知 PR'}`,
211+
elements: [
212+
{
213+
tag: 'markdown',
214+
content: [
215+
createContentItem('链接:', createLink(comment!.html_url)),
216+
createContentItem('作者:', createUserLink(comment!.user!)),
217+
createContentItem('PR:', createLink(pull_request!.html_url, `#${pull_request!.number}`)),
218+
createContentItem('评论:', comment?.body || '无'),
219+
].join('\n'),
220+
},
131221
],
132222
}),
133223
};
@@ -147,9 +237,19 @@ const processEvent = (event: GitHubAction) => {
147237
}
148238
};
149239

150-
// Main execution:Processing GitHub Events and Outputting Results
240+
// Main execution
151241
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`);
242+
const result = processEvent(event);
243+
244+
if (!result) throw new Error(`Unsupported ${event.event_name} event & ${event.action} action`);
245+
246+
const card = {
247+
schema: '2.0',
248+
config: { wide_screen_mode: true },
249+
header: {
250+
title: { tag: 'plain_text', content: result.title },
251+
template: 'blue',
252+
},
253+
body: { elements: result.elements },
254+
};
255+
console.log(JSON.stringify(card));

.github/workflows/Lark-notification.yml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on:
88
discussion:
99
issue_comment:
1010
discussion_comment:
11+
pull_request_review_comment:
1112
release:
1213
types:
1314
- published
@@ -16,7 +17,7 @@ jobs:
1617
send-Lark-message:
1718
runs-on: ubuntu-latest
1819
steps:
19-
- uses: actions/checkout@v4
20+
- uses: actions/checkout@v6
2021

2122
- uses: denoland/setup-deno@v2
2223
with:
@@ -25,18 +26,22 @@ jobs:
2526
- name: Event Message serialization
2627
id: message
2728
run: |
28-
YAML=$(echo '${{ toJSON(github) }}' | deno --allow-all .github/scripts/transform-message.ts)
29+
YAML=$(
30+
cat <<JSON | deno run --allow-all .github/scripts/transform-message.ts
31+
${{ toJSON(github) }}
32+
JSON
33+
)
2934
{
3035
echo 'content<<EOF'
31-
echo $YAML
36+
echo "$YAML"
3237
echo 'EOF'
33-
} >> $GITHUB_OUTPUT
38+
} >> "$GITHUB_OUTPUT"
3439
3540
- name: Send message to Lark
3641
if: ${{ contains(steps.message.outputs.content, ':') }}
37-
uses: foxundermoon/feishu-action@v2
42+
uses: Open-Source-Bazaar/feishu-action@v3
3843
with:
3944
url: ${{ secrets.LARK_CHATBOT_HOOK_URL }}
40-
msg_type: post
45+
msg_type: interactive
4146
content: |
4247
${{ steps.message.outputs.content }}

0 commit comments

Comments
 (0)