Exemples d'API
Utilisez cette page pour copier la forme de requête appropriée pour chaque flux d'extension.
- Les exemples de pont Iframe utilisent des actions
postMessagetelles quesession.get,state.getetfiles.get. - Les exemples privilégiés utilisent votre propre backend d'extension avec
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEYetx-chastify-main-token. - Ne jamais envoyer une clé API développeur à un code iframe ou navigateur.
Le pont iframe sert à initialiser l'interface utilisateur et à gérer les opérations de session à faible risque. Utilisez-le pour lire le contexte, stocker l'état géré par l'extension et résoudre les URL de fichiers.
Le code iframe du navigateur est contrôlé par l'utilisateur. N'utilisez pas les requêtes de pont iframe pour appliquer/supprimer du temps, effectuer des tâches, lever les blocages de déverrouillage, télécharger/supprimer des fichiers d'exécution, envoyer des notifications, écrire des journaux ou commander des appareils.
Format de demande et de réponse
Les actions sécurisées du pont iframe sont envoyées dans cette enveloppe de requête de pont :
{
"type": "chastify:ext:req", // required
"v": 1, // protocol version
"id": "req-123", // your unique request id
"nonce": "from-iframe-hash",
"action": "session.get",
"payload": {}
}
Et vous recevez :
{
"type": "chastify:ext:resp",
"v": 1,
"id": "req-123", // same id you sent
"ok": true,
"data": {}
}
Si ok est false, vérifiez error.code et error.message.
Les exemples côté serveur utilisent des requêtes HTTPS classiques depuis votre serveur. Elles ne transitent pas par le pont iframe.
Session et contexte
session.get
Utilisez ceci en premier dans presque tous les flux d'extension.
Ce que cela fait :
- Vérifie que votre connexion pont fonctionne.
- Renvoie le contexte de session (état de verrouillage, rôle, configuration de l'extension, capacités).
- Renvoie les informations de capacité du périphérique que votre interface utilisateur peut utiliser avant de demander à votre serveur d'appeler
device.command.
À quoi ça sert :
- Initialisation de votre interface utilisateur.
- Activation/désactivation des fonctionnalités en fonction du rôle et des autorisations.
- Vérification des commandes prises en charge par le périphérique avant le rendu des commandes du périphérique.
Exemple de charge utile d'action :
{
"action": "session.get",
"payload": {}
}
Exemple d'extrait de réponse session.get (données de verrouillage d'exécution) :
{
"ok": true,
"data": {
"lockData": {
"frozen": false,
"unlockable": false,
"trusted": true,
"taskAssigned": true,
"timeLockedSeconds": 1420,
"timeRemainingSeconds": 27800,
"maxTimeRemainingSeconds": 86400,
"taskPoints": 12,
"taskPointsRequired": 20,
"lockTitle": "Weekend Challenge",
"wearerUsername": "alice",
"keyholderUsername": "kh_bob",
"wearerLastSeenTimestamp": 1739640505123,
"keyholderLastSeenTimestamp": null
}
}
}
Remarques relatives à la confidentialité :
- Les codes
wearerLastSeenTimestampetkeyholderLastSeenTimestampne sont renvoyés que si l'utilisateur possède le codeshowOnlineStatus !== false. - Si la visibilité du statut en ligne est désactivée, ces champs sont
null.
Flux d'extension du backend
Ces exemples illustrent le modèle de production sûr :
- L'iframe lit
mainTokenetsessionIdà partir de la charge utile de hachage. - L'iframe les envoie à votre serveur.
- Votre système dorsal valide l'état de votre jeu/tâche/activité.
- Votre serveur appelle Chastify avec une clé API développeur à portée d'application plus
x-chastify-main-token.
N’utilisez pas sessionId seul comme méthode d’authentification. Considérez mainToken comme un contexte de lancement visible par le navigateur et votre clé API développeur comme un secret réservé au serveur.
Votre serveur doit valider l'état de son propre jeu/de sa propre tâche avant d'appeler Chastify. La transmission de mainToken et sessionId depuis l'iframe identifie uniquement la session d'extension ouverte ; elle ne prouve pas que l'utilisateur a relevé un défi.
Récupérer le contexte de session en toute sécurité
Votre iframe peut utiliser le pont sécurisé session.get pour l'initialisation de l'interface utilisateur. Si votre serveur a besoin du même contexte avant d'appliquer une action privilégiée, récupérez-le auprès de votre serveur avec les deux identifiants.
Iframe :
const hash = JSON.parse(decodeURIComponent(window.location.hash.slice(1)));
await fetch("/api/my-extension/session-context", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
mainToken: hash.mainToken,
sessionId: hash.sessionId
})
});
Votre backend :
app.post("/api/my-extension/session-context", async (req, res) => {
const { mainToken, sessionId } = req.body;
const response = await fetch(
`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}`,
{
headers: {
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
}
}
);
if (!response.ok) {
return res.status(response.status).json(await response.json());
}
const context = await response.json();
res.json({
role: context.role,
config: context.extensionConfig,
lockData: context.lockData
});
});
Appliquer l'heure depuis un backend d'extension
Le navigateur peut demander à votre serveur d'appliquer une récompense ou une sanction, mais il ne doit pas décider de la validité de cette action. La vérification côté serveur doit être effectuée en premier lieu.
app.post("/api/my-extension/apply-reward", async (req, res) => {
const { mainToken, sessionId, runId } = req.body;
const run = await db.gameRuns.findUnique({ where: { id: runId } });
if (!run || run.sessionId !== sessionId || !run.serverVerifiedWin) {
return res.status(403).json({ error: "game_not_verified" });
}
const response = await fetch(
`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}/action`,
{
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
},
body: JSON.stringify({
name: "remove_time",
params: 300
})
}
);
if (!response.ok) {
return res.status(response.status).json(await response.json());
}
res.json(await response.json());
});
Utilisez add_time pour les punitions et remove_time pour les récompenses.
Terminaison du jeu vérifiée par le serveur
Pour les jeux comme Simon Says, ne vous fiez pas à une victoire annoncée par le client. Créez la séquence d'exécution côté serveur, stockez la séquence attendue ou son hachage, et vérifiez les données saisies avant d'appeler Chastify.
Un jeu de mémoire accessible via un navigateur ne permet pas de prouver l'honnêteté d'un joueur, car le navigateur doit recevoir la séquence pour afficher le jeu. La vérification côté serveur empêche néanmoins la falsification des mutations Chastify et permet à votre système de gérer les identifiants de partie, l'expiration, la difficulté, la cadence, le système de points et la protection contre la relecture avant l'attribution des récompenses ou des pénalités.
app.post("/api/simon/runs", async (req, res) => {
const { mainToken, sessionId } = req.body;
await verifySessionLaunch({ mainToken, sessionId });
const sequence = createSimonSequence();
const run = await db.simonRuns.create({
data: {
sessionId,
sequenceHash: hashSequence(sequence),
expiresAt: new Date(Date.now() + 5 * 60_000)
}
});
res.json({
runId: run.id,
sequence
});
});
app.post("/api/simon/runs/:runId/complete", async (req, res) => {
const { mainToken, sessionId, input } = req.body;
await verifySessionLaunch({ mainToken, sessionId });
const run = await db.simonRuns.findUnique({ where: { id: req.params.runId } });
if (!run || run.sessionId !== sessionId || run.expiresAt < new Date()) {
return res.status(403).json({ error: "run_invalid" });
}
const won = hashSequence(input) === run.sequenceHash;
await db.simonRuns.update({
where: { id: run.id },
data: { completedAt: new Date(), serverVerifiedWin: won }
});
if (won) {
await fetch(`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}/requirements/progress`, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
},
body: JSON.stringify({
key: "simon_says_wins",
amount: 1
})
});
}
res.json({ won });
});
Le schéma de stockage exact des exécutions vous appartient. verifySessionLaunch doit appeler Chastify avec votre clé API développeur (à portée de l'application) et x-chastify-main-token avant de faire confiance à sessionId. Il est important de noter que les modifications de Chastify ne sont effectuées qu'après vérification du lancement et du résultat par votre système dorsal.
Exigences planifiées
Votre système dorsal gère les planifications, les contrôles de cadence et la validation des preuves. Utilisez Chastify uniquement pour enregistrer les progrès fiables ou mettre à jour les éléments bloquants une fois que votre système dorsal a déterminé que la condition est remplie.
async function recordDailyRequirementProgress({ sessionId, mainToken, userId }) {
const completed = await db.dailyCheckins.exists({
where: {
userId,
day: new Date().toISOString().slice(0, 10),
verified: true
}
});
if (!completed) return;
await fetch(`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}/requirements/progress`, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
},
body: JSON.stringify({
key: "daily_checkin",
amount: 1
})
});
}
Les API de session des extensions installées nécessitent actuellement un jeton de lancement iframe valide au format x-chastify-main-token. Ces jetons expirent après 10 heures. Pour les tâches planifiées s'exécutant après cette expiration, stockez votre propre preuve d'exécution en attente et soumettez la progression lors du prochain lancement valide de l'extension, ou utilisez un flux serveur interne conçu pour les opérations en arrière-plan automatisées.
Ne considérez pas les tâches planifiées comme fiables simplement parce qu'elles s'exécutent sur votre serveur. La tâche nécessite toujours une preuve côté serveur, des contrôles de cadence, une protection contre la relecture et un chemin d'autorisation Chastify valide avant l'enregistrement de la progression des exigences.
Notifications
notifications.custom
Utilisez cette fonction depuis le backend de votre extension pour envoyer une notification personnalisée à l'utilisateur, au détenteur de la clé ou aux deux.
Point final :
POST /api/extensions/sessions/:sessionId/notifications/custom
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Exemple de corps :
{
"title": "Extension Reminder",
"message": "Your next challenge is ready.",
"showPageOverlay": false,
"target": "both"
}
Remarques :
showPageOverlayest par défaut égal àfalse.targetest par défaut égal àwearer.- L'API crée un type de notification
extension_app_message.
État d'extension
L'état de l'extension correspond à vos données JSON appartenant à l'extension pour la session de verrouillage actuelle.
Les modifications d'état sont réservées au serveur et nécessitent une clé API développeur spécifique à l'application ainsi que la session mainToken. Les iframes peuvent lire l'état avec state.get, mais ne peuvent pas y modifier directement l'état.
state.put
Ce que cela fait :
- Remplace l'objet d'état entier par le nouvel objet
data. - Nécessite des identifiants d'accès au serveur.
Quand l'utiliser :
- Sauvegarde initiale.
- Écrasement complet alors que vous disposez déjà du nouvel état complet.
Exemple:
curl -X PUT "https://chastify.net/api/extensions/sessions/$SESSION_ID/state" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"counter": 1,
"notes": "first test"
}
}'
Notes de terrain :
payload.data: toute valeur/objet JSON valide dont votre extension a besoin.- Avoid Mongo-unsafe key names (
$prefix or keys containing.). - L'état est limité à la session et à la taille par le
stateMaxBytesde l'application d'extension, avec une valeur par défaut de 64 KiB. - Stockez les identifiants de fichiers dans l'état, et non les fichiers binaires ou les URL signées. Utilisez
files.getpour actualiser les URL signées avant le rendu multimédia.
state.patch
Ce que cela fait :
- Applique un correctif de fusion JSON à l'état existant.
- Seules les clés changées doivent être envoyées.
- Nécessite des identifiants d'accès au serveur.
Quand l'utiliser :
- Mises à jour incrémentales issues des interactions des utilisateurs.
- Mise à jour d'un seul champ sans tout renvoyer.
Exemple:
curl -X PATCH "https://chastify.net/api/extensions/sessions/$SESSION_ID/state" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"counter": 2
}
}'
state.get
Ce que cela fait :
- Lit l'état actuel de l'extension.
- Disponible via le pont iframe.
Quand l'utiliser :
- Au chargement de l'iframe.
- Après l'écriture, si vous souhaitez resynchroniser l'interface utilisateur locale.
Exemple:
{
"action": "state.get",
"payload": {}
}
Stockage de fichiers
Utilisez files.* pour les médias binaires tels que les images de puzzle, les aperçus générés ou les photos de défis. Stockez le code file.id renvoyé dans l'état côté serveur ou dans votre propre base de données. Effectuez le rendu avec file.signedUrl, puis actualisez l'URL signée avec files.get lors du chargement ultérieur de l'iframe.
Les écrans de configuration qui s'affichent avant la fin d'une session utilisent des chargements intermédiaires au lieu de files.upload. Les fichiers intermédiaires sont temporaires jusqu'à ce que la configuration de l'extension soit enregistrée avec les références provider: "chastify_storage" et fileId. Chastify revendique automatiquement ces fichiers lors de l'enregistrement du verrouillage ou du modèle ; aucun appel de revendication supplémentaire n'est nécessaire côté navigateur.
Exemple de séparation état/fichier depuis votre backend :
curl -X PATCH "https://chastify.net/api/extensions/sessions/$SESSION_ID/state" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"puzzleImageFileId": "file_record_id"
}
}'
Ensuite, résolvez l'image avant l'affichage :
const state = await bridge.request("state.get", {});
const file = await bridge.request("files.get", {
fileId: state.data.puzzleImageFileId
});
image.src = file.file.signedUrl;
files.capabilities
Vérifiez ceci avant d'afficher les commandes de téléchargement.
{
"action": "files.capabilities",
"payload": {}
}
Exécution files.upload
Le chargement de fichiers à l'exécution modifie les données de session de l'extension ; il ne s'agit donc pas d'une commande de pont iframe. Chargez les fichiers d'exécution depuis votre serveur avec une clé API développeur spécifique à l'application et le code x-chastify-main-token.
files.get
Actualiser une URL R2 signée à partir d'un identifiant de fichier stable.
{
"action": "files.get",
"payload": {
"fileId": "file_record_id"
}
}
files.list
{
"action": "files.list",
"payload": {}
}
L'environnement d'exécution files.delete est également réservé au backend pour la même raison que le téléchargement.
Actions de verrouillage
Ajouter du temps
Point de terminaison : POST /api/extensions/sessions/:sessionId/action
Ce que cela fait :
- Ajoute ou retire du temps au compte à rebours du verrouillage en fonction de
deltaSeconds.
Quand l'utiliser :
- Boutons de récompense/pénalité.
- Résultats de la partie (une victoire ajoute du temps, une défaite en retire).
Exemple:
{
"name": "add_time",
"params": 300 // +300 sec = +5 minutes
}
Notes de terrain :
- La valeur positive ajoute du temps.
- Une valeur négative supprime du temps (si les règles du serveur le permettent).
Geler
Point de terminaison : POST /api/extensions/sessions/:sessionId/action
Ce que cela fait :
- Les gels bloquent la progression pendant une durée déterminée.
Quand l'utiliser :
- Mécanismes de temps de recharge.
- Points de contrôle de récompense.
Exemple:
{
"name": "freeze",
"params": { "durationSeconds": 120 }
}
Vous pouvez également l'appeler sans durationSeconds :
{
"name": "freeze",
"params": {}
}
Notes de terrain :
durationSecondsest facultatif.- Si cette valeur est omise, la valeur par défaut actuelle est de
3600secondes (1 heure). - La plage acceptée est de
60à86400secondes.
Dégeler
Point de terminaison : POST /api/extensions/sessions/:sessionId/action
Ce que cela fait :
- Met fin au gel actif et rétablit le comportement normal du minuteur.
Quand l'utiliser :
- Remplacement manuel dans les flux de travail d'extension.
- Commandes « Annuler le gel ».
Exemple:
{
"name": "unfreeze",
"params": {}
}
Pilori
Point de terminaison : POST /api/extensions/sessions/:sessionId/action
Ce que cela fait :
- Démarre une période de mise au pilori pour la session de verrouillage active.
Quand l'utiliser :
- Mécanismes de pénalité après échec de tâches/défis.
- Flux d'escalade qui restreignent temporairement l'interaction avec les verrous.
Exemple:
{
"name": "pillory",
"params": {
"durationSeconds": 600,
"reason": "Missed scheduled check-in"
}
}
Notes de terrain :
namedoit êtrepillory.params.durationSecondsest requis.params.reasonest facultatif.- Nécessite l'activation du pilori dans la configuration de la session.
Pilori final
Point de terminaison : POST /api/extensions/sessions/:sessionId/action
Ce que cela fait :
- Met fin immédiatement à la séance de pilori en cours.
Exemple:
{
"name": "pillory.end",
"params": {}
}
Notes de terrain :
namedoit êtrepillory.end.- Échec avec le code
pillory_not_activesi le cadenas n'est pas actuellement au pilori.
Attribuer une tâche
Point de terminaison : POST /api/extensions/sessions/:sessionId/action
Ce que cela fait :
- Crée une exécution de tâche active pour l'utilisateur à partir de la logique d'extension.
- Peut remplacer une tâche déjà en cours d'exécution.
Exemple:
{
"name": "task.assign",
"params": {
"taskText": "Clean your room",
"points": 10,
"verificationRequired": true,
"durationSeconds": 1800
}
}
Notes de terrain :
taskTextest requis.pointsest optionnel et limité côté serveur.verificationRequiredest par défaut égal àfalse.durationSecondsest facultatif (0signifie qu'aucune minuterie n'est requise).- Nécessite l'activation du module Tâches sur le verrou.
Démarrer le minuteur de tâche
Point de terminaison : POST /api/extensions/sessions/:sessionId/action
Ce que cela fait :
- Démarre ou redémarre le compte à rebours de la tâche chronométrée actuellement active.
Exemple:
{
"name": "task.start_timer",
"params": {}
}
Notes de terrain :
- Nécessite l'exécution d'une tâche active.
- Échec si aucune durée n'est configurée pour la tâche en cours.
Tâche terminée
Point de terminaison : POST /api/extensions/sessions/:sessionId/action
Ce que cela fait :
- Indique si la tâche en cours d'exécution est terminée ou a échoué.
Exemple (succès) :
{
"name": "task.complete",
"params": {
"successful": true
}
}
Exemple (échec) :
{
"name": "task.complete",
"params": {
"successful": false,
"reason": "Did not finish in time"
}
}
Déclencher l'ouverture temporaire
Point de terminaison : POST /api/extensions/sessions/:sessionId/action
Ce que cela fait :
- Démarre une fenêtre d'ouverture temporaire pour l'hygiène à partir de la logique d'extension.
Exemple:
{
"name": "hygienic_unlock.start",
"params": {
"durationSeconds": 900
}
}
Notes de terrain :
- Nécessite l'activation de l'ouverture hygiénique sur la serrure.
- Échoue si une opération d'hygiène est déjà en cours.
durationSecondsest facultatif ; la valeur par défaut du verrou est utilisée lorsqu'il est omis.
Métadonnées et actions d'accueil
metadata.patch
Ce que cela fait :
- Stocke les métadonnées de l'extension utilisées par l'interface utilisateur de la page de verrouillage.
- Prend en charge
unlockBlockersethomeActions. - Prend en charge
homeActions[].intentpour le comportement des liens profonds lors de l'ouverture de l'extension.
Quand l'utiliser :
- Appliquez les conditions de déverrouillage de session verrouillée propres à votre extension.
- Ajoutez des actions rapides sur la page de verrouillage qui ouvrent votre extension avec une intention précise.
- Dirigez les utilisateurs directement vers un écran/flux de travail spécifique lorsqu'ils cliquent sur une action de retour à la page d'accueil.
Point final :
PATCH /api/extensions/sessions/:sessionId/metadata
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Exemple de corps :
{
"unlockBlockers": ["Finish extension task"],
"homeActions": [
{
"slug": "tasks",
"title": "Tasks",
"description": "Open tasks panel",
"intent": {
"type": "open_panel",
"title": "Tasks",
"message": "Open tasks panel",
"payload": {
"panel": "regular-actions"
}
}
}
]
}
Notes de terrain :
unlockBlockers: liste des bloqueurs de déverrouillage de session de verrouillage actifs de votre extension.- Vous pouvez inclure plusieurs bloqueurs à la fois (jusqu'à 25), un par condition non satisfaite.
- Le déverrouillage reste bloqué tant qu'un bloqueur est présent sur les extensions activées.
- Chastify regroupe les bloqueurs de toutes les extensions pour la session de verrouillage.
- Votre extension ne devrait ajouter/supprimer que ses propres bloqueurs dans ses propres métadonnées.
- Ne supprimez pas les éléments bloquants provenant d'autres extensions ; effacez votre tableau uniquement lorsque vos propres conditions sont remplies.
homeActions: boutons d’action rapide affichés dans l’interface de verrouillage.homeActions[].slug: identifiant stable pour votre action.homeActions[].title: étiquette destinée à l’utilisateur.homeActions[].description: texte d'aide facultatif.homeActions[].intent: instruction de lien profond optionnelle transmise à votre extension lors de son ouverture.- Dans l'interface utilisateur de la carte d'extension, ces actions sont affichées sous forme de menu/liste par titre d'action (indexé en interne par slug).
- Lorsqu'un utilisateur clique sur un élément, Chastify ouvre l'extension et transmet :
homeActionSlughomeAction(objet d'action sélectionné)intent(objet d'intention normalisé) Ainsi, votre extension pourra immédiatement rediriger vers la vue/action appropriée au chargement.
Intentions : exemple d'application pour développeur
Utilisez ce modèle dans votre application d'extension pour réagir aux intentions de clic sur le menu lors du chargement.
import { useEffect, useRef } from "react";
import { parseHashPayload, type IframeHashPayload } from "../lib/ChastifyBridge";
export function useHomeActionIntent(
payload: IframeHashPayload,
routeToPanel: (panel: string) => void,
showToast: (message: string) => void,
) {
const handledRef = useRef<string>("");
useEffect(() => {
const homeActionSlug = payload?.homeActionSlug ?? null;
if (!homeActionSlug) return;
// Prevent duplicate handling if component re-renders.
const key = `${payload.lockId || "lock"}:${homeActionSlug}`;
if (handledRef.current === key) return;
handledRef.current = key;
const intent = payload?.intent ?? payload?.homeAction?.intent ?? null;
if (!intent) return;
if (intent.type === "open_panel") {
const panel = String(intent.payload?.panel || "");
if (panel) routeToPanel(panel);
return;
}
if (intent.message) {
showToast(String(intent.message));
}
}, [payload, routeToPanel, showToast]);
}
// Example app bootstrap
const payload = parseHashPayload();
if (!payload) throw new Error("Missing iframe hash payload");
Ce que fait cet exemple :
- Lit
homeActionSlug+intentà partir de la charge utile de hachage de l'iframe. - Chaque clic n'est traité qu'une seule fois par verrou/élément d'action.
- Itinéraires vers un panneau lorsque l'intention est
open_panel. - Pour les types d'intentions personnalisés, un message d'intention est affiché.
Commande de périphérique
device.command
Ce que cela fait :
- Envoie une commande de contrôle de périphérique (si prise en charge pour cette session/ce périphérique).
- Permet à votre extension de déclencher des actions de choc/vibration standardisées via Chastify.
Quand l'utiliser :
- Déclenchement des commandes de choc/vibration prises en charge à partir de la logique d'extension.
- Création de fonctionnalités d'extension interactives (jeux, punitions, récompenses, routines).
Point final :
POST /api/extensions/sessions/:sessionId/device-command
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Exemple de corps :
{
"command": "shock.start",
"params": {
"durationSeconds": 30,
"intensityPct": 50
}
}
Commandes courantes :
shock.startavec les paramètres :{ durationSeconds, intensityPct, message? }shock.stopvibration.startavec les paramètres :{ durationSeconds, intensityPct, frequencyPct?, message? }vibration.stopall.stopshock.random.setavec paramètres :{ enabled, minIntensityPct?, maxIntensityPct?, message? }(Lockink AA-A1012 uniquement)shock.berserk.setavec paramètres :{ enabled, message? }(Lockink AA-A1012 uniquement)
Débit recommandé :
- Appelez
session.getau chargement. - Lire les capacités du périphérique à partir de
deviceControl.supportedCommands. - Afficher uniquement les commandes compatibles.
- Envoyez
device.commandavec des valeurs validées. - Actualisez ou faites confiance aux indicateurs de réponse
activepour l'état de l'interface utilisateur en direct.
Instructions relatives aux paramètres :
durationSeconds: le serveur limite les risques (la durée maximale actuelle est de 300 secondes).intensityPct: valeur de pourcentage attendue (entrée de style1-100, limitée côté serveur).frequencyPct: valeur de pourcentage attendue (1-100) pour les flux de vibration (côté serveur bloqué).- Pour le mode aléatoire, assurez-vous que
minIntensityPct <= maxIntensityPct.
Exemple de modèle d'interface utilisateur plus sûr :
// 1) Get capabilities first
const session = await bridge.request("session.get", {});
const supported = new Set(session?.deviceControl?.supportedCommands ?? []);
// 2) Only call command if supported
if (supported.has("shock.start")) {
await bridge.request("device.command", {
command: "shock.start",
params: { durationSeconds: 30, intensityPct: 50 }
});
}
Important:
- Appelez d'abord
session.getet consultez les commandes prises en charge. - Afficher uniquement les commandes compatibles avec la session en cours.
- Validez les entrées de l'utilisateur avant d'envoyer les paramètres de commande.
device.commandrequiert l'autorisation d'écriture (locks:write) pour la session d'extension.- Gérer les erreurs de pont/serveur avec élégance (
insufficient_scope, commande non prise en charge, erreurs de validation).
Exigences de prolongation
Les exigences d'extension permettent à une extension de définir des règles d'achèvement récurrentes qui s'affichent dans l'interface utilisateur de progression actuelle du verrou et peuvent appliquer une pénalité lorsque le porteur manque une fenêtre.
Utilisez ceci pour les activités d'extension de confiance du serveur, telles que :
- Résolvez 1 puzzle par jour.
- Effectuez 3 enregistrements tous les 2 jours.
- Effectuez 5 actions de prolongement par semaine.
Forme de configuration
L'exigence est stockée dans la configuration de session d'extension sous extensionRequirements :
{
"extensionRequirements": {
"enabled": true,
"metric": "completion",
"requiredCount": 1,
"cadence": {
"every": 1,
"unit": "day"
},
"punishment": {
"type": "add_time",
"seconds": 900,
"reason": "Missed extension requirement"
}
}
}
Champs :
enabled: active/désactive l’exigence récurrente.metric: nom du compteur stable. Utilisez des noms simples tels quecompletion,winouverification.requiredCount: nombre d’événements de confiance qui doivent se produire dans la fenêtre.cadence.every: taille de l’intervalle.cadence.unit:dayouweek.- Le fuseau horaire de la fenêtre est déterminé par Chastify à partir du fuseau horaire configuré par l'utilisateur (
User.timezone). La configuration de l'extension ne doit pas définir de fuseau horaire en dur. punishment.type:none,add_time,freezeoupillory.punishment.seconds: durée de la punition pouradd_time,freezeoupillory.punishment.reason: motif d’audit/de débogage facultatif.
Modèle de progrès
L'état d'avancement des exigences est un état fiable côté serveur, et non un état local à l'iframe.
N’utilisez pas state.patch / state.put pour marquer une exigence comme terminée. Ces actions correspondent à des modifications d’état générales côté serveur et ne constituent pas des API de suivi de la progression des exigences. La progression des exigences ne doit être enregistrée qu’après validation par le serveur de l’événement concerné.
Par exemple:
- Une extension de puzzle ne devrait enregistrer la progression qu'après que le serveur a validé l'exécution et l'état final du puzzle signé.
- Une extension de vérification ne devrait enregistrer la progression qu'une fois que le serveur a accepté la preuve soumise.
- Une extension de jeu ne devrait enregistrer la progression qu'après que le serveur ait validé le résultat du jeu ou l'événement de fin de jeu confirmé.
Comportement d'exécution
Une fois configuré :
- Le tableau de bord de verrouillage peut afficher l'état d'avancement dans la section « Progression du jour ».
- Chastify suit la progression par session d'extension et par fenêtre de cadence.
- La tâche planifiée évalue les fenêtres terminées et applique la sanction configurée une fois par fenêtre manquée.
- Les sanctions sont idempotentes par fenêtre, les nouvelles tentatives n'entraînent donc pas de pénalités identiques.
Flux d'exigences recommandé
- Configurez
extensionRequirementsdans l'interface utilisateur de configuration de l'extension. - Au démarrage en cours d'exécution, appelez
session.getet lisez la configuration active. - Terminez l'activité d'extension dans l'interface utilisateur.
- Envoyer la confirmation à une route backend de confiance pour cette extension.
- Laissez le système dorsal valider l'événement et enregistrer la progression des exigences.
- Actualisez l'interface utilisateur locale avec
session.get/state.getune fois l'opération terminée.
Important:
- Considérez
state.*comme un espace de stockage appartenant exclusivement à l'extension. Utilisez des API dédiées et fiables pour la progression, les tentatives, les récompenses et les sanctions. - Ne vous fiez pas aux indicateurs de complétion réservés au client pour les exigences.
- Veillez à ce que les noms
metricrestent stables ; toute modification de la métrique entraîne un comptage dans un compartiment différent. - Chastify utilise le fuseau horaire configuré par l'utilisateur pour les fenêtres de cadence. Si aucun fuseau horaire n'est disponible pour l'utilisateur, le serveur utilise
UTCpar défaut. - Les sanctions doivent être limitées et explicables dans les journaux d'audit.
Ordre d'action recommandé
- Appelez
session.getau démarrage. - Lire l'état avec
state.get. - Effectuez des écritures d'état depuis votre backend avec
PUT/PATCH /statelorsque nécessaire. - N’exécutez les actions de verrouillage/périphérique que lorsqu’elles sont prises en charge et visibles dans l’interface utilisateur.
- Pour les activités basées sur des exigences, signalez leur achèvement à un système dorsal de confiance.
- Actualiser la vue locale après les écritures/actions importantes.