Passa al contenuto principale

Esempi di API

Utilizza questa pagina per copiare il formato di richiesta corretto per ciascun flusso di estensione.

  • Gli esempi di bridge Iframe utilizzano azioni postMessage come session.get, state.get e files.get.
  • Gli esempi privilegiati utilizzano il proprio backend di estensione con Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY e x-chastify-main-token.
  • Non inviare mai una chiave API per sviluppatori a un iframe o al codice del browser.
informazioni

Il bridge iframe serve per l'avvio dell'interfaccia utente e per le operazioni di sessione a basso rischio. Usalo per leggere il contesto, memorizzare lo stato di proprietà dell'estensione e risolvere gli URL dei file.

attenzione

Il codice iframe del browser è controllato dall'utente. Non utilizzare le richieste bridge iframe per applicare/rimuovere tempo, completare attività, rimuovere blocchi di sblocco, caricare/eliminare file di runtime, inviare notifiche, scrivere log o impartire comandi ai dispositivi.

Formato richiesta e risposta

Le azioni sicure del bridge iframe vengono inviate all'interno di questa richiesta di bridge:

{
"type": "chastify:ext:req", // required
"v": 1, // protocol version
"id": "req-123", // your unique request id
"nonce": "from-iframe-hash",
"action": "session.get",
"payload": {}
}

E tu riceverai:

{
"type": "chastify:ext:resp",
"v": 1,
"id": "req-123", // same id you sent
"ok": true,
"data": {}
}

Se ok è false, controlla error.code e error.message.

Gli esempi che utilizzano solo il backend si basano su normali richieste HTTPS provenienti dal server. Queste richieste non vengono inviate tramite il bridge iframe.

Sessione e sfondo

session.get

Utilizzalo per primo in quasi tutti i flussi di estensione.

Cosa fa:

  • Verifica che la connessione bridge funzioni correttamente.
  • Restituisce il contesto della sessione (stato di blocco, ruolo, configurazione dell'estensione, funzionalità).
  • Restituisce informazioni sulle funzionalità del dispositivo che l'interfaccia utente può utilizzare prima di chiedere al backend di chiamare device.command.

A cosa serve:

  • Avvio della tua interfaccia utente.
  • Attivazione/disattivazione delle funzionalità in base al ruolo e alle autorizzazioni.
  • Verifica dei comandi del dispositivo supportati prima del rendering dei controlli del dispositivo.

Esempio di payload di azione:

{
"action": "session.get",
"payload": {}
}

Esempio di estratto della risposta session.get (dati di blocco in fase di esecuzione):

{
"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
}
}
}

Note sulla privacy:

  • I codici wearerLastSeenTimestamp e keyholderLastSeenTimestamp vengono restituiti solo se l'utente possiede il codice showOnlineStatus !== false.
  • Se la visibilità dello stato online è disabilitata, questi campi sono null.

Flussi di estensione del backend

Questi esempi mostrano il modello di produzione sicuro:

  1. L'iframe legge mainToken e sessionId dal payload dell'hash.
  2. L'iframe li invia al tuo backend.
  3. Il tuo backend convalida lo stato del tuo gioco/attività/business.
  4. Il tuo backend chiama Chastify con una chiave API per sviluppatori con ambito app più x-chastify-main-token.

Non utilizzare sessionId da solo come metodo di autenticazione. Considera mainToken come contesto di avvio visibile nel browser e tratta la tua chiave API per sviluppatori come un segreto accessibile solo dal backend.

warning

Il tuo backend deve convalidare il proprio stato di gioco/attività prima di chiamare Chastify. Il passaggio di mainToken e sessionId dall'iframe identifica solo la sessione dell'estensione aperta; non dimostra che l'utente abbia completato una sfida.

Recupera il contesto della sessione in modo sicuro

Il tuo iframe può utilizzare il bridge sicuro session.get per l'avvio dell'interfaccia utente. Se il tuo backend necessita dello stesso contesto prima di applicare un'azione privilegiata, recuperalo dal backend con entrambe le credenziali.

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
})
});

Il tuo 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
});
});

Applicazione del tempo da un backend di estensione

Il browser può chiedere al tuo backend di applicare una ricompensa o una penalità, ma non deve decidere se l'azione è valida. Verifica prima l'azione lato server.

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());
});

