- Removed all Grok-related code, API routes, and services - Removed crawl4ai service and meta-crawl client - Simplified Settings to always show cookie inputs for Meta AI - Hid advanced wrapper settings behind collapsible section - Provider selection now only shows Whisk and Meta AI - Fixed unused imports and type definitions
208 lines
7.6 KiB
TypeScript
208 lines
7.6 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { MetaAIClient } from '@/lib/providers/meta-client';
|
|
|
|
/**
|
|
* Meta AI Chat API
|
|
* Uses MetaAIClient directly (GraphQL-based) for Llama 3 chat
|
|
* No external Crawl4AI service needed
|
|
*/
|
|
export async function POST(req: NextRequest) {
|
|
try {
|
|
const body = await req.json();
|
|
const { message, history, metaCookies } = body;
|
|
|
|
if (!message) {
|
|
return NextResponse.json({ error: "Message is required" }, { status: 400 });
|
|
}
|
|
|
|
if (!metaCookies) {
|
|
return NextResponse.json(
|
|
{ error: "Meta AI cookies required. Configure in Settings." },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
console.log(`[Meta Chat] Message: "${message.substring(0, 50)}..."`);
|
|
|
|
// Use MetaAIClient to send chat message
|
|
// The Meta AI API is primarily designed for image generation,
|
|
// but we can use it for text chat by not prefixing with "Imagine"
|
|
const client = new MetaAIClient({ cookies: metaCookies });
|
|
|
|
// For chat, we need to initialize session and send via GraphQL
|
|
// Since MetaAIClient.generate() adds "Imagine" prefix for images,
|
|
// we'll create a direct chat method or adapt the prompt
|
|
|
|
// Send message directly - the response will contain text, not images
|
|
const chatPrompt = message; // Don't add "Imagine" prefix for chat
|
|
|
|
try {
|
|
// Use the internal sendPrompt mechanism via generate
|
|
// But we'll extract the text response instead of images
|
|
const response = await sendMetaChatMessage(client, chatPrompt, metaCookies);
|
|
return NextResponse.json({ response });
|
|
} catch (chatError: any) {
|
|
// If the direct approach fails, provide helpful error
|
|
throw new Error(chatError.message || "Failed to get response from Meta AI");
|
|
}
|
|
|
|
} catch (error: any) {
|
|
console.error('[Meta Chat] Error:', error);
|
|
|
|
const msg = error.message || "";
|
|
const isAuthError = msg.includes("401") || msg.includes("cookies") ||
|
|
msg.includes("expired") || msg.includes("Login");
|
|
|
|
return NextResponse.json(
|
|
{ error: error.message || 'Internal Server Error' },
|
|
{ status: isAuthError ? 401 : 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a chat message to Meta AI and extract text response
|
|
*/
|
|
async function sendMetaChatMessage(client: MetaAIClient, message: string, cookies: string): Promise<string> {
|
|
const META_AI_BASE = "https://www.meta.ai";
|
|
const GRAPHQL_ENDPOINT = `${META_AI_BASE}/api/graphql/`;
|
|
|
|
// Normalize cookies from JSON array to string format
|
|
const normalizedCookies = normalizeCookies(cookies);
|
|
|
|
// First we need to get session tokens
|
|
const sessionResponse = await fetch(META_AI_BASE, {
|
|
headers: {
|
|
"Cookie": normalizedCookies,
|
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
|
|
}
|
|
});
|
|
const html = await sessionResponse.text();
|
|
|
|
// Extract LSD token
|
|
const lsdMatch = html.match(/\"LSD\",\[\],\{\"token\":\"([^\"]+)\"/) ||
|
|
html.match(/\"lsd\":\"([^\"]+)\"/) ||
|
|
html.match(/name=\"lsd\" value=\"([^\"]+)\"/);
|
|
const lsd = lsdMatch?.[1] || '';
|
|
|
|
// Extract access token
|
|
const tokenMatch = html.match(/\"accessToken\":\"([^\"]+)\"/);
|
|
const accessToken = tokenMatch?.[1];
|
|
|
|
if (html.includes('login_form') || html.includes('login_page')) {
|
|
throw new Error("Meta AI: Cookies expired. Please update in Settings.");
|
|
}
|
|
|
|
// Send chat message
|
|
const variables = {
|
|
message: {
|
|
text: message,
|
|
content_type: "TEXT"
|
|
},
|
|
source: "PDT_CHAT_INPUT",
|
|
external_message_id: Math.random().toString(36).substring(2) + Date.now().toString(36)
|
|
};
|
|
|
|
const body = new URLSearchParams({
|
|
fb_api_caller_class: "RelayModern",
|
|
fb_api_req_friendly_name: "useAbraSendMessageMutation",
|
|
variables: JSON.stringify(variables),
|
|
doc_id: "7783822248314888",
|
|
...(lsd && { lsd }),
|
|
});
|
|
|
|
const response = await fetch(GRAPHQL_ENDPOINT, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
"Cookie": normalizedCookies,
|
|
"Origin": META_AI_BASE,
|
|
"Referer": `${META_AI_BASE}/`,
|
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
|
"Sec-Fetch-Site": "same-origin",
|
|
"Sec-Fetch-Mode": "cors",
|
|
"Sec-Fetch-Dest": "empty",
|
|
"Accept": "application/json",
|
|
"Accept-Language": "en-US,en;q=0.9",
|
|
...(accessToken && { "Authorization": `OAuth ${accessToken}` })
|
|
},
|
|
body: body.toString()
|
|
});
|
|
|
|
// Get response text first
|
|
const responseText = await response.text();
|
|
|
|
// Check if response is HTML (error page)
|
|
if (responseText.trim().startsWith('<') || responseText.includes('<!DOCTYPE')) {
|
|
console.error('[Meta Chat] Received HTML instead of JSON:', responseText.substring(0, 200));
|
|
if (responseText.includes('login') || responseText.includes('checkpoint')) {
|
|
throw new Error("Meta AI: Cookies expired or account requires verification. Please update cookies in Settings.");
|
|
}
|
|
throw new Error("Meta AI: Service returned an error page. Please try again later.");
|
|
}
|
|
|
|
if (!response.ok) {
|
|
console.error('[Meta Chat] Response not OK:', response.status, responseText.substring(0, 200));
|
|
throw new Error(`Meta AI Error: ${response.status}`);
|
|
}
|
|
|
|
// Parse JSON response
|
|
let data;
|
|
try {
|
|
data = JSON.parse(responseText);
|
|
} catch (e) {
|
|
console.error('[Meta Chat] Failed to parse JSON:', responseText.substring(0, 200));
|
|
throw new Error("Meta AI: Invalid response format. Please try again.");
|
|
}
|
|
|
|
// Extract text response from the GraphQL response
|
|
const messageData = data?.data?.node?.bot_response_message ||
|
|
data?.data?.xabraAIPreviewMessageSendMutation?.message;
|
|
|
|
// Check for GraphQL errors
|
|
if (data?.errors) {
|
|
console.error('[Meta Chat] GraphQL errors:', JSON.stringify(data.errors));
|
|
throw new Error("Meta AI: Request failed. " + (data.errors[0]?.message || "Unknown error"));
|
|
}
|
|
|
|
const textResponse = messageData?.text ||
|
|
messageData?.snippet ||
|
|
messageData?.message_text ||
|
|
"I'm sorry, I couldn't generate a response.";
|
|
|
|
return textResponse;
|
|
}
|
|
|
|
/**
|
|
* Normalize cookies from JSON array format to header string format
|
|
* Handles: [{name: "foo", value: "bar"}, ...] -> "foo=bar; ..."
|
|
*/
|
|
function normalizeCookies(cookies: string): string {
|
|
if (!cookies) return '';
|
|
|
|
try {
|
|
const trimmed = cookies.trim();
|
|
// Check if it's JSON array
|
|
if (trimmed.startsWith('[')) {
|
|
const parsed = JSON.parse(trimmed);
|
|
if (Array.isArray(parsed)) {
|
|
return parsed
|
|
.map((c: any) => `${c.name}=${c.value}`)
|
|
.join('; ');
|
|
}
|
|
}
|
|
// Check if it's JSON object with multiple arrays (merged cookies)
|
|
if (trimmed.startsWith('{')) {
|
|
const parsed = JSON.parse(trimmed);
|
|
// If it's an object, iterate values
|
|
const entries = Object.entries(parsed)
|
|
.map(([k, v]) => `${k}=${v}`)
|
|
.join('; ');
|
|
return entries;
|
|
}
|
|
} catch (e) {
|
|
// Not JSON, assume it's already a string format
|
|
}
|
|
|
|
return cookies;
|
|
}
|