Skip to content

Latest commit

History

202 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

GitHubNugetStatic BadgeGitHub Actions Workflow StatusNuGet version (Mattermost.NET)CodeFactorGitHub repo size

Mattermost.NET

Mattermost.NET is a ready-to-use .NET Standard library for building Mattermost bots and integrations in C#.

It provides a clean, strongly typed wrapper around the Mattermost API, including messages, channels, users, file uploads, post props, and real-time WebSocket events. The client supports token-based authentication, username/password login, automatic WebSocket reconnects, custom HttpClient transport, and configurable incoming message filtering.

For a detailed endpoint map and implementation status, see API coverage.


Installation

Install the package from NuGet:

dotnet add package Mattermost.NET

Quick start

usingMattermost;conststringserver="https://mm.your-server.com";conststringtoken="your-personal-access-token-or-bot-token";conststringchannelId="target-channel-id";usingvarclient=newMattermostClient(server,token);awaitclient.CreatePostAsync(channelId,"Hello from Mattermost.NET!");

Authentication

Use a personal access token or bot token

usingMattermost;conststringserver="https://mm.your-server.com";conststringtoken="your-personal-access-token-or-bot-token";usingvarclient=newMattermostClient(server,token);varme=awaitclient.GetMeAsync();Console.WriteLine($"Authenticated as @{me.Username}");

When a token is provided through the constructor, Mattermost.NET validates and caches the current user on the first authorized API call.

Use username and password

usingMattermost;conststringserver="https://mm.your-server.com";usingvarclient=newMattermostClient(server);varme=awaitclient.LoginAsync("username-or-email","password");Console.WriteLine($"Authenticated as @{me.Username}");

You cannot use LoginAsync on a client that was constructed with an API token.


Receiving real-time events

Call StartReceivingAsync to connect to the Mattermost WebSocket API and receive events.

usingMattermost;conststringserver="https://mm.your-server.com";conststringtoken="your-personal-access-token-or-bot-token";usingvarclient=newMattermostClient(server,token);client.OnConnected+=(_,e)=>{Console.WriteLine($"Connected to {e.Uri}");};client.OnDisconnected+=(_,e)=>{Console.WriteLine($"Disconnected: {e.CloseStatusDescription}");};client.OnLogMessage+=(_,e)=>{Console.WriteLine(e.Message);};client.OnMessageReceived+=(_,e)=>{stringtext=e.Message.Post.Text??string.Empty;if(string.Equals(text,"ping",StringComparison.OrdinalIgnoreCase)){_=e.Client.CreatePostAsync(e.Message.Post.ChannelId,"pong");}};awaitclient.StartReceivingAsync();Console.WriteLine("Bot is running. Press Enter to stop.");Console.ReadLine();awaitclient.StopReceivingAsync();

The client automatically reconnects when the WebSocket connection is lost. You only need StartReceivingAsync when you want to receive WebSocket events; regular REST API calls work without it.


Incoming message filtering

By default, MattermostClient ignores messages authored by the currently authorized user. This prevents common bot loops where a bot reacts to its own posts.

client.Options.IgnoreOwnMessages=true;// default

To receive the bot's own messages too, disable this option:

client.Options.IgnoreOwnMessages=false;

MessageEventArgs.IsCurrentUser tells whether the received message was authored by the currently authorized user.

client.OnMessageReceived+=(_,e)=>{if(e.IsCurrentUser){Console.WriteLine("Received my own message.");}};

You can also provide a custom incoming message filter. Return true to dispatch OnMessageReceived; return false to suppress the event.

client.Options.IncomingMessageFilter= e =>{stringtext=e.Message.Post.Text??string.Empty;returntext.StartsWith("!",StringComparison.Ordinal);};

The custom filter runs after the built-in own-message filter. If you want the custom filter to evaluate own messages, set IgnoreOwnMessages to false.

client.Options.IgnoreOwnMessages=false;client.Options.IncomingMessageFilter= e =>!e.IsCurrentUser;

