MessageX
MessageX provides provider-native .NET libraries and a PowerShell module for Microsoft Teams, Slack, and Discord. It succeeds the PSTeams and PSDiscord modules.

Release status
The PowerShell Gallery currently contains the historical PSTeams release. The MessageX NuGet packages and the MessageX PowerShell module described below are unpublished release candidates in this repository.
Do not use Install-Module PSTeams as proof that the MessageX code is installed. Nothing in the MessageX package set will be published until the mandatory live-provider, clean-consumer, downstream-pilot, signing, and exact-head release gates in ROADMAP.md pass. The MessageX NuGet packages, MessageX module, tag, and GitHub release will then ship as one coordinated release.
Design
MessageX keeps one reusable C# owner for each capability and leaves PowerShell, ASP.NET Core, and product integrations as thin surfaces:
MessageX.Core owns the product notification data contract, delivery results, durable references, capability flags, errors, bounded provider data, and shared HTTP behavior.
MessageX.Teams owns Teams Workflow payloads and Adaptive Cards with URL actions.
MessageX.Slack owns Slack incoming webhooks, Web API messaging, Block Kit, file upload, and transient interaction responses.
MessageX.Discord owns Discord webhooks, bot REST messaging, embeds, attachments, components, and transient interaction responses.
MessageX.Hosting owns provider-neutral routing, acknowledgement deadlines, deduplication, queues, retries, and durable dispatch contracts.
- Provider hosting packages verify native requests and project them into the shared hosting pipeline.
MessageX.Persistence.DbaClientX persists MessageX domain state through DbaClientX without taking ownership of database-provider behavior.
MessageX.PowerShell exposes compiled cmdlets over the same libraries.
Provider-native rich content stays provider-native. MessageX does not flatten Adaptive Cards, Block Kit, and Discord components into a lowest-common-denominator document model.
Current capability matrix
| Capability |
Teams |
Slack |
Discord |
| Notification send |
Workflow webhook |
Incoming webhook and bot Web API |
Incoming webhook and bot REST |
| Rich content |
Adaptive Cards with Action.OpenUrl |
Sections, headers, context, actions, buttons, modal inputs |
Embeds, attachments, buttons, selects, modal inputs |
| Message lifecycle |
Workflow URLs remain send-only |
Reply, update, delete, reactions |
Reply, read, update, delete, reactions |
| File delivery |
Card images and media links; no channel file upload |
Current external upload workflow |
Multipart attachments |
| Verified HTTP receive |
Teams app activities and card actions |
Events API, commands, actions, views |
Commands, components, autocomplete, modals |
| Interaction continuation |
Verified app activity receipt; bot-owned outbound actions are deferred |
Transient response URL and views.open |
Follow-up, edit, and delete within token lifetime |
| Durable hosting |
Shared queue, replay, retry, dead-letter, health, DbaClientX adapter |
Shared |
Shared |
| Realtime connection |
Deferred |
Socket Mode deferred |
Gateway deferred |
Teams Graph administration and collaboration lifecycle belong in GraphEssentialsX. MessageX does not embed a second Microsoft Graph client.
PowerShell quick start
Credentials should come from environment variables, a secret store, or SecureString; never commit webhook URLs or tokens.
Teams Workflow notification
$target = New-TeamsWebhookTarget `
-Uri $Env:MESSAGEX_TEAMS_WORKFLOW_URL `
-Destination Channel `
-DisplayName 'Release alerts'
$openBuild = New-TeamsAdaptiveOpenUrlAction `
-Title 'Open build' `
-Url 'https://example.com/build/42'
$card = New-TeamsAdaptiveCard -Version '1.2' -Body {
New-TeamsAdaptiveTextBlock -Text 'Build 42 is ready'
} -Actions { $openBuild } -FallbackText 'Build 42 is ready. Open https://example.com/build/42'
$message = New-TeamsMessage -Summary 'Build ready' -AdaptiveCard $card
Send-TeamsMessage -Message $message -Target $target -PassThru
Workflow URLs are send-only capabilities. They do not grant conversation reads, replies, updates, deletes, file upload, or inbound events. Adaptive Card actions in this route are Action.OpenUrl only. The shared product notification contract carries severity, title, explanation, facts, incident ID, report URL, and plain-text fallback; provider rendering will be completed with the card design in the next wave.
Slack Block Kit and file upload
$connection = New-SlackConnection `
-BotToken (Read-Host 'Slack bot token' -AsSecureString) `
-WorkspaceId 'T0123456789'
$target = New-SlackConversationTarget -ConversationId 'C0123456789' -DisplayName 'Release alerts'
$message = New-SlackMessage -Text 'Build 42 is ready' -Blocks @(
New-SlackHeader -Text 'Build approval'
New-SlackSection -Markdown 'Build *42* is ready for review.'
New-SlackActions -Elements @(
New-SlackButton -Text 'Approve' -ActionId 'approve-build' -Value '42' -Style Primary
New-SlackButton -Text 'Reject' -ActionId 'reject-build' -Value '42' -Style Danger
)
)
Send-SlackMessage -Message $message -Target $target -Connection $connection -PassThru
Send-SlackFile -Path .\build.log -ConversationId 'C0123456789' -Connection $connection -PassThru
Send-SlackFile uses files.getUploadURLExternal, the provider upload URL, and files.completeUploadExternal. It does not use the retired files.upload API.
Discord components
$connection = New-DiscordConnection -BotToken (Read-Host 'Discord bot token' -AsSecureString)
$target = New-DiscordChannelTarget -ChannelId '123456789012345678' -GuildId '223456789012345678'
$message = New-DiscordMessage -Content 'Build 42 is ready' -Components @(
New-DiscordActionRow -Components @(
New-DiscordButton -Label 'Approve' -CustomId 'approve-build' -Style Success
New-DiscordButton -Label 'Open build' -Url 'https://example.com/build/42'
)
)
Send-DiscordMessage -Message $message -Target $target -Connection $connection -PassThru
Discord mention parsing defaults to nobody. Opt in with an explicit New-DiscordAllowedMentions policy.
C# example
using MessageX.Slack;
var connection = SlackConnection.ForBotToken(
Environment.GetEnvironmentVariable("SLACK_BOT_TOKEN")!,
workspaceId: "T0123456789");
var message = new SlackMessageRequest { Text = "Deployment completed" };
message.Blocks.Add(new SlackHeaderBlock {
Text = SlackTextObject.Plain("Release")
});
message.Blocks.Add(new SlackSectionBlock {
Text = SlackTextObject.Markdown("Production deployment completed successfully.")
});
using var client = new SlackClient(connection);
var result = await client.SendAsync(
message,
SlackMessageTarget.ForConversation("C0123456789"));
if (!result.IsSuccess) {
throw new InvalidOperationException($"Slack delivery failed: {result.ErrorKind}");
}
Hosting
The ASP.NET Core provider packages expose verified endpoints over the shared MessageX host. The host acknowledges within provider deadlines, bounds synchronous work, deduplicates retries, and can move asynchronous work into durable storage.
Routes that must open a Slack or Discord modal use an explicit inline registration so the handler runs before the provider acknowledgement:
router.OnAction<SlackInteractionEvent>(
"open-approval",
HandleApprovalAsync,
MessageDispatchMode.Synchronous);
Keep ordinary handlers deferred. Synchronous registrations use bounded process-local replay protection and cannot be persisted or replayed after restart.
Transient Slack response URLs, Slack trigger IDs, and Discord interaction tokens never enter durable codecs. A restored durable event intentionally cannot use those short-lived capabilities.
Realtime transports are deliberately outside the first package candidate. Slack Socket Mode and the Discord Gateway need separate reconnect, heartbeat, sequence, resume, and health contracts; HTTP receive remains the supported production path for this preview.
Supported runtimes
MessageX.Core, provider libraries, hosting core, and MessageX.PowerShell: .NET Framework 4.7.2, .NET 8, and .NET 10 where the project contract allows it.
- ASP.NET Core hosting and DbaClientX persistence: .NET 8 and .NET 10.
- PowerShell: Windows PowerShell 5.1 and supported PowerShell 7 runtimes through target-specific binary assets.
Build and test
dotnet restore Sources/MessageX.slnx
dotnet build Sources/MessageX.slnx --configuration Release --no-restore
dotnet test --project Sources/MessageX.Tests/MessageX.Tests.csproj --configuration Release --no-build
NuGet staging is configured by Build/project.build.json. The PowerShell module is built by Build/Build-Module.ps1 through PSPublishModule/PowerForge. Both publish paths are disabled by default.
Compatibility and boundaries
- The
legacy branch preserves the historical script implementation.
- MessageX is a separate module with its own identity. Familiar builders remain useful, but historical PSTeams and PSDiscord parameter compatibility is not a release contract. See MIGRATION.md.
- New code should target typed
MessageX.* libraries and binary cmdlets rather than adding another PowerShell implementation layer.
- Repository renaming and public package publication are separate maintainer decisions; neither is implied by a local release-candidate build.
See ARCHITECTURE.md, SUPPORT.md, and ROADMAP.md for ownership, supported boundaries, and the remaining release gates.
License and support
This project is licensed under the repository LICENSE. Issues and focused pull requests are welcome. Commercial users can support ongoing maintenance through GitHub Sponsors.
评论