Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
291 changes: 175 additions & 116 deletions .github/scripts/transform-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ type GitHubSchema = components['schemas'];

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

interface GitHubAction
extends Record<'event_name' | 'actor' | 'server_url' | 'repository', string> {
interface GitHubAction extends Record<
'event_name' | 'actor' | 'server_url' | 'repository',
string
> {
action?: string;
ref?: string;
ref_name?: string;
Expand All @@ -21,148 +23,201 @@ interface GitHubAction
}

// Helper functions
const getActionText = (action?: string) =>
action === 'closed' ? '关闭' : action?.includes('open') ? '打开' : '编辑';

const createLink = (href: string, text = href) => ({ tag: 'a', href, text });

const createText = (text: string) => ({ tag: 'text', text });

// create user link
const createUserLink = (user: GitHubUser) =>
user ? createLink(user.html_url, user.login) : createText('无');
const ACTION_TEXT_MAP: Record<string, string> = {
created: '创建',
opened: '创建',
submitted: '创建',
closed: '关闭',
reopened: '重新打开',
labeled: '添加标签',
unlabeled: '移除标签',
assigned: '指派',
unassigned: '取消指派',
edited: '编辑',
deleted: '删除',
synchronize: '更新',
review_requested: '请求审核',
};

const createContentItem = (
label: string,
value?: string | { tag: string; text: string },
) =>
[
createText(label),
typeof value === 'string'
? createText(value || '无')
: value || createText('无'),
] as [object, object];
const getActionText = (action?: string) => (action ? ACTION_TEXT_MAP[action] || action : '编辑');

const createLink = (href: string, text = href) => `[${text}](${href})`;

const createUserLink = (user: any) => (user ? createLink(user.html_url, user.login) : '无');

// Convert GitHub markdown to Lark card supported format
const sanitizeMarkdown = (text: string): string =>
text
// Remove code blocks
.replace(/```[\s\S]*?```/g, '[代码块]')
// Remove inline code
.replace(/`[^`]+`/g, match => match.slice(1, -1))
// Convert images to link text
.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '🖼️ [$1]($2)')
// Convert ### headers to bold
.replace(/^###\s+(.+)$/gm, '**$1**')
// Convert ## headers to bold
.replace(/^##\s+(.+)$/gm, '**$1**')
// Convert # headers to bold
.replace(/^#\s+(.+)$/gm, '**$1**')
// Remove HTML comments
.replace(/<!--[\s\S]*?-->/g, '')
// Remove HTML tags (keep content)
.replace(/<[^>]+>/g, '')
// Remove excess empty lines
.replace(/\n{3,}/g, '\n\n')
// Truncate long content
.slice(0, 800) + (text.length > 800 ? '\n...' : '');

const createContentItem = (label: string, value?: string) =>
`**${label}** ${value ? sanitizeMarkdown(value) : '无'}`;

interface LarkMessageElement {
tag: string;
content: string | [object, object][];
}

type EventHandler = (
event: GitHubAction,
actionText: string,
) => {
title: string;
content: [object, object][];
elements: LarkMessageElement[];
};

// Event handlers
const eventHandlers: Record<string, EventHandler> = {
push: ({
event: { head_commit },
ref,
ref_name,
server_url,
repository,
actor,
}) => ({
title: 'GitHub 代码提交',
content: [
[createText('提交链接:'), createLink(head_commit!.url)],
[
createText('代码分支:'),
createLink(`${server_url}/${repository}/tree/${ref_name}`, ref),
push: ({ event: { head_commit }, ref, ref_name, server_url, repository, actor }) => {
const commitUrl = head_commit?.url || `${server_url}/${repository}/tree/${ref_name}`;
const commitMessage = head_commit?.message || 'Create/Delete/Update Branch (No head commit)';

return {
title: 'GitHub 代码提交',
elements: [
{
tag: 'markdown',
content: [
createContentItem('提交链接:', createLink(commitUrl)),
createContentItem(
'代码分支:',
createLink(`${server_url}/${repository}/tree/${ref_name}`, ref_name),
),
createContentItem('提交作者:', createLink(`${server_url}/${actor}`, actor)),
createContentItem('提交信息:', commitMessage),
].join('\n'),
},
],
[createText('提交作者:'), createLink(`${server_url}/${actor}`, actor)],
[createText('提交信息:'), createText(head_commit!.message)],
],
}),
};
},

issues: ({ event: { issue } }, actionText) => ({
title: `GitHub issue ${actionText}:${issue?.title}`,
content: [
[createText('链接:'), createLink(issue!.html_url)],
[
createText('作者:'),
createLink(issue!.user!.html_url!, issue!.user!.login),
],
[
createText('指派:'),
issue?.assignee
? createLink(issue.assignee.html_url!, issue.assignee.login)
: createText('无'),
],
[
createText('标签:'),
createText(issue?.labels?.map(({ name }) => name).join(', ') || '无'),
],
[createText('里程碑:'), createText(issue?.milestone?.title || '无')],
[createText('描述:'), createText(issue?.body || '无')],
elements: [
{
tag: 'markdown',
content: [
createContentItem('链接:', createLink(issue!.html_url)),
createContentItem('作者:', createUserLink(issue!.user!)),
createContentItem('指派:', issue?.assignee ? createUserLink(issue.assignee) : '无'),
createContentItem('标签:', issue?.labels?.map(({ name }) => name).join(', ') || '无'),
createContentItem('里程碑:', issue?.milestone?.title || '无'),
createContentItem('描述:', issue?.body || '无'),
].join('\n'),
},
],
}),

pull_request: ({ event: { pull_request } }, actionText) => ({
title: `GitHub PR ${actionText}:${pull_request?.title}`,
content: [
[createText('链接:'), createLink(pull_request!.html_url)],
[
createText('作者:'),
createLink(pull_request!.user.html_url, pull_request!.user.login),
],
[
createText('指派:'),
pull_request?.assignee
? createLink(
pull_request.assignee.html_url,
pull_request.assignee.login,
)
: createText('无'),
],
[
createText('标签:'),
createText(
pull_request?.labels?.map(({ name }) => name).join(', ') || '无',
),
],
[
createText('里程碑:'),
createText(pull_request?.milestone?.title || '无'),
],
[createText('描述:'), createText(pull_request?.body || '无')],
elements: [
{
tag: 'markdown',
content: [
createContentItem('链接:', createLink(pull_request!.html_url)),
createContentItem('作者:', createUserLink(pull_request!.user)),
createContentItem(
'指派:',
pull_request?.assignee ? createUserLink(pull_request.assignee) : '无',
),
createContentItem(
'标签:',
pull_request?.labels?.map(({ name }) => name).join(', ') || '无',
),
createContentItem('里程碑:', pull_request?.milestone?.title || '无'),
createContentItem('描述:', pull_request?.body || '无'),
].join('\n'),
},
],
}),

discussion: ({ event: { discussion } }, actionText) => ({
title: `GitHub 讨论 ${actionText}:${discussion?.title || '无'}`,
content: [
createContentItem('链接:', discussion?.html_url),
createContentItem(
'作者:',
createUserLink(discussion!.user as GitHubUser),
),
createContentItem('描述:', discussion?.body || '无'),
elements: [
{
tag: 'markdown',
content: [
createContentItem('链接:', createLink(discussion!.html_url)),
createContentItem('作者:', createUserLink(discussion!.user as GitHubUser)),
createContentItem('描述:', discussion?.body || '无'),
].join('\n'),
},
],
}),

issue_comment: ({ event: { comment, issue } }) => ({
title: `GitHub issue 评论:${issue?.title || '未知 issue'}`,
content: [
createContentItem('链接:', comment?.html_url),
createContentItem('作者:', createUserLink(comment!.user!)),
createContentItem('描述:', comment?.body || '无'),
elements: [
{
tag: 'markdown',
content: [
createContentItem('链接:', createLink(comment!.html_url)),
createContentItem('作者:', createUserLink(comment!.user!)),
createContentItem('描述:', comment?.body || '无'),
].join('\n'),
},
],
}),

discussion_comment: ({ event: { comment, discussion } }) => ({
title: `GitHub 讨论评论:${discussion?.title || '无'}`,
content: [
createContentItem('链接:', comment?.html_url),
createContentItem('作者:', createUserLink(comment!.user!)),
createContentItem('描述:', comment?.body || '无'),
elements: [
{
tag: 'markdown',
content: [
createContentItem('链接:', createLink(comment!.html_url)),
createContentItem('作者:', createUserLink(comment!.user!)),
createContentItem('描述:', comment?.body || '无'),
].join('\n'),
},
],
}),

release: ({ event: { release } }) => ({
title: `GitHub Release 发布:${release!.name || release!.tag_name}`,
content: [
createContentItem('链接:', release!.html_url),
createContentItem('作者:', createUserLink(release!.author)),
createContentItem('描述:', release!.body!),
elements: [
{
tag: 'markdown',
content: [
createContentItem('链接:', createLink(release!.html_url)),
createContentItem('作者:', createUserLink(release!.author)),
createContentItem('描述:', release?.body || '无'),
].join('\n'),
},
],
}),

pull_request_review_comment: ({ event: { comment, pull_request } }) => ({
title: `GitHub PR 代码评论:${pull_request?.title || '未知 PR'}`,
elements: [
{
tag: 'markdown',
content: [
createContentItem('链接:', createLink(comment!.html_url)),
createContentItem('作者:', createUserLink(comment!.user!)),
createContentItem('PR:', createLink(pull_request!.html_url, `#${pull_request!.number}`)),
createContentItem('评论:', comment?.body || '无'),
].join('\n'),
},
],
}),
};
Expand All @@ -178,19 +233,23 @@ const processEvent = (event: GitHubAction) => {
try {
return handler(event, actionText);
} catch (cause) {
throw new Error(
`Error processing ${event_name} event: ${(cause as Error).message}`,
{ cause },
);
throw new Error(`Error processing ${event_name} event: ${(cause as Error).message}`, { cause });
}
};

