* Initial plan * feat: add public API, SDK, and comprehensive documentation Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com> * test: add SDK tests and public API integration tests Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com> * fix: improve type safety for query parameters Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com> * security: update axios to fix DoS vulnerability Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com>
@spywatcher/sdk
Official TypeScript/JavaScript SDK for the Discord Spywatcher API.
Installation
npm install @spywatcher/sdk
or
yarn add @spywatcher/sdk
Quick Start
import { Spywatcher } from '@spywatcher/sdk';
// Initialize the client
const client = new Spywatcher({
baseUrl: 'https://api.spywatcher.com',
apiKey: 'spy_live_your_api_key_here',
timeout: 30000, // optional, default: 30000ms
debug: false, // optional, enable debug logging
});
// Use the API
try {
// Get ghost users (inactive users)
const ghosts = await client.analytics.getGhosts();
console.log('Ghost users:', ghosts);
// Get lurkers (low activity users)
const lurkers = await client.analytics.getLurkers();
console.log('Lurkers:', lurkers);
// Get suspicion data
const suspicions = await client.getSuspicionData();
console.log('Suspicious users:', suspicions);
} catch (error) {
if (error instanceof SpywatcherError) {
console.error('API Error:', error.message, error.statusCode);
}
}
Authentication
The SDK uses API key authentication. You can generate an API key from the Spywatcher dashboard:
- Log in to the Spywatcher dashboard
- Navigate to Settings > API Keys
- Click "Create New API Key"
- Copy your API key (format:
spy_live_...)
⚠️ Keep your API key secure! Never commit it to version control or expose it in client-side code.
API Reference
Configuration
interface SpywatcherConfig {
baseUrl: string; // Base URL of the Spywatcher API
apiKey: string; // API key (format: spy_live_...)
timeout?: number; // Request timeout in ms (default: 30000)
debug?: boolean; // Enable debug logging (default: false)
headers?: Record<string, string>; // Custom headers
}
Analytics API
The Analytics API provides access to user analytics and behavioral data.
Get Ghost Users
const ghosts = await client.analytics.getGhosts({
guildId: 'optional-guild-id',
startDate: '2024-01-01',
endDate: '2024-12-31',
page: 1,
perPage: 50,
});
Get Lurkers
const lurkers = await client.analytics.getLurkers({
guildId: 'optional-guild-id',
});
Get Activity Heatmap
const heatmap = await client.analytics.getHeatmap({
guildId: 'optional-guild-id',
startDate: '2024-01-01',
endDate: '2024-12-31',
});
Get Role Changes
const roleChanges = await client.analytics.getRoleChanges({
page: 1,
perPage: 50,
});
Get Client Data
const clients = await client.analytics.getClients();
Get Status Shifts
const shifts = await client.analytics.getShifts();
Suspicion API
Get Suspicion Data
const suspicions = await client.getSuspicionData({
guildId: 'optional-guild-id',
});
Timeline API
Get Timeline Events
const timeline = await client.getTimeline({
page: 1,
perPage: 50,
});
Get User Timeline
const userTimeline = await client.getUserTimeline('user-id', {
page: 1,
perPage: 50,
});
Bans API
Get Banned Guilds
const bannedGuilds = await client.getBannedGuilds();
Ban a Guild
await client.banGuild('guild-id', 'Reason for ban');
Unban a Guild
await client.unbanGuild('guild-id');
Get Banned Users
const bannedUsers = await client.getBannedUsers();
Ban a User
await client.banUser('user-id', 'Reason for ban');
Unban a User
await client.unbanUser('user-id');
Auth & User API
Get Current User
const user = await client.getCurrentUser();
Get API Keys
const apiKeys = await client.getApiKeys();
Create API Key
const newKey = await client.createApiKey('My API Key', ['read', 'write']);
console.log('New API key:', newKey.key); // Save this securely!
Revoke API Key
await client.revokeApiKey('key-id');
Health Check
const health = await client.healthCheck();
console.log('API Status:', health.status);
Error Handling
The SDK provides custom error classes for different types of errors:
import {
SpywatcherError,
AuthenticationError,
RateLimitError,
ValidationError,
} from '@spywatcher/sdk';
try {
const data = await client.analytics.getGhosts();
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Authentication failed:', error.message);
} else if (error instanceof RateLimitError) {
console.error('Rate limit exceeded:', error.message);
} else if (error instanceof ValidationError) {
console.error('Validation failed:', error.message);
} else if (error instanceof SpywatcherError) {
console.error('API error:', error.message, error.statusCode);
} else {
console.error('Unexpected error:', error);
}
}
TypeScript Support
The SDK is written in TypeScript and includes full type definitions. All API methods return strongly-typed data:
import { Spywatcher, GhostUser, LurkerUser } from '@spywatcher/sdk';
const client = new Spywatcher({ /* ... */ });
// TypeScript knows the exact shape of the data
const ghosts: GhostUser[] = await client.analytics.getGhosts();
const lurkers: LurkerUser[] = await client.analytics.getLurkers();
Rate Limiting
The Spywatcher API enforces rate limits to ensure fair usage:
- Global: 100 requests per 15 minutes
- Analytics endpoints: 30 requests per minute
- Admin endpoints: 100 requests per 15 minutes
The SDK will throw a RateLimitError when rate limits are exceeded. Implement exponential backoff or retry logic as needed:
async function fetchWithRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error instanceof RateLimitError && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}
// Usage
const ghosts = await fetchWithRetry(() => client.analytics.getGhosts());
Examples
See the examples directory for complete example applications:
- Basic Usage - Simple examples of common use cases
- Advanced Analytics - Complex analytics queries
- Error Handling - Comprehensive error handling
- Rate Limiting - Handle rate limits gracefully
License
MIT