Utilizzare add_time per le punizioni e remove_time per le ricompense.

Completamento del gioco verificato dal server

Per giochi come Simon Says, non fidatevi della vittoria segnalata dal client. Create la sequenza lato server, memorizzate la sequenza prevista o un suo hash e verificate l'input inviato prima di chiamare Chastify.

Un gioco di memoria visibile nel browser non può dimostrare che un essere umano abbia giocato onestamente, perché il browser deve ricevere la sequenza per visualizzare il gioco. La verifica del server impedisce comunque la falsificazione delle mutazioni Chastify e consente al backend di applicare ID di esecuzione, scadenza, difficoltà, cadenza, punteggio e protezione dalla replay prima di applicare premi o penalità.

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 });
});

Lo schema di archiviazione dell'esecuzione esatto è a tua disposizione. verifySessionLaunch dovrebbe chiamare Chastify con la tua chiave API per sviluppatori con ambito app e x-chastify-main-token prima di fidarsi di sessionId. La regola importante è che le modifiche a Chastify avvengono solo dopo che il tuo backend ha verificato l'avvio e il risultato.

Requisiti programmati

Il tuo backend gestisce le pianificazioni, i controlli di cadenza e la convalida delle prove. Utilizza Chastify solo per registrare progressi affidabili o aggiornare i blocchi di sblocco dopo che il tuo backend ha stabilito che il requisito è stato soddisfatto.

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
})
});
}

Le API di sessione delle estensioni installate attualmente richiedono un token di avvio iframe valido in formato x-chastify-main-token; i token di avvio scadono dopo 10 ore. Per le attività pianificate che vengono eseguite dopo la scadenza del token, è possibile memorizzare una propria prova di avanzamento e inviare i progressi al successivo avvio valido dell'estensione, oppure utilizzare un flusso server integrato progettato per l'esecuzione automatica in background.

attenzione

Non considerate i processi pianificati come affidabili solo perché vengono eseguiti sul vostro server. Il processo necessita comunque di una verifica lato server, controlli di cadenza, protezione contro gli attacchi di replay e un percorso di autorizzazione Chastify valido prima di poter registrare l'avanzamento dei requisiti.

Notifiche

notifications.custom

Utilizza questa funzione dal backend della tua estensione per inviare una notifica personalizzata dell'estensione a chi indossa l'estensione, al detentore della chiave o a entrambi.

Punto finale:

POST /api/extensions/sessions/:sessionId/notifications/custom
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

Esempio di corpo del testo:

{
"title": "Extension Reminder",
"message": "Your next challenge is ready.",
"showPageOverlay": false,
"target": "both"
}

Note:

  • showPageOverlay viene automaticamente sostituito con false.
  • target viene automaticamente sostituito con wearer.
  • L'API crea il tipo di notifica extension_app_message.

Stato di estensione

Lo stato dell'estensione corrisponde ai dati JSON di proprietà dell'estensione per la sessione di blocco corrente. Le operazioni di scrittura dello stato sono possibili solo nel backend e richiedono una chiave API per sviluppatori con ambito app, oltre alla sessione mainToken. Gli iframe possono leggere lo stato con state.get, ma non possono scriverlo direttamente.

state.put

Cosa fa:

  • Sostituisce l'intero oggetto di stato con il nuovo oggetto data.
  • Richiede le credenziali di accesso al backend.

Quando usarlo:

  • Salvataggio iniziale.
  • Sovrascrittura completa quando si dispone già del nuovo stato completo.

Esempio:

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"
}
}'

Appunti sul campo:

  • payload.data: qualsiasi valore/oggetto JSON valido richiesto dalla tua estensione.
  • Avoid Mongo-unsafe key names ($ prefix or keys containing .).
  • Lo stato è limitato alla sessione e alle dimensioni dal codice stateMaxBytes dell'app di estensione, con un valore predefinito di 64 KiB.
  • Memorizza gli ID dei file nello stato, non nei file binari o negli URL firmati. Usa files.get per aggiornare gli URL firmati prima del rendering dei contenuti multimediali.

state.patch

Cosa fa:

  • Applica una patch di unione JSON allo stato esistente.
  • È necessario inviare solo le chiavi modificate.
  • Richiede le credenziali di accesso al backend.

