e-cosplay/chastify-sdk
Composer 安装命令:
composer require e-cosplay/chastify-sdk
包简介
PHP client for the Chastify Developer API. Developed free of charge by e-cosplay.fr for Chastify.net.
README 文档
README
A PHP client for the Chastify Developer API, for PHP ≥ 8.1. PSR-4 autoloaded, with pluggable cache (PSR-16: array / Redis), PSR-3 logging, typed exceptions, automatic retry/backoff, and webhook verification.
Developed free of charge by the e-cosplay.fr association for the Chastify.net community. Chastify.net retains all rights over its API and data. Source-available — see LICENSE. AI/automated ingestion or training is forbidden without written agreement — see AGENTS.md.
Install
cd php && composer install
In your project (once published): composer require e-cosplay/chastify-sdk.
Requires PHP ≥ 8.1 with ext-curl, ext-json, ext-mbstring, ext-openssl.
Optional ext-redis (phpredis) for the Redis cache.
Quick start
use Ecosplay\Chastify\ChastifyClient;
$chastify = new ChastifyClient(['devToken' => getenv('CHASTIFY_DEV_TOKEN')]);
$session = $chastify->session()->get();
echo $session['lockData']['remainingSeconds'] ?? 0;
$chastify->lock()->addTime(600); // +10 min
$chastify->lock()->freeze(1800); // freeze 30 min
$chastify->device()->shock(['intensityPct' => 40, 'durationSeconds' => 5]);
Configuration
new ChastifyClient([
'devToken' => null, // External API (/api/apps/v1/*) — Authorization: Bearer
'appKey' => null, // Extensions API — app-scoped developer key
'appId' => null, // app-scoped file staging
'baseUrl' => 'https://chastify.net',
'timeoutMs' => 30000,
'maxRetries' => 4, // 429 + 5xx + network
'retryBaseDelayMs' => 500, // exponential, jittered, honors Retry-After
'cache' => null, // Psr\SimpleCache\CacheInterface (default: ArrayCache)
'cacheTtlMs' => 0, // 0 → caching OFF (lock state is real-time)
'logger' => null, // Psr\Log\LoggerInterface (default: NullLogger)
'redactSecrets' => true,
'transport' => null, // Ecosplay\Chastify\Http\Transport (default: CurlTransport) — injectable
]);
Caching (array or Redis, PSR-16)
Caching applies to GET reads only and is off by default (cacheTtlMs => 0).
Any PSR-16 CacheInterface works
(Symfony Cache, Laravel, etc.). Bundled implementations:
use Ecosplay\Chastify\Cache\ArrayCache;
use Ecosplay\Chastify\Cache\RedisCache;
// In-memory (local), 3s TTL:
$c = new ChastifyClient(['devToken' => $t, 'cache' => new ArrayCache(), 'cacheTtlMs' => 3000]);
// Redis via phpredis:
$redis = new \Redis(); $redis->connect('127.0.0.1', 6379);
$c = new ChastifyClient(['devToken' => $t, 'cache' => new RedisCache($redis), 'cacheTtlMs' => 5000]);
Per-call override:
$chastify->session()->get(['cacheTtlMs' => 10000]); // cache 10s
$chastify->session()->get(['cache' => false]); // bypass + refresh
Logging (PSR-3, debug requests/responses)
use Ecosplay\Chastify\Logging\StderrLogger;
$chastify = new ChastifyClient([
'devToken' => $t,
'logger' => new StderrLogger('debug'), // or Monolog, etc. (any Psr\Log\LoggerInterface)
]);
At debug, every request/response is logged (method, URL, redacted
headers/body, status, duration, retries). Drop in Monolog for production.
API coverage
External API — your own active lock (devToken)
$chastify->session()->get(); // GET /api/apps/v1/session
$chastify->lock()->applyTime(600); // POST /api/apps/v1/lock/apply-time
$chastify->lock()->addTime(600);
$chastify->lock()->removeTime(300);
$chastify->lock()->freeze(1800); // POST /api/apps/v1/lock/freeze
$chastify->lock()->unfreeze(); // POST /api/apps/v1/lock/unfreeze
$chastify->lock()->toggleFreeze();
$chastify->lock()->pillory(['durationSeconds' => 600, 'reason' => '...']);
$chastify->lock()->endPillory();
$chastify->tasks()->assign(['taskText' => '...', 'points' => 10, 'durationSeconds' => 1800]);
$chastify->tasks()->startTimer();
$chastify->tasks()->complete(['successful' => true, 'reason' => '...']);
$chastify->hygiene()->startUnlock(900);
$chastify->settings()->patch([...]);
$chastify->device()->shock(['intensityPct' => 50, 'durationSeconds' => 30, 'message' => '...']);
$chastify->device()->stopShock();
$chastify->device()->vibrate(['intensityPct' => 50, 'durationSeconds' => 30, 'frequencyPct' => 50]);
$chastify->device()->stopVibration();
$chastify->device()->allStop();
$chastify->device()->setRandomShock(['enabled' => true, 'minIntensityPct' => 10, 'maxIntensityPct' => 60]);
$chastify->device()->setBerserkShock(['enabled' => true]);
$chastify->logs()->custom(['title' => '...', 'role' => 'extension', 'color' => '#ff0000']);
$chastify->actions()->run('add_time', 300); // generic action
$chastify->device()->command('shock.start', [...]);// generic device command
Extensions session API (appKey + per-session mainToken)
$s = $chastify->extensions()->session($sessionId, $mainToken);
$s->get(); $s->getState(); $s->setState($data); $s->patchState($partial);
$s->getMetadata(); $s->patchMetadata(['unlockBlockers' => [...], 'homeActions' => [...]]);
$s->action('add_time', 300); $s->deviceCommand('shock.start', [...]);
$s->regularActions(); $s->submitRegularAction(['kind' => 'checkin', 'score' => 92]);
$s->recordProgress(['metric' => 'memory_win', 'amount' => 1]);
$s->startAttempt(['metric' => 'memory_win', 'attemptId' => '...']);
$s->failExpiredAttempt(['metric' => 'memory_win', 'attemptId' => '...']);
$s->notify(['title' => '...', 'message' => '...', 'target' => 'wearer']);
$s->customLog(['title' => '...', 'role' => 'extension']);
$s->files()->capabilities(); $s->files()->list(); $s->files()->get($fileId);
$s->files()->upload('/path/to/img.webp', 'puzzle-image'); // upload(file, ?purpose)
$s->files()->delete($fileId);
App file staging (appKey + appId)
$f = $chastify->appFiles($appId);
$f->capabilities(); $f->stage('/path/img.webp', 'cover', 'draft_123'); // stage(file, ?purpose, ?draftId)
$f->listStaged(['purpose' => 'cover']); $f->get($fileId); $f->deleteStaged($fileId);
Errors / Exceptions
Every non-2xx throws a typed exception extending ChastifyException
(getStatus(), getErrorCode(), getResponseBody(), getRequestId()):
| Class | When |
|---|---|
AuthenticationException | 401 missing_token/invalid_token/revoked_token |
AuthorizationException | 403 insufficient_scope/not_authorized |
NotFoundException | 404 lock_not_found/no_device |
ConflictException | 409 no_active_lock_session/lock_ended |
ValidationException | 400 / 422 |
RateLimitException | 429 (getRetryAfterMs()) |
DeviceTimeoutException | 504 device_timeout |
ServerException | 5xx |
NetworkException / TimeoutException | transport-level |
use Ecosplay\Chastify\Exception\{AuthenticationException, RateLimitException};
try { $chastify->lock()->addTime(600); }
catch (AuthenticationException $e) { /* refresh token */ }
catch (RateLimitException $e) { sleep((int)ceil($e->getRetryAfterMs() / 1000)); }
Rate limits (per-minute): read 300, write 120, upload 10, action 30, token 30.
429/5xx are retried automatically with exponential backoff.
Webhooks
use Ecosplay\Chastify\Webhooks;
$raw = file_get_contents('php://input');
$headers = getallheaders();
try {
$event = Webhooks::constructEvent($raw, $headers, getenv('CHASTIFY_WEBHOOK_TOKEN'));
} catch (\Ecosplay\Chastify\Exception\WebhookVerificationException $e) {
http_response_code(400); exit;
}
// delivery is at-least-once → dedupe on $event['id']
match ($event['event']) {
'lock.time_changed' => /* ... */ null,
'task.completed' => /* ... */ null,
default => null,
};
http_response_code(200);
Testing
Tests run entirely against a bundled mock server (tests/mock-server.php,
served by PHP's built-in web server) simulating success and error responses —
the live Chastify API is never contacted.
composer install
composer test # boots the mock server and runs PHPUnit
composer test:coverage # 100% line coverage of src/ (requires ext-pcov or xdebug)
Runnable usage examples live in examples/ (quickstart, device,
cache+Redis, logging, extensions, webhooks).
Maintenance & future API changes
If the Chastify API changes, this SDK is intended to be updated on a best-effort basis, subject to the e-cosplay.fr association's volunteer time. No delivery guarantee is implied. Reports → contact@e-cosplay.fr.
License
Source-available under the e-cosplay.fr Source-Available License. © 2026 e-cosplay.fr. Chastify and the Chastify API are property of Chastify.net. No AI/automated ingestion or training without written agreement — see AGENTS.md.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: LicenseRef-ECSAL-1.0
- 更新时间: 2026-06-25