{"id":13471322,"url":"https://github.com/Phineas/lanyard","last_synced_at":"2025-03-26T13:30:58.340Z","repository":{"id":37025044,"uuid":"346433598","full_name":"Phineas/lanyard","owner":"Phineas","description":"🏷️   Expose your Discord presence and activities to a RESTful API and WebSocket in less than 10 seconds","archived":false,"fork":false,"pushed_at":"2025-02-27T18:23:19.000Z","size":412,"stargazers_count":1066,"open_issues_count":12,"forks_count":156,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-03-22T15:31:23.714Z","etag":null,"topics":["discord","discord-rpc","elixir"],"latest_commit_sha":null,"homepage":"https://discord.gg/lanyard","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Phineas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2021-03-10T17:13:33.000Z","updated_at":"2025-03-20T16:55:18.000Z","dependencies_parsed_at":"2024-01-16T06:08:39.341Z","dependency_job_id":"419dd941-6849-4dc5-89dc-319b31a738d0","html_url":"https://github.com/Phineas/lanyard","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Phineas%2Flanyard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Phineas%2Flanyard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Phineas%2Flanyard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Phineas%2Flanyard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Phineas","download_url":"https://codeload.github.com/Phineas/lanyard/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245662780,"owners_count":20652082,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["discord","discord-rpc","elixir"],"created_at":"2024-07-31T16:00:43.078Z","updated_at":"2025-03-26T13:30:58.333Z","avatar_url":"https://github.com/Phineas.png","language":"Elixir","funding_links":[],"categories":["Index","Elixir"],"sub_categories":["Social"],"readme":"\u003cimg src=\"https://storage.googleapis.com/lanyard/static/lanyardtemplogo.png\" alt=\"Lanyard Logo\" width=\"300\"/\u003e\n\n# 🏷️ Expose your Discord presence and activities to a RESTful API and WebSocket in less than 10 seconds\n\nLanyard is a service that makes it super easy to export your live Discord presence to an API endpoint (`api.lanyard.rest/v1/users/:your_id`) and to a WebSocket (see below) for you to use wherever you want - for example, I use this to display what I'm listening to on Spotify on my personal website. It also acts as a globally-accessible realtime KV store which you can update from the Lanyard Discord bot or from the Lanyard API.\n\nYou can use Lanyard's API without deploying anything yourself - but if you want to self host it, you have the option to, though it'll require a tiny bit of configuration.\n\n## Get started in \u003c 10 seconds\n\nJust [join this Discord server](https://discord.gg/UrXF2cfJ7F) and your presence will start showing up when you `GET api.lanyard.rest/v1/users/:your_id`. It's that easy.\n\n## Table of Contents\n\n- [Community Projects](#community-projects)\n- [API Docs](#api-docs)\n  - [Getting a user's presence data](#getting-a-user-s-presence-data)\n  * [KV](#kv)\n    - [Use cases](#use-cases)\n    - [Limits](#limits)\n    - [Getting an API Key](#getting-an-api-key)\n    - [Setting a key-\u003evalue pair](#setting-a-key--value-pair)\n    - [Deleting a key](#deleting-a-key)\n- [Socket Docs](#socket-docs)\n  - [Subscribing to multiple user presences](#subscribing-to-multiple-user-presences)\n  - [Subscribing to a single user presence](#subscribing-to-a-single-user-presence)\n  - [Subscribing to every user presence](#subscribing-to-every-user-presence)\n  * [List of Opcodes](#list-of-opcodes)\n  * [Events](#events)\n  * [Error Codes](#error-codes)\n- [Quicklinks](#quicklinks)\n- [Self-host with Docker](#self-host-with-docker)\n- [Showcase](#showcase)\n- [Todo](#todo)\n\n## Community Projects\n\nThe Lanyard community has worked on some pretty cool projects that allows you to extend the functionality of Lanyard. PR to add a project!\n\n[lanyard-web](https://lanyard.eggsy.xyz) - Landing page and API documentation concept for Lanyard API with a sleek UI. \\\n[lanyard-profile-readme](https://github.com/cnrad/lanyard-profile-readme) - Utilize Lanyard to display your Discord Presence in your GitHub Profile \\\n[vue-lanyard](https://github.com/eggsy/vue-lanyard) - Lanyard API plugin for Vue. Supports REST and WebSocket methods \\\n[react-use-lanyard](https://github.com/barbarbar338/react-use-lanyard) - React hook for Lanyard - supports REST \u0026 WebSocket \\\n[use-lanyard](https://github.com/alii/use-lanyard) - Another React hook for Lanyard that uses SWR \\\n[lanyard-visualizer](https://lanyard-visualizer.netlify.app/) - Beautifully display your Discord presence on a website \\\n[hawser](https://github.com/5elenay/hawser) - Lanyard API wrapper for python. Supports both REST and WebSocket. \\\n[js-lanyard](https://github.com/xaronnn/js-lanyard/) - Use Lanyard in your Web App. \\\n[go-lanyard](https://github.com/barbarbar338/go-lanyard) - Lanyard API wrapper for GoLang - supports REST \u0026 WebSocket \\\n[use-lanyard](https://github.com/LeonardSSH/use-lanyard) - Lanyard with Composition API for Vue. Supports REST and WebSocket methods \\\n[use-listen-along](https://github.com/punctuations/use-listen-along) - Mock the discord 'Listen Along' feature within a react hook powered by the Lanyard API. \\\n[lanyard-graphql](https://github.com/CesiumLabs/lanyard-graphql) - A GraphQL port of the Lanyard API. \\\n[sk-lanyard](https://github.com/nebulatgs/sk-lanyard) - SvelteKit integration with Lanyard, supports REST \u0026 WebSocket. \\\n[svelte-lanyard](https://github.com/iGalaxyYT/svelte-lanyard) - A Lanyard API wrapper for Svelte. Supports REST \u0026 WebSocket. \\\n[denyard](https://github.com/xHyroM/denyard) - Lanyard API wrapper for Deno - Supports REST \u0026 WebSocket. \\\n[lanyard-ui](https://lanyard.sakurajima.cloud/) - Lanyard visualizer focused on the KV aspect \\\n[discord-status-actions](https://github.com/CompeyDev/discord-status-action) - Updates a file to include your discord status using the Lanyard API. \\\n[discordstatus-website](https://github.com/lucaledd/discordstatus-website) - Display your Discord status on your own website with premade CSS, and JS \\\n[osu-nowplaying](https://github.com/Hexality/osu-nowplaying) - A small tool to scrape the info of the map you're curently playing on osu!lazer and dump into a file for obs to read. \\\n[lanyard.py](https://github.com/sawshadev/lanyard-py) - An asynchronous implementation of the Lanyard websocket and HTTP for python \\\n[landart](https://pub.dev/packages/landart) - A featureful API wrapper for Lanyard \u0026 Lanyard KV written in Dart.\n\n## API Docs\n\n#### Getting a user's presence data\n\n`GET https://api.lanyard.rest/v1/users/:user_id`\n\nExample response:\n\n```js\n{\n  \"success\": true,\n  \"data\": {\n    \"active_on_discord_mobile\": false,\n    \"active_on_discord_desktop\": true,\n    \"listening_to_spotify\": true,\n    // Lanyard KV\n    \"kv\": {\n      \"location\": \"Los Angeles, CA\"\n    },\n    // Below is a custom crafted \"spotify\" object, which will be null if listening_to_spotify is false\n    \"spotify\": {\n      \"track_id\": \"3kdlVcMVsSkbsUy8eQcBjI\",\n      \"timestamps\": {\n        \"start\": 1615529820677,\n        \"end\": 1615530068733\n      },\n      \"song\": \"Let Go\",\n      \"artist\": \"Ark Patrol; Veronika Redd\",\n      \"album_art_url\": \"https://i.scdn.co/image/ab67616d0000b27364840995fe43bb2ec73a241d\",\n      \"album\": \"Let Go\"\n    },\n    \"discord_user\": {\n      \"username\": \"Phineas\",\n      \"public_flags\": 131584,\n      \"id\": \"94490510688792576\",\n      \"discriminator\": \"0001\",\n      \"avatar\": \"a_7484f82375f47a487f41650f36d30318\"\n    },\n    \"discord_status\": \"online\",\n    // activities contains the plain Discord activities array that gets sent down with presences\n    \"activities\": [\n      {\n        \"type\": 2,\n        \"timestamps\": {\n          \"start\": 1615529820677,\n          \"end\": 1615530068733\n        },\n        \"sync_id\": \"3kdlVcMVsSkbsUy8eQcBjI\",\n        \"state\": \"Ark Patrol; Veronika Redd\",\n        \"session_id\": \"140ecdfb976bdbf29d4452d492e551c7\",\n        \"party\": {\n          \"id\": \"spotify:94490510688792576\"\n        },\n        \"name\": \"Spotify\",\n        \"id\": \"spotify:1\",\n        \"flags\": 48,\n        \"details\": \"Let Go\",\n        \"created_at\": 1615529838051,\n        \"assets\": {\n          \"large_text\": \"Let Go\",\n          \"large_image\": \"spotify:ab67616d0000b27364840995fe43bb2ec73a241d\"\n        }\n      },\n      {\n        \"type\": 0,\n        \"timestamps\": {\n          \"start\": 1615438153941\n        },\n        \"state\": \"Workspace: lanyard\",\n        \"name\": \"Visual Studio Code\",\n        \"id\": \"66b84f5317e9de6c\",\n        \"details\": \"Editing README.md\",\n        \"created_at\": 1615529838050,\n        \"assets\": {\n          \"small_text\": \"Visual Studio Code\",\n          \"small_image\": \"565945770067623946\",\n          \"large_text\": \"Editing a MARKDOWN file\",\n          \"large_image\": \"565945077491433494\"\n        },\n        \"application_id\": \"383226320970055681\"\n      }\n    ]\n  }\n}\n```\n\n### KV\n\nLanyard KV is a a dynamic, real-time key-\u003evalue store which is added to the Lanyard user API response. When a KV pair is updated, a `PRESENCE_UPDATE` for the user will also be emitted through the Lanyard socket.\n\n#### Use cases\n\n- Configuration values for your website\n- Configuration values for Lanyard 3rd party projects\n- Dynamic data for your website/profile (e.g. current location)\n\n#### Limits\n\n1. Keys and values can only be strings\n2. Values can be 30,000 characters maximum\n3. Keys must be alphanumeric (a-zA-Z0-9) and 255 characters max length\n4. Your user can have a maximum of 512 key-\u003evalue pairs linked\n\n#### Getting an API Key\n\nDM the Lanyard bot (`Lanyard#5766`) with `.apikey` to get your API key.\n\nWhen making Lanyard KV API requests, set an `Authorization` header with the API key you received from the Lanyard bot as the value.\n\n#### Setting a key-\u003evalue pair\n\n##### Discord\n\n`.set \u003ckey\u003e \u003cvalue\u003e`\n\n##### HTTP\n\n`PUT https://api.lanyard.rest/v1/users/:user_id/kv/:key`\nThe value will be set to the body of the request. The body can be any type of data, but it will be string-encoded when set in Lanyard KV.\n\n#### Setting multiple key-\u003evalue pairs\n\n##### Discord\n\nNot yet implemented\n\n##### HTTP\n\n`PATCH https://api.lanyard.rest/v1/users/:user_id/kv`\nThe user's KV store will be merged with the body of the request. Conflicting keys will be overwritten. The body must be keyvalue pair object with a maximum depth of 1.\n\n#### Deleting a key\n\n##### Discord\n\n`.del \u003ckey\u003e`\n\n##### HTTP\n\n`DELETE https://api.lanyard.rest/v1/users/:user_id/kv/:key`\n\n## Socket Docs\n\nThe websocket is available at `wss://api.lanyard.rest/socket`. If you would like to use compression, please specify `?compression=zlib_json` at the end of the URL.\n\nOnce connected, you will receive Opcode 1: Hello which will contain heartbeat_interval in the data field. You should set a repeating interval for the time specified in heartbeat_interval which should send Opcode 3: Heartbeat on the interval.\n\nYou should send `Opcode 2: Initialize` immediately after receiving Opcode 1.\n\nExample of `Opcode 2: Initialize`:\n\n```js\n{\n  op: 2,\n  d: {\n    // subscribe_to_ids should be an array of user IDs you want to subscribe to presences from\n    // if Lanyard doesn't monitor an ID specified, it won't be included in INIT_STATE\n    subscribe_to_ids: [\"94490510688792576\"]\n  }\n}\n```\n\n#### Subscribing to multiple user presences\n\nTo subscribe to multiple presences, send `subscribe_to_ids` in the data object with a `string[]` list of user IDs to subscribe to. Then, INIT_STATE's data object will contain a user_id-\u003epresence map. You can find examples below.\n\n#### Subscribing to a single user presence\n\nIf you just want to subscribe to one user, you can send `subscribe_to_id` instead with a string of a single user ID to subscribe to. Then, the INIT_STATE's data will just contain the presence object for the user you've subscribed to instead of a user_id-\u003epresence map.\n\n#### Subscribing to every user presence\n\nIf you want to subscribe to every presence being monitored by Lanyard, you can specify `subscribe_to_all` with (bool) `true` in the data object, and you will then receive a user_id-\u003epresence map with every user presence in INIT_STATE, and their respective PRESENCE_UPDATES when they happen.\n\nOnce Op 2 is sent, you should immediately receive an `INIT_STATE` event payload if connected successfully. If not, you will be disconnected with an error (see below).\n\n### List of Opcodes\n\n| Opcode | Name       | Description                                                                                                                 | Client Send/Recv |\n| ------ | ---------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------- |\n| 0      | Event      | This is the default opcode when receiving core events from Lanyard, like `INIT_STATE`                                       | Receive          |\n| 1      | Hello      | Lanyard sends this when clients initially connect, and it includes the heartbeat interval                                   | Receive Only     |\n| 2      | Initialize | This is what the client sends when receiving Opcode 1 from Lanyard - it should contain an array of user IDs to subscribe to | Send only        |\n| 3      | Heartbeat  | Clients should send Opcode 3 every 30 seconds (or whatever the Hello Opcode says to heartbeat at)                           | Send only        |\n\n### Events\n\nEvents are received on `Opcode 0: Event` - the event type will be part of the root message object under the `t` key.\n\n#### Example Event Message Objects\n\n#### `INIT_STATE`\n\n```js\n{\n  op: 0,\n  seq: 1,\n  t: \"INIT_STATE\",\n  d: {\n    \"94490510688792576\": {\n      // Full Lanyard presence (see API docs above for example)\n    }\n  }\n}\n```\n\n#### `PRESENCE_UPDATE`\n\n```js\n{\n  op: 0,\n  seq: 2,\n  t: \"PRESENCE_UPDATE\",\n  d: {\n    // Full Lanyard presence and an extra \"user_id\" field\n  }\n}\n```\n\n### Error Codes\n\nLanyard can disconnect clients for multiple reasons, usually to do with messages being badly formatted. Please refer to your WebSocket client to see how you should handle errors - they do not get received as regular messages.\n\n#### Types of Errors\n\n| Name                   | Code | Data                   |\n| ---------------------- | ---- | ---------------------- |\n| Invalid/Unknown Opcode | 4004 | `unknown_opcode`       |\n| Opcode Requires Data   | 4005 | `requires_data_object` |\n| Invalid Payload        | 4006 | `invalid_payload`      |\n\n## Quicklinks\n\nLanyard quicklinks allow you to easily access resources from Discord, such as profile pictures.\n\n### User Icons\n\n`https://api.lanyard.rest/\u003cid\u003e.\u003cfile_type\u003e`\nWhere `id` is the Discord user ID. `file_type` can be one of: `png`, `gif`, `webp`, `jpg`, or `jpeg`\n\n## Self-host with Docker\n\nBuild the Docker image by cloning this repo and running:\n\n```bash\n# The latest version is already on the docker hub, you can skip this step unless you would like to run a modified version.\ndocker build -t phineas/lanyard:latest .\n```\n\nIf you don't already have a redis server you'll need to run one, here's the docker command to run one:\n\n```bash\ndocker run -d --name lanyard-redis -v docker_mount_location_on_host:/data redis\n```\n\nAnd run Lanyard API server using:\n\n```bash\ndocker run --rm -it -p 4001:4001 -e REDIS_HOST=redis -e BOT_TOKEN=\u003ctoken\u003e --link lanyard-redis:redis phineas/lanyard:latest\n```\n\nYou'll be able to access the API using **port 4001**.\n\nYou also need to create a Discord bot and use its token above.\n\nCreate a bot here: https://discord.com/developers/applications\n\n**Make sure you enable** these settings in your bot settings:\n\n- Privileged Gateway Intents \u003e **PRESENCE INTENT**\n- Privileged Gateway Intents \u003e **SERVER MEMBERS INTENT**\n\nIf you'd like to run Lanyard with `docker-compose`, here's an example:\n\n```yml\nversion: \"3.8\"\n\nservices:\n  redis:\n    image: redis\n    restart: always\n    container_name: lanyard_redis\n  lanyard:\n    image: phineas/lanyard:latest\n    restart: always\n    container_name: lanyard\n    depends_on:\n      - redis\n    ports:\n      - 4001:4001\n    environment:\n      BOT_TOKEN: \u003ctoken\u003e\n      REDIS_HOST: redis\n```\n\nNote, that you're **hosting a http server, not https**. You'll need to use a **reverse proxy** such as [traefik](https://traefik.io/traefik/) if you want to secure your API endpoint.\n\n## Showcase\n\nBelow is a curated selection of websites using Lanyard right now, check them out! Some of them will only show an activity when they're active.\n\n- [alistair.sh](https://alistair.sh)\n- [eggsy.xyz](https://eggsy.xyz)\n- [igalaxy.dev](https://igalaxy.dev)\n- [anahoward.me](https://www.anahoward.me/)\n- [chezzer.dev](https://chezzer.dev)\n- [makidoll.io](https://makidoll.io/)\n- [dan.onl](https://dan.onl/)\n- [cnrad.dev](https://cnrad.dev)\n- [loefey.world](https://loefey.world/)\n- [phineas.io](https://phineas.io)\n- [timcole.me](https://timcole.me)\n- [itspolar.dev](https://itspolar.dev)\n- [vasc.dev](https://vasc.dev)\n- [dstn.to](https://dstn.to)\n- [voided.dev](https://voided.dev)\n- [neb.bio](https://neb.bio)\n- [looskie.com](https://looskie.com)\n- [krypton.ninja](https://krypton.ninja)\n- [eosis.space](https://eosis.space/)\n- [dromzeh.dev](https://dromzeh.dev)\n- [littleprinceonu.com](https://littlepriceonu.com/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPhineas%2Flanyard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FPhineas%2Flanyard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPhineas%2Flanyard/lists"}