Appearance
kapp.dynamicTriggers (triggers à l'exécution)
Les dynamic triggers existent uniquement pendant une exécution d'appli. Ils sont liés à son app_uuid, déclenchent un callback dans cette instance et sont supprimés automatiquement quand elle se ferme.
Deux contrats publics coexistent volontairement :
createFromConfig(config, callback?)est la méthode conseillée. Elle accepte uneDynamicTriggerConfigcomplète, construite manuellement ou hydratée par un WorkUnitdynamicTrigger, et utilise le contrat strictPOST/PUT /trigger/v2.create(data: DynamicTriggerCreateData)est le contrat historique déprécié. Il reste pleinement fonctionnel et conserve exactement ses payloads et ses appelsPOST/PATCH /trigger/v1pour les applis déjà publiées.
Une migration de create() vers createFromConfig() doit fournir la configuration V2 complète et normalisée ; ce n'est pas un simple changement de nom.
Créer depuis une préférence
js
const config = kapp.preferences.get('viewerAction');
const trigger = await kapp.dynamicTriggers.createFromConfig(
config,
async (_trigger, request, resolve, reject) => {
if (!gameIsRunning) {
await reject('La partie est terminée');
return 'rejected';
}
await kapp.chat.say(`${request.setter.display_name} déclenche l'action !`);
await resolve();
return 'ok';
},
);
if (trigger) {
// La préférence optionnelle était configurée par le streamer.
await trigger.update({
...config,
title: 'Nouvelle action',
cooldown: null,
max_per_stream: null,
});
}Signature exacte :
ts
createFromConfig(
config: DynamicTriggerConfig,
onTrigger?: DynamicTriggerCallFunction
): Promise<DynamicTrigger<DynamicTriggerConfig>>
createFromConfig(
config: null,
onTrigger?: DynamicTriggerCallFunction
): Promise<null>
createFromConfig(
config: DynamicTriggerConfig | null,
onTrigger?: DynamicTriggerCallFunction
): Promise<DynamicTrigger<DynamicTriggerConfig> | null>Avec une configuration non nulle, le retour est non nullable. Avec null, aucun appel réseau n'est effectué et le résultat est null. Le callback peut être passé à la création ou enregistré ensuite avec trigger.onTrigger(callback).
La méthode n'est pas liée au système de préférences. Une configuration complète peut être construite directement :
js
const intervalTrigger = await kapp.dynamicTriggers.createFromConfig({
type: 'INTERVAL',
title: 'Toutes les 5 minutes',
interval_duration: 300,
});Le trigger retourné mémorise son contrat : trigger.update(configComplet) remplace toute sa configuration via PUT /trigger/v2/{id}. Le type est immuable et les champs d'un autre type sont refusés en 400. Les champs nullable (cooldown, limites, autorisation) acceptent explicitement null pour être effacés.
Arguments propres au trigger dynamique
Ajoute arguments dans le premier paramètre de createFromConfig(). Le code de l'app porte ce schéma ; le WorkUnit et le streamer continuent de gérer uniquement les réglages du déclencheur. Ne déclare pas ce schéma dans infos.json, dans la préférence ou dans les arguments de lancement de l'app.
ts
const config = kapp.preferences.get('give') as DynamicTriggerConfig | null;
const trigger = await kapp.dynamicTriggers.createFromConfig(config === null ? null : {
...config,
arguments: [
{name: 'Joueur', key: 'player', type: 'Player', required: true},
{name: 'Montant', key: 'amount', type: 'int', required: true},
],
}, async (_trigger, request, resolve) => {
// !give @viewer 25 : player est un User du SDK, amount est un number.
console.info(request.args.player.display_name, request.args.amount);
console.info(request.args._raw); // saisie brute, par exemple "@viewer 25"
await resolve();
return 'ok';
});
trigger?.onTrigger(async (_trigger, request) => {
// Même inférence TypeScript lors d'un enregistrement différé.
console.info(request.args.amount);
return 'observed';
});Une définition contient name, key, type, puis éventuellement required (défaut false), description et separator. L'ordre définit le parsing des saisies textuelles. Les clés doivent être uniques, respecter [A-Za-z_][A-Za-z0-9_]* et ne pas être réservées au transport (_raw, _config) ou au prototype JavaScript (__proto__, constructor, etc.). Un schéma invalide produit un HTTP 400 avant la création du trigger ou de sa récompense Twitch.
| Type du schéma | Valeur reçue dans request.args[key] |
|---|---|
Player | User du SDK |
int | number |
bool | boolean |
string, long_string, tts_input | string |
string_list | string[] |
Les arguments facultatifs absents valent null. Les littéraux permettent l'inférence automatique ; pour réutiliser un schéma déclaré séparément, utilise as const, éventuellement avec satisfies DynamicTriggerArgumentSchema. DynamicTriggerConfigWithArguments<typeof schema> permet d'annoter une configuration enrichie sans perdre ses clés.
Les règles des triggers existants sont conservées : guillemets pour les valeurs contenant des espaces, dernier long_string/tts_input/string_list non quoté consommant le reste, booléens oui/non, true/false, etc. string_list utilise separator (défaut ,, jamais une chaîne vide), ou accepte directement un tableau depuis le dashboard. La conversion des entiers reste permissive : abc devient 0 ; valide les contraintes métier comme un montant positif dans ton callback.
Le backend applique les contrôles d'accès de l'app propriétaire à l'auteur et aux cibles Player, dans le channel courant, avant débit et appel du callback. Aucun nouveau scope n'est nécessaire au-delà de triggers:manage. Le callback garde les mêmes paramètres et helpers resolve/reject.
Les formulaires streamer/viewers utilisent ce schéma automatiquement. Pour les rewards Twitch, needs_input reste un réglage du trigger. Une source sans saisie fournit null pour les arguments facultatifs et échoue si un argument requis manque. _raw garde la saisie chat/reward verbatim ; pour les formulaires, il est reconstruit depuis les valeurs. _config contient les définitions normalisées.
Le schéma reste immuable pendant l'exécution. trigger.update(config) peut l'omettre ou répéter le même schéma ; ajouter, modifier ou retirer le schéma produit un HTTP 400. Pour changer le contrat, supprime le trigger puis recrée-le. Les réglages restent modifiables selon le contrat V2 habituel.
Sans propriété arguments, les payloads historiques restent identiques. Une liste explicite arguments: [] active le nouveau format avec uniquement _raw et _config. Les arguments du callback ne remplacent jamais kapp.arguments, qui décrit le lancement de l'app.
Contrat historique create()
js
const trigger = await kapp.dynamicTriggers.create({
type: 'CHATCMD',
title: 'Boom',
keyword: 'boom',
cost: 0,
});
trigger.onTrigger(async (_trigger, request, resolve) => {
await kapp.chat.say(`Boom pour ${request.setter.display_name} !`);
await resolve();
return 'ok';
});
await trigger.update({
type: 'CHATCMD',
title: 'Super boom',
keyword: 'boom',
cost: 0,
});Cette voie dépréciée continue d'appeler /trigger/v1 : création en POST, mise à jour partielle en PATCH. DynamicTriggerCreateData reste son type public historique.
Objet DynamicTrigger
trigger.onTrigger(cb)enregistre un callback exécuté à chaque activation.- Le callback reçoit
(trigger, trigger_request, resolve, reject). resolve()confirme l'activation ;reject(reason?)la refuse et rembourse la monnaie si nécessaire.trigger.remove()supprime le trigger avant la fin de l'exécution.resolve,reject,removeet le routage des callbacks restent communs aux deux contrats.
Permissions, feature gate et limites
- Déclare le scope
triggers:managedansinfos.json. Ses règles habituelles de compatibilité de manifeste s'appliquent ; il n'existe pas de nouveau scope V2. - Un type
TWREWexige en plus que la feature Twitch Rewards soit accessible sur le channel. Coût, prompt, couleur, input, cooldown et quotas sont synchronisés avec la récompense Twitch. - Pour
CHDASHBetPLDASHB, le formulaire éventuel vient des arguments déclarés par l'appli. L'ancien champneeds_inputest accepté puis ignoré ; seulTWREW.needs_inputconfigure réellement une saisie externe, côté Twitch. - Une exécution peut détenir au maximum 20 dynamic triggers, tous types confondus. Ce plafond technique n'est pas un quota d'abonnement.
- À la fermeture de l'appli, ses triggers et éventuelles récompenses Twitch associées sont nettoyés automatiquement.
Validation des titres
Le contrat V2 refuse les titres de plus de 25 caractères, sauf TWREW limité à 45. Le contrat historique V1 conserve sa compatibilité : un titre trop long y est tronqué dans la réponse, jamais rejeté.
La matrice complète des neuf types, champs et valeurs par défaut du WorkUnit est dans preferences.md. La forme exacte de l'union discriminée DynamicTriggerConfig est aussi publiée dans app-sdk.d.ts.
Pièges
- Passe à
createFromConfig()une configuration complète. Une valeur hydratée par le WorkUnit peut être transmise telle quelle ; une configuration manuelle doit respecter la même union discriminée. - Un
update()V2 attend à nouveau la configuration complète du même type ; ce n'est pas un PATCH. - Ne crée pas un trigger par viewer ou dans une boucle non bornée : tu atteindrais le plafond de 20.
- Un dynamic trigger n'est pas un trigger durable de channel : il disparaît avec l'exécution de l'appli.