Skip to content

Commit 8e7616c

Browse files
authored
Merge branch 'main' into memory/cron
2 parents 541d8a1 + 14053c9 commit 8e7616c

205 files changed

Lines changed: 14146 additions & 3475 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
name: Issue Welcome Bot
2+
3+
on:
4+
issues:
5+
types: [opened]
6+
7+
permissions:
8+
issues: write
9+
10+
jobs:
11+
welcome:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Welcome Issue Author
15+
uses: actions/github-script@v7
16+
with:
17+
script: |
18+
// Helper function for English ordinal suffix (1st, 2nd, 3rd, 4th...)
19+
function getOrdinalSuffix(num) {
20+
const j = num % 10;
21+
const k = num % 100;
22+
if (j === 1 && k !== 11) return 'st';
23+
if (j === 2 && k !== 12) return 'nd';
24+
if (j === 3 && k !== 13) return 'rd';
25+
return 'th';
26+
}
27+
28+
const author = context.payload.issue.user.login;
29+
const issueNumber = context.payload.issue.number;
30+
const issueTitle = context.payload.issue.title;
31+
const issueBody = context.payload.issue.body || '';
32+
33+
// Check if Chinese characters exist in title
34+
const hasChinese = /[\u4e00-\u9fff]/.test(issueTitle);
35+
36+
// Count user's total issues in this repo using search API
37+
let issueCount = 0;
38+
let hasValidIssueCount = false;
39+
let isFirstIssue = false;
40+
41+
try {
42+
const { data: searchResult } = await github.rest.search.issuesAndPullRequests({
43+
q: `repo:${context.repo.owner}/${context.repo.repo} type:issue author:${author}`,
44+
});
45+
issueCount = searchResult.total_count;
46+
hasValidIssueCount = true;
47+
isFirstIssue = issueCount === 1;
48+
} catch (err) {
49+
console.log(`Could not get issue count for ${author}, will skip count display`);
50+
hasValidIssueCount = false;
51+
}
52+
53+
// Check if user has starred the repo by listing user's starred
54+
// repos
55+
let hasStarred = false;
56+
try {
57+
const fullRepoName = `${context.repo.owner}/${context.repo.repo}`;
58+
let page = 1;
59+
let found = false;
60+
61+
while (page <= 10 && !found) {
62+
const { data: starredRepos } = await github.rest.activity.listReposStarredByUser({
63+
username: author,
64+
per_page: 100,
65+
page: page
66+
});
67+
68+
if (starredRepos.length === 0) break;
69+
70+
found = starredRepos.some(repo => repo.full_name === fullRepoName);
71+
if (found) {
72+
hasStarred = true;
73+
console.log(`User ${author} has starred the repo`);
74+
break;
75+
}
76+
77+
page++;
78+
}
79+
80+
if (!found) {
81+
console.log(`User ${author} has not starred the repo`);
82+
}
83+
} catch (error) {
84+
console.log(
85+
`Could not verify star status for ${author}:`,
86+
error.message
87+
);
88+
}
89+
90+
// Check if this is a bug report by title or label
91+
// Match "bug", "bugs", or "[Bug]:" prefix (case-insensitive)
92+
const titleContainsBug = /\bbug(s)?\b/i.test(issueTitle) || /^\s*\[bug\]\s*:/i.test(issueTitle);
93+
const labels = context.payload.issue.labels.map(label => label.name);
94+
const hasBugLabel = labels.some(name => name && name.toLowerCase() === 'bug');
95+
const isBugRelated = titleContainsBug || hasBugLabel;
96+
97+
// Check if issue uses bug report template
98+
const usedBugTemplate = issueBody.includes('## CoPaw Version') ||
99+
issueBody.includes('## Environment') ||
100+
issueBody.includes('## Steps to Reproduce');
101+
102+
// If bug-related but template not used, need to remind
103+
const needBugTemplateReminder = isBugRelated && !usedBugTemplate;
104+
105+
// Build comment
106+
let comment = '';
107+
108+
if (hasChinese) {
109+
// Bilingual response with logo
110+
comment = `<div align="center">\n\n`;
111+
comment += `<img src="https://copaw.agentscope.io/copaw_ip.svg" width="80" height="80" />\n\n`;
112+
comment += `## 欢迎来到 CoPaw! 🐾 Welcome to CoPaw!\n\n`;
113+
comment += `</div>\n\n`;
114+
115+
if (hasValidIssueCount) {
116+
if (isFirstIssue) {
117+
comment += `你好 @${author},感谢你提交的第一个 issue!\n`;
118+
comment += `Hi @${author}, thank you for your first issue!\n\n`;
119+
} else {
120+
comment += `你好 @${author},这是你提交的第 ${issueCount} 个 issue。\n`;
121+
comment += `Hi @${author}, this is your ${issueCount}${getOrdinalSuffix(issueCount)} issue.\n\n`;
122+
}
123+
} else {
124+
comment += `你好 @${author},感谢你提交 issue!\n`;
125+
comment += `Hi @${author}, thank you for your issue!\n\n`;
126+
}
127+
128+
if (needBugTemplateReminder) {
129+
comment += `### 📋 关于 Bug Report 模板 / About Bug Report Template\n\n`;
130+
comment += `我们注意到你的 issue 似乎与 bug 相关,但没有使用 Bug Report 模板。为了帮助我们更快地定位和修复问题,请确保你的 bug report 包含以下信息:\n`;
131+
comment += `We noticed your issue seems to be bug-related, but doesn't use the Bug Report template. To help us reproduce and fix the issue faster, please make sure your bug report includes:\n\n`;
132+
comment += `- ✅ **CoPaw 版本 / Version** (使用 \`copaw --version\` 查看)\n`;
133+
comment += `- ✅ **操作系统 / OS** (macOS/Linux/Windows 及版本)\n`;
134+
comment += `- ✅ **复现步骤 / Steps to Reproduce** (详细的步骤)\n`;
135+
comment += `- ✅ **实际结果 vs 预期结果 / Actual vs Expected**\n`;
136+
comment += `- ✅ **日志或截图 / Logs or Screenshots**\n\n`;
137+
comment += `你可以编辑 issue 来补充这些信息。如果你的 issue 缺少这些信息,维护者可能需要额外时间来询问细节。\n`;
138+
comment += `You can edit your issue to add this information. Missing information may require maintainers to ask follow-up questions.\n\n`;
139+
}
140+
141+
comment += `我们会尽快查看你的 issue。感谢你对 CoPaw 的支持!\n`;
142+
comment += `We'll review your issue soon. Thank you for supporting CoPaw!\n\n`;
143+
144+
// Internationalization reminder
145+
comment += `---\n\n`;
146+
comment += `> **🌍 关于国际化 / About Internationalization**\n`;
147+
comment += `> \n`;
148+
comment += `> CoPaw 是一个国际化的开源社区。我们建议使用英文提交 issue,这样可以让更多的开发者参与讨论和贡献。\n`;
149+
comment += `> \n`;
150+
comment += `> CoPaw is an international open-source community. We recommend using English for issues so that more developers worldwide can participate in discussions and contributions.\n\n`;
151+
152+
// Star reminder at the end
153+
if (!hasStarred) {
154+
comment += `> [!TIP]\n`;
155+
comment += `> **⭐ 如果你觉得 CoPaw 有帮助,请给我们一个 Star!**\n`;
156+
comment += `> \n`;
157+
comment += `> **⭐ If you find CoPaw useful, please give us a Star!**\n`;
158+
comment += `> \n`;
159+
comment += `> 你的 star 将帮助更多开发者发现这个项目!🐾\n`;
160+
comment += `> \n`;
161+
comment += `> Your star helps more developers discover this project! 🐾`;
162+
}
163+
164+
} else {
165+
// English only with logo
166+
comment = `<div align="center">\n\n`;
167+
comment += `<img src="https://copaw.agentscope.io/copaw_ip.svg" width="80" height="80" />\n\n`;
168+
comment += `## Welcome to CoPaw! 🐾\n\n`;
169+
comment += `</div>\n\n`;
170+
171+
if (hasValidIssueCount) {
172+
if (isFirstIssue) {
173+
comment += `Hi @${author}, thank you for your first issue!\n\n`;
174+
} else {
175+
comment += `Hi @${author}, this is your ${issueCount}${getOrdinalSuffix(issueCount)} issue.\n\n`;
176+
}
177+
} else {
178+
comment += `Hi @${author}, thank you for your issue!\n\n`;
179+
}
180+
181+
if (needBugTemplateReminder) {
182+
comment += `### 📋 About Bug Report Template\n\n`;
183+
comment += `We noticed your issue seems to be bug-related, but doesn't use the Bug Report template. To help us reproduce and fix the issue faster, please make sure your bug report includes:\n\n`;
184+
comment += `- ✅ **CoPaw Version** (use \`copaw --version\`)\n`;
185+
comment += `- ✅ **Operating System** (macOS/Linux/Windows and version)\n`;
186+
comment += `- ✅ **Steps to Reproduce** (detailed steps)\n`;
187+
comment += `- ✅ **Actual vs Expected Behavior**\n`;
188+
comment += `- ✅ **Logs or Screenshots**\n\n`;
189+
comment += `You can edit your issue to add this information. Missing information may require us to ask follow-up questions, which can delay the fix.\n\n`;
190+
}
191+
192+
comment += `We'll review your issue soon. Thank you for supporting CoPaw!\n\n`;
193+
194+
// Star reminder at the end
195+
if (!hasStarred) {
196+
comment += `---\n\n`;
197+
comment += `> [!TIP]\n`;
198+
comment += `> **⭐ If you find CoPaw useful, please give us a Star!**\n`;
199+
comment += `> \n`;
200+
comment += `> Your star helps more developers discover this project! 🐾`;
201+
}
202+
}
203+
204+
// Post comment
205+
await github.rest.issues.createComment({
206+
owner: context.repo.owner,
207+
repo: context.repo.repo,
208+
issue_number: issueNumber,
209+
body: comment
210+
});
211+
212+
const logMessage = hasValidIssueCount
213+
? `Posted welcome comment on issue #${issueNumber} for @${author} (issue #${issueCount})`
214+
: `Posted welcome comment on issue #${issueNumber} for @${author}`;
215+
console.log(logMessage);

.github/workflows/pr-label.yml

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)