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
4 changes: 3 additions & 1 deletion .github/workflows/build-catalog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ on:
workflow_dispatch: # Allow maintainers to rebuild/verify the catalog on demand.

permissions:
contents: write
contents: read
discussions: read

jobs:
Expand All @@ -50,6 +50,8 @@ jobs:
publish:
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.actor != 'github-actions[bot]'
runs-on: ubuntu-latest
permissions:
contents: write # Only the publish job may push the rebuilt catalog.
steps:
- uses: actions/checkout@v4
with:
Expand Down
21 changes: 9 additions & 12 deletions .github/workflows/update-message-center.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ permissions:
contents: write
pull-requests: write

concurrency:
group: update-message-center
cancel-in-progress: false

jobs:
update-message-center:
# Only run on the canonical upstream repo, not on forks.
# Mirrors the guard used by traffic-stats.yml so forks skip cleanly
# instead of failing when repo-specific secrets are unavailable.
if: github.repository == 'microsoft/FastTrack'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

- name: Set up Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: '22'

Expand Down Expand Up @@ -53,23 +58,15 @@ jobs:
git commit -m "chore: weekly message center posts refresh $DATE"
git push --set-upstream origin "$BRANCH"

# Create PR if one doesn't already exist
# Create a normal PR for human review if one doesn't already exist.
existing_pr=$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number // empty')
if [ -n "$existing_pr" ]; then
echo "PR #$existing_pr already exists for $BRANCH"
else
gh pr create \
--title "chore: weekly message center posts refresh $DATE" \
--body "Automated weekly message center data update." \
--body "Automated weekly message center data update. This PR contains externally derived content and requires human review." \
--head "$BRANCH" \
--base master
echo "✅ PR created"
fi

