/lanyard

🏷️   Expose your Discord presence and activities to a RESTful API and WebSocket in less than 10 seconds

Primary LanguageElixirMIT LicenseMIT

Lanyard Logo

🏷️ Expose your Discord presence and activities to a RESTful API and WebSocket in less than 10 seconds

Lanyard 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.

You 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.

Get started in < 10 seconds

Just join this Discord server and your presence will start showing up when you GET api.lanyard.rest/v1/users/:your_id. It's that easy.

Table of Contents

Community Projects

The Lanyard community has worked on some pretty cool projects that allows you to extend the functionality of Lanyard. PR to add a project!

lanyard-web - Landing page and API documentation concept for Lanyard API with a sleek UI.
lanyard-profile-readme - Utilize Lanyard to display your Discord Presence in your GitHub Profile
vue-lanyard - Lanyard API plugin for Vue. Supports REST and WebSocket methods
react-use-lanyard - React hook for Lanyard - supports REST & WebSocket
use-lanyard - Another React hook for Lanyard that uses SWR
lanyard-visualizer - Beautifully display your Discord presence on a website
hawser - Lanyard API wrapper for python. Supports both REST and WebSocket.
js-lanyard - Use Lanyard in your Web App.
go-lanyard - Lanyard API wrapper for GoLang - supports REST & WebSocket
use-lanyard - Lanyard with Composition API for Vue. Supports REST and WebSocket methods
use-listen-along - Mock the discord 'Listen Along' feature within a react hook powered by the Lanyard API.
lanyard-graphql - A GraphQL port of the Lanyard API.
sk-lanyard - SvelteKit integration with Lanyard, supports REST & WebSocket.
svelte-lanyard - A Lanyard API wrapper for Svelte. Supports REST & WebSocket.
denyard - Lanyard API wrapper for Deno - Supports REST & WebSocket.
lanyard-ui - Lanyard visualizer focused on the KV aspect
discord-status-actions - Updates a file to include your discord status using the Lanyard API.
discordstatus-website - Display your Discord status on your own website with premade CSS, and JS
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.
lanyard.py - An asynchronous implementation of the Lanyard websocket and HTTP for python
landart - A featureful API wrapper for Lanyard & Lanyard KV written in Dart.

API Docs

Getting a user's presence data

GET https://api.lanyard.rest/v1/users/:user_id

Example response:

{
  "success": true,
  "data": {
    "active_on_discord_mobile": false,
    "active_on_discord_desktop": true,
    "listening_to_spotify": true,
    // Lanyard KV
    "kv": {
      "location": "Los Angeles, CA"
    },
    // Below is a custom crafted "spotify" object, which will be null if listening_to_spotify is false
    "spotify": {
      "track_id": "3kdlVcMVsSkbsUy8eQcBjI",
      "timestamps": {
        "start": 1615529820677,
        "end": 1615530068733
      },
      "song": "Let Go",
      "artist": "Ark Patrol; Veronika Redd",
      "album_art_url": "https://i.scdn.co/image/ab67616d0000b27364840995fe43bb2ec73a241d",
      "album": "Let Go"
    },
    "discord_user": {
      "username": "Phineas",
      "public_flags": 131584,
      "id": "94490510688792576",
      "discriminator": "0001",
      "avatar": "a_7484f82375f47a487f41650f36d30318"
    },
    "discord_status": "online",
    // activities contains the plain Discord activities array that gets sent down with presences
    "activities": [
      {
        "type": 2,
        "timestamps": {
          "start": 1615529820677,
          "end": 1615530068733
        },
        "sync_id": "3kdlVcMVsSkbsUy8eQcBjI",
        "state": "Ark Patrol; Veronika Redd",
        "session_id": "140ecdfb976bdbf29d4452d492e551c7",
        "party": {
          "id": "spotify:94490510688792576"
        },
        "name": "Spotify",
        "id": "spotify:1",
        "flags": 48,
        "details": "Let Go",
        "created_at": 1615529838051,
        "assets": {
          "large_text": "Let Go",
          "large_image": "spotify:ab67616d0000b27364840995fe43bb2ec73a241d"
        }
      },
      {
        "type": 0,
        "timestamps": {
          "start": 1615438153941
        },
        "state": "Workspace: lanyard",
        "name": "Visual Studio Code",
        "id": "66b84f5317e9de6c",
        "details": "Editing README.md",
        "created_at": 1615529838050,
        "assets": {
          "small_text": "Visual Studio Code",
          "small_image": "565945770067623946",
          "large_text": "Editing a MARKDOWN file",
          "large_image": "565945077491433494"
        },
        "application_id": "383226320970055681"
      }
    ]
  }
}