Using a custom HttpClient

You can pass your own HttpClient when you need custom transport behavior, such as a proxy, timeout, custom handler, logging handler, or IHttpClientFactory integration.

usingMattermost;usingSystem.Net;usingSystem.Net.Http;conststringserver="https://mm.your-server.com";conststringtoken="your-personal-access-token-or-bot-token";varhandler=newHttpClientHandler{Proxy=newWebProxy("http://corp-proxy:8080")};usingvarhttpClient=newHttpClient(handler){Timeout=TimeSpan.FromSeconds(20)};usingvarclient=newMattermostClient(server,token,httpClient);varme=awaitclient.GetMeAsync();Console.WriteLine($"Authenticated as @{me.Username}");

When an external HttpClient is provided, Mattermost.NET uses it only as transport and does not dispose it. The Mattermost server URL still comes from server / serverUri; HttpClient.BaseAddress is not used as the Mattermost server identity.

Mattermost.NET sends authentication per request and does not mutate HttpClient.DefaultRequestHeaders.Authorization. Any default headers configured by the caller remain owned by the caller.

Available constructors:

newMattermostClient();newMattermostClient(stringserverUrl);newMattermostClient(UriserverUri);newMattermostClient(stringserverUrl,stringapiKey);newMattermostClient(UriserverUri,stringapiKey);newMattermostClient(stringserverUrl,HttpClienthttpClient);newMattermostClient(UriserverUri,HttpClienthttpClient);newMattermostClient(stringserverUrl,stringapiKey,HttpClienthttpClient);newMattermostClient(UriserverUri,stringapiKey,HttpClienthttpClient);

Common operations

Send a message

awaitclient.CreatePostAsync(channelId,"Hello, World!");

Send a priority message

usingMattermost.Enums;awaitclient.CreatePostAsync(channelId,"@username Please acknowledge this incident.",priority:MessagePriority.Urgent,requestedAck:true,persistentNotifications:true);

Acknowledgement requests require Important or Urgent priority. Persistent notifications require Urgent priority and may also depend on the Mattermost server license, configuration, and mention rules.

Reply to a thread

awaitclient.CreatePostAsync(channelId:channelId,message:"Thread reply",replyToPostId:rootPostId);

Edit a post

awaitclient.UpdatePostAsync(postId,"Updated message text");

Delete a post

awaitclient.DeletePostAsync(postId);

Upload a file and attach it to a post

varfile=awaitclient.UploadFileAsync(channelId,"report.pdf",stream);awaitclient.CreatePostAsync(channelId:channelId,message:"Uploaded report",files:new[]{file.Id});

Read channel posts

varposts=awaitclient.GetChannelPostsAsync(channelId,perPage:60);foreach(varpostinposts.Posts.Values){Console.WriteLine(post.Text);}

Get current user

varme=awaitclient.GetMeAsync();Console.WriteLine(me.Username);

Find users

varbyId=awaitclient.GetUserAsync(userId);varbyUsername=awaitclient.GetUserByUsernameAsync("username");varbyEmail=awaitclient.GetUserByEmailAsync("user@example.com");

Work with channels

varchannel=awaitclient.GetChannelAsync(channelId);varfound=awaitclient.FindChannelByNameAsync(teamId,"town-square");vardirect=awaitclient.CreateDirectChannelAsync(userId);

Work with calls

boolcallActive=awaitclient.GetCallActiveAsync(channelId);if(callActive){awaitclient.EndCallAsync(channelId);}

These methods require the Mattermost Calls plugin. EndCallAsync expects a channel identifier, despite the route parameter being named call_id by the plugin. Ending a call also requires host permissions.


Post props and attachments

Mattermost.NET supports Mattermost post props, including attachments and interactive action metadata.

usingMattermost.Models.Posts;varprops=newPostProps();props.Attachments.Add(newPostPropsAttachment{Text="Attachment text"});awaitclient.CreatePostAsync(channelId:channelId,message:"Message with props",props:props);