// Main execution:Processing GitHub Events and Outputting Results
// Main execution
const event = JSON.parse((await stdin()) || '{}') as GitHubAction;
const zh_cn = processEvent(event);

if (zh_cn) console.log(JSON.stringify({ post: { zh_cn } }));
else
throw new Error(
`Unsupported ${event.event_name} event & ${event.action} action`,
);
const result = processEvent(event);

if (!result) throw new Error(`Unsupported ${event.event_name} event & ${event.action} action`);

const card = {
schema: '2.0',
config: { wide_screen_mode: true },
header: {
title: { tag: 'plain_text', content: result.title },
template: 'blue',
},
body: { elements: result.elements },
};
console.log(JSON.stringify(card));
17 changes: 11 additions & 6 deletions .github/workflows/Lark-notification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
discussion:
issue_comment:
discussion_comment:
pull_request_review_comment:
release:
types:
- published
Expand All @@ -16,7 +17,7 @@ jobs:
send-Lark-message:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- uses: denoland/setup-deno@v2
with:
Expand All @@ -25,18 +26,22 @@ jobs:
- name: Event Message serialization
id: message
run: |
YAML=$(echo '${{ toJSON(github) }}' | deno --allow-all .github/scripts/transform-message.ts)
YAML=$(
cat <<JSON | deno run --allow-all .github/scripts/transform-message.ts
${{ toJSON(github) }}
JSON
)
{
echo 'content<<EOF'
echo $YAML
echo "$YAML"
echo 'EOF'
} >> $GITHUB_OUTPUT
} >> "$GITHUB_OUTPUT"

- name: Send message to Lark
if: ${{ contains(steps.message.outputs.content, ':') }}
uses: foxundermoon/feishu-action@v2
uses: Open-Source-Bazaar/feishu-action@v3
with:
url: ${{ secrets.LARK_CHATBOT_HOOK_URL }}
msg_type: post
msg_type: interactive
content: |
${{ steps.message.outputs.content }}
Loading
Loading