e-cosplay/chastify-sdk 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

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

Quality Gate Status Coverage Reliability Rating Security Rating Maintainability Rating Reliability Issues Security Issues Maintainability Issues Security Hotspots Technical Debt Duplicated Lines (%) Lines of Code

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()):

ClassWhen
AuthenticationException401 missing_token/invalid_token/revoked_token
AuthorizationException403 insufficient_scope/not_authorized
NotFoundException404 lock_not_found/no_device
ConflictException409 no_active_lock_session/lock_ended
ValidationException400 / 422
RateLimitException429 (getRetryAfterMs())
DeviceTimeoutException504 device_timeout
ServerException5xx
NetworkException / TimeoutExceptiontransport-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

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: LicenseRef-ECSAL-1.0
  • 更新时间: 2026-06-25

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固