Skip to main content
Glama
gpt-plus.ts13.3 kB
/** * GPT Plus Client * * Enables using a ChatGPT Plus subscription as an LLM provider. * Uses browser session cookies to authenticate with ChatGPT. * * IMPORTANT: This is for personal use only. Ensure compliance with OpenAI's Terms of Service. */ import { logger } from '../../logging/logger.js'; import { db } from '../../db/postgres.js'; // Session storage interface interface GPTPlusSession { id: string; accessToken: string; refreshToken?: string; sessionToken?: string; expiresAt: Date; userEmail: string; isPremium: boolean; createdAt: Date; updatedAt: Date; } // ChatGPT conversation interface interface ChatGPTMessage { id: string; role: 'user' | 'assistant' | 'system'; content: string; createTime?: number; } interface ChatGPTConversation { conversationId: string; messages: ChatGPTMessage[]; model: string; } // Response from ChatGPT interface ChatGPTResponse { message: { id: string; content: { parts: string[]; }; role: string; }; conversationId: string; error?: string; } /** * GPT Plus Provider Client * Manages session authentication and API calls to ChatGPT */ class GPTPlusClient { private static instance: GPTPlusClient; private currentSession: GPTPlusSession | null = null; private baseUrl = 'https://chat.openai.com'; private apiUrl = 'https://chat.openai.com/backend-api'; private constructor() { } static getInstance(): GPTPlusClient { if (!GPTPlusClient.instance) { GPTPlusClient.instance = new GPTPlusClient(); } return GPTPlusClient.instance; } /** * Initialize - load session from database if available */ async initialize(): Promise<void> { try { if (!db.isReady()) { logger.warn('Database not ready, GPT Plus session cannot be loaded'); return; } const client = await db.getClient(); try { const result = await client.query<{ id: string; access_token: string; refresh_token: string; session_token: string; expires_at: Date; user_email: string; is_premium: boolean; created_at: Date; updated_at: Date; }>( 'SELECT * FROM gpt_plus_sessions WHERE expires_at > NOW() ORDER BY updated_at DESC LIMIT 1' ); if (result.rows.length > 0) { const row = result.rows[0]; this.currentSession = { id: row.id, accessToken: row.access_token, refreshToken: row.refresh_token, sessionToken: row.session_token, expiresAt: row.expires_at, userEmail: row.user_email, isPremium: row.is_premium, createdAt: row.created_at, updatedAt: row.updated_at, }; logger.info('GPT Plus session loaded from database', { email: this.currentSession.userEmail, expiresAt: this.currentSession.expiresAt, }); } } finally { client.release(); } } catch (error) { logger.error('Failed to initialize GPT Plus client', { error: error instanceof Error ? error.message : 'Unknown', }); } } /** * Check if GPT Plus is configured and session is valid */ isAvailable(): boolean { if (!this.currentSession) return false; return new Date() < this.currentSession.expiresAt; } /** * Get current session info (without sensitive tokens) */ getSessionInfo(): { email: string; isPremium: boolean; expiresAt: Date } | null { if (!this.currentSession) return null; return { email: this.currentSession.userEmail, isPremium: this.currentSession.isPremium, expiresAt: this.currentSession.expiresAt, }; } /** * Login with access token from browser * Users need to get this from browser dev tools after logging into chat.openai.com */ async loginWithAccessToken( accessToken: string, userEmail: string ): Promise<{ success: boolean; error?: string }> { try { // Verify the token by making a test request const response = await fetch(`${this.apiUrl}/me`, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, }); if (!response.ok) { return { success: false, error: 'Invalid access token' }; } const userProfile = await response.json() as { email?: string; id?: string }; const resolvedEmail = userProfile.email || userEmail; // Check if user has Plus subscription (this might need adjustment based on API response) const isPremium = true; // We assume Plus since user is logging in for this purpose // Calculate expiration (typically 2 weeks for ChatGPT sessions) const expiresAt = new Date(); expiresAt.setDate(expiresAt.getDate() + 14); // Save session to database const sessionId = `gptplus-${Date.now()}`; if (db.isReady()) { const client = await db.getClient(); try { // Delete old sessions for this email await client.query( 'DELETE FROM gpt_plus_sessions WHERE user_email = $1', [resolvedEmail] ); // Insert new session await client.query( `INSERT INTO gpt_plus_sessions (id, access_token, user_email, is_premium, expires_at) VALUES ($1, $2, $3, $4, $5)`, [sessionId, accessToken, resolvedEmail, isPremium, expiresAt] ); } finally { client.release(); } } // Update current session this.currentSession = { id: sessionId, accessToken, expiresAt, userEmail: resolvedEmail, isPremium, createdAt: new Date(), updatedAt: new Date(), }; logger.info('GPT Plus login successful', { email: resolvedEmail, expiresAt, }); return { success: true }; } catch (error) { logger.error('GPT Plus login failed', { error: error instanceof Error ? error.message : 'Unknown', }); return { success: false, error: error instanceof Error ? error.message : 'Login failed', }; } } /** * Logout - clear session */ async logout(): Promise<void> { if (!this.currentSession) return; if (db.isReady()) { const client = await db.getClient(); try { await client.query( 'DELETE FROM gpt_plus_sessions WHERE id = $1', [this.currentSession.id] ); } finally { client.release(); } } logger.info('GPT Plus logged out', { email: this.currentSession.userEmail }); this.currentSession = null; } /** * Send a message to ChatGPT */ async chat( messages: Array<{ role: 'user' | 'assistant' | 'system'; content: string }>, options?: { model?: string; conversationId?: string; parentMessageId?: string; } ): Promise<{ content: string; conversationId: string; messageId: string; error?: string; }> { if (!this.isAvailable()) { throw new Error('GPT Plus session not available. Please login first.'); } const model = options?.model || 'gpt-4'; const conversationId = options?.conversationId; const parentMessageId = options?.parentMessageId || crypto.randomUUID(); const accessToken = this.currentSession?.accessToken; if (!accessToken) { throw new Error('GPT Plus session missing access token'); } try { // Format the last user message for ChatGPT API const lastUserMessage = messages.filter(m => m.role === 'user').pop(); if (!lastUserMessage) { throw new Error('No user message provided'); } // Build system prompt if provided const systemMessage = messages.find(m => m.role === 'system'); const prompt = systemMessage ? `${systemMessage.content}\n\n${lastUserMessage.content}` : lastUserMessage.content; const payload = { action: 'next', messages: [ { id: crypto.randomUUID(), author: { role: 'user' }, content: { content_type: 'text', parts: [prompt], }, }, ], model, parent_message_id: parentMessageId, ...(conversationId && { conversation_id: conversationId }), }; const response = await fetch(`${this.apiUrl}/conversation`, { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', 'Accept': 'text/event-stream', }, body: JSON.stringify(payload), }); if (!response.ok) { const errorText = await response.text(); throw new Error(`ChatGPT API error: ${response.status} - ${errorText}`); } // Parse SSE response const text = await response.text(); const lines = text.split('\n').filter(line => line.startsWith('data: ')); let lastMessage = ''; let newConversationId = conversationId || ''; let messageId = ''; for (const line of lines) { const data = line.replace('data: ', ''); if (data === '[DONE]') break; try { const parsed = JSON.parse(data) as { message?: { id: string; content: { parts: string[] }; }; conversation_id?: string; }; if (parsed.message?.content?.parts) { lastMessage = parsed.message.content.parts.join(''); messageId = parsed.message.id; } if (parsed.conversation_id) { newConversationId = parsed.conversation_id; } } catch { // Skip invalid JSON lines } } return { content: lastMessage, conversationId: newConversationId, messageId, }; } catch (error) { logger.error('GPT Plus chat failed', { error: error instanceof Error ? error.message : 'Unknown', }); throw error; } } /** * Get available models for GPT Plus users */ getAvailableModels(): Array<{ id: string; name: string; description: string }> { return [ { id: 'gpt-4', name: 'GPT-4', description: 'Most capable model, great for complex tasks', }, { id: 'gpt-4-turbo', name: 'GPT-4 Turbo', description: 'Faster GPT-4 with 128k context', }, { id: 'gpt-4o', name: 'GPT-4o', description: 'Latest GPT-4 with optimized performance', }, { id: 'o1-preview', name: 'o1-preview', description: 'Advanced reasoning model', }, { id: 'o1-mini', name: 'o1-mini', description: 'Smaller reasoning model', }, ]; } } // Export singleton instance export const gptPlusClient = GPTPlusClient.getInstance(); // Export types export type { GPTPlusSession, ChatGPTMessage, ChatGPTConversation, ChatGPTResponse };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/babasida246/ai-mcp-gateway'

If you have feedback or need assistance with the MCP directory API, please join our Discord server