# شات بوت بالذكاء الاصطناعي لموقعك مع Claude أو GPT أو Gemini

> أضف شات بوت إلى أي موقع بنافذة محادثة صغيرة وخادم Node.js يحفظ مفتاح الـ API سرًا، مع كود مُختبَر وحدود أمان وحساب التكلفة.

آخر تحديث: 2026-11-01

لإضافة شات بوت إلى موقعك، ضع في صفحتك نافذة محادثة صغيرة، واجعل خادمك هو الذي يستدعي النموذج. ترسل النافذة كل سؤال إلى خادمك، فيضيف إليه المفتاح والتعليمات، ولا يصل المفتاح إلى المتصفح أبدًا. وبمفتاح من 5abeer تصل مكتبة OpenAI إلى نماذج Claude وGPT وGemini بأقل من أسعارها الرسمية.

## ما الذي تحتاجه لبناء شات بوت لموقعك؟

- مفتاح API من 5abeer لمزوّد النموذج الذي سيستخدمه روبوت المحادثة. يستخدم الكود النموذج الافتراضي [`claude-sonnet-5.5`](https://5abeer.ai/ar/models/claude-sonnet-5.5)، فيحتاج إلى مفتاح Claude. الخطوات في [أنشئ مفتاحًا](https://5abeer.ai/ar/docs/start/keys.md#create) وفي [ما هو مفتاح API وكيف تحصل على واحد لـ Claude وGPT وGemini](https://5abeer.ai/ar/guides/ai-api-key-guide).
- Node.js بالإصدار 22 أو أحدث، وهو ما تتطلبه مكتبة `openai` الحالية، مع الحزمتين `express` و`openai`.
- خادم يشغّل Node.js ويبقى يعمل.

## لماذا لا يستدعي المتصفح الـ API مباشرة؟

كل ما يحمّله المتصفح يستطيع الزائر قراءته. فالمفتاح المكتوب في جافاسكريبت صفحتك يظهر في مصدرها، ومن ينسخه يستطيع إنفاق رصيدك. ويوصي مرجع OpenAI بالشيء نفسه لمفاتيحه: لا تشارك المفتاح مع غيرك، ولا تكشفه في أي كود يعمل عند المستخدم، مثل المتصفحات أو التطبيقات.

لذلك يتكوّن روبوت المحادثة من ثلاثة أجزاء:

1. نافذة المحادثة: قسم HTML وسكربت قصير في صفحتك، ترسل كل سؤال إلى `/api/chat` على خادمك.
2. خادمك: تطبيق Express صغير يفحص كل طلب، ويضيف المفتاح والتعليمات، ويستدعي النموذج، ثم يعيد الإجابة وحدها.
3. واجهة 5abeer API: تشغّل النموذج وتخصم ثمن التوكنات من رصيدك.

وتبقى التعليمات، أي موجّه النظام، على الخادم أيضًا، حيث لا يستطيع الزائر قراءتها.

## كيف تربط خادمك بنماذج Claude أو GPT أو Gemini؟

تعمل 5abeer بصيغة Chat Completions من OpenAI، لذلك يستخدم الخادم مكتبة OpenAI الرسمية مع عنوان أساس ومفتاح مختلفين. وبين Claude وGPT وGemini لا يتغير إلا معرّف النموذج ومزوّد المفتاح. والإعداد نفسه يشغّل [بوت تليجرام بالذكاء الاصطناعي](https://5abeer.ai/ar/guides/telegram-bot-ai-api).

| الصيغة | العنوان الأساسي | ترويسة المفتاح |
| --- | --- | --- |
| OpenAI | `https://api.5abeer.ai/v1` | `Authorization: Bearer <KEY>` |
| Anthropic | `https://api.5abeer.ai` | `x-api-key: <KEY>` |
| Gemini | `https://api.5abeer.ai/v1beta` | `x-goog-api-key: <KEY>` |

أرسل أولًا طلبًا تجريبيًا واحدًا بمفتاحك. إذا جاءت الإجابة، فالمفتاح والنموذج جاهزان. والصيغة مشروحة في [Chat Completions](https://5abeer.ai/ar/docs/api/chat-completions.md#quick).

```bash
curl --request POST "https://api.5abeer.ai/v1/chat/completions" \
  --header "Authorization: Bearer $KHABEER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"model":"claude-sonnet-5.5","messages":[{"role":"user","content":"اكتب فكرة منتج في سطر واحد"}]}'
```

```python
import os
import requests

response = requests.post(
    "https://api.5abeer.ai/v1/chat/completions",
    headers={
        "Authorization": f"Bearer {os.environ['KHABEER_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
    "model": "claude-sonnet-5.5",
    "messages": [
        {
            "role": "user",
            "content": "اكتب فكرة منتج في سطر واحد"
        }
    ]
},
    stream=False,
    timeout=60,
)

response.raise_for_status()
print(response.json())
```

## كيف يبدو كود الخادم؟

الخادم ملف واحد، يقدّم نافذة المحادثة من مجلد `public` ويجيب عن `POST /api/chat`.

```javascript
import express from "express";
import OpenAI from "openai";

const client = new OpenAI({ apiKey: process.env.KHABEER_API_KEY, baseURL: "https://api.5abeer.ai/v1" });
const MODEL = process.env.CHAT_MODEL || "claude-sonnet-5.5";

// The assistant's instructions stay on the server: visitors can't read or change them.
const SYSTEM = `You are the website assistant of Example Store.
Answer only questions about the store, in the visitor's language, briefly and in plain text.
Opening hours: 9:00 to 21:00, Saturday to Thursday. Shipping takes 2 to 4 days.
If you don't know the answer, say so and suggest the contact page.`;

const MAX_MESSAGES = 11; // the last 5 questions and answers, plus the new question
const MAX_CHARS = 4000; // per message
const LIMIT = 20; // requests per visitor IP every 10 minutes
const hits = new Map();
setInterval(() => hits.clear(), 10 * 60 * 1000).unref();

function clean(messages) {
  if (!Array.isArray(messages) || messages.length === 0 || messages.length > MAX_MESSAGES) return null;
  const out = [];
  for (const m of messages) {
    if (!m || (m.role !== "user" && m.role !== "assistant") || typeof m.content !== "string") return null;
    out.push({ role: m.role, content: m.content.slice(0, MAX_CHARS) });
  }
  return out[0].role === "user" && out.at(-1).role === "user" ? out : null;
}

const app = express();
// app.set("trust proxy", 1); // behind Nginx, Caddy or a load balancer: see the notes below
app.use(express.json());
app.use(express.static("public"));

app.post("/api/chat", async (req, res) => {
  const count = (hits.get(req.ip) ?? 0) + 1;
  hits.set(req.ip, count);
  if (count > LIMIT) return res.status(429).json({ error: "Too many messages. Try again in a few minutes." });

  const messages = clean(req.body?.messages);
  if (!messages) return res.status(400).json({ error: "Invalid conversation." });

  try {
    const reply = await client.chat.completions.create({
      model: MODEL,
      messages: [{ role: "system", content: SYSTEM }, ...messages],
      max_tokens: 800,
    });
    const answer = (reply.choices[0]?.message?.content ?? "").trim();
    if (!answer) throw new Error("empty answer");
    res.json({ answer });
  } catch (error) {
    console.error("model error:", error.message);
    res.status(502).json({ error: "The assistant can't answer right now. Try again in a minute." });
  }
});

app.listen(process.env.PORT || 3000, () => console.log("Chat server running on port", process.env.PORT || 3000));
```

## كيف تبدو نافذة المحادثة؟

النافذة ملفان في مجلد `public`: صفحة فيها قسم المحادثة، وسكربت يرسل الأسئلة.

```text
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Chat with us</title>
  <style>
    .chat { max-width: 28rem; font: 16px/1.5 system-ui, sans-serif; border: 1px solid #767676; border-radius: 0.75rem; padding: 0.75rem; }
    .chat-log { height: 20rem; overflow-y: auto; display: flex; flex-direction: column; gap: 0.5rem; padding: 0.25rem; }
    .chat-log p { margin: 0; padding: 0.5rem 0.75rem; border-radius: 0.75rem; max-width: 85%; white-space: pre-wrap; }
    .chat-log .user { align-self: flex-end; background: #1d4ed8; color: #fff; }
    .chat-log .assistant { align-self: flex-start; background: #f1f5f9; color: #111; }
    .chat form { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.75rem; }
    .chat label { flex-basis: 100%; }
    .chat input { flex: 1; min-width: 0; font: inherit; padding: 0.5rem; }
    .chat button { font: inherit; padding: 0.5rem 1rem; }
    .chat :focus-visible { outline: 3px solid #1d4ed8; outline-offset: 2px; }
  </style>
</head>
<body>
  <section class="chat" aria-labelledby="chat-title">
    <h2 id="chat-title">Ask our assistant</h2>
    <div id="chat-log" class="chat-log" role="log" aria-label="Conversation" tabindex="0"></div>
    <form id="chat-form">
      <label for="chat-input">Your question</label>
      <input id="chat-input" name="question" autocomplete="off" maxlength="2000" required>
      <button type="submit">Send</button>
    </form>
  </section>
  <script type="module" src="chat.js"></script>
</body>
</html>
```

```javascript
const log = document.getElementById("chat-log");
const form = document.getElementById("chat-form");
const input = document.getElementById("chat-input");
const button = form.querySelector("button");
const turns = [];

function add(role, text) {
  const p = document.createElement("p");
  p.className = role;
  p.textContent = text; // text only: a reply can never inject HTML into the page
  log.append(p);
  log.scrollTop = log.scrollHeight;
  return p;
}

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const question = input.value.trim();
  if (!question) return;
  input.value = "";
  button.disabled = true;
  add("user", question);
  const reply = add("assistant", "Thinking…");
  let text = "Sorry, something went wrong. Please try again.";
  try {
    const res = await fetch("/api/chat", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ messages: [...turns, { role: "user", content: question }] }),
    });
    const data = await res.json().catch(() => ({}));
    if (res.ok && data.answer) {
      text = data.answer;
      turns.push({ role: "user", content: question }, { role: "assistant", content: data.answer });
      turns.splice(0, turns.length - 10); // keep the last 5 questions and answers
    } else if (data.error) {
      text = data.error;
    }
  } catch {
    // network error: keep the default message
  }
  reply.textContent = text;
  button.disabled = false;
  input.focus();
});
```

اختبرنا هذه الملفات الثلاثة في 2026-11-01 على Node.js 22 وExpress 5.3 ومكتبة `openai` بالإصدار 7.47، أمام بديل محلي للـ API وفي متصفح حقيقي، بمفتاح وهمي.

## كيف تشغّله على جهازك؟

ضع `server.mjs` في مجلد، وضع `index.html` و`chat.js` في مجلد `public` داخله. ثم شغّل هذه الأوامر في ذلك المجلد:

```bash
npm install express openai
export KHABEER_API_KEY="YOUR_5ABEER_API_KEY"
node server.mjs
```

```powershell
npm install express openai
$env:KHABEER_API_KEY="YOUR_5ABEER_API_KEY"
node server.mjs
```

افتح `http://localhost:3000` واكتب سؤالًا. لا تفتح `index.html` بنقرة مزدوجة، فالنافذة تحتاج إلى الخادم. ولتغيير النموذج أو المنفذ، اضبط `CHAT_MODEL` أو `PORT` بالطريقة التي ضبطت بها المفتاح.

## كيف يعمل الكود؟

- التعليمات: يحدد `SYSTEM` للنموذج وظيفة المساعد. ضع بيانات نشاطك مكان بيانات المتجر في المثال.
- الفحص: يقبل الخادم 11 رسالة على الأكثر من المستخدم والمساعد، تبدأ بسؤال وتنتهي بسؤال، ويقتطع كل رسالة عند 4000 حرف. وأي شيء آخر، ومنه رسالة نظام يرسلها الزائر، يعيد الخطأ 400 دون استدعاء النموذج.
- الذاكرة: ترسل النافذة آخر 5 أسئلة وإجاباتها مع كل سؤال جديد، فتنجح أسئلة المتابعة.
- حد المعدل: لكل عنوان IP عشرون طلبًا كل 10 دقائق. والطلب الحادي والعشرون يعيد الخطأ 429 ولا يكلّف شيئًا. ويبقى العدّاد في الذاكرة، فيعود إلى الصفر عند إعادة تشغيل الخادم.
- الأخطاء: تعيد المكتبة المحاولة مرتين من تلقاء نفسها عند تجاوز حد المعدل أو انتهاء المهلة أو أخطاء الخادم. وإذا فشل الطلب بعدها، يرى الزائر اعتذارًا قصيرًا ويسجّل الخادم الخطأ.
- يحدّ `max_tokens: 800` من طول الإجابة في نماذج Claude وGemini، وتتجاهله نماذج GPT، كما في [المعاملات](https://5abeer.ai/ar/docs/api/chat-completions.md#params).
- الإخراج الآمن: تكتب النافذة الإجابات عبر `textContent`، فلا يعمل أي HTML تحمله الإجابة داخل صفحتك.
- سهولة الوصول: المحادثة منطقة `role="log"` لها اسم، تعلن قارئات الشاشة رسائلها الجديدة، ولحقل الإدخال تسمية ظاهرة.

## كيف تضيفه إلى موقع قائم؟

1. انسخ العنصر `<section class="chat">` إلى صفحتك، وقواعد `<style>` إلى ملف التنسيق.
2. أضف وسم السكربت الخاص بملف `chat.js` قبل `</body>`، مع مسار الملف في موقعك. ولأنه يعمل وحدةً مستقلة، فلا يتعارض مع سكربتات موقعك الأخرى.
3. اجعل المسار `/api/chat` في موقعك يصل إلى خادم Node.js.

أبسط طريقة للخطوة الثالثة نطاق واحد: في خادم الويب، مثل Nginx أو Caddy، أو في إعدادات الوكيل لدى الاستضافة، حوّل `/api/chat` إلى خادم Node.js. وعندها تعمل النافذة كما هي، دون CORS. وإذا كان الخادم على نطاق آخر، فضع عنوانه الكامل في `chat.js`، واسمح بطلبات CORS من أصل موقعك وحده، حتى لا تستخدم مواقع أخرى روبوت المحادثة من صفحاتها.

وللصفحة العربية، اضبط `lang="ar" dir="rtl"` على العنصر `html`، وترجم النصوص الظاهرة في `index.html` و`chat.js`، ورسائل الخطأ في `server.mjs`. وتنتقل الفقاعات إلى الجهة الصحيحة من تلقاء نفسها، ويطلب `SYSTEM` أصلًا أن تأتي الإجابة بلغة الزائر.

## ما الذي يتغير خلف وكيل عكسي أو موزّع أحمال؟

يعدّ حد المعدل الطلبات حسب `req.ip`، ويأخذه Express افتراضيًا من الاتصال نفسه. وخلف Nginx أو Caddy أو موزّع أحمال يكون هذا عنوان الوكيل، فيشترك كل الزوار في عدّاد واحد.

والحل في السطر المعلّق داخل `server.mjs`: اضبط `trust proxy` على عدد الوكلاء الذين يسبقون التطبيق، وهو في الغالب `1`. وهذه القيم كما في دليل Express، راجعناه في 2026-11-01:

| قيمة `trust proxy` | من أين يأتي عنوان IP للزائر |
| --- | --- |
| غير مضبوطة (الافتراضي) | الاتصال المباشر |
| رقم، مثل `1` | العنوان الذي يبعد هذا العدد من الخطوات على الأكثر، بقراءة `X-Forwarded-For` من آخره إلى أوله |
| `true` | أول عنوان في `X-Forwarded-For` |

لا تستخدم `true` إلا إذا كان آخر وكيل لديك يستبدل قيمة `X-Forwarded-For` بقيمته. وإلا، كما يحذّر الدليل، قد يتمكن العميل من وضع أي قيمة يريدها، فيتجاوز الزائر الحد. وإذا شغّلت عدة نسخ من الخادم، فاحفظ العدّادات في مخزن مشترك، مثل Redis.

## كم يكلّف شات بوت الموقع؟

لا تضيف النافذة ولا Express أي رسوم، فأنت تدفع ثمن الخادم وتوكنات النموذج. ويرسل كل سؤال التعليمات والمحادثة الأخيرة والرسالة الجديدة. وفي هذا المثال يجيب روبوت المحادثة عن 3000 سؤال في الشهر، بنحو 1500 توكن إدخال و300 توكن إخراج لكل سؤال:

مثال: 4,500,000 توكن إدخال و900,000 توكن إخراج على Claude Sonnet 5.5 (`claude-sonnet-5.5`) تكلّف 5.40 US$ بسعرنا مقابل 18.00 US$ بالسعر الرسمي (أقل بنسبة 70%). آخر مراجعة للسعر الرسمي: 19 أكتوبر 2026.

وهذا سعر التوكن في النموذج الافتراضي:

Claude Sonnet 5.5 (`claude-sonnet-5.5`) لكل مليون توكن: الإدخال 0.60 US$ (الرسمي 2 US$)، الإخراج 3 US$ (الرسمي 10 US$). آخر مراجعة للسعر الرسمي: 19 أكتوبر 2026.

ولتنفق أقل، اختر نموذجًا أصغر من [صفحة النماذج](https://5abeer.ai/ar/models)، أو اختصر `SYSTEM`، أو اخفض `max_tokens`. وقارن بين المزوّدين في [صفحة الـ API](https://5abeer.ai/ar/api) وصفحات [Claude API](https://5abeer.ai/ar/api/claude) و[OpenAI API](https://5abeer.ai/ar/api/openai) و[Gemini API](https://5abeer.ai/ar/api/gemini)، والباقات في [صفحة الأسعار](https://5abeer.ai/ar/pricing).

وخصّص لروبوت المحادثة مفتاحًا له حد إنفاق، كما في [تحكّم في كل مفتاح](https://5abeer.ai/ar/docs/start/keys.md#controls). وعند بلوغ الحد تفشل الطلبات بالخطأ [`API_KEY_QUOTA_EXHAUSTED`](https://5abeer.ai/ar/docs/help/errors.md#API_KEY_QUOTA_EXHAUSTED) ويرى الزوار الاعتذار، فلا يتجاوز يوم مزدحم أو استخدام مسيء المبلغ الذي حددته.

## كيف تحمي روبوت المحادثة؟

- احفظ المفتاح في متغير بيئة على الخادم، ولا تضعه أبدًا في الصفحة أو في `chat.js` أو في مستودع كود. وإذا تسرّب، فعطّله وأنشئ مفتاحًا جديدًا.
- إذا كان للخادم عنوان ثابت، فأضف إلى مفتاحه قائمة عناوين IP مسموح بها، كما في [تحكّم في كل مفتاح](https://5abeer.ai/ar/docs/start/keys.md#controls).
- يستطيع أي شخص استدعاء `/api/chat` دون المرور بصفحتك. ويحدّ حد المعدل وحد الإنفاق مما قد يكلّفه ذلك.
- يستطيع الزائر تعديل السجل الذي ترسله النافذة، أو محاولة إقناع المساعد بترك تعليماته. فلا تضع في `SYSTEM` أسرارًا أو بيانات عملاء، وتعامل مع الإجابات على أنها معلومات لا وعود.
- قدّم الصفحة عبر HTTPS، وأخبر الزوار أن مساعدًا بالذكاء الاصطناعي هو الذي يجيبهم.

## أسئلة شائعة

### هل يستطيع روبوت المحادثة استخدام GPT أو Gemini بدل Claude؟

نعم. اضبط `CHAT_MODEL` على معرّف نموذج من [صفحة النماذج](https://5abeer.ai/ar/models)، واستخدم مفتاحًا لمزوّد ذلك النموذج. فكل مفتاح يغطي مزوّدًا واحدًا، واستدعاء نموذج GPT بمفتاح Claude يعيد الخطأ [`MODEL_PLATFORM_NOT_ALLOWED`](https://5abeer.ai/ar/docs/help/errors.md#MODEL_PLATFORM_NOT_ALLOWED).

### هل يجيب من محتوى موقعي؟

المحتوى القصير، مثل مواعيد العمل والشحن والأسئلة المتكررة، يتسع له `SYSTEM`. أما الكتالوج الكبير، فابحث فيه عند كل سؤال وأرسل معه الأجزاء المتعلقة به وحدها. فالنموذج لا يعرف إلا ما ترسله إليه.

### هل يمكن أن تظهر الإجابة كلمة بكلمة؟

نعم، بالبث المتدفق: اضبط `stream: true` على الخادم، ومرّر الأجزاء إلى النافذة فور وصولها، كما في [البث المتدفق](https://5abeer.ai/ar/docs/api/streaming.md#enable). وينتظر هذا الدليل الإجابة كاملة ليبقى الكود قصيرًا.

### هل يعمل على ووردبريس أو منصات بناء المواقع؟

نعم، إذا كانت منصتك تقبل HTML وسكربتات من عندك، في كتلة HTML مخصصة مثلًا. ويعمل خادم Node.js على أي استضافة تدعم Node.js.

### ماذا يحدث عندما ينفد رصيدي؟

تفشل الطلبات بالخطأ [`INSUFFICIENT_BALANCE`](https://5abeer.ai/ar/docs/help/errors.md#INSUFFICIENT_BALANCE)، ويرى الزوار الاعتذار حتى تشحن رصيدك.

### كيف أدفع ثمن الـ API؟

تشحن رصيدك في خبير بأي طريقة من [طرق الدفع](https://5abeer.ai/ar/docs/start/payment.md#methods)، وتُخصم طلبات روبوت المحادثة من هذا الرصيد.

### هل 5abeer جزء من OpenAI أو Anthropic أو Google؟

لا. 5abeer خدمة مستقلة لا تتبع أيًّا منها.

## المصادر

راجعناها في 2026-11-01:

- [OpenAI API reference: Overview](https://developers.openai.com/api/reference/overview): إبعاد مفاتيح الـ API عن المتصفحات والتطبيقات
- [Express: Behind proxies](https://expressjs.com/en/guide/behind-proxies.html): قيم `trust proxy`
- [MDN: ARIA log role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/log_role): سجلات المحادثة بصفتها مناطق تُعلن تحديثاتها
- [openai](https://www.npmjs.com/package/openai) و[express](https://www.npmjs.com/package/express) على npm: الإصدارات المختبرة وما تتطلبه من Node.js

> خبير خدمة مستقلة، ولا تتبع OpenAI أو Anthropic أو Google.

## ابدأ

أنشئ مفتاح Claude، وضعه في `KHABEER_API_KEY`، ثم شغّل الخادم.
