Skip to content

Commit 5afaf4a

Browse files
committed
[migrate] upgrade Lark notification to Interactive Card
1 parent 4825d1a commit 5afaf4a

File tree

2 files changed

+186
-122
lines changed

2 files changed

+186
-122
lines changed

.github/scripts/transform-message.ts

Lines changed: 175 additions & 116 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,148 +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 = (
36-
label: string,
37-
value?: string | { tag: string; text: string },
38-
) =>
39-
[
40-
createText(label),
41-
typeof value === 'string'
42-
? createText(value || '无')
43-
: value || createText('无'),
44-
] 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+
}
4579

4680
type EventHandler = (
4781
event: GitHubAction,
4882
actionText: string,
4983
) => {
5084
title: string;
51-
content: [object, object][];
85+
elements: LarkMessageElement[];
5286
};
5387

5488
// Event handlers
5589
const eventHandlers: Record<string, EventHandler> = {
56-
push: ({
57-
event: { head_commit },
58-
ref,
59-
ref_name,
60-
server_url,
61-
repository,
62-
actor,
63-
}) => ({
64-
title: 'GitHub 代码提交',
65-
content: [
66-
[createText('提交链接:'), createLink(head_commit!.url)],
67-
[
68-
createText('代码分支:'),
69-
createLink(`${server_url}/${repository}/tree/${ref_name}`, ref),
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+
},
70109
],
71-
[createText('提交作者:'), createLink(`${server_url}/${actor}`, actor)],
72-
[createText('提交信息:'), createText(head_commit!.message)],
73-
],
74-
}),
110+
};
111+
},
75112

76113
issues: ({ event: { issue } }, actionText) => ({
77114
title: `GitHub issue ${actionText}${issue?.title}`,
78-
content: [
79-
[createText('链接:'), createLink(issue!.html_url)],
80-
[
81-
createText('作者:'),
82-
createLink(issue!.user!.html_url!, issue!.user!.login),
83-
],
84-
[
85-
createText('指派:'),
86-
issue?.assignee
87-
? createLink(issue.assignee.html_url!, issue.assignee.login)
88-
: createText('无'),
89-
],
90-
[
91-
createText('标签:'),
92-
createText(issue?.labels?.map(({ name }) => name).join(', ') || '无'),
93-
],
94-
[createText('里程碑:'), createText(issue?.milestone?.title || '无')],
95-
[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+
},
96127
],
97128
}),
98129

99130
pull_request: ({ event: { pull_request } }, actionText) => ({
100131
title: `GitHub PR ${actionText}${pull_request?.title}`,
101-
content: [
102-
[createText('链接:'), createLink(pull_request!.html_url)],
103-
[
104-
createText('作者:'),
105-
createLink(pull_request!.user.html_url, pull_request!.user.login),
106-
],
107-
[
108-
createText('指派:'),
109-
pull_request?.assignee
110-
? createLink(
111-
pull_request.assignee.html_url,
112-
pull_request.assignee.login,
113-
)
114-
: createText('无'),
115-
],
116-
[
117-
createText('标签:'),
118-
createText(
119-
pull_request?.labels?.map(({ name }) => name).join(', ') || '无',
120-
),
121-
],
122-
[
123-
createText('里程碑:'),
124-
createText(pull_request?.milestone?.title || '无'),
125-
],
126-
[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+
},
127150
],
128151
}),
129152

130153
discussion: ({ event: { discussion } }, actionText) => ({
131154
title: `GitHub 讨论 ${actionText}${discussion?.title || '无'}`,
132-
content: [
133-
createContentItem('链接:', discussion?.html_url),
134-
createContentItem(
135-
'作者:',
136-
createUserLink(discussion!.user as GitHubUser),
137-
),
138-
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+
},
139164
],
140165
}),
141166

142167
issue_comment: ({ event: { comment, issue } }) => ({
143168
title: `GitHub issue 评论:${issue?.title || '未知 issue'}`,
144-
content: [
145-
createContentItem('链接:', comment?.html_url),
146-
createContentItem('作者:', createUserLink(comment!.user!)),
147-
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+
},
148178
],
149179
}),
150180

151181
discussion_comment: ({ event: { comment, discussion } }) => ({
152182
title: `GitHub 讨论评论:${discussion?.title || '无'}`,
153-
content: [
154-
createContentItem('链接:', comment?.html_url),
155-
createContentItem('作者:', createUserLink(comment!.user!)),
156-
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+
},
157192
],
158193
}),
159194

160195
release: ({ event: { release } }) => ({
161196
title: `GitHub Release 发布:${release!.name || release!.tag_name}`,
162-
content: [
163-
createContentItem('链接:', release!.html_url),
164-
createContentItem('作者:', createUserLink(release!.author)),
165-
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+
},
166221
],
167222
}),
168223
};
@@ -178,19 +233,23 @@ const processEvent = (event: GitHubAction) => {
178233
try {
179234
return handler(event, actionText);
180235
} catch (cause) {
181-
throw new Error(
182-
`Error processing ${event_name} event: ${(cause as Error).message}`,
183-
{ cause },
184-
);
236+
throw new Error(`Error processing ${event_name} event: ${(cause as Error).message}`, { cause });
185237
}
186238
};
187239

188-
// Main execution:Processing GitHub Events and Outputting Results
240+
// Main execution
189241
const event = JSON.parse((await stdin()) || '{}') as GitHubAction;
190-
const zh_cn = processEvent(event);
191-
192-
if (zh_cn) console.log(JSON.stringify({ post: { zh_cn } }));
193-
else
194-
throw new Error(
195-
`Unsupported ${event.event_name} event & ${event.action} action`,
196-
);
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@v5
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)