{"_id":"@bozonx/social-posting-discord","name":"@bozonx/social-posting-discord","dist-tags":{"latest":"0.8.0"},"versions":{"0.8.0":{"name":"@bozonx/social-posting-discord","version":"0.8.0","description":"Discord platform for @bozonx/social-posting","keywords":["social-media","posting","discord"],"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","workerd":"./dist/index.js","import":"./dist/index.js","default":"./dist/index.js"}},"dependencies":{},"peerDependencies":{"@bozonx/social-posting":"^0.8.0"},"devDependencies":{"@bozonx/social-posting":"0.8.0","@bozonx/social-posting-conformance":"0.8.0"},"publishConfig":{"access":"public","provenance":true},"sideEffects":false,"author":"Ivan K","license":"MIT","engines":{"node":">=24.0.0"},"scripts":{"build":"tsc -p tsconfig.build.json","clean":"rm -rf dist *.tsbuildinfo","typecheck":"tsc -p tsconfig.json --noEmit"},"_nodeVersion":"24.19.0","_id":"@bozonx/social-posting-discord@0.8.0","dist":{"integrity":"sha512-f8sgyjG/J9f/6WTllraEyRuzpRV33CFWr8mGHSl1dCO9XuIgZ2tM4X4xrz4JASMzkg/cBW8nKvTTALnudiXAgg==","shasum":"71f5a1db2dc05dc9a242d5d5b66a86bfbc39543d","tarball":"https://registry.npmjs.org/@bozonx/social-posting-discord/-/social-posting-discord-0.8.0.tgz","fileCount":21,"unpackedSize":85357,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAqFfKd+3IgHjIAPQ3ktOhRq20LlyMqeoLwn6jDnsGB2AiEAlmLwIPdhW0/QB+02b0E6ZWbdR76bZ0X4c334pLDRZb8="}]},"_npmUser":{"name":"bozonx","email":"ipkozyrin@gmail.com"},"directories":{},"maintainers":[{"name":"bozonx","email":"ipkozyrin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/social-posting-discord_0.8.0_1788181096833_0.7271581832961755"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-31T12:58:16.574Z","0.8.0":"2026-08-31T12:58:16.988Z","modified":"2026-08-31T12:58:17.334Z"},"maintainers":[{"name":"bozonx","email":"ipkozyrin@gmail.com"}],"description":"Discord platform for @bozonx/social-posting","keywords":["social-media","posting","discord"],"author":"Ivan K","license":"MIT","readme":"# @bozonx/social-posting-discord\n\nDiscord support for [`@bozonx/social-posting`](https://www.npmjs.com/package/@bozonx/social-posting).\nZero runtime dependencies, Web APIs only — it runs on Node, Bun, Deno and Cloudflare Workers.\n\n```bash\npnpm add @bozonx/social-posting @bozonx/social-posting-discord\n```\n\n```ts\nimport { createPostingClient } from '@bozonx/social-posting';\nimport { discord } from '@bozonx/social-posting-discord';\n\nconst client = createPostingClient({\n  platforms: [discord],\n  accounts: {\n    announcements: {\n      platform: 'discord',\n      auth: { webhookUrl: process.env.DISCORD_WEBHOOK_URL },\n    },\n  },\n});\n\nawait client.post({ platform: 'discord', account: 'announcements', body: 'Ship it.' });\n```\n\n## Two access models, and why the difference matters\n\nDiscord is the only network in this set where the credential and the destination can be the same\nstring. Pick one per account; an account carrying both is refused rather than guessed at.\n\n|                               | `webhookUrl`                                                    | `botToken`                                                                                 |\n| ----------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |\n| Setup                         | Server settings → Integrations. No OAuth, no app review.        | A registered application, invited to the server with `Send Messages` (and `Attach Files`). |\n| Destination                   | **Baked into the URL.** No `target` needed.                     | `target.id` is the channel id, and it is required.                                         |\n| Replies                       | No.                                                             | Yes, via `inReplyTo`.                                                                      |\n| Name and avatar               | Overridable per message (`extra.username`, `extra.avatar_url`). | Fixed, the bot's own.                                                                      |\n| Deletes its own messages      | Yes, through the webhook token.                                 | Yes, by channel and message id.                                                            |\n| Reads the server's boost tier | No — keeps the unboosted attachment limit.                      | Yes, through `resolveCapabilities()`.                                                      |\n\n**A webhook URL is a secret, not a channel id.** Anyone holding it can post to that channel.\nStore it the way you store a token, never in a column named `channelId`.\n\n## Addressing\n\nThe channel is `target.id`. Two further parts are declared in `capabilities.targetSchema`:\n\n```ts\ntarget: {\n  id: '1290000000000000002',      // channel id\n  guildId: '1280000000000000003', // server: used for permalinks and boost-tier limits\n  threadId: '1295000000000000007' // optional: post into a thread of that channel\n}\n```\n\nA scalar `target` is accepted as shorthand for `{ id }`, but then no permalink can be built:\nDiscord message URLs contain the server id.\n\n## What it publishes\n\n| Type                                  | Support                                                           |\n| ------------------------------------- | ----------------------------------------------------------------- |\n| `post`                                | Up to 2 000 characters, Discord-flavoured Markdown.               |\n| `image`, `video`, `audio`, `document` | One attachment, uploaded as `multipart/form-data`.                |\n| `album`                               | Up to 10 attachments of any mix.                                  |\n| `poll`                                | Native poll: `title` is the question, `poll.options` the answers. |\n\nNot supported, because Discord has no such product: `shortVideo`, `story`, `article`, scheduling\nand drafts. A vertical video is an ordinary attachment.\n\n### Media\n\nDiscord never fetches a URL — every file is uploaded to it. A `url` source still works: this\nadapter downloads it first, which is why the declared transport is `both`. The URL therefore has\nto be reachable from **your** process, not from Discord, and no signed-URL lifetime requirement\napplies.\n\nSet `sensitive: true` on a media item to spoiler it. Discord has no flag for this: the adapter\nprefixes the file name with `SPOILER_`, which is the mechanism Discord actually uses.\n`altText` becomes the attachment description (max 1 024 characters).\n\n### Attachment size is a property of the server\n\nThe shipped descriptor states the **unboosted** ceiling of 10 MiB. The real ceiling rises with the\nserver's boost tier, so a bot-token account should ask before publishing:\n\n```ts\nconst resolved = await platform.resolveCapabilities(accountConfig);\n// resolved.capabilities.media.video.maxBytes  → 10 / 50 / 100 MiB by boost tier\n// resolved.cacheableForSecs                   → 3600\n```\n\nThe library caches nothing: it hands the reading back with a lifetime and your host decides where\nit lives. A webhook account cannot read the server and keeps the floor.\n\n## Platform options (`extra`)\n\n`embeds`, `tts`, `flags`, `allowed_mentions`, `components`, plus `username` and `avatar_url` for\nwebhook accounts only. Keys the adapter builds itself — `content`, `attachments`, `poll`,\n`message_reference` — are refused rather than silently overwritten.\n\n## Errors\n\n| Code               | When                                                                     |\n| ------------------ | ------------------------------------------------------------------------ |\n| `RATE_LIMIT_ERROR` | 429, carrying Discord's own `retry_after` as `retryAfterMs`.             |\n| `AUTH_ERROR`       | 401 (token rejected) and 403 (the bot lacks permission in this channel). |\n| `VALIDATION_ERROR` | Unknown channel or deleted webhook (404), and malformed requests.        |\n| `CONTENT_REJECTED` | An attachment or embed Discord refuses (400 with code 50035, 413).       |\n| `PLATFORM_ERROR`   | 5xx, retryable.                                                          |\n\nPublishing is a single call, so there is no resume handle and no `processing` state.\n\n## A product note\n\nDiscord is an announcement channel, not a social network. There are no impressions, no reach and\nno post analytics behind a message. Label it accordingly in your UI, or users will expect numbers\nthat do not exist.\n\n## Sources\n\n- [Create Message](https://docs.discord.com/developers/resources/message#create-message)\n- [Execute Webhook](https://docs.discord.com/developers/resources/webhook#execute-webhook)\n- [Uploading files](https://docs.discord.com/developers/reference#uploading-files)\n\n## License\n\nMIT\n","readmeFilename":"","_rev":"1-504eb2bb9d7535f1b02198473818625a"}