Quando usarlo:

  • Aggiornamenti incrementali derivanti dalle interazioni dell'utente.
  • Aggiornare un singolo campo senza dover reinviare tutto.

Esempio:

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

Cosa fa:

  • Legge lo stato attuale dell'estensione.
  • Disponibile tramite il bridge iframe.

Quando usarlo:

  • Al caricamento dell'iframe.
  • Dopo aver scritto, se si desidera risincronizzare l'interfaccia utente locale.

Esempio:

{
"action": "state.get",
"payload": {}
}

Archiviazione dei file

Utilizza files.* per i media binari come immagini di puzzle, anteprime generate o foto di sfide. Memorizza il valore file.id restituito nello stato scritto nel backend o nel tuo database. Esegui il rendering con file.signedUrl e aggiorna l'URL firmato con files.get al successivo caricamento dell'iframe.

Le schermate di configurazione che vengono eseguite prima che esista un blocco/sessione utilizzano caricamenti in fase di staging anziché files.upload. I file in fase di staging sono temporanei finché la configurazione dell'estensione non viene salvata con un riferimento a provider: "chastify_storage" e fileId. Chastify rivendica automaticamente questi file al salvataggio del blocco/modello; non è prevista alcuna chiamata di rivendicazione separata lato browser.

Esempio di suddivisione stato/file dal tuo 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"
}
}'

Successivamente, risolvi l'immagine prima di visualizzarla:

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

Seleziona questa opzione prima di mostrare i controlli di caricamento.

{
"action": "files.capabilities",
"payload": {}
}

Tempo di esecuzione files.upload

Il caricamento dei file in fase di esecuzione modifica i dati della sessione dell'estensione, quindi non è un comando di bridge iframe. Carica i file in fase di esecuzione dal tuo backend con una chiave API per sviluppatori con ambito app e x-chastify-main-token.

files.get

Aggiorna un URL R2 firmato da un ID file stabile.

{
"action": "files.get",
"payload": {
"fileId": "file_record_id"
}
}

files.list

{
"action": "files.list",
"payload": {}
}

Anche il runtime files.delete è disponibile solo per il backend, per lo stesso motivo dell'upload.

Azioni di blocco

Aggiungi tempo

Punto finale: POST /api/extensions/sessions/:sessionId/action

Cosa fa:

  • Aggiunge o rimuove tempo dal conto alla rovescia del lucchetto a seconda di deltaSeconds.

Quando usarlo:

  • Pulsanti premio/penalità.
  • Esiti della partita (la vittoria aggiunge tempo, la sconfitta lo sottrae).

Esempio:

{
"name": "add_time",
"params": 300 // +300 sec = +5 minutes
}

Appunti sul campo:

  • Un valore positivo aggiunge tempo.
  • Un valore negativo rimuove il tempo (se consentito dalle regole del server).

Congelare

Punto finale: POST /api/extensions/sessions/:sessionId/action

Cosa fa:

  • Il blocco impedisce la progressione per un certo periodo di tempo.

Quando usarlo:

  • Meccaniche di ricarica.
  • Punti di controllo premio.

Esempio:

{
"name": "freeze",
"params": { "durationSeconds": 120 }
}

È possibile chiamarlo anche senza durationSeconds:

{
"name": "freeze",
"params": {}
}

Appunti sul campo:

  • durationSeconds è facoltativo.
  • Se omesso, il valore predefinito corrente è 3600 secondi (1 ora).
  • L'intervallo accettato è compreso tra 60 e 86400 secondi.

Scongelare

Punto finale: POST /api/extensions/sessions/:sessionId/action

Cosa fa:

  • Termina il blocco attivo e ripristina il normale funzionamento del timer.

Quando usarlo:

  • Sovrascrittura manuale nei flussi di lavoro delle estensioni.
  • Comandi “Annulla congelamento”.

Esempio:

{
"name": "unfreeze",
"params": {}
}

Pillory

Punto finale: POST /api/extensions/sessions/:sessionId/action

Cosa fa:

  • Avvia un periodo di punizione per la sessione di blocco attiva.

Quando usarlo:

  • Meccanismi di penalità in caso di fallimento di compiti/sfide.
  • Flussi di escalation che limitano temporaneamente l'interazione con il blocco.

Esempio:

{
"name": "pillory",
"params": {
"durationSeconds": 600,
"reason": "Missed scheduled check-in"
}
}