Raw props are also supported when you need to send a custom JSON property bag.

varrawProps=newDictionary<string,object>{["custom_key"]="custom value"};awaitclient.CreatePostWithRawPropsAsync(channelId:channelId,message:"Message with raw props",rawProps:rawProps);

Interactive message buttons and menus

Message actions support Mattermost buttons and select menus.

usingMattermost.Models.Posts;usingSystem.Collections.Generic;varprops=newPostProps();props.Attachments.Add(newPostPropsAttachment{Text="Choose an option",Actions={newPostPropsSelectAction{Id="actionoptions",Name="Select an option...",DefaultOption="opt2",Integration=newIntegration{Url="https://example.com/actionoptions",Context={["action"]="do_something"}},Options=newList<PostActionOption>{newPostActionOption("Option1","opt1"),newPostActionOption("Option2","opt2"),newPostActionOption("Option3","opt3")}}}});awaitclient.CreatePostAsync(channelId,"Message with a select menu",props:props);

For server-populated menus, set DataSource instead of Options:

newPostPropsSelectAction{Id="actionusers",Name="Select a user...",DataSource=PostActionDataSource.Users,Integration=newIntegration{Url="https://example.com/actionusers"}};

When a user clicks a button or selects a menu option, Mattermost sends an HTTP POST request to the action's Integration.Url. Host that URL in your application and deserialize the JSON body with PostActionIntegrationRequest.

app.MapPost("/mattermost/actions",(PostActionIntegrationRequestrequest)=>{stringaction=request.Context["action"].GetString()??string.Empty;returnResults.Ok(new{ephemeral_text=$"Received {action}"});});

Interactive dialogs

Interactive message actions and slash commands can open Mattermost interactive dialogs. Use the action payload's trigger_id, build an InteractiveDialog, and call OpenInteractiveDialogAsync.

usingMattermost;usingMattermost.Models.Dialogs;usingMattermost.Models.Posts;usingSystem.Collections.Generic;app.MapPost("/mattermost/actions",async(PostActionIntegrationRequestrequest,IMattermostClientmattermostClient)=>{InteractiveDialogdialog=newInteractiveDialog{Title="Create ticket",Elements=newList<InteractiveDialogElement>{newInteractiveDialogElement{DisplayName="Summary",Name="summary",Type=InteractiveDialogElementType.Text}}};awaitmattermostClient.OpenInteractiveDialogAsync(request.TriggerId,"https://example.com/mattermost/dialogs/submit",dialog);returnResults.Ok();});

The submit URL is your application endpoint. Deserialize the submitted payload with InteractiveDialogSubmissionRequest and return InteractiveDialogResponse when validation errors or multi-step form updates are needed.

usingMattermost.Models.Dialogs;usingSystem.Collections.Generic;usingSystem.Text.Json;app.MapPost("/mattermost/dialogs/submit",(InteractiveDialogSubmissionRequestrequest)=>{JsonElementsummaryElement;if(!request.Submission.TryGetValue("summary",outsummaryElement)||string.IsNullOrWhiteSpace(summaryElement.GetString())){returnResults.Ok(newInteractiveDialogResponse{Errors=newDictionary<string,string>{["summary"]="Summary is required."}});}returnResults.Ok(newInteractiveDialogResponse{Type="ok"});});

For dynamic selects, set InteractiveDialogElement.DataSource to InteractiveDialogDataSource.Dynamic, set DataSourceUrl, and return InteractiveDialogLookupResponse from that lookup endpoint. For refresh or multi-step flows, set InteractiveDialog.SourceUrl and return InteractiveDialogResponse with Type = "form" and the replacement Form.

See Mattermost's interactive dialogs documentation for the full server-side flow and field behavior.

Custom slash commands

