Mastodon
Mastodon is the first of two channels where sending means publishing. The verb is
post, not send, so a line that goes to the world never looks like a line that goes
to your phone.
Copy this
Get an access token
In your instance: Preferences → Development → New application. Give it the scopes
write:statuses and, for receiving, read:notifications. Submit, open the application
again, and copy Your access token.
Configure it
PIGEON_MASTODON_INSTANCE=https://mastodon.social
PIGEON_MASTODON_TOKEN=...
export default defineNuxtConfig({
modules: ['nuxt-pigeon'],
nuxtPigeon: {
channels: {
mastodon: { instance: 'https://mastodon.social' },
},
},
})
Post
export default defineEventHandler(async () => {
const status = await mastodon.post('Release 1.0 is out', {
visibility: 'direct',
})
return { url: status.url }
})
Open the url it returns. direct keeps it to mentioned accounts while you are
trying things out.
Every option
| Option | What it does |
|---|---|
visibility | public, unlisted, private (followers), direct |
media | Up to four. Downloaded and uploaded again, Mastodon takes no url |
spoilerText | Content warning, the post collapses behind it |
sensitive | Marks the media as sensitive |
language | ISO 639, drives the translation offer in clients |
inReplyToId | Makes it a reply, which is how threads are built |
scheduledAt | ISO 8601, at least five minutes out |
idempotencyKey | The same key twice creates no second post |
idempotencyKey is the one real retry guard Mastodon offers. If a request times out and
you are not sure whether it landed, sending again with the same key cannot duplicate it.The character count is not length
The limit is per instance, 500 is only Mastodon's default, and the counting has two rules of its own:
- A link always costs 23, however long it is. Shortening a url changes nothing.
- The domain of a remote mention is free.
@flo@example.comcosts as much as@flo.
The module asks your instance once for its real numbers and remembers them. If the instance cannot be reached it falls back to the defaults rather than blocking, because the instance decides in the end anyway.
Receiving
nuxtPigeon: {
channels: {
mastodon: { instance: 'https://mastodon.social', receive: true, intervalMs: 60000 },
},
}
Polled, not pushed. There is no webhook for your own account, so this needs a process that stays alive and does not work on serverless.
export default defineNitroPlugin(() => {
mastodon.listen((notification) => {
console.log(notification.type, notification.account?.acct)
})
})
What will cost you an hour
- Mastodon never notifies you about your own doing. Testing needs a second account, or a friend who mentions you.
- The first poll only marks where we are. Trigger something after the server started, or you will wait for an event that already happened.
- A large upload answers
202while it is still being processed, and posting before that finishes is refused. The module waits for you. - Whether an edit keeps the images is not in the docs. It is in the source: the
update only touches attachments when
media_idsis present. The module leaves the field out to keep them, which is the opposite work from Discord and reads the same for you. - Editing is public. Mastodon keeps a version history and clients show an "edited" marker.
- Deleting answers with the post, in plain text, so a client can offer delete and redraft. Its attachments stay available for about 24 hours.
Read more
ntfy
Push notifications to your phone from Nuxt with ntfy: no account and no keys, priorities, tags and one notification that replaces itself instead of repeating.
Bluesky
Post to Bluesky from Nuxt with an app password: images, link cards, and facets computed for you, so links and hashtags are clickable instead of dead text.