Appunti sul campo:

  • name deve essere pillory.
  • Il codice params.durationSeconds è obbligatorio.
  • params.reason è facoltativo.
  • Richiede che la gogna sia abilitata nella configurazione della sessione.

Fine della gogna

Punto finale: POST /api/extensions/sessions/:sessionId/action

Cosa fa:

  • Termina immediatamente la sessione di gogna attualmente attiva.

Esempio:

{
"name": "pillory.end",
"params": {}
}

Appunti sul campo:

  • name deve essere pillory.end.
  • Si verifica un errore con codice pillory_not_active se il lucchetto non è attualmente in posizione di blocco.

Assegna il compito

Punto finale: POST /api/extensions/sessions/:sessionId/action

Cosa fa:

  • Crea un'attività attiva in esecuzione per chi lo indossa, basata sulla logica di estensione.
  • Può sovrascrivere l'esecuzione di un'attività già aperta.

Esempio:

{
"name": "task.assign",
"params": {
"taskText": "Clean your room",
"points": 10,
"verificationRequired": true,
"durationSeconds": 1800
}
}

Appunti sul campo:

  • Il codice taskText è obbligatorio.
  • points è opzionale e viene limitato lato server.
  • verificationRequired viene automaticamente sostituito con false.
  • durationSeconds è opzionale (0 significa che non è richiesto alcun timer).
  • Richiede che il modulo Attività sia abilitato sulla serratura.

Avvia il timer dell'attività

Punto finale: POST /api/extensions/sessions/:sessionId/action

Cosa fa:

  • Avvia o riavvia il conto alla rovescia per l'attività a tempo attualmente attiva.

Esempio:

{
"name": "task.start_timer",
"params": {}
}

Appunti sul campo:

  • Richiede l'esecuzione di un'attività attiva.
  • L'operazione fallisce se all'attività corrente non è stata configurata alcuna durata.

Completare l'attività

Punto finale: POST /api/extensions/sessions/:sessionId/action

Cosa fa:

  • Contrassegna l'attività attiva come completata o non riuscita.

Esempio (di successo):

{
"name": "task.complete",
"params": {
"successful": true
}
}

Esempio (fallimento):

{
"name": "task.complete",
"params": {
"successful": false,
"reason": "Did not finish in time"
}
}

Aprire temporaneamente il grilletto

Punto finale: POST /api/extensions/sessions/:sessionId/action

Cosa fa:

  • Avvia una finestra di apertura igienica temporanea dalla logica di estensione.

Esempio:

{
"name": "hygienic_unlock.start",
"params": {
"durationSeconds": 900
}
}

Appunti sul campo:

  • Richiede l'attivazione dell'apertura igienica sulla serratura.
  • Il test fallisce se è già in corso un'apertura igienica.
  • durationSeconds è facoltativo; se omesso, viene utilizzato il valore di blocco predefinito.

Metadati e azioni della home page

metadata.patch

Cosa fa:

  • Memorizza i metadati dell'estensione utilizzati dall'interfaccia utente della pagina di blocco.
  • Supporta unlockBlockers e homeActions.
  • Supporta homeActions[].intent per il comportamento di deep link all'apertura dell'estensione.

Quando usarlo:

  • Applica le condizioni di sblocco della sessione di blocco di proprietà della tua estensione.
  • Aggiungi azioni rapide sulla pagina di blocco che aprano l'estensione con un intento specifico.
  • Indirizza gli utenti direttamente a una schermata/flusso di lavoro specifico quando fanno clic su un'azione nella home page.

Punto finale:

PATCH /api/extensions/sessions/:sessionId/metadata
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

Esempio di corpo del testo:

{
"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"
}
}
}
]
}

