API-Beispiele
Nutzen Sie diese Seite, um die richtige Anfrageform für jeden Erweiterungsablauf zu kopieren.
- Beispiele für Iframe-Bridges verwenden
postMessage-Aktionen wiesession.get,state.getundfiles.get. - Privilegierte Beispiele verwenden Ihr eigenes Erweiterungs-Backend mit
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEYundx-chastify-main-token. - Senden Sie niemals einen Entwickler-API-Schlüssel an iFrame- oder Browsercode.
Die iFrame-Brücke dient dem UI-Bootstrap und risikoarmen Sitzungsoperationen. Sie wird verwendet, um Kontext zu lesen, den Status von Erweiterungen zu speichern und Datei-URLs aufzulösen.
Der Code von Browser-iFrames wird vom Benutzer gesteuert. Verwenden Sie keine iFrame-Bridge-Anfragen, um die Zeit zu ändern, Aufgaben abzuschließen, Entsperrsperren zu entfernen, Laufzeitdateien hochzuladen oder zu löschen, Benachrichtigungen zu senden, Protokolle zu schreiben oder Geräte zu steuern.
Anfrage- und Antwortformat
Sichere iFrame-Bridge-Aktionen werden innerhalb dieses Bridge-Anforderungs-Envelopes gesendet:
{
"type": "chastify:ext:req", // required
"v": 1, // protocol version
"id": "req-123", // your unique request id
"nonce": "from-iframe-hash",
"action": "session.get",
"payload": {}
}
Und Sie erhalten:
{
"type": "chastify:ext:resp",
"v": 1,
"id": "req-123", // same id you sent
"ok": true,
"data": {}
}
Wenn ok gleich false ist, prüfe error.code und error.message.
Die reinen Backend-Beispiele verwenden normale HTTPS-Anfragen von Ihrem Server. Sie werden nicht über die iFrame-Brücke gesendet.
Sitzung und Kontext
session.get
Verwenden Sie dies als Erstes in nahezu jedem Erweiterungsablauf.
Was es bewirkt:
- Überprüft, ob Ihre Brückenverbindung funktioniert.
- Gibt den Sitzungskontext zurück (Sperrstatus, Rolle, Erweiterungskonfiguration, Fähigkeiten).
- Gibt Informationen zur Gerätefunktionalität zurück, die Ihre Benutzeroberfläche verwenden kann, bevor Sie Ihr Backend auffordern,
device.commandaufzurufen.
Wofür es verwendet wird:
- Bootstrapping Ihrer Benutzeroberfläche.
- Funktionen basierend auf Rolle und Berechtigungen aktivieren/deaktivieren.
- Vor dem Rendern der Gerätesteuerung werden die unterstützten Gerätebefehle geprüft.
Beispiel einer Aktionsnutzlast:
{
"action": "session.get",
"payload": {}
}
Beispielhafter Auszug aus der Antwort von session.get (Laufzeit-Sperrdaten):
{
"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
}
}
}
Datenschutzhinweise:
wearerLastSeenTimestamp/keyholderLastSeenTimestampwerden nur zurückgegeben, wenn der BenutzershowOnlineStatus !== falsebesitzt.- Wenn die Online-Statusanzeige deaktiviert ist, lauten diese Felder
null.
Backend-Erweiterungsabläufe
Diese Beispiele veranschaulichen das sichere Produktionsmuster:
- Der iFrame liest
mainTokenundsessionIdaus der Hash-Nutzlast. - Der iFrame sendet sie an Ihr Backend.
- Ihr Backend validiert Ihren eigenen Spiel-/Aufgaben-/Geschäftsstatus.
- Ihr Backend ruft Chastify mit einem anwendungsbezogenen Entwickler-API-Schlüssel plus
x-chastify-main-tokenauf.
Verwenden Sie sessionId nicht allein zur Authentifizierung. Behandeln Sie mainToken als für den Browser sichtbaren Startkontext und Ihren Entwickler-API-Schlüssel als ein nur im Backend gespeichertes Geheimnis.
Ihr Backend muss seinen eigenen Spiel-/Aufgabenstatus validieren, bevor Chastify aufgerufen wird. Die Übergabe von mainToken und sessionId aus dem iFrame identifiziert lediglich die geöffnete Erweiterungssitzung; sie beweist nicht, dass der Benutzer eine Herausforderung abgeschlossen hat.
Sitzungskontext sicher abrufen
Ihr iFrame kann die sichere Bridge session.get für den UI-Bootstrap verwenden. Falls Ihr Backend denselben Kontext benötigt, bevor es eine privilegierte Aktion ausführt, rufen Sie ihn mit beiden Anmeldeinformationen von Ihrem Backend ab.
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
})
});
Ihr 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
});
});
Zeit über ein Erweiterungs-Backend anwenden
Der Browser kann Ihr Backend auffordern, eine Belohnung oder Bestrafung anzuwenden, darf aber nicht entscheiden, ob die Aktion gültig ist. Überprüfen Sie die Aktion daher zuerst serverseitig.
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());
});
Verwenden Sie add_time für Bestrafungen und remove_time für Belohnungen.
Serverbestätigter Spielabschluss
Bei Spielen wie Simon Says sollte man einem vom Client gemeldeten Sieg nicht vertrauen. Erstellen Sie den Spielablauf serverseitig, speichern Sie die erwartete Sequenz oder deren Hash und überprüfen Sie die übermittelte Eingabe, bevor Sie Chastify aufrufen.
Ein im Browser sichtbares Memory-Spiel kann nicht beweisen, dass ein Mensch ehrlich gespielt hat, da der Browser die Sequenz empfangen muss, um das Spiel darzustellen. Die Serververifizierung verhindert weiterhin gefälschte Chastify-Mutationen und ermöglicht es Ihrem Backend, Lauf-IDs, Ablaufzeit, Schwierigkeitsgrad, Spielrhythmus, Punktevergabe und Wiederholungsschutz durchzusetzen, bevor Belohnungen oder Strafen angewendet werden.
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 });
});
Das genaue Speicherschema für die Ausführung liegt in Ihrer Verantwortung. verifySessionLaunch sollte Chastify mit Ihrem anwendungsbezogenen Entwickler-API-Schlüssel und x-chastify-main-token aufrufen, bevor sessionId vertraut wird. Wichtig ist, dass Änderungen an Chastify erst erfolgen, nachdem Ihr Backend den Start und das Ergebnis verifiziert hat.
Geplante Anforderungen
Ihr Backend verwaltet Zeitpläne, Taktprüfungen und Nachweisvalidierungen. Verwenden Sie Chastify nur, um vertrauenswürdige Fortschritte zu protokollieren oder Entsperrsperren zu aktualisieren, nachdem Ihr Backend die Erfüllung der Anforderung bestätigt hat.
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
})
});
}
Die aktuellen APIs für installierte Erweiterungssitzungen benötigen ein gültiges iFrame-Starttoken im Format x-chastify-main-token. Starttoken verfallen derzeit nach 10 Stunden. Für geplante Aufgaben, die nach Ablauf des Tokens ausgeführt werden, speichern Sie einen eigenen Nachweis über den ausstehenden Status und übermitteln Sie den Fortschritt beim nächsten gültigen Erweiterungsstart oder verwenden Sie einen Server-Workflow, der für unbeaufsichtigte Hintergrundprozesse ausgelegt ist.
Behandeln Sie geplante Jobs nicht automatisch als vertrauenswürdig, nur weil sie auf Ihrem Server ausgeführt werden. Der Job benötigt weiterhin serverseitigen Nachweis, Überprüfung der Ausführungsfrequenz, Schutz vor Replay-Angriffen und einen gültigen Autorisierungspfad (Chastify), bevor der Fortschritt der Anforderungen protokolliert werden kann.
Benachrichtigungen
notifications.custom
Verwenden Sie dies von Ihrem Erweiterungs-Backend aus, um eine benutzerdefinierte Erweiterungsbenachrichtigung an den Träger, den Schlüsselhalter oder beide zu senden.
Endpunkt:
POST /api/extensions/sessions/:sessionId/notifications/custom
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Beispieltext:
{
"title": "Extension Reminder",
"message": "Your next challenge is ready.",
"showPageOverlay": false,
"target": "both"
}
Anmerkungen:
showPageOverlayist standardmäßigfalse.targetist standardmäßigwearer.- Die API erstellt eine Benachrichtigung vom Typ
extension_app_message.
Erweiterungsstatus
Der Erweiterungsstatus sind Ihre erweiterungseigenen JSON-Daten für die aktuelle Sperrsitzung.
Statusänderungen sind ausschließlich serverseitig möglich und erfordern einen anwendungsbezogenen Entwickler-API-Schlüssel sowie die Sitzungskennung mainToken. Iframes können den Status mit state.get lesen, aber nicht direkt schreiben.
state.put
Was es bewirkt:
- Ersetzt das gesamte Statusobjekt durch das neue Objekt
data. - Erfordert Backend-Zugangsdaten.
Wann verwenden?
- Erster Speicherstand.
- Vollständiges Überschreiben, wenn der neue Zustand bereits vollständig vorhanden ist.
Beispiel:
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"
}
}'
Feldnotizen:
payload.data: ein beliebiger gültiger JSON-Wert/ein beliebiges gültiges JSON-Objekt, das Ihre Erweiterung benötigt.- Avoid Mongo-unsafe key names (
$prefix or keys containing.). - Der Status ist sitzungsbezogen und durch den
stateMaxBytesder Erweiterungs-App größenbegrenzt; der Standardwert beträgt 64 KiB. - Speichern Sie Datei-IDs im Status, nicht Binärdateien oder signierte URLs. Verwenden Sie
files.get, um signierte URLs vor dem Rendern von Medien zu aktualisieren.
state.patch
Was es bewirkt:
- Wendet einen JSON-Merge-Patch auf den bestehenden Zustand an.
- Es müssen nur die geänderten Schlüssel gesendet werden.
- Erfordert Backend-Zugangsdaten.
Wann verwenden?
- Schrittweise Aktualisierungen basierend auf Benutzerinteraktionen.
- Ein Feld aktualisieren, ohne alles erneut zu senden.
Beispiel:
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
Was es bewirkt:
- Liest den aktuellen Erweiterungsstatus.
- Verfügbar über die iFrame-Bridge.
Wann verwenden?
- Beim Laden des iFrames.
- Nach dem Schreiben, falls Sie die lokale Benutzeroberfläche erneut synchronisieren möchten.
Beispiel:
{
"action": "state.get",
"payload": {}
}
Dateispeicherung
Verwenden Sie files.* für Binärmedien wie Puzzlebilder, generierte Vorschauen oder Herausforderungsfotos. Speichern Sie den zurückgegebenen Wert file.id im Backend oder in Ihrer eigenen Datenbank. Rendern Sie mit file.signedUrl und aktualisieren Sie die signierte URL mit files.get, sobald der iFrame später geladen wird.
Einrichtungsbildschirme, die vor dem Beenden einer Sperre/Sitzung ausgeführt werden, verwenden temporäre Uploads anstelle von files.upload. Die temporären Dateien sind so lange temporär, bis die Erweiterungskonfiguration mit einer provider: "chastify_storage"- und einer fileId-Referenz gespeichert wird. Chastify beansprucht diese Dateien automatisch beim Speichern der Sperre/Vorlage; es erfolgt kein separater Aufruf im Browser.
Beispiel für die Aufteilung von Status/Datei aus Ihrem 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"
}
}'
Anschließend das Bild vor der Anzeige auflösen:
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
Prüfen Sie dies, bevor Sie die Upload-Steuerelemente anzeigen.
{
"action": "files.capabilities",
"payload": {}
}
Laufzeit files.upload
Der Upload von Dateien zur Laufzeit verändert die Sitzungsdaten der Erweiterung und ist daher kein Befehl zur Überbrückung von iFrames. Laden Sie Laufzeitdateien von Ihrem Backend mit einem anwendungsbezogenen Entwickler-API-Schlüssel und x-chastify-main-token hoch.
files.get
Eine signierte R2-URL anhand einer stabilen Datei-ID aktualisieren.
{
"action": "files.get",
"payload": {
"fileId": "file_record_id"
}
}
files.list
{
"action": "files.list",
"payload": {}
}
Die Laufzeitumgebung files.delete ist aus dem gleichen Grund wie der Upload ebenfalls nur im Backend verfügbar.
Sperraktionen
Zeit hinzufügen
Endpunkt: POST /api/extensions/sessions/:sessionId/action
Was es bewirkt:
- Fügt je nach
deltaSecondsZeit zum Sperr-Countdown hinzu oder entfernt sie davon.
Wann verwenden?
- Belohnungs-/Strafknöpfe.
- Spielausgänge (Sieg bringt Zeit, Niederlage reduziert Zeit).
Beispiel:
{
"name": "add_time",
"params": 300 // +300 sec = +5 minutes
}
Feldnotizen:
- Positiver Wert fügt Zeit hinzu.
- Ein negativer Wert entfernt die Zeit (sofern dies von den Serverregeln zugelassen ist).
Einfrieren
Endpunkt: POST /api/extensions/sessions/:sessionId/action
Was es bewirkt:
- Einfrieren blockiert den Spielfortschritt für eine bestimmte Zeit.
Wann verwenden?
- Abklingmechaniken.
- Belohnungs-Checkpoints.
Beispiel:
{
"name": "freeze",
"params": { "durationSeconds": 120 }
}
Sie können es auch ohne durationSeconds aufrufen:
{
"name": "freeze",
"params": {}
}
Feldnotizen:
durationSecondsist optional.- Wird dieser Wert weggelassen, beträgt der aktuelle Standardwert
3600Sekunden (1 Stunde). - Der zulässige Bereich liegt zwischen
60und86400Sekunden.
Auftauen
Endpunkt: POST /api/extensions/sessions/:sessionId/action
Was es bewirkt:
- Beendet den aktiven Einfriervorgang und stellt das normale Timerverhalten wieder her.
Wann verwenden?
- Manuelle Überschreibung in Erweiterungs-Workflows.
- „Einfrieren abbrechen“-Steuerelemente.
Beispiel:
{
"name": "unfreeze",
"params": {}
}
Pranger
Endpunkt: POST /api/extensions/sessions/:sessionId/action
Was es bewirkt:
- Startet eine Prangerperiode für die aktive Sperrsitzung.
Wann verwenden?
- Strafmechanismen nach fehlgeschlagenen Aufgaben/Herausforderungen.
- Eskalationsabläufe, die die Interaktion mit Sperren vorübergehend einschränken.
Beispiel:
{
"name": "pillory",
"params": {
"durationSeconds": 600,
"reason": "Missed scheduled check-in"
}
}
Feldnotizen:
namemusspillorysein.params.durationSecondsist erforderlich.params.reasonist optional.- Erfordert, dass der Pranger in der Sitzungskonfiguration aktiviert ist.
Schluss mit dem Pranger
Endpunkt: POST /api/extensions/sessions/:sessionId/action
Was es bewirkt:
- Beendet die aktuell laufende Prangersitzung sofort.
Beispiel:
{
"name": "pillory.end",
"params": {}
}
Feldnotizen:
namemusspillory.endsein.- Fehler mit
pillory_not_active, wenn sich das Schloss nicht im Pranger befindet.
Aufgabe zuweisen
Endpunkt: POST /api/extensions/sessions/:sessionId/action
Was es bewirkt:
- Erstellt einen aktiven Aufgabenlauf für den Träger aus der Erweiterungslogik.
- Kann einen bereits geöffneten Task-Lauf überschreiben.
Beispiel:
{
"name": "task.assign",
"params": {
"taskText": "Clean your room",
"points": 10,
"verificationRequired": true,
"durationSeconds": 1800
}
}
Feldnotizen:
taskTextist erforderlich.pointsist optional und serverseitig begrenzt.verificationRequiredist standardmäßigfalse.durationSecondsist optional (0bedeutet, dass kein Timer erforderlich ist).- Erfordert, dass das Aufgabenmodul auf dem Schloss aktiviert ist.
Task-Timer starten
Endpunkt: POST /api/extensions/sessions/:sessionId/action
Was es bewirkt:
- Startet oder startet das Countdown-Fenster für die aktuell aktive zeitgesteuerte Aufgabe neu.
Beispiel:
{
"name": "task.start_timer",
"params": {}
}
Feldnotizen:
- Erfordert einen aktiven Task-Lauf.
- Schlägt fehl, wenn für die aktuelle Aufgabe keine Dauer konfiguriert ist.
Aufgabe abschließen
Endpunkt: POST /api/extensions/sessions/:sessionId/action
Was es bewirkt:
- Markiert den aktuell ausgeführten Task als abgeschlossen oder fehlgeschlagen.
Beispiel (Erfolg):
{
"name": "task.complete",
"params": {
"successful": true
}
}
Beispiel (fehlgeschlagen):
{
"name": "task.complete",
"params": {
"successful": false,
"reason": "Did not finish in time"
}
}
Temporäre Öffnung auslösen
Endpunkt: POST /api/extensions/sessions/:sessionId/action
Was es bewirkt:
- Startet ein temporäres Hygieneöffnungsfenster aus der Erweiterungslogik.
Beispiel:
{
"name": "hygienic_unlock.start",
"params": {
"durationSeconds": 900
}
}
Feldnotizen:
- Erfordert die Aktivierung der hygienischen Öffnungsfunktion am Schloss.
- Schlägt fehl, wenn eine Hygieneöffnung bereits im Gange ist.
durationSecondsist optional; wird es weggelassen, wird der Standardwert für die Sperre verwendet.
Metadaten und Startseitenaktionen
metadata.patch
Was es bewirkt:
- Speichert Erweiterungsmetadaten, die von der Sperrseiten-Benutzeroberfläche verwendet werden.
- Unterstützt
unlockBlockersundhomeActions. - Unterstützt
homeActions[].intentfür das Deep-Link-Verhalten beim Öffnen der Erweiterung.
Wann verwenden?
- Setzen Sie die Sperr- und Entsperrbedingungen Ihrer Erweiterung durch.
- Fügen Sie auf der Sperrseite Schnellaktionen hinzu, die Ihre Erweiterung mit einer Absicht öffnen.
- Leiten Sie Benutzer direkt zu einem bestimmten Bildschirm/Workflow weiter, wenn sie auf eine Aktion auf der Startseite klicken.
Endpunkt:
PATCH /api/extensions/sessions/:sessionId/metadata
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Beispieltext:
{
"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"
}
}
}
]
}
Feldnotizen:
unlockBlockers: Liste der aktiven Sperr- und Entsperrungsblocker Ihrer Nebenstelle.- Sie können mehrere Blocker gleichzeitig einbeziehen (bis zu 25), einen pro nicht erfüllter Bedingung.
- Die Entsperrung bleibt so lange blockiert, wie in den aktivierten Erweiterungen eine Blockierung aktiv ist.
- Chastify fasst Blocker aller Nebenstellen für die Sperrsitzung zusammen.
- Ihre Erweiterung sollte nur ihre eigenen Blocker in ihren eigenen Metadaten hinzufügen/entfernen.
- Löschen Sie keine Blocker anderer Erweiterungen; leeren Sie Ihr Array nur dann, wenn Ihre eigenen Bedingungen erfüllt sind.
homeActions: Schnellaktionsschaltflächen werden in der Sperransicht angezeigt.homeActions[].slug: Stabile ID für Ihre Aktion.homeActions[].title: Benutzerseitiges Etikett.homeActions[].description: optionaler Hilfetext.homeActions[].intent: Optionale Deep-Link-Anweisung, die beim Öffnen an Ihre Erweiterung übergeben wird.- In der Benutzeroberfläche der Erweiterungskarte werden diese Aktionen als Menü/Liste nach Aktionstitel angezeigt (intern nach Slug kategorisiert).
- Wenn ein Benutzer darauf klickt, öffnet Chastify die Erweiterung und übergibt:
homeActionSlughomeAction(ausgewähltes Aktionsobjekt)intent(normalisiertes Intent-Objekt) damit Ihre Erweiterung beim Laden sofort zur richtigen Ansicht/Aktion weiterleiten kann.
Intents: Beispiel für eine Entwickler-App
Verwenden Sie dieses Muster in Ihrer Erweiterungs-App, um beim Laden auf Menüklick-Intents zu reagieren.
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");
Was dieses Beispiel bewirkt:
- Liest
homeActionSlug+intentaus der Hash-Nutzlast des iFrames. - Verarbeitet jeden Klick nur einmal pro Sperr-/Aktionssegment.
- Leitet zu einem Panel weiter, wenn die Absicht
open_panelist. - Bei benutzerdefinierten Absichtstypen wird stattdessen eine Absichtsnachricht angezeigt.
Gerätebefehl
device.command
Was es bewirkt:
- Sendet einen Gerätesteuerungsbefehl (sofern für diese Sitzung/dieses Gerät unterstützt).
- Ermöglicht es Ihrer Verlängerung, standardisierte Schock-/Vibrationsaktionen über Chastify auszulösen.
Wann verwenden?
- Auslösen unterstützter Schock-/Vibrationsbefehle aus der Erweiterungslogik.
- Entwicklung interaktiver Erweiterungsfunktionen (Spiele, Strafen, Belohnungen, Routinen).
Endpunkt:
POST /api/extensions/sessions/:sessionId/device-command
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Beispieltext:
{
"command": "shock.start",
"params": {
"durationSeconds": 30,
"intensityPct": 50
}
}
Allgemeine Befehle:
shock.startmit Parametern:{ durationSeconds, intensityPct, message? }shock.stopvibration.startmit Parametern:{ durationSeconds, intensityPct, frequencyPct?, message? }vibration.stopall.stopshock.random.setmit Parametern:{ enabled, minIntensityPct?, maxIntensityPct?, message? }(nur Lockink AA-A1012)shock.berserk.setmit Parametern:{ enabled, message? }(nur Lockink AA-A1012)
Empfohlener Ablauf:
- Beim Laden
session.getaufrufen. - Gerätefunktionen von
deviceControl.supportedCommandsauslesen. - Nur Steuerelemente für unterstützte Befehle rendern.
- Senden Sie
device.commandmit validierten Werten. - Aktualisieren oder vertrauen Sie den Antwortflags
activefür den Live-UI-Status.
Parameterhinweise:
durationSeconds: Server begrenzt auf sichere Grenzwerte (aktuelle Richtlinie: Maximalwert 300 Sekunden).intensityPct: erwarteter Prozentwert (Eingabe im Stil1-100, serverseitig begrenzt).frequencyPct: erwarteter Prozentwert (1-100) für Schwingungsströme (eingespannt serverseitig).- Für den Zufallsmodus muss
minIntensityPct <= maxIntensityPctgelten.
Beispiel für ein sichereres UI-Muster:
// 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 }
});
}
Wichtig:
- Rufen Sie zuerst
session.getauf und lesen Sie die Liste der unterstützten Befehle. - Es werden nur Steuerelemente für Befehle angezeigt, die in der aktuellen Sitzung unterstützt werden.
- Benutzereingaben vor dem Senden von Befehlsparametern prüfen.
- Für
device.commandwird die Schreibberechtigung (locks:write) für die Erweiterungssitzung benötigt. - Bridge-/Serverfehler ordnungsgemäß behandeln (
insufficient_scope, nicht unterstützter Befehl, Validierungsfehler).
Verlängerungsanforderungen
Die Erweiterungsanforderungen ermöglichen es einer Erweiterung, wiederkehrende Abschlussregeln zu definieren, die in der Fortschrittsanzeige des Schlosses angezeigt werden und eine Strafe verhängen können, wenn der Träger ein Zeitfenster verpasst.
Verwenden Sie dies für serverseitig vertrauenswürdige Erweiterungsaktivitäten wie zum Beispiel:
- Löse täglich 1 Rätsel.
- Führen Sie alle zwei Tage drei Check-ins durch.
- Erledigen Sie 5 Verlängerungsmaßnahmen pro Woche.
Konfigurationsform
Die Anforderung ist in der Erweiterungssitzungskonfiguration unter extensionRequirements gespeichert:
{
"extensionRequirements": {
"enabled": true,
"metric": "completion",
"requiredCount": 1,
"cadence": {
"every": 1,
"unit": "day"
},
"punishment": {
"type": "add_time",
"seconds": 900,
"reason": "Missed extension requirement"
}
}
}
Felder:
enabled: Schaltet die wiederkehrende Anforderung ein/aus.metric: Name des stabilen Zählers. Verwenden Sie einfache Namen wiecompletion,winoderverification.requiredCount: Wie viele vertrauenswürdige Ereignisse müssen innerhalb des Fensters stattfinden?cadence.every: Intervallgröße.cadence.unit:dayoderweek.- Die Fensterzeitzone wird über Chastify aus dem vom Träger konfigurierten
User.timezoneaufgelöst. Die Konfiguration der Erweiterung sollte keine fest codierte Zeitzone enthalten. punishment.type:none,add_time,freezeoderpillory.punishment.seconds: Strafdauer füradd_time,freezeoderpillory.punishment.reason: optionaler Prüf-/Debug-Grund.
Fortschrittsmodell
Der Fortschritt der Anforderungen ist ein vertrauenswürdiger serverseitiger Zustand, kein iframe-lokaler Zustand.
Verwenden Sie state.patch / state.put nicht, um eine Anforderung als abgeschlossen zu markieren. Diese Aktionen sind allgemeine Statusänderungen im Backend und keine APIs zur Fortschrittsverfolgung von Anforderungen. Der Anforderungsfortschritt darf erst protokolliert werden, nachdem der Server das entsprechende Ereignis validiert hat.
Zum Beispiel:
- Eine Puzzle-Erweiterung sollte den Fortschritt erst dann aufzeichnen, nachdem der Server den signierten Puzzle-Lauf und den Abschlussstatus validiert hat.
- Eine Verifizierungserweiterung sollte den Fortschritt erst dann aufzeichnen, nachdem der Server den eingereichten Nachweis akzeptiert hat.
- Eine Spielerweiterung sollte den Spielfortschritt erst dann aufzeichnen, nachdem das Backend das Spielergebnis oder ein vertrauenswürdiges Abschlussereignis validiert hat.
Laufzeitverhalten
Im konfigurierten Zustand:
- Das Sperr-Dashboard kann die Anforderungen im heutigen Fortschritt anzeigen.
- Chastify verfolgt den Fortschritt pro Erweiterungssitzung und pro Kadenzfenster.
- Der geplante Anforderungsjob wertet abgeschlossene Fenster aus und wendet die konfigurierte Strafe einmal pro versäumtem Fenster an.
- Strafen sind pro Fenster idempotent, sodass Wiederholungsversuche keine doppelten Strafen verursachen.
Empfohlener Anforderungsablauf
- Konfigurieren Sie
extensionRequirementsin der Benutzeroberfläche für die Einrichtung/Konfiguration der Erweiterung. - Beim Start zur Laufzeit wird
session.getaufgerufen und die aktive Konfiguration gelesen. - Schließen Sie die Erweiterungsaktivität in der Benutzeroberfläche ab.
- Sende die Abschlussmeldung an eine vertrauenswürdige Backend-Route für diese Erweiterung.
- Das Backend soll das Ereignis validieren und den Fortschritt der Anforderung protokollieren.
- Nach Abschluss die lokale Benutzeroberfläche mit
session.get/state.getaktualisieren.
Wichtig:
- Behandeln Sie
state.*ausschließlich als Speicherbereich der Erweiterung. Verwenden Sie dedizierte, vertrauenswürdige APIs für Fortschritt, Versuche, Belohnungen und Strafen. - Vertrauen Sie nicht ausschließlich clientseitigen Abschlussflags für Anforderungen.
- Die Namen
metricsollten unverändert bleiben; bei einer Änderung der Metrik wird in einem anderen Bucket gezählt. - Chastify verwendet die vom Benutzer konfigurierte Zeitzone für die Kadenzfenster. Falls keine Zeitzone für den Benutzer verfügbar ist, greift der Server auf
UTCzurück. - Strafen sollten begrenzt und in den Prüfprotokollen nachvollziehbar sein.
Empfohlene Vorgehensweise
- Beim Start
session.getaufrufen. - Status mit
state.getlesen. - Führen Sie bei Bedarf Statusänderungen von Ihrem Backend mit
PUT/PATCH /statedurch. - Sperr-/Geräteaktionen werden nur ausgeführt, wenn sie unterstützt werden und in der Benutzeroberfläche sichtbar sind.
- Bei anforderungsbasierten Aktivitäten wird der Abschluss an eine vertrauenswürdige Backend-Route gemeldet.
- Lokale Ansicht nach wichtigen Schreibvorgängen/Aktionen aktualisieren.