UNPKG

sveltekit-lanyard

Version:

A Svelte 5 library for integrating Discord Lanyard API with type-safe WebSocket and REST support

484 lines (383 loc) 12.1 kB
# sveltekit-lanyard A modern, type-safe Svelte 5 library for seamless Discord Lanyard API integration with real-time WebSocket and REST support. [![npm version](https://img.shields.io/npm/v/sveltekit-lanyard.svg)](https://www.npmjs.com/package/sveltekit-lanyard) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) ## Features - **Fully Type-Safe** - Complete TypeScript support with Discord API types - **Fine-Grained Reactivity** - Built with Svelte 5 runes - **Dual Connection Modes** - Choose between WebSocket (real-time) or REST (polling) - **Automatic Polling** - Optional automatic data refreshing for REST connections - **Auto-Reconnection** - Configurable automatic reconnection for WebSocket connections - **Heartbeat Management** - Automatic WebSocket heartbeat handling to keep connections alive - **Custom URLs** - Support for self-hosted Lanyard instances - **SSR Compatible** - Works seamlessly with SvelteKit's server-side rendering ## Installation ```bash # pnpm pnpm add sveltekit-lanyard # bun bun add sveltekit-lanyard ``` ## Quick Start ### REST Mode ```svelte <script lang="ts"> import { useLanyard } from 'sveltekit-lanyard'; const lanyard = useLanyard({ connectionType: 'rest', userId: '769702535124090904' }); </script> {#if lanyard.loading} <p>Loading presence...</p> {:else if lanyard.data} <p>{lanyard.data.discord_user.username} is {lanyard.data.discord_status}</p> {/if} ``` ### WebSocket Mode ```svelte <script lang="ts"> import { useLanyard } from 'sveltekit-lanyard'; const lanyard = useLanyard({ connectionType: 'ws', subscriptionScope: { subscribe_to_id: '769702535124090904' } }); </script> {#if lanyard.connected && lanyard.data} <p>{lanyard.data.discord_user.username} is {lanyard.data.discord_status}</p> {/if} ``` ## Usage ### REST API The REST API is ideal for simple use cases where you want to fetch presence data on-demand or at intervals. #### Basic Example ```svelte <script lang="ts"> import { useLanyard } from 'sveltekit-lanyard'; const lanyard = useLanyard({ connectionType: 'rest', userId: '94490510688792576' }); </script> <div class="presence-card"> {#if lanyard.loading} <div class="loading"> <p>Loading presence data...</p> </div> {:else if lanyard.error} <div class="error"> <p>Error: {lanyard.error.message}</p> <button onclick={() => lanyard.refetch()}>Retry</button> </div> {:else if lanyard.data} <div class="user-info"> <img src="https://cdn.discordapp.com/avatars/{lanyard.data.discord_user.id}/{lanyard.data .discord_user.avatar}.png" alt="Avatar" /> <h2>{lanyard.data.discord_user.username}</h2> <span class="status status-{lanyard.data.discord_status}"> {lanyard.data.discord_status} </span> </div> <button onclick={() => lanyard.refetch()}>Refresh</button> {/if} </div> ``` #### Manual Refetch ```svelte <script lang="ts"> import { useLanyard } from 'sveltekit-lanyard'; const lanyard = useLanyard({ connectionType: 'rest', userId: '94490510688792576' }); // Refetch every 30 seconds $effect(() => { const interval = setInterval(() => { lanyard.refetch(); }, 30000); return () => clearInterval(interval); }); </script> ``` ### WebSocket API The WebSocket API provides real-time updates whenever a user's presence changes on Discord. #### Single User Subscription ```svelte <script lang="ts"> import { useLanyard } from 'sveltekit-lanyard'; const lanyard = useLanyard({ connectionType: 'ws', subscriptionScope: { subscribe_to_id: '94490510688792576' } }); </script> <div class="presence-realtime"> {#if !lanyard.connected} <p>Connecting to WebSocket...</p> {:else if lanyard.error} <p class="error">{lanyard.error}</p> {:else if lanyard.data} <div class="user"> <h2>{lanyard.data.discord_user.username}</h2> <p>Status: {lanyard.data.discord_status}</p> {#if lanyard.lastUpdate} <p class="timestamp"> Last update: {new Date(lanyard.lastUpdate.d.timestamp).toLocaleString()} </p> {/if} </div> {/if} </div> ``` #### Multiple Users Subscription ```svelte <script lang="ts"> import { useLanyard } from 'sveltekit-lanyard'; const lanyard = useLanyard({ connectionType: 'ws', subscriptionScope: { subscribe_to_ids: ['94490510688792576', '162619778399240192', '261304190928461824'] } }); </script> {#if lanyard.connected && lanyard.data} <div class="users-grid"> {#each Object.values(lanyard.data) as user} <div class="user-card"> <h3>{user.discord_user.username}</h3> <span class="status-{user.discord_status}"> {user.discord_status} </span> {#if user.listening_to_spotify} <div class="spotify-mini"> 🎵 Listening to {user.spotify.song} </div> {/if} </div> {/each} </div> {/if} ``` #### Subscribe to All Users (Advanced) ```svelte <script lang="ts"> import { useLanyard } from 'sveltekit-lanyard'; const lanyard = useLanyard({ connectionType: 'ws', subscriptionScope: { subscribe_to_all: true } }); </script> ``` ## API Reference ### `useLanyard(options)` The main function to initialize a Lanyard connection. #### Options ##### REST Mode Options ```typescript { connectionType: 'rest'; userId: string; // Discord user ID (Snowflake) polling?: boolean; // Enable automatic polling (default: false) pollingInterval?: number; // Polling interval in ms (default: 5000) apiUrl?: string; // Custom REST API URL (default: 'https://api.lanyard.rest/v1') } ``` **REST Options:** - `connectionType` - Must be `'rest'` for REST mode - `userId` - Discord user ID (Snowflake) - `polling` - _Optional_. Enable automatic polling to refresh data at intervals (default: `false`) - `pollingInterval` - _Optional_. Polling interval in milliseconds (default: `5000`, only applies when `polling` is `true`) - `apiUrl` - _Optional_. Custom REST API base URL for self-hosted Lanyard instances (default: `'https://api.lanyard.rest/v1'`) ##### WebSocket Mode Options ```typescript { connectionType: 'ws'; subscriptionScope: | { subscribe_to_id: string } // Single user | { subscribe_to_ids: string[] } // Multiple users (array of user IDs) | { subscribe_to_all: true } // All users maxReconnectAttempts?: number; // Max reconnection attempts (default: 3) reconnectDelay?: number; // Delay between reconnects in ms (default: 1000) wsUrl?: string; // Custom WebSocket URL (default: 'wss://api.lanyard.rest/socket') } ``` **WebSocket Options:** - `connectionType` - Must be `'ws'` for WebSocket mode - `subscriptionScope` - Subscription configuration: - `subscribe_to_id` - Single user ID to subscribe to - `subscribe_to_ids` - Array of user IDs to subscribe to - `subscribe_to_all` - Subscribe to all users (requires special access) - `maxReconnectAttempts` - _Optional_. Maximum number of automatic reconnection attempts (default: `3`) - `reconnectDelay` - _Optional_. Delay in milliseconds between reconnection attempts (default: `1000`) - `wsUrl` - _Optional_. Custom WebSocket URL for self-hosted Lanyard instances (default: `'wss://api.lanyard.rest/socket'`) #### Return Value ##### REST Mode Return ```typescript { data: LanyardPresenceData | null; error: { message: string; code: string } | null; loading: boolean; refetch: () => Promise<void>; } ``` **Properties:** - `data` - The user's presence data, or `null` if not loaded - `error` - Error object if request failed, or `null` - `loading` - `true` during initial load or hydration - `refetch()` - Function to manually refresh the data ##### WebSocket Mode Return ```typescript { data: LanyardPresenceData | Record<string, LanyardPresenceData> | null; error: string | null; connected: boolean; initState: InitStatePayload | null; lastUpdate: PresenceUpdatePayload | null; } ``` **Properties:** - `data` - Presence data (single user object or multi-user record) - `error` - Error message if connection failed, or `null` - `connected` - `true` when WebSocket is connected - `initState` - Initial state received from WebSocket on connection - `lastUpdate` - The most recent presence update event ## Advanced Configuration ### Automatic Polling (REST) Enable automatic polling to keep REST data fresh without manual intervention: ```svelte <script lang="ts"> import { useLanyard } from 'sveltekit-lanyard'; const lanyard = useLanyard({ connectionType: 'rest', userId: '94490510688792576', polling: true, // Enable automatic polling pollingInterval: 3000 // Poll every 3 seconds (default: 5000ms) }); </script> <div> {#if lanyard.data} <p>{lanyard.data.discord_user.username} is {lanyard.data.discord_status}</p> <p class="info">Data automatically refreshes every 3 seconds</p> {/if} <!-- Manual refresh is still available --> <button onclick={() => lanyard.refetch()}>Refresh Now</button> </div> ``` ### Automatic Reconnection (WebSocket) WebSocket connections automatically attempt to reconnect when disconnected: ```svelte <script lang="ts"> import { useLanyard } from 'sveltekit-lanyard'; const lanyard = useLanyard({ connectionType: 'ws', subscriptionScope: { subscribe_to_id: '94490510688792576' }, maxReconnectAttempts: 5, // Try up to 5 times (default: 3) reconnectDelay: 2000 // Wait 2 seconds between attempts (default: 1000ms) }); </script> <div> {#if lanyard.connected} <span class="badge badge-success">Connected</span> {:else if lanyard.error} <span class="badge badge-error">Connection Error</span> <p>Attempting to reconnect...</p> {:else} <span class="badge badge-warning">Connecting...</span> {/if} </div> ``` **Default URLs:** - REST API: `https://api.lanyard.rest/v1` - WebSocket: `wss://api.lanyard.rest/socket` ## Type Definitions ### Core Types ```typescript import type { LanyardPresenceData, LanyardRestApi, LanyardWebSocketApi, LanyardClient, LanyardGeneric } from 'sveltekit-lanyard'; ``` ### LanyardPresenceData ```typescript type LanyardPresenceData = { active_on_discord_mobile: boolean; active_on_discord_desktop: boolean; active_on_discord_embedded: boolean; active_on_discord_web: boolean; listening_to_spotify: boolean; spotify?: { track_id: string; song: string; artist: string; album: string; album_art_url: string; timestamps: { start: number; end: number; }; }; discord_user: { id: string; username: string; avatar: string; discriminator: string; bot: boolean; global_name: string | null; avatar_decoration_data: any | null; display_name: string | null; public_flags: number; }; discord_status: 'online' | 'dnd' | 'idle' | 'offline'; activities: Activity[]; kv: Record<string, string>; }; ``` ### Activity Discord activity object following Discord API types. ```typescript type Activity = { name: string; type: number; state?: string; details?: string; timestamps?: { start?: number; end?: number; }; assets?: { large_image?: string; large_text?: string; small_image?: string; small_text?: string; }; // ... and more Discord activity properties }; ``` ## Examples You can check out a few examples in /examples ## FAQ ### Q: What is Lanyard? **A:** Lanyard is a service that exposes Discord presence data through a simple API. You need to join the [Lanyard Discord server](https://discord.gg/lanyard) to use it with your Discord account. ### Q: How do I get a Discord user ID? **A:** Enable Developer Mode in Discord settings, then right-click a user and select "Copy ID". ### Q: How do I handle errors? **A:** Both modes expose an `error` property. For REST, you can retry with `refetch()`. WebSocket automatically attempts to reconnect based on your `maxReconnectAttempts` setting. ## License MIT © [Mufaro](https://mufaro.dev) ## Credits - [Lanyard API](https://github.com/Phineas/lanyard) by Phineas - Built with [SvelteKit](https://kit.svelte.dev/) - Discord API types from [discord-api-types](https://github.com/discordjs/discord-api-types)