Aller au contenu

Ajout d'un overlay Killathon

Cet overlay a été conçu pour fonctionner avec le service StreamElements et son Chatbot.

Killathon

Une commande (personnalisable) vous permet de modifier le compteur (par ajouter, retrait ou définition de la valeur).

Des permissions sont aussi à définir : est-ce que les modos, les vips voire une liste de comptes Twitch particuliers peuvent utiliser la commande.

Le style du widget est personnalisables (titre, cadre et emote).

Le compteur se met également à jour en fonction des évenements de type bits, sub (prime, T1, T2, T3), subgift (T1, T2, T3) et don paypal par StreamElements avec un taux de conversion personnalisable par type et tier.

Mise en place

Partie Dashboard

Partie Overlay

  • Aller dans Outils de streaming > Overlays : https://streamelements.com/dashboard/overlays
  • Ajouter un overlay de 1080p (résolution courante pour du streaming) avec un nom parlant ("killathon" par exemple)

  • Ajouter un widget de type Custom widget (Static / custom > Custom widget)

  • Sélectionner le nouveau widget et cliquer sur le menu Layers
  • Sous l'intitulé "Custom widget 1", vous devriez avoir 3 sous-menu : "Settings", "Position, size and style" et "animation settings"
  • Cliquer sur "Position, size and style" pour développer le sous-menu
  • Mettre les settings suivants :
    • Width: 300
    • Height: 85
    • Top: 0
    • Left: 0
  • Cliquer sur le sous-menu "Settings" maintenant puis le bouton "Open Editor"
  • Vider les contenus des 5 onglets : HTML, CSS, JS, FIELDS et data
  • Mettre les nouveaux contenus suivants dans les onglets correspondants

    Contenu à copier-coller

    <div id="widget">
        <div id="cadre">
            <div id="title">{{title}}</div>
            <div id="content">
                <img src="{{emoteUrl}}" style="margin-right: 2rem; height: 3rem;" alt="Kill Icon" />
                <div>
                    <span id="killCountDisplay">0</span>
                    <span id="killSuffix">kills</span>
                </div>
            </div>
        </div>
    </div>
    
    #widget {
      font-family: Arial, sans-serif;
      font-size: 2rem;
      color: white;
      display: flex;
      align-items: center;
      justify-content: left;
      height: 100vh;  
    }
    
    #cadre {
      position: relative;
      border: 5px {{cadreBorder}} rgba(255, 255, 255, 0.5);
      padding: 10px;
      display: inline-block;
      text-align: center;
      background-color: {{cadreBackground}};
      border-radius: 15px;
    }
    
    #title {
      position: absolute;
      top: -0.5rem;
      left: 50%;
      transform: translateX(-50%);
      color: white;
      background-color: {{titleBackground}};
      padding: 0 10px;
      font-size: 1rem;
      border-radius: 10px; 
      white-space: nowrap;
      display: inline-block;
    }
    
    #content {
      display: flex;
      align-items: center;
      padding-right: 1rem; 
    }
    
    #killCountDisplay {
      color: {{killCountColor}};
      font-weight: bold;
      text-shadow: 0 0 2px white, 0 0 2px white;
    }
    
    #killSuffix {
      color: white;
      font-weight: normal;
    }
    
    let killCount = 0;
    let fieldData;
    
    function updateDisplay() {
      $('#killCountDisplay').text(killCount);
    }
    
    function saveCounter() {
      SE_API.store.set('killCount', killCount);
    }
    
    function loadCounter() {
      SE_API.store.get('killCount').then(obj => {
        if (obj && obj.value !== null) {
          killCount = parseInt(obj.value);
          updateDisplay();
        }
      }).catch(error => {
        console.error('Erreur lors du chargement du compteur:', error);
      });
    }
    
    function isUserAuthorized(username, badges, checkMods, checkVips) {
      console.log('Badges:', badges, 'Typeof:', typeof badges);
    
      is_mod = false;
      is_vip = false;
    
      is_broadcaster = badges.broadcaster || 
             badges.hasOwnProperty('broadcaster') ||
             badges.includes('broadcaster/1');
    
      if (checkMods) {
        is_mod = badges.moderator || badges.hasOwnProperty('moderator') || badges.includes('moderator/1');
      }
    
      if (checkVips) {
        is_vip = badges.vip || badges.hasOwnProperty('vip') || badges.includes('vip/1');
      }
    
      return is_broadcaster || is_mod || is_vip ||
             authorizedUsers.includes(username.toLowerCase());
    }
    
    function handleKillCommand(data) {
      let checkMods = fieldData.allowModerators;
      let checkVips = fieldData.allowVips;
    
      if (!isUserAuthorized(data.nick, data.tags.badges, checkMods, checkVips)) {
        console.log(`Utilisateur non autorisé: ${data.nick} ${data.tags.badges}`);
        return;
      }
    
      const parts = data.text.toLowerCase().split(' ');
      if (parts.length === 3) {
        const action = parts[1];
        const value = parseInt(parts[2]);
    
        if (!isNaN(value)) {
          switch (action) {
            case 'add':
              killCount += value;
              break;
            case 'del':
              killCount = Math.max(0, killCount - value);
              break;
            case 'set':
              killCount = Math.max(0, value);
              break;
          }
          updateDisplay();
          saveCounter();
        }
      }
    }
    
    function handleDonation(amount) {
      killCount += Math.floor(amount * fieldData.paypalRate);
      updateDisplay();
      saveCounter();
    }
    
    function handleCheer(bits) {
      killCount += Math.floor((bits / 100) * fieldData.cheerRate);
      updateDisplay();
      saveCounter();
    }
    
    function handleSubscription(tier) {
      switch (tier) {
        case 'prime':
          killCount += fieldData.subPrimeRate;
          break;
        case '1000':
          killCount += fieldData.subT1Rate;
          break;
        case '2000':
          killCount += fieldData.subT2Rate;
          break;
        case '3000':
          killCount += fieldData.subT3Rate;
          break;
      }
      updateDisplay();
      saveCounter();
    }
    
    function handleSubGift(tier, count) {
      let giftRate = fieldData.subT1Rate;
      if (tier === '2000') {
        giftRate = fieldData.subT2Rate;
      } else if (tier === '3000') {
        giftRate = fieldData.subT3Rate;
      }
      killCount += count * giftRate;
      updateDisplay();
      saveCounter();
    }
    
    window.addEventListener('onEventReceived', function (obj) {
      const { listener, event } = obj.detail;
      switch (listener) {
        case 'message':
          //if (event.data.text.startsWith('!kill')) {
            // {{command}}
          if (event.data.text.startsWith('{{command}}')) {
            handleKillCommand(event.data);
          }
          break;
        case 'tip-latest':
          handleDonation(event.amount);
          break;
        case 'cheer-latest':
          handleCheer(event.amount);
          break;
        case 'subscriber-latest':
          if (event.gifted) {
            handleSubGift(event.tier, event.amount);
          } else {
            handleSubscription(event.tier);
          }
          break;
      }
    });
    
    window.addEventListener('onWidgetLoad', function (obj) {
      fieldData = obj.detail.fieldData;
      authorizedUsers = fieldData.authorizedUsers.toLowerCase().split(',').map(user => user.trim());
      loadCounter();
    });
    
    {
      "command": {
        "type": "text",
        "label": "Commande de tchat (!xxx)",
        "value": "!kill",
        "group": "Commande"
      },
      "allowModerators": {
        "type": "checkbox",
        "label": "Autoriser les modos",
        "group": "Permissions"
      },
      "allowVips": {
        "type": "checkbox",
        "label": "Autoriser les VIPs",
        "group": "Permissions"
      },
      "authorizedUsers": {
        "type": "text",
        "label": "Utilisateurs autorisés (séparés par des virgules)",
        "value": "",
        "group": "Permissions"
      },
      "cadreBorder": {
        "type": "dropdown",
        "label": "Bordure du cadre :",
        "value": "double",
        "options": {
          "none": "Aucune",
          "double": "Double",
          "solid": "Solide"
        },
        "group": "Style"
      },
      "cadreBackground": {
        "type": "colorpicker",
        "value": "rgba(255, 255, 255, 0.5)",
        "label": "Couleur fond cadre",
        "group": "Style"
      },
      "titleBackground": {
        "type": "colorpicker",
        "value": "rgba(0, 0, 255, 0.3)",
        "label": "Couleur fond titre",
        "group": "Style"
      },
      "title": {
        "type": "text",
        "label": "Titre du cadre",
        "value": "Restant à faire",
        "group": "Style"
      },
      "killCountColor": {
        "type": "colorpicker",
        "value": "rgba(255,0,0,1)",
        "label": "Couleur compteur kill",
        "group": "Style"
      },
      "emoteUrl": {
        "type": "text",
        "label": "URL de l'emote",
        "value": "https://media2.giphy.com/media/GaqnjVbSLs2uA/giphy.gif",
        "group": "Style"
      },
      "paypalRate": {
        "type": "number",
        "label": "Taux PayPal (kills par €)",
        "value": 1,
        "step": 0.1,
        "group": "Conversion"
      },
      "cheerRate": {
        "type": "number",
        "label": "Taux Cheers (kills par 100 bits)",
        "value": 1,
        "step": 0.1,
        "group": "Conversion"
      },
      "subPrimeRate": {
        "type": "number",
        "label": "Taux Sub Prime (kills)",
        "value": 1,
        "step": 1,
        "group": "Conversion"
      },
      "subT1Rate": {
        "type": "number",
        "label": "Taux Sub T1 (kills)",
        "value": 1,
        "step": 1,
        "group": "Conversion"
      },
      "subT2Rate": {
        "type": "number",
        "label": "Taux Sub T2 (kills)",
        "value": 2,
        "step": 1,
        "group": "Conversion"
      },
      "subT3Rate": {
        "type": "number",
        "label": "Taux Sub T3 (kills)",
        "value": 5,
        "step": 1,
        "group": "Conversion"
      },
      "widgetName": {
        "type": "hidden",
        "value": "Killathon"
      },
      "widgetAuthor": {
        "type": "hidden",
        "value": "vignemail1@gmail.com"
      }
    }
    
    {}
    
    • Valider avec Done
    • Pensez à cliquer sur "Save" en haut à droite pour enregistrer les modifications de l'overlay
    • Rafraîchir la fenêtre du navigateur (StreamElements souffre d'un défaut de mise à jour en temps-réel) et recontrôler les configurations faites sur l'overlay
    • Copier l'URL de l'overlay en cliquant sur le bouton "maillon de chaine" en haut à droite, c'est l'URL pour OBS

Partie OBS

  • Aller sur la scène qui vous intéresse
  • Ajouter une source navigateur web et ajouter les paramètres suivants :
    • url: l'url que vous avez copié à la fin de la configuration de l'overlay
    • height: 100
    • width: 300
    • Disable when inactive : coché

Commandes type

Commandes streameur (et modos, vips et extra users)

Sur la base d'avoir conservé !kill comme commande (possible de changer dans le panel Settings de l'overlay)

  • !kill add <x> : ajoute x au compteur (ex: !kill add 10)
  • !kill del <x> : retirer x au compteur (ex: !kill del 10)
  • !kill set 0 : met à 0 le compteur (ex: !kill set 100)