Skip to content

使用form上传文件流的,选了一个文件,然后编辑了本地文件,然后在点提交就会报错 #5846

使用form上传文件流的,选了一个文件,然后编辑了本地文件,然后在点提交就会报错

使用form上传文件流的,选了一个文件,然后编辑了本地文件,然后在点提交就会报错 #5846

name: Issue Spam Filter
on:
issues:
types: [opened, edited]
jobs:
spam-check:
runs-on: ubuntu-latest
steps:
- name: Run spam check script
uses: actions/github-script@v7
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const issue = context.payload.issue;
if (!issue) return;
// load config from repo (fallback defaults if missing)
let config = {
dry_run: true,
min_account_age_days: 3,
max_urls_for_spam: 1,
min_body_len_for_links: 40,
spam_words: [
"call now","zadzwoń","zadzwoń teraz","kontakt","telefon","telefone","contato",
"suporte","infolinii","click here","buy now","subscribe","visit"
],
bracket_max: 6,
special_char_density_threshold: 0.12,
phone_regex: "\\+?\\d[\\d\\-\\s\\(\\)\\.]{6,}\\d",
labels_for_spam: ["spam"],
labels_for_review: ["needs-triage"]
};
try {
const cfg = await github.rest.repos.getContent({ owner, repo, path: ".github/issue-spam-config.json" });
const raw = Buffer.from(cfg.data.content, "base64").toString();
config = Object.assign(config, JSON.parse(raw));
} catch (e) {
// allow missing config file — use defaults
}
// Ensure labels exist
async function ensureLabel(name, color, description) {
try {
await github.rest.issues.getLabel({ owner, repo, name });
} catch (e) {
if (e.status === 404) {
try {
await github.rest.issues.createLabel({ owner, repo, name, color, description });
} catch (e2) {
// ignore concurrent create errors
}
}
}
}
for (const lb of config.labels_for_spam) {
await ensureLabel(lb, "B60205", "Auto-marked as spam by workflow");
}
for (const lb of config.labels_for_review) {
await ensureLabel(lb, "FBCA04", "Needs triage");
}
const title = (issue.title || "").toLowerCase();
const body = (issue.body || "").toLowerCase();
const combined = title + "\n" + body;
// helpers
const countMatches = (text, re) => ((text.match(re) || []).length);
const urlRegex = /https?:\/\/\S+/g;
const urlCount = countMatches(combined, urlRegex);
const bodyLenNoSpace = combined.replace(/\s+/g, '').length;
const spamWordsHit = config.spam_words.some(w => combined.includes(w));
const bracketCount = countMatches(title, /[\{\}\[\]\<\>\|\~\^\_]/g) + countMatches(body, /[\{\}\[\]\<\>\|\~\^\_]/g);
const specialChars = combined.match(/[^a-z0-9\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af\s]/gi) || [];
const specialCharDensity = specialChars.length / Math.max(1, combined.length);
const phoneRe = new RegExp(config.phone_regex, "g");
const phoneCount = countMatches(combined, phoneRe);
// account age
let accountAgeDays = 3650;
try {
if (issue.user && issue.user.login) {
const u = await github.rest.users.getByUsername({ username: issue.user.login });
accountAgeDays = (Date.now() - new Date(u.data.created_at)) / (1000*60*60*24);
}
} catch (e) {
// ignore user fetch errors
}
// Decision logic (tunable)
let isSpam = false;
let reasons = [];
if (urlCount > config.max_urls_for_spam) {
isSpam = true;
reasons.push(`包含 ${urlCount} 个链接`);
}
if (urlCount >= 1 && accountAgeDays < config.min_account_age_days) {
isSpam = true;
reasons.push(`账号创建 ${Math.floor(accountAgeDays)} 天且包含链接`);
}
if (spamWordsHit) {
isSpam = true;
reasons.push("命中垃圾关键词");
}
if (bodyLenNoSpace < config.min_body_len_for_links && urlCount >= 1) {
isSpam = true;
reasons.push("正文过短且含链接");
}
if (bracketCount >= config.bracket_max) {
isSpam = true;
reasons.push(`标题/正文中存在大量特殊括号 (${bracketCount})`);
}
if (specialCharDensity >= config.special_char_density_threshold && combined.length < 200) {
isSpam = true;
reasons.push(`特殊字符密度高 (${(specialCharDensity*100).toFixed(1)}%)`);
}
if (phoneCount >= 1 && bracketCount >= 2) {
isSpam = true;
reasons.push("包含电话号码模式且带有异常符号格式");
}
// Apply actions
const issue_number = issue.number;
if (isSpam) {
try {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: config.labels_for_spam });
} catch (e) { /* ignore */ }
const commentBody = `该 issue 被自动判定为可能的垃圾(${reasons.join(',')})。` +
(config.dry_run ? "当前处于 dry-run 模式,仅已打标签并留言;如需自动关闭,请在配置中关闭 dry_run。" :
"已自动关闭。如误判请联系维护者。");
await github.rest.issues.createComment({ owner, repo, issue_number, body: commentBody });
if (!config.dry_run) {
await github.rest.issues.update({ owner, repo, issue_number, state: "closed" });
}
return;
}
try {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: config.labels_for_review });
} catch (e) { /* ignore */ }