Appearance
kapp.announces
Les consignes d'enrichissement utilisent {…} ; les consignes vocales libres utilisent […], selon le contrat TTS commun. Tous les blocs entre crochets restent sur la piste TTS et sont retirés des pistes non vocales, y compris après résolution de #last_message#. Les crochets comme [VIP] sont aussi des consignes : éviter cette notation pour une mention destinée à toutes les pistes.
Le message commun et les variantes des pistes texte utilisent des champs texte classiques. L’éditeur vocal avec choix de voix, raccourcis de consignes et effets est réservé aux variantes de la piste TTS.
Diffuser une annonce sur plusieurs canaux (chat, écran, voix, Discord…). Pluriel : kapp.announces. La diffusion multicanal passe par un moteur backend unique ; ne pas reconstruire son orchestration avec plusieurs appels client.
Service partagé appli + widget. Les sources de configuration et les permissions diffèrent selon le contexte ; les exemples de préférences ci-dessous sont réservés aux applis. Voir permissions.
Quand l'utiliser (vs alternative)
- Appli : annonce configurée dans ses préférences →
play('chemin.de.la.pref'). Le streamer choisit le contenu et les canaux, le code choisit le moment. - Widget : sa propre annonce configurée →
play('announces'), sans service de préférences. Le runtime la joue déjà si la sortie intégrée est active : ne pas la jouer deux fois. Les référencesflowchart:<id>désignent un nœud d’annonce de son propre flowchart. - Raccourcis simples :
twitchChat,screenNotification,screenAnnounce,speech. - Pour cibler UN seul canal précis, tu peux aussi appeler directement le service dédié (
kapp.chat.say,kapp.overlay.notification,kapp.textToSpeech.speech). announce(data)= format riche multi-canal que ton code fabrique (avancé, rarement nécessaire).
Méthodes
play(preference: string, tags = {}, title = null, metadata = {}): Promise<void>— joue l'annonce configurée par le streamer : chemin de préférence d'appli, ou référence du widget indiquée ci-dessus. async.twitchChat(message: string): Promise<void>— écrit dans le chat (=kapp.chat.say). async.twitchAnnounce(message, color = 'primary'): Promise<void>— annonce Twitch colorée. async.speech(message, moderate = false)ouspeech(message, { moderate, target: 'main' | 'current' }): Promise<void>— alias dekapp.textToSpeech.speech. Confirme l’acceptation, sans attendre la lecture. La pistetextToSpeechd’une annonce accepte égalementoptions.target;currentnécessite un contexte overlay d’origine.screenNotification(message, options = {}): Promise<void>— notification à l'écran.options:{ duration?, html?, title?, title_html?, target?, visual? }. async.screenAnnounce(message, options = {}): Promise<void>— grande annonce à l'écran. Mêmesoptions. async.announce(data: AnnounceCreateData, tags = {}, title = null, metadata = {}): Promise<AnnounceModel>— annonce multi-canal fabriquée par le code. async.
Exemple commun (V2)
js
await kapp.announces.screenNotification('Nouveau record battu !', {
duration: 5,
title: 'GG',
});Deux façons de déclencher une annonce
play('chemin') | announce({...}) | |
|---|---|---|
| D'où vient le contenu | des réglages du streamer, lus en base | de ton code |
| Scope à déclarer dans une appli | announces:play, un seul | un par track diffusée (chat:write, discord:message, dashboard-event:create…) |
| Track Discord | fonctionne, le webhook reste côté serveur | il faut fournir TON webhook |
Appli uniquement : une préférence passée à announce() déclenche la même configuration serveur que play(), grâce à sa référence. Le traitement des erreurs diffère : play() rejette, announce() absorbe le refus et le signale en console. Pour la configuration, lire préférences d’annonce. Un widget n’a ni prefs ni kapp.preferences.
js
await kapp.announces.announce(kapp.preferences.get('messages.onWin'), { user: 'Sam' });
await kapp.announces.play('messages.onWin', { user: 'Sam' });Tracks
Une annonce diffuse le même événement sur plusieurs « tracks », chacune activable indépendamment. La colonne « scope » ne concerne que announce({...}) : avec play(), un seul scope suffit (announces:play), puisque c'est le streamer qui a écrit le contenu. Ces scopes sont ceux du manifeste d’une appli ; les widgets n’en déclarent pas.
| Track | Effet | Champ propre | Scope (payload libre) |
|---|---|---|---|
chatMessage | message dans le chat Twitch | — | chat:write |
chatAnnounce | annonce Twitch colorée | color | chat:write |
chatAction | message /me | — | chat:write |
screenNotification | notification à l'écran | options | — |
screenAnnounce | grande annonce à l'écran | options | — |
textToSpeech | lu à voix haute | — | — |
channelDashboardEvent | événement dans le dashboard du streamer | — | dashboard-event:create |
discord | message posté sur un webhook Discord | webhook | discord:message |
service | relais vers des services d'appli | services | app-service:call |
- Chaque track accepte des
variations: une est tirée au sort, sinon on retombe surmessages. channelDashboardEventdécoupe le message : première ligne = titre, reste = corps.discordne part pas si aucun webhook n'est configuré.- Si aucune track n'est activée, l'annonce se replie sur
k.channel.preferences.default_announce_tracks. - Une track qui échoue n'empêche pas les autres de se jouer.
- Chat, annonce Twitch,
/meet Discord attendent lestream_delaydu channel ; les autres partent tout de suite.
Tags de message (#tag#)
Les tags que tu passes en 2e argument sont ajoutés à ceux que la plateforme fournit d'office. Un tag inconnu reste écrit en clair dans le message — c'est ainsi qu'on repère une faute de frappe plutôt que de perdre un bout de phrase.
| Tag | Contenu |
|---|---|
#channel_name# / #channel_display_name# | nom d'affichage du streamer |
#channel_login# | login Twitch |
#currency_name# / #channel_currency_name# | nom de la monnaie du channel |
#currency_symbol# / #channel_currency_symbol# | symbole de la monnaie |
#command_symbol# / #channel_command_symbol# | préfixe des commandes chat |
#channel_language# | langue du channel (fr / en) |
#twitch_stream_title# | titre du stream |
#twitch_stream_category# | catégorie Twitch en cours |
#twitch_stream_chatters# | nombre de chatteurs actifs |
#twitch_stream_messages# | messages échangés depuis le début du live |
#twitch_stream_started_from# | durée du live, 2h30m12s |
#last_message# | le message de base tiré pour cette annonce |
Les deux conventions de nommage (#currency_name# et #channel_currency_name#) sont servies : elles viennent de deux moteurs historiques, les deux marchent.
#twitch_stream_started_at#n'existe plus. Il promettait une heure de début, que le serveur ne peut pas rendre correctement faute de fuseau horaire de channel — et il écrivait en réalité une durée absurde. Pour parler de l'ancienneté du live, utilise#twitch_stream_started_from#.
Habillage : le streamer décide, l'appli décrit
L'appli fournit le contenu de l'événement ; les couleurs, la police, la position et les animations viennent de l'habillage configuré par le streamer dans son dashboard. Il n'existe donc aucune option de style (accentColor, motion, variant…) : toute clé hors contrat est ignorée en silence côté serveur.
visual — joindre une image à l'événement
js
// Une image de la médiathèque du channel, désignée par son token (jamais une URL).
await kapp.announces.screenAnnounce('Nouveau palier atteint !', {
visual: { type: 'image', source: 'channel-media', token: media.token, shape: 'circle' },
});
// Appli uniquement : une image livrée dans son dossier `public_assets`.
await kapp.announces.screenAnnounce('Victoire !', {
visual: { type: 'image', source: 'app-asset', file: 'icons/trophy.png' },
});
// La photo de profil Twitch du viewer concerné.
await kapp.announces.screenNotification(`${user.getDisplayName()} vient de suivre !`, {
visual: { type: 'profile', url: await user.getProfileImageUrl() },
});Sources acceptées : channel-media, library-media (bibliothèque publique), app-asset (fichier de l'appli, chemin relatif à public_assets) et profile (CDN Twitch uniquement). Aucune URL externe n'est acceptée, quelle que soit la source.
Pour app-asset, c'est la version de l'appli installée sur le channel qui est servie : le chemin ne peut pas sortir de public_assets, et seules les images matricielles (png, jpg, jpeg, gif, webp, avif) sont acceptées — pas de SVG.
Un visuel non résolu — jeton inconnu, média d'un autre channel, URL externe, chemin de fichier hors contrat — est simplement retiré : le texte reste affiché.
target — dans quel overlay jouer
main(défaut) : l'overlay principal, même si l'appli tourne dans un overlay secondaire.current: l'overlay qui héberge réellement l'appli, résolu côté serveur.
Une appli ne peut jamais désigner un overlay arbitraire. Si l'overlay ciblé n'est plus ouvert, l'appel échoue avec OVERLAY_NOT_FOUND — il n'y a pas de repli silencieux vers main.
Pièges
options.durationest en secondes (et non en millisecondes, contrairement à ce qu'indiquaient les anciennes versions de cette doc). Une valeur hors bornes est ramenée dans les clous : 1 à 180 s pour une annonce, 1 à 60 s pour une notification.html: truereste supporté, mais le contenu est nettoyé avant affichage : la mise en forme passe, pas les scripts ni les gestionnaires d'événements.announce()attend un objetAnnounceCreateDatastructuré — pour un simple texte, préfèrescreenNotification/screenAnnounce. Ses tracksscreenNotification/screenAnnounceacceptent leurs propresoptions.- La diffusion est asynchrone.
awaitveut dire « la plateforme a accepté », pas « c'est déjà à l'écran » : le délai de stream et l'enrichissement IA passent par une file côté serveur. announce()ne rejette jamais (un refus part dans la console de l'overlay) ;play()rejette. Si tu veux réagir à un mauvais chemin ou un scope manquant, utiliseplay().- Le webhook Discord ne t'est jamais donné. Dans une préférence d'annonce,
discord.webhookest un booléen (« le streamer en a configuré un, ou non »). Pour poster sur Discord avec ton propre webhook, il fautannounce({...})et le scopediscord:message. - Ne modifie pas une préférence d'annonce avant de la jouer : elle porte une référence (
_ref) et c'est la version en base qui sera diffusée, tes surcharges seront ignorées. Si tu veux vraiment décider du contenu, construis un objet littéral. - V1 mort :
kapp.playAnnounceSet(...)n'existe plus.