These types help you implement the HTTP endpoint for a custom slash command; they do not register the command in Mattermost. Configure the command's request URL and HTTP method in Mattermost, then use SlashCommandRequestParser to decode the URL-encoded POST body or GET query string and return a SlashCommandResponse as JSON.

For a POST command in an ASP.NET Core minimal API:

usingMattermost.Helpers;usingMattermost.Models.SlashCommands;usingSystem.IO;app.MapPost("/mattermost/commands/weather",async(HttpRequesthttpRequest)=>{usingStreamReaderreader=newStreamReader(httpRequest.Body);stringencodedParameters=awaitreader.ReadToEndAsync();SlashCommandRequestcommand=SlashCommandRequestParser.Parse(encodedParameters);// Validate the command token or Authorization header before processing the request.returnResults.Json(newSlashCommandResponse{ResponseType=SlashCommandResponseType.InChannel,Text=$"Weather request: {command.Text}"});});

For a GET command, pass the raw query string to the same parser:

app.MapGet("/mattermost/commands/weather",(HttpRequesthttpRequest)=>{stringencodedParameters=httpRequest.QueryString.Value??string.Empty;SlashCommandRequestcommand=SlashCommandRequestParser.Parse(encodedParameters);returnResults.Json(newSlashCommandResponse{ResponseType=SlashCommandResponseType.Ephemeral,Text=$"Weather request: {command.Text}"});});

SlashCommandRequest.RootId identifies the parent post when a command is invoked in a thread, while UserMentions and ChannelMentions map names in the command text to Mattermost identifiers. To return multiple immediate posts, add SlashCommandResponseItem values to ExtraResponses:

returnResults.Json(newSlashCommandResponse{ResponseType=SlashCommandResponseType.InChannel,Text="Weather report",ExtraResponses=newList<SlashCommandResponseItem>{newSlashCommandResponseItem{ResponseType=SlashCommandResponseType.Ephemeral,Text="Only the command author can see this detail."}}});

For work that completes after the initial request, send a SlashCommandResponse to the request's ResponseUrl using an application-managed HttpClient:

awaithttpClient.PostAsJsonAsync(command.ResponseUrl,newSlashCommandResponse{ResponseType=SlashCommandResponseType.InChannel,Text="The delayed weather report is ready."});

Validate the command token or authorization header before processing either request method. See Mattermost's custom slash commands documentation for command registration, request validation, and response behavior.


Builders

PostBuilder

usingMattermost.Builders;usingMattermost.Enums;awaitnewPostBuilder().ToChannel(channelId).AddText("@username Please acknowledge this incident.").SetPriority(MessagePriority.Urgent,requestedAck:true,persistentNotifications:true).SendMessageAsync(client);

Markdown table builder

usingMattermost.Builders;usingMattermost.Models.Enums;stringtable=newTableMarkdownBuilder(3,TableAlignment.Center).AddHeader("Name","Status","Score").AddRow("Build","OK",100).AddRow("Tests","OK",100).ToString();awaitclient.CreatePostAsync(channelId,table);

API coverage

The public API is exposed through IMattermostClient and includes:

  • authentication and logout;
  • current user, users by id, username, or email;
  • create, update, delete, read, and list posts;
  • thread posts;
  • channel lookup, creation, archiving, and membership changes;
  • direct and group channels;
  • file upload, download, streaming, and metadata;
  • Calls plugin channel state, active call checks, and host call termination;
  • WebSocket events for messages, status changes, connection changes, and raw events.

See IMattermostClient for the full list of implemented methods.

Missing a Mattermost API method? Please open an issue with the exact Mattermost endpoint or scenario you need:

https://github.com/bvdcode/Mattermost.NET/issues/new?template=Blank+issue


Target framework

Mattermost.NET targets both .NET Standard 2.0 and .NET Standard 2.1.


License

Distributed under the MIT License. See LICENSE.md for more information.

Contact

E-Mail

About

.NET SDK for Mattermost v4 API with WebSocket real-time updates

Resources

Stars

17 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages