🔌 Client Packages
Overview
OKai's client packages enable integration with various platforms and services. Each client provides a standardized interface for sending and receiving messages, handling media, and interacting with platform-specific features.
Architecture Overview
Available Clients
- Discord (
@okai/client-discord
) - Full Discord bot integration - Twitter (
@okai/client-twitter
) - Twitter bot and interaction handling - Telegram (
@okai/client-telegram
) - Telegram bot integration - Direct (
@okai/client-direct
) - Direct API interface for custom integrations - Auto (
@okai/client-auto
) - Automated trading and interaction client
Installation
# Discord
pnpm add @okai/client-discord
# Twitter
pnpm add @okai/client-twitter
# Telegram
pnpm add @okai/client-telegram
# Direct API
pnpm add @okai/client-direct
# Auto Client
pnpm add @okai/client-auto
Discord Client
The Discord client provides full integration with Discord's features including voice, reactions, and attachments.
Basic Setup
import { DiscordClientInterface } from "@okai/client-discord";
// Initialize client
const client = await DiscordClientInterface.start(runtime);
// Configuration in .env
DISCORD_APPLICATION_ID = your_app_id;
DISCORD_API_TOKEN = your_bot_token;
Features
- Voice channel integration
- Message attachments
- Reactions handling
- Media transcription
- Room management
Voice Integration
class VoiceManager {
// Join a voice channel
async handleJoinChannelCommand(interaction) {
await this.joinVoiceChannel(channel);
}
// Handle voice state updates
async handleVoiceStateUpdate(oldState, newState) {
if (newState.channelId) {
await this.handleUserJoinedChannel(newState);
}
}
}
Message Handling
class MessageManager {
async handleMessage(message) {
// Ignore bot messages
if (message.author.bot) return;
// Process attachments
if (message.attachments.size > 0) {
await this.processAttachments(message);
}
// Generate response
await this.generateResponse(message);
}
}
Twitter Client
The Twitter client enables posting, searching, and interacting with Twitter users.
Basic Setup
import { TwitterClientInterface } from "@okai/client-twitter";
// Initialize client
const client = await TwitterClientInterface.start(runtime);
// Configuration in .env
TWITTER_USERNAME = your_username;
TWITTER_PASSWORD = your_password;
TWITTER_EMAIL = your_email;
TWITTER_COOKIES = your_cookies;
Components
- PostClient: Handles creating and managing posts
- SearchClient: Handles search functionality
- InteractionClient: Manages user interactions
Post Management
class TwitterPostClient {
async createPost(content: string) {
return await this.post({
text: content,
media: await this.processMedia(),
});
}
async replyTo(tweetId: string, content: string) {
return await this.post({
text: content,
reply: { in_reply_to_tweet_id: tweetId },
});
}
}
Search Features
class TwitterSearchClient {
async searchTweets(query: string) {
return await this.search({
query,
filters: {
recency: "recent",
language: "en",
},
});
}
}
Telegram Client
The Telegram client provides messaging and bot functionality for Telegram.
Basic Setup
import { TelegramClientInterface } from "@okai/client-telegram";
// Initialize client
const client = await TelegramClientInterface.start(runtime);
// Configuration in .env
TELEGRAM_BOT_TOKEN = your_bot_token;
Message Management
class TelegramClient {
async handleMessage(message) {
// Process message content
const content = await this.processMessage(message);
// Generate response
const response = await this.generateResponse(content);
// Send response
await this.sendMessage(message.chat.id, response);
}
}
Direct Client
The Direct client provides a REST API interface for custom integrations.
Basic Setup
import { DirectClientInterface } from "@okai/client-direct";
// Initialize client
const client = await DirectClientInterface.start(runtime);
API Endpoints
class DirectClient {
constructor() {
// Message endpoint
this.app.post("/:agentId/message", async (req, res) => {
const response = await this.handleMessage(req.body);
res.json(response);
});
// Image generation endpoint
this.app.post("/:agentId/image", async (req, res) => {
const images = await this.generateImage(req.body);
res.json(images);
});
}
}
Auto Client
The Auto client enables automated interactions and trading.
Basic Setup
import { AutoClientInterface } from "@okai/client-auto";
// Initialize client
const client = await AutoClientInterface.start(runtime);
Automated Trading
class AutoClient {
constructor(runtime: IAgentRuntime) {
this.runtime = runtime;
// Start trading loop
this.interval = setInterval(
() => {
this.makeTrades();
},
60 * 60 * 1000,
); // 1 hour interval
}
async makeTrades() {
// Get recommendations
const recommendations = await this.getHighTrustRecommendations();
// Analyze tokens
const analysis = await this.analyzeTokens(recommendations);
// Execute trades
await this.executeTrades(analysis);
}
}
Common Features
Message Handling
All clients implement standard message handling:
interface ClientInterface {
async handleMessage(message: Message): Promise<void>;
async generateResponse(context: Context): Promise<Response>;
async sendMessage(destination: string, content: Content): Promise<void>;
}
Media Processing
interface MediaProcessor {
async processImage(image: Image): Promise<ProcessedImage>;
async processVideo(video: Video): Promise<ProcessedVideo>;
async processAudio(audio: Audio): Promise<ProcessedAudio>;
}
Error Handling
class BaseClient {
protected async handleError(error: Error) {
console.error("Client error:", error);
if (error.code === "RATE_LIMIT") {
await this.handleRateLimit(error);
} else if (error.code === "AUTH_FAILED") {
await this.refreshAuth();
}
}
}
Best Practices
-
Authentication
- Store credentials securely in environment variables
- Implement token refresh mechanisms
- Handle authentication errors gracefully
-
Rate Limiting
- Implement exponential backoff
- Track API usage
- Queue messages during rate limits
-
Error Handling
- Log errors with context
- Implement retry logic
- Handle platform-specific errors
-
Media Processing
- Validate media before processing
- Handle different file formats
- Implement size limits
Error Handling
class BaseClient {
protected async handleError(error: Error) {
if (error.code === "RATE_LIMIT") {
await this.handleRateLimit(error);
} else if (error.code === "AUTH_FAILED") {
await this.refreshAuth();
} else if (error.code === "NETWORK_ERROR") {
await this.reconnect();
}
// Log error
console.error("Client error:", {
type: error.name,
message: error.message,
code: error.code,
stack: error.stack,
});
}
}
Resource Management
class ClientManager {
private async cleanup() {
// Close connections
await Promise.all(this.connections.map((conn) => conn.close()));
// Clear caches
this.cache.clear();
// Cancel timers
this.timers.forEach((timer) => clearInterval(timer));
}
private async reconnect() {
await this.cleanup();
await wait(this.calculateBackoff());
await this.initialize();
}
}
Rate Limiting
class RateLimiter {
private async handleRateLimit(error: RateLimitError) {
const delay = this.calculateBackoff(error);
await wait(delay);
return this.retryRequest();
}
private calculateBackoff(error: RateLimitError): number {
return Math.min(this.baseDelay * Math.pow(2, this.attempts), this.maxDelay);
}
}
Performance Optimization
Connection Management
class ClientManager {
private reconnect() {
await this.disconnect();
await wait(this.backoff());
await this.connect();
}
}
Message Queuing
class MessageQueue {
async queueMessage(message: Message) {
await this.queue.push(message);
this.processQueue();
}
}
Troubleshooting
Common Issues
- Authentication Failures
// Implement token refresh
async refreshAuth() {
const newToken = await this.requestNewToken();
await this.updateToken(newToken);
}
- Rate Limits
// Handle rate limiting
async handleRateLimit(error) {
const delay = this.calculateBackoff(error);
await wait(delay);
return this.retryRequest();
}
- Connection Issues
// Implement reconnection logic
async handleDisconnect() {
await this.reconnect({
maxAttempts: 5,
backoff: 'exponential'
});
}
- Message Processing Failure
async processMessage(message) {
try {
return await this.messageProcessor(message);
} catch (error) {
if (error.code === "INVALID_FORMAT") {
return this.handleInvalidFormat(message);
}
throw error;
}
}