תפריט המדריכים

ווידג'ט לאתר

הווידג'ט הוא הצ'אט שהמבקרים רואים באתר שלכם. הוא טוען את הבוט שפרסמתם ומנהל את כל השיחה בדפדפן. בלי מודל שפה, בלי בקשות לצד שלישי, ובגודל של כ-22KB אחרי דחיסה. הצ'אט עם נציג נטען מקובץ נפרד, רק כשמפעילים אותו.

התקנה

מדביקים את השורה הזו לפני </body> בכל עמוד שבו הבוט צריך להופיע. לוח הבקרה מציג אותה עם המפתח שלכם בעמוד התקנה.

HTML
<script src="https://cdn.kavchat.com/v1/kavchat.js" data-key="pk_live_..." async></script>

מותר לשים בעמוד את המפתח הציבורי (pk_live_...). את המפתח הסודי (sk_live_...) אסור לשים בעמוד אף פעם.

מאפיינים

מאפייןברירת מחדלתיאור
data-keyחובההמפתח הציבורי שלכם.
data-apihttps://api.kavchat.comכתובת ה-API.
data-localeשפת הדפדפןשפת הממשק של הווידג'ט (כפתורים, טופס, הודעות): he, en, ar, ru, es, fr, de, pt, it, tr. אם אין תרגום לשפה, נעשה שימוש בשפת הבוט. תוכן התסריט מוצג תמיד כפי שכתבתם אותו. he ו-ar מוצגות מימין לשמאל.
data-user-tokenאיןטוקן זהות של משתמש מחובר. ראו זיהוי משתמשים.
data-user-nameאיןמוצג בברכה ({name}) וממולא מראש בטופס הפנייה.
data-openfalseעם "true" הצ'אט נפתח מיד כשהוא נטען.
HTML
<script
  src="https://cdn.kavchat.com/v1/kavchat.js"
  data-key="pk_live_..."
  data-locale="he"
  data-user-token="<identity JWT>"
  data-user-name="Dana"
  data-open="false"
  async></script>

ממשק JavaScript

הסקריפט מתקין את window.Kavchat:

JavaScript
Kavchat.init({
  publicKey: 'pk_live_...',
  apiBase: 'https://api.kavchat.com', // optional
  locale: 'he',                       // optional
  userToken: '<identity JWT>',        // optional
  userName: 'Dana',                   // optional
  open: false,                        // optional
  onScreen(screen, targetId) {},      // optional: "screen" buttons in your scripts
  onEvent(event) {},                  // optional: ready, unavailable, open, close, effect, ticket
});
Kavchat.open();
Kavchat.close();
Kavchat.toggle();
Kavchat.setUser({ token: '<identity JWT>', name: 'Dana' }); // on login; setUser(null) on logout
Kavchat.offset({ bottom: 80 });       // lift the launcher, e.g. above a cookie banner; offset(null) resets
Kavchat.destroy();

אין צורך ב-init כשבתגית הסקריפט יש data-key. קריאה נוספת ל-init מחליפה את הווידג'ט שרץ.

קריאה לממשק לפני שהסקריפט נטען

תגית הסקריפט היא async, ולכן ייתכן שהקוד שלכם ירוץ קודם. יוצרים תור ומכניסים אליו קריאות, והווידג'ט מריץ אותן לפי הסדר כשהוא נטען. init רץ תמיד ראשון, וההגדרות שלו גוברות על המאפיינים שבתגית.

HTML
<script>
  window.Kavchat = window.Kavchat || { q: [] };
  Kavchat.q.push(['init', { publicKey: 'pk_live_...', onScreen: function (s, id) { location.href = '/' + s + '/' + id; } }]);
  Kavchat.q.push(['setUser', { token: '<identity JWT>', name: 'Dana' }]);
</script>
<script src="https://cdn.kavchat.com/v1/kavchat.js" async></script>

כל פריט בתור הוא [methodName, ...args]. גם אחרי שהסקריפט נטען, Kavchat.q.push(...) ממשיך לעבוד ומריץ את הקריאה מיד, כך שאותו קוד מתאים לשני המקרים.

