Managing a Mastodon domain block list
Learn how to manage the list of domain blocks on a Mastodon server.
If you are hosting a Mastodon server or helping administer one, domain blocks are something you should be using. Domain blocks help administrators prevent other servers and accounts from appearing on your server.
In order to automate this, I wrote a basic Node script that can read a list of domains and then automatically update my Mastodon server via the admin API.
Let's look at what you will need to run the script.
Requirements
You will need three things to run the node script. The first is the URL of your server, which you are likely familiar with - in my case, this is https://zsiegel.social. Next, you must create an access token with the appropriate Admin API scopes.
On your Mastodon server, navigate to Preferences -> Development and click New Application. You can give it a name and then adjust the scopes to only include admin:read and admin:write. After you hit submit, you will be shown the Access Token.
Finally, you will need to create a text file that contains a list of domains you want to block. Each domain should be placed on a new line.
Updating the server
To update the Mastodon server, you need a Node environment v17 or above. I always recommend running on the latest version available if possible. You then need to set the information you collected in the requirements section as environment variables.
- MASTODON_BLOCKLIST Contains the file path to your blocklist
- MASTODON_ACCESS_TOKEN Set this to the access token you got from your server
- MASTODON_URL Finally set the URL of your Mastodon server
The script has no dependencies, so you can run node update.js to run the script. If you name the script something other than update.js simply replace that in your command.
You can grab the contents of the script below.
const { readFile } = require("fs/promises");
(async function main() {
let MASTODON_URL = process.env.MASTODON_URL;
let MASTODON_ACCESS_TOKEN = process.env.MASTODON_ACCESS_TOKEN;
let MASTODON_BLOCKLIST = process.env.MASTODON_BLOCKLIST;
console.log(`Reading blocklist: ${MASTODON_BLOCKLIST}`);
let blocklist = await (await readFile(MASTODON_BLOCKLIST, "utf8")).split(
"\n"
);
console.log(`Found ${blocklist.length} domain(s) to block`);
let currentDomains = await getCurrentDomains(
MASTODON_URL,
MASTODON_ACCESS_TOKEN
);
console.log(`Server has ${currentDomains.length} domain(s) defined`);
//Convert the array to a map of { domain : item }
let domainMapping = new Map(
currentDomains.map((item) => [item.domain, item])
);
//Remove any domains from the server that are not in the blocklist
currentDomains.forEach(async (item) => {
if (!blocklist.includes(item.domain)) {
console.log(`Domain ${item.domain} should be removed`);
let response = await deleteDomain(
MASTODON_URL,
MASTODON_ACCESS_TOKEN,
item.id
);
if (response.ok) {
console.log(`--- Domain ${item.domain} deleted`);
} else {
console.log("--- Unable to delete domain");
}
}
});
//Add any domains to the server that exist in the blocklist
blocklist.forEach(async (domain) => {
if (!domainMapping.has(domain)) {
console.log(`Adding domain ${domain}`);
let response = await addDomain(
MASTODON_URL,
MASTODON_ACCESS_TOKEN,
domain
);
if (response.ok) {
console.log(`--- Domain ${domain} added.`);
} else {
console.log(
`--- Error adding the domain ${domain}. Status: ${response.status}`
);
}
}
});
})();
const addDomain = async (server, authToken, domain) => {
let url = new URL("/api/v1/admin/domain_blocks", server);
url.searchParams.set("domain", domain);
url.searchParams.set("severity", "suspend");
url.searchParams.set("reject_media", true);
url.searchParams.set("reject_reports", true);
url.searchParams.set("obfuscate", true);
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${authToken}`,
},
});
return response;
};
const deleteDomain = async (server, authToken, id) => {
const response = await fetch(`${server}/api/v1/admin/domain_blocks/${id}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${authToken}`,
},
});
return response;
};
const getCurrentDomains = async (server, authToken) => {
const response = await fetch(
`${server}/api/v1/admin/domain_blocks?limit=500`,
{
headers: {
Authorization: `Bearer ${authToken}`,
},
}
);
if (!response.ok) {
console.log(
`Unable to get domains from server. status: ${response.status}`
);
}
return response.json();
};