javascript oleh fazzcode 14 Sep 2026, 03.34

Gpt 4.1 Nano

Udah Gitu Aja

#!/usr/bin/env node
/**
 * ============================================================
 *  Overchat.ai — Chat Scraper (auto SSE/JSON)
 *  Endpoint: https://api.overchat.ai/v1/chat/completions
 *  Runtime : Node.js >= 18 (zero dep)
 *
 *  Credits : FazzCodeID
 * ============================================================
 */

import fs from "fs";
import crypto from "crypto";
import { pathToFileURL } from "url";

const CONFIG = {
  BASE_URL: "https://api.overchat.ai/v1",
  MODEL:  "openai/gpt-4o",
  PERSONA_ID: "gpt-4o-landing",
  DEVICE_PLATFORM: "web",
  DEVICE_LANGUAGE: "id-ID",
  DEVICE_VERSION: "1.0.44",
  ORIGIN: "https://overchat.ai",
  TIMEOUT_MS: 180_000,
  DEFAULT_PROMPT: "Siapa Itu FazzCodeID?",
  TEMPERATURE: 0.5,
  TOP_P: 0.95,
  MAX_TOKENS: 4000,
  FREQUENCY_PENALTY: 0,
  PRESENCE_PENALTY: 0,
};

const ts = () => new Date().toISOString().slice(11, 19);
const log = {
  info: (t, m) => console.error(`[${ts()}] [${t}] ${m}`),
  ok:   (t, m) => console.error(`[${ts()}] [\x1b[32m${t}\x1b[0m] ${m}`),
  warn: (t, m) => console.error(`[${ts()}] [\x1b[33m${t}\x1b[0m] ${m}`),
  err:  (t, m) => console.error(`[${ts()}] [\x1b[31m${t}\x1b[0m] ${m}`),
};

const uuid = () => crypto.randomUUID();

function coerce(v) {
  if (typeof v !== "string") return v;
  if (v.trim() === "") return v;
  if (/^-?\d+$/.test(v)) return parseInt(v, 10);
  if (/^-?\d*\.\d+$/.test(v)) return parseFloat(v);
  if (v === "true") return true;
  if (v === "false") return false;
  return v;
}

function buildMessages(prompt, options = {}) {
  if (Array.isArray(options.messages) && options.messages.length > 0) {
    return options.messages;
  }
  const msgs = [];
  msgs.push({ id: uuid(), role: "system", content: options.systemPrompt ?? "" });
  msgs.push({ id: uuid(), role: "user", content: prompt });
  return msgs;
}

function buildHeaders() {
  return {
    "Content-Type": "application/json",
    Accept: "*/*",
    "X-Device-Platform": CONFIG.DEVICE_PLATFORM,
    "X-Device-Language": CONFIG.DEVICE_LANGUAGE,
    "X-Device-Uuid": uuid(),
    "X-Device-Version": CONFIG.DEVICE_VERSION,
    Origin: CONFIG.ORIGIN,
    "User-Agent":
      "Mozilla/5.0 (Linux; Android 13; SM-G991B) AppleWebKit/537.36 " +
      "(KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36",
    Referer: `${CONFIG.ORIGIN}/`,
  };
}