מקום לבאנר עוגיות

אם יש באתר רכיב קבוע בתחתית המסך, כמו באנר עוגיות, אפשר להזיז מעליו את הכפתור, החלון והבועות. יש שתי דרכים: משתנה CSS בשם --kavchat-bottom או הפקודה Kavchat.offset, שמקבלת מספר בפיקסלים או ערך אורך כמו 5rem. ההזזה מתווספת לגובה שנקבע במסך הניהול. כשהבאנר נסגר, מסירים את המשתנה או קוראים ל-offset(null). שתי הדרכים עובדות גם באתר עם CSP מחמיר. בטלפון החלון נפתח על כל המסך ולא זז.

CSS
/* while your cookie banner is shown */
body.cookie-banner-open { --kavchat-bottom: 80px; }
JavaScript
// works before or after the widget script loads
window.Kavchat = window.Kavchat || { q: [] };
Kavchat.q.push(['offset', { bottom: banner.offsetHeight + 12 }]);

// after the visitor makes a choice
Kavchat.q.push(['offset', null]);

אירועים

הפונקציה onEvent מקבלת אחד מהאירועים הבאים:

typeשדותמתי
readyversion, offlineהבוט נטען, מהרשת או מהעותק השמור כשאין רשת.
unavailablereason? ('quota')הבוט כבוי, פתוח רק לבודקים או שנגמרה המכסה. שום דבר לא מוצג.
open, closeהחלון נפתח או נסגר.
effecteffectכפתור ביקש לפתוח את טופס הפנייה, קישור או מסך.
ticketid, numberנפתחה פנייה.
livestate, idמצב השיחה עם הנציג השתנה: queued, active, closed או ticket.
JavaScript
Kavchat.init({
  publicKey: 'pk_live_...',
  onEvent(event) {
    if (event.type === 'ready') console.log('Bot version', event.version, event.offline ? '(offline copy)' : '');
    if (event.type === 'ticket') console.log('Request opened', event.number);
  },
});

פעולות בתסריט

  • פנייה: פותחת את טופס הפנייה בתוך הצ'אט. תמליל השיחה מצורף לפנייה.
  • קישור: פותח קישורי https: ו-tel: בלשונית חדשה (rel="noopener noreferrer"). סוגי קישורים אחרים לא נפתחים.
  • מסך: קורא לפונקציה onScreen(screen, targetId) שלכם. בלעדיה הכפתור לא עושה כלום באתר.

רשימת המסכים בעורך

מצהירים פעם אחת על המסכים שהאפליקציה יודעת לפתוח באפשרות screens של Kavchat.init. ערכת הפיתוח שולחת את הרשימה ל-Kavchat בעצמה, ועורך התסריטים מציע את המסכים האלה ברשימה נפתחת עם השמות שלהם, במקום שדה טקסט חופשי. העורך גם מבקש מזהה פריט כשמסך צריך אותו, ומזהיר על כפתור שמוביל למסך שלא הוצהר. אף אחד לא מקליד את רשימת המסכים ידנית בלוח הבקרה.

JavaScript
Kavchat.init({
  publicKey: 'pk_live_...',
  screens: [
    { id: 'orders', label: 'My orders' },
    { id: 'product', label: 'Product page', needsTarget: true }, // the button also carries a product id
  ],
  onScreen(screen, targetId) {
    location.href = targetId ? `/${screen}/${targetId}` : `/${screen}`;
  },
});
  • id הוא הערך שמגיע ל-onScreen: אותיות לטיניות, ספרות, _, . ו--, עד 40 תווים. label הוא השם שהמנהל רואה, עד 60 תווים. עד 100 מסכים. רשומות לא תקינות ומזהים כפולים מושמטים.
  • needsTarget: הכפתור נושא גם מזהה פריט (למשל מזהה מוצר), והוא מגיע ל-onScreen בתור targetId.
  • הרשימה נשלחת ברקע אחרי שהגרסה נטענת, רק כשהיא השתנתה או פעם בשבוע. כישלון לא מורגש, והשליחה תנוסה שוב בהפעלה הבאה. המשתמש לא רואה כלום.
  • המסכים שנרשמו מופיעים בלוח הבקרה תחת בוט, הגדרות, "מסכי האפליקציה", עם הפלטפורמות שדיווחו על כל מסך. מסך שהוסר מהאפליקציה אפשר למחוק שם.

