diff --git a/scripts/crowdin-stats.mjs b/scripts/crowdin-stats.mjs index fbfe64adf..697a6412b 100644 --- a/scripts/crowdin-stats.mjs +++ b/scripts/crowdin-stats.mjs @@ -206,32 +206,46 @@ function formatSlackMessage(stats, projectName, from, to, generatedAt) { }, }); } else { + // Slack section text is limited to 3000 characters. Keep a safety margin. + const MAX_SECTION_CHARS = 2900; for (const [language, users] of Object.entries(stats)) { const userEntries = Object.entries(users) .filter(([, counts]) => counts.translated > 0 || counts.approved > 0) .sort(([, a], [, b]) => (b.translated + b.approved) - (a.translated + a.approved)); - // Skip this language if no users have activity if (userEntries.length === 0) { continue; } - const userLines = userEntries - .map(([user, counts]) => - `• *${user}*: ${counts.translated} translated, ${counts.approved} approved` - ) - .join('\n'); - const totalTranslated = userEntries.reduce((sum, [, u]) => sum + u.translated, 0); const totalApproved = userEntries.reduce((sum, [, u]) => sum + u.approved, 0); + const header = `*${language}* (Total: ${totalTranslated} translated, ${totalApproved} approved)`; - blocks.push({ - type: 'section', - text: { - type: 'mrkdwn', - text: `*${language}* (Total: ${totalTranslated} translated, ${totalApproved} approved)\n${userLines}`, - }, - }); + // Build bullet lines + const lines = userEntries.map(([user, counts]) => `• *${user}*: ${counts.translated} translated, ${counts.approved} approved`); + + // Chunk lines so each section stays under limit + let current = header; + let buf = []; + + const flush = () => { + const text = buf.length ? `${current}\n${buf.join('\n')}` : current; + blocks.push({ + type: 'section', + text: {type: 'mrkdwn', text}, + }); + buf = []; + current = header; // next chunk keeps the same header for clarity + }; + + for (const line of lines) { + const tentative = buf.length ? `${current}\n${buf.join('\n')}\n${line}` : `${current}\n${line}`; + if (tentative.length > MAX_SECTION_CHARS) { + flush(); + } + buf.push(line); + } + flush(); } } diff --git a/scripts/github-contrib-stats.mjs b/scripts/github-contrib-stats.mjs index 17c920fdb..e5ae20596 100644 --- a/scripts/github-contrib-stats.mjs +++ b/scripts/github-contrib-stats.mjs @@ -197,9 +197,9 @@ function formatSlackMessage(pullRequests, from, to, generatedAt) { }); } else { // Create table header - const tableHeader = '| Created | Merged | Author | Repository | Title | Complexity |\n|---------|--------|--------|------------|-------|------------|'; + const tableHeader = '| # | Created | Merged | Author | Repository | Title | Complexity |\n|---|---------|--------|--------|------------|-------|------------|'; // Create table rows - const tableRows = pullRequests.map((pr) => { + const tableRowLines = pullRequests.map((pr, index) => { // Format created date (when PR was created) const createdDate = new Date(pr.created_at).toLocaleDateString('en-US', { month: 'short', @@ -220,16 +220,36 @@ function formatSlackMessage(pullRequests, from, to, generatedAt) { prTitle = prTitle.substring(0, MAX_TITLE_LENGTH - 1) + '…'; } - return `| ${createdDate} | ${mergedDate} | [${authorName}](${authorUrl}) | ${pr.repository} | [${prTitle}](${pr.html_url}) | |`; - }).join('\n'); - - blocks.push({ - type: 'section', - text: { - type: 'mrkdwn', - text: `\`\`\`\n${tableHeader}\n${tableRows}\n\`\`\``, - }, + return `| ${index + 1} | ${createdDate} | ${mergedDate} | [${authorName}](${authorUrl}) | ${pr.repository} | [${prTitle}](${pr.html_url}) | |`; }); + + // Slack section text has a 3000 character limit. Keep under ~2900 to be safe. + const MAX_SECTION_CHARS = 2900; + let currentLines = []; + let currentLen = tableHeader.length + 6; // include code fence/newlines overhead + + const flushSection = () => { + if (currentLines.length === 0) { + return; + } + const content = `\`\`\`\n${tableHeader}\n${currentLines.join('\n')}\n\`\`\``; + blocks.push({ + type: 'section', + text: {type: 'mrkdwn', text: content}, + }); + currentLines = []; + currentLen = tableHeader.length + 6; + }; + + for (const line of tableRowLines) { + const addLen = line.length + 1; // plus newline + if (currentLen + addLen > MAX_SECTION_CHARS) { + flushSection(); + } + currentLines.push(line); + currentLen += addLen; + } + flushSection(); } blocks.push(