export async function chat(prompt, options = {}) {
  const tStart = Date.now();
  const chatId = options.chatId || uuid();
  const messages = buildMessages(prompt, options);

  const result = {
    success: false,
    chatId,
    model: options.model || CONFIG.MODEL,
    personaId: options.personaId || CONFIG.PERSONA_ID,
    prompt,
    messages_count: messages.length,
    content: null,
    chunks: 0,
    finish_reason: null,
    provider: null,
    usage: null,
    error: null,
    timing: {},
    streaming: false,
    stream_forced: false,
  };

  try {
    const body = {
      chatId,
      model: options.model || CONFIG.MODEL,
      messages,
      personaId: options.personaId || CONFIG.PERSONA_ID,
      frequency_penalty: options.frequencyPenalty ?? CONFIG.FREQUENCY_PENALTY,
      max_tokens: options.maxTokens ?? CONFIG.MAX_TOKENS,
      presence_penalty: options.presencePenalty ?? CONFIG.PRESENCE_PENALTY,
      stream: false,
      temperature: options.temperature ?? CONFIG.TEMPERATURE,
      top_p: options.topP ?? CONFIG.TOP_P,
    };

    log.info("POST", `/chat/completions (request stream=false)`);
    log.info("BODY", `model=${body.model} persona=${body.personaId} chatId=${chatId}`);

    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? CONFIG.TIMEOUT_MS);

    let res;
    try {
      res = await fetch(`${CONFIG.BASE_URL}/chat/completions`, {
        method: "POST",
        signal: controller.signal,
        headers: buildHeaders(),
        body: JSON.stringify(body),
      });
    } finally {
      clearTimeout(timer);
    }

    if (!res.ok) {
      const text = await res.text().catch(() => "");
      throw new Error(`HTTP ${res.status} ${res.statusText}: ${text.slice(0, 300)}`);
    }

    const contentType = (res.headers.get("content-type") || "").toLowerCase();
    log.info("CT", `content-type: ${contentType}`);

    // Branch 1: JSON
    if (contentType.includes("application/json")) {
      const json = await res.json();
      const choice = json?.choices?.[0];
      if (!choice) throw new Error(`Respons tidak valid: ${JSON.stringify(json).slice(0, 200)}`);

      result.content = choice.message?.content ?? null;
      result.finish_reason = choice.finish_reason ?? null;
      result.usage = json.usage ?? null;
      result.provider = json.provider ?? null;
      result.model = json.model ?? result.model;
      result.raw = json;
      result.streaming = false;

      log.ok("MODE", "JSON non-stream");
    }
    // Branch 2: SSE
    else {
      log.warn("MODE", "Server paksa SSE (stream:false diabaikan)");
      result.stream_forced = true;
      result.streaming = true;

      const reader = res.body.getReader();
      const decoder = new TextDecoder("utf-8");
      let buffer = "";
      const aggregated = [];

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });

        let idx;
        while ((idx = buffer.indexOf("\n")) !== -1) {
          const line = buffer.slice(0, idx).trim();
          buffer = buffer.slice(idx + 1);

          if (!line) continue;
          if (line.startsWith(":")) continue;
          if (!line.startsWith("data:")) continue;

          const payload = line.slice(5).trim();
          if (payload === "[DONE]") continue;

          let chunk;
          try {
            chunk = JSON.parse(payload);
          } catch {
            continue;
          }

          if (chunk.provider) result.provider = chunk.provider;
          if (chunk.model) result.model = chunk.model;
          if (chunk.usage) result.usage = chunk.usage;

          const delta = chunk?.choices?.[0]?.delta;
          if (delta?.content) {
            aggregated.push(delta.content);
            result.chunks++;
            options.onChunk?.(delta.content);
          }

          const finish = chunk?.choices?.[0]?.finish_reason;
          if (finish) result.finish_reason = finish;
        }
      }

      result.content = aggregated.join("");
    }

    result.success = true;
    result.timing.total = Date.now() - tStart;
    log.ok("DONE", `${result.timing.total}ms | ${result.chunks} chunks | ${(result.content || "").length} char`);
    if (result.provider) log.info("PROVIDER", result.provider);
  } catch (err) {
    result.error = { message: err.message };
    result.timing.total = Date.now() - tStart;
    log.err("ERROR", err.message);
  }

  return result;
}

export async function chatStream(prompt, options = {}) {
  const tStart = Date.now();
  const chatId = options.chatId || uuid();
  const messages = buildMessages(prompt, options);

  const result = {
    success: false,
    chatId,
    model: options.model || CONFIG.MODEL,
    personaId: options.personaId || CONFIG.PERSONA_ID,
    prompt,
    messages_count: messages.length,
    content: "",
    chunks: 0,
    finish_reason: null,
    provider: null,
    error: null,
    timing: {},
    streaming: true,
  };

  try {
    const body = {
      chatId,
      model: options.model || CONFIG.MODEL,
      messages,
      personaId: options.personaId || CONFIG.PERSONA_ID,
      frequency_penalty: options.frequencyPenalty ?? CONFIG.FREQUENCY_PENALTY,
      max_tokens: options.maxTokens ?? CONFIG.MAX_TOKENS,
      presence_penalty: options.presencePenalty ?? CONFIG.PRESENCE_PENALTY,
      stream: true,
      temperature: options.temperature ?? CONFIG.TEMPERATURE,
      top_p: options.topP ?? CONFIG.TOP_P,
    };

    log.info("POST", `/chat/completions (stream=true)`);
    log.info("BODY", `model=${body.model} persona=${body.personaId} chatId=${chatId}`);

    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? CONFIG.TIMEOUT_MS);

    let res;
    try {
      res = await fetch(`${CONFIG.BASE_URL}/chat/completions`, {
        method: "POST",
        signal: controller.signal,
        headers: buildHeaders(),
        body: JSON.stringify(body),
      });
    } finally {
      clearTimeout(timer);
    }

    if (!res.ok || !res.body) {
      const text = await res.text().catch(() => "");
      throw new Error(`HTTP ${res.status}: ${text.slice(0, 300)}`);
    }

    const reader = res.body.getReader();
    const decoder = new TextDecoder("utf-8");
    let buffer = "";

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });

      let idx;
      while ((idx = buffer.indexOf("\n")) !== -1) {
        const line = buffer.slice(0, idx).trim();
        buffer = buffer.slice(idx + 1);

        if (!line) continue;
        if (line.startsWith(":")) continue;
        if (!line.startsWith("data:")) continue;

        const payload = line.slice(5).trim();
        if (payload === "[DONE]") continue;

        let chunk;
        try {
          chunk = JSON.parse(payload);
        } catch {
          continue;
        }

        if (chunk.provider) result.provider = chunk.provider;
        if (chunk.model) result.model = chunk.model;

        const delta = chunk?.choices?.[0]?.delta;
        if (delta?.content) {
          result.content += delta.content;
          result.chunks++;
          options.onChunk?.(delta.content);
        }

        const finish = chunk?.choices?.[0]?.finish_reason;
        if (finish) result.finish_reason = finish;
      }
    }

    result.success = true;
    result.timing.total = Date.now() - tStart;
    log.ok("DONE", `${result.timing.total}ms | ${result.chunks} chunks | ${result.content.length} char`);
    if (result.provider) log.info("PROVIDER", result.provider);
  } catch (err) {
    result.error = { message: err.message };
    result.timing.total = Date.now() - tStart;
    log.err("ERROR", err.message);
  }

  return result;
}


