Automating Posts to Mastodon

Saving time with automatic posts to Mastodon is fairly simple with their API

Automating Posts to Mastodon

A couple years ago, I built an integration to automatically publish my posts to X, after all of the API changes and Zapier's integration stopped working. I recently did the same thing for Mastodon. It turns out that Mastodon makes things a lot easier!

Create a Mastodon Application

Before we can publish a post, we need to create a new application. You can do that by logging in, going to preferences, and clicking <> Development on the left-hand side.

Mastodon Preferences

Then you click the New Application button, give it a name, and select the scopes it will need. I went with the default read, write, and follow

After creating the application, you'll want to make note of the access token, which we need to send with our API request

Create a Service

Once the application is created, we can build a service to interact with the API. As usual, I did this with C# & .Net

Since there are multiple servers that can be used and hosted, you'll need the URL of your host. This and the access token from before can be stored in a configuration

public class MastodonSettings
{
    public string HostUrl { get; set; } = string.Empty;
    public string AccessToken { get; set; } = string.Empty;
}

Those settings and an HttpClient can then be injected into a service where you send MultipartFormData to the Mastodon API

public class MastodonService : ISocialMediaService<MastodonService>
{
    private readonly HttpClient _httpClient;
    
    public MastodonService(
        IOptions<MastodonSettings> options,
        HttpClient httpClient
    )
    {
        var settings = options.Value ?? throw new ArgumentNullException(nameof(options));
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        
        _httpClient.DefaultRequestHeaders.Accept
            .Add(new MediaTypeWithQualityHeaderValue("application/json"));
        _httpClient.BaseAddress = new Uri(settings.HostUrl);
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {settings.AccessToken}");
    }
    
    /// <inheritdoc />
    public async Task<StatusResult> PublishStatus(string status)
    {
        var postData = new MultipartFormDataContent();
        postData.Add(new StringContent(status), "status");
        
        var response = await _httpClient.PostAsync("api/v1/statuses", postData);
        
        var responseBody = await response.Content.ReadAsStringAsync();
        if (!response.IsSuccessStatusCode)
            throw new ApplicationException($"Error publishing Mastodon status: {response.StatusCode} :: {responseBody}");
        
        var typedResponse = JsonSerializer.Deserialize<StatusResult>(responseBody);
        if (typedResponse == null)
            throw new ApplicationException($"Error deserializing Mastodon response : {responseBody}");
        
        return new StatusResult { Id = typedResponse.Id };
    }
}

Implement the Service

Finally, we can add the service to DI on startup, and utilize it in the appropriate place

builder.Services.Configure<MastodonSettings>(
    builder.Configuration.GetSection(Constants.Config.MastodonSettings));

builder.Services
    .AddHttpClient()
    .AddTransient<ISocialMediaService<MastodonService>, MastodonService>();

Program.cs

using var scope = _serviceProvider.CreateScope();
var mastodonService = scope.ServiceProvider.GetRequiredService<ISocialMediaService<MastodonService>>();

await mastodonService.PublishStatus(postContent);

Host the Service

The last thing to do is host the application running your service. That could be an Azure App Service, Azure Function, docker container - whatever works for your situation