זיהוי המשתמשים שלכם

כדי להצמיד לשיחות ולפניות את מזהה המשתמש שלכם, או להציג את הבוט רק לבודקים, חותמים JWT בשרת ומעבירים אותו ב-data-user-token או ב-userToken. כשהמשתמש מתחבר או מתנתק, קוראים ל-Kavchat.setUser(...). הווידג'ט שולח את הטוקן בכותרת Authorization: Bearer <token> בכל קריאה, ומשתמש מחובר יכול לפתוח פנייה בלי להשאיר מייל או טלפון. איך יוצרים את הטוקן: זיהוי משתמשים.

מדיניות CSP

הווידג'ט עובד באתר עם CSP קפדני, בלי 'unsafe-inline' ובלי 'unsafe-eval'. מספיק שתאשרו את שני המקורות שלנו:

CSP
script-src https://cdn.kavchat.com;
connect-src https://api.kavchat.com;

העיצוב נטען כ-constructable stylesheets בתוך ה-Shadow DOM, ולכן אין צורך לשנות את style-src. אם לבוט יש תמונה משלו או שיש תמונות בתסריטים, הוסיפו ל-img-src את השרת שממנו הן נטענות. הווידג'ט לא טוען דבר ממקור אחר ולא פותח iframe.

צ'אט עם נציג

אין צורך להגדיר דבר באתר. כשמפעילים צ'אט עם נציגים במסך הניהול, הווידג'ט טוען קובץ נוסף, kavchat-live.js, מאותה תיקייה ובאותה גרסה של kavchat.js. אתרים בלי צ'אט עם נציגים לא מורידים את הקובץ הזה כלל.

  • האפשרות: ליד "פנייה לצוות" מופיעה האפשרות "שיחה עם נציג", רק אחרי שהשרת אישר שיש נציג פנוי. מחוץ לשעות הפעילות, או כשכל הנציגים עסוקים, מוצג רק טופס פנייה. לצידו מופיע הסבר קצר, וגם מועד החזרה של הצוות, אם הוא ידוע. כשהמכסה החודשית נגמרת, הטופס מוצג בלי הסבר.
  • הבקשה: אורחים משאירים אימייל או טלפון, למקרה שאף נציג לא יתפנה בזמן. אפשר להוסיף גם שם והודעה ראשונה. כשיש יותר ממחלקה פתוחה אחת, בוחרים מחלקה. משתמשים מזוהים (userToken) לא צריכים למלא פרטי קשר. אם אף נציג לא יכול לקבל את השיחה, נפתח טופס פנייה עם הפרטים שכבר הוזנו.
  • ההמתנה: מוצג "ממתינים לנציג" עם ספירה לאחור עד סוף זמן ההמתנה שהגדרתם, והכפתורים "להשאיר הודעה במקום" ו"סיום השיחה". אפשר לכתוב כבר בזמן ההמתנה. כשהזמן נגמר, השיחה הופכת אוטומטית לפנייה. למבקר מוצגים מספר הפנייה והסבר שאף נציג לא התפנה בזמן.
  • השיחה: שם הנציג, ההודעות, חיווי הקלדה, העברה לנציג אחר וסיום השיחה. כל מה שמגיע מהשרת מוצג כטקסט רגיל, בלי HTML ובלי קישורים. קוראי מסך מקריאים הודעות חדשות, וכשהחלון סגור מופיעה נקודה על הכפתור הצף.
  • זמן אמת: החיבור מתבצע באמצעות Server-Sent Events. אם החיבור מתנתק, הווידג'ט מנסה להתחבר מחדש במרווחי זמן הולכים וגדלים ומשלים את כל מה שהוחמץ. כך שום הודעה לא הולכת לאיבוד ולא מופיעה פעמיים. ברשתות שחוסמות את החיבור הזה, הווידג'ט עובר לבדיקות חוזרות (long-poll). לכל הודעה יש מזהה, ולכן ניסיון חוזר לא יוצר כפילות. הודעה שלא נשלחה מסומנת, ואפשר לשלוח אותה שוב בהקשה.
  • טעינה מחדש: אישור הגישה לשיחה נשמר ב-localStorage לפי המפתח הציבורי והשיחה, ולעולם לא בכתובת. הוא תקף עד 12 שעות או עד שהשיחה מסתיימת. כך, אחרי טעינה מחדש או מעבר לעמוד אחר, כל השיחה משוחזרת: החלק עם הבוט, ואחריו החלק עם הנציג.
  • בלי preflight: הקריאות נשלחות בלי כותרות מיוחדות, וגוף הבקשה הוא JSON שנשלח כ-text/plain.

