From 2d7d68dcabc6c0fb0215eaa991792bf18968569c Mon Sep 17 00:00:00 2001 From: Evereasy Date: Fri, 29 Aug 2025 15:47:09 +0200 Subject: [PATCH 1/7] Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 100.0% (405 of 405 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/zh_Hant/ --- _locales/zh_Hant/messages.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/_locales/zh_Hant/messages.json b/_locales/zh_Hant/messages.json index 011ab5d0..693be535 100644 --- a/_locales/zh_Hant/messages.json +++ b/_locales/zh_Hant/messages.json @@ -788,7 +788,7 @@ "message": "ThunderAI 選項中定義的預設簽章。" }, "prefs_OptionText_add_tags_auto_force_existing": { - "message": "自動標記或快顯功能選單時強制使用現有標記" + "message": "強制使用現有標記" }, "prefs_OptionText_add_tags_auto_Info2": { "message": "在此頁面底部選擇要啟動此功能的帳戶。" @@ -1063,7 +1063,7 @@ "message": "垃圾郵件閾值太低!您可能會將太多郵件標記為垃圾郵件!" }, "prefs_OptionText_add_tags_auto_force_existing_Info": { - "message": "如果勾選,AI 將僅對新收到的電子郵件新增現有標籤,而不會建立新標籤。" + "message": "如果勾選,AI 將僅新增現有標籤,而不會建立新標籤。" }, "prefs_OptionText_CustomGPT_Warn": { "message": "如果在選項或提示中指定了專案,它將覆蓋自訂 GPT 設定。" @@ -1232,5 +1232,14 @@ }, "prompt_reply_custom_command_full_text": { "message": "請回覆以下電子郵件 \"{%mail_text_body%}”。{%additional_text%}。僅回覆所需內容,不要提供任何註解或其他文字。" + }, + "prefs_OptionText_chatgpt_web_br_replace_info": { + "message": "請注意,AI 回應中的任何
標籤將被替換為換行。" + }, + "prefs_OpenAIComp_ClearModelsList": { + "message": "清除模型清單" + }, + "OpenAIComp_ClearModelsList_Confirm": { + "message": "確定要清除模型清單嗎?這個動作無法復原。" } } From a981654ec885db91c694c3d73dbd5eb16dd9e83b Mon Sep 17 00:00:00 2001 From: Mic Date: Mon, 1 Sep 2025 22:27:51 +0200 Subject: [PATCH 2/7] Add workflow to automatically label closed issues as "released" when needed --- .github/workflows/auto-mark-released.yml | 86 ++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/auto-mark-released.yml diff --git a/.github/workflows/auto-mark-released.yml b/.github/workflows/auto-mark-released.yml new file mode 100644 index 00000000..08efb3ab --- /dev/null +++ b/.github/workflows/auto-mark-released.yml @@ -0,0 +1,86 @@ +name: Mark released issues on close + +on: + issues: + types: [closed] + +permissions: + contents: read + issues: write + +jobs: + mark-released: + # Run only if closed as completed AND has at least one "status:" label + if: ${{ github.event.issue.state_reason == 'completed' && contains(toJSON(github.event.issue.labels), 'status:') }} + runs-on: ubuntu-latest + steps: + - name: Set "released" label when closed completed and was ready for release + uses: actions/github-script@v7 + with: + script: | + // Helpers + const owner = context.repo.owner; + const repo = context.repo.repo; + const issue = context.payload.issue; + if (!issue) { + core.info('No issue payload found, skipping.'); + return; + } + + // Only proceed if closed as "completed" (not "not_planned") + const isCompleted = issue.state_reason === 'completed'; + if (!isCompleted) { + core.info('Issue state_reason is not "completed"; skipping.'); + return; + } + + const getName = (l) => typeof l === 'string' ? l : l.name; + const labels = (issue.labels || []).map(getName); + + // Status detection (robust to case/spacing) + const isStatusLabel = (name) => /^status:\s*/i.test(String(name || '')); + const hasAnyStatus = labels.some(isStatusLabel); + if (!hasAnyStatus) { + core.info('Issue has no "status:*" label; skipping.'); + return; + } + + const READY_REGEX = /^status:\s*ready\s*for\s*release$/i; + const hasReady = labels.some((n) => READY_REGEX.test(String(n || ''))); + if (!hasReady) { + core.info('Issue is not "status: ready for release"; nothing to do.'); + return; + } + + const releasedLabel = 'status: released'; + + // Ensure "released" label exists + try { + await github.rest.issues.getLabel({ owner, repo, name: releasedLabel }); + } catch (e) { + if (e.status === 404) { + core.info(`Label "${releasedLabel}" not found. Creating it.`); + await github.rest.issues.createLabel({ + owner, + repo, + name: releasedLabel, + color: '184738', // deep green from your palette + description: 'Feature released.' + }); + } else { + throw e; + } + } + + // Remove all status:* labels, add "status: released" + const filtered = labels.filter((name) => !isStatusLabel(name)); + const newLabels = [...new Set([...filtered, releasedLabel])]; + + await github.rest.issues.update({ + owner, + repo, + issue_number: issue.number, + labels: newLabels + }); + + core.info(`Issue #${issue.number} relabeled as "${releasedLabel}".`); From 1cbdd3c485c5cbfb6cf41badb6e35119f11146ec Mon Sep 17 00:00:00 2001 From: mic Date: Wed, 3 Sep 2025 22:28:40 +0200 Subject: [PATCH 3/7] now adding the release comment automatically --- .github/workflows/auto-mark-released.yml | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/auto-mark-released.yml b/.github/workflows/auto-mark-released.yml index 08efb3ab..9e75695e 100644 --- a/.github/workflows/auto-mark-released.yml +++ b/.github/workflows/auto-mark-released.yml @@ -64,7 +64,7 @@ jobs: owner, repo, name: releasedLabel, - color: '184738', // deep green from your palette + color: '184738', // deep green description: 'Feature released.' }); } else { @@ -84,3 +84,34 @@ jobs: }); core.info(`Issue #${issue.number} relabeled as "${releasedLabel}".`); + + // === NEW: add release comment with milestone if present === + const milestone = issue.milestone; + if (!milestone || !milestone.title) { + core.info('Issue has no milestone; no release comment added.'); + return; + } + + const commentBody = `Released in version ${milestone.title}.`; + + // Avoid duplicate comment if the action reruns + const existingComments = await github.paginate( + github.rest.issues.listComments, + { owner, repo, issue_number: issue.number, per_page: 100 } + ); + + const alreadyCommented = existingComments.some(c => + typeof c.body === 'string' && c.body.trim() === commentBody + ); + + if (alreadyCommented) { + core.info('Release comment already present; skipping comment creation.'); + } else { + await github.rest.issues.createComment({ + owner, + repo, + issue_number: issue.number, + body: commentBody + }); + core.info('Release comment added.'); + } From 9895b6f8414e5ae2a4e27ed763af43e1f2718157 Mon Sep 17 00:00:00 2001 From: mic Date: Fri, 12 Sep 2025 22:26:18 +0200 Subject: [PATCH 4/7] prerelease comment on issue workflow added --- .../workflows/prerelease_comment_issue.yml | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .github/workflows/prerelease_comment_issue.yml diff --git a/.github/workflows/prerelease_comment_issue.yml b/.github/workflows/prerelease_comment_issue.yml new file mode 100644 index 00000000..c61d4fda --- /dev/null +++ b/.github/workflows/prerelease_comment_issue.yml @@ -0,0 +1,102 @@ +name: pre-release comment issues + +on: + release: + types: [published, edited] + # opzionale: test manuale + workflow_dispatch: {} + +permissions: + contents: read + issues: write + +jobs: + comment-milestone-issues: + # consenti il run manuale per test; in produzione resta solo prerelease + if: ${{ github.event.release.prerelease == true }} + runs-on: ubuntu-latest + steps: + - name: Debug context (no checkout) + run: | + echo "Event: ${{ github.event_name }}" + echo "Default branch: ${{ github.event.repository.default_branch }}" + echo "Release tag: ${{ github.event.release.tag_name || 'N/A' }}" + echo "This workflow file lives on MAIN and is executed as-is (no checkout)." + + - name: Add comment to issues of matching milestone (filtered by labels) + uses: actions/github-script@v7 + with: + script: | + // core, github, context are already available in github-script + await (async () => { + const parsePrereleaseTag = (tag) => { + const m = /^v?(\d+\.\d+\.\d+)pre(\d+)$/.exec(tag ?? ""); + return m ? { milestoneTitle: m[1], preNumber: Number(m[2]) } : null; + }; + + const { owner, repo } = context.repo; + + // If this is a manual run, exit with a clear message + if (context.eventName !== 'release') { + core.notice("Manual run: no release payload, nothing to do."); + return; + } + + const tagName = context.payload.release?.tag_name; + if (!tagName) { core.notice("No tag_name found on release event."); return; } + + const parsed = parsePrereleaseTag(tagName); + if (!parsed) { core.notice(`Tag '${tagName}' does not match vX.Y.ZpreN. Skipping.`); return; } + + const milestoneTitle = parsed.milestoneTitle; // es. "3.7.0" + const prereleaseTagDisplay = tagName.startsWith("v") ? tagName : `v${tagName}`; + const commentBody = `This feature could be tested in pre-release ${prereleaseTagDisplay}.`; + + core.info(`(MAIN) Looking for milestone '${milestoneTitle}' in ${owner}/${repo}`); + + // Find milestone (open or closed) + const milestones = await github.paginate( + github.rest.issues.listMilestones, + { owner, repo, state: "all", per_page: 100 } + ); + const milestone = milestones.find(m => m.title === milestoneTitle); + if (!milestone) { core.warning(`Milestone '${milestoneTitle}' not found.`); return; } + + // All issues in the milestone + const issues = await github.paginate( + github.rest.issues.listForRepo, + { owner, repo, milestone: milestone.number, state: "all", per_page: 100 } + ); + + // Only issues (no PRs) with the requested labels + const allowed = new Set(["status: in review", "status: ready for release"]); + const targets = issues.filter(i => + !i.pull_request && + Array.isArray(i.labels) && + i.labels.some(l => allowed.has(l.name)) + ); + + core.info(`Candidate issues: ${targets.length}`); + + let commented = 0; + for (const issue of targets) { + const comments = await github.paginate( + github.rest.issues.listComments, + { owner, repo, issue_number: issue.number, per_page: 100 } + ); + + const exists = comments.some(c => (c.body || "").trim() === commentBody); + if (exists) { + core.info(`#${issue.number}: comment already present, skip.`); + continue; + } + + await github.rest.issues.createComment({ + owner, repo, issue_number: issue.number, body: commentBody + }); + commented++; + core.info(`#${issue.number}: comment added.`); + } + + core.info(`Done. Added comment to ${commented} issue(s).`); + })(); \ No newline at end of file From 229809fd1d76c1dfd6319b20a8ea8bda70cf0603 Mon Sep 17 00:00:00 2001 From: Mic Date: Tue, 16 Sep 2025 19:36:00 +0200 Subject: [PATCH 5/7] added a wf to add a comment on a bug issue without the integration reported --- .github/workflows/issues-validate-integration-field.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .github/workflows/issues-validate-integration-field.yml diff --git a/.github/workflows/issues-validate-integration-field.yml b/.github/workflows/issues-validate-integration-field.yml new file mode 100644 index 00000000..e69de29b From f87e481b12d8c6d1f98cf2a004684a1e6a78a17f Mon Sep 17 00:00:00 2001 From: Mic Date: Wed, 17 Sep 2025 23:18:23 +0200 Subject: [PATCH 6/7] added a wf to add a comment on a bug issue without the integration reported --- .../issues-validate-integration-field.yml | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/.github/workflows/issues-validate-integration-field.yml b/.github/workflows/issues-validate-integration-field.yml index e69de29b..83f2153a 100644 --- a/.github/workflows/issues-validate-integration-field.yml +++ b/.github/workflows/issues-validate-integration-field.yml @@ -0,0 +1,75 @@ +name: Validate Integration Field + +on: + issues: + types: [opened, edited] + +permissions: + issues: write + +jobs: + check-integration: + runs-on: ubuntu-latest + steps: + - name: Validate dropdown selection (only for bug report form) + uses: actions/github-script@v7 + with: + script: | + const issue = context.payload.issue; + const bodyText = issue.body || ""; + + // ---- Guard: only act if this looks like a Bug Report form ---- + if (!bodyText.includes("### The problem")) { + core.info("Skipped: This issue does not contain '### The problem', so it is not a Bug Report."); + return; + } + core.info("Detected a Bug Report issue. Proceeding with validation..."); + + // Extract selection under "Which integration are you using?" + const header = "### Which integration are you using?"; + const nextHeaderRegex = /\n### /; + let selection = ""; + + const startIdx = bodyText.indexOf(header); + if (startIdx !== -1) { + const afterHeader = bodyText.slice(startIdx + header.length).replace(/^\r?\n+/, ""); + const nextHeaderIdx = afterHeader.search(nextHeaderRegex); + selection = (nextHeaderIdx === -1 ? afterHeader : afterHeader.slice(0, nextHeaderIdx)).trim(); + } + + core.info(`Integration field extracted: "${selection}"`); + + const isInvalid = !selection || selection.startsWith("-- Select an option"); + if (isInvalid) { + core.info("Integration field is invalid or not selected."); + } else { + core.info("Integration field is valid. Nothing to do."); + } + + const owner = context.repo.owner; + const repo = context.repo.repo; + const issue_number = issue.number; + + // Avoid duplicate comments + const marker = "please specify **Which integration are you using?**"; + const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number }); + const alreadyCommented = comments.some(c => c.body && c.body.includes(marker)); + + if (isInvalid) { + if (!alreadyCommented) { + core.info("No existing reminder found. Posting a new comment..."); + await github.rest.issues.createComment({ + owner, repo, issue_number, + body: [ + `👋 Hello @${issue.user.login}!`, + `To complete your bug report, please specify which integration you are using.`, + "", + "Right now the field `**Which integration are you using?**` is set to `-- Select an option --`.", + "", + "Click **Edit** at the top right of the issue, choose a valid option from the dropdown, and save.", + ].join("\n") + }); + } else { + core.info("Reminder comment already exists. Skipping new comment."); + } + } From a2fe88e12a7435597a97e49069010419a76c1da2 Mon Sep 17 00:00:00 2001 From: Mic Date: Wed, 17 Sep 2025 23:19:52 +0200 Subject: [PATCH 7/7] wf renamed --- .github/workflows/issues-validate-integration-field.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issues-validate-integration-field.yml b/.github/workflows/issues-validate-integration-field.yml index 83f2153a..70682fa9 100644 --- a/.github/workflows/issues-validate-integration-field.yml +++ b/.github/workflows/issues-validate-integration-field.yml @@ -1,4 +1,4 @@ -name: Validate Integration Field +name: Issues Validate Integration Field on: issues: