{"id":35429874,"url":"https://github.com/elliotwms/fakediscord","last_synced_at":"2026-02-16T07:18:38.258Z","repository":{"id":65788723,"uuid":"528623327","full_name":"elliotwms/fakediscord","owner":"elliotwms","description":"Test your Discord bots!","archived":false,"fork":false,"pushed_at":"2026-02-01T17:47:03.000Z","size":285,"stargazers_count":2,"open_issues_count":9,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-02-02T01:43:29.261Z","etag":null,"topics":["bot","discord","testing"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elliotwms.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["elliotwms"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2022-08-24T23:11:59.000Z","updated_at":"2026-02-01T14:48:49.000Z","dependencies_parsed_at":"2026-02-01T19:03:15.014Z","dependency_job_id":null,"html_url":"https://github.com/elliotwms/fakediscord","commit_stats":{"total_commits":111,"total_committers":3,"mean_commits":37.0,"dds":0.6036036036036037,"last_synced_commit":"2f2058be1176636ec21702864abd3e6481c2dc32"},"previous_names":[],"tags_count":88,"template":false,"template_full_name":null,"purl":"pkg:github/elliotwms/fakediscord","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elliotwms%2Ffakediscord","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elliotwms%2Ffakediscord/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elliotwms%2Ffakediscord/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elliotwms%2Ffakediscord/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elliotwms","download_url":"https://codeload.github.com/elliotwms/fakediscord/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elliotwms%2Ffakediscord/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29502934,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T05:57:17.024Z","status":"ssl_error","status_checked_at":"2026-02-16T05:56:49.929Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bot","discord","testing"],"created_at":"2026-01-02T20:16:50.867Z","updated_at":"2026-02-16T07:18:38.253Z","avatar_url":"https://github.com/elliotwms.png","language":"Go","funding_links":["https://github.com/sponsors/elliotwms"],"categories":[],"sub_categories":[],"readme":"# fakediscord\n\nThe aim of `fakediscord` is to replicate the behaviour of the Discord HTTP and Websocket APIs, based on the documentation and observed behaviour, in order to enable the integration testing of Discord bots without calling the real Discord API. \n\nAnalogous to [LocalStack](https://github.com/localstack/localstack), `fakediscord` should be run locally using Docker when running your bot's tests.\n\nWhile written in Go, `fakediscord` can be used to test bots in any language, provided they adhere to Discord's specifications.\n\n```mermaid\nflowchart LR\nt[\"Your tests\"] --\u003e b[\"Your Bot\"] --\u003e d[\"Discord\"]\nb --\u003e f[\"fakediscord\"]\nt --\u003e f[\"fakediscord\"]\n```\n\n\n`fakediscord` fakes the HTTP and WebSocket endpoints of the Discord API, triggering corresponding events via the WebSocket connection. `fakediscord` pairs well with (and is based on the hard work of) [bwmarrin/discordgo](https://github.com/bwmarrin/discordgo). \n\nOf course, you should also test your bot manually before releasing to the public: there are many features currently not present, such as authorization, -- any action is currently allowed.\n\n## Usage\n\n`fakediscord` should work with any Discord client in any language, and is intended to be run via Docker:\n\n```shell\ndocker run -p 8080:8080 ghcr.io/elliotwms/fakediscord:{version}\n```\n\nIt is possible to provide a `config.yml` file to bootstrap users and guilds (todo: document config): \n\n```yaml\nservices:\n  fakediscord:\n    image: ghcr.io/elliotwms/fakediscord:{version}\n    ports:\n      - 8080:8080\n    volumes:\n      - ${PWD}/fakediscord.yaml:/config.yml:ro\n```\n\n`fakediscord` provides a Go client as a convenience wrapper for internal endpoints, as well as a shim for discordgo to allow you to override the endpoints, which can be found in `pkg/fakediscord`.\n\nOverride the Discord Base URL to `fakediscord`'s, then proceed to use your client as normal:\n\n```go\npackage main\n\nimport \"github.com/elliotwms/fakediscord/pkg/fakediscord\"\n\nfunc main() { \n\t// override discordgo URLs\n\tfakediscord.Configure(\"http://localhost:8080\") \n\t\n\t// Client for internal endpoints (e.g. interactions)\n\tc := fakediscord.NewClient()\n}\n```\n\n### Authentication\n\n* Any token value will pass authentication (`Bot {token}`)\n* If the token matches one specified in the config then the relevant user will be authenticated\n* Otherwise, a user will be generated with the token value as the username\n* For testing purposes, all users are assumed to be in all guilds\n\n### Interactions\n\n`fakediscord` provides an endpoint for triggering interactions, which would normally only be possible via a user initiating via the UI. A `POST` of an `InteractionCreate` event to `/api/:version/interactions` will create an interaction.\n\nA suggested pattern for testing interactions within a webhook application would be as follows: \n\n1. Build the expected interaction within your test suite\n2. Create the initial interaction in `fakediscord`. This will provide you with IDs, tokens etc\n3. Send the interaction to your application's endpoint\n4. Your application will likely call the [interaction's callback url](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback) to acknowledge the interaction\n\n```mermaid\nsequenceDiagram\n    participant t as Tests\n    participant a as App\n    participant d as fakediscord\n    \n    t-\u003e\u003et: Set up interaction\n    t-\u003e\u003ed: POST /interactions\n    d-\u003e\u003et: 201 Created: Interaction\n    t-\u003e\u003ea: Interaction\n    activate a\n    a-\u003e\u003ed: POST /interactions/:id/:token/callback\n    d-\u003e\u003ea: 204 No Content\n    a-\u003e\u003ea: Process interaction\n    a-\u003e\u003ed: POST/webhooks/:appID/:token/@original\n    d-\u003e\u003ea: 200 OK\n    a-\u003e\u003et: 202 Accepted\n    deactivate a\n    t-\u003e\u003ed: GET /webhooks/:appID/:token/@original\n    d-\u003e\u003et: 200 OK: Message\n    t-\u003e\u003et: Assert on message\n```\n\n## Features\n\n`fakediscord` currently supports the following API operations, and emits the corresponding [events](https://discord.com/developers/docs/topics/gateway-events):\n\n#### Gateway\n\n* Get Gateway\n* Connect\n  * `HELLO`\n  * `READY`\n  * [`GUILD_CREATE`](https://discord.com/developers/docs/events/gateway-events#guild-create)\n\n### Guilds\n\n* [Create](https://discord.com/developers/docs/resources/guild#create-guild)\n  * [`GUILD_CREATE`](https://discord.com/developers/docs/events/gateway-events#guild-create)\n* [Get](https://discord.com/developers/docs/resources/guild#get-guild)\n* [Delete](https://discord.com/developers/docs/resources/guild#delete-guild)\n  * [`GUILD_DELETE`](https://discord.com/developers/docs/events/gateway-events#guild-delete)\n* [Get channels](https://discord.com/developers/docs/resources/guild#get-guild-channels)\n* [Create channel](https://discord.com/developers/docs/resources/guild#create-guild-channel)\n  * [`CHANNEL_CREATE`](https://discord.com/developers/docs/events/gateway-events#channel-create)\n\n### Channels\n\n* [Get](https://discord.com/developers/docs/resources/channel#get-channel)\n* [Delete](https://discord.com/developers/docs/resources/channel#deleteclose-channel)\n  * [`CHANNEL_DELETE`](https://discord.com/developers/docs/events/gateway-events#channel-delete)\n* [Get Pinned Messages](https://discord.com/developers/docs/resources/channel#get-pinned-messages)\n* [Pin Message](https://discord.com/developers/docs/resources/channel#pin-message)\n  * [`CHANNEL_PINS_UPDATE`](https://discord.com/developers/docs/events/gateway-events#channel-pins-update)\n\n### Messages\n\n* [Create Message](https://discord.com/developers/docs/resources/message#create-message)\n  * [`MESSAGE_CREATE`](https://discord.com/developers/docs/events/gateway-events#message-create)\n  * Supports basic, embeds and multipart \n* [Get Message](https://discord.com/developers/docs/resources/message#get-channel-message)\n* [Delete Message](https://discord.com/developers/docs/resources/message#delete-message)\n  * [`MESSAGE_DELETE`](https://discord.com/developers/docs/resources/message#delete-message)\n* [Get Message Reactions](https://discord.com/developers/docs/resources/message#get-reactions)\n* [Create Reaction](https://discord.com/developers/docs/resources/message#create-reaction)\n  * [`MESSAGE_REACTION_ADD`](https://discord.com/developers/docs/events/gateway-events#message-reaction-add)\n* [Delete Reactions](https://discord.com/developers/docs/resources/message#delete-all-reactions)\n  * [`MESSAGE_REACTION_REMOVE_ALL`](https://discord.com/developers/docs/events/gateway-events#message-reaction-remove-all)\n\n### Interactions\n\n* Create (see [docs](#interactions))\n* [Callback](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback)\n\n## Examples\n\nCheck out how the following projects use `fakediscord` for inspiration:\n\n### [Pinbot](https://github.com/elliotwms/pinbot/tree/master/tests)\n\n* Docker [Compose](https://github.com/elliotwms/pinbot/blob/master/compose.yaml) contains Pinbot config, including the bot user in [fakediscord.yaml](https://github.com/elliotwms/pinbot/blob/master/fakediscord.yaml)\n* [TestMain](https://github.com/elliotwms/pinbot/blob/20debf13a3dff8e58b7d61ec5e04c18c1542be3d/tests/setup_test.go#L21) calls `fakediscord.Configure` to set base URLs etc, sets up the client, creates a test guild for the run and opens a general session for the test suite\n* Individual tests then create channels in the test guild to execute their tests within ([example](https://github.com/elliotwms/pinbot/blob/20debf13a3dff8e58b7d61ec5e04c18c1542be3d/tests/pin_test.go#L7))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felliotwms%2Ffakediscord","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felliotwms%2Ffakediscord","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felliotwms%2Ffakediscord/lists"}