# Enable auto-merge
pr_number=$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number')
if [ -n "$pr_number" ]; then
gh pr merge "$pr_number" --auto --squash \
&& echo "✅ Auto-merge enabled for PR #$pr_number" \
|| echo "::warning::Could not enable auto-merge — check repo settings"
fi
16 changes: 8 additions & 8 deletions scripts/Get-RecurringTeamsMeetings/sample-csv-file.csv
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
UserPrincipalName
CoraT@M365CPI23966391.OnMicrosoft.com
CoreyG@M365CPI23966391.OnMicrosoft.com
DakotaS@M365CPI23966391.OnMicrosoft.com
KaiC@M365CPI23966391.OnMicrosoft.com
HadarC@M365CPI23966391.OnMicrosoft.com
AdilE@M365CPI23966391.OnMicrosoft.com
BillieV@M365CPI23966391.OnMicrosoft.com
admin@m365cpi23966391.onmicrosoft.com
AdeleV@contoso.onmicrosoft.com
AlexW@contoso.onmicrosoft.com
MeganB@contoso.onmicrosoft.com
DiegoS@contoso.onmicrosoft.com
NestorW@contoso.onmicrosoft.com
PradeepG@contoso.onmicrosoft.com
JohannaL@contoso.onmicrosoft.com
admin@contoso.onmicrosoft.com
16 changes: 8 additions & 8 deletions scripts/Get-TeamsUserActivityReport/sample-csv-file.csv
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
UserPrincipalName
CoraT@M365CPI23966391.OnMicrosoft.com
CoreyG@M365CPI23966391.OnMicrosoft.com
DakotaS@M365CPI23966391.OnMicrosoft.com
KaiC@M365CPI23966391.OnMicrosoft.com
HadarC@M365CPI23966391.OnMicrosoft.com
AdilE@M365CPI23966391.OnMicrosoft.com
BillieV@M365CPI23966391.OnMicrosoft.com
admin@m365cpi23966391.onmicrosoft.com
AdeleV@contoso.onmicrosoft.com
AlexW@contoso.onmicrosoft.com
MeganB@contoso.onmicrosoft.com
DiegoS@contoso.onmicrosoft.com
NestorW@contoso.onmicrosoft.com
PradeepG@contoso.onmicrosoft.com
JohannaL@contoso.onmicrosoft.com
admin@contoso.onmicrosoft.com
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
UserPrincipalName,TeamsCallingPolicy,CallingLineIdentity,TeamsMOHPolicy,TeamsCallParkPolicy,TenantDialPlan,TeamsIPPhonePolicy,LocationID,City,OnlineVoiceRoutingPolicy,PhoneNumberToAssign,OperatorType
AllanD@MODERNCOMMS509747.OnMicrosoft.com,TMS_CALL_Recording_PSTN_ON,TMS_CLID_Redmond,TMS_MOH_US_Audio,TMS_CallPark_ON_US_100-300,TMS-DP-US-Redmond,,,Redmond,,+14254190479,CallingPlan
AlexW@MODERNCOMMS509747.OnMicrosoft.com,TMS_CALL_Recording_PSTN_ON,TMS_CLID_Redmond,TMS_MOH_US_Audio,TMS_CallPark_ON_US_100-300,TMS-DP-US-Red,,,,,+14254190485,CallingPlan
BiancaP@ModernComms509747.onmicrosoft.com,TMS_CALL_Recording_PSTN_ON,TMS_CLID_Redmond,TMS_MOH_US_Audio,,TMS-DP-US-Redmond,,701d2c9e-1525-4551-a958-6a9195d35f68,,,+14254190487,CallingPlan
DeliaD@ModernComms509747.onmicrosoft.com,TMS_CALL_Recording_PSTN_ON,TMS_CLID_Redmond,TMS_MOH_US_Audio,,TMS-DP-US-Redmond,,701d2c9e-1525-4551-a958-6a9195d35f68,,,+16784594494,CallingPlan
LeeG@ModernComms509747.onmicrosoft.com,TMS_CALL_Recording_PSTN_ON,,,,TMS-DP-US-Redmond,,,London,,+442045912449,CallingPlan
CAP-US-Redmond-Reception@ModernComms509747.onmicrosoft.com,TMS_CALL_Recording_PSTN_ON,TMS_CLID_Redmond,TMS_MOH_US_Audio,TMS_CallPark_ON_US_100-300,TMS-DP-US-Redmond,TMS_IPPhone_CAP_HotDesk,,Redmond,,+14254190515,CallingPlan
ChristieC@ModernComms509747.onmicrosoft.com,TMS_CALL_Recording_PSTN_ON,TMS_CLID_Redmond,TMS_MOH_US_Audio,TMS_CallPark_ON_US_100-300,TMS-DP-US-Redmond,,,Redmond,VRP-US-Unrestricted,+12345678901,DirectRouting
MegB@ModernComms509747.onmicrosoft.com,TMS_CALL_Recording_PSTN_ON,TMS_CLID_Paris,TMS_MOH_FR_Audio,,TMS-DP-FR-Paris,,,Paris,VRP-FR-Domestic,+33123456789,CallingPlan
AdeleV@contoso.onmicrosoft.com,CONTOSO_CALL_Recording_ON,CONTOSO_CLID_Seattle,CONTOSO_MOH_US_Audio,CONTOSO_CallPark_US_100-300,CONTOSO-DP-US-Seattle,,,Seattle,,+12025550100,CallingPlan
AlexW@contoso.onmicrosoft.com,CONTOSO_CALL_Recording_ON,CONTOSO_CLID_Seattle,CONTOSO_MOH_US_Audio,CONTOSO_CallPark_US_100-300,CONTOSO-DP-US-Seattle,,,,,+12025550101,CallingPlan
MeganB@contoso.onmicrosoft.com,CONTOSO_CALL_Recording_ON,CONTOSO_CLID_Seattle,CONTOSO_MOH_US_Audio,,CONTOSO-DP-US-Seattle,,f9dc9dc2-2752-4b8d-8267-ce0366056b86,,,+12025550102,CallingPlan
DiegoS@contoso.onmicrosoft.com,CONTOSO_CALL_Recording_ON,CONTOSO_CLID_Seattle,CONTOSO_MOH_US_Audio,,CONTOSO-DP-US-Seattle,,3f0198e7-f1f2-43b1-963f-04e7100c02f5,,,+12025550103,CallingPlan
NestorW@contoso.onmicrosoft.com,CONTOSO_CALL_Recording_ON,,,,CONTOSO-DP-US-NewYork,,,New York,,+12025550104,CallingPlan
CAP-US-Seattle-Reception@contoso.onmicrosoft.com,CONTOSO_CALL_Recording_ON,CONTOSO_CLID_Seattle,CONTOSO_MOH_US_Audio,CONTOSO_CallPark_US_100-300,CONTOSO-DP-US-Seattle,CONTOSO_IPPhone_CAP_HotDesk,,Seattle,,+12025550105,CallingPlan
PradeepG@contoso.onmicrosoft.com,CONTOSO_CALL_Recording_ON,CONTOSO_CLID_Seattle,CONTOSO_MOH_US_Audio,CONTOSO_CallPark_US_100-300,CONTOSO-DP-US-Seattle,,,Seattle,CONTOSO-VRP-US-Unrestricted,+12025550106,DirectRouting
JohannaL@contoso.onmicrosoft.com,CONTOSO_CALL_Recording_ON,CONTOSO_CLID_Chicago,CONTOSO_MOH_US_Audio,,CONTOSO-DP-US-Chicago,,,Chicago,CONTOSO-VRP-US-Domestic,+12025550107,CallingPlan
83 changes: 63 additions & 20 deletions scripts/update-message-center.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,53 @@ function fetchJson(url) {
});
}