KV

Lanyard KV is a a dynamic, real-time key->value 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.

Use cases

  • Configuration values for your website
  • Configuration values for Lanyard 3rd party projects
  • Dynamic data for your website/profile (e.g. current location)

Limits

  1. Keys and values can only be strings
  2. Values can be 30,000 characters maximum
  3. Keys must be alphanumeric (a-zA-Z0-9) and 255 characters max length
  4. Your user can have a maximum of 512 key->value pairs linked

Getting an API Key

DM the Lanyard bot (Lanyard#5766) with .apikey to get your API key.

When making Lanyard KV API requests, set an Authorization header with the API key you received from the Lanyard bot as the value.

Setting a key->value pair

Discord

.set <key> <value>

HTTP

PUT https://api.lanyard.rest/v1/users/:user_id/kv/:key The 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.

Setting multiple key->value pairs

Discord

Not yet implemented

HTTP

PATCH https://api.lanyard.rest/v1/users/:user_id/kv The 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.

Deleting a key

Discord

.del <key>

HTTP

DELETE https://api.lanyard.rest/v1/users/:user_id/kv/:key

Socket Docs

The 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.

Once 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.

You should send Opcode 2: Initialize immediately after receiving Opcode 1.

Example of Opcode 2: Initialize:

{
  op: 2,
  d: {
    // subscribe_to_ids should be an array of user IDs you want to subscribe to presences from
    // if Lanyard doesn't monitor an ID specified, it won't be included in INIT_STATE
    subscribe_to_ids: ["94490510688792576"]
  }
}

Subscribing to multiple user presences

To 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->presence map. You can find examples below.

Subscribing to a single user presence

If 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->presence map.

Subscribing to every user presence

If 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->presence map with every user presence in INIT_STATE, and their respective PRESENCE_UPDATES when they happen.

Once 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).

List of Opcodes

Opcode Name Description Client Send/Recv
0 Event This is the default opcode when receiving core events from Lanyard, like INIT_STATE Receive
1 Hello Lanyard sends this when clients initially connect, and it includes the heartbeat interval Receive Only
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
3 Heartbeat Clients should send Opcode 3 every 30 seconds (or whatever the Hello Opcode says to heartbeat at) Send only

Events

Events are received on Opcode 0: Event - the event type will be part of the root message object under the t key.

Example Event Message Objects

INIT_STATE

{
  op: 0,
  seq: 1,
  t: "INIT_STATE",
  d: {
    "94490510688792576": {
      // Full Lanyard presence (see API docs above for example)
    }
  }
}

PRESENCE_UPDATE

{
  op: 0,
  seq: 2,
  t: "PRESENCE_UPDATE",
  d: {
    // Full Lanyard presence and an extra "user_id" field
  }
}

Error Codes

Lanyard 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.

Types of Errors

Name Code Data
Invalid/Unknown Opcode 4004 unknown_opcode
Opcode Requires Data 4005 requires_data_object
Invalid Payload 4006 invalid_payload

Quicklinks

Lanyard quicklinks allow you to easily access resources from Discord, such as profile pictures.

User Icons

https://api.lanyard.rest/<id>.<file_type> Where id is the Discord user ID. file_type can be one of: png, gif, webp, jpg, or jpeg

Self-host with Docker

Build the Docker image by cloning this repo and running:

# The latest version is already on the docker hub, you can skip this step unless you would like to run a modified version.
docker build -t phineas/lanyard:latest .

If you don't already have a redis server you'll need to run one, here's the docker command to run one:

docker run -d --name lanyard-redis -v docker_mount_location_on_host:/data redis

And run Lanyard API server using:

docker run --rm -it -p 4001:4001 -e REDIS_HOST=redis -e BOT_TOKEN=<token> --link lanyard-redis:redis phineas/lanyard:latest

You'll be able to access the API using port 4001.

You also need to create a Discord bot and use its token above.

Create a bot here: https://discord.com/developers/applications

Make sure you enable these settings in your bot settings:

  • Privileged Gateway Intents > PRESENCE INTENT
  • Privileged Gateway Intents > SERVER MEMBERS INTENT

If you'd like to run Lanyard with docker-compose, here's an example:

version: "3.8"

services:
  redis:
    image: redis
    restart: always
    container_name: lanyard_redis
  lanyard:
    image: phineas/lanyard:latest
    restart: always
    container_name: lanyard
    depends_on:
      - redis
    ports:
      - 4001:4001
    environment:
      BOT_TOKEN: <token>
      REDIS_HOST: redis

Note, that you're hosting a http server, not https. You'll need to use a reverse proxy such as traefik if you want to secure your API endpoint.

Showcase

Below 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.