# Reference Tvůrce widgetů

> Reference kódu pro widgety z Tvůrce widgetů KickBotu: všechny události z Kicku a jejich data, globalValues, userVariables, pomocníci pro chat a jak běží náhled.

Source: https://kickbot.com/cs/docs/widgets/widget-builder-reference

Tato stránka popisuje, co může tvůj kód používat ve widgetu z [Tvůrce widgetů](https://kickbot.com/cs/docs/widgets/widget-builder) KickBotu: události `widgetEvent`, objekt `globalValues`, objekt `userVariables` a pomocníky `window.kickbotWidget`. Každý widget je obyčejné HTML, CSS a JavaScript, které v OBS běží jako zdroj prohlížeče. KickBot se za tebe připojí ke Kicku a posílá události do tvého kódu.

## Jak widget běží

Když OBS otevře URL widgetu, KickBot:

1. Načte stránku widgetu s průhledným pozadím.
2. Vloží tvoje CSS do prvku `<style>` a tvoje HTML do stránky.
3. Načte z Kicku data tvého kanálu (počet sledujících, počet diváků, stav streamu a nedávný chat) do `globalValues`.
4. Spustí tvůj JavaScript. Ten běží jednou, po kroku 3, takže `globalValues` je připravený, když tvůj kód startuje.
5. Posílá do tvého kódu živé události jako události `widgetEvent` na `document`.

Tvůj JavaScript může bez jakéhokoli importu číst dva objekty: `globalValues` a `userVariables`. Pomocníci jsou na `window.kickbotWidget`.

Limity:

| Položka | Limit |
| --- | --- |
| Šířka a výška | Nastavuje se v **Základním nastavení**. Nový prázdný widget má 800 × 600. Okno Nový widget přijme až 7680 × 4320. |
| HTML, CSS a JavaScript | Až 2 000 000 znaků na soubor |
| Název widgetu | Až 500 znaků |
| Soubory pro uživatelské proměnné | Až 25 MB na soubor |
| Widgety z Tvůrce v tarifu zdarma | 10 (s Premium bez limitu) |

## Poslouchej události

Přidej na `document` jeden posluchač pro `widgetEvent`. Každá událost má `event.detail` se třemi poli:

| Pole | Typ | Hodnota |
| --- | --- | --- |
| `event_name` | string | Událost, třeba `followEvent` |
| `source` | string | Vždy `kick` |
| `data` | object | Data události (viz níže) |

```javascript
document.addEventListener('widgetEvent', (event) => {
    const { event_name, data } = event.detail;

    if (event_name === 'followEvent') {
        showAlert(data.user.username + ' followed!');
    }
    if (event_name === 'subEvent') {
        showAlert(data.username + ' subscribed for ' + data.months + ' months');
    }
});
```

### Seznam událostí

| Název události | Kdy nastane | Testovací tlačítko |
| --- | --- | --- |
| `subEvent` | Divák si předplatí kanál | Sub |
| `subGiftEvent` | Divák daruje suby | Darovaný sub |
| `kicksEvent` | Divák pošle KICKs | KICKs |
| `followEvent` | Divák začne sledovat | Sledování |
| `unfollowEvent` | Divák přestane sledovat | Zrušení sledování |
| `chatMessageEvent` | Někdo pošle zprávu do chatu | Zpráva |
| `chatMessageDeletedEvent` | Moderátor smaže zprávu | Zpráva smazána |
| `chatMessagePinnedEvent` | Zpráva se připne | Připnutí zprávy |
| `chatMessageUnpinnedEvent` | Připnutá zpráva se odepne | Odepnutí zprávy |
| `chatClearedEvent` | Moderátor vymaže chat | Chat vymazán |
| `followerCountUpdatedEvent` | Kick pošle nový počet sledujících | Změna počtu sledujících |
| `subCountUpdatedEvent` | Počet subů na Kicku se změnil | Změna počtu subů |
| `viewerCountUpdatedEvent` | Kick pošle nový počet diváků | Změna počtu diváků |
| `raidEvent` | Jiný kanál tě raiduje (hostuje) | Raid |
| `streamOnline` | Tvůj stream začne | Stream začal |
| `streamOffline` | Tvůj stream skončí | Stream skončil |

Událost pro donate neexistuje. Na alerty na donaty použij [widget s alertem na donate](https://kickbot.com/cs/docs/tipping/tip-alerts).

### Data událostí

Toto jsou data v `event.detail.data`. Odpovídají testovacím událostem v editoru. Skutečné události můžou mít víc polí. Jména, čísla a ID jsou tu jen příklady.

`subEvent`

```javascript
{
    months: 1,
    username: 'viewer_one',
    chatroom_id: 3847194
}
```

`subGiftEvent`

```javascript
{
    chatroom_id: 3847194,
    gifted_usernames: ['viewer_one', 'viewer_two', 'viewer_three'],
    gifter_username: 'big_gifter'
}
```

`kicksEvent`

```javascript
{
    message: 'Great stream!',
    sender: { id: 12345678, username: 'viewer_one', username_color: '#93EBE0' },
    gift: {
        gift_id: 'full_send',
        name: 'Full Send',
        amount: 100,
        type: 'BASIC',
        tier: 'BASIC',
        character_limit: 0,
        pinned_time: 0
    },
    channel: 'channel_3984507'
}
```

`followEvent` a `unfollowEvent` (stejný tvar; `followers_count` je nový počet)

```javascript
{
    id: 'd4e5f6a7-89b0-12c3-d456-78901e2f3a4b',
    channel: { id: 3984507, slug: 'kickbotdev' },
    user: { id: 14873539, slug: 'viewer_one', username: 'viewer_one' },
    followers_count: 36,
    created_at: '2024-05-13T22:19:07+00:00'
}
```

`chatMessageEvent`

```javascript
{
    id: '85c884b3-7377-478c-b6aa-22b7db752b93',
    chatroom_id: 3974112,
    content: 'hello [emote:37226:KEKW]',
    type: 'message',
    created_at: '2024-05-13T22:29:52+00:00',
    sender: {
        id: 4063326,
        username: 'viewer_one',
        slug: 'viewer_one',
        identity: {
            color: '#72ACED',
            badges: [{ type: 'subscriber', text: 'Subscriber', count: 3 }]
        }
    }
}
```

Emoty z Kicku jsou v `content` jako `[emote:ID:NAME]`. Na obrázky je převedeš pomocí `createChatMessageFragment` (viz [Bezpečné zobrazení textu z chatu](#show-chat-text-safely)).

`chatMessageDeletedEvent`

```javascript
{
    id: '9cd3788a-ecd2-4051-a162-a7ea9be8ce7a',
    message: { id: '89d6be86-63db-4581-922e-14a6c4a9c2e6' }
}
```

Podle `data.message.id` najdeš a odstraníš zprávu, kterou widget ukazuje.

`chatMessagePinnedEvent` (`data.message` má stejný tvar jako data `chatMessageEvent`)

```javascript
{
    message: {
        id: '5d798a38-ad17-45e4-8169-bda2faa85b79',
        chatroom_id: 3974112,
        content: 'This is a pinned message!',
        type: 'message',
        created_at: '2024-05-13T22:30:41+00:00',
        sender: { id: 4063326, username: 'kickbotdev', slug: 'kickbotdev', identity: { color: '#72ACED', badges: [] } }
    }
}
```

`chatMessageUnpinnedEvent`: `data` je prázdné pole (`[]`).

`chatClearedEvent`

```javascript
{ id: '47e3596f-0274-46e0-85b1-029fa3bfc5ea' }
```

`followerCountUpdatedEvent` (pozor na camelCase `followersCount`)

```javascript
{ followersCount: 1532, channel_id: 4599, created_at: 1715639984 }
```

`subCountUpdatedEvent`

```javascript
{ subscriber_count: 184, channel_id: 4599 }
```

`viewerCountUpdatedEvent`

```javascript
{ viewer_count: 320, channel_id: 5937485 }
```

`raidEvent`

```javascript
{
    chatroom_id: 839485,
    optional_message: 'gg stream',
    number_viewers: 150,
    host_username: 'raiding_channel'
}
```

`streamOnline`

```javascript
{
    livestream: {
        id: 3984507,
        channel_id: 3984507,
        session_title: 'Test stream!',
        created_at: '2024-05-13T22:30:41+00:00'
    }
}
```

`streamOffline`

```javascript
{
    livestream: {
        id: 3984507,
        channel: { id: 3984507, is_banned: false }
    }
}
```

## Globální hodnoty

`globalValues` obsahuje aktuální stav tvého kanálu. KickBot ho naplní, než se tvůj kód spustí, a s každou příchozí událostí ho aktualizuje. Použij ho k vykreslení widgetu hned na začátku, ještě před první událostí.

```javascript
globalValues = {
    subCount: 184,
    followerCount: 1532,
    viewerCount: 320,
    lastFollower: { /* the last follow */ },
    lastSub: { /* the last sub, for example { username, months } */ },
    lastGiftedSub: { /* the last gift, for example { gifter_username, gifted_usernames } */ },
    chatMessages: [ /* chatMessageEvent payloads */ ],
    streamerInfo: {
        kick: {
            username: 'KickBotDev',
            slug: 'kickbotdev',
            profile_picture: 'https://files.kick.com/images/user/.../fullsize.webp',
            user_id: '4063326',
            channel_id: '3984507',
            chatroom_id: '3974112',
            bio: 'Channel bio',
            subscriber_badges: [ /* your channel's sub badges from Kick */ ]
        }
    },
    currentStream: {
        is_live: true,
        title: 'Stream title',
        started_at: '2026-10-24 18:00:00'
    }
}
```

| Hodnota | Jak se aktualizuje |
| --- | --- |
| `subCount` | Načte se z Kicku. S každým `subEvent` a s každým darovaným subem se zvýší o 1 a `subCountUpdatedEvent` ho nastaví znovu. |
| `followerCount` | Načte se z Kicku. Nastavují ho `followEvent`, `unfollowEvent` a `followerCountUpdatedEvent`. |
| `viewerCount` | Načte se z Kicku (0, když nestreamuješ). Nastavuje ho `viewerCountUpdatedEvent`. |
| `lastFollower`, `lastSub`, `lastGiftedSub` | Poslední, kterého KickBot na tvém kanálu zaznamenal, nebo `null`. Aktualizuje ho příslušná událost. |
| `chatMessages` | Nedávný chat z Kicku při načtení widgetu, pak každá nová zpráva. Drží 100 nejnovějších. `chatClearedEvent` ho vyprázdní. Pokud chceš skrýt smazané zprávy, zpracuj `chatMessageDeletedEvent` ve vlastním kódu. |
| `streamerInfo.kick` | Tvůj profil na Kicku. Během běhu widgetu se nemění. |
| `currentStream` | Načte se z Kicku při načtení widgetu. `streamOnline` ani `streamOffline` ho neaktualizují. Pokud potřebuješ aktuální stav, poslouchej tyto události. |

## Uživatelské proměnné

Uživatelské proměnné jsou nastavení, která streameři vidí, když tvůj widget otevřou v Uložených widgetech. Přidáváš je v editoru v části **Uživatelské proměnné**. V kódu hodnotu přečteš přes `userVariables.<variable name>.value`.

```javascript
const goal = Number(userVariables.follower_goal.value);
document.body.style.color = userVariables.text_color.value;
```

Pole proměnné:

| Pole | Co to je |
| --- | --- |
| Titulek | Popisek, který streameři vidí. |
| Název proměnné | Název v tvém kódu. Jen písmena, čísla a `_`. Po vytvoření proměnné ho už nezměníš. |
| Nápověda | Doplňující text pod titulkem. |
| Typ proměnné | Viz tabulku níže. |
| Výchozí hodnota | Hodnota, než ji streamer změní. Může být prázdná. |
| Povinné | Streameři musí zadat hodnotu, než můžou uložit. |

| Typ | Co streamer vidí | `value` v tvém kódu |
| --- | --- | --- |
| Text | Textové pole | String |
| Číslo | Pole na číslo | Číslo nebo číselný string. Pro jistotu použij `Number()`. |
| Barva | Výběr barvy | Barva jako hex string, třeba `#ffffff` |
| Rozbalovací nabídka | Nabídka s tvými možnostmi | Vybraná možnost jako string. Výchozí je první možnost. |
| Přepínač | Vypínač zapnuto/vypnuto | `true` nebo `false` |
| Obrázek/GIF | Pole na URL a tlačítko pro nahrání | URL souboru |
| Video | Pole na URL a tlačítko pro nahrání | URL souboru |
| Zvuk | Pole na URL a tlačítko pro nahrání | URL souboru |

Nahrané soubory ukládá KickBot a `value` je URL souboru. Nabídkou vedle proměnné ji posuneš nahoru nebo dolů, upravíš nebo smažeš. Pořadí v editoru je stejné pořadí, jaké vidí streameři.

## Bezpečné zobrazení textu z chatu

Každé pole události a každou uživatelskou proměnnou ber jako text od cizího člověka. KickBot text z chatu a z událostí vyčistí dřív, než ho tvůj kód dostane, takže je bezpečný jako text v HTML, ale ne jako URL, CSS nebo skript. Nevkládej surová pole událostí do `innerHTML`. Použij tyto pomocníky:

| Pomocník | Co dělá |
| --- | --- |
| `window.kickbotWidget.createChatMessageFragment(content, font_size)` | Vrátí `DocumentFragment` s textovými uzly a obrázky emotů z Kicku. Emoty mají `font_size + 3` pixelů. Výchozí `font_size` je 20. |
| `window.kickbotWidget.createTextNode(value)` | Vrátí textový uzel pro jakoukoli hodnotu. |
| `window.kickbotWidget.escapeHtmlText(value)` | Escapuje text pro použití mezi HTML tagy, když musíš sestavit HTML string. Nepoužívej ho pro URL, CSS ani skripty. |

```javascript
document.addEventListener('widgetEvent', (event) => {
    if (event.detail.event_name !== 'chatMessageEvent') return;
    const data = event.detail.data;

    const line = document.createElement('div');
    line.id = 'msg-' + data.id;
    const name = document.createElement('strong');
    name.style.color = data.sender.identity.color;
    name.append(window.kickbotWidget.createTextNode(data.sender.username + ': '));
    line.append(name);
    line.append(window.kickbotWidget.createChatMessageFragment(data.content, 20));
    document.getElementById('chat').append(line);
});
```

Pro jiný text použij `textContent` nebo `createTextNode`.

## Náhled

Náhled v editoru funguje jako OBS, jen s několika rozdíly kvůli testování:

- Když změníš kód nebo uživatelskou proměnnou, náhled spustí tvůj JavaScript znovu. KickBot nejdřív odebere posluchače `widgetEvent` z předchozího běhu. Časovače z `setInterval` a `setTimeout` se neodebírají, takže když se nahromadí, klikni na tlačítko pro znovunačtení.
- Když změníš šířku nebo výšku, náhled se načte znovu.
- Testovací události jdou jen do náhledu. Testovací **Sledování** přičte 1 k `globalValues.followerCount` a **Zrušení sledování** 1 odečte. **Změna počtu diváků** pošle ukázkové číslo. **Zpráva smazána** odstraní náhodnou zprávu z chatu v náhledu.
- Pokud tvůj kanál ještě nemá posledního sledujícího, sub ani darovaný sub, náhled použije pro `lastFollower`, `lastSub` a `lastGiftedSub` ukázková data.
- Výstup `console.log` a chyby uvidíš, když klikneš na tlačítko pro vyskakovací okno a v tom okně otevřeš nástroje pro vývojáře.

## Příklad: cíl sledujících

Tohle je krátký cíl sledujících. Používá posluchač událostí, `globalValues` a dvě uživatelské proměnné: `follower_goal` (Číslo) a `bar_color` (Barva).

HTML:

```html
<div class="goal">
    <span id="count"></span> / <span id="target"></span>
    <div class="bar"><div class="fill"></div></div>
</div>
```

CSS:

```css
body { margin: 0; font-family: Arial, sans-serif; color: #fff; }
.goal { padding: 12px; background: #100c28; border-radius: 16px; font-size: 32px; font-weight: bold; }
.bar { height: 24px; margin-top: 8px; background: #3d3957; border-radius: 12px; overflow: hidden; }
.fill { height: 100%; width: 0; transition: width 0.3s ease; }
```

JavaScript:

```javascript
const target = Number(userVariables.follower_goal.value) || 100;
document.getElementById('target').textContent = target;
document.querySelector('.fill').style.background = userVariables.bar_color.value;

function update(count) {
    document.getElementById('count').textContent = count;
    document.querySelector('.fill').style.width = Math.min((count / target) * 100, 100) + '%';
}

document.addEventListener('widgetEvent', (event) => {
    const { event_name, data } = event.detail;
    if (event_name === 'followEvent' || event_name === 'unfollowEvent') update(data.followers_count);
    if (event_name === 'followerCountUpdatedEvent') update(data.followersCount);
});

update(globalValues.followerCount);
```

Plnou verzi s dalšími nastaveními dostaneš, když založíš nový widget ze šablony **Simple Follower Goal**.

## Tipy

- Výchozí stav vykresli z `globalValues` a při událostech ho měň. Widget, který čeká na událost, je prázdný, dokud se něco nestane.
- Uložené widgety ukazují testovací tlačítka jen pro názvy událostí, které se objeví v tvém JavaScriptu. Do kódu piš celý název události (třeba `'followEvent'`), aby streameři mohli tvůj widget otestovat.
- Nech pozadí průhledné, pokud pozadí není součástí designu. Widget pak leží nad scénou.
- Widgety z Tvůrce můžeš spravovat i přes API KickBotu. Viz [Reference API pro widgety](https://kickbot.com/docs/api/reference/widgets).