function printBanner(prompt, opts) {
  console.error("  Overchat.ai Scraper (auto SSE/JSON)  ");
 console.log(" ")
  console.error(`  Model   : ${opts.model}`);
  console.error(`  Persona : ${opts.personaId}`);
  console.error(`  Prompt  : ${prompt.slice(0, 60)}${prompt.length > 60 ? "..." : ""}`);
  console.error(`  Stream  : ${opts.stream ? "yes" : "auto"}`);
  console.error("");
}

function parseArgs(argv) {
  const flags = {};
  const positional = [];
  for (const a of argv) {
    if (a.startsWith("--")) {
      const [k, v] = a.slice(2).split("=");
      flags[k] = v ?? true;
    } else {
      positional.push(a);
    }
  }
  return { positional, flags };
}

async function main() {
  const { positional, flags } = parseArgs(process.argv.slice(2));

  if (positional.length === 0 && !flags.prompt) {
    console.error("Usage:");
    console.error('  node ai/gpt4o.js "hallo"');
    console.error('  node ai/gpt4o.js "hallo" --stream');
    console.error("");
    console.error("Flags:");
    console.error('  --prompt="..."        Prompt');
    console.error('  --system="..."        System prompt');
    console.error("  --model=...           Model (default: openai/gpt-4o)");
    console.error("  --persona=...         PersonaId");
    console.error("  --chatId=uuid         Chat ID");
    console.error("  --temp=0.5            Temperature");
    console.error("  --top_p=0.95          Top-p");
    console.error("  --maxTokens=4000      Max tokens");
    console.error("  --stream              Streaming mode");
    console.error("  --show-content        Live print ke stderr");
    console.error("  --out=answer.txt      Simpan content");
    process.exit(1);
  }

  const prompt = flags.prompt || positional[0] || CONFIG.DEFAULT_PROMPT;

  const opts = {
    model: flags.model || CONFIG.MODEL,
    personaId: flags.persona || CONFIG.PERSONA_ID,
    chatId: flags.chatId,
    systemPrompt: flags.system,
    temperature: flags.temp != null ? coerce(flags.temp) : undefined,
    topP: flags.top_p != null ? coerce(flags.top_p) : undefined,
    maxTokens: flags.maxTokens != null ? coerce(flags.maxTokens) : undefined,
    timeoutMs: flags.timeout ? coerce(flags.timeout) : undefined,
    stream: !!flags.stream,
  };

  printBanner(prompt, opts);

  let result;
  if (opts.stream) {
    result = await chatStream(prompt, {
      ...opts,
      onChunk: flags["show-content"]
        ? (chunk) => process.stderr.write(chunk)
        : undefined,
    });
    if (flags["show-content"]) process.stderr.write("\n");
  } else {
    result = await chat(prompt, opts);
  }

  process.stdout.write(JSON.stringify(result, null, 2) + "\n");

  if (result.success && flags.out && result.content) {
    fs.writeFileSync(flags.out, result.content, "utf-8");
    log.ok("SAVE", `${result.content.length} char -> ${flags.out}`);
  }

  process.exit(result.success ? 0 : 1);
}


const isDirectRun =
  process.argv[1] &&
  import.meta.url === pathToFileURL(process.argv[1]).href;

if (isDirectRun) {
  main().catch((e) => {
    console.error("Fatal:", e);
    process.exit(1);
  });
}
Discussion

Thread

Belum ada komentarJadilah yang pertama membuka diskusi.