תשובות AI (פרימיום)

אין מה להגדיר באתר. כשמפעילים תשובות AI במסך הניהול (עם מפתח של ספק AI משלכם), שאלה שאף תסריט לא עונה עליה נשלחת ל-Kavchat, ובזמן ההמתנה, עד 20 שניות, הצ'אט מראה שהבוט מקליד. התשובה מוצגת כטקסט רגיל עם תווית קטנה "תשובת AI" בשפת הממשק, ומתחתיה כפתור לנושא קשור, אם יש כזה, וכפתור "פנייה לצוות".

אם אין תשובה, או שהבקשה נכשלה, לקחה יותר מדי זמן או עברה את המגבלה (10 תשובות לשיחה בכל 15 דקות), הצ'אט מציג את ההודעה הרגילה של "לא מצאתי תשובה". השאלה עדיין מופיעה בדוח השאלות שלא נמצאה להן תשובה, ותשובות AI נשמרות בשיחה השמורה כמו כל הודעה אחרת.

איך הווידג'ט מתנהג

  • בידוד: הכל מוצג בתוך Shadow DOM. ה-CSS שלכם לא משפיע על הווידג'ט, וה-CSS של הווידג'ט לא משפיע על העמוד.
  • מטמון: הבוט שפורסם נשמר ב-localStorage לפי המפתח הציבורי, יחד עם ה-ETag שלו. בכל טעינה נשלחת הכותרת If-None-Match, וכשלא השתנה כלום השרת עונה 304. כשאין רשת, הווידג'ט משתמש בעותק השמור ומציג הודעה על כך.
  • שיחות: מזהה שיחה אקראי נשמר ב-sessionStorage, כך שמעבר בין עמודים ממשיך את אותה שיחה.
  • צ'אט עם נציג: שיחה פתוחה נשמרת ב-localStorage ומשוחזרת בעמוד הבא.
  • נתונים: מהלך השיחה מדווח כ-2 שניות אחרי כל פעולה, וגם כשהעמוד מוסתר.
  • התג של Kavchat: מוצג בכל החבילות. בפרימיום אפשר להסתיר אותו בהגדרות הבוט.
  • נגישות: החלון הוא דיאלוג עם תווית, המיקוד עובר אליו וחוזר ממנו, Esc סוגר אותו, הכפתורים הם כפתורים אמיתיים, הודעות חדשות של הבוט מוקראות, והאנימציות מצטמצמות כשהמבקר ביקש זאת.

התקנה מ-npm

באפליקציית עמוד יחיד אפשר לארוז את הווידג'ט עם הקוד שלכם במקום לטעון את תגית הסקריפט:

Terminal
yarn add @kavchat/widget
# or: npm install @kavchat/widget
JavaScript
import { createWidget } from '@kavchat/widget';

const widget = createWidget({ publicKey: 'pk_live_...' });
await widget.ready;
widget.open();