Примеры API
Используйте эту страницу, чтобы скопировать правильную форму запроса для каждого потока расширения.
- В примерах использования моста Iframe применяются действия
postMessage, такие какsession.get,state.getиfiles.get. - В примерах с привилегиями используется собственный бэкэнд расширения с кодами
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEYиx-chastify-main-token. - Никогда не отправляйте ключ API разработчика в iframe или код браузера.
Мост iframe предназначен для инициализации пользовательского интерфейса и операций с сессиями с низким уровнем риска. Используйте его для чтения контекста, хранения состояния, принадлежащего расширению, и разрешения URL-адресов файлов.
Код iframe в браузере контролируется пользователем. Не используйте запросы iframe bridge для применения/удаления времени, завершения задач, снятия блокировок разблокировки, загрузки/удаления файлов среды выполнения, отправки уведомлений, записи журналов или управления устройствами.
Формат запроса и ответа
Действия безопасного моста iframe отправляются внутри этого запроса моста:
{
"type": "chastify:ext:req", // required
"v": 1, // protocol version
"id": "req-123", // your unique request id
"nonce": "from-iframe-hash",
"action": "session.get",
"payload": {}
}
И вы получаете:
{
"type": "chastify:ext:resp",
"v": 1,
"id": "req-123", // same id you sent
"ok": true,
"data": {}
}
Если ok равен false, проверьте error.code и error.message.
Примеры, предназначенные только для бэкэнда, используют обычные HTTPS-запросы с вашего сервера. Они не передаются через iframe-мост.
Сессия и контекст
session.get
Используйте это в первую очередь практически в каждом процессе расширения.
Что это делает:
- Проверяет работоспособность вашего мостового соединения.
- Возвращает контекст сессии (состояние блокировки, роль, конфигурация расширения, возможности).
- Возвращает информацию о возможностях устройства, которую ваш пользовательский интерфейс может использовать, прежде чем запросить у бэкэнда вызов
device.command.
Для чего он используется:
- Создание собственного пользовательского интерфейса с нуля.
- Включение/отключение функций в зависимости от роли и прав доступа.
- Проверка поддерживаемых команд устройства перед отображением элементов управления устройства.
Пример полезной нагрузки действия:
{
"action": "session.get",
"payload": {}
}
Пример фрагмента ответа session.get (данные блокировки во время выполнения):
{
"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
}
}
}
Уведомление о конфиденциальности:
- Коды
wearerLastSeenTimestamp/keyholderLastSeenTimestampвозвращаются только в том случае, если у пользователя есть кодshowOnlineStatus !== false. - Если отображение статуса в сети отключено, эти поля будут иметь значение
null.
Потоки расширений бэкэнда
Эти примеры демонстрируют безопасную схему производства:
- iframe считывает
mainTokenиsessionIdиз хэш-нагрузки. - iframe отправляет их на ваш бэкэнд.
- Ваш бэкэнд проверяет состояние вашей игры/задачи/бизнеса.
- Ваш бэкэнд вызывает Chastify, используя ключ API разработчика, ограниченный областью действия приложения, плюс
x-chastify-main-token.
Не используйте sessionId отдельно для аутентификации. Рассматривайте mainToken как видимый в браузере контекст запуска, а ваш ключ API разработчика — как секретный ключ, доступный только на стороне бэкэнда.
Перед вызовом Chastify ваш бэкэнд должен проверить собственное состояние игры/задачи. Передача mainToken и sessionId из iframe идентифицирует только открытую сессию расширения; это не доказывает, что пользователь выполнил проверку.
Безопасное получение контекста сессии
Ваш iframe может использовать безопасный мост session.get для инициализации пользовательского интерфейса. Если вашему бэкэнду требуется тот же контекст перед применением привилегированного действия, получите его из бэкэнда, используя оба учетных данных.
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
})
});
Ваш бэкэнд:
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
});
});
Применение времени из бэкэнда расширения
Браузер может запросить у вашего бэкэнда разрешение на применение награды или наказания, но браузер не должен решать, является ли действие допустимым. Сначала проверьте действие на стороне сервера.
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());
});
Используйте add_time для наказаний и remove_time для поощрений.
Прохождение игры подтверждено сервером
В таких играх, как «Саймон говорит», не следует доверять результатам, полученным от клиента. Создайте последовательность действий на стороне сервера, сохраните ожидаемую последовательность или ее хеш и проверьте введенные данные перед вызовом Chastify.
В игре на запоминание, видимой в браузере, невозможно доказать честность игры человека, поскольку браузер должен получить последовательность для отображения игры. Серверная проверка по-прежнему предотвращает поддельные изменения Chastify и позволяет вашему бэкэнду проверять идентификаторы запуска, срок действия, сложность, частоту, подсчет очков и защиту от повторного воспроизведения, прежде чем применять награды или наказания.
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 });
});
Точная схема хранения результатов выполнения зависит от вас. verifySessionLaunch должен вызвать Chastify, используя ваш ключ API разработчика, привязанный к приложению, и x-chastify-main-token, прежде чем доверять sessionId. Важное правило: изменения Chastify происходят только после того, как ваш бэкэнд проверит запуск и результат.
Запланированные требования
Ваша серверная часть отвечает за расписание, проверку ритма и подтверждение выполнения заданий. Используйте Chastify только для записи достоверного прогресса или обновления блокировщиков после того, как ваша серверная часть решит, что требование выполнено.
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
})
});
}
Текущие API для сеансов установленных расширений требуют наличия действительного токена запуска iframe в формате x-chastify-main-token; срок действия токенов запуска в настоящее время истекает через 10 часов. Для запланированных заданий, которые выполняются после истечения срока действия этого токена, сохраните собственное ожидающее подтверждение и отправьте отчет о ходе выполнения при следующем действительном запуске расширения, или используйте собственный/встроенный серверный поток, предназначенный для работы в фоновом режиме без участия пользователя.
Не следует считать запланированные задания доверенными только потому, что они выполняются на вашем сервере. Задание по-прежнему должно иметь подтверждение на стороне сервера, проверку частоты выполнения, защиту от повторного воспроизведения и действительный путь авторизации Chastify, прежде чем будет зафиксирован ход выполнения требований.
Уведомления
notifications.custom
Используйте это из административной панели вашего расширения, чтобы отправлять пользовательское уведомление пользователю, владельцу ключа или обоим.
Конечная точка:
POST /api/extensions/sessions/:sessionId/notifications/custom
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Пример текста:
{
"title": "Extension Reminder",
"message": "Your next challenge is ready.",
"showPageOverlay": false,
"target": "both"
}
Примечания:
showPageOverlayпо умолчанию равенfalse.targetпо умолчанию равенwearer.- API создает уведомление типа
extension_app_message.
Штат расширения
Состояние расширения — это принадлежащие расширению JSON-данные для текущей сессии блокировки.
Запись состояния осуществляется только на бэкэнде и требует ключа API разработчика, привязанного к приложению, а также сессии mainToken. Iframe могут считывать состояние с помощью state.get, но не могут записывать его напрямую.
state.put
Что это делает:
- Заменяет весь объект состояния новым объектом
data. - Для работы требуются учетные данные бэкэнда.
Когда использовать:
- Первое сохранение.
- Полная перезапись, если у вас уже есть полное новое состояние.
Пример:
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"
}
}'
Полевые заметки:
payload.data: любое допустимое значение/объект JSON, необходимое вашему расширению.- Avoid Mongo-unsafe key names (
$prefix or keys containing.). - Состояние ограничено рамками сессии и размером, заданным кодом
stateMaxBytesприложения расширения, по умолчанию — 64 КиБ. - В состоянии следует хранить идентификаторы файлов, а не бинарные файлы или подписанные URL-адреса. Используйте
files.getдля обновления подписанных URL-адресов перед рендерингом медиафайлов.
state.patch
Что это делает:
- Применяет патч слияния JSON к существующему состоянию.
- Отправлять нужно только измененные ключи.
- Для работы требуются учетные данные бэкэнда.
Когда использовать:
- Постепенные обновления, основанные на взаимодействии с пользователем.
- Обновление одного поля без повторной отправки всего содержимого.
Пример:
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
Что это делает:
- Считывает текущее состояние расширения.
- Доступно через мост iframe.
Когда использовать:
- При загрузке iframe.
- После записи, если вы хотите повторно синхронизировать локальный пользовательский интерфейс.
Пример:
{
"action": "state.get",
"payload": {}
}
Хранилище файлов
Используйте files.* для бинарных медиафайлов, таких как изображения головоломок, сгенерированные предварительные просмотры или фотографии для заданий. Сохраните возвращаемый file.id в состоянии, записанном бэкэндом, или в вашей собственной базе данных. Выполните рендеринг с помощью file.signedUrl и обновите подписанный URL-адрес с помощью files.get при последующей загрузке iframe.
В окнах настройки, запускаемых до закрытия блокировки/сессии, вместо files.upload используются файлы, загруженные поэтапно. Эти файлы являются временными до тех пор, пока конфигурация расширения не будет сохранена со ссылками provider: "chastify_storage" и fileId. Chastify автоматически запрашивает эти файлы при сохранении блокировки/шаблона; отдельного вызова запроса на стороне браузера не происходит.
Пример разделения данных по состоянию/файлу из вашей административной панели:
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"
}
}'
Позже выполните разрешение изображения перед отображением:
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
Проверьте это перед отображением элементов управления загрузкой.
{
"action": "files.capabilities",
"payload": {}
}
Runtime files.upload
Загрузка файлов во время выполнения изменяет данные сессии расширения, поэтому это не команда моста iframe. Загружайте файлы во время выполнения из вашей серверной части, используя ключ API разработчика, ограниченный областью действия приложения, и код x-chastify-main-token.
files.get
Обновите один подписанный URL-адрес R2, используя стабильный идентификатор файла.
{
"action": "files.get",
"payload": {
"fileId": "file_record_id"
}
}
files.list
{
"action": "files.list",
"payload": {}
}
Runtime files.delete также работает только на бэкэнде по той же причине, что и загрузка.
Блокировка действий
Добавить время
Конечная точка: POST /api/extensions/sessions/:sessionId/action
Что это делает:
- Добавляет или удаляет время из обратного отсчета блокировки в зависимости от значения
deltaSeconds.
Когда использовать:
- Кнопки «Награда/Штраф».
- Исходы игры (победа добавляет время, поражение вычитает время).
Пример:
{
"name": "add_time",
"params": 300 // +300 sec = +5 minutes
}
Полевые заметки:
- Положительная ценность добавляет времени.
- Отрицательное значение отменяет время (если это разрешено правилами сервера).
Заморозить
Конечная точка: POST /api/extensions/sessions/:sessionId/action
Что это делает:
- Замораживает прогресс на определённый период.
Когда использовать:
- Механика перезарядки.
- Контрольные точки вознаграждения.
Пример:
{
"name": "freeze",
"params": { "durationSeconds": 120 }
}
Вы также можете вызвать его без durationSeconds:
{
"name": "freeze",
"params": {}
}
Полевые заметки:
durationSeconds— необязательный параметр.- Если параметр опущен, текущее значение по умолчанию —
3600секунд (1 час). - Допустимый диапазон секунд: от
60до86400.
Разморозить
Конечная точка: POST /api/extensions/sessions/:sessionId/action
Что это делает:
- Завершает активную зависание и возобновляет нормальную работу таймера.
Когда использовать:
- Ручное управление в рабочих процессах расширений.
- Элементы управления «Отмена зависания».
Пример:
{
"name": "unfreeze",
"params": {}
}
Позорный столб
Конечная точка: POST /api/extensions/sessions/:sessionId/action
Что это делает:
- Запускает период блокировки для активной сессии блокировки.
Когда использовать:
- Механизм наложения штрафов после неудачного выполнения заданий/испытаний.
- Потоки эскалации, временно ограничивающие взаимодействие при блокировке.
Пример:
{
"name": "pillory",
"params": {
"durationSeconds": 600,
"reason": "Missed scheduled check-in"
}
}
Полевые заметки:
nameдолжно бытьpillory.- Требуется код
params.durationSeconds. params.reason— необязательный параметр.- Для этого необходимо включить функцию "позорный столб" в конфигурации сессии.
Конечный позорный столб
Конечная точка: POST /api/extensions/sessions/:sessionId/action
Что это делает:
- Немедленно завершает текущее текущее заседание с применением позорного столба.
Пример:
{
"name": "pillory.end",
"params": {}
}
Полевые заметки:
nameдолжно бытьpillory.end.- Ошибка с кодом
pillory_not_activeвозникает, если замок в данный момент не находится в позорном столбе.
Назначить задание
Конечная точка: POST /api/extensions/sessions/:sessionId/action
Что это делает:
- Создает активную задачу, выполняемую для пользователя на основе логики расширения.
- Может переопределить уже запущенную задачу.
Пример:
{
"name": "task.assign",
"params": {
"taskText": "Clean your room",
"points": 10,
"verificationRequired": true,
"durationSeconds": 1800
}
}
Полевые заметки:
- Требуется код
taskText. pointsявляется необязательным параметром и ограничивается на стороне сервера.verificationRequiredпо умолчанию равенfalse.durationSecondsявляется необязательным параметром (0означает, что таймер не требуется).- Для работы требуется включить модуль «Задачи» на замке.
Запуск таймера задачи
Конечная точка: POST /api/extensions/sessions/:sessionId/action
Что это делает:
- Запускает или перезапускает окно обратного отсчета для текущей активной задачи с таймером.
Пример:
{
"name": "task.start_timer",
"params": {}
}
Полевые заметки:
- Требуется запущенная активная задача.
- Сбой произойдет, если для текущей задачи не указана продолжительность.
Выполните задание
Конечная точка: POST /api/extensions/sessions/:sessionId/action
Что это делает:
- Помечает выполнение активной задачи как завершенное или неудачное.
Пример (успеха):
{
"name": "task.complete",
"params": {
"successful": true
}
}
Пример (неудача):
{
"name": "task.complete",
"params": {
"successful": false,
"reason": "Did not finish in time"
}
}
Запустить временное открытие
Конечная точка: POST /api/extensions/sessions/:sessionId/action
Что это делает:
- Запускает временное окно для обеспечения гигиены на основе логики расширения.
Пример:
{
"name": "hygienic_unlock.start",
"params": {
"durationSeconds": 900
}
}
Полевые заметки:
- Для открытия замка необходимо включить функцию гигиенического открывания.
- Сбой произойдёт, если гигиеническое отверстие уже открыто.
- Параметр
durationSecondsявляется необязательным; если он опущен, используется значение по умолчанию для блокировки.
Метаданные и действия на главной странице
metadata.patch
Что это делает:
- Сохраняет метаданные расширений, используемые пользовательским интерфейсом страницы блокировки.
- Поддерживает коды
unlockBlockersиhomeActions. - Поддерживает
homeActions[].intentдля работы с глубокими ссылками при открытии расширения.
Когда использовать:
- Обеспечьте соблюдение условий разблокировки сеанса блокировки, принадлежащих вашему расширению.
- Добавьте на страницу блокировки быстрые действия, которые откроют ваше расширение с помощью Intent.
- Направляйте пользователей непосредственно на определенный экран/в нужный момент при нажатии на кнопку "Главная".
Конечная точка:
PATCH /api/extensions/sessions/:sessionId/metadata
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Пример текста:
{
"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"
}
}
}
]
}
Полевые заметки:
unlockBlockers: список активных блокировщиков разблокировки сеанса блокировки из вашего расширения.- Вы можете включить несколько блокирующих факторов одновременно (до 25), по одному на каждое невыполненное условие.
- Функция разблокировки остается заблокированной, пока существуют какие-либо блокировщики среди включенных расширений.
- Chastify объединяет блокировщики со всех расширений для сеанса блокировки.
- Ваше расширение должно добавлять/удалять блокирующие элементы только в своих собственных метаданных.
- Не удаляйте блокирующие элементы из других расширений; очищайте свой массив только тогда, когда выполняются ваши собственные условия.
homeActions: кнопки быстрого действия, отображаемые в интерфейсе блокировки.homeActions[].slug: стабильный идентификатор для вашего действия.homeActions[].title: метка, видимая пользователю.homeActions[].description: необязательный вспомогательный текст.homeActions[].intent: необязательная инструкция для создания глубокой ссылки, передаваемая вашему расширению при открытии.- В пользовательском интерфейсе карточки расширения эти действия отображаются в виде меню/списка по заголовку действия (внутренне идентифицируемого по идентификатору).
- Когда пользователь нажимает на одну из этих кнопок, Chastify открывает расширение и передает:
homeActionSlughomeAction(выбранный объект действия)intent(нормализованный объект намерения) Таким образом, ваше расширение сможет немедленно перенаправить вас на нужное представление/действие при загрузке.
Назначение: пример приложения для разработчиков
Используйте этот шаблон в своем расширении, чтобы реагировать на события клика по меню при загрузке.
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");
Что делает этот пример:
- Считывает
homeActionSlug+intentиз хэша полезной нагрузки iframe. - Обрабатывает каждый щелчок только один раз для каждого блока/действия.
- Маршруты к панели, когда намерение имеет значение
open_panel. - В случае пользовательских типов намерений происходит автоматическое отображение сообщения, соответствующего заданному намерению.
Команда устройства
device.command
Что это делает:
- Отправляет команду управления устройством (если поддерживается для данной сессии/устройства).
- Позволяет вашему расширению запускать стандартные действия вибрации/электрошока через Chastify.
Когда использовать:
- Запуск поддерживаемых команд вибраций/шока из логики расширения.
- Создание интерактивных дополнительных функций (игры, наказания, награды, распорядок дня).
Конечная точка:
POST /api/extensions/sessions/:sessionId/device-command
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH
Пример текста:
{
"command": "shock.start",
"params": {
"durationSeconds": 30,
"intensityPct": 50
}
}
Типичные команды:
shock.startс параметрами:{ durationSeconds, intensityPct, message? }shock.stopvibration.startс параметрами:{ durationSeconds, intensityPct, frequencyPct?, message? }vibration.stopall.stopshock.random.setс параметрами:{ enabled, minIntensityPct?, maxIntensityPct?, message? }(только для Lockink AA-A1012)shock.berserk.setс параметрами:{ enabled, message? }(только для Lockink AA-A1012)
Рекомендуемый порядок действий:
- Вызовите
session.getпри загрузке. - Считайте характеристики устройства по коду
deviceControl.supportedCommands. - Отображение элементов управления доступно только для поддерживаемых команд.
- Отправьте
device.commandс проверенными значениями. - Обновить или доверить флаги ответа
activeдля отображения текущего состояния пользовательского интерфейса.
Рекомендации по параметрам:
durationSeconds: сервер ограничивает время работы до безопасных пределов (текущий максимальный срок действия политики — 300 секунд).intensityPct: ожидаемое процентное значение (ввод в стиле1-100, ограничение на стороне сервера).frequencyPct: ожидаемое процентное значение (1-100) для потоков вибрации (закрепленных на стороне сервера).- Для режима случайного выбора убедитесь, что
minIntensityPct <= maxIntensityPct.
Пример более безопасного шаблона пользовательского интерфейса:
// 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 }
});
}
Важный:
- Сначала вызовите
session.getи ознакомьтесь с поддерживаемыми командами. - Отображать только элементы управления для команд, поддерживаемых в текущей сессии.
- Перед отправкой параметров команды необходимо проверить ввод пользователя.
- Для доступа к сессии расширения требуется разрешение на запись (
locks:write) дляdevice.command. - Обеспечьте корректную обработку ошибок моста/сервера (
insufficient_scope, неподдерживаемая команда, ошибки проверки).
Требования к продлению
Требования к расширению позволяют расширению определять правила периодического выполнения, которые отображаются в пользовательском интерфейсе «Прогресс на сегодня» замка и могут применять штрафные санкции, если пользователь пропустит окно выполнения.
Используйте это для действий расширений, которым доверяет сервер, например:
- Решайте по одной головоломке в день.
- Проводите 3 проверки каждые 2 дня.
- Выполняйте 5 дополнительных действий в неделю.
Форма конфигурации
Требование хранится в конфигурации сессии расширения по адресу extensionRequirements:
{
"extensionRequirements": {
"enabled": true,
"metric": "completion",
"requiredCount": 1,
"cadence": {
"every": 1,
"unit": "day"
},
"punishment": {
"type": "add_time",
"seconds": 900,
"reason": "Missed extension requirement"
}
}
}
Поля:
enabled: включает/выключает повторяющееся требование.metric: стабильное имя счетчика. Используйте простые имена, такие какcompletion,winилиverification.requiredCount: сколько доверенных событий должно произойти в течение этого окна.cadence.every: размер интервала.cadence.unit:dayилиweek.- Часовой пояс определяется модулем Chastify на основе настроенного пользователем модуля
User.timezone. В конфигурации расширения не следует жестко задавать часовой пояс. punishment.type:none,add_time,freezeилиpillory.punishment.seconds: продолжительность наказания дляadd_time,freezeилиpillory.punishment.reason: необязательная причина аудита/отладки.
Модель прогресса
Информация о ходе выполнения требований передается на серверную сторону, а не в локальные настройки iframe.
Не используйте state.patch / state.put для пометки требования как выполненного. Эти действия представляют собой общие операции записи состояния, доступные только на бэкэнде, а не API для отслеживания прогресса выполнения требования. Отслеживание прогресса выполнения требования должно регистрироваться только после того, как сервер подтвердит событие, которое должно учитываться.
Например:
- Расширение для решения головоломок должно записывать ход выполнения только после того, как сервер подтвердит подписанный запуск головоломки и ее завершение.
- Функция проверки должна фиксировать ход выполнения только после того, как сервер примет предоставленное подтверждение.
- Игровое расширение должно записывать прогресс только после того, как серверная часть подтвердит результат игры или событие завершения, заслуживающее доверия.
Поведение во время выполнения
При настройке:
- Панель мониторинга блокировки может отображать требования в разделе «Сегодняшний прогресс».
- Chastify отслеживает прогресс по каждой дополнительной сессии и по каждому временному интервалу.
- Запланированное задание по проверке требований оценивает завершенные окна и применяет заданное наказание один раз за каждое пропущенное окно.
- Наказания являются идемпотентными для каждого окна, поэтому повторные попытки не суммируют повторяющиеся наказания.
Рекомендуемый алгоритм выполнения требований
- Настройте
extensionRequirementsв пользовательском интерфейсе настройки/конфигурации расширения. - При запуске программы вызовите
session.getи считайте активную конфигурацию. - Завершите действие расширения в пользовательском интерфейсе.
- Отправьте запрос на завершение работы на доверенный внутренний маршрут для этого расширения.
- Пусть бэкэнд проверит событие и зафиксирует ход выполнения требований.
- После завершения обновите локальный пользовательский интерфейс с помощью
session.get/state.get.
Важный:
- Рассматривайте
state.*исключительно как хранилище, принадлежащее расширению. Используйте выделенные доверенные API для отслеживания прогресса, попыток, наград и наказаний. - Не следует доверять флагам завершения, отображаемым только клиентом, для подтверждения выполнения требований.
- Сохраняйте неизменными названия
metric; изменение метрики приводит к попаданию в другую категорию. - Chastify использует заданный пользователем часовой пояс для окон частоты обновления экрана. Если часовой пояс пользователя недоступен, сервер переключается на
UTC. - Необходимо четко определять и объяснять применяемые наказания, фиксируя это в журналах аудита.
Рекомендуемый порядок действий
- Вызовите
session.getпри запуске. - Считывание состояния с помощью
state.get. - При необходимости выполняйте запись состояния из вашего бэкэнда с помощью
PUT/PATCH /state. - Выполняйте действия по блокировке/отключению устройства только тогда, когда это поддерживается и отображается в пользовательском интерфейсе.
- Для задач, выполняемых в соответствии с требованиями, сообщайте о завершении в надежный внутренний канал.
- Обновляйте локальное представление после важных операций записи/действий.