Widget na stronie
Widget to czat Twojego bota bezpośrednio na stronie: okrągły przycisk w rogu, z którego otwiera się rozmowa. Dla odwiedzającego to kolejny sposób, żeby się z Tobą skontaktować; dla bota to zwykły kanał — te same reakcje i te same dialogi operatora co w komunikatorach. Przegląd wielokanałowości: Kanały.
Instalacja to jeden fragment kodu wklejony w szablon strony. Cała reszta konfiguruje się w panelu i działa bez ponownego dotykania strony.
Gdzie znaleźć kod
W panelu otwórz Widget → blok Instalacja na stronie → pole Kod do wklejenia. Przycisk Kopiuj przenosi cały fragment do schowka.
Kod wygląda tak, z Twoim własnym kluczem zamiast wielokropka:
<script>
window.mybot = { key: "eu-1a2b3c4d-..." };
</script>
<script async src="https://getmybot.dev/loader.js"></script>
Jeśli zamiast kodu widzisz informację, że dla tego bota nie wydano jeszcze klucza instalacyjnego, kanał widgetu nie jest podłączony. Napisz do wsparcia: klucz wydawany jest przy podłączeniu kanału.
Pod kodem jest podpowiedź, do jakiego hosta widget będzie się łączył. Jeśli Twoja strona ma CSP (Content Security Policy), zezwól temu hostowi na ładowanie skryptów i połączenia — inaczej przeglądarka po cichu zablokuje widget.
Gdzie wkleić kod
Fragment trafia do HTML każdej strony, na której widget ma się pojawiać. W praktyce: raz do wspólnego szablonu — stopka, pole „kod przed </body>" w CMS-ie albo kontener w menedżerze tagów.
Reguły są krótkie:
- Najlepsze miejsce to tuż przed zamykającym
</body>.<head>też zadziała, ale wtedy przeglądarka poświęca czas widgetowi zanim pokaże Twoją treść. - Kolejność obu znaczników ma znaczenie: pierwszy ustawia klucz, drugi ładuje widget. Nie zamieniaj ich i nie rozdzielaj po stronie.
- Drugi znacznik ma
asynci nie blokuje renderowania. Nie usuwaj tego atrybutu. - Jeden fragment na stronę. Dwie kopie to dwie próby uruchomienia.
Dalej loader sam ustala, z którego centrum danych obsługiwany jest Twój bot, i stamtąd pobiera kod widgetu. Z Twojej strony nic więcej nie trzeba.
Klucz instalacyjny jest publiczny
Klucz w pierwszym znaczniku to publiczny identyfikator bota, a nie hasło. Znajduje się w źródle strony: każdy odwiedzający może otworzyć „Pokaż źródło strony" i go odczytać. Każdy widget czatu działa tak samo i to normalne.
Co jest istotne:
- Kluczem nie da się zalogować do panelu, odczytać cudzych dialogów, wyeksportować bazy subskrybentów ani zmienić czegokolwiek w bocie. Otwiera dokładnie jedno: rozpoczęcie nowej rozmowy z tym botem.
- Nie traktuj klucza jak sekretu — ukrywanie go czy zaciemnianie nic nie daje.
- Jeśli klucz trzeba wymienić (np. po rozstaniu z wykonawcą), robi to wsparcie. Po wymianie stary kod na stronie przestaje działać i trzeba go zaktualizować.
Lista dozwolonych domen
Widget ma listę dozwolonych domen (origin allowlist) — adresy stron, z których wolno mu działać. Ustala się ją przy podłączaniu kanału i zmienia później bez ponownego wydawania klucza, więc kod na stronie pozostaje ten sam.
Wpisy to originy: schemat, host oraz — jeśli niestandardowy — port.
https://example.com
https://www.example.com
https://shop.example.com
http://localhost:3000
Porównanie jest dokładne, znak po znaku:
- Nie ma symboli wieloznacznych.
https://*.example.comnie zadziała — wypisz subdomeny pojedynczo. example.comiwww.example.comto różne wpisy. Jeśli strona odpowiada pod oboma, dodaj oba.http://ihttps://to również różne wpisy. Zwykle wystarczyhttps://, ale środowisko testowe nahttp://trzeba dodać wprost.- Bez ścieżek:
https://example.com/shopzostanie odrzucone — origin to sam adres strony.
Jeśli Twojej rzeczywistej domeny nie ma na liście, widget się nie uruchomi. Przeglądarka odwiedzającego dostanie odmowę „origin not allowed", a czat po prostu się nie otworzy. Typowo zdarza się to po przeprowadzce na nową domenę, dodaniu subdomeny albo uruchomieniu drugiej wersji językowej pod własnym adresem — każda potrzebuje osobnego wpisu.
Jeśli lista jest pusta, nie ma żadnego ograniczenia i widget działa zewsząd. Traktuj to jako „jeszcze nieskonfigurowane", a nie jako świadome otwarcie: gdy tylko znasz swoje domeny, wpisz je.
Co to ograniczenie chroni, a czego nie
Chroni: obca strona nie osadzi Twojego widgetu u siebie. Inaczej ktoś mógłby skopiować fragment na swoją stronę, a przeglądarki prawdziwych odwiedzających oddawałyby tej stronie treść rozmów z Twoim botem. Dokładnie to lista zamyka.
Nie chroni: nie powstrzymuje kogoś, kto skopiował klucz i odpytuje platformę bezpośrednio — nie z przeglądarki, lecz np. skryptem. Nagłówek z adresem strony ustawia przeglądarka; program poza przeglądarką może go pominąć albo wysłać dowolny. Traktuj listę jako ograniczenie osadzania, a nie granicę bezpieczeństwa. Przed nadużyciami chronią limity częstotliwości i moderacja dialogów po stronie platformy, nie ta lista.
Wygląd
W Widget → Wygląd ustawia się:
- Położenie — przycisk w lewym lub prawym dolnym rogu.
- Kolor akcentu — kolor przycisku i elementów czatu. Użyj koloru swojej marki, żeby widget nie wyglądał obco.
- Nazwa czatu — podpis przycisku i tytuł panelu. Zwykle nazwa firmy albo imię, którym przedstawia się bot.
Obok jest podgląd — żywy widget pokazujący efekt jeszcze przed zapisaniem.
Widget nie ma jeszcze wiadomości powitalnej: to trzy ustawienia i tyle. Pierwszą wiadomość bot wysyła w odpowiedzi na wypowiedź odwiedzającego, według Twoich zwykłych reakcji.
Zmiany docierają na stronę same: widget odczytuje ustawienia przy ładowaniu strony, a wklejonego kodu nigdy nie trzeba poprawiać.
Odwiedzający są anonimowi
Odwiedzający nie wypełnia niczego, żeby napisać. Przy pierwszym uruchomieniu widget dostaje z platformy anonimowy identyfikator odwiedzającego i zapisuje go w przeglądarce, żeby po powrocie człowiek zobaczył swoją rozmowę, a nie pusty czat.
Wynika z tego, że:
- W dialogach taki odwiedzający jest anonimowy: bez imienia, telefonu i adresu e-mail, dopóki sam ich nie napisze.
- Identyfikator żyje w konkretnej przeglądarce. Inna przeglądarka, inne urządzenie albo wyczyszczone dane strony to już nowy odwiedzający z czystą historią.
- Anonimowego odwiedzającego można powiązać ze znanym Ci klientem, np. z zalogowanym użytkownikiem Twojego panelu. Jak to zrobić, opisuje następny rozdział.
Powiązanie odwiedzającego z Twoim użytkownikiem
Jeśli odwiedzający jest już zalogowany na Twojej stronie, możesz powiedzieć platformie, kto to jest. Po powiązaniu rozmowa przestaje być anonimowa: zostaje scalona z profilem tej osoby, a jej wcześniejsze anonimowe wiadomości nie giną.
Wystarczy jedno wywołanie z funkcją, która produkuje dowód:
mybot.identify(async (visitorId) => {
const res = await fetch("/mybot-sign", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: currentUser.id, visitorId }),
});
return res.json(); // { userId, signature, expiresAt }
});
Widget wywołuje Twoją funkcję z visitorId — bieżącym anonimowym identyfikatorem odwiedzającego dla tej przeglądarki, jedyną wartością, którą zna tylko widget — i czeka, aż zwróci ona, lub rozwiąże się do, { userId, signature, expiresAt }. Jedynym zadaniem Twojej funkcji jest przekazanie visitorId do Twojego własnego serwera, razem z identyfikatorem zalogowanego użytkownika (currentUser.id powyżej to to, jak wygląda to u Ciebie), i zwrócenie dokładnie tego, co odpowie Twój serwer. Samo podpisywanie odbywa się na Twoim serwerze, nie w tej funkcji — patrz niżej.
Callback, a nie gotowa wartość ani sposób na bezpośrednie odczytanie visitorId, z dwóch powodów:
- Moment powstania. Widget tworzy
visitorIdasynchronicznie podczas uruchamiania — nie istnieje on w chwili, gdy działa skrypt ładujący, a sammybot.identifyjest instalowany dopiero po zakończeniu tego procesu (więcej niżej). Dzięki temu w momencie, gdy Twoja strona w ogóle może wywołaćmybot.identify, identyfikator otrzymywany przez Twoją funkcję jest gwarantowanie prawdziwy. Zwykły getter nie dawałby takiej gwarancji: nic nie powstrzymałoby strony przed odczytaniem go o linijkę za wcześnie i otrzymaniem pustki, po cichu produkując podpis, który nigdy się nie zgadza, bez żadnej wskazówki dlaczego. - Zasięg. Kod Twojej własnej strony nigdy nie musi przechowywać, zapamiętywać ani ręcznie przekazywać
visitorId— istnieje on tylko wewnątrz tej jednej funkcji, na potrzeby jedynego wywołania, którego potrzebuje.
Od chwili, gdy opuszcza widget, visitorId jest uprawnieniem okaziciela (bearer capability): każdy, kto zdobędzie dla niego podpis, może zostać połączony z sesją tego odwiedzającego. Wysyłaj go tylko do swojego własnego serwera, w swoim własnym uwierzytelnionym żądaniu, i nigdzie indziej — nie loguj go, nie przekazuj stronom trzecim i nie umieszczaj w wywołaniu analityki po stronie klienta.
Podpis liczy się wyłącznie na Twoim własnym serwerze
Wewnątrz Twojej funkcji powyżej Twój własny serwer — nigdy przeglądarka — oblicza podpis: HMAC-SHA256, kluczowany sekretem widgetu, w zapisie szesnastkowym, pierwsze 32 znaki, z trzech wartości połączonych w jedną wiadomość. Ta sama formuła widnieje w panelu obok sekretu, pod nagłówkiem „Signing algorithm":
signature = hex(HMAC-SHA256(key: secret, message: userId + "\n" + visitorId + "\n" + expiresAt)).slice(0, 32)
Każde + "\n" + powyżej oznacza prawdziwy znak nowej linii między częściami, a nie dwa znaki: ukośnik wsteczny i n. Jeśli Twoja funkcja HMAC przyjmuje wiadomość jako jeden ciąg znaków, sklej trzy części prawdziwym znakiem nowej linii — trzy osobne wywołania .update() bez znaku nowej linii między nimi liczą hash z innej, błędnej wiadomości.
Trzy części:
userId— identyfikator zalogowanego użytkownika, ta sama wartość, którą Twój serwer otrzymuje z funkcji powyżej i zwraca w swojej odpowiedzi.visitorId— anonimowy identyfikator odwiedzającego, który Twoja funkcja otrzymała jako argument i przekazała bez zmian do Twojego serwera.expiresAt— znacznik czasu unix w sekundach (nie w milisekundach), do którego ten konkretny podpis pozostaje ważny, wystawiony przez Twój serwer w momencie podpisywania. Platforma odrzuca wywołanie, któregoexpiresAtjest już w przeszłości, oraz takie, które jest więcej niż 24 godziny w przyszłości — podpisuj tuż przed zwróceniem wartości z Twojej funkcji, a nie raz, do buforowania na potrzeby kolejnych żądań.
Ten termin ważności to sedno tej zmiany, a nie szczegół poboczny. Poprzednia formuła obejmowała tylko identyfikator użytkownika, więc raz przechwycony podpis — gdzieś zalogowany, podsłuchany w ruchu sieciowym, obojętnie jak — pozostawał ważny na zawsze i pasował do każdego odwiedzającego, nie tylko tego, dla którego został wystawiony. Ktokolwiek go zdobył, mógł odtworzyć go w zupełnie innej przeglądarce, a platforma połączyłaby anonimowe przeglądanie obcej osoby z profilem prawdziwego klienta. Związanie podpisu z konkretnym visitorId i krótki czas życia zamykają obie strony tej luki: podpis weryfikuje się tylko dla sesji, dla której został wystawiony, i dla żadnej innej, a przestaje się weryfikować w ogóle, gdy minie expiresAt — więc nawet wyciekły podpis pozostaje krótkotrwałym zagrożeniem jednej sesji, a nie trwałym.
Licz wszystkie trzy wartości na swoim serwerze i zwracaj je z funkcji gotowe. To nie formalność: żeby policzyć podpis w przeglądarce, trzeba by wysłać tam sam sekret, czyli wręczyć go każdemu odwiedzającemu stronę. Od tego momentu każdy mógłby podać się za dowolnego Twojego klienta i przeczytać jego rozmowę. Podpis (i visitorId/expiresAt, dla których został policzony) można oddać z powrotem do przeglądarki, sekretu nie.
Samo wywołanie niczego nie pokazuje na ekranie: powiązanie dzieje się po stronie serwera i po cichu. Jeśli Twoja funkcja rzuci błąd albo jej obietnica zostanie odrzucona, albo platforma odrzuci wynikowy podpis — nie zgadza się, albo expiresAt brakuje, jest już w przeszłości lub jest więcej niż 24 godziny w przyszłości — nic więcej się nie dzieje: odwiedzający niczego nie zauważy i będzie pisał dalej jako anonim.
mybot.identify nie pojawia się natychmiast — istnieje dopiero po załadowaniu głównego pakietu widgetu, razem z prawdziwym visitorId (patrz „Moment powstania" powyżej). Wywołuj go z obsługi zdarzenia ładowania strony, a nie w pierwszej linii <head>.
Gdzie znaleźć sekret i jak go zmienić
Sekret widgetu to klucz, którym podpisujesz. Leży w ustawieniach widgetu, w sekcji „identify() secret". Jeśli sekcja mówi, że widget nie jest jeszcze podłączony, najpierw podłącz kanał web — sekret pojawi się razem z nim.
Pokazanie. Przycisk „Reveal secret" pobiera wartość. Nie pojawia się ona otwartym tekstem: najpierw zobaczysz kropki, a znaki odsłania osobny przełącznik „Show" („Mask" chowa je z powrotem). Obok jest „Clear from screen", który usuwa wartość z widoku.
Pokazywać można dowolnie wiele razy i pokazanie niczego nie unieważnia: sekret pozostaje ważny także po zamknięciu ekranu. Tak jest dlatego, że przechowywany jest zaszyfrowany, a nie zahaszowany — platforma potrzebuje jego prawdziwej wartości do sprawdzenia każdego podpisu. Jeśli za pół roku wdrażasz nowy backend, po prostu wejdź i odczytaj go ponownie.
Po skopiowaniu posprzątaj po sobie. Schowka platforma nie wyczyści: z poziomu przeglądarki nie da się tego zrobić niezawodnie, więc tego nie obiecujemy. Usuń wartość z ekranu i wyczyść schowek ręcznie, gdy skończysz, zwłaszcza na wspólnym komputerze.
Zmiana. Przycisk „Rotate secret", z krokiem potwierdzenia. To działanie psujące natychmiast: w chwili, gdy nowy sekret zaczyna istnieć, stary przestaje weryfikować. Nie ma okna zakładki — nie istnieje moment, w którym działają oba klucze.
Psuje się dokładnie powiązywanie odwiedzających z Twoimi użytkownikami. Rozmowy nie zostają przerwane: odwiedzający dalej piszą i dostają odpowiedzi, tyle że jako anonimowi, dopóki backend nie zacznie podpisywać nowym sekretem. Kod instalacyjny i lista dozwolonych domen pozostają nietknięte, na stronie nic nie trzeba zmieniać.
Dlatego zmieniaj sekret świadomie: najpierw przygotuj wdrożenie backendu z nową wartością, a dopiero potem kliknij „Rotate secret". Kliknięcie „na próbę" to zły pomysł. Nowa wartość pokazuje się na ekranie zaraz po zmianie, więc możesz ją od razu skopiować.
Jak widget zachowuje się na stronie
Widget jest celowo zbudowany tak, żeby nie wchodzić stronie w drogę:
- Żyje w izolowanym kontenerze (zamknięty Shadow DOM). Twoje style nie wchodzą do środka, a style widgetu nie wychodzą na stronę. Efekt uboczny: nie przemalujesz widgetu własnym CSS — służą do tego ustawienia wyglądu.
- Kontener rozciąga się na cały ekran, ale nie przechwytuje kliknięć: przechodzą na stronę, reagują tylko przycisk i panel czatu.
- Widget zawsze jest nad Twoją treścią; własne warstwy go nie przykryją.
- Każdy błąd wewnątrz widgetu zostaje wewnątrz: w najgorszym razie nie działa przycisk czatu, a strona chodzi dalej. Jeśli widget się nie pojawił, zajrzyj do konsoli przeglądarki — prawie zawsze to CSP albo brak domeny na liście.
Jeśli widget się nie pojawił
Sprawdź po kolei:
- Otwórz źródło strony i upewnij się, że oba znaczniki są na miejscu, a klucz nie jest pusty.
- Sprawdź, czy adres strony to dokładnie ten z listy, razem z
https://iwww. - Zajrzyj do konsoli przeglądarki: komunikat o Content Security Policy oznacza, że host platformy trzeba dopuścić w CSP strony.
- Upewnij się, że widgetu nie wycina blokada reklam — sprawdź w oknie prywatnym bez rozszerzeń.
Co dalej
- Kanały — wielokanałowość i możliwości kanałów.
- Reakcje: podstawy — co bot odpowie odwiedzającemu.
- Czaty i operatorzy — jak operator odpowiada w rozmowie.