Appunti sul campo:

  • unlockBlockers: elenco dei blocchi di sblocco della sessione di blocco attivi dalla tua estensione.
  • È possibile includere più bloccanti contemporaneamente (fino a 25), uno per ogni condizione non soddisfatta.
  • Lo sblocco rimane bloccato finché è presente un qualsiasi blocco sulle estensioni abilitate.
  • Chastify aggrega i blocchi di tutte le estensioni per la sessione di blocco.
  • La tua estensione dovrebbe aggiungere/rimuovere i propri blocchi solo nei propri metadati.
  • Non rimuovere i blocchi da altre estensioni; rimuovi il tuo array solo quando le tue condizioni sono soddisfatte.
  • homeActions: pulsanti di azione rapida visualizzati nell'esperienza di blocco.
  • homeActions[].slug: ID stabile per la tua azione.
  • homeActions[].title: etichetta rivolta all'utente.
  • homeActions[].description: testo di supporto facoltativo.
  • homeActions[].intent: istruzione di deep-link opzionale passata all'estensione all'apertura.
  • Nell'interfaccia utente della scheda di estensione, queste azioni vengono visualizzate come un menu/elenco in base al titolo dell'azione (internamente identificato dallo slug).
  • Quando un utente fa clic su uno, Chastify apre l'estensione e passa:
    • homeActionSlug
    • homeAction (oggetto azione selezionato)
    • intent (oggetto di intent normalizzato) in modo che la tua estensione possa reindirizzare immediatamente alla vista/azione corretta al caricamento.

Intenti: esempio di app per sviluppatori

Utilizza questo schema nella tua app di estensione per reagire agli intent di clic sul menu al caricamento.

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");

Ecco cosa fa questo esempio:

  • Legge homeActionSlug + intent dal payload dell'hash dell'iframe.
  • Gestisce ogni clic una sola volta per ogni blocco/azione.
  • Indirizza a un pannello quando l'intento è open_panel.
  • Il sistema torna a mostrare un messaggio di intent per i tipi di intent personalizzati.

Comando del dispositivo

device.command

Cosa fa:

  • Invia un comando di controllo del dispositivo (se supportato per quella sessione/dispositivo).
  • Consente alla tua estensione di attivare azioni standardizzate di urto/vibrazione tramite Chastify.

Quando usarlo:

  • Attivazione dei comandi di urto/vibrazione supportati dalla logica di estensione.
  • Creazione di funzionalità di estensione interattive (giochi, punizioni, ricompense, routine).

Punto finale:

POST /api/extensions/sessions/:sessionId/device-command
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

Esempio di corpo del testo:

{
"command": "shock.start",
"params": {
"durationSeconds": 30,
"intensityPct": 50
}
}

Comandi comuni:

  • shock.start con parametri: { durationSeconds, intensityPct, message? }
  • shock.stop
  • vibration.start con parametri: { durationSeconds, intensityPct, frequencyPct?, message? }
  • vibration.stop
  • all.stop
  • shock.random.set con parametri: { enabled, minIntensityPct?, maxIntensityPct?, message? } (solo Lockink AA-A1012)
  • shock.berserk.set con parametri: { enabled, message? } (solo Lockink AA-A1012)

Flusso consigliato:

  1. Chiama session.get all'avvio.
  2. Leggere le funzionalità del dispositivo da deviceControl.supportedCommands.
  3. Visualizza solo i controlli per i comandi supportati.
  4. Inviare device.command con valori validati.
  5. Aggiorna o attendi la risposta active flag per lo stato dell'interfaccia utente in tempo reale.

Linee guida sui parametri:

  • durationSeconds: il server limita i tempi entro i limiti di sicurezza (la policy attuale prevede un massimo di 300 secondi).
  • intensityPct: valore percentuale previsto (input in stile 1-100, limitato lato server).
  • frequencyPct: valore percentuale previsto (1-100) per i flussi di vibrazione (vincolati lato server).
  • Per la modalità casuale, assicurarsi che minIntensityPct <= maxIntensityPct.

Esempio di modello di interfaccia utente più sicuro:

// 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 }
});
}

Importante:

  • Chiama prima session.get e leggi i comandi supportati.
  • Mostra solo i controlli per i comandi supportati nella sessione corrente.
  • Convalidare gli input dell'utente prima di inviare i parametri del comando.
  • device.command richiede l'autorizzazione di scrittura (locks:write) per la sessione di estensione.
  • Gestire correttamente gli errori del bridge/server (insufficient_scope, comando non supportato, errori di convalida).

Requisiti di estensione

I requisiti di estensione consentono a un'estensione di definire regole di completamento ricorrenti che vengono visualizzate nell'interfaccia utente "Avanzamento odierno" della serratura e possono applicare una penalità quando chi la indossa non rispetta una finestra temporale.

Utilizzare questa opzione per attività di estensione attendibili dal server, come ad esempio:

  • Completa un puzzle al giorno.
  • Effettua 3 check-in ogni 2 giorni.
  • Completa 5 attività di estensione a settimana.

