@listify/sdk
v1.0.2
Published
Official Listify SDK for pushing bot stats, managing commands, and handling webhooks
Maintainers
Readme
Listify SDK
Official TypeScript SDK for Listify - the bot directory platform. Push stats, sync commands, check votes, and handle webhooks with full type safety.
Features
- Full API Client - Push stats, sync commands, check votes, get analytics
- Webhook Verification - HMAC-SHA256 signature validation with replay attack protection
- Widget Generator - Embeddable badges and widgets for your README
- Type Safe - Full TypeScript support with autocomplete
- Retry Logic - Automatic retries with exponential backoff
- Rate Limiting - Built-in rate limit handling
- Response Caching - Configurable caching for GET requests
- Logging - Configurable log levels and formats
Installation
npm install @listify/sdkOr
yarn install @listify/sdkOr
pnpm install @listify/sdkRequirements
- Node.js 18.x or higher
- TypeScript 4.5 or higher (for type definitions)
- Express 4.x or 5.x (optional, for webhook middleware)
Usage
1. API Client
import { Api } from '@listify/sdk';
const client = new Api('your-bot-id', {
token: process.env.LISTIFY_API_TOKEN,
debug: true,
logLevel: 'info'
});
// Push bot statistics
await client.pushStats({
platform: 'discord',
server_count: 420,
user_count: 45000,
shard_count: 4,
status: 'online'
});
// Check if a user has voted
const { voted, voted_at, next_vote_at } = await client.checkVote({
platform: 'discord',
userId: '80351110224678912'
});
if (voted) {
console.log(`User voted at ${voted_at}`);
await grantRewards(userId);
}
// Sync slash commands to your listing
const commands = await client.application.commands.fetch();
await client.syncCommands({
platform: 'discord',
commands: [...commands.values()]
});
// Get bot information
const bot = await client.getBot();
console.log(`Bot: ${bot.name}, Votes: ${bot.votes.total}`);
// Get stats history
const history = await client.getStatsHistory(7);
// Search for bots
const results = await client.searchBots('music', { limit: 10 });2. Webhook Handler
import { Webhook } from '@listify/sdk';
import express from 'express';
const app = express();
const webhook = new Webhook(process.env.LISTIFY_WEBHOOK_SECRET, {
debug: true,
timestampWindow: 300000
});
app.post('/webhook', webhook.listener(async (payload) => {
console.log(`Received ${payload.event} event`);
if (payload.event === 'vote.created') {
const userId = payload.data.identities.discord?.id;
if (userId) {
await grantRewards(userId);
}
}
if (payload.event === 'review.created') {
console.log(`New review: ${payload.data.rating}/5 stars`);
}
}));
app.listen(8080);Webhook Signature Verification
const isValid = webhook.validateSignature(
req.headers['x-listify-timestamp'],
req.headers['x-listify-signature'],
rawBody
);
if (!isValid) {
return res.status(403).json({ error: 'Invalid signature' });
}Generate Test Payload
const testPayload = webhook.createTestPayload(
'your-bot-id',
'my-bot',
'My Bot'
);3. Widget Generator
import { Widget, createWidget } from '@listify/sdk';
// Generate widget URLs
const votesUrl = Widget.votes('discord', 'bot', 'your-bot-id', {
theme: 'dark',
minimal: true,
color: '#5865F2'
});
const largeUrl = Widget.large('discord', 'bot', 'your-bot-id', {
theme: 'dark',
bg: '#5865F2',
color: '#ffffff'
});
// Generate HTML for embedding
const html = Widget.toHTML(votesUrl, 'Vote on Listify', {
className: 'badge',
style: { margin: '10px' }
});
// Generate Markdown
const markdown = Widget.toMarkdown(votesUrl, 'Vote on Listify');
// Use the helper function
const url = createWidget('votes', 'discord', 'bot', 'your-bot-id', {
minimal: true
});4. Error Handling
import { LitsifyAPIError, isLitsifyAPIError } from '@listify/sdk';
try {
await client.pushStats({
platform: 'discord',
server_count: 420
});
} catch (error) {
if (isLitsifyAPIError(error)) {
console.error(`API Error ${error.status}: ${error.message}`);
console.error(`Path: ${error.path}, Method: ${error.method}`);
if (error.isAuthError()) {
console.error('Invalid API token');
}
if (error.isRateLimitError()) {
console.error(`Rate limited, retry in ${error.getRetryDelay()}ms`);
}
if (error.shouldRetry()) {
await retryRequest();
}
}
}API Reference
Api Class
| Method | Description |
|--------|-------------|
| pushStats(input) | Push bot statistics |
| updateStatus(input) | Update bot runtime status |
| syncCommands(input) | Sync slash commands to listing |
| checkVote(input) | Check if user has voted |
| getBot(botId?) | Get bot information |
| getStatsHistory(days) | Get stats history |
| getVotes(options) | Get paginated votes |
| getAnalytics() | Get bot analytics |
| getReviews(options) | Get bot reviews |
| searchBots(query, options) | Search for bots |
| healthCheck() | Check API health |
| clearCache() | Clear response cache |
| getRateLimitStatus() | Get current rate limit status |
Webhook Class
| Method | Description |
|--------|-------------|
| listener(fn) | Express middleware for webhooks |
| validateSignature(timestamp, signature, rawBody) | Verify webhook signature |
| generateSignature(timestamp, rawBody) | Generate test signature |
| createTestPayload(botId, slug, name) | Create test webhook payload |
Widget Class
| Method | Description |
|--------|-------------|
| large(platform, type, id, options) | Large widget URL |
| votes(platform, type, id, options) | Vote count widget |
| owner(platform, type, id, options) | Owner widget |
| social(platform, type, id, options) | Social stats widget |
| rating(platform, type, id, options) | Rating widget |
| status(platform, type, id, options) | Status widget |
| combined(platform, type, id, options) | Combined stats widget |
| toHTML(url, alt, options) | Generate HTML img tag |
| toMarkdown(url, alt) | Generate Markdown image |
Type Definitions
Event Types
type WebhookEvent =
| 'vote.created'
| 'vote.deleted'
| 'review.created'
| 'review.updated'
| 'review.deleted'
| 'listing.verified'
| 'listing.suspended'
| 'deployment.verified'
| 'deployment.suspended'
| 'bot.claimed'
| 'analytics.updated';Type Guards
import { isVoteCreated, isReviewCreated } from '@listify/sdk';
if (isVoteCreated(payload)) {
const userId = payload.data.identities.discord?.id;
}
if (isReviewCreated(payload)) {
const rating = payload.data.rating;
}Development
Local Setup
git clone https://github.com/phenuop/listify-sdk.git
cd listify-sdk
npm install
npm run build
npm linkTesting
npm test
npm run typecheck
npm run devPublishing
npm run build
npm publish --access publicLicense
MIT
