EaseMate2api deno

小网站,大量调用会空回

付费模型加个鉴权参数也能2,不过没claude就不搞了

import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
import { crypto } from "https://deno.land/std@0.208.0/crypto/mod.ts";

const MODEL_MAP: Record<string, number> = {
  "llama-3.3": 1,
  "claude-3-haiku": 2,
  "gpt-4o-mini": 3,
  "deepseek-v3": 4,
  "deepseek-r1": 5,
  "gemini-2.0-flash": 6,
  "gemini-2.5-flash": 7,
  "kimi-k2": 10,
  "qwen3-235b": 11,
};

function md5(text: string): string {
  const hash = crypto.subtle.digestSync("MD5", new TextEncoder().encode(text));
  return Array.from(new Uint8Array(hash))
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

function generateVisitorId(): string {
  const randomBytes = crypto.getRandomValues(new Uint8Array(16));
  return Array.from(randomBytes)
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

function generateSign(params: Record<string, any>, visitorId: string) {
  const timestamp = String(Date.now() * 1_000_000);
  
  const sortDict = (d: any): any => {
    if (typeof d !== "object" || d === null) return d;
    return Object.keys(d).sort().reduce((acc, k) => {
      acc[k] = sortDict(d[k]);
      return acc;
    }, {} as any);
  };

  const serialize = (prefix: string, value: any, result: string[]) => {
    if (typeof value === "object" && value !== null && !Array.isArray(value)) {
      Object.entries(value).forEach(([k, v]) => serialize(`${prefix}[${k}]`, v, result));
    } else if (Array.isArray(value)) {
      value.forEach((item, i) => serialize(`${prefix}[${i}]`, item, result));
    } else {
      result.push(`${prefix}=${value}`);
    }
  };

  const sorted = sortDict(params);
  sorted.appKey = "G!JLAE6jg*m&C2m&";
  sorted.timestamp = timestamp;

  const result: string[] = [];
  Object.entries(sorted).forEach(([key, value]) => serialize(key, value, result));

  const signString = `${visitorId}${result.join("&")}${visitorId}`;
  const md5First = md5(signString).split("").reverse().join("").slice(0, 16);
  const finalSign = md5(md5First);

  return { sign: finalSign, timestamp };
}

function processMessages(messages: any[]): string {
  if (!messages || messages.length === 0) return "";
  
  if (messages.length === 1) {
    return messages[0].content || "";
  }

  return messages
    .map(msg => {
      const role = msg.role || "user";
      const content = msg.content || "";
      return `${role}:${content}`;
    })
    .join("\n");
}

function streamToOpenAI(stream: ReadableStream<Uint8Array>, chatId: string, model: string) {
  const reader = stream.getReader();
  const decoder = new TextDecoder();
  const created = Math.floor(Date.now() / 1000);

  return new ReadableStream({
    async start(controller) {
      let buffer = "";
      
      try {
        while (true) {
          const { done, value } = await reader.read();
          if (done) break;

          buffer += decoder.decode(value, { stream: true });
          const lines = buffer.split("\n");
          buffer = lines.pop() || "";

          for (const line of lines) {
            if (!line.startsWith("data: ")) continue;

            try {
              const data = JSON.parse(line.slice(6));
              if (data.code === 200) {
                const answerData = JSON.parse(data.data);
                const content = answerData.answer || "";

                if (content) {
                  const chunk = {
                    id: chatId,
                    object: "chat.completion.chunk",
                    created,
                    model,
                    choices: [{
                      index: 0,
                      delta: { content },
                      finish_reason: null,
                    }],
                  };
                  controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(chunk)}\n\n`));
                }
              }
            } catch (_) {
              continue;
            }
          }
        }

        const finalChunk = {
          id: chatId,
          object: "chat.completion.chunk",
          created,
          model,
          choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
        };
        controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(finalChunk)}\n\n`));
        controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"));
      } finally {
        controller.close();
      }
    },
  });
}

async function collectStream(stream: ReadableStream<Uint8Array>): Promise<string> {
  const reader = stream.getReader();
  const decoder = new TextDecoder();
  let fullContent = "";

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

    const text = decoder.decode(value, { stream: true });
    const lines = text.split("\n");

    for (const line of lines) {
      if (!line.startsWith("data: ")) continue;
      if (line.includes("[DONE]")) continue;

      try {
        const json = JSON.parse(line.slice(6));
        const content = json.choices?.[0]?.delta?.content;
        if (content) fullContent += content;
      } catch (_) {
        continue;
      }
    }
  }

  return fullContent;
}

async function handleRequest(req: Request): Promise<Response> {
  const url = new URL(req.url);

  if (url.pathname === "/v1/models") {
    return Response.json({
      object: "list",
      data: Object.keys(MODEL_MAP).map((id) => ({
        id,
        object: "model",
        created: 1700000000,
        owned_by: "easemate",
      })),
    });
  }

  if (url.pathname === "/v1/chat/completions" && req.method === "POST") {
    const body = await req.json();
    const model = body.model || "gpt-4o-mini";
    const messages = body.messages || [];
    const stream = body.stream !== false;
    const userInput = processMessages(messages);
    const modelId = MODEL_MAP[model] || 3;
    const sessionId = parseInt(`100${Math.floor(Math.random() * 8000000 + 2000000)}`);
    const visitorId = generateVisitorId();
    
    const payload = {
      model_id: modelId,
      session_id: sessionId,
      operation_info: {
        operation: userInput,
        id: 10000,
      },
      parameters: JSON.stringify({ webSearch: false, isThinking: false }),
    };

    const signs = generateSign(payload, visitorId);
    
    const response = await fetch("https://api.easemate.ai/api2/stream/exec_operation", {
      method: "POST",
      headers: {
        "content-type": "application/json;charset=UTF-8",
        "device-uuid": visitorId,
        "sign": signs.sign,
        "timestamp": signs.timestamp,
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
      },
      body: JSON.stringify(payload),
    });

    if (!response.ok) {
      return Response.json({ error: "Upstream API error" }, { status: 502 });
    }

    const chatId = `chatcmpl-${crypto.randomUUID().replace(/-/g, "").slice(0, 24)}`;

    if (stream) {
      const transformedStream = streamToOpenAI(response.body!, chatId, model);
      return new Response(transformedStream, {
        headers: { "Content-Type": "text/event-stream" },
      });
    } else {
      const transformedStream = streamToOpenAI(response.body!, chatId, model);
      const content = await collectStream(transformedStream);
      
      return Response.json({
        id: chatId,
        object: "chat.completion",
        created: Math.floor(Date.now() / 1000),
        model,
        choices: [{
          index: 0,
          message: { role: "assistant", content },
          finish_reason: "stop",
        }],
        usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
      });
    }
  }

  return Response.json({ error: "Not found" }, { status: 404 });
}

serve(handleRequest, { port: 80 });
console.log("Server running on http://localhost:80");
8 个赞

前排膜拜

1 个赞

感谢大佬

大佬你好,我直接复制到deno deploy中,发现{“error”:“Not found”},无法使用,curl了也没有结果,是代码已经过时了吗?还是我操作问题呢?(我是小白,操作步骤是问ai的)