Forma della configurazione

Il requisito è memorizzato nella configurazione della sessione di estensione sotto extensionRequirements:

{
"extensionRequirements": {
"enabled": true,
"metric": "completion",
"requiredCount": 1,
"cadence": {
"every": 1,
"unit": "day"
},
"punishment": {
"type": "add_time",
"seconds": 900,
"reason": "Missed extension requirement"
}
}
}

Campi:

  • enabled: attiva/disattiva il requisito ricorrente.
  • metric: nome del contatore stabile. Utilizzare nomi semplici come completion, win o verification.
  • requiredCount: quanti eventi attendibili devono verificarsi nella finestra temporale.
  • cadence.every: dimensione dell'intervallo.
  • cadence.unit: day o week.
  • Il fuso orario di Windows viene risolto tramite Chastify a partire dal User.timezone configurato dall'utente. La configurazione dell'estensione non deve includere un fuso orario fisso.
  • punishment.type: none, add_time, freeze o pillory.
  • punishment.seconds: durata della punizione per add_time, freeze o pillory.
  • punishment.reason: motivo di audit/debug facoltativo.

Modello di progresso

Lo stato di avanzamento dei requisiti è uno stato affidabile lato server, non uno stato locale all'iframe.

Non utilizzare state.patch / state.put per contrassegnare un requisito come completato. Queste azioni sono scritture di stato generali solo sul backend, non API di avanzamento dei requisiti. L'avanzamento dei requisiti deve essere registrato solo dopo che il server ha convalidato l'evento da conteggiare.

Per esempio:

  • Un'estensione per i puzzle dovrebbe registrare i progressi solo dopo che il server ha convalidato l'esecuzione del puzzle e lo stato di completamento firmato.
  • Un'estensione di verifica dovrebbe registrare l'avanzamento solo dopo che il server ha accettato la prova inviata.
  • Un'estensione di gioco dovrebbe registrare i progressi solo dopo che il backend ha convalidato il risultato del gioco o l'evento di completamento attendibile.

Comportamento in fase di esecuzione

Quando configurato:

  • Il pannello di controllo della serratura può mostrare i requisiti nello stato di avanzamento odierno.
  • Chastify tiene traccia dei progressi per ogni sessione di estensione e per ogni intervallo di tempo.
  • Il processo di requisito pianificato valuta le finestre completate e applica la penalità configurata una volta per ogni finestra non rispettata.
  • Le punizioni sono idempotenti per ogni finestra temporale, quindi i tentativi successivi non comportano l'accumulo di sanzioni duplicate.
  1. Configura extensionRequirements nell'interfaccia utente di configurazione dell'estensione.
  2. All'avvio del runtime, chiama session.get e leggi la configurazione attiva.
  3. Completa l'attività di estensione nell'interfaccia utente.
  4. Invia il completamento a una rotta di backend affidabile per quell'estensione.
  5. Lascia che il backend convalidi l'evento e registri l'avanzamento dei requisiti.
  6. Aggiorna l'interfaccia utente locale con session.get / state.get al termine dell'operazione.

Importante:

  • Considera state.* esclusivamente come spazio di archiviazione di proprietà dell'estensione. Utilizza API dedicate e affidabili per progressi, tentativi, ricompense e penalità.
  • Non fidarti dei flag di completamento disponibili solo lato client per i requisiti.
  • Mantieni stabili i nomi metric; la modifica della metrica comporta l'inserimento in un bucket diverso.
  • Chastify utilizza il fuso orario configurato dall'utente per le finestre di cadenza. Se non è disponibile alcun fuso orario per l'utente, il server utilizza UTC come fallback.
  • Nei registri di controllo, assicurati che le sanzioni siano circoscritte e comprensibili.
  1. Chiama session.get all'avvio.
  2. Leggi lo stato con state.get.
  3. Eseguire le scritture di stato dal backend con PUT/PATCH /state quando necessario.
  4. Esegui le azioni di blocco/sblocco del dispositivo solo se supportate e visibili nell'interfaccia utente.
  5. Per le attività basate su requisiti, segnala il completamento a un percorso di backend affidabile.
  6. Aggiorna la visualizzazione locale dopo operazioni di scrittura/azioni importanti.