function sanitiseRawPost(raw) {
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;

const string = (value, maxLength = 10000) =>
typeof value === 'string' ? value.slice(0, maxLength) : '';
const stringArray = (value, maxItems = 50, maxLength = 200) =>
Array.isArray(value)
? value
.filter((item) => typeof item === 'string')
.slice(0, maxItems)
.map((item) => item.slice(0, maxLength))
: [];
const dateString = (value) => {
if (typeof value !== 'string' || !Number.isFinite(Date.parse(value))) return null;
return value;
};

const post = {
Id: string(raw.Id, 100),
Title: string(raw.Title, 500),
Category: string(raw.Category, 100),
Severity: string(raw.Severity, 50),
StartDateTime: dateString(raw.StartDateTime),
ActionRequiredByDateTime: dateString(raw.ActionRequiredByDateTime),
Services: stringArray(raw.Services),
Tags: stringArray(raw.Tags),
Details: Array.isArray(raw.Details)
? raw.Details
.filter((detail) => detail && typeof detail === 'object')
.slice(0, 50)
.map((detail) => ({
Name: string(detail.Name, 100),
Value: string(detail.Value),
}))
: [],
Body: {
Content:
raw.Body && typeof raw.Body === 'object'
? string(raw.Body.Content, 50000)
: '',
},
IsMajorChange: raw.IsMajorChange === true,
};

return post.Id && post.Title && post.StartDateTime ? post : null;
}

// ── category mapping ─────────────────────────────────────────────────────────
// merill/mc uses camelCase categories; we map to display names.

Expand Down Expand Up @@ -424,31 +471,28 @@ function isCopilotAgentRelevant(raw) {
// ── serialise a post to inline JS ────────────────────────────────────────────

function serialisePost(p) {
const esc = (s) => s.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\r?\n/g, ' ').replace(/\s{2,}/g, ' ');
const arr = (a) => '[' + a.map((v) => `'${esc(v)}'`).join(', ') + ']';
const parts = [
`id: '${p.id}'`,
`title: '${esc(p.title)}'`,
`category: '${p.category}'`,
`severity: '${p.severity}'`,
`datePublished: '${p.datePublished}'`,
`actionRequiredBy: ${p.actionRequiredBy ? `'${p.actionRequiredBy}'` : 'null'}`,
`status: '${p.status}'`,
`services: ${arr(p.services)}`,
`summary: '${esc(p.summary)}'`,
`tags: ${arr(p.tags)}`,
`isHighImpact: ${p.isHighImpact}`,
`agentAudience: ${arr(p.agentAudience)}`,
];
return ` { ${parts.join(', ')} }`;
return (
' ' +
JSON.stringify(p)
.replace(/</g, '\\u003c')
.replace(/>/g, '\\u003e')
.replace(/&/g, '\\u0026')
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029')
);
}

// ── main ─────────────────────────────────────────────────────────────────────

async function main() {
console.log('⏳ Fetching message center posts from merill/mc...');
const rawPosts = await fetchJson(MC_DATA_URL);
console.log(` Fetched ${rawPosts.length} total posts from archive`);
const fetchedPosts = await fetchJson(MC_DATA_URL);
if (!Array.isArray(fetchedPosts)) {
throw new Error('Message Center archive response must be an array');
}
const rawPosts = fetchedPosts.map(sanitiseRawPost).filter(Boolean);
console.log(` Fetched ${fetchedPosts.length} total posts from archive`);
console.log(` ${rawPosts.length} posts passed schema validation`);

const today = new Date();
const windowStart = addDays(today, -60);
Expand Down Expand Up @@ -548,4 +592,3 @@ main().catch((err) => {
console.error('❌ Failed to update message center posts:', err.message);
process.exit(1);
});

Loading