Laravel AT Protocol Client (alpha & unstable)

Compare changes

Choose any two refs to compare.

Changed files
+3412 -1530
docs
src
Attributes
Auth
Builders
Client
Console
Data
Enums
Facades
Http
RichText
+3 -5
composer.json
··· 45 } 46 }, 47 "scripts": { 48 - "test": "vendor/bin/pest", 49 - "test-coverage": "vendor/bin/pest --coverage", 50 "format": "vendor/bin/php-cs-fixer fix" 51 }, 52 "extra": { ··· 62 "minimum-stability": "dev", 63 "prefer-stable": true, 64 "config": { 65 - "allow-plugins": { 66 - "pestphp/pest-plugin": false 67 - } 68 } 69 }
··· 45 } 46 }, 47 "scripts": { 48 + "test": "vendor/bin/phpunit", 49 + "test-coverage": "vendor/bin/phpunit --coverage-html coverage", 50 "format": "vendor/bin/php-cs-fixer fix" 51 }, 52 "extra": { ··· 62 "minimum-stability": "dev", 63 "prefer-stable": true, 64 "config": { 65 + "sort-packages": true 66 } 67 }
+41 -38
docs/extensions.md
··· 14 | `AtpClient::hasDomainExtension($domain, $name)` | Check if a request client extension is registered | 15 | `AtpClient::flushExtensions()` | Clear all extensions (useful for testing) | 16 17 - The same methods are available on `AtpPublicClient` for unauthenticated extensions. 18 - 19 ### Extension Types 20 21 | Type | Access Pattern | Use Case | ··· 30 ```bash 31 # Create a domain client extension 32 php artisan make:atp-client AnalyticsClient 33 - 34 - # Create a public domain client extension 35 - php artisan make:atp-client DiscoverClient --public 36 37 # Create a request client extension for an existing domain 38 php artisan make:atp-request MetricsClient --domain=bsky 39 - 40 - # Create a public request client extension 41 - php artisan make:atp-request TrendingClient --domain=bsky --public 42 ``` 43 44 The generated files are placed in configurable directories. You can customize these paths in `config/client.php`: ··· 46 ```php 47 'generators' => [ 48 'client_path' => 'app/Services/Clients', 49 - 'client_public_path' => 'app/Services/Clients/Public', 50 'request_path' => 'app/Services/Clients/Requests', 51 - 'request_public_path' => 'app/Services/Clients/Public/Requests', 52 ], 53 ``` 54 ··· 225 $authorMetrics = $client->bsky->metrics->getAuthorMetrics('someone.bsky.social'); 226 ``` 227 228 - ## Public Client Extensions 229 230 - The `AtpPublicClient` supports the same extension system for unauthenticated API access: 231 232 ```php 233 - use SocialDept\AtpClient\Client\Public\AtpPublicClient; 234 235 - // Domain client extension 236 - AtpPublicClient::extend('discover', fn($atp) => new DiscoverClient($atp)); 237 - 238 - // Request client extension on existing domain 239 - AtpPublicClient::extendDomain('bsky', 'trending', fn($bsky) => new TrendingClient($bsky)); 240 ``` 241 242 - For public request clients, extend `PublicRequest` instead of `Request`: 243 - 244 - ```php 245 - <?php 246 - 247 - namespace App\Atp; 248 - 249 - use SocialDept\AtpClient\Client\Public\Requests\PublicRequest; 250 - 251 - class TrendingPublicClient extends PublicRequest 252 - { 253 - public function getPopularFeeds(int $limit = 10): array 254 - { 255 - return $this->atp->bsky->feed->getPopularFeedGenerators($limit)->feeds; 256 - } 257 - } 258 - ``` 259 260 ## Registering Multiple Extensions 261 ··· 272 AtpClient::extendDomain('bsky', 'metrics', fn($bsky) => new BskyMetricsClient($bsky)); 273 AtpClient::extendDomain('bsky', 'lists', fn($bsky) => new BskyListsClient($bsky)); 274 AtpClient::extendDomain('atproto', 'backup', fn($atproto) => new RepoBackupClient($atproto)); 275 - 276 - // Public client extensions 277 - AtpPublicClient::extend('discover', fn($atp) => new DiscoverClient($atp)); 278 } 279 ``` 280 ··· 451 } 452 } 453 ``` 454 455 ## Available Domains 456
··· 14 | `AtpClient::hasDomainExtension($domain, $name)` | Check if a request client extension is registered | 15 | `AtpClient::flushExtensions()` | Clear all extensions (useful for testing) | 16 17 ### Extension Types 18 19 | Type | Access Pattern | Use Case | ··· 28 ```bash 29 # Create a domain client extension 30 php artisan make:atp-client AnalyticsClient 31 32 # Create a request client extension for an existing domain 33 php artisan make:atp-request MetricsClient --domain=bsky 34 ``` 35 36 The generated files are placed in configurable directories. You can customize these paths in `config/client.php`: ··· 38 ```php 39 'generators' => [ 40 'client_path' => 'app/Services/Clients', 41 'request_path' => 'app/Services/Clients/Requests', 42 ], 43 ``` 44 ··· 215 $authorMetrics = $client->bsky->metrics->getAuthorMetrics('someone.bsky.social'); 216 ``` 217 218 + ## Public vs Authenticated Mode 219 220 + The `AtpClient` class works in both public and authenticated modes. Both `Atp::public()` and `Atp::as()` return the same `AtpClient` class: 221 222 ```php 223 + // Public mode - no authentication 224 + $publicClient = Atp::public('https://public.api.bsky.app'); 225 + $publicClient->bsky->actor->getProfile('someone.bsky.social'); 226 227 + // Authenticated mode - with session 228 + $authClient = Atp::as('did:plc:xxx'); 229 + $authClient->bsky->actor->getProfile('someone.bsky.social'); 230 ``` 231 232 + Extensions registered on `AtpClient` work in both modes. The underlying HTTP layer automatically handles authentication based on whether a session is present. 233 234 ## Registering Multiple Extensions 235 ··· 246 AtpClient::extendDomain('bsky', 'metrics', fn($bsky) => new BskyMetricsClient($bsky)); 247 AtpClient::extendDomain('bsky', 'lists', fn($bsky) => new BskyListsClient($bsky)); 248 AtpClient::extendDomain('atproto', 'backup', fn($atproto) => new RepoBackupClient($atproto)); 249 } 250 ``` 251 ··· 422 } 423 } 424 ``` 425 + 426 + ### Documenting Scope Requirements 427 + 428 + Use the `#[ScopedEndpoint]` and `#[PublicEndpoint]` attributes to document the authentication requirements of your extension methods: 429 + 430 + ```php 431 + use SocialDept\AtpClient\Attributes\PublicEndpoint; 432 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 433 + use SocialDept\AtpClient\Client\Requests\Request; 434 + use SocialDept\AtpClient\Enums\Scope; 435 + 436 + class BskyMetricsClient extends Request 437 + { 438 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getTimeline')] 439 + public function getTimelineMetrics(): array 440 + { 441 + $timeline = $this->atp->bsky->feed->getTimeline(); 442 + // Process and return metrics... 443 + } 444 + 445 + #[PublicEndpoint] 446 + public function getPublicPostMetrics(string $uri): array 447 + { 448 + $thread = $this->atp->bsky->feed->getPostThread($uri); 449 + // Process and return metrics... 450 + } 451 + } 452 + ``` 453 + 454 + > **Note:** These attributes currently serve as documentation only. Runtime scope enforcement will be implemented in a future release. Using them correctly now ensures forward compatibility. 455 + 456 + Methods with `#[ScopedEndpoint]` indicate they require authentication, while methods with `#[PublicEndpoint]` work without authentication. See [scopes.md](scopes.md) for full documentation on scope handling. 457 458 ## Available Domains 459
+408
docs/scopes.md
···
··· 1 + # OAuth Scopes 2 + 3 + The AT Protocol uses OAuth scopes to control what actions an application can perform on behalf of a user. AtpClient provides attributes for documenting scope requirements on endpoints. 4 + 5 + > **Note:** The `#[ScopedEndpoint]` and `#[PublicEndpoint]` attributes currently serve as documentation only. Runtime scope validation and enforcement will be implemented in a future release. Using these attributes correctly now ensures forward compatibility. 6 + 7 + ## Quick Reference 8 + 9 + ### Scope Enum 10 + 11 + ```php 12 + use SocialDept\AtpClient\Enums\Scope; 13 + 14 + // Transition scopes (current AT Protocol scopes) 15 + Scope::Atproto // 'atproto' - Full access 16 + Scope::TransitionGeneric // 'transition:generic' - General API access 17 + Scope::TransitionEmail // 'transition:email' - Email access 18 + Scope::TransitionChat // 'transition:chat.bsky' - Chat access 19 + 20 + // Granular scope builders (future AT Protocol scopes) 21 + Scope::repo('app.bsky.feed.post', ['create', 'delete']) // Record operations 22 + Scope::rpc('app.bsky.feed.getTimeline') // RPC endpoint access 23 + Scope::blob('image/*') // Blob upload access 24 + Scope::account('email') // Account attribute access 25 + Scope::identity('handle') // Identity attribute access 26 + ``` 27 + 28 + ### ScopedEndpoint Attribute 29 + 30 + ```php 31 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 32 + use SocialDept\AtpClient\Enums\Scope; 33 + 34 + #[ScopedEndpoint(Scope::TransitionGeneric)] 35 + public function getTimeline(): GetTimelineResponse 36 + { 37 + // Method implementation 38 + } 39 + ``` 40 + 41 + ## Understanding AT Protocol Scopes 42 + 43 + ### Current Transition Scopes 44 + 45 + The AT Protocol is currently in a transition period where broad "transition scopes" are used: 46 + 47 + | Scope | Description | 48 + |-------|-------------| 49 + | `atproto` | Full access to the AT Protocol | 50 + | `transition:generic` | General API access for most operations | 51 + | `transition:email` | Access to email-related operations | 52 + | `transition:chat.bsky` | Access to Bluesky chat features | 53 + 54 + ### Future Granular Scopes 55 + 56 + The AT Protocol is moving toward granular scopes that provide fine-grained access control: 57 + 58 + ```php 59 + // Record operations 60 + 'repo:app.bsky.feed.post' // All operations on posts 61 + 'repo:app.bsky.feed.post?action=create' // Only create posts 62 + 'repo:app.bsky.feed.like?action=create&action=delete' // Create or delete likes 63 + 'repo:*' // All collections, all actions 64 + 65 + // RPC endpoint access 66 + 'rpc:app.bsky.feed.getTimeline' // Access to timeline endpoint 67 + 'rpc:app.bsky.feed.*' // All feed endpoints 68 + 69 + // Blob operations 70 + 'blob:image/*' // Upload images 71 + 'blob:*/*' // Upload any blob type 72 + 73 + // Account and identity 74 + 'account:email' // Access email 75 + 'identity:handle' // Manage handle 76 + ``` 77 + 78 + ## The ScopedEndpoint Attribute 79 + 80 + The `#[ScopedEndpoint]` attribute documents scope requirements on methods that require authentication. 81 + 82 + ### Basic Usage 83 + 84 + ```php 85 + <?php 86 + 87 + namespace App\Atp; 88 + 89 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 90 + use SocialDept\AtpClient\Client\Requests\Request; 91 + use SocialDept\AtpClient\Enums\Scope; 92 + 93 + class CustomClient extends Request 94 + { 95 + #[ScopedEndpoint(Scope::TransitionGeneric)] 96 + public function getTimeline(): array 97 + { 98 + return $this->atp->client->get('app.bsky.feed.getTimeline')->json(); 99 + } 100 + } 101 + ``` 102 + 103 + ### With Granular Scope 104 + 105 + Document the future granular scope that will replace the transition scope: 106 + 107 + ```php 108 + #[ScopedEndpoint( 109 + Scope::TransitionGeneric, 110 + granular: 'rpc:app.bsky.feed.getTimeline' 111 + )] 112 + public function getTimeline(): GetTimelineResponse 113 + { 114 + // ... 115 + } 116 + ``` 117 + 118 + ### With Description 119 + 120 + Add a human-readable description for documentation: 121 + 122 + ```php 123 + #[ScopedEndpoint( 124 + Scope::TransitionGeneric, 125 + granular: 'rpc:app.bsky.feed.getTimeline', 126 + description: 'Access to the user\'s home timeline' 127 + )] 128 + public function getTimeline(): GetTimelineResponse 129 + { 130 + // ... 131 + } 132 + ``` 133 + 134 + ### Multiple Scopes (AND Logic) 135 + 136 + When a method requires multiple scopes, all must be present: 137 + 138 + ```php 139 + #[ScopedEndpoint([Scope::TransitionGeneric, Scope::TransitionEmail])] 140 + public function getEmailPreferences(): array 141 + { 142 + // Requires BOTH scopes 143 + } 144 + ``` 145 + 146 + ### Multiple Attributes (OR Logic) 147 + 148 + Use multiple attributes for alternative scope requirements: 149 + 150 + ```php 151 + #[ScopedEndpoint(Scope::Atproto)] 152 + #[ScopedEndpoint(Scope::TransitionGeneric)] 153 + public function getProfile(string $actor): ProfileViewDetailed 154 + { 155 + // Either scope satisfies the requirement 156 + } 157 + ``` 158 + 159 + ## Scope Enforcement (Planned) 160 + 161 + > **Coming Soon:** Runtime scope enforcement is not yet implemented. The following documentation describes planned functionality for a future release. 162 + 163 + ### Configuration 164 + 165 + Configure scope enforcement in `config/client.php` or via environment variables: 166 + 167 + ```php 168 + 'scope_enforcement' => ScopeEnforcementLevel::Permissive, 169 + ``` 170 + 171 + | Level | Behavior | 172 + |-------|----------| 173 + | `Strict` | Throws `MissingScopeException` if required scopes are missing | 174 + | `Permissive` | Logs a warning but attempts the request anyway | 175 + 176 + Set via environment variable: 177 + 178 + ```env 179 + ATP_SCOPE_ENFORCEMENT=strict 180 + ``` 181 + 182 + ### Programmatic Scope Checking 183 + 184 + Check scopes programmatically using the `ScopeChecker`: 185 + 186 + ```php 187 + use SocialDept\AtpClient\Auth\ScopeChecker; 188 + use SocialDept\AtpClient\Facades\Atp; 189 + 190 + $checker = app(ScopeChecker::class); 191 + $session = Atp::as($did)->client->session(); 192 + 193 + // Check if session has a scope 194 + if ($checker->hasScope($session, Scope::TransitionGeneric)) { 195 + // Session has the scope 196 + } 197 + 198 + // Check multiple scopes 199 + if ($checker->check($session, [Scope::TransitionGeneric, Scope::TransitionEmail])) { 200 + // Session has ALL required scopes 201 + } 202 + 203 + // Check and fail if missing (respects enforcement level) 204 + $checker->checkOrFail($session, [Scope::TransitionGeneric]); 205 + 206 + // Check repo scope for specific action 207 + if ($checker->checkRepoScope($session, 'app.bsky.feed.post', 'create')) { 208 + // Can create posts 209 + } 210 + ``` 211 + 212 + ### Granular Pattern Matching 213 + 214 + The scope checker supports wildcard patterns: 215 + 216 + ```php 217 + // Check if session can access any feed endpoint 218 + $checker->matchesGranular($session, 'rpc:app.bsky.feed.*'); 219 + 220 + // Check if session can upload images 221 + $checker->matchesGranular($session, 'blob:image/*'); 222 + 223 + // Check if session has any repo access 224 + $checker->matchesGranular($session, 'repo:*'); 225 + ``` 226 + 227 + ## Route Middleware (Planned) 228 + 229 + > **Coming Soon:** Route middleware is not yet implemented. The following documentation describes planned functionality for a future release. 230 + 231 + Protect Laravel routes based on ATP session scopes: 232 + 233 + ```php 234 + use Illuminate\Support\Facades\Route; 235 + 236 + // Single scope 237 + Route::get('/timeline', TimelineController::class) 238 + ->middleware('atp.scope:transition:generic'); 239 + 240 + // Multiple scopes (AND logic) 241 + Route::get('/email-settings', EmailSettingsController::class) 242 + ->middleware('atp.scope:transition:generic,transition:email'); 243 + ``` 244 + 245 + ### Middleware Configuration 246 + 247 + Configure middleware behavior in `config/client.php`: 248 + 249 + ```php 250 + 'scope_authorization' => [ 251 + // What to do when scope check fails 252 + 'failure_action' => ScopeAuthorizationFailure::Abort, // abort, redirect, or exception 253 + 254 + // Where to redirect (when failure_action is 'redirect') 255 + 'redirect_to' => '/login', 256 + ], 257 + ``` 258 + 259 + | Failure Action | Behavior | 260 + |----------------|----------| 261 + | `Abort` | Returns 403 Forbidden response | 262 + | `Redirect` | Redirects to configured URL | 263 + | `Exception` | Throws `ScopeAuthorizationException` | 264 + 265 + Set via environment variables: 266 + 267 + ```env 268 + ATP_SCOPE_FAILURE_ACTION=redirect 269 + ATP_SCOPE_REDIRECT=/auth/login 270 + ``` 271 + 272 + ### User Model Integration 273 + 274 + For the middleware to work, your User model must implement `HasAtpSession`: 275 + 276 + ```php 277 + <?php 278 + 279 + namespace App\Models; 280 + 281 + use Illuminate\Foundation\Auth\User as Authenticatable; 282 + use SocialDept\AtpClient\Contracts\HasAtpSession; 283 + 284 + class User extends Authenticatable implements HasAtpSession 285 + { 286 + public function getAtpDid(): ?string 287 + { 288 + return $this->atp_did; 289 + } 290 + } 291 + ``` 292 + 293 + ## Public Mode and Scopes 294 + 295 + Methods marked with `#[PublicEndpoint]` can be called without authentication using `Atp::public()`: 296 + 297 + ```php 298 + // Public mode - no authentication required 299 + $client = Atp::public('https://public.api.bsky.app'); 300 + $client->bsky->actor->getProfile('someone.bsky.social'); // Works without auth 301 + 302 + // Authenticated mode - for endpoints requiring scopes 303 + $client = Atp::as($did); 304 + $client->bsky->feed->getTimeline(); // Requires transition:generic scope 305 + ``` 306 + 307 + Methods with `#[PublicEndpoint]` work in both modes, while methods with `#[ScopedEndpoint]` require authentication. 308 + 309 + ## Exception Handling (Planned) 310 + 311 + > **Coming Soon:** These exceptions will be thrown when scope enforcement is implemented in a future release. 312 + 313 + ### MissingScopeException 314 + 315 + Will be thrown when required scopes are missing and enforcement is strict: 316 + 317 + ```php 318 + use SocialDept\AtpClient\Exceptions\MissingScopeException; 319 + 320 + try { 321 + $timeline = $client->bsky->feed->getTimeline(); 322 + } catch (MissingScopeException $e) { 323 + $missing = $e->getMissingScopes(); // Scopes that are missing 324 + $granted = $e->getGrantedScopes(); // Scopes the session has 325 + 326 + // Handle missing scope 327 + } 328 + ``` 329 + 330 + ### ScopeAuthorizationException 331 + 332 + Will be thrown by middleware when route access is denied: 333 + 334 + ```php 335 + use SocialDept\AtpClient\Exceptions\ScopeAuthorizationException; 336 + 337 + try { 338 + // Route protected by atp.scope middleware 339 + } catch (ScopeAuthorizationException $e) { 340 + $required = $e->getRequiredScopes(); 341 + $granted = $e->getGrantedScopes(); 342 + $message = $e->getMessage(); 343 + } 344 + ``` 345 + 346 + ## Best Practices 347 + 348 + ### 1. Document All Scope Requirements 349 + 350 + Always add `#[ScopedEndpoint]` to methods that require authentication: 351 + 352 + ```php 353 + #[ScopedEndpoint( 354 + Scope::TransitionGeneric, 355 + granular: 'rpc:app.bsky.feed.getTimeline', 356 + description: 'Fetches the authenticated user\'s home timeline' 357 + )] 358 + public function getTimeline(): GetTimelineResponse 359 + ``` 360 + 361 + ### 2. Use the Scope Enum 362 + 363 + Prefer the `Scope` enum over string literals for type safety: 364 + 365 + ```php 366 + // Good 367 + #[ScopedEndpoint(Scope::TransitionGeneric)] 368 + 369 + // Avoid 370 + #[ScopedEndpoint('transition:generic')] 371 + ``` 372 + 373 + ### 3. Request Minimal Scopes 374 + 375 + When implementing OAuth, request only the scopes your application needs: 376 + 377 + ```php 378 + $authUrl = Atp::oauth()->getAuthorizationUrl([ 379 + 'scope' => 'atproto transition:generic', 380 + ]); 381 + ``` 382 + 383 + ### 4. Handle Missing Scopes Gracefully 384 + 385 + Check for scope availability before attempting operations: 386 + 387 + ```php 388 + $checker = app(ScopeChecker::class); 389 + $session = $client->client->session(); 390 + 391 + if ($checker->hasScope($session, Scope::TransitionChat)) { 392 + $conversations = $client->chat->getConversations(); 393 + } else { 394 + // Inform user they need to re-authorize with chat scope 395 + } 396 + ``` 397 + 398 + ### 5. Use Permissive Mode in Development 399 + 400 + Start with permissive enforcement during development, then switch to strict for production: 401 + 402 + ```env 403 + # .env.local 404 + ATP_SCOPE_ENFORCEMENT=permissive 405 + 406 + # .env.production 407 + ATP_SCOPE_ENFORCEMENT=strict 408 + ```
+14 -4
src/AtpClient.php
··· 13 class AtpClient 14 { 15 use HasExtensions; 16 /** 17 * Raw API communication/networking class 18 */ ··· 39 public OzoneClient $ozone; 40 41 public function __construct( 42 - SessionManager $sessions, 43 - string $did, 44 ) { 45 - // Load the network client 46 - $this->client = new Client($this, $sessions, $did); 47 48 // Load all function collections 49 $this->bsky = new BskyClient($this); 50 $this->atproto = new AtprotoClient($this); 51 $this->chat = new ChatClient($this); 52 $this->ozone = new OzoneClient($this); 53 } 54 }
··· 13 class AtpClient 14 { 15 use HasExtensions; 16 + 17 /** 18 * Raw API communication/networking class 19 */ ··· 40 public OzoneClient $ozone; 41 42 public function __construct( 43 + ?SessionManager $sessions = null, 44 + ?string $did = null, 45 + ?string $serviceUrl = null, 46 ) { 47 + // Load the network client (supports both public and authenticated modes) 48 + $this->client = new Client($this, $sessions, $did, $serviceUrl); 49 50 // Load all function collections 51 $this->bsky = new BskyClient($this); 52 $this->atproto = new AtprotoClient($this); 53 $this->chat = new ChatClient($this); 54 $this->ozone = new OzoneClient($this); 55 + } 56 + 57 + /** 58 + * Check if client is in public mode (no authentication). 59 + */ 60 + public function isPublicMode(): bool 61 + { 62 + return $this->client->isPublicMode(); 63 } 64 }
+5 -4
src/AtpClientServiceProvider.php
··· 25 use SocialDept\AtpClient\Http\DPoPClient; 26 use SocialDept\AtpClient\Session\SessionManager; 27 use SocialDept\AtpClient\Storage\EncryptedFileKeyStore; 28 - use SocialDept\AtpClient\Client\Public\AtpPublicClient; 29 30 class AtpClientServiceProvider extends ServiceProvider 31 { ··· 120 $this->app->instance(CredentialProvider::class, $provider); 121 } 122 123 - public function public(?string $service = null): AtpPublicClient 124 { 125 - return new AtpPublicClient( 126 - $service ?? config('atp-client.public.service_url', 'https://public.api.bsky.app') 127 ); 128 } 129 };
··· 25 use SocialDept\AtpClient\Http\DPoPClient; 26 use SocialDept\AtpClient\Session\SessionManager; 27 use SocialDept\AtpClient\Storage\EncryptedFileKeyStore; 28 29 class AtpClientServiceProvider extends ServiceProvider 30 { ··· 119 $this->app->instance(CredentialProvider::class, $provider); 120 } 121 122 + public function public(?string $service = null): AtpClient 123 { 124 + return new AtpClient( 125 + sessions: null, 126 + did: null, 127 + serviceUrl: $service ?? config('atp-client.public.service_url', 'https://public.api.bsky.app') 128 ); 129 } 130 };
+43
src/Attributes/PublicEndpoint.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Attributes; 4 + 5 + use Attribute; 6 + 7 + /** 8 + * Documents that a method is a public endpoint that does not require authentication. 9 + * 10 + * This attribute currently serves as documentation to indicate which AT Protocol 11 + * endpoints can be called without an authenticated session. It helps developers 12 + * understand which endpoints work with `Atp::public()` against public API endpoints 13 + * like `https://public.api.bsky.app`. 14 + * 15 + * While this attribute does not currently perform runtime enforcement, scope 16 + * validation will be implemented in a future release. Correctly attributing 17 + * endpoints now ensures forward compatibility when enforcement is enabled. 18 + * 19 + * Public endpoints typically include operations like: 20 + * - Reading public profiles and posts 21 + * - Searching actors and content 22 + * - Resolving handles to DIDs 23 + * - Accessing repository data (sync endpoints) 24 + * - Describing servers and feed generators 25 + * 26 + * @example Basic usage 27 + * ```php 28 + * #[PublicEndpoint] 29 + * public function getProfile(string $actor): ProfileViewDetailed 30 + * ``` 31 + * 32 + * @see \SocialDept\AtpClient\Attributes\ScopedEndpoint For endpoints that require authentication 33 + */ 34 + #[Attribute(Attribute::TARGET_METHOD)] 35 + class PublicEndpoint 36 + { 37 + /** 38 + * @param string $description Human-readable description of the endpoint 39 + */ 40 + public function __construct( 41 + public readonly string $description = '', 42 + ) {} 43 + }
-37
src/Attributes/RequiresScope.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Attributes; 4 - 5 - use Attribute; 6 - use SocialDept\AtpClient\Enums\Scope; 7 - 8 - #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] 9 - class RequiresScope 10 - { 11 - public array $scopes; 12 - 13 - /** 14 - * @param string|Scope|array<string|Scope> $scopes Required scope(s) for this method 15 - * @param string|null $granular Future granular scope equivalent 16 - * @param string $description Human-readable description of scope requirement 17 - */ 18 - public function __construct( 19 - string|Scope|array $scopes, 20 - public readonly ?string $granular = null, 21 - public readonly string $description = '', 22 - ) { 23 - $this->scopes = $this->normalizeScopes($scopes); 24 - } 25 - 26 - protected function normalizeScopes(string|Scope|array $scopes): array 27 - { 28 - if (! is_array($scopes)) { 29 - $scopes = [$scopes]; 30 - } 31 - 32 - return array_map( 33 - fn ($scope) => $scope instanceof Scope ? $scope->value : $scope, 34 - $scopes 35 - ); 36 - } 37 - }
···
+67
src/Attributes/ScopedEndpoint.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Attributes; 4 + 5 + use Attribute; 6 + use SocialDept\AtpClient\Enums\Scope; 7 + 8 + /** 9 + * Documents that a method requires authentication with specific OAuth scopes. 10 + * 11 + * This attribute currently serves as documentation to indicate which AT Protocol 12 + * endpoints require authentication and what scopes they need. It helps developers 13 + * understand scope requirements when building applications. 14 + * 15 + * While this attribute does not currently perform runtime enforcement, scope 16 + * validation will be implemented in a future release. Correctly attributing 17 + * endpoints now ensures forward compatibility when enforcement is enabled. 18 + * 19 + * The AT Protocol currently uses "transition scopes" (like `transition:generic`) while 20 + * moving toward more granular scopes. The `granular` parameter allows documenting the 21 + * future granular scope that will replace the transition scope. 22 + * 23 + * @example Basic usage with a transition scope 24 + * ```php 25 + * #[ScopedEndpoint(Scope::TransitionGeneric)] 26 + * public function getTimeline(): GetTimelineResponse 27 + * ``` 28 + * 29 + * @example With future granular scope documented 30 + * ```php 31 + * #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getTimeline')] 32 + * public function getTimeline(): GetTimelineResponse 33 + * ``` 34 + * 35 + * @see \SocialDept\AtpClient\Attributes\PublicEndpoint For endpoints that don't require authentication 36 + * @see \SocialDept\AtpClient\Enums\Scope For available scope values 37 + */ 38 + #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] 39 + class ScopedEndpoint 40 + { 41 + public array $scopes; 42 + 43 + /** 44 + * @param string|Scope|array<string|Scope> $scopes Required scope(s) for this method 45 + * @param string|null $granular Future granular scope equivalent 46 + * @param string $description Human-readable description of scope requirement 47 + */ 48 + public function __construct( 49 + string|Scope|array $scopes, 50 + public readonly ?string $granular = null, 51 + public readonly string $description = '', 52 + ) { 53 + $this->scopes = $this->normalizeScopes($scopes); 54 + } 55 + 56 + protected function normalizeScopes(string|Scope|array $scopes): array 57 + { 58 + if (! is_array($scopes)) { 59 + $scopes = [$scopes]; 60 + } 61 + 62 + return array_map( 63 + fn ($scope) => $scope instanceof Scope ? $scope->value : $scope, 64 + $scopes 65 + ); 66 + } 67 + }
+5 -2
src/Auth/ScopeChecker.php
··· 2 3 namespace SocialDept\AtpClient\Auth; 4 5 use Illuminate\Support\Facades\Log; 6 use SocialDept\AtpClient\Enums\Scope; 7 use SocialDept\AtpClient\Enums\ScopeEnforcementLevel; ··· 197 /** 198 * Check if the session has repo access for a specific collection and action. 199 */ 200 - public function checkRepoScope(Session $session, string $collection, string $action): bool 201 { 202 $required = "repo:{$collection}?action={$action}"; 203 204 return $this->sessionHasScope($session, $required); ··· 209 * 210 * @throws MissingScopeException 211 */ 212 - public function checkRepoScopeOrFail(Session $session, string $collection, string $action): void 213 { 214 $required = "repo:{$collection}?action={$action}"; 215 216 $this->checkOrFail($session, [$required]);
··· 2 3 namespace SocialDept\AtpClient\Auth; 4 5 + use BackedEnum; 6 use Illuminate\Support\Facades\Log; 7 use SocialDept\AtpClient\Enums\Scope; 8 use SocialDept\AtpClient\Enums\ScopeEnforcementLevel; ··· 198 /** 199 * Check if the session has repo access for a specific collection and action. 200 */ 201 + public function checkRepoScope(Session $session, string|BackedEnum $collection, string $action): bool 202 { 203 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 204 $required = "repo:{$collection}?action={$action}"; 205 206 return $this->sessionHasScope($session, $required); ··· 211 * 212 * @throws MissingScopeException 213 */ 214 + public function checkRepoScopeOrFail(Session $session, string|BackedEnum $collection, string $action): void 215 { 216 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 217 $required = "repo:{$collection}?action={$action}"; 218 219 $this->checkOrFail($session, [$required]);
+197
src/Builders/Concerns/BuildsRichText.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Builders\Concerns; 4 + 5 + use SocialDept\AtpClient\RichText\FacetDetector; 6 + use SocialDept\AtpResolver\Facades\Resolver; 7 + 8 + trait BuildsRichText 9 + { 10 + protected string $text = ''; 11 + 12 + protected array $facets = []; 13 + 14 + /** 15 + * Add plain text 16 + */ 17 + public function text(string $text): self 18 + { 19 + $this->text .= $text; 20 + 21 + return $this; 22 + } 23 + 24 + /** 25 + * Add one or more new lines 26 + */ 27 + public function newLine(int $count = 1): self 28 + { 29 + $this->text .= str_repeat("\n", $count); 30 + 31 + return $this; 32 + } 33 + 34 + /** 35 + * Add mention (@handle) 36 + */ 37 + public function mention(string $handle, ?string $did = null): self 38 + { 39 + $handle = ltrim($handle, '@'); 40 + $start = $this->getBytePosition(); 41 + $this->text .= '@'.$handle; 42 + $end = $this->getBytePosition(); 43 + 44 + if (! $did) { 45 + try { 46 + $did = Resolver::handleToDid($handle); 47 + } catch (\Exception $e) { 48 + return $this; 49 + } 50 + } 51 + 52 + $this->facets[] = [ 53 + 'index' => [ 54 + 'byteStart' => $start, 55 + 'byteEnd' => $end, 56 + ], 57 + 'features' => [ 58 + [ 59 + '$type' => 'app.bsky.richtext.facet#mention', 60 + 'did' => $did, 61 + ], 62 + ], 63 + ]; 64 + 65 + return $this; 66 + } 67 + 68 + /** 69 + * Add link with custom display text 70 + */ 71 + public function link(string $text, string $uri): self 72 + { 73 + $start = $this->getBytePosition(); 74 + $this->text .= $text; 75 + $end = $this->getBytePosition(); 76 + 77 + $this->facets[] = [ 78 + 'index' => [ 79 + 'byteStart' => $start, 80 + 'byteEnd' => $end, 81 + ], 82 + 'features' => [ 83 + [ 84 + '$type' => 'app.bsky.richtext.facet#link', 85 + 'uri' => $uri, 86 + ], 87 + ], 88 + ]; 89 + 90 + return $this; 91 + } 92 + 93 + /** 94 + * Add a URL (displayed as-is) 95 + */ 96 + public function url(string $url): self 97 + { 98 + return $this->link($url, $url); 99 + } 100 + 101 + /** 102 + * Add hashtag 103 + */ 104 + public function tag(string $tag): self 105 + { 106 + $tag = ltrim($tag, '#'); 107 + 108 + $start = $this->getBytePosition(); 109 + $this->text .= '#'.$tag; 110 + $end = $this->getBytePosition(); 111 + 112 + $this->facets[] = [ 113 + 'index' => [ 114 + 'byteStart' => $start, 115 + 'byteEnd' => $end, 116 + ], 117 + 'features' => [ 118 + [ 119 + '$type' => 'app.bsky.richtext.facet#tag', 120 + 'tag' => $tag, 121 + ], 122 + ], 123 + ]; 124 + 125 + return $this; 126 + } 127 + 128 + /** 129 + * Auto-detect and add facets from plain text 130 + */ 131 + public function autoDetect(string $text): self 132 + { 133 + $start = $this->getBytePosition(); 134 + $this->text .= $text; 135 + 136 + $detected = FacetDetector::detect($text); 137 + 138 + foreach ($detected as $facet) { 139 + $facet['index']['byteStart'] += $start; 140 + $facet['index']['byteEnd'] += $start; 141 + $this->facets[] = $facet; 142 + } 143 + 144 + return $this; 145 + } 146 + 147 + /** 148 + * Get current byte position (UTF-8 byte offset) 149 + */ 150 + protected function getBytePosition(): int 151 + { 152 + return strlen($this->text); 153 + } 154 + 155 + /** 156 + * Get the text content 157 + */ 158 + public function getText(): string 159 + { 160 + return $this->text; 161 + } 162 + 163 + /** 164 + * Get the facets 165 + */ 166 + public function getFacets(): array 167 + { 168 + return $this->facets; 169 + } 170 + 171 + /** 172 + * Get text and facets as array 173 + */ 174 + protected function getTextAndFacets(): array 175 + { 176 + return [ 177 + 'text' => $this->text, 178 + 'facets' => $this->facets, 179 + ]; 180 + } 181 + 182 + /** 183 + * Get grapheme count (closest to what AT Protocol uses for limits) 184 + */ 185 + public function getGraphemeCount(): int 186 + { 187 + return grapheme_strlen($this->text); 188 + } 189 + 190 + /** 191 + * Check if text exceeds AT Protocol post limit (300 graphemes) 192 + */ 193 + public function exceedsLimit(int $limit = 300): bool 194 + { 195 + return $this->getGraphemeCount() > $limit; 196 + } 197 + }
+93
src/Builders/Embeds/ImagesBuilder.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Builders\Embeds; 4 + 5 + class ImagesBuilder 6 + { 7 + protected array $images = []; 8 + 9 + /** 10 + * Create a new images builder instance 11 + */ 12 + public static function make(): self 13 + { 14 + return new self; 15 + } 16 + 17 + /** 18 + * Add an image to the embed 19 + * 20 + * @param mixed $blob BlobReference or blob array 21 + * @param string $alt Alt text for the image 22 + * @param array|null $aspectRatio [width, height] aspect ratio 23 + */ 24 + public function add(mixed $blob, string $alt, ?array $aspectRatio = null): self 25 + { 26 + $image = [ 27 + 'image' => $this->normalizeBlob($blob), 28 + 'alt' => $alt, 29 + ]; 30 + 31 + if ($aspectRatio !== null) { 32 + $image['aspectRatio'] = [ 33 + 'width' => $aspectRatio[0], 34 + 'height' => $aspectRatio[1], 35 + ]; 36 + } 37 + 38 + $this->images[] = $image; 39 + 40 + return $this; 41 + } 42 + 43 + /** 44 + * Get all images 45 + */ 46 + public function getImages(): array 47 + { 48 + return $this->images; 49 + } 50 + 51 + /** 52 + * Check if builder has images 53 + */ 54 + public function hasImages(): bool 55 + { 56 + return ! empty($this->images); 57 + } 58 + 59 + /** 60 + * Get the count of images 61 + */ 62 + public function count(): int 63 + { 64 + return count($this->images); 65 + } 66 + 67 + /** 68 + * Convert to embed array format 69 + */ 70 + public function toArray(): array 71 + { 72 + return [ 73 + '$type' => 'app.bsky.embed.images', 74 + 'images' => $this->images, 75 + ]; 76 + } 77 + 78 + /** 79 + * Normalize blob to array format 80 + */ 81 + protected function normalizeBlob(mixed $blob): array 82 + { 83 + if (is_array($blob)) { 84 + return $blob; 85 + } 86 + 87 + if (method_exists($blob, 'toArray')) { 88 + return $blob->toArray(); 89 + } 90 + 91 + return (array) $blob; 92 + } 93 + }
+257
src/Builders/PostBuilder.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Builders; 4 + 5 + use Closure; 6 + use DateTimeInterface; 7 + use SocialDept\AtpClient\Builders\Concerns\BuildsRichText; 8 + use SocialDept\AtpClient\Builders\Embeds\ImagesBuilder; 9 + use SocialDept\AtpClient\Client\Records\PostRecordClient; 10 + use SocialDept\AtpClient\Contracts\Recordable; 11 + use SocialDept\AtpClient\Data\StrongRef; 12 + use SocialDept\AtpClient\Enums\Nsid\BskyFeed; 13 + 14 + class PostBuilder implements Recordable 15 + { 16 + use BuildsRichText; 17 + 18 + protected ?array $embed = null; 19 + 20 + protected ?array $reply = null; 21 + 22 + protected ?array $langs = null; 23 + 24 + protected ?DateTimeInterface $createdAt = null; 25 + 26 + protected ?PostRecordClient $client = null; 27 + 28 + /** 29 + * Create a new post builder instance 30 + */ 31 + public static function make(): self 32 + { 33 + return new self; 34 + } 35 + 36 + /** 37 + * Add images embed 38 + * 39 + * @param Closure|array $images Closure receiving ImagesBuilder, or array of image data 40 + */ 41 + public function images(Closure|array $images): self 42 + { 43 + if ($images instanceof Closure) { 44 + $builder = ImagesBuilder::make(); 45 + $images($builder); 46 + $this->embed = $builder->toArray(); 47 + } else { 48 + $this->embed = [ 49 + '$type' => 'app.bsky.embed.images', 50 + 'images' => array_map(fn ($img) => $this->normalizeImageData($img), $images), 51 + ]; 52 + } 53 + 54 + return $this; 55 + } 56 + 57 + /** 58 + * Add external link embed (link card) 59 + * 60 + * @param string $uri URL of the external content 61 + * @param string $title Title of the link card 62 + * @param string $description Description text 63 + * @param mixed|null $thumb Optional thumbnail blob 64 + */ 65 + public function external(string $uri, string $title, string $description, mixed $thumb = null): self 66 + { 67 + $external = [ 68 + 'uri' => $uri, 69 + 'title' => $title, 70 + 'description' => $description, 71 + ]; 72 + 73 + if ($thumb !== null) { 74 + $external['thumb'] = $this->normalizeBlob($thumb); 75 + } 76 + 77 + $this->embed = [ 78 + '$type' => 'app.bsky.embed.external', 79 + 'external' => $external, 80 + ]; 81 + 82 + return $this; 83 + } 84 + 85 + /** 86 + * Add video embed 87 + * 88 + * @param mixed $blob Video blob reference 89 + * @param string|null $alt Alt text for the video 90 + * @param array|null $captions Optional captions array 91 + */ 92 + public function video(mixed $blob, ?string $alt = null, ?array $captions = null): self 93 + { 94 + $video = [ 95 + '$type' => 'app.bsky.embed.video', 96 + 'video' => $this->normalizeBlob($blob), 97 + ]; 98 + 99 + if ($alt !== null) { 100 + $video['alt'] = $alt; 101 + } 102 + 103 + if ($captions !== null) { 104 + $video['captions'] = $captions; 105 + } 106 + 107 + $this->embed = $video; 108 + 109 + return $this; 110 + } 111 + 112 + /** 113 + * Add quote embed (embed another post) 114 + */ 115 + public function quote(StrongRef $post): self 116 + { 117 + $this->embed = [ 118 + '$type' => 'app.bsky.embed.record', 119 + 'record' => $post->toArray(), 120 + ]; 121 + 122 + return $this; 123 + } 124 + 125 + /** 126 + * Set as a reply to another post 127 + * 128 + * @param StrongRef $parent The post being replied to 129 + * @param StrongRef|null $root The root post of the thread (defaults to parent if not provided) 130 + */ 131 + public function replyTo(StrongRef $parent, ?StrongRef $root = null): self 132 + { 133 + $this->reply = [ 134 + 'parent' => $parent->toArray(), 135 + 'root' => ($root ?? $parent)->toArray(), 136 + ]; 137 + 138 + return $this; 139 + } 140 + 141 + /** 142 + * Set the post languages 143 + * 144 + * @param array $langs Array of BCP-47 language codes 145 + */ 146 + public function langs(array $langs): self 147 + { 148 + $this->langs = $langs; 149 + 150 + return $this; 151 + } 152 + 153 + /** 154 + * Set the creation timestamp 155 + */ 156 + public function createdAt(DateTimeInterface $date): self 157 + { 158 + $this->createdAt = $date; 159 + 160 + return $this; 161 + } 162 + 163 + /** 164 + * Bind to a PostRecordClient for creating the post 165 + */ 166 + public function for(PostRecordClient $client): self 167 + { 168 + $this->client = $client; 169 + 170 + return $this; 171 + } 172 + 173 + /** 174 + * Create the post (requires client binding via for() or build()) 175 + * 176 + * @throws \RuntimeException If no client is bound 177 + */ 178 + public function create(): StrongRef 179 + { 180 + if ($this->client === null) { 181 + throw new \RuntimeException( 182 + 'No client bound. Use ->for($client) or create via $client->bsky->post->build()' 183 + ); 184 + } 185 + 186 + return $this->client->create($this); 187 + } 188 + 189 + /** 190 + * Convert to array for XRPC (implements Recordable) 191 + */ 192 + public function toArray(): array 193 + { 194 + $record = $this->getTextAndFacets(); 195 + 196 + if ($this->embed !== null) { 197 + $record['embed'] = $this->embed; 198 + } 199 + 200 + if ($this->reply !== null) { 201 + $record['reply'] = $this->reply; 202 + } 203 + 204 + if ($this->langs !== null) { 205 + $record['langs'] = $this->langs; 206 + } 207 + 208 + $record['createdAt'] = ($this->createdAt ?? now())->format('c'); 209 + $record['$type'] = $this->getType(); 210 + 211 + return $record; 212 + } 213 + 214 + /** 215 + * Get the record type (implements Recordable) 216 + */ 217 + public function getType(): string 218 + { 219 + return BskyFeed::Post->value; 220 + } 221 + 222 + /** 223 + * Normalize image data from array format 224 + */ 225 + protected function normalizeImageData(array $data): array 226 + { 227 + $image = [ 228 + 'image' => $this->normalizeBlob($data['blob'] ?? $data['image']), 229 + 'alt' => $data['alt'] ?? '', 230 + ]; 231 + 232 + if (isset($data['aspectRatio'])) { 233 + $ratio = $data['aspectRatio']; 234 + $image['aspectRatio'] = is_array($ratio) && isset($ratio['width']) 235 + ? $ratio 236 + : ['width' => $ratio[0], 'height' => $ratio[1]]; 237 + } 238 + 239 + return $image; 240 + } 241 + 242 + /** 243 + * Normalize blob to array format 244 + */ 245 + protected function normalizeBlob(mixed $blob): array 246 + { 247 + if (is_array($blob)) { 248 + return $blob; 249 + } 250 + 251 + if (method_exists($blob, 'toArray')) { 252 + return $blob->toArray(); 253 + } 254 + 255 + return (array) $blob; 256 + } 257 + }
+13
src/Client/BskyClient.php
··· 13 class BskyClient 14 { 15 use HasDomainExtensions; 16 /** 17 * The parent AtpClient instance 18 */ ··· 29 public Bsky\ActorRequestClient $actor; 30 31 /** 32 * Post record client 33 */ 34 public PostRecordClient $post; ··· 53 $this->atp = $parent; 54 $this->feed = new Bsky\FeedRequestClient($this); 55 $this->actor = new Bsky\ActorRequestClient($this); 56 $this->post = new PostRecordClient($this); 57 $this->profile = new ProfileRecordClient($this); 58 $this->like = new LikeRecordClient($this);
··· 13 class BskyClient 14 { 15 use HasDomainExtensions; 16 + 17 /** 18 * The parent AtpClient instance 19 */ ··· 30 public Bsky\ActorRequestClient $actor; 31 32 /** 33 + * Graph operations (app.bsky.graph.*) 34 + */ 35 + public Bsky\GraphRequestClient $graph; 36 + 37 + /** 38 + * Labeler operations (app.bsky.labeler.*) 39 + */ 40 + public Bsky\LabelerRequestClient $labeler; 41 + 42 + /** 43 * Post record client 44 */ 45 public PostRecordClient $post; ··· 64 $this->atp = $parent; 65 $this->feed = new Bsky\FeedRequestClient($this); 66 $this->actor = new Bsky\ActorRequestClient($this); 67 + $this->graph = new Bsky\GraphRequestClient($this); 68 + $this->labeler = new Bsky\LabelerRequestClient($this); 69 $this->post = new PostRecordClient($this); 70 $this->profile = new ProfileRecordClient($this); 71 $this->like = new LikeRecordClient($this);
+93 -4
src/Client/Client.php
··· 2 3 namespace SocialDept\AtpClient\Client; 4 5 use SocialDept\AtpClient\AtpClient; 6 use SocialDept\AtpClient\Http\DPoPClient; 7 use SocialDept\AtpClient\Http\HasHttp; 8 use SocialDept\AtpClient\Session\Session; 9 use SocialDept\AtpClient\Session\SessionManager; 10 11 class Client 12 { 13 - use HasHttp; 14 15 /** 16 * The parent AtpClient instance we belong to 17 */ 18 protected AtpClient $atp; 19 20 public function __construct( 21 AtpClient $parent, 22 - SessionManager $sessions, 23 - string $did, 24 ) { 25 $this->atp = $parent; 26 $this->sessions = $sessions; 27 $this->did = $did; 28 - $this->dpopClient = app(DPoPClient::class); 29 } 30 31 /** ··· 34 public function session(): Session 35 { 36 return $this->sessions->session($this->did); 37 } 38 }
··· 2 3 namespace SocialDept\AtpClient\Client; 4 5 + use BackedEnum; 6 + use Illuminate\Support\Facades\Http; 7 use SocialDept\AtpClient\AtpClient; 8 + use SocialDept\AtpClient\Exceptions\AtpResponseException; 9 use SocialDept\AtpClient\Http\DPoPClient; 10 use SocialDept\AtpClient\Http\HasHttp; 11 + use SocialDept\AtpClient\Http\Response; 12 use SocialDept\AtpClient\Session\Session; 13 use SocialDept\AtpClient\Session\SessionManager; 14 15 class Client 16 { 17 + use HasHttp { 18 + call as authenticatedCall; 19 + postBlob as authenticatedPostBlob; 20 + } 21 22 /** 23 * The parent AtpClient instance we belong to 24 */ 25 protected AtpClient $atp; 26 27 + /** 28 + * Service URL for public mode 29 + */ 30 + protected ?string $serviceUrl; 31 + 32 public function __construct( 33 AtpClient $parent, 34 + ?SessionManager $sessions = null, 35 + ?string $did = null, 36 + ?string $serviceUrl = null, 37 ) { 38 $this->atp = $parent; 39 $this->sessions = $sessions; 40 $this->did = $did; 41 + $this->serviceUrl = $serviceUrl; 42 + 43 + if (! $this->isPublicMode()) { 44 + $this->dpopClient = app(DPoPClient::class); 45 + } 46 + } 47 + 48 + /** 49 + * Check if client is in public mode (no authentication). 50 + */ 51 + public function isPublicMode(): bool 52 + { 53 + return $this->sessions === null || $this->did === null; 54 } 55 56 /** ··· 59 public function session(): Session 60 { 61 return $this->sessions->session($this->did); 62 + } 63 + 64 + /** 65 + * Get the service URL. 66 + */ 67 + public function serviceUrl(): string 68 + { 69 + return $this->serviceUrl; 70 + } 71 + 72 + /** 73 + * Make XRPC call - routes to public or authenticated based on mode. 74 + */ 75 + protected function call( 76 + string|BackedEnum $endpoint, 77 + string $method, 78 + ?array $params = null, 79 + ?array $body = null 80 + ): Response { 81 + if ($this->isPublicMode()) { 82 + return $this->publicCall($endpoint, $method, $params, $body); 83 + } 84 + 85 + return $this->authenticatedCall($endpoint, $method, $params, $body); 86 + } 87 + 88 + /** 89 + * Make public XRPC call (no authentication). 90 + */ 91 + protected function publicCall( 92 + string|BackedEnum $endpoint, 93 + string $method, 94 + ?array $params = null, 95 + ?array $body = null 96 + ): Response { 97 + $endpoint = $endpoint instanceof BackedEnum ? $endpoint->value : $endpoint; 98 + $url = rtrim($this->serviceUrl, '/') . '/xrpc/' . $endpoint; 99 + $params = array_filter($params ?? [], fn ($v) => ! is_null($v)); 100 + 101 + $response = match ($method) { 102 + 'GET' => Http::get($url, $params), 103 + 'POST' => Http::post($url, $body ?? $params), 104 + 'DELETE' => Http::delete($url, $params), 105 + default => throw new \InvalidArgumentException("Unsupported method: {$method}"), 106 + }; 107 + 108 + if ($response->failed() || isset($response->json()['error'])) { 109 + throw AtpResponseException::fromResponse($response, $endpoint); 110 + } 111 + 112 + return new Response($response); 113 + } 114 + 115 + /** 116 + * Make POST request with raw binary body (for blob uploads). 117 + * Only works in authenticated mode. 118 + */ 119 + public function postBlob(string|BackedEnum $endpoint, string $data, string $mimeType): Response 120 + { 121 + if ($this->isPublicMode()) { 122 + throw new \RuntimeException('Blob uploads require authentication.'); 123 + } 124 + 125 + return $this->authenticatedPostBlob($endpoint, $data, $mimeType); 126 } 127 }
-20
src/Client/Public/AtpPublicClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public; 4 - 5 - use SocialDept\AtpClient\Concerns\HasExtensions; 6 - 7 - class AtpPublicClient 8 - { 9 - use HasExtensions; 10 - public PublicClient $client; 11 - public BskyPublicClient $bsky; 12 - public AtprotoPublicClient $atproto; 13 - 14 - public function __construct(string $serviceUrl) 15 - { 16 - $this->client = new PublicClient($serviceUrl); 17 - $this->bsky = new BskyPublicClient($this); 18 - $this->atproto = new AtprotoPublicClient($this); 19 - } 20 - }
···
-38
src/Client/Public/AtprotoPublicClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public; 4 - 5 - use SocialDept\AtpClient\Client\Public\Requests\Atproto\IdentityPublicRequestClient; 6 - use SocialDept\AtpClient\Client\Public\Requests\Atproto\RepoPublicRequestClient; 7 - use SocialDept\AtpClient\Concerns\HasDomainExtensions; 8 - 9 - class AtprotoPublicClient 10 - { 11 - use HasDomainExtensions; 12 - 13 - protected AtpPublicClient $atp; 14 - public IdentityPublicRequestClient $identity; 15 - public RepoPublicRequestClient $repo; 16 - 17 - public function __construct(AtpPublicClient $parent) 18 - { 19 - $this->atp = $parent; 20 - $this->identity = new IdentityPublicRequestClient($this); 21 - $this->repo = new RepoPublicRequestClient($this); 22 - } 23 - 24 - protected function getDomainName(): string 25 - { 26 - return 'atproto'; 27 - } 28 - 29 - protected function getRootClientClass(): string 30 - { 31 - return AtpPublicClient::class; 32 - } 33 - 34 - public function root(): AtpPublicClient 35 - { 36 - return $this->atp; 37 - } 38 - }
···
-44
src/Client/Public/BskyPublicClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public; 4 - 5 - use SocialDept\AtpClient\Client\Public\Requests\Bsky\ActorPublicRequestClient; 6 - use SocialDept\AtpClient\Client\Public\Requests\Bsky\FeedPublicRequestClient; 7 - use SocialDept\AtpClient\Client\Public\Requests\Bsky\GraphPublicRequestClient; 8 - use SocialDept\AtpClient\Client\Public\Requests\Bsky\LabelerPublicRequestClient; 9 - use SocialDept\AtpClient\Concerns\HasDomainExtensions; 10 - 11 - class BskyPublicClient 12 - { 13 - use HasDomainExtensions; 14 - 15 - protected AtpPublicClient $atp; 16 - public ActorPublicRequestClient $actor; 17 - public FeedPublicRequestClient $feed; 18 - public GraphPublicRequestClient $graph; 19 - public LabelerPublicRequestClient $labeler; 20 - 21 - public function __construct(AtpPublicClient $parent) 22 - { 23 - $this->atp = $parent; 24 - $this->actor = new ActorPublicRequestClient($this); 25 - $this->feed = new FeedPublicRequestClient($this); 26 - $this->graph = new GraphPublicRequestClient($this); 27 - $this->labeler = new LabelerPublicRequestClient($this); 28 - } 29 - 30 - protected function getDomainName(): string 31 - { 32 - return 'bsky'; 33 - } 34 - 35 - protected function getRootClientClass(): string 36 - { 37 - return AtpPublicClient::class; 38 - } 39 - 40 - public function root(): AtpPublicClient 41 - { 42 - return $this->atp; 43 - } 44 - }
···
-33
src/Client/Public/PublicClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public; 4 - 5 - use Illuminate\Support\Facades\Http; 6 - use SocialDept\AtpClient\Exceptions\AtpResponseException; 7 - use SocialDept\AtpClient\Http\Response; 8 - 9 - class PublicClient 10 - { 11 - public function __construct( 12 - protected string $serviceUrl 13 - ) {} 14 - 15 - public function get(string $endpoint, array $params = []): Response 16 - { 17 - $url = rtrim($this->serviceUrl, '/') . '/xrpc/' . $endpoint; 18 - $params = array_filter($params, fn ($v) => !is_null($v)); 19 - 20 - $response = Http::get($url, $params); 21 - 22 - if ($response->failed() || isset($response->json()['error'])) { 23 - throw AtpResponseException::fromResponse($response, $endpoint); 24 - } 25 - 26 - return new Response($response); 27 - } 28 - 29 - public function serviceUrl(): string 30 - { 31 - return $this->serviceUrl; 32 - } 33 - }
···
-15
src/Client/Public/Requests/Atproto/IdentityPublicRequestClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public\Requests\Atproto; 4 - 5 - use SocialDept\AtpClient\Client\Public\Requests\PublicRequest; 6 - 7 - class IdentityPublicRequestClient extends PublicRequest 8 - { 9 - public function resolveHandle(string $handle): string 10 - { 11 - $response = $this->atp->client->get('com.atproto.identity.resolveHandle', compact('handle')); 12 - 13 - return $response->json()['did']; 14 - } 15 - }
···
-65
src/Client/Public/Requests/Atproto/RepoPublicRequestClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public\Requests\Atproto; 4 - 5 - use SocialDept\AtpClient\Client\Public\Requests\PublicRequest; 6 - use SocialDept\AtpClient\Data\Responses\Atproto\Repo\DescribeRepoResponse; 7 - use SocialDept\AtpClient\Data\Responses\Atproto\Repo\GetRecordResponse; 8 - use SocialDept\AtpClient\Data\Responses\Atproto\Repo\ListRecordsResponse; 9 - 10 - class RepoPublicRequestClient extends PublicRequest 11 - { 12 - /** 13 - * Get a record 14 - * 15 - * @see https://docs.bsky.app/docs/api/com-atproto-repo-get-record 16 - */ 17 - public function getRecord( 18 - string $repo, 19 - string $collection, 20 - string $rkey, 21 - ?string $cid = null 22 - ): GetRecordResponse { 23 - $response = $this->atp->client->get( 24 - 'com.atproto.repo.getRecord', 25 - compact('repo', 'collection', 'rkey', 'cid') 26 - ); 27 - 28 - return GetRecordResponse::fromArray($response->json()); 29 - } 30 - 31 - /** 32 - * List records in a collection 33 - * 34 - * @see https://docs.bsky.app/docs/api/com-atproto-repo-list-records 35 - */ 36 - public function listRecords( 37 - string $repo, 38 - string $collection, 39 - int $limit = 50, 40 - ?string $cursor = null, 41 - bool $reverse = false 42 - ): ListRecordsResponse { 43 - $response = $this->atp->client->get( 44 - 'com.atproto.repo.listRecords', 45 - compact('repo', 'collection', 'limit', 'cursor', 'reverse') 46 - ); 47 - 48 - return ListRecordsResponse::fromArray($response->json()); 49 - } 50 - 51 - /** 52 - * Describe the repository 53 - * 54 - * @see https://docs.bsky.app/docs/api/com-atproto-repo-describe-repo 55 - */ 56 - public function describeRepo(string $repo): DescribeRepoResponse 57 - { 58 - $response = $this->atp->client->get( 59 - 'com.atproto.repo.describeRepo', 60 - compact('repo') 61 - ); 62 - 63 - return DescribeRepoResponse::fromArray($response->json()); 64 - } 65 - }
···
-48
src/Client/Public/Requests/Bsky/ActorPublicRequestClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public\Requests\Bsky; 4 - 5 - use SocialDept\AtpClient\Client\Public\Requests\PublicRequest; 6 - use SocialDept\AtpClient\Data\Responses\Bsky\Actor\GetProfilesResponse; 7 - use SocialDept\AtpClient\Data\Responses\Bsky\Actor\GetSuggestionsResponse; 8 - use SocialDept\AtpClient\Data\Responses\Bsky\Actor\SearchActorsResponse; 9 - use SocialDept\AtpClient\Data\Responses\Bsky\Actor\SearchActorsTypeaheadResponse; 10 - use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileViewDetailed; 11 - 12 - class ActorPublicRequestClient extends PublicRequest 13 - { 14 - public function getProfile(string $actor): ProfileViewDetailed 15 - { 16 - $response = $this->atp->client->get('app.bsky.actor.getProfile', compact('actor')); 17 - 18 - return ProfileViewDetailed::fromArray($response->json()); 19 - } 20 - 21 - public function getProfiles(array $actors): GetProfilesResponse 22 - { 23 - $response = $this->atp->client->get('app.bsky.actor.getProfiles', compact('actors')); 24 - 25 - return GetProfilesResponse::fromArray($response->json()); 26 - } 27 - 28 - public function getSuggestions(int $limit = 50, ?string $cursor = null): GetSuggestionsResponse 29 - { 30 - $response = $this->atp->client->get('app.bsky.actor.getSuggestions', compact('limit', 'cursor')); 31 - 32 - return GetSuggestionsResponse::fromArray($response->json()); 33 - } 34 - 35 - public function searchActors(string $q, int $limit = 25, ?string $cursor = null): SearchActorsResponse 36 - { 37 - $response = $this->atp->client->get('app.bsky.actor.searchActors', compact('q', 'limit', 'cursor')); 38 - 39 - return SearchActorsResponse::fromArray($response->json()); 40 - } 41 - 42 - public function searchActorsTypeahead(string $q, int $limit = 10): SearchActorsTypeaheadResponse 43 - { 44 - $response = $this->atp->client->get('app.bsky.actor.searchActorsTypeahead', compact('q', 'limit')); 45 - 46 - return SearchActorsTypeaheadResponse::fromArray($response->json()); 47 - } 48 - }
···
-120
src/Client/Public/Requests/Bsky/FeedPublicRequestClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public\Requests\Bsky; 4 - 5 - use SocialDept\AtpClient\Client\Public\Requests\PublicRequest; 6 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\DescribeFeedGeneratorResponse; 7 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetActorFeedsResponse; 8 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetActorLikesResponse; 9 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetAuthorFeedResponse; 10 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetFeedGeneratorResponse; 11 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetFeedGeneratorsResponse; 12 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetFeedResponse; 13 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetLikesResponse; 14 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetPostsResponse; 15 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetPostThreadResponse; 16 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetQuotesResponse; 17 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetRepostedByResponse; 18 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetSuggestedFeedsResponse; 19 - use SocialDept\AtpClient\Data\Responses\Bsky\Feed\SearchPostsResponse; 20 - 21 - class FeedPublicRequestClient extends PublicRequest 22 - { 23 - public function describeFeedGenerator(): DescribeFeedGeneratorResponse 24 - { 25 - $response = $this->atp->client->get('app.bsky.feed.describeFeedGenerator'); 26 - 27 - return DescribeFeedGeneratorResponse::fromArray($response->json()); 28 - } 29 - 30 - public function getAuthorFeed(string $actor, int $limit = 50, ?string $cursor = null, ?string $filter = null): GetAuthorFeedResponse 31 - { 32 - $response = $this->atp->client->get('app.bsky.feed.getAuthorFeed', compact('actor', 'limit', 'cursor', 'filter')); 33 - 34 - return GetAuthorFeedResponse::fromArray($response->json()); 35 - } 36 - 37 - public function getActorFeeds(string $actor, int $limit = 50, ?string $cursor = null): GetActorFeedsResponse 38 - { 39 - $response = $this->atp->client->get('app.bsky.feed.getActorFeeds', compact('actor', 'limit', 'cursor')); 40 - 41 - return GetActorFeedsResponse::fromArray($response->json()); 42 - } 43 - 44 - public function getActorLikes(string $actor, int $limit = 50, ?string $cursor = null): GetActorLikesResponse 45 - { 46 - $response = $this->atp->client->get('app.bsky.feed.getActorLikes', compact('actor', 'limit', 'cursor')); 47 - 48 - return GetActorLikesResponse::fromArray($response->json()); 49 - } 50 - 51 - public function getFeed(string $feed, int $limit = 50, ?string $cursor = null): GetFeedResponse 52 - { 53 - $response = $this->atp->client->get('app.bsky.feed.getFeed', compact('feed', 'limit', 'cursor')); 54 - 55 - return GetFeedResponse::fromArray($response->json()); 56 - } 57 - 58 - public function getFeedGenerator(string $feed): GetFeedGeneratorResponse 59 - { 60 - $response = $this->atp->client->get('app.bsky.feed.getFeedGenerator', compact('feed')); 61 - 62 - return GetFeedGeneratorResponse::fromArray($response->json()); 63 - } 64 - 65 - public function getFeedGenerators(array $feeds): GetFeedGeneratorsResponse 66 - { 67 - $response = $this->atp->client->get('app.bsky.feed.getFeedGenerators', compact('feeds')); 68 - 69 - return GetFeedGeneratorsResponse::fromArray($response->json()); 70 - } 71 - 72 - public function getLikes(string $uri, int $limit = 50, ?string $cursor = null, ?string $cid = null): GetLikesResponse 73 - { 74 - $response = $this->atp->client->get('app.bsky.feed.getLikes', compact('uri', 'limit', 'cursor', 'cid')); 75 - 76 - return GetLikesResponse::fromArray($response->json()); 77 - } 78 - 79 - public function getPostThread(string $uri, int $depth = 6, int $parentHeight = 80): GetPostThreadResponse 80 - { 81 - $response = $this->atp->client->get('app.bsky.feed.getPostThread', compact('uri', 'depth', 'parentHeight')); 82 - 83 - return GetPostThreadResponse::fromArray($response->json()); 84 - } 85 - 86 - public function getPosts(array $uris): GetPostsResponse 87 - { 88 - $response = $this->atp->client->get('app.bsky.feed.getPosts', compact('uris')); 89 - 90 - return GetPostsResponse::fromArray($response->json()); 91 - } 92 - 93 - public function getQuotes(string $uri, int $limit = 50, ?string $cursor = null, ?string $cid = null): GetQuotesResponse 94 - { 95 - $response = $this->atp->client->get('app.bsky.feed.getQuotes', compact('uri', 'limit', 'cursor', 'cid')); 96 - 97 - return GetQuotesResponse::fromArray($response->json()); 98 - } 99 - 100 - public function getRepostedBy(string $uri, int $limit = 50, ?string $cursor = null, ?string $cid = null): GetRepostedByResponse 101 - { 102 - $response = $this->atp->client->get('app.bsky.feed.getRepostedBy', compact('uri', 'limit', 'cursor', 'cid')); 103 - 104 - return GetRepostedByResponse::fromArray($response->json()); 105 - } 106 - 107 - public function getSuggestedFeeds(int $limit = 50, ?string $cursor = null): GetSuggestedFeedsResponse 108 - { 109 - $response = $this->atp->client->get('app.bsky.feed.getSuggestedFeeds', compact('limit', 'cursor')); 110 - 111 - return GetSuggestedFeedsResponse::fromArray($response->json()); 112 - } 113 - 114 - public function searchPosts(string $q, int $limit = 25, ?string $cursor = null, ?string $sort = null): SearchPostsResponse 115 - { 116 - $response = $this->atp->client->get('app.bsky.feed.searchPosts', compact('q', 'limit', 'cursor', 'sort')); 117 - 118 - return SearchPostsResponse::fromArray($response->json()); 119 - } 120 - }
···
-80
src/Client/Public/Requests/Bsky/GraphPublicRequestClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public\Requests\Bsky; 4 - 5 - use SocialDept\AtpClient\Client\Public\Requests\PublicRequest; 6 - use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetFollowersResponse; 7 - use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetFollowsResponse; 8 - use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetKnownFollowersResponse; 9 - use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetListResponse; 10 - use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetListsResponse; 11 - use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetRelationshipsResponse; 12 - use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetStarterPacksResponse; 13 - use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetSuggestedFollowsByActorResponse; 14 - use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\StarterPackView; 15 - 16 - class GraphPublicRequestClient extends PublicRequest 17 - { 18 - public function getFollowers(string $actor, int $limit = 50, ?string $cursor = null): GetFollowersResponse 19 - { 20 - $response = $this->atp->client->get('app.bsky.graph.getFollowers', compact('actor', 'limit', 'cursor')); 21 - 22 - return GetFollowersResponse::fromArray($response->json()); 23 - } 24 - 25 - public function getFollows(string $actor, int $limit = 50, ?string $cursor = null): GetFollowsResponse 26 - { 27 - $response = $this->atp->client->get('app.bsky.graph.getFollows', compact('actor', 'limit', 'cursor')); 28 - 29 - return GetFollowsResponse::fromArray($response->json()); 30 - } 31 - 32 - public function getKnownFollowers(string $actor, int $limit = 50, ?string $cursor = null): GetKnownFollowersResponse 33 - { 34 - $response = $this->atp->client->get('app.bsky.graph.getKnownFollowers', compact('actor', 'limit', 'cursor')); 35 - 36 - return GetKnownFollowersResponse::fromArray($response->json()); 37 - } 38 - 39 - public function getList(string $list, int $limit = 50, ?string $cursor = null): GetListResponse 40 - { 41 - $response = $this->atp->client->get('app.bsky.graph.getList', compact('list', 'limit', 'cursor')); 42 - 43 - return GetListResponse::fromArray($response->json()); 44 - } 45 - 46 - public function getLists(string $actor, int $limit = 50, ?string $cursor = null): GetListsResponse 47 - { 48 - $response = $this->atp->client->get('app.bsky.graph.getLists', compact('actor', 'limit', 'cursor')); 49 - 50 - return GetListsResponse::fromArray($response->json()); 51 - } 52 - 53 - public function getRelationships(string $actor, array $others = []): GetRelationshipsResponse 54 - { 55 - $response = $this->atp->client->get('app.bsky.graph.getRelationships', compact('actor', 'others')); 56 - 57 - return GetRelationshipsResponse::fromArray($response->json()); 58 - } 59 - 60 - public function getStarterPack(string $starterPack): StarterPackView 61 - { 62 - $response = $this->atp->client->get('app.bsky.graph.getStarterPack', compact('starterPack')); 63 - 64 - return StarterPackView::fromArray($response->json()['starterPack']); 65 - } 66 - 67 - public function getStarterPacks(array $uris): GetStarterPacksResponse 68 - { 69 - $response = $this->atp->client->get('app.bsky.graph.getStarterPacks', compact('uris')); 70 - 71 - return GetStarterPacksResponse::fromArray($response->json()); 72 - } 73 - 74 - public function getSuggestedFollowsByActor(string $actor): GetSuggestedFollowsByActorResponse 75 - { 76 - $response = $this->atp->client->get('app.bsky.graph.getSuggestedFollowsByActor', compact('actor')); 77 - 78 - return GetSuggestedFollowsByActorResponse::fromArray($response->json()); 79 - } 80 - }
···
-16
src/Client/Public/Requests/Bsky/LabelerPublicRequestClient.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public\Requests\Bsky; 4 - 5 - use SocialDept\AtpClient\Client\Public\Requests\PublicRequest; 6 - use SocialDept\AtpClient\Data\Responses\Bsky\Labeler\GetServicesResponse; 7 - 8 - class LabelerPublicRequestClient extends PublicRequest 9 - { 10 - public function getServices(array $dids, bool $detailed = false): GetServicesResponse 11 - { 12 - $response = $this->atp->client->get('app.bsky.labeler.getServices', compact('dids', 'detailed')); 13 - 14 - return GetServicesResponse::fromArray($response->json(), $detailed); 15 - } 16 - }
···
-15
src/Client/Public/Requests/PublicRequest.php
··· 1 - <?php 2 - 3 - namespace SocialDept\AtpClient\Client\Public\Requests; 4 - 5 - use SocialDept\AtpClient\Client\Public\AtpPublicClient; 6 - 7 - class PublicRequest 8 - { 9 - protected AtpPublicClient $atp; 10 - 11 - public function __construct($parent) 12 - { 13 - $this->atp = $parent->root(); 14 - } 15 - }
···
+20 -21
src/Client/Records/FollowRecordClient.php
··· 3 namespace SocialDept\AtpClient\Client\Records; 4 5 use DateTimeInterface; 6 - use SocialDept\AtpClient\Attributes\RequiresScope; 7 use SocialDept\AtpClient\Client\Requests\Request; 8 - use SocialDept\AtpClient\Data\StrongRef; 9 use SocialDept\AtpClient\Enums\Scope; 10 11 class FollowRecordClient extends Request ··· 15 * 16 * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.graph.follow?action=create) 17 */ 18 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 19 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.graph.follow?action=create')] 20 public function create( 21 string $subject, 22 ?DateTimeInterface $createdAt = null 23 - ): StrongRef { 24 $record = [ 25 - '$type' => 'app.bsky.graph.follow', 26 'subject' => $subject, // DID 27 'createdAt' => ($createdAt ?? now())->format('c'), 28 ]; 29 30 - $response = $this->atp->atproto->repo->createRecord( 31 - repo: $this->atp->client->session()->did(), 32 - collection: 'app.bsky.graph.follow', 33 record: $record 34 ); 35 - 36 - return StrongRef::fromResponse($response->json()); 37 } 38 39 /** ··· 41 * 42 * @requires transition:generic OR (rpc:com.atproto.repo.deleteRecord AND repo:app.bsky.graph.follow?action=delete) 43 */ 44 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.deleteRecord')] 45 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.graph.follow?action=delete')] 46 - public function delete(string $rkey): void 47 { 48 - $this->atp->atproto->repo->deleteRecord( 49 - repo: $this->atp->client->session()->did(), 50 - collection: 'app.bsky.graph.follow', 51 rkey: $rkey 52 ); 53 } ··· 57 * 58 * @requires transition:generic (rpc:com.atproto.repo.getRecord) 59 */ 60 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.getRecord')] 61 - public function get(string $rkey, ?string $cid = null): array 62 { 63 $response = $this->atp->atproto->repo->getRecord( 64 repo: $this->atp->client->session()->did(), 65 - collection: 'app.bsky.graph.follow', 66 rkey: $rkey, 67 cid: $cid 68 ); 69 70 - return $response->json('value'); 71 } 72 }
··· 3 namespace SocialDept\AtpClient\Client\Records; 4 5 use DateTimeInterface; 6 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 7 use SocialDept\AtpClient\Client\Requests\Request; 8 + use SocialDept\AtpClient\Data\Record; 9 + use SocialDept\AtpClient\Data\Responses\Atproto\Repo\CreateRecordResponse; 10 + use SocialDept\AtpClient\Data\Responses\Atproto\Repo\DeleteRecordResponse; 11 + use SocialDept\AtpClient\Enums\Nsid\BskyGraph; 12 use SocialDept\AtpClient\Enums\Scope; 13 14 class FollowRecordClient extends Request ··· 18 * 19 * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.graph.follow?action=create) 20 */ 21 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 22 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.graph.follow?action=create')] 23 public function create( 24 string $subject, 25 ?DateTimeInterface $createdAt = null 26 + ): CreateRecordResponse { 27 $record = [ 28 + '$type' => BskyGraph::Follow->value, 29 'subject' => $subject, // DID 30 'createdAt' => ($createdAt ?? now())->format('c'), 31 ]; 32 33 + return $this->atp->atproto->repo->createRecord( 34 + collection: BskyGraph::Follow, 35 record: $record 36 ); 37 } 38 39 /** ··· 41 * 42 * @requires transition:generic OR (rpc:com.atproto.repo.deleteRecord AND repo:app.bsky.graph.follow?action=delete) 43 */ 44 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.deleteRecord')] 45 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.graph.follow?action=delete')] 46 + public function delete(string $rkey): DeleteRecordResponse 47 { 48 + return $this->atp->atproto->repo->deleteRecord( 49 + collection: BskyGraph::Follow, 50 rkey: $rkey 51 ); 52 } ··· 56 * 57 * @requires transition:generic (rpc:com.atproto.repo.getRecord) 58 */ 59 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.getRecord')] 60 + public function get(string $rkey, ?string $cid = null): Record 61 { 62 $response = $this->atp->atproto->repo->getRecord( 63 repo: $this->atp->client->session()->did(), 64 + collection: BskyGraph::Follow, 65 rkey: $rkey, 66 cid: $cid 67 ); 68 69 + return Record::fromArrayRaw($response->toArray()); 70 } 71 }
+20 -20
src/Client/Records/LikeRecordClient.php
··· 3 namespace SocialDept\AtpClient\Client\Records; 4 5 use DateTimeInterface; 6 - use SocialDept\AtpClient\Attributes\RequiresScope; 7 use SocialDept\AtpClient\Client\Requests\Request; 8 use SocialDept\AtpClient\Data\StrongRef; 9 use SocialDept\AtpClient\Enums\Scope; 10 11 class LikeRecordClient extends Request ··· 15 * 16 * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.feed.like?action=create) 17 */ 18 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 19 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.like?action=create')] 20 public function create( 21 StrongRef $subject, 22 ?DateTimeInterface $createdAt = null 23 - ): StrongRef { 24 $record = [ 25 - '$type' => 'app.bsky.feed.like', 26 'subject' => $subject->toArray(), 27 'createdAt' => ($createdAt ?? now())->format('c'), 28 ]; 29 30 - $response = $this->atp->atproto->repo->createRecord( 31 - repo: $this->atp->client->session()->did(), 32 - collection: 'app.bsky.feed.like', 33 record: $record 34 ); 35 - 36 - return StrongRef::fromResponse($response->json()); 37 } 38 39 /** ··· 41 * 42 * @requires transition:generic OR (rpc:com.atproto.repo.deleteRecord AND repo:app.bsky.feed.like?action=delete) 43 */ 44 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.deleteRecord')] 45 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.like?action=delete')] 46 - public function delete(string $rkey): void 47 { 48 - $this->atp->atproto->repo->deleteRecord( 49 - repo: $this->atp->client->session()->did(), 50 - collection: 'app.bsky.feed.like', 51 rkey: $rkey 52 ); 53 } ··· 57 * 58 * @requires transition:generic (rpc:com.atproto.repo.getRecord) 59 */ 60 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.getRecord')] 61 - public function get(string $rkey, ?string $cid = null): array 62 { 63 $response = $this->atp->atproto->repo->getRecord( 64 repo: $this->atp->client->session()->did(), 65 - collection: 'app.bsky.feed.like', 66 rkey: $rkey, 67 cid: $cid 68 ); 69 70 - return $response->json('value'); 71 } 72 }
··· 3 namespace SocialDept\AtpClient\Client\Records; 4 5 use DateTimeInterface; 6 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 7 use SocialDept\AtpClient\Client\Requests\Request; 8 + use SocialDept\AtpClient\Data\Record; 9 + use SocialDept\AtpClient\Data\Responses\Atproto\Repo\CreateRecordResponse; 10 + use SocialDept\AtpClient\Data\Responses\Atproto\Repo\DeleteRecordResponse; 11 use SocialDept\AtpClient\Data\StrongRef; 12 + use SocialDept\AtpClient\Enums\Nsid\BskyFeed; 13 use SocialDept\AtpClient\Enums\Scope; 14 15 class LikeRecordClient extends Request ··· 19 * 20 * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.feed.like?action=create) 21 */ 22 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 23 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.like?action=create')] 24 public function create( 25 StrongRef $subject, 26 ?DateTimeInterface $createdAt = null 27 + ): CreateRecordResponse { 28 $record = [ 29 + '$type' => BskyFeed::Like->value, 30 'subject' => $subject->toArray(), 31 'createdAt' => ($createdAt ?? now())->format('c'), 32 ]; 33 34 + return $this->atp->atproto->repo->createRecord( 35 + collection: BskyFeed::Like, 36 record: $record 37 ); 38 } 39 40 /** ··· 42 * 43 * @requires transition:generic OR (rpc:com.atproto.repo.deleteRecord AND repo:app.bsky.feed.like?action=delete) 44 */ 45 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.deleteRecord')] 46 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.like?action=delete')] 47 + public function delete(string $rkey): DeleteRecordResponse 48 { 49 + return $this->atp->atproto->repo->deleteRecord( 50 + collection: BskyFeed::Like, 51 rkey: $rkey 52 ); 53 } ··· 57 * 58 * @requires transition:generic (rpc:com.atproto.repo.getRecord) 59 */ 60 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.getRecord')] 61 + public function get(string $rkey, ?string $cid = null): Record 62 { 63 $response = $this->atp->atproto->repo->getRecord( 64 repo: $this->atp->client->session()->did(), 65 + collection: BskyFeed::Like, 66 rkey: $rkey, 67 cid: $cid 68 ); 69 70 + return Record::fromArrayRaw($response->toArray()); 71 } 72 }
+49 -165
src/Client/Records/PostRecordClient.php
··· 3 namespace SocialDept\AtpClient\Client\Records; 4 5 use DateTimeInterface; 6 - use SocialDept\AtpClient\Attributes\RequiresScope; 7 use SocialDept\AtpClient\Client\Requests\Request; 8 use SocialDept\AtpClient\Contracts\Recordable; 9 use SocialDept\AtpClient\Data\StrongRef; 10 use SocialDept\AtpClient\Enums\Scope; 11 use SocialDept\AtpClient\RichText\TextBuilder; 12 13 class PostRecordClient extends Request 14 { 15 /** 16 * Create a post 17 * 18 * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.feed.post?action=create) 19 */ 20 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 21 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=create')] 22 public function create( 23 string|array|Recordable $content, 24 ?array $facets = null, ··· 26 ?array $reply = null, 27 ?array $langs = null, 28 ?DateTimeInterface $createdAt = null 29 - ): StrongRef { 30 // Handle different input types 31 if (is_string($content)) { 32 $record = [ ··· 39 $record = $content; 40 } 41 42 - // Add optional fields 43 - if ($embed) { 44 - $record['embed'] = $embed; 45 - } 46 - if ($reply) { 47 - $record['reply'] = $reply; 48 } 49 - if ($langs) { 50 - $record['langs'] = $langs; 51 - } 52 if (! isset($record['createdAt'])) { 53 $record['createdAt'] = ($createdAt ?? now())->format('c'); 54 } 55 56 // Ensure $type is set 57 if (! isset($record['$type'])) { 58 - $record['$type'] = 'app.bsky.feed.post'; 59 } 60 61 - $response = $this->atp->atproto->repo->createRecord( 62 - repo: $this->atp->client->session()->did(), 63 - collection: 'app.bsky.feed.post', 64 record: $record 65 ); 66 - 67 - return StrongRef::fromResponse($response->json()); 68 } 69 70 /** ··· 72 * 73 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.feed.post?action=update) 74 */ 75 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 76 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=update')] 77 - public function update(string $rkey, array $record): StrongRef 78 { 79 // Ensure $type is set 80 if (! isset($record['$type'])) { 81 - $record['$type'] = 'app.bsky.feed.post'; 82 } 83 84 - $response = $this->atp->atproto->repo->putRecord( 85 - repo: $this->atp->client->session()->did(), 86 - collection: 'app.bsky.feed.post', 87 rkey: $rkey, 88 record: $record 89 ); 90 - 91 - return StrongRef::fromResponse($response->json()); 92 } 93 94 /** ··· 96 * 97 * @requires transition:generic OR (rpc:com.atproto.repo.deleteRecord AND repo:app.bsky.feed.post?action=delete) 98 */ 99 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.deleteRecord')] 100 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=delete')] 101 - public function delete(string $rkey): void 102 { 103 - $this->atp->atproto->repo->deleteRecord( 104 - repo: $this->atp->client->session()->did(), 105 - collection: 'app.bsky.feed.post', 106 rkey: $rkey 107 ); 108 } ··· 112 * 113 * @requires transition:generic (rpc:com.atproto.repo.getRecord) 114 */ 115 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.getRecord')] 116 - public function get(string $rkey, ?string $cid = null): array 117 { 118 $response = $this->atp->atproto->repo->getRecord( 119 repo: $this->atp->client->session()->did(), 120 - collection: 'app.bsky.feed.post', 121 rkey: $rkey, 122 cid: $cid 123 ); 124 125 - return $response->json('value'); 126 - } 127 - 128 - /** 129 - * Create a reply to another post 130 - * 131 - * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.feed.post?action=create) 132 - */ 133 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 134 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=create')] 135 - public function reply( 136 - StrongRef $parent, 137 - StrongRef $root, 138 - string|array|Recordable $content, 139 - ?array $facets = null, 140 - ?array $embed = null, 141 - ?array $langs = null, 142 - ?DateTimeInterface $createdAt = null 143 - ): StrongRef { 144 - $reply = [ 145 - 'parent' => $parent->toArray(), 146 - 'root' => $root->toArray(), 147 - ]; 148 - 149 - return $this->create( 150 - content: $content, 151 - facets: $facets, 152 - embed: $embed, 153 - reply: $reply, 154 - langs: $langs, 155 - createdAt: $createdAt 156 - ); 157 - } 158 - 159 - /** 160 - * Create a quote post (post with embedded post) 161 - * 162 - * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.feed.post?action=create) 163 - */ 164 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 165 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=create')] 166 - public function quote( 167 - StrongRef $quotedPost, 168 - string|array|Recordable $content, 169 - ?array $facets = null, 170 - ?array $langs = null, 171 - ?DateTimeInterface $createdAt = null 172 - ): StrongRef { 173 - $embed = [ 174 - '$type' => 'app.bsky.embed.record', 175 - 'record' => $quotedPost->toArray(), 176 - ]; 177 - 178 - return $this->create( 179 - content: $content, 180 - facets: $facets, 181 - embed: $embed, 182 - langs: $langs, 183 - createdAt: $createdAt 184 - ); 185 } 186 187 - /** 188 - * Create a post with images 189 - * 190 - * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.feed.post?action=create) 191 - */ 192 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 193 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=create')] 194 - public function withImages( 195 - string|array|Recordable $content, 196 - array $images, 197 - ?array $facets = null, 198 - ?array $langs = null, 199 - ?DateTimeInterface $createdAt = null 200 - ): StrongRef { 201 - $embed = [ 202 - '$type' => 'app.bsky.embed.images', 203 - 'images' => $images, 204 - ]; 205 - 206 - return $this->create( 207 - content: $content, 208 - facets: $facets, 209 - embed: $embed, 210 - langs: $langs, 211 - createdAt: $createdAt 212 - ); 213 - } 214 - 215 - /** 216 - * Create a post with external link embed 217 - * 218 - * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.feed.post?action=create) 219 - */ 220 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 221 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=create')] 222 - public function withLink( 223 - string|array|Recordable $content, 224 - string $uri, 225 - string $title, 226 - string $description, 227 - ?string $thumbBlob = null, 228 - ?array $facets = null, 229 - ?array $langs = null, 230 - ?DateTimeInterface $createdAt = null 231 - ): StrongRef { 232 - $external = [ 233 - 'uri' => $uri, 234 - 'title' => $title, 235 - 'description' => $description, 236 - ]; 237 - 238 - if ($thumbBlob) { 239 - $external['thumb'] = $thumbBlob; 240 - } 241 - 242 - $embed = [ 243 - '$type' => 'app.bsky.embed.external', 244 - 'external' => $external, 245 - ]; 246 - 247 - return $this->create( 248 - content: $content, 249 - facets: $facets, 250 - embed: $embed, 251 - langs: $langs, 252 - createdAt: $createdAt 253 - ); 254 - } 255 }
··· 3 namespace SocialDept\AtpClient\Client\Records; 4 5 use DateTimeInterface; 6 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 7 + use SocialDept\AtpClient\Builders\PostBuilder; 8 use SocialDept\AtpClient\Client\Requests\Request; 9 use SocialDept\AtpClient\Contracts\Recordable; 10 + use SocialDept\AtpClient\Data\Record; 11 + use SocialDept\AtpClient\Data\Responses\Atproto\Repo\CreateRecordResponse; 12 + use SocialDept\AtpClient\Data\Responses\Atproto\Repo\DeleteRecordResponse; 13 + use SocialDept\AtpClient\Data\Responses\Atproto\Repo\PutRecordResponse; 14 use SocialDept\AtpClient\Data\StrongRef; 15 + use SocialDept\AtpClient\Enums\Nsid\BskyFeed; 16 use SocialDept\AtpClient\Enums\Scope; 17 use SocialDept\AtpClient\RichText\TextBuilder; 18 + use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\PostView; 19 20 class PostRecordClient extends Request 21 { 22 /** 23 + * Create a new post builder bound to this client 24 + */ 25 + public function build(): PostBuilder 26 + { 27 + return PostBuilder::make()->for($this); 28 + } 29 + 30 + /** 31 * Create a post 32 * 33 * @requires transition:generic OR (rpc:com.atproto.repo.createRecord AND repo:app.bsky.feed.post?action=create) 34 */ 35 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.createRecord')] 36 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=create')] 37 public function create( 38 string|array|Recordable $content, 39 ?array $facets = null, ··· 41 ?array $reply = null, 42 ?array $langs = null, 43 ?DateTimeInterface $createdAt = null 44 + ): CreateRecordResponse { 45 // Handle different input types 46 if (is_string($content)) { 47 $record = [ ··· 54 $record = $content; 55 } 56 57 + // Add optional fields (only for non-Recordable inputs) 58 + if (! ($content instanceof Recordable)) { 59 + if ($embed) { 60 + $record['embed'] = $embed; 61 + } 62 + if ($reply) { 63 + $record['reply'] = $reply; 64 + } 65 + if ($langs) { 66 + $record['langs'] = $langs; 67 + } 68 } 69 + 70 if (! isset($record['createdAt'])) { 71 $record['createdAt'] = ($createdAt ?? now())->format('c'); 72 } 73 74 // Ensure $type is set 75 if (! isset($record['$type'])) { 76 + $record['$type'] = BskyFeed::Post->value; 77 } 78 79 + return $this->atp->atproto->repo->createRecord( 80 + collection: BskyFeed::Post, 81 record: $record 82 ); 83 } 84 85 /** ··· 87 * 88 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.feed.post?action=update) 89 */ 90 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 91 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=update')] 92 + public function update(string $rkey, array $record): PutRecordResponse 93 { 94 // Ensure $type is set 95 if (! isset($record['$type'])) { 96 + $record['$type'] = BskyFeed::Post->value; 97 } 98 99 + return $this->atp->atproto->repo->putRecord( 100 + collection: BskyFeed::Post, 101 rkey: $rkey, 102 record: $record 103 ); 104 } 105 106 /** ··· 108 * 109 * @requires transition:generic OR (rpc:com.atproto.repo.deleteRecord AND repo:app.bsky.feed.post?action=delete) 110 */ 111 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.deleteRecord')] 112 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.feed.post?action=delete')] 113 + public function delete(string $rkey): DeleteRecordResponse 114 { 115 + return $this->atp->atproto->repo->deleteRecord( 116 + collection: BskyFeed::Post, 117 rkey: $rkey 118 ); 119 } ··· 123 * 124 * @requires transition:generic (rpc:com.atproto.repo.getRecord) 125 */ 126 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.getRecord')] 127 + public function get(string $rkey, ?string $cid = null): Record 128 { 129 $response = $this->atp->atproto->repo->getRecord( 130 repo: $this->atp->client->session()->did(), 131 + collection: BskyFeed::Post, 132 rkey: $rkey, 133 cid: $cid 134 ); 135 136 + return Record::fromArrayRaw($response->toArray()); 137 } 138 139 }
+28 -29
src/Client/Records/ProfileRecordClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Records; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 - use SocialDept\AtpClient\Data\StrongRef; 8 use SocialDept\AtpClient\Enums\Scope; 9 10 class ProfileRecordClient extends Request ··· 14 * 15 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 16 */ 17 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 18 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 19 - public function update(array $profile): StrongRef 20 { 21 // Ensure $type is set 22 if (! isset($profile['$type'])) { 23 - $profile['$type'] = 'app.bsky.actor.profile'; 24 } 25 26 - $response = $this->atp->atproto->repo->putRecord( 27 - repo: $this->atp->client->session()->did(), 28 - collection: 'app.bsky.actor.profile', 29 rkey: 'self', // Profile records always use 'self' as rkey 30 record: $profile 31 ); 32 - 33 - return StrongRef::fromResponse($response->json()); 34 } 35 36 /** ··· 38 * 39 * @requires transition:generic (rpc:com.atproto.repo.getRecord) 40 */ 41 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.getRecord')] 42 - public function get(): array 43 { 44 $response = $this->atp->atproto->repo->getRecord( 45 repo: $this->atp->client->session()->did(), 46 - collection: 'app.bsky.actor.profile', 47 rkey: 'self' 48 ); 49 50 - return $response->json('value'); 51 } 52 53 /** ··· 55 * 56 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 57 */ 58 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 59 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 60 - public function updateDisplayName(string $displayName): StrongRef 61 { 62 $profile = $this->getOrCreateProfile(); 63 $profile['displayName'] = $displayName; ··· 70 * 71 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 72 */ 73 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 74 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 75 - public function updateDescription(string $description): StrongRef 76 { 77 $profile = $this->getOrCreateProfile(); 78 $profile['description'] = $description; ··· 85 * 86 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 87 */ 88 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 89 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 90 - public function updateAvatar(array $avatarBlob): StrongRef 91 { 92 $profile = $this->getOrCreateProfile(); 93 $profile['avatar'] = $avatarBlob; ··· 100 * 101 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 102 */ 103 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 104 - #[RequiresScope(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 105 - public function updateBanner(array $bannerBlob): StrongRef 106 { 107 $profile = $this->getOrCreateProfile(); 108 $profile['banner'] = $bannerBlob; ··· 116 protected function getOrCreateProfile(): array 117 { 118 try { 119 - return $this->get(); 120 } catch (\Exception $e) { 121 // Profile doesn't exist, return empty structure 122 return [ 123 - '$type' => 'app.bsky.actor.profile', 124 ]; 125 } 126 }
··· 2 3 namespace SocialDept\AtpClient\Client\Records; 4 5 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 + use SocialDept\AtpClient\Data\Record; 8 + use SocialDept\AtpClient\Data\Responses\Atproto\Repo\PutRecordResponse; 9 + use SocialDept\AtpClient\Enums\Nsid\BskyActor; 10 use SocialDept\AtpClient\Enums\Scope; 11 12 class ProfileRecordClient extends Request ··· 16 * 17 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 18 */ 19 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 20 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 21 + public function update(array $profile): PutRecordResponse 22 { 23 // Ensure $type is set 24 if (! isset($profile['$type'])) { 25 + $profile['$type'] = BskyActor::Profile->value; 26 } 27 28 + return $this->atp->atproto->repo->putRecord( 29 + collection: BskyActor::Profile, 30 rkey: 'self', // Profile records always use 'self' as rkey 31 record: $profile 32 ); 33 } 34 35 /** ··· 37 * 38 * @requires transition:generic (rpc:com.atproto.repo.getRecord) 39 */ 40 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.getRecord')] 41 + public function get(): Record 42 { 43 $response = $this->atp->atproto->repo->getRecord( 44 repo: $this->atp->client->session()->did(), 45 + collection: BskyActor::Profile, 46 rkey: 'self' 47 ); 48 49 + return Record::fromArrayRaw($response->toArray()); 50 } 51 52 /** ··· 54 * 55 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 56 */ 57 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 58 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 59 + public function updateDisplayName(string $displayName): PutRecordResponse 60 { 61 $profile = $this->getOrCreateProfile(); 62 $profile['displayName'] = $displayName; ··· 69 * 70 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 71 */ 72 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 73 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 74 + public function updateDescription(string $description): PutRecordResponse 75 { 76 $profile = $this->getOrCreateProfile(); 77 $profile['description'] = $description; ··· 84 * 85 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 86 */ 87 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 88 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 89 + public function updateAvatar(array $avatarBlob): PutRecordResponse 90 { 91 $profile = $this->getOrCreateProfile(); 92 $profile['avatar'] = $avatarBlob; ··· 99 * 100 * @requires transition:generic OR (rpc:com.atproto.repo.putRecord AND repo:app.bsky.actor.profile?action=update) 101 */ 102 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.putRecord')] 103 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'repo:app.bsky.actor.profile?action=update')] 104 + public function updateBanner(array $bannerBlob): PutRecordResponse 105 { 106 $profile = $this->getOrCreateProfile(); 107 $profile['banner'] = $bannerBlob; ··· 115 protected function getOrCreateProfile(): array 116 { 117 try { 118 + return $this->get()->value; 119 } catch (\Exception $e) { 120 // Profile doesn't exist, return empty structure 121 return [ 122 + '$type' => BskyActor::Profile->value, 123 ]; 124 } 125 }
+14 -10
src/Client/Requests/Atproto/IdentityRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Atproto; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Enums\Scope; 8 9 class IdentityRequestClient extends Request ··· 11 /** 12 * Resolve handle to DID 13 * 14 - * @requires transition:generic (rpc:com.atproto.identity.resolveHandle) 15 - * 16 * @see https://docs.bsky.app/docs/api/com-atproto-identity-resolve-handle 17 */ 18 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.identity.resolveHandle')] 19 - public function resolveHandle(string $handle): string 20 { 21 $response = $this->atp->client->get( 22 - endpoint: 'com.atproto.identity.resolveHandle', 23 params: compact('handle') 24 ); 25 26 - return $response->json()['did']; 27 } 28 29 /** ··· 33 * 34 * @see https://docs.bsky.app/docs/api/com-atproto-identity-update-handle 35 */ 36 - #[RequiresScope(Scope::Atproto, granular: 'identity:handle')] 37 - public function updateHandle(string $handle): void 38 { 39 $this->atp->client->post( 40 - endpoint: 'com.atproto.identity.updateHandle', 41 body: compact('handle') 42 ); 43 } 44 }
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Atproto; 4 5 + use SocialDept\AtpClient\Attributes\PublicEndpoint; 6 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 7 use SocialDept\AtpClient\Client\Requests\Request; 8 + use SocialDept\AtpClient\Data\Responses\Atproto\Identity\ResolveHandleResponse; 9 + use SocialDept\AtpClient\Data\Responses\EmptyResponse; 10 + use SocialDept\AtpClient\Enums\Nsid\AtprotoIdentity; 11 use SocialDept\AtpClient\Enums\Scope; 12 13 class IdentityRequestClient extends Request ··· 15 /** 16 * Resolve handle to DID 17 * 18 * @see https://docs.bsky.app/docs/api/com-atproto-identity-resolve-handle 19 */ 20 + #[PublicEndpoint] 21 + public function resolveHandle(string $handle): ResolveHandleResponse 22 { 23 $response = $this->atp->client->get( 24 + endpoint: AtprotoIdentity::ResolveHandle, 25 params: compact('handle') 26 ); 27 28 + return ResolveHandleResponse::fromArray($response->json()); 29 } 30 31 /** ··· 35 * 36 * @see https://docs.bsky.app/docs/api/com-atproto-identity-update-handle 37 */ 38 + #[ScopedEndpoint(Scope::Atproto, granular: 'identity:handle')] 39 + public function updateHandle(string $handle): EmptyResponse 40 { 41 $this->atp->client->post( 42 + endpoint: AtprotoIdentity::UpdateHandle, 43 body: compact('handle') 44 ); 45 + 46 + return new EmptyResponse; 47 } 48 }
+31 -29
src/Client/Requests/Atproto/RepoRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Atproto; 4 5 use Illuminate\Http\UploadedFile; 6 use InvalidArgumentException; 7 - use SocialDept\AtpClient\Attributes\RequiresScope; 8 use SocialDept\AtpClient\Auth\ScopeChecker; 9 use SocialDept\AtpClient\Client\Requests\Request; 10 use SocialDept\AtpClient\Data\Responses\Atproto\Repo\CreateRecordResponse; ··· 13 use SocialDept\AtpClient\Data\Responses\Atproto\Repo\GetRecordResponse; 14 use SocialDept\AtpClient\Data\Responses\Atproto\Repo\ListRecordsResponse; 15 use SocialDept\AtpClient\Data\Responses\Atproto\Repo\PutRecordResponse; 16 use SocialDept\AtpClient\Enums\Scope; 17 use SocialDept\AtpSchema\Data\BlobReference; 18 use SplFileInfo; ··· 27 * 28 * @see https://docs.bsky.app/docs/api/com-atproto-repo-create-record 29 */ 30 - #[RequiresScope(Scope::TransitionGeneric, description: 'Create records in repository')] 31 public function createRecord( 32 - string $repo, 33 - string $collection, 34 array $record, 35 ?string $rkey = null, 36 bool $validate = true, 37 ?string $swapCommit = null 38 ): CreateRecordResponse { 39 $this->checkCollectionScope($collection, 'create'); 40 41 $response = $this->atp->client->post( 42 - endpoint: 'com.atproto.repo.createRecord', 43 body: array_filter( 44 compact('repo', 'collection', 'record', 'rkey', 'validate', 'swapCommit'), 45 fn ($v) => ! is_null($v) ··· 56 * 57 * @see https://docs.bsky.app/docs/api/com-atproto-repo-delete-record 58 */ 59 - #[RequiresScope(Scope::TransitionGeneric, description: 'Delete records from repository')] 60 public function deleteRecord( 61 - string $repo, 62 - string $collection, 63 string $rkey, 64 ?string $swapRecord = null, 65 ?string $swapCommit = null 66 ): DeleteRecordResponse { 67 $this->checkCollectionScope($collection, 'delete'); 68 69 $response = $this->atp->client->post( 70 - endpoint: 'com.atproto.repo.deleteRecord', 71 body: array_filter( 72 compact('repo', 'collection', 'rkey', 'swapRecord', 'swapCommit'), 73 fn ($v) => ! is_null($v) ··· 84 * 85 * @see https://docs.bsky.app/docs/api/com-atproto-repo-put-record 86 */ 87 - #[RequiresScope(Scope::TransitionGeneric, description: 'Update records in repository')] 88 public function putRecord( 89 - string $repo, 90 - string $collection, 91 string $rkey, 92 array $record, 93 bool $validate = true, 94 ?string $swapRecord = null, 95 ?string $swapCommit = null 96 ): PutRecordResponse { 97 $this->checkCollectionScope($collection, 'update'); 98 99 $response = $this->atp->client->post( 100 - endpoint: 'com.atproto.repo.putRecord', 101 body: array_filter( 102 compact('repo', 'collection', 'rkey', 'record', 'validate', 'swapRecord', 'swapCommit'), 103 fn ($v) => ! is_null($v) ··· 110 /** 111 * Get a record 112 * 113 - * @requires transition:generic (rpc:com.atproto.repo.getRecord) 114 - * 115 * @see https://docs.bsky.app/docs/api/com-atproto-repo-get-record 116 */ 117 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.getRecord')] 118 public function getRecord( 119 string $repo, 120 - string $collection, 121 string $rkey, 122 ?string $cid = null 123 ): GetRecordResponse { 124 $response = $this->atp->client->get( 125 - endpoint: 'com.atproto.repo.getRecord', 126 params: compact('repo', 'collection', 'rkey', 'cid') 127 ); 128 ··· 132 /** 133 * List records in a collection 134 * 135 - * @requires transition:generic (rpc:com.atproto.repo.listRecords) 136 - * 137 * @see https://docs.bsky.app/docs/api/com-atproto-repo-list-records 138 */ 139 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.listRecords')] 140 public function listRecords( 141 string $repo, 142 - string $collection, 143 int $limit = 50, 144 ?string $cursor = null, 145 bool $reverse = false 146 ): ListRecordsResponse { 147 $response = $this->atp->client->get( 148 - endpoint: 'com.atproto.repo.listRecords', 149 params: compact('repo', 'collection', 'limit', 'cursor', 'reverse') 150 ); 151 ··· 166 * 167 * @see https://docs.bsky.app/docs/api/com-atproto-repo-upload-blob 168 */ 169 - #[RequiresScope(Scope::TransitionGeneric, granular: 'blob:*/*')] 170 public function uploadBlob(UploadedFile|SplFileInfo|string $file, ?string $mimeType = null): BlobReference 171 { 172 // Handle different input types ··· 182 } 183 184 $response = $this->atp->client->postBlob( 185 - endpoint: 'com.atproto.repo.uploadBlob', 186 data: $data, 187 mimeType: $mimeType 188 ); ··· 193 /** 194 * Describe the repository 195 * 196 - * @requires transition:generic (rpc:com.atproto.repo.describeRepo) 197 - * 198 * @see https://docs.bsky.app/docs/api/com-atproto-repo-describe-repo 199 */ 200 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:com.atproto.repo.describeRepo')] 201 public function describeRepo(string $repo): DescribeRepoResponse 202 { 203 $response = $this->atp->client->get( 204 - endpoint: 'com.atproto.repo.describeRepo', 205 params: compact('repo') 206 ); 207
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Atproto; 4 5 + use BackedEnum; 6 use Illuminate\Http\UploadedFile; 7 use InvalidArgumentException; 8 + use SocialDept\AtpClient\Attributes\PublicEndpoint; 9 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 10 use SocialDept\AtpClient\Auth\ScopeChecker; 11 use SocialDept\AtpClient\Client\Requests\Request; 12 use SocialDept\AtpClient\Data\Responses\Atproto\Repo\CreateRecordResponse; ··· 15 use SocialDept\AtpClient\Data\Responses\Atproto\Repo\GetRecordResponse; 16 use SocialDept\AtpClient\Data\Responses\Atproto\Repo\ListRecordsResponse; 17 use SocialDept\AtpClient\Data\Responses\Atproto\Repo\PutRecordResponse; 18 + use SocialDept\AtpClient\Enums\Nsid\AtprotoRepo; 19 use SocialDept\AtpClient\Enums\Scope; 20 use SocialDept\AtpSchema\Data\BlobReference; 21 use SplFileInfo; ··· 30 * 31 * @see https://docs.bsky.app/docs/api/com-atproto-repo-create-record 32 */ 33 + #[ScopedEndpoint(Scope::TransitionGeneric, description: 'Create records in repository')] 34 public function createRecord( 35 + string|BackedEnum $collection, 36 array $record, 37 ?string $rkey = null, 38 bool $validate = true, 39 ?string $swapCommit = null 40 ): CreateRecordResponse { 41 + $repo = $this->atp->client->session()->did(); 42 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 43 $this->checkCollectionScope($collection, 'create'); 44 45 $response = $this->atp->client->post( 46 + endpoint: AtprotoRepo::CreateRecord, 47 body: array_filter( 48 compact('repo', 'collection', 'record', 'rkey', 'validate', 'swapCommit'), 49 fn ($v) => ! is_null($v) ··· 60 * 61 * @see https://docs.bsky.app/docs/api/com-atproto-repo-delete-record 62 */ 63 + #[ScopedEndpoint(Scope::TransitionGeneric, description: 'Delete records from repository')] 64 public function deleteRecord( 65 + string|BackedEnum $collection, 66 string $rkey, 67 ?string $swapRecord = null, 68 ?string $swapCommit = null 69 ): DeleteRecordResponse { 70 + $repo = $this->atp->client->session()->did(); 71 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 72 $this->checkCollectionScope($collection, 'delete'); 73 74 $response = $this->atp->client->post( 75 + endpoint: AtprotoRepo::DeleteRecord, 76 body: array_filter( 77 compact('repo', 'collection', 'rkey', 'swapRecord', 'swapCommit'), 78 fn ($v) => ! is_null($v) ··· 89 * 90 * @see https://docs.bsky.app/docs/api/com-atproto-repo-put-record 91 */ 92 + #[ScopedEndpoint(Scope::TransitionGeneric, description: 'Update records in repository')] 93 public function putRecord( 94 + string|BackedEnum $collection, 95 string $rkey, 96 array $record, 97 bool $validate = true, 98 ?string $swapRecord = null, 99 ?string $swapCommit = null 100 ): PutRecordResponse { 101 + $repo = $this->atp->client->session()->did(); 102 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 103 $this->checkCollectionScope($collection, 'update'); 104 105 $response = $this->atp->client->post( 106 + endpoint: AtprotoRepo::PutRecord, 107 body: array_filter( 108 compact('repo', 'collection', 'rkey', 'record', 'validate', 'swapRecord', 'swapCommit'), 109 fn ($v) => ! is_null($v) ··· 116 /** 117 * Get a record 118 * 119 * @see https://docs.bsky.app/docs/api/com-atproto-repo-get-record 120 */ 121 + #[PublicEndpoint] 122 public function getRecord( 123 string $repo, 124 + string|BackedEnum $collection, 125 string $rkey, 126 ?string $cid = null 127 ): GetRecordResponse { 128 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 129 $response = $this->atp->client->get( 130 + endpoint: AtprotoRepo::GetRecord, 131 params: compact('repo', 'collection', 'rkey', 'cid') 132 ); 133 ··· 137 /** 138 * List records in a collection 139 * 140 * @see https://docs.bsky.app/docs/api/com-atproto-repo-list-records 141 */ 142 + #[PublicEndpoint] 143 public function listRecords( 144 string $repo, 145 + string|BackedEnum $collection, 146 int $limit = 50, 147 ?string $cursor = null, 148 bool $reverse = false 149 ): ListRecordsResponse { 150 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 151 $response = $this->atp->client->get( 152 + endpoint: AtprotoRepo::ListRecords, 153 params: compact('repo', 'collection', 'limit', 'cursor', 'reverse') 154 ); 155 ··· 170 * 171 * @see https://docs.bsky.app/docs/api/com-atproto-repo-upload-blob 172 */ 173 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'blob:*/*')] 174 public function uploadBlob(UploadedFile|SplFileInfo|string $file, ?string $mimeType = null): BlobReference 175 { 176 // Handle different input types ··· 186 } 187 188 $response = $this->atp->client->postBlob( 189 + endpoint: AtprotoRepo::UploadBlob, 190 data: $data, 191 mimeType: $mimeType 192 ); ··· 197 /** 198 * Describe the repository 199 * 200 * @see https://docs.bsky.app/docs/api/com-atproto-repo-describe-repo 201 */ 202 + #[PublicEndpoint] 203 public function describeRepo(string $repo): DescribeRepoResponse 204 { 205 $response = $this->atp->client->get( 206 + endpoint: AtprotoRepo::DescribeRepo, 207 params: compact('repo') 208 ); 209
+7 -7
src/Client/Requests/Atproto/ServerRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Atproto; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Atproto\Server\DescribeServerResponse; 8 use SocialDept\AtpClient\Data\Responses\Atproto\Server\GetSessionResponse; 9 use SocialDept\AtpClient\Enums\Scope; 10 11 class ServerRequestClient extends Request ··· 17 * 18 * @see https://docs.bsky.app/docs/api/com-atproto-server-get-session 19 */ 20 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.server.getSession')] 21 public function getSession(): GetSessionResponse 22 { 23 $response = $this->atp->client->get( 24 - endpoint: 'com.atproto.server.getSession' 25 ); 26 27 return GetSessionResponse::fromArray($response->json()); ··· 30 /** 31 * Describe server 32 * 33 - * @requires atproto (rpc:com.atproto.server.describeServer) 34 - * 35 * @see https://docs.bsky.app/docs/api/com-atproto-server-describe-server 36 */ 37 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.server.describeServer')] 38 public function describeServer(): DescribeServerResponse 39 { 40 $response = $this->atp->client->get( 41 - endpoint: 'com.atproto.server.describeServer' 42 ); 43 44 return DescribeServerResponse::fromArray($response->json());
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Atproto; 4 5 + use SocialDept\AtpClient\Attributes\PublicEndpoint; 6 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 7 use SocialDept\AtpClient\Client\Requests\Request; 8 use SocialDept\AtpClient\Data\Responses\Atproto\Server\DescribeServerResponse; 9 use SocialDept\AtpClient\Data\Responses\Atproto\Server\GetSessionResponse; 10 + use SocialDept\AtpClient\Enums\Nsid\AtprotoServer; 11 use SocialDept\AtpClient\Enums\Scope; 12 13 class ServerRequestClient extends Request ··· 19 * 20 * @see https://docs.bsky.app/docs/api/com-atproto-server-get-session 21 */ 22 + #[ScopedEndpoint(Scope::Atproto, granular: 'rpc:com.atproto.server.getSession')] 23 public function getSession(): GetSessionResponse 24 { 25 $response = $this->atp->client->get( 26 + endpoint: AtprotoServer::GetSession 27 ); 28 29 return GetSessionResponse::fromArray($response->json()); ··· 32 /** 33 * Describe server 34 * 35 * @see https://docs.bsky.app/docs/api/com-atproto-server-describe-server 36 */ 37 + #[PublicEndpoint] 38 public function describeServer(): DescribeServerResponse 39 { 40 $response = $this->atp->client->get( 41 + endpoint: AtprotoServer::DescribeServer 42 ); 43 44 return DescribeServerResponse::fromArray($response->json());
+44 -35
src/Client/Requests/Atproto/SyncRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Atproto; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Atproto\Sync\GetRepoStatusResponse; 8 use SocialDept\AtpClient\Data\Responses\Atproto\Sync\ListBlobsResponse; 9 use SocialDept\AtpClient\Data\Responses\Atproto\Sync\ListReposResponse; 10 - use SocialDept\AtpClient\Enums\Scope; 11 use SocialDept\AtpClient\Http\Response; 12 use SocialDept\AtpSchema\Generated\Com\Atproto\Repo\Defs\CommitMeta; 13 ··· 16 /** 17 * Get a blob associated with a given account 18 * 19 - * @requires atproto (rpc:com.atproto.sync.getBlob) 20 - * 21 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-blob 22 */ 23 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.sync.getBlob')] 24 public function getBlob(string $did, string $cid): Response 25 { 26 return $this->atp->client->get( 27 - endpoint: 'com.atproto.sync.getBlob', 28 params: compact('did', 'cid') 29 ); 30 } ··· 32 /** 33 * Download a repository export as CAR file 34 * 35 - * @requires atproto (rpc:com.atproto.sync.getRepo) 36 - * 37 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-repo 38 */ 39 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.sync.getRepo')] 40 public function getRepo(string $did, ?string $since = null): Response 41 { 42 return $this->atp->client->get( 43 - endpoint: 'com.atproto.sync.getRepo', 44 params: compact('did', 'since') 45 ); 46 } 47 48 /** 49 * Enumerates all the DID, rev, and commit CID for all repos hosted by this service 50 - * 51 - * @requires atproto (rpc:com.atproto.sync.listRepos) 52 * 53 * @see https://docs.bsky.app/docs/api/com-atproto-sync-list-repos 54 */ 55 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.sync.listRepos')] 56 public function listRepos(int $limit = 500, ?string $cursor = null): ListReposResponse 57 { 58 $response = $this->atp->client->get( 59 - endpoint: 'com.atproto.sync.listRepos', 60 params: compact('limit', 'cursor') 61 ); 62 ··· 64 } 65 66 /** 67 * Get the current commit CID & revision of the specified repo 68 * 69 - * @requires atproto (rpc:com.atproto.sync.getLatestCommit) 70 - * 71 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-latest-commit 72 */ 73 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.sync.getLatestCommit')] 74 public function getLatestCommit(string $did): CommitMeta 75 { 76 $response = $this->atp->client->get( 77 - endpoint: 'com.atproto.sync.getLatestCommit', 78 params: compact('did') 79 ); 80 ··· 84 /** 85 * Get data blocks needed to prove the existence or non-existence of record 86 * 87 - * @requires atproto (rpc:com.atproto.sync.getRecord) 88 - * 89 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-record 90 */ 91 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.sync.getRecord')] 92 - public function getRecord(string $did, string $collection, string $rkey): Response 93 { 94 return $this->atp->client->get( 95 - endpoint: 'com.atproto.sync.getRecord', 96 params: compact('did', 'collection', 'rkey') 97 ); 98 } ··· 100 /** 101 * List blob CIDs for an account, since some repo revision 102 * 103 - * @requires atproto (rpc:com.atproto.sync.listBlobs) 104 - * 105 * @see https://docs.bsky.app/docs/api/com-atproto-sync-list-blobs 106 */ 107 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.sync.listBlobs')] 108 public function listBlobs( 109 string $did, 110 ?string $since = null, ··· 112 ?string $cursor = null 113 ): ListBlobsResponse { 114 $response = $this->atp->client->get( 115 - endpoint: 'com.atproto.sync.listBlobs', 116 params: compact('did', 'since', 'limit', 'cursor') 117 ); 118 ··· 122 /** 123 * Get data blocks from a given repo, by CID 124 * 125 - * @requires atproto (rpc:com.atproto.sync.getBlocks) 126 - * 127 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-blocks 128 */ 129 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.sync.getBlocks')] 130 public function getBlocks(string $did, array $cids): Response 131 { 132 return $this->atp->client->get( 133 - endpoint: 'com.atproto.sync.getBlocks', 134 params: compact('did', 'cids') 135 ); 136 } 137 138 /** 139 * Get the hosting status for a repository, on this server 140 - * 141 - * @requires atproto (rpc:com.atproto.sync.getRepoStatus) 142 * 143 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-repo-status 144 */ 145 - #[RequiresScope(Scope::Atproto, granular: 'rpc:com.atproto.sync.getRepoStatus')] 146 public function getRepoStatus(string $did): GetRepoStatusResponse 147 { 148 $response = $this->atp->client->get( 149 - endpoint: 'com.atproto.sync.getRepoStatus', 150 params: compact('did') 151 ); 152
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Atproto; 4 5 + use BackedEnum; 6 + use SocialDept\AtpClient\Attributes\PublicEndpoint; 7 use SocialDept\AtpClient\Client\Requests\Request; 8 use SocialDept\AtpClient\Data\Responses\Atproto\Sync\GetRepoStatusResponse; 9 use SocialDept\AtpClient\Data\Responses\Atproto\Sync\ListBlobsResponse; 10 + use SocialDept\AtpClient\Data\Responses\Atproto\Sync\ListReposByCollectionResponse; 11 use SocialDept\AtpClient\Data\Responses\Atproto\Sync\ListReposResponse; 12 + use SocialDept\AtpClient\Enums\Nsid\AtprotoSync; 13 use SocialDept\AtpClient\Http\Response; 14 use SocialDept\AtpSchema\Generated\Com\Atproto\Repo\Defs\CommitMeta; 15 ··· 18 /** 19 * Get a blob associated with a given account 20 * 21 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-blob 22 */ 23 + #[PublicEndpoint] 24 public function getBlob(string $did, string $cid): Response 25 { 26 return $this->atp->client->get( 27 + endpoint: AtprotoSync::GetBlob, 28 params: compact('did', 'cid') 29 ); 30 } ··· 32 /** 33 * Download a repository export as CAR file 34 * 35 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-repo 36 */ 37 + #[PublicEndpoint] 38 public function getRepo(string $did, ?string $since = null): Response 39 { 40 return $this->atp->client->get( 41 + endpoint: AtprotoSync::GetRepo, 42 params: compact('did', 'since') 43 ); 44 } 45 46 /** 47 * Enumerates all the DID, rev, and commit CID for all repos hosted by this service 48 * 49 * @see https://docs.bsky.app/docs/api/com-atproto-sync-list-repos 50 */ 51 + #[PublicEndpoint] 52 public function listRepos(int $limit = 500, ?string $cursor = null): ListReposResponse 53 { 54 $response = $this->atp->client->get( 55 + endpoint: AtprotoSync::ListRepos, 56 params: compact('limit', 'cursor') 57 ); 58 ··· 60 } 61 62 /** 63 + * Enumerates all the DIDs with records in a specific collection 64 + * 65 + * @see https://docs.bsky.app/docs/api/com-atproto-sync-list-repos-by-collection 66 + */ 67 + #[PublicEndpoint] 68 + public function listReposByCollection( 69 + string|BackedEnum $collection, 70 + int $limit = 500, 71 + ?string $cursor = null 72 + ): ListReposByCollectionResponse { 73 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 74 + 75 + $response = $this->atp->client->get( 76 + endpoint: AtprotoSync::ListReposByCollection, 77 + params: compact('collection', 'limit', 'cursor') 78 + ); 79 + 80 + return ListReposByCollectionResponse::fromArray($response->json()); 81 + } 82 + 83 + /** 84 * Get the current commit CID & revision of the specified repo 85 * 86 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-latest-commit 87 */ 88 + #[PublicEndpoint] 89 public function getLatestCommit(string $did): CommitMeta 90 { 91 $response = $this->atp->client->get( 92 + endpoint: AtprotoSync::GetLatestCommit, 93 params: compact('did') 94 ); 95 ··· 99 /** 100 * Get data blocks needed to prove the existence or non-existence of record 101 * 102 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-record 103 */ 104 + #[PublicEndpoint] 105 + public function getRecord(string $did, string|BackedEnum $collection, string $rkey): Response 106 { 107 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 108 + 109 return $this->atp->client->get( 110 + endpoint: AtprotoSync::GetRecord, 111 params: compact('did', 'collection', 'rkey') 112 ); 113 } ··· 115 /** 116 * List blob CIDs for an account, since some repo revision 117 * 118 * @see https://docs.bsky.app/docs/api/com-atproto-sync-list-blobs 119 */ 120 + #[PublicEndpoint] 121 public function listBlobs( 122 string $did, 123 ?string $since = null, ··· 125 ?string $cursor = null 126 ): ListBlobsResponse { 127 $response = $this->atp->client->get( 128 + endpoint: AtprotoSync::ListBlobs, 129 params: compact('did', 'since', 'limit', 'cursor') 130 ); 131 ··· 135 /** 136 * Get data blocks from a given repo, by CID 137 * 138 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-blocks 139 */ 140 + #[PublicEndpoint] 141 public function getBlocks(string $did, array $cids): Response 142 { 143 return $this->atp->client->get( 144 + endpoint: AtprotoSync::GetBlocks, 145 params: compact('did', 'cids') 146 ); 147 } 148 149 /** 150 * Get the hosting status for a repository, on this server 151 * 152 * @see https://docs.bsky.app/docs/api/com-atproto-sync-get-repo-status 153 */ 154 + #[PublicEndpoint] 155 public function getRepoStatus(string $did): GetRepoStatusResponse 156 { 157 $response = $this->atp->client->get( 158 + endpoint: AtprotoSync::GetRepoStatus, 159 params: compact('did') 160 ); 161
+73 -7
src/Client/Requests/Bsky/ActorRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Bsky; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 - use SocialDept\AtpClient\Enums\Scope; 8 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileViewDetailed; 9 10 class ActorRequestClient extends Request 11 { 12 /** 13 * Get actor profile 14 - * 15 - * @requires transition:generic (rpc:app.bsky.actor.getProfile) 16 * 17 * @see https://docs.bsky.app/docs/api/app-bsky-actor-get-profile 18 */ 19 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:app.bsky.actor.getProfile')] 20 public function getProfile(string $actor): ProfileViewDetailed 21 { 22 $response = $this->atp->client->get( 23 - endpoint: 'app.bsky.actor.getProfile', 24 params: compact('actor') 25 ); 26 27 - return ProfileViewDetailed::fromArray($response->json()); 28 } 29 }
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Bsky; 4 5 + use SocialDept\AtpClient\Attributes\PublicEndpoint; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 + use SocialDept\AtpClient\Data\Responses\Bsky\Actor\GetProfilesResponse; 8 + use SocialDept\AtpClient\Data\Responses\Bsky\Actor\GetSuggestionsResponse; 9 + use SocialDept\AtpClient\Data\Responses\Bsky\Actor\SearchActorsResponse; 10 + use SocialDept\AtpClient\Data\Responses\Bsky\Actor\SearchActorsTypeaheadResponse; 11 + use SocialDept\AtpClient\Enums\Nsid\BskyActor; 12 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileViewDetailed; 13 14 class ActorRequestClient extends Request 15 { 16 /** 17 * Get actor profile 18 * 19 * @see https://docs.bsky.app/docs/api/app-bsky-actor-get-profile 20 */ 21 + #[PublicEndpoint] 22 public function getProfile(string $actor): ProfileViewDetailed 23 { 24 $response = $this->atp->client->get( 25 + endpoint: BskyActor::GetProfile, 26 params: compact('actor') 27 ); 28 29 + return ProfileViewDetailed::fromArray($response->toArray()); 30 + } 31 + 32 + /** 33 + * Get multiple actor profiles 34 + * 35 + * @see https://docs.bsky.app/docs/api/app-bsky-actor-get-profiles 36 + */ 37 + #[PublicEndpoint] 38 + public function getProfiles(array $actors): GetProfilesResponse 39 + { 40 + $response = $this->atp->client->get( 41 + endpoint: BskyActor::GetProfiles, 42 + params: compact('actors') 43 + ); 44 + 45 + return GetProfilesResponse::fromArray($response->json()); 46 + } 47 + 48 + /** 49 + * Get suggestions for actors to follow 50 + * 51 + * @see https://docs.bsky.app/docs/api/app-bsky-actor-get-suggestions 52 + */ 53 + #[PublicEndpoint] 54 + public function getSuggestions(int $limit = 50, ?string $cursor = null): GetSuggestionsResponse 55 + { 56 + $response = $this->atp->client->get( 57 + endpoint: BskyActor::GetSuggestions, 58 + params: compact('limit', 'cursor') 59 + ); 60 + 61 + return GetSuggestionsResponse::fromArray($response->json()); 62 + } 63 + 64 + /** 65 + * Search for actors 66 + * 67 + * @see https://docs.bsky.app/docs/api/app-bsky-actor-search-actors 68 + */ 69 + #[PublicEndpoint] 70 + public function searchActors(string $q, int $limit = 25, ?string $cursor = null): SearchActorsResponse 71 + { 72 + $response = $this->atp->client->get( 73 + endpoint: BskyActor::SearchActors, 74 + params: compact('q', 'limit', 'cursor') 75 + ); 76 + 77 + return SearchActorsResponse::fromArray($response->json()); 78 + } 79 + 80 + /** 81 + * Search for actors matching a prefix (typeahead/autocomplete) 82 + * 83 + * @see https://docs.bsky.app/docs/api/app-bsky-actor-search-actors-typeahead 84 + */ 85 + #[PublicEndpoint] 86 + public function searchActorsTypeahead(string $q, int $limit = 10): SearchActorsTypeaheadResponse 87 + { 88 + $response = $this->atp->client->get( 89 + endpoint: BskyActor::SearchActorsTypeahead, 90 + params: compact('q', 'limit') 91 + ); 92 + 93 + return SearchActorsTypeaheadResponse::fromArray($response->json()); 94 } 95 }
+192 -42
src/Client/Requests/Bsky/FeedRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Bsky; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetAuthorFeedResponse; 8 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetLikesResponse; 9 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetPostThreadResponse; 10 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetRepostedByResponse; 11 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetTimelineResponse; 12 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\SearchPostsResponse; 13 use SocialDept\AtpClient\Enums\Scope; 14 15 class FeedRequestClient extends Request 16 { 17 /** 18 - * Get timeline feed 19 * 20 - * @requires transition:generic (rpc:app.bsky.feed.getTimeline) 21 * 22 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-timeline 23 */ 24 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getTimeline')] 25 public function getTimeline(int $limit = 50, ?string $cursor = null): GetTimelineResponse 26 { 27 $response = $this->atp->client->get( 28 - endpoint: 'app.bsky.feed.getTimeline', 29 params: compact('limit', 'cursor') 30 ); 31 ··· 35 /** 36 * Get author feed 37 * 38 - * @requires transition:generic (rpc:app.bsky.feed.getAuthorFeed) 39 - * 40 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-author-feed 41 */ 42 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getAuthorFeed')] 43 public function getAuthorFeed( 44 string $actor, 45 int $limit = 50, 46 - ?string $cursor = null 47 ): GetAuthorFeedResponse { 48 $response = $this->atp->client->get( 49 - endpoint: 'app.bsky.feed.getAuthorFeed', 50 params: compact('actor', 'limit', 'cursor') 51 ); 52 53 - return GetAuthorFeedResponse::fromArray($response->json()); 54 } 55 56 /** 57 - * Get post thread 58 * 59 - * @requires transition:generic (rpc:app.bsky.feed.getPostThread) 60 * 61 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-post-thread 62 */ 63 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getPostThread')] 64 - public function getPostThread(string $uri, int $depth = 6): GetPostThreadResponse 65 { 66 $response = $this->atp->client->get( 67 - endpoint: 'app.bsky.feed.getPostThread', 68 - params: compact('uri', 'depth') 69 ); 70 71 return GetPostThreadResponse::fromArray($response->json()); 72 } 73 74 /** 75 - * Search posts 76 * 77 - * @requires transition:generic (rpc:app.bsky.feed.searchPosts) 78 - * 79 - * @see https://docs.bsky.app/docs/api/app-bsky-feed-search-posts 80 */ 81 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.searchPosts')] 82 - public function searchPosts( 83 - string $q, 84 - int $limit = 25, 85 - ?string $cursor = null 86 - ): SearchPostsResponse { 87 $response = $this->atp->client->get( 88 - endpoint: 'app.bsky.feed.searchPosts', 89 - params: compact('q', 'limit', 'cursor') 90 ); 91 92 - return SearchPostsResponse::fromArray($response->json()); 93 } 94 95 /** 96 * Get likes for a post 97 * 98 - * @requires transition:generic (rpc:app.bsky.feed.getLikes) 99 - * 100 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-likes 101 */ 102 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getLikes')] 103 public function getLikes( 104 string $uri, 105 int $limit = 50, 106 - ?string $cursor = null 107 ): GetLikesResponse { 108 $response = $this->atp->client->get( 109 - endpoint: 'app.bsky.feed.getLikes', 110 - params: compact('uri', 'limit', 'cursor') 111 ); 112 113 return GetLikesResponse::fromArray($response->json()); 114 } 115 116 /** 117 * Get reposts for a post 118 * 119 - * @requires transition:generic (rpc:app.bsky.feed.getRepostedBy) 120 - * 121 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-reposted-by 122 */ 123 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getRepostedBy')] 124 public function getRepostedBy( 125 string $uri, 126 int $limit = 50, 127 - ?string $cursor = null 128 ): GetRepostedByResponse { 129 $response = $this->atp->client->get( 130 - endpoint: 'app.bsky.feed.getRepostedBy', 131 - params: compact('uri', 'limit', 'cursor') 132 ); 133 134 return GetRepostedByResponse::fromArray($response->json()); 135 } 136 }
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Bsky; 4 5 + use SocialDept\AtpClient\Attributes\PublicEndpoint; 6 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 7 use SocialDept\AtpClient\Client\Requests\Request; 8 + use SocialDept\AtpClient\Data\Responses\Bsky\Feed\DescribeFeedGeneratorResponse; 9 + use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetActorFeedsResponse; 10 + use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetActorLikesResponse; 11 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetAuthorFeedResponse; 12 + use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetFeedGeneratorResponse; 13 + use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetFeedGeneratorsResponse; 14 + use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetFeedResponse; 15 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetLikesResponse; 16 + use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetPostsResponse; 17 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetPostThreadResponse; 18 + use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetQuotesResponse; 19 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetRepostedByResponse; 20 + use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetSuggestedFeedsResponse; 21 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\GetTimelineResponse; 22 use SocialDept\AtpClient\Data\Responses\Bsky\Feed\SearchPostsResponse; 23 + use SocialDept\AtpClient\Enums\Nsid\BskyFeed; 24 use SocialDept\AtpClient\Enums\Scope; 25 26 class FeedRequestClient extends Request 27 { 28 /** 29 + * Describe feed generator 30 * 31 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-describe-feed-generator 32 + */ 33 + #[PublicEndpoint] 34 + public function describeFeedGenerator(): DescribeFeedGeneratorResponse 35 + { 36 + $response = $this->atp->client->get( 37 + endpoint: BskyFeed::DescribeFeedGenerator 38 + ); 39 + 40 + return DescribeFeedGeneratorResponse::fromArray($response->json()); 41 + } 42 + 43 + /** 44 + * Get timeline feed (requires authentication) 45 * 46 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-timeline 47 */ 48 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getTimeline')] 49 public function getTimeline(int $limit = 50, ?string $cursor = null): GetTimelineResponse 50 { 51 $response = $this->atp->client->get( 52 + endpoint: BskyFeed::GetTimeline, 53 params: compact('limit', 'cursor') 54 ); 55 ··· 59 /** 60 * Get author feed 61 * 62 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-author-feed 63 */ 64 + #[PublicEndpoint] 65 public function getAuthorFeed( 66 string $actor, 67 int $limit = 50, 68 + ?string $cursor = null, 69 + ?string $filter = null 70 ): GetAuthorFeedResponse { 71 $response = $this->atp->client->get( 72 + endpoint: BskyFeed::GetAuthorFeed, 73 + params: compact('actor', 'limit', 'cursor', 'filter') 74 + ); 75 + 76 + return GetAuthorFeedResponse::fromArray($response->json()); 77 + } 78 + 79 + /** 80 + * Get feeds created by an actor 81 + * 82 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-actor-feeds 83 + */ 84 + #[PublicEndpoint] 85 + public function getActorFeeds(string $actor, int $limit = 50, ?string $cursor = null): GetActorFeedsResponse 86 + { 87 + $response = $this->atp->client->get( 88 + endpoint: BskyFeed::GetActorFeeds, 89 + params: compact('actor', 'limit', 'cursor') 90 + ); 91 + 92 + return GetActorFeedsResponse::fromArray($response->json()); 93 + } 94 + 95 + /** 96 + * Get posts liked by an actor 97 + * 98 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-actor-likes 99 + */ 100 + #[PublicEndpoint] 101 + public function getActorLikes(string $actor, int $limit = 50, ?string $cursor = null): GetActorLikesResponse 102 + { 103 + $response = $this->atp->client->get( 104 + endpoint: BskyFeed::GetActorLikes, 105 params: compact('actor', 'limit', 'cursor') 106 ); 107 108 + return GetActorLikesResponse::fromArray($response->json()); 109 } 110 111 /** 112 + * Get a feed 113 * 114 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-feed 115 + */ 116 + #[PublicEndpoint] 117 + public function getFeed(string $feed, int $limit = 50, ?string $cursor = null): GetFeedResponse 118 + { 119 + $response = $this->atp->client->get( 120 + endpoint: BskyFeed::GetFeed, 121 + params: compact('feed', 'limit', 'cursor') 122 + ); 123 + 124 + return GetFeedResponse::fromArray($response->json()); 125 + } 126 + 127 + /** 128 + * Get a feed generator 129 + * 130 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-feed-generator 131 + */ 132 + #[PublicEndpoint] 133 + public function getFeedGenerator(string $feed): GetFeedGeneratorResponse 134 + { 135 + $response = $this->atp->client->get( 136 + endpoint: BskyFeed::GetFeedGenerator, 137 + params: compact('feed') 138 + ); 139 + 140 + return GetFeedGeneratorResponse::fromArray($response->json()); 141 + } 142 + 143 + /** 144 + * Get multiple feed generators 145 + * 146 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-feed-generators 147 + */ 148 + #[PublicEndpoint] 149 + public function getFeedGenerators(array $feeds): GetFeedGeneratorsResponse 150 + { 151 + $response = $this->atp->client->get( 152 + endpoint: BskyFeed::GetFeedGenerators, 153 + params: compact('feeds') 154 + ); 155 + 156 + return GetFeedGeneratorsResponse::fromArray($response->json()); 157 + } 158 + 159 + /** 160 + * Get post thread 161 * 162 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-post-thread 163 */ 164 + #[PublicEndpoint] 165 + public function getPostThread(string $uri, int $depth = 6, int $parentHeight = 80): GetPostThreadResponse 166 { 167 $response = $this->atp->client->get( 168 + endpoint: BskyFeed::GetPostThread, 169 + params: compact('uri', 'depth', 'parentHeight') 170 ); 171 172 return GetPostThreadResponse::fromArray($response->json()); 173 } 174 175 /** 176 + * Get multiple posts by URI 177 * 178 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-posts 179 */ 180 + #[PublicEndpoint] 181 + public function getPosts(array $uris): GetPostsResponse 182 + { 183 $response = $this->atp->client->get( 184 + endpoint: BskyFeed::GetPosts, 185 + params: compact('uris') 186 ); 187 188 + return GetPostsResponse::fromArray($response->json()); 189 } 190 191 /** 192 * Get likes for a post 193 * 194 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-likes 195 */ 196 + #[PublicEndpoint] 197 public function getLikes( 198 string $uri, 199 int $limit = 50, 200 + ?string $cursor = null, 201 + ?string $cid = null 202 ): GetLikesResponse { 203 $response = $this->atp->client->get( 204 + endpoint: BskyFeed::GetLikes, 205 + params: compact('uri', 'limit', 'cursor', 'cid') 206 ); 207 208 return GetLikesResponse::fromArray($response->json()); 209 } 210 211 /** 212 + * Get quotes of a post 213 + * 214 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-quotes 215 + */ 216 + #[PublicEndpoint] 217 + public function getQuotes( 218 + string $uri, 219 + int $limit = 50, 220 + ?string $cursor = null, 221 + ?string $cid = null 222 + ): GetQuotesResponse { 223 + $response = $this->atp->client->get( 224 + endpoint: BskyFeed::GetQuotes, 225 + params: compact('uri', 'limit', 'cursor', 'cid') 226 + ); 227 + 228 + return GetQuotesResponse::fromArray($response->json()); 229 + } 230 + 231 + /** 232 * Get reposts for a post 233 * 234 * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-reposted-by 235 */ 236 + #[PublicEndpoint] 237 public function getRepostedBy( 238 string $uri, 239 int $limit = 50, 240 + ?string $cursor = null, 241 + ?string $cid = null 242 ): GetRepostedByResponse { 243 $response = $this->atp->client->get( 244 + endpoint: BskyFeed::GetRepostedBy, 245 + params: compact('uri', 'limit', 'cursor', 'cid') 246 ); 247 248 return GetRepostedByResponse::fromArray($response->json()); 249 + } 250 + 251 + /** 252 + * Get suggested feeds 253 + * 254 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-get-suggested-feeds 255 + */ 256 + #[PublicEndpoint] 257 + public function getSuggestedFeeds(int $limit = 50, ?string $cursor = null): GetSuggestedFeedsResponse 258 + { 259 + $response = $this->atp->client->get( 260 + endpoint: BskyFeed::GetSuggestedFeeds, 261 + params: compact('limit', 'cursor') 262 + ); 263 + 264 + return GetSuggestedFeedsResponse::fromArray($response->json()); 265 + } 266 + 267 + /** 268 + * Search posts 269 + * 270 + * @see https://docs.bsky.app/docs/api/app-bsky-feed-search-posts 271 + */ 272 + #[PublicEndpoint] 273 + public function searchPosts( 274 + string $q, 275 + int $limit = 25, 276 + ?string $cursor = null, 277 + ?string $sort = null 278 + ): SearchPostsResponse { 279 + $response = $this->atp->client->get( 280 + endpoint: BskyFeed::SearchPosts, 281 + params: compact('q', 'limit', 'cursor', 'sort') 282 + ); 283 + 284 + return SearchPostsResponse::fromArray($response->json()); 285 } 286 }
+163
src/Client/Requests/Bsky/GraphRequestClient.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Client\Requests\Bsky; 4 + 5 + use SocialDept\AtpClient\Attributes\PublicEndpoint; 6 + use SocialDept\AtpClient\Client\Requests\Request; 7 + use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetFollowersResponse; 8 + use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetFollowsResponse; 9 + use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetKnownFollowersResponse; 10 + use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetListResponse; 11 + use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetListsResponse; 12 + use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetRelationshipsResponse; 13 + use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetStarterPacksResponse; 14 + use SocialDept\AtpClient\Data\Responses\Bsky\Graph\GetSuggestedFollowsByActorResponse; 15 + use SocialDept\AtpClient\Enums\Nsid\BskyGraph; 16 + use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\StarterPackView; 17 + 18 + class GraphRequestClient extends Request 19 + { 20 + /** 21 + * Get followers of an actor 22 + * 23 + * @see https://docs.bsky.app/docs/api/app-bsky-graph-get-followers 24 + */ 25 + #[PublicEndpoint] 26 + public function getFollowers(string $actor, int $limit = 50, ?string $cursor = null): GetFollowersResponse 27 + { 28 + $response = $this->atp->client->get( 29 + endpoint: BskyGraph::GetFollowers, 30 + params: compact('actor', 'limit', 'cursor') 31 + ); 32 + 33 + return GetFollowersResponse::fromArray($response->json()); 34 + } 35 + 36 + /** 37 + * Get accounts that an actor follows 38 + * 39 + * @see https://docs.bsky.app/docs/api/app-bsky-graph-get-follows 40 + */ 41 + #[PublicEndpoint] 42 + public function getFollows(string $actor, int $limit = 50, ?string $cursor = null): GetFollowsResponse 43 + { 44 + $response = $this->atp->client->get( 45 + endpoint: BskyGraph::GetFollows, 46 + params: compact('actor', 'limit', 'cursor') 47 + ); 48 + 49 + return GetFollowsResponse::fromArray($response->json()); 50 + } 51 + 52 + /** 53 + * Get followers of an actor that you also follow 54 + * 55 + * @see https://docs.bsky.app/docs/api/app-bsky-graph-get-known-followers 56 + */ 57 + #[PublicEndpoint] 58 + public function getKnownFollowers(string $actor, int $limit = 50, ?string $cursor = null): GetKnownFollowersResponse 59 + { 60 + $response = $this->atp->client->get( 61 + endpoint: BskyGraph::GetKnownFollowers, 62 + params: compact('actor', 'limit', 'cursor') 63 + ); 64 + 65 + return GetKnownFollowersResponse::fromArray($response->json()); 66 + } 67 + 68 + /** 69 + * Get a list by URI 70 + * 71 + * @see https://docs.bsky.app/docs/api/app-bsky-graph-get-list 72 + */ 73 + #[PublicEndpoint] 74 + public function getList(string $list, int $limit = 50, ?string $cursor = null): GetListResponse 75 + { 76 + $response = $this->atp->client->get( 77 + endpoint: BskyGraph::GetList, 78 + params: compact('list', 'limit', 'cursor') 79 + ); 80 + 81 + return GetListResponse::fromArray($response->json()); 82 + } 83 + 84 + /** 85 + * Get lists created by an actor 86 + * 87 + * @see https://docs.bsky.app/docs/api/app-bsky-graph-get-lists 88 + */ 89 + #[PublicEndpoint] 90 + public function getLists(string $actor, int $limit = 50, ?string $cursor = null): GetListsResponse 91 + { 92 + $response = $this->atp->client->get( 93 + endpoint: BskyGraph::GetLists, 94 + params: compact('actor', 'limit', 'cursor') 95 + ); 96 + 97 + return GetListsResponse::fromArray($response->json()); 98 + } 99 + 100 + /** 101 + * Get relationships between actors 102 + * 103 + * @see https://docs.bsky.app/docs/api/app-bsky-graph-get-relationships 104 + */ 105 + #[PublicEndpoint] 106 + public function getRelationships(string $actor, array $others = []): GetRelationshipsResponse 107 + { 108 + $response = $this->atp->client->get( 109 + endpoint: BskyGraph::GetRelationships, 110 + params: compact('actor', 'others') 111 + ); 112 + 113 + return GetRelationshipsResponse::fromArray($response->json()); 114 + } 115 + 116 + /** 117 + * Get a starter pack by URI 118 + * 119 + * @see https://docs.bsky.app/docs/api/app-bsky-graph-get-starter-pack 120 + */ 121 + #[PublicEndpoint] 122 + public function getStarterPack(string $starterPack): StarterPackView 123 + { 124 + $response = $this->atp->client->get( 125 + endpoint: BskyGraph::GetStarterPack, 126 + params: compact('starterPack') 127 + ); 128 + 129 + return StarterPackView::fromArray($response->json()['starterPack']); 130 + } 131 + 132 + /** 133 + * Get multiple starter packs 134 + * 135 + * @see https://docs.bsky.app/docs/api/app-bsky-graph-get-starter-packs 136 + */ 137 + #[PublicEndpoint] 138 + public function getStarterPacks(array $uris): GetStarterPacksResponse 139 + { 140 + $response = $this->atp->client->get( 141 + endpoint: BskyGraph::GetStarterPacks, 142 + params: compact('uris') 143 + ); 144 + 145 + return GetStarterPacksResponse::fromArray($response->json()); 146 + } 147 + 148 + /** 149 + * Get suggested follows based on an actor 150 + * 151 + * @see https://docs.bsky.app/docs/api/app-bsky-graph-get-suggested-follows-by-actor 152 + */ 153 + #[PublicEndpoint] 154 + public function getSuggestedFollowsByActor(string $actor): GetSuggestedFollowsByActorResponse 155 + { 156 + $response = $this->atp->client->get( 157 + endpoint: BskyGraph::GetSuggestedFollowsByActor, 158 + params: compact('actor') 159 + ); 160 + 161 + return GetSuggestedFollowsByActorResponse::fromArray($response->json()); 162 + } 163 + }
+27
src/Client/Requests/Bsky/LabelerRequestClient.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Client\Requests\Bsky; 4 + 5 + use SocialDept\AtpClient\Attributes\PublicEndpoint; 6 + use SocialDept\AtpClient\Client\Requests\Request; 7 + use SocialDept\AtpClient\Data\Responses\Bsky\Labeler\GetServicesResponse; 8 + use SocialDept\AtpClient\Enums\Nsid\BskyLabeler; 9 + 10 + class LabelerRequestClient extends Request 11 + { 12 + /** 13 + * Get labeler services 14 + * 15 + * @see https://docs.bsky.app/docs/api/app-bsky-labeler-get-services 16 + */ 17 + #[PublicEndpoint] 18 + public function getServices(array $dids, bool $detailed = false): GetServicesResponse 19 + { 20 + $response = $this->atp->client->get( 21 + endpoint: BskyLabeler::GetServices, 22 + params: compact('dids', 'detailed') 23 + ); 24 + 25 + return GetServicesResponse::fromArray($response->json(), $detailed); 26 + } 27 + }
+12 -8
src/Client/Requests/Chat/ActorRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Chat; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Enums\Scope; 8 use SocialDept\AtpClient\Http\Response; 9 ··· 16 * 17 * @see https://docs.bsky.app/docs/api/chat-bsky-actor-export-account-data 18 */ 19 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.actor.getActorMetadata')] 20 public function getActorMetadata(): Response 21 { 22 return $this->atp->client->get( 23 - endpoint: 'chat.bsky.actor.getActorMetadata' 24 ); 25 } 26 ··· 31 * 32 * @see https://docs.bsky.app/docs/api/chat-bsky-actor-export-account-data 33 */ 34 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.actor.exportAccountData')] 35 public function exportAccountData(): Response 36 { 37 return $this->atp->client->get( 38 - endpoint: 'chat.bsky.actor.exportAccountData' 39 ); 40 } 41 ··· 46 * 47 * @see https://docs.bsky.app/docs/api/chat-bsky-actor-delete-account 48 */ 49 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.actor.deleteAccount')] 50 - public function deleteAccount(): void 51 { 52 $this->atp->client->post( 53 - endpoint: 'chat.bsky.actor.deleteAccount' 54 ); 55 } 56 }
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Chat; 4 5 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 + use SocialDept\AtpClient\Data\Responses\EmptyResponse; 8 + use SocialDept\AtpClient\Enums\Nsid\ChatActor; 9 use SocialDept\AtpClient\Enums\Scope; 10 use SocialDept\AtpClient\Http\Response; 11 ··· 18 * 19 * @see https://docs.bsky.app/docs/api/chat-bsky-actor-export-account-data 20 */ 21 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.actor.getActorMetadata')] 22 public function getActorMetadata(): Response 23 { 24 return $this->atp->client->get( 25 + endpoint: ChatActor::GetActorMetadata 26 ); 27 } 28 ··· 33 * 34 * @see https://docs.bsky.app/docs/api/chat-bsky-actor-export-account-data 35 */ 36 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.actor.exportAccountData')] 37 public function exportAccountData(): Response 38 { 39 return $this->atp->client->get( 40 + endpoint: ChatActor::ExportAccountData 41 ); 42 } 43 ··· 48 * 49 * @see https://docs.bsky.app/docs/api/chat-bsky-actor-delete-account 50 */ 51 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.actor.deleteAccount')] 52 + public function deleteAccount(): EmptyResponse 53 { 54 $this->atp->client->post( 55 + endpoint: ChatActor::DeleteAccount 56 ); 57 + 58 + return new EmptyResponse; 59 } 60 }
+26 -25
src/Client/Requests/Chat/ConvoRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Chat; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Chat\Convo\GetLogResponse; 8 use SocialDept\AtpClient\Data\Responses\Chat\Convo\GetMessagesResponse; 9 use SocialDept\AtpClient\Data\Responses\Chat\Convo\LeaveConvoResponse; 10 use SocialDept\AtpClient\Data\Responses\Chat\Convo\ListConvosResponse; 11 use SocialDept\AtpClient\Data\Responses\Chat\Convo\SendMessageBatchResponse; 12 use SocialDept\AtpClient\Enums\Scope; 13 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\ConvoView; 14 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\DeletedMessageView; ··· 23 * 24 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-get-convo 25 */ 26 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.getConvo')] 27 public function getConvo(string $convoId): ConvoView 28 { 29 $response = $this->atp->client->get( 30 - endpoint: 'chat.bsky.convo.getConvo', 31 params: compact('convoId') 32 ); 33 ··· 41 * 42 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-get-convo-for-members 43 */ 44 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.getConvoForMembers')] 45 public function getConvoForMembers(array $members): ConvoView 46 { 47 $response = $this->atp->client->get( 48 - endpoint: 'chat.bsky.convo.getConvoForMembers', 49 params: compact('members') 50 ); 51 ··· 59 * 60 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-list-convos 61 */ 62 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.listConvos')] 63 public function listConvos(int $limit = 50, ?string $cursor = null): ListConvosResponse 64 { 65 $response = $this->atp->client->get( 66 - endpoint: 'chat.bsky.convo.listConvos', 67 params: compact('limit', 'cursor') 68 ); 69 ··· 77 * 78 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-get-messages 79 */ 80 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.getMessages')] 81 public function getMessages( 82 string $convoId, 83 int $limit = 50, 84 ?string $cursor = null 85 ): GetMessagesResponse { 86 $response = $this->atp->client->get( 87 - endpoint: 'chat.bsky.convo.getMessages', 88 params: compact('convoId', 'limit', 'cursor') 89 ); 90 ··· 98 * 99 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-send-message 100 */ 101 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.sendMessage')] 102 public function sendMessage(string $convoId, array $message): MessageView 103 { 104 $response = $this->atp->client->post( 105 - endpoint: 'chat.bsky.convo.sendMessage', 106 body: compact('convoId', 'message') 107 ); 108 ··· 116 * 117 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-send-message-batch 118 */ 119 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.sendMessageBatch')] 120 public function sendMessageBatch(array $items): SendMessageBatchResponse 121 { 122 $response = $this->atp->client->post( 123 - endpoint: 'chat.bsky.convo.sendMessageBatch', 124 body: compact('items') 125 ); 126 ··· 134 * 135 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-delete-message-for-self 136 */ 137 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.deleteMessageForSelf')] 138 public function deleteMessageForSelf(string $convoId, string $messageId): DeletedMessageView 139 { 140 $response = $this->atp->client->post( 141 - endpoint: 'chat.bsky.convo.deleteMessageForSelf', 142 body: compact('convoId', 'messageId') 143 ); 144 ··· 152 * 153 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-update-read 154 */ 155 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.updateRead')] 156 public function updateRead(string $convoId, ?string $messageId = null): ConvoView 157 { 158 $response = $this->atp->client->post( 159 - endpoint: 'chat.bsky.convo.updateRead', 160 body: compact('convoId', 'messageId') 161 ); 162 ··· 170 * 171 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-mute-convo 172 */ 173 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.muteConvo')] 174 public function muteConvo(string $convoId): ConvoView 175 { 176 $response = $this->atp->client->post( 177 - endpoint: 'chat.bsky.convo.muteConvo', 178 body: compact('convoId') 179 ); 180 ··· 188 * 189 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-unmute-convo 190 */ 191 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.unmuteConvo')] 192 public function unmuteConvo(string $convoId): ConvoView 193 { 194 $response = $this->atp->client->post( 195 - endpoint: 'chat.bsky.convo.unmuteConvo', 196 body: compact('convoId') 197 ); 198 ··· 206 * 207 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-leave-convo 208 */ 209 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.leaveConvo')] 210 public function leaveConvo(string $convoId): LeaveConvoResponse 211 { 212 $response = $this->atp->client->post( 213 - endpoint: 'chat.bsky.convo.leaveConvo', 214 body: compact('convoId') 215 ); 216 ··· 224 * 225 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-get-log 226 */ 227 - #[RequiresScope(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.getLog')] 228 public function getLog(?string $cursor = null): GetLogResponse 229 { 230 $response = $this->atp->client->get( 231 - endpoint: 'chat.bsky.convo.getLog', 232 params: compact('cursor') 233 ); 234
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Chat; 4 5 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Chat\Convo\GetLogResponse; 8 use SocialDept\AtpClient\Data\Responses\Chat\Convo\GetMessagesResponse; 9 use SocialDept\AtpClient\Data\Responses\Chat\Convo\LeaveConvoResponse; 10 use SocialDept\AtpClient\Data\Responses\Chat\Convo\ListConvosResponse; 11 use SocialDept\AtpClient\Data\Responses\Chat\Convo\SendMessageBatchResponse; 12 + use SocialDept\AtpClient\Enums\Nsid\ChatConvo; 13 use SocialDept\AtpClient\Enums\Scope; 14 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\ConvoView; 15 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\DeletedMessageView; ··· 24 * 25 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-get-convo 26 */ 27 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.getConvo')] 28 public function getConvo(string $convoId): ConvoView 29 { 30 $response = $this->atp->client->get( 31 + endpoint: ChatConvo::GetConvo, 32 params: compact('convoId') 33 ); 34 ··· 42 * 43 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-get-convo-for-members 44 */ 45 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.getConvoForMembers')] 46 public function getConvoForMembers(array $members): ConvoView 47 { 48 $response = $this->atp->client->get( 49 + endpoint: ChatConvo::GetConvoForMembers, 50 params: compact('members') 51 ); 52 ··· 60 * 61 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-list-convos 62 */ 63 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.listConvos')] 64 public function listConvos(int $limit = 50, ?string $cursor = null): ListConvosResponse 65 { 66 $response = $this->atp->client->get( 67 + endpoint: ChatConvo::ListConvos, 68 params: compact('limit', 'cursor') 69 ); 70 ··· 78 * 79 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-get-messages 80 */ 81 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.getMessages')] 82 public function getMessages( 83 string $convoId, 84 int $limit = 50, 85 ?string $cursor = null 86 ): GetMessagesResponse { 87 $response = $this->atp->client->get( 88 + endpoint: ChatConvo::GetMessages, 89 params: compact('convoId', 'limit', 'cursor') 90 ); 91 ··· 99 * 100 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-send-message 101 */ 102 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.sendMessage')] 103 public function sendMessage(string $convoId, array $message): MessageView 104 { 105 $response = $this->atp->client->post( 106 + endpoint: ChatConvo::SendMessage, 107 body: compact('convoId', 'message') 108 ); 109 ··· 117 * 118 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-send-message-batch 119 */ 120 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.sendMessageBatch')] 121 public function sendMessageBatch(array $items): SendMessageBatchResponse 122 { 123 $response = $this->atp->client->post( 124 + endpoint: ChatConvo::SendMessageBatch, 125 body: compact('items') 126 ); 127 ··· 135 * 136 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-delete-message-for-self 137 */ 138 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.deleteMessageForSelf')] 139 public function deleteMessageForSelf(string $convoId, string $messageId): DeletedMessageView 140 { 141 $response = $this->atp->client->post( 142 + endpoint: ChatConvo::DeleteMessageForSelf, 143 body: compact('convoId', 'messageId') 144 ); 145 ··· 153 * 154 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-update-read 155 */ 156 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.updateRead')] 157 public function updateRead(string $convoId, ?string $messageId = null): ConvoView 158 { 159 $response = $this->atp->client->post( 160 + endpoint: ChatConvo::UpdateRead, 161 body: compact('convoId', 'messageId') 162 ); 163 ··· 171 * 172 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-mute-convo 173 */ 174 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.muteConvo')] 175 public function muteConvo(string $convoId): ConvoView 176 { 177 $response = $this->atp->client->post( 178 + endpoint: ChatConvo::MuteConvo, 179 body: compact('convoId') 180 ); 181 ··· 189 * 190 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-unmute-convo 191 */ 192 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.unmuteConvo')] 193 public function unmuteConvo(string $convoId): ConvoView 194 { 195 $response = $this->atp->client->post( 196 + endpoint: ChatConvo::UnmuteConvo, 197 body: compact('convoId') 198 ); 199 ··· 207 * 208 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-leave-convo 209 */ 210 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.leaveConvo')] 211 public function leaveConvo(string $convoId): LeaveConvoResponse 212 { 213 $response = $this->atp->client->post( 214 + endpoint: ChatConvo::LeaveConvo, 215 body: compact('convoId') 216 ); 217 ··· 225 * 226 * @see https://docs.bsky.app/docs/api/chat-bsky-convo-get-log 227 */ 228 + #[ScopedEndpoint(Scope::TransitionChat, granular: 'rpc:chat.bsky.convo.getLog')] 229 public function getLog(?string $cursor = null): GetLogResponse 230 { 231 $response = $this->atp->client->get( 232 + endpoint: ChatConvo::GetLog, 233 params: compact('cursor') 234 ); 235
+18 -17
src/Client/Requests/Ozone/ModerationRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Ozone; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Ozone\Moderation\QueryEventsResponse; 8 use SocialDept\AtpClient\Data\Responses\Ozone\Moderation\QueryStatusesResponse; 9 use SocialDept\AtpClient\Data\Responses\Ozone\Moderation\SearchReposResponse; 10 use SocialDept\AtpClient\Enums\Scope; 11 use SocialDept\AtpClient\Http\Response; 12 use SocialDept\AtpSchema\Generated\Tools\Ozone\Moderation\Defs\ModEventView; ··· 23 * 24 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-get-event 25 */ 26 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.getEvent')] 27 public function getModerationEvent(int $id): ModEventViewDetail 28 { 29 $response = $this->atp->client->get( 30 - endpoint: 'tools.ozone.moderation.getEvent', 31 params: compact('id') 32 ); 33 ··· 41 * 42 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-query-events 43 */ 44 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.getEvents')] 45 public function getModerationEvents( 46 ?string $subject = null, 47 ?array $types = null, ··· 50 ?string $cursor = null 51 ): Response { 52 return $this->atp->client->get( 53 - endpoint: 'tools.ozone.moderation.getEvents', 54 params: array_filter( 55 compact('subject', 'types', 'createdBy', 'limit', 'cursor'), 56 fn ($v) => ! is_null($v) ··· 65 * 66 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-get-record 67 */ 68 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.getRecord')] 69 public function getRecord(string $uri, ?string $cid = null): RecordViewDetail 70 { 71 $response = $this->atp->client->get( 72 - endpoint: 'tools.ozone.moderation.getRecord', 73 params: compact('uri', 'cid') 74 ); 75 ··· 83 * 84 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-get-repo 85 */ 86 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.getRepo')] 87 public function getRepo(string $did): RepoViewDetail 88 { 89 $response = $this->atp->client->get( 90 - endpoint: 'tools.ozone.moderation.getRepo', 91 params: compact('did') 92 ); 93 ··· 101 * 102 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-query-events 103 */ 104 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.queryEvents')] 105 public function queryEvents( 106 ?array $types = null, 107 ?string $createdBy = null, ··· 111 bool $sortDirection = false 112 ): QueryEventsResponse { 113 $response = $this->atp->client->get( 114 - endpoint: 'tools.ozone.moderation.queryEvents', 115 params: array_filter( 116 compact('types', 'createdBy', 'subject', 'limit', 'cursor', 'sortDirection'), 117 fn ($v) => ! is_null($v) ··· 128 * 129 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-query-statuses 130 */ 131 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.queryStatuses')] 132 public function queryStatuses( 133 ?string $subject = null, 134 ?array $tags = null, ··· 137 ?string $cursor = null 138 ): QueryStatusesResponse { 139 $response = $this->atp->client->get( 140 - endpoint: 'tools.ozone.moderation.queryStatuses', 141 params: array_filter( 142 compact('subject', 'tags', 'excludeTags', 'limit', 'cursor'), 143 fn ($v) => ! is_null($v) ··· 154 * 155 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-search-repos 156 */ 157 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.searchRepos')] 158 public function searchRepos( 159 ?string $term = null, 160 ?string $invitedBy = null, ··· 162 ?string $cursor = null 163 ): SearchReposResponse { 164 $response = $this->atp->client->get( 165 - endpoint: 'tools.ozone.moderation.searchRepos', 166 params: array_filter( 167 compact('term', 'invitedBy', 'limit', 'cursor'), 168 fn ($v) => ! is_null($v) ··· 179 * 180 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-emit-event 181 */ 182 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.emitEvent')] 183 public function emitEvent( 184 array $event, 185 string $subject, ··· 187 ?string $createdBy = null 188 ): ModEventView { 189 $response = $this->atp->client->post( 190 - endpoint: 'tools.ozone.moderation.emitEvent', 191 body: compact('event', 'subject', 'subjectBlobCids', 'createdBy') 192 ); 193
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Ozone; 4 5 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Ozone\Moderation\QueryEventsResponse; 8 use SocialDept\AtpClient\Data\Responses\Ozone\Moderation\QueryStatusesResponse; 9 use SocialDept\AtpClient\Data\Responses\Ozone\Moderation\SearchReposResponse; 10 + use SocialDept\AtpClient\Enums\Nsid\OzoneModeration; 11 use SocialDept\AtpClient\Enums\Scope; 12 use SocialDept\AtpClient\Http\Response; 13 use SocialDept\AtpSchema\Generated\Tools\Ozone\Moderation\Defs\ModEventView; ··· 24 * 25 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-get-event 26 */ 27 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.getEvent')] 28 public function getModerationEvent(int $id): ModEventViewDetail 29 { 30 $response = $this->atp->client->get( 31 + endpoint: OzoneModeration::GetEvent, 32 params: compact('id') 33 ); 34 ··· 42 * 43 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-query-events 44 */ 45 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.getEvents')] 46 public function getModerationEvents( 47 ?string $subject = null, 48 ?array $types = null, ··· 51 ?string $cursor = null 52 ): Response { 53 return $this->atp->client->get( 54 + endpoint: OzoneModeration::GetEvents, 55 params: array_filter( 56 compact('subject', 'types', 'createdBy', 'limit', 'cursor'), 57 fn ($v) => ! is_null($v) ··· 66 * 67 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-get-record 68 */ 69 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.getRecord')] 70 public function getRecord(string $uri, ?string $cid = null): RecordViewDetail 71 { 72 $response = $this->atp->client->get( 73 + endpoint: OzoneModeration::GetRecord, 74 params: compact('uri', 'cid') 75 ); 76 ··· 84 * 85 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-get-repo 86 */ 87 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.getRepo')] 88 public function getRepo(string $did): RepoViewDetail 89 { 90 $response = $this->atp->client->get( 91 + endpoint: OzoneModeration::GetRepo, 92 params: compact('did') 93 ); 94 ··· 102 * 103 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-query-events 104 */ 105 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.queryEvents')] 106 public function queryEvents( 107 ?array $types = null, 108 ?string $createdBy = null, ··· 112 bool $sortDirection = false 113 ): QueryEventsResponse { 114 $response = $this->atp->client->get( 115 + endpoint: OzoneModeration::QueryEvents, 116 params: array_filter( 117 compact('types', 'createdBy', 'subject', 'limit', 'cursor', 'sortDirection'), 118 fn ($v) => ! is_null($v) ··· 129 * 130 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-query-statuses 131 */ 132 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.queryStatuses')] 133 public function queryStatuses( 134 ?string $subject = null, 135 ?array $tags = null, ··· 138 ?string $cursor = null 139 ): QueryStatusesResponse { 140 $response = $this->atp->client->get( 141 + endpoint: OzoneModeration::QueryStatuses, 142 params: array_filter( 143 compact('subject', 'tags', 'excludeTags', 'limit', 'cursor'), 144 fn ($v) => ! is_null($v) ··· 155 * 156 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-search-repos 157 */ 158 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.searchRepos')] 159 public function searchRepos( 160 ?string $term = null, 161 ?string $invitedBy = null, ··· 163 ?string $cursor = null 164 ): SearchReposResponse { 165 $response = $this->atp->client->get( 166 + endpoint: OzoneModeration::SearchRepos, 167 params: array_filter( 168 compact('term', 'invitedBy', 'limit', 'cursor'), 169 fn ($v) => ! is_null($v) ··· 180 * 181 * @see https://docs.bsky.app/docs/api/tools-ozone-moderation-emit-event 182 */ 183 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.moderation.emitEvent')] 184 public function emitEvent( 185 array $event, 186 string $subject, ··· 188 ?string $createdBy = null 189 ): ModEventView { 190 $response = $this->atp->client->post( 191 + endpoint: OzoneModeration::EmitEvent, 192 body: compact('event', 'subject', 'subjectBlobCids', 'createdBy') 193 ); 194
+6 -5
src/Client/Requests/Ozone/ServerRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Ozone; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Ozone\Server\GetConfigResponse; 8 use SocialDept\AtpClient\Enums\Scope; 9 use SocialDept\AtpClient\Http\Response; 10 ··· 17 * 18 * @see https://docs.bsky.app/docs/api/tools-ozone-server-get-config 19 */ 20 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.server.getBlob')] 21 public function getBlob(string $did, string $cid): Response 22 { 23 return $this->atp->client->get( 24 - endpoint: 'tools.ozone.server.getBlob', 25 params: compact('did', 'cid') 26 ); 27 } ··· 33 * 34 * @see https://docs.bsky.app/docs/api/tools-ozone-server-get-config 35 */ 36 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.server.getConfig')] 37 public function getConfig(): GetConfigResponse 38 { 39 $response = $this->atp->client->get( 40 - endpoint: 'tools.ozone.server.getConfig' 41 ); 42 43 return GetConfigResponse::fromArray($response->json());
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Ozone; 4 5 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Ozone\Server\GetConfigResponse; 8 + use SocialDept\AtpClient\Enums\Nsid\OzoneServer; 9 use SocialDept\AtpClient\Enums\Scope; 10 use SocialDept\AtpClient\Http\Response; 11 ··· 18 * 19 * @see https://docs.bsky.app/docs/api/tools-ozone-server-get-config 20 */ 21 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.server.getBlob')] 22 public function getBlob(string $did, string $cid): Response 23 { 24 return $this->atp->client->get( 25 + endpoint: OzoneServer::GetBlob, 26 params: compact('did', 'cid') 27 ); 28 } ··· 34 * 35 * @see https://docs.bsky.app/docs/api/tools-ozone-server-get-config 36 */ 37 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.server.getConfig')] 38 public function getConfig(): GetConfigResponse 39 { 40 $response = $this->atp->client->get( 41 + endpoint: OzoneServer::GetConfig 42 ); 43 44 return GetConfigResponse::fromArray($response->json());
+23 -24
src/Client/Requests/Ozone/TeamRequestClient.php
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Ozone; 4 5 - use SocialDept\AtpClient\Attributes\RequiresScope; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 use SocialDept\AtpClient\Data\Responses\Ozone\Team\ListMembersResponse; 8 use SocialDept\AtpClient\Enums\Scope; 9 10 class TeamRequestClient extends Request ··· 14 * 15 * @requires transition:generic (rpc:tools.ozone.team.getMember) 16 * 17 - * @return array<string, mixed> Team member object 18 - * 19 * @see https://docs.bsky.app/docs/api/tools-ozone-team-list-members 20 */ 21 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.getMember')] 22 - public function getTeamMember(string $did): array 23 { 24 $response = $this->atp->client->get( 25 - endpoint: 'tools.ozone.team.getMember', 26 params: compact('did') 27 ); 28 29 - return $response->json(); 30 } 31 32 /** ··· 36 * 37 * @see https://docs.bsky.app/docs/api/tools-ozone-team-list-members 38 */ 39 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.listMembers')] 40 public function listTeamMembers(int $limit = 50, ?string $cursor = null): ListMembersResponse 41 { 42 $response = $this->atp->client->get( 43 - endpoint: 'tools.ozone.team.listMembers', 44 params: compact('limit', 'cursor') 45 ); 46 ··· 52 * 53 * @requires transition:generic (rpc:tools.ozone.team.addMember) 54 * 55 - * @return array<string, mixed> Team member object 56 - * 57 * @see https://docs.bsky.app/docs/api/tools-ozone-team-add-member 58 */ 59 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.addMember')] 60 - public function addTeamMember(string $did, string $role): array 61 { 62 $response = $this->atp->client->post( 63 - endpoint: 'tools.ozone.team.addMember', 64 body: compact('did', 'role') 65 ); 66 67 - return $response->json(); 68 } 69 70 /** ··· 72 * 73 * @requires transition:generic (rpc:tools.ozone.team.updateMember) 74 * 75 - * @return array<string, mixed> Team member object 76 - * 77 * @see https://docs.bsky.app/docs/api/tools-ozone-team-update-member 78 */ 79 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.updateMember')] 80 public function updateTeamMember( 81 string $did, 82 ?bool $disabled = null, 83 ?string $role = null 84 - ): array { 85 $response = $this->atp->client->post( 86 - endpoint: 'tools.ozone.team.updateMember', 87 body: array_filter( 88 compact('did', 'disabled', 'role'), 89 fn ($v) => ! is_null($v) 90 ) 91 ); 92 93 - return $response->json(); 94 } 95 96 /** ··· 100 * 101 * @see https://docs.bsky.app/docs/api/tools-ozone-team-delete-member 102 */ 103 - #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.deleteMember')] 104 - public function deleteTeamMember(string $did): void 105 { 106 $this->atp->client->post( 107 - endpoint: 'tools.ozone.team.deleteMember', 108 body: compact('did') 109 ); 110 } 111 }
··· 2 3 namespace SocialDept\AtpClient\Client\Requests\Ozone; 4 5 + use SocialDept\AtpClient\Attributes\ScopedEndpoint; 6 use SocialDept\AtpClient\Client\Requests\Request; 7 + use SocialDept\AtpClient\Data\Responses\EmptyResponse; 8 use SocialDept\AtpClient\Data\Responses\Ozone\Team\ListMembersResponse; 9 + use SocialDept\AtpClient\Data\Responses\Ozone\Team\MemberResponse; 10 + use SocialDept\AtpClient\Enums\Nsid\OzoneTeam; 11 use SocialDept\AtpClient\Enums\Scope; 12 13 class TeamRequestClient extends Request ··· 17 * 18 * @requires transition:generic (rpc:tools.ozone.team.getMember) 19 * 20 * @see https://docs.bsky.app/docs/api/tools-ozone-team-list-members 21 */ 22 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.getMember')] 23 + public function getTeamMember(string $did): MemberResponse 24 { 25 $response = $this->atp->client->get( 26 + endpoint: OzoneTeam::GetMember, 27 params: compact('did') 28 ); 29 30 + return MemberResponse::fromArray($response->json()); 31 } 32 33 /** ··· 37 * 38 * @see https://docs.bsky.app/docs/api/tools-ozone-team-list-members 39 */ 40 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.listMembers')] 41 public function listTeamMembers(int $limit = 50, ?string $cursor = null): ListMembersResponse 42 { 43 $response = $this->atp->client->get( 44 + endpoint: OzoneTeam::ListMembers, 45 params: compact('limit', 'cursor') 46 ); 47 ··· 53 * 54 * @requires transition:generic (rpc:tools.ozone.team.addMember) 55 * 56 * @see https://docs.bsky.app/docs/api/tools-ozone-team-add-member 57 */ 58 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.addMember')] 59 + public function addTeamMember(string $did, string $role): MemberResponse 60 { 61 $response = $this->atp->client->post( 62 + endpoint: OzoneTeam::AddMember, 63 body: compact('did', 'role') 64 ); 65 66 + return MemberResponse::fromArray($response->json()); 67 } 68 69 /** ··· 71 * 72 * @requires transition:generic (rpc:tools.ozone.team.updateMember) 73 * 74 * @see https://docs.bsky.app/docs/api/tools-ozone-team-update-member 75 */ 76 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.updateMember')] 77 public function updateTeamMember( 78 string $did, 79 ?bool $disabled = null, 80 ?string $role = null 81 + ): MemberResponse { 82 $response = $this->atp->client->post( 83 + endpoint: OzoneTeam::UpdateMember, 84 body: array_filter( 85 compact('did', 'disabled', 'role'), 86 fn ($v) => ! is_null($v) 87 ) 88 ); 89 90 + return MemberResponse::fromArray($response->json()); 91 } 92 93 /** ··· 97 * 98 * @see https://docs.bsky.app/docs/api/tools-ozone-team-delete-member 99 */ 100 + #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:tools.ozone.team.deleteMember')] 101 + public function deleteTeamMember(string $did): EmptyResponse 102 { 103 $this->atp->client->post( 104 + endpoint: OzoneTeam::DeleteMember, 105 body: compact('did') 106 ); 107 + 108 + return new EmptyResponse; 109 } 110 }
+13 -44
src/Console/MakeAtpClientCommand.php
··· 10 { 11 protected $signature = 'make:atp-client 12 {name : The name of the client class} 13 - {--public : Generate a public client extension instead of authenticated} 14 {--force : Overwrite existing file}'; 15 16 protected $description = 'Create a new ATP domain client extension'; ··· 23 public function handle(): int 24 { 25 $name = $this->argument('name'); 26 - $isPublic = $this->option('public'); 27 28 if (! Str::endsWith($name, 'Client')) { 29 $name .= 'Client'; 30 } 31 32 - $path = $this->getPath($name, $isPublic); 33 34 if ($this->files->exists($path) && ! $this->option('force')) { 35 $this->components->error("Client [{$name}] already exists!"); ··· 39 40 $this->makeDirectory($path); 41 42 - $stub = $isPublic ? $this->getPublicStub() : $this->getStub(); 43 - $content = $this->populateStub($stub, $name, $isPublic); 44 45 $this->files->put($path, $content); 46 47 $this->components->info("Client [{$path}] created successfully."); 48 49 - $this->outputRegistrationHint($name, $isPublic); 50 51 return self::SUCCESS; 52 } 53 54 - protected function getPath(string $name, bool $isPublic = false): string 55 { 56 - $basePath = $isPublic 57 - ? config('client.generators.client_public_path', 'app/Services/Clients/Public') 58 - : config('client.generators.client_path', 'app/Services/Clients'); 59 60 return base_path($basePath.'/'.$name.'.php'); 61 } ··· 67 } 68 } 69 70 - protected function getNamespace(bool $isPublic = false): string 71 { 72 - $basePath = $isPublic 73 - ? config('client.generators.client_public_path', 'app/Services/Clients/Public') 74 - : config('client.generators.client_path', 'app/Services/Clients'); 75 76 return Str::of($basePath) 77 ->replace('/', '\\') ··· 80 ->toString(); 81 } 82 83 - protected function populateStub(string $stub, string $name, bool $isPublic = false): string 84 { 85 return str_replace( 86 ['{{ namespace }}', '{{ class }}'], 87 - [$this->getNamespace($isPublic), $name], 88 $stub 89 ); 90 } 91 92 - protected function outputRegistrationHint(string $name, bool $isPublic): void 93 { 94 $this->newLine(); 95 $this->components->info('Register the extension in your AppServiceProvider:'); 96 $this->newLine(); 97 98 - $namespace = $this->getNamespace($isPublic); 99 $extensionName = Str::of($name)->before('Client')->camel()->toString(); 100 - $clientClass = $isPublic ? 'AtpPublicClient' : 'AtpClient'; 101 102 $this->line("use {$namespace}\\{$name};"); 103 - $this->line("use SocialDept\\AtpClient\\".($isPublic ? 'Client\\Public\\' : '').$clientClass.';'); 104 $this->newLine(); 105 $this->line("// In boot() method:"); 106 - $this->line("{$clientClass}::extend('{$extensionName}', fn({$clientClass} \$atp) => new {$name}(\$atp));"); 107 } 108 109 protected function getStub(): string ··· 120 protected AtpClient $atp; 121 122 public function __construct(AtpClient $parent) 123 - { 124 - $this->atp = $parent; 125 - } 126 - 127 - // 128 - } 129 - STUB; 130 - } 131 - 132 - protected function getPublicStub(): string 133 - { 134 - return <<<'STUB' 135 - <?php 136 - 137 - namespace {{ namespace }}; 138 - 139 - use SocialDept\AtpClient\Client\Public\AtpPublicClient; 140 - 141 - class {{ class }} 142 - { 143 - protected AtpPublicClient $atp; 144 - 145 - public function __construct(AtpPublicClient $parent) 146 { 147 $this->atp = $parent; 148 }
··· 10 { 11 protected $signature = 'make:atp-client 12 {name : The name of the client class} 13 {--force : Overwrite existing file}'; 14 15 protected $description = 'Create a new ATP domain client extension'; ··· 22 public function handle(): int 23 { 24 $name = $this->argument('name'); 25 26 if (! Str::endsWith($name, 'Client')) { 27 $name .= 'Client'; 28 } 29 30 + $path = $this->getPath($name); 31 32 if ($this->files->exists($path) && ! $this->option('force')) { 33 $this->components->error("Client [{$name}] already exists!"); ··· 37 38 $this->makeDirectory($path); 39 40 + $content = $this->populateStub($this->getStub(), $name); 41 42 $this->files->put($path, $content); 43 44 $this->components->info("Client [{$path}] created successfully."); 45 46 + $this->outputRegistrationHint($name); 47 48 return self::SUCCESS; 49 } 50 51 + protected function getPath(string $name): string 52 { 53 + $basePath = config('client.generators.client_path', 'app/Services/Clients'); 54 55 return base_path($basePath.'/'.$name.'.php'); 56 } ··· 62 } 63 } 64 65 + protected function getNamespace(): string 66 { 67 + $basePath = config('client.generators.client_path', 'app/Services/Clients'); 68 69 return Str::of($basePath) 70 ->replace('/', '\\') ··· 73 ->toString(); 74 } 75 76 + protected function populateStub(string $stub, string $name): string 77 { 78 return str_replace( 79 ['{{ namespace }}', '{{ class }}'], 80 + [$this->getNamespace(), $name], 81 $stub 82 ); 83 } 84 85 + protected function outputRegistrationHint(string $name): void 86 { 87 $this->newLine(); 88 $this->components->info('Register the extension in your AppServiceProvider:'); 89 $this->newLine(); 90 91 + $namespace = $this->getNamespace(); 92 $extensionName = Str::of($name)->before('Client')->camel()->toString(); 93 94 $this->line("use {$namespace}\\{$name};"); 95 + $this->line("use SocialDept\\AtpClient\\AtpClient;"); 96 $this->newLine(); 97 $this->line("// In boot() method:"); 98 + $this->line("AtpClient::extend('{$extensionName}', fn(AtpClient \$atp) => new {$name}(\$atp));"); 99 } 100 101 protected function getStub(): string ··· 112 protected AtpClient $atp; 113 114 public function __construct(AtpClient $parent) 115 { 116 $this->atp = $parent; 117 }
+13 -37
src/Console/MakeAtpRequestCommand.php
··· 11 protected $signature = 'make:atp-request 12 {name : The name of the request client class} 13 {--domain=bsky : The domain to extend (bsky, atproto, chat, ozone)} 14 - {--public : Generate a public request client instead of authenticated} 15 {--force : Overwrite existing file}'; 16 17 protected $description = 'Create a new ATP request client extension for an existing domain'; ··· 27 { 28 $name = $this->argument('name'); 29 $domain = $this->option('domain'); 30 - $isPublic = $this->option('public'); 31 32 if (! in_array($domain, $this->validDomains)) { 33 $this->components->error("Invalid domain [{$domain}]. Valid domains: ".implode(', ', $this->validDomains)); ··· 39 $name .= 'Client'; 40 } 41 42 - $path = $this->getPath($name, $isPublic); 43 44 if ($this->files->exists($path) && ! $this->option('force')) { 45 $this->components->error("Request client [{$name}] already exists!"); ··· 49 50 $this->makeDirectory($path); 51 52 - $stub = $isPublic ? $this->getPublicStub() : $this->getStub(); 53 - $content = $this->populateStub($stub, $name, $isPublic); 54 55 $this->files->put($path, $content); 56 57 $this->components->info("Request client [{$path}] created successfully."); 58 59 - $this->outputRegistrationHint($name, $domain, $isPublic); 60 61 return self::SUCCESS; 62 } 63 64 - protected function getPath(string $name, bool $isPublic = false): string 65 { 66 - $basePath = $isPublic 67 - ? config('client.generators.request_public_path', 'app/Services/Clients/Public/Requests') 68 - : config('client.generators.request_path', 'app/Services/Clients/Requests'); 69 70 return base_path($basePath.'/'.$name.'.php'); 71 } ··· 77 } 78 } 79 80 - protected function getNamespace(bool $isPublic = false): string 81 { 82 - $basePath = $isPublic 83 - ? config('client.generators.request_public_path', 'app/Services/Clients/Public/Requests') 84 - : config('client.generators.request_path', 'app/Services/Clients/Requests'); 85 86 return Str::of($basePath) 87 ->replace('/', '\\') ··· 90 ->toString(); 91 } 92 93 - protected function populateStub(string $stub, string $name, bool $isPublic = false): string 94 { 95 return str_replace( 96 ['{{ namespace }}', '{{ class }}'], 97 - [$this->getNamespace($isPublic), $name], 98 $stub 99 ); 100 } 101 102 - protected function outputRegistrationHint(string $name, string $domain, bool $isPublic): void 103 { 104 $this->newLine(); 105 $this->components->info('Register the extension in your AppServiceProvider:'); 106 $this->newLine(); 107 108 - $namespace = $this->getNamespace($isPublic); 109 $extensionName = Str::of($name)->before('Client')->camel()->toString(); 110 - $clientClass = $isPublic ? 'AtpPublicClient' : 'AtpClient'; 111 112 $this->line("use {$namespace}\\{$name};"); 113 - $this->line("use SocialDept\\AtpClient\\".($isPublic ? 'Client\\Public\\' : '').$clientClass.';'); 114 $this->newLine(); 115 $this->line("// In boot() method:"); 116 - $this->line("{$clientClass}::extendDomain('{$domain}', '{$extensionName}', fn(\$domain) => new {$name}(\$domain));"); 117 } 118 119 protected function getStub(): string ··· 126 use SocialDept\AtpClient\Client\Requests\Request; 127 128 class {{ class }} extends Request 129 - { 130 - // 131 - } 132 - STUB; 133 - } 134 - 135 - protected function getPublicStub(): string 136 - { 137 - return <<<'STUB' 138 - <?php 139 - 140 - namespace {{ namespace }}; 141 - 142 - use SocialDept\AtpClient\Client\Public\Requests\PublicRequest; 143 - 144 - class {{ class }} extends PublicRequest 145 { 146 // 147 }
··· 11 protected $signature = 'make:atp-request 12 {name : The name of the request client class} 13 {--domain=bsky : The domain to extend (bsky, atproto, chat, ozone)} 14 {--force : Overwrite existing file}'; 15 16 protected $description = 'Create a new ATP request client extension for an existing domain'; ··· 26 { 27 $name = $this->argument('name'); 28 $domain = $this->option('domain'); 29 30 if (! in_array($domain, $this->validDomains)) { 31 $this->components->error("Invalid domain [{$domain}]. Valid domains: ".implode(', ', $this->validDomains)); ··· 37 $name .= 'Client'; 38 } 39 40 + $path = $this->getPath($name); 41 42 if ($this->files->exists($path) && ! $this->option('force')) { 43 $this->components->error("Request client [{$name}] already exists!"); ··· 47 48 $this->makeDirectory($path); 49 50 + $content = $this->populateStub($this->getStub(), $name); 51 52 $this->files->put($path, $content); 53 54 $this->components->info("Request client [{$path}] created successfully."); 55 56 + $this->outputRegistrationHint($name, $domain); 57 58 return self::SUCCESS; 59 } 60 61 + protected function getPath(string $name): string 62 { 63 + $basePath = config('client.generators.request_path', 'app/Services/Clients/Requests'); 64 65 return base_path($basePath.'/'.$name.'.php'); 66 } ··· 72 } 73 } 74 75 + protected function getNamespace(): string 76 { 77 + $basePath = config('client.generators.request_path', 'app/Services/Clients/Requests'); 78 79 return Str::of($basePath) 80 ->replace('/', '\\') ··· 83 ->toString(); 84 } 85 86 + protected function populateStub(string $stub, string $name): string 87 { 88 return str_replace( 89 ['{{ namespace }}', '{{ class }}'], 90 + [$this->getNamespace(), $name], 91 $stub 92 ); 93 } 94 95 + protected function outputRegistrationHint(string $name, string $domain): void 96 { 97 $this->newLine(); 98 $this->components->info('Register the extension in your AppServiceProvider:'); 99 $this->newLine(); 100 101 + $namespace = $this->getNamespace(); 102 $extensionName = Str::of($name)->before('Client')->camel()->toString(); 103 104 $this->line("use {$namespace}\\{$name};"); 105 + $this->line("use SocialDept\\AtpClient\\AtpClient;"); 106 $this->newLine(); 107 $this->line("// In boot() method:"); 108 + $this->line("AtpClient::extendDomain('{$domain}', '{$extensionName}', fn(\$domain) => new {$name}(\$domain));"); 109 } 110 111 protected function getStub(): string ··· 118 use SocialDept\AtpClient\Client\Requests\Request; 119 120 class {{ class }} extends Request 121 { 122 // 123 }
+61
src/Data/Record.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Data; 4 + 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * Generic wrapper for AT Protocol records. 9 + * 10 + * @template T 11 + * @implements Arrayable<string, mixed> 12 + */ 13 + class Record implements Arrayable 14 + { 15 + /** 16 + * @param T $value 17 + */ 18 + public function __construct( 19 + public readonly string $uri, 20 + public readonly string $cid, 21 + public readonly mixed $value, 22 + ) {} 23 + 24 + /** 25 + * @template U 26 + * @param array $data 27 + * @param callable(array): U $transformer 28 + * @return self<U> 29 + */ 30 + public static function fromArray(array $data, callable $transformer): self 31 + { 32 + return new self( 33 + uri: $data['uri'], 34 + cid: $data['cid'], 35 + value: $transformer($data['value']), 36 + ); 37 + } 38 + 39 + /** 40 + * Create without transforming value. 41 + */ 42 + public static function fromArrayRaw(array $data): self 43 + { 44 + return new self( 45 + uri: $data['uri'], 46 + cid: $data['cid'], 47 + value: $data['value'], 48 + ); 49 + } 50 + 51 + public function toArray(): array 52 + { 53 + return [ 54 + 'uri' => $this->uri, 55 + 'cid' => $this->cid, 56 + 'value' => $this->value instanceof Arrayable 57 + ? $this->value->toArray() 58 + : $this->value, 59 + ]; 60 + } 61 + }
+60
src/Data/RecordCollection.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Data; 4 + 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 + 8 + /** 9 + * Collection wrapper for paginated AT Protocol records. 10 + * 11 + * @template T 12 + * @implements Arrayable<string, mixed> 13 + */ 14 + class RecordCollection implements Arrayable 15 + { 16 + /** 17 + * @param Collection<int, Record<T>> $records 18 + */ 19 + public function __construct( 20 + public readonly Collection $records, 21 + public readonly ?string $cursor = null, 22 + ) {} 23 + 24 + /** 25 + * @template U 26 + * @param array $data 27 + * @param callable(array): U $transformer 28 + * @return self<U> 29 + */ 30 + public static function fromArray(array $data, callable $transformer): self 31 + { 32 + return new self( 33 + records: collect($data['records'] ?? [])->map( 34 + fn (array $record) => Record::fromArray($record, $transformer) 35 + ), 36 + cursor: $data['cursor'] ?? null, 37 + ); 38 + } 39 + 40 + /** 41 + * Create without transforming values. 42 + */ 43 + public static function fromArrayRaw(array $data): self 44 + { 45 + return new self( 46 + records: collect($data['records'] ?? [])->map( 47 + fn (array $record) => Record::fromArrayRaw($record) 48 + ), 49 + cursor: $data['cursor'] ?? null, 50 + ); 51 + } 52 + 53 + public function toArray(): array 54 + { 55 + return [ 56 + 'records' => $this->records->map(fn (Record $r) => $r->toArray())->all(), 57 + 'cursor' => $this->cursor, 58 + ]; 59 + } 60 + }
+29
src/Data/Responses/Atproto/Identity/ResolveHandleResponse.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Data\Responses\Atproto\Identity; 4 + 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, string> 9 + */ 10 + class ResolveHandleResponse implements Arrayable 11 + { 12 + public function __construct( 13 + public readonly string $did, 14 + ) {} 15 + 16 + public static function fromArray(array $data): self 17 + { 18 + return new self( 19 + did: $data['did'], 20 + ); 21 + } 22 + 23 + public function toArray(): array 24 + { 25 + return [ 26 + 'did' => $this->did, 27 + ]; 28 + } 29 + }
+15 -1
src/Data/Responses/Atproto/Repo/CreateRecordResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 use SocialDept\AtpSchema\Generated\Com\Atproto\Repo\Defs\CommitMeta; 6 7 - class CreateRecordResponse 8 { 9 public function __construct( 10 public readonly string $uri, ··· 21 commit: isset($data['commit']) ? CommitMeta::fromArray($data['commit']) : null, 22 validationStatus: $data['validationStatus'] ?? null, 23 ); 24 } 25 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 use SocialDept\AtpSchema\Generated\Com\Atproto\Repo\Defs\CommitMeta; 7 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class CreateRecordResponse implements Arrayable 12 { 13 public function __construct( 14 public readonly string $uri, ··· 25 commit: isset($data['commit']) ? CommitMeta::fromArray($data['commit']) : null, 26 validationStatus: $data['validationStatus'] ?? null, 27 ); 28 + } 29 + 30 + public function toArray(): array 31 + { 32 + return [ 33 + 'uri' => $this->uri, 34 + 'cid' => $this->cid, 35 + 'commit' => $this->commit?->toArray(), 36 + 'validationStatus' => $this->validationStatus, 37 + ]; 38 } 39 }
+12 -1
src/Data/Responses/Atproto/Repo/DeleteRecordResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 use SocialDept\AtpSchema\Generated\Com\Atproto\Repo\Defs\CommitMeta; 6 7 - class DeleteRecordResponse 8 { 9 public function __construct( 10 public readonly ?CommitMeta $commit = null, ··· 15 return new self( 16 commit: isset($data['commit']) ? CommitMeta::fromArray($data['commit']) : null, 17 ); 18 } 19 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 use SocialDept\AtpSchema\Generated\Com\Atproto\Repo\Defs\CommitMeta; 7 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class DeleteRecordResponse implements Arrayable 12 { 13 public function __construct( 14 public readonly ?CommitMeta $commit = null, ··· 19 return new self( 20 commit: isset($data['commit']) ? CommitMeta::fromArray($data['commit']) : null, 21 ); 22 + } 23 + 24 + public function toArray(): array 25 + { 26 + return [ 27 + 'commit' => $this->commit?->toArray(), 28 + ]; 29 } 30 }
+17 -1
src/Data/Responses/Atproto/Repo/DescribeRepoResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 - class DescribeRepoResponse 6 { 7 /** 8 * @param array<string> $collections ··· 24 collections: $data['collections'] ?? [], 25 handleIsCorrect: $data['handleIsCorrect'], 26 ); 27 } 28 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, mixed> 9 + */ 10 + class DescribeRepoResponse implements Arrayable 11 { 12 /** 13 * @param array<string> $collections ··· 29 collections: $data['collections'] ?? [], 30 handleIsCorrect: $data['handleIsCorrect'], 31 ); 32 + } 33 + 34 + public function toArray(): array 35 + { 36 + return [ 37 + 'handle' => $this->handle, 38 + 'did' => $this->did, 39 + 'didDoc' => $this->didDoc, 40 + 'collections' => $this->collections, 41 + 'handleIsCorrect' => $this->handleIsCorrect, 42 + ]; 43 } 44 }
+15 -1
src/Data/Responses/Atproto/Repo/GetRecordResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 - class GetRecordResponse 6 { 7 public function __construct( 8 public readonly string $uri, ··· 17 value: $data['value'], 18 cid: $data['cid'] ?? null, 19 ); 20 } 21 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, mixed> 9 + */ 10 + class GetRecordResponse implements Arrayable 11 { 12 public function __construct( 13 public readonly string $uri, ··· 22 value: $data['value'], 23 cid: $data['cid'] ?? null, 24 ); 25 + } 26 + 27 + public function toArray(): array 28 + { 29 + return [ 30 + 'uri' => $this->uri, 31 + 'value' => $this->value, 32 + 'cid' => $this->cid, 33 + ]; 34 } 35 }
+18 -4
src/Data/Responses/Atproto/Repo/ListRecordsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 - class ListRecordsResponse 6 { 7 /** 8 - * @param array<array{uri: string, cid: string, value: mixed}> $records 9 */ 10 public function __construct( 11 - public readonly array $records, 12 public readonly ?string $cursor = null, 13 ) {} 14 15 public static function fromArray(array $data): self 16 { 17 return new self( 18 - records: $data['records'] ?? [], 19 cursor: $data['cursor'] ?? null, 20 ); 21 } 22 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 + 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class ListRecordsResponse implements Arrayable 12 { 13 /** 14 + * @param Collection<int, array{uri: string, cid: string, value: mixed}> $records 15 */ 16 public function __construct( 17 + public readonly Collection $records, 18 public readonly ?string $cursor = null, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + records: collect($data['records'] ?? []), 25 cursor: $data['cursor'] ?? null, 26 ); 27 + } 28 + 29 + public function toArray(): array 30 + { 31 + return [ 32 + 'records' => $this->records->all(), 33 + 'cursor' => $this->cursor, 34 + ]; 35 } 36 }
+15 -1
src/Data/Responses/Atproto/Repo/PutRecordResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 use SocialDept\AtpSchema\Generated\Com\Atproto\Repo\Defs\CommitMeta; 6 7 - class PutRecordResponse 8 { 9 public function __construct( 10 public readonly string $uri, ··· 21 commit: isset($data['commit']) ? CommitMeta::fromArray($data['commit']) : null, 22 validationStatus: $data['validationStatus'] ?? null, 23 ); 24 } 25 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Repo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 use SocialDept\AtpSchema\Generated\Com\Atproto\Repo\Defs\CommitMeta; 7 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class PutRecordResponse implements Arrayable 12 { 13 public function __construct( 14 public readonly string $uri, ··· 25 commit: isset($data['commit']) ? CommitMeta::fromArray($data['commit']) : null, 26 validationStatus: $data['validationStatus'] ?? null, 27 ); 28 + } 29 + 30 + public function toArray(): array 31 + { 32 + return [ 33 + 'uri' => $this->uri, 34 + 'cid' => $this->cid, 35 + 'commit' => $this->commit?->toArray(), 36 + 'validationStatus' => $this->validationStatus, 37 + ]; 38 } 39 }
+18 -1
src/Data/Responses/Atproto/Server/DescribeServerResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Server; 4 5 - class DescribeServerResponse 6 { 7 /** 8 * @param array<string> $availableUserDomains ··· 26 links: $data['links'] ?? null, 27 contact: $data['contact'] ?? null, 28 ); 29 } 30 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Server; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, mixed> 9 + */ 10 + class DescribeServerResponse implements Arrayable 11 { 12 /** 13 * @param array<string> $availableUserDomains ··· 31 links: $data['links'] ?? null, 32 contact: $data['contact'] ?? null, 33 ); 34 + } 35 + 36 + public function toArray(): array 37 + { 38 + return [ 39 + 'did' => $this->did, 40 + 'availableUserDomains' => $this->availableUserDomains, 41 + 'inviteCodeRequired' => $this->inviteCodeRequired, 42 + 'phoneVerificationRequired' => $this->phoneVerificationRequired, 43 + 'links' => $this->links, 44 + 'contact' => $this->contact, 45 + ]; 46 } 47 }
+20 -1
src/Data/Responses/Atproto/Server/GetSessionResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Server; 4 5 - class GetSessionResponse 6 { 7 public function __construct( 8 public readonly string $handle, ··· 27 active: $data['active'] ?? null, 28 status: $data['status'] ?? null, 29 ); 30 } 31 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Server; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, mixed> 9 + */ 10 + class GetSessionResponse implements Arrayable 11 { 12 public function __construct( 13 public readonly string $handle, ··· 32 active: $data['active'] ?? null, 33 status: $data['status'] ?? null, 34 ); 35 + } 36 + 37 + public function toArray(): array 38 + { 39 + return [ 40 + 'handle' => $this->handle, 41 + 'did' => $this->did, 42 + 'email' => $this->email, 43 + 'emailConfirmed' => $this->emailConfirmed, 44 + 'emailAuthFactor' => $this->emailAuthFactor, 45 + 'didDoc' => $this->didDoc, 46 + 'active' => $this->active, 47 + 'status' => $this->status, 48 + ]; 49 } 50 }
+16 -1
src/Data/Responses/Atproto/Sync/GetRepoStatusResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Sync; 4 5 - class GetRepoStatusResponse 6 { 7 public function __construct( 8 public readonly string $did, ··· 19 status: $data['status'] ?? null, 20 rev: $data['rev'] ?? null, 21 ); 22 } 23 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Sync; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, mixed> 9 + */ 10 + class GetRepoStatusResponse implements Arrayable 11 { 12 public function __construct( 13 public readonly string $did, ··· 24 status: $data['status'] ?? null, 25 rev: $data['rev'] ?? null, 26 ); 27 + } 28 + 29 + public function toArray(): array 30 + { 31 + return [ 32 + 'did' => $this->did, 33 + 'active' => $this->active, 34 + 'status' => $this->status, 35 + 'rev' => $this->rev, 36 + ]; 37 } 38 }
+14 -1
src/Data/Responses/Atproto/Sync/ListBlobsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Sync; 4 5 - class ListBlobsResponse 6 { 7 /** 8 * @param array<string> $cids ··· 18 cids: $data['cids'] ?? [], 19 cursor: $data['cursor'] ?? null, 20 ); 21 } 22 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Sync; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, mixed> 9 + */ 10 + class ListBlobsResponse implements Arrayable 11 { 12 /** 13 * @param array<string> $cids ··· 23 cids: $data['cids'] ?? [], 24 cursor: $data['cursor'] ?? null, 25 ); 26 + } 27 + 28 + public function toArray(): array 29 + { 30 + return [ 31 + 'cids' => $this->cids, 32 + 'cursor' => $this->cursor, 33 + ]; 34 } 35 }
+36
src/Data/Responses/Atproto/Sync/ListReposByCollectionResponse.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Data\Responses\Atproto\Sync; 4 + 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 + 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class ListReposByCollectionResponse implements Arrayable 12 + { 13 + /** 14 + * @param Collection<int, array{did: string, rev: string}> $repos 15 + */ 16 + public function __construct( 17 + public readonly Collection $repos, 18 + public readonly ?string $cursor = null, 19 + ) {} 20 + 21 + public static function fromArray(array $data): self 22 + { 23 + return new self( 24 + repos: collect($data['repos'] ?? []), 25 + cursor: $data['cursor'] ?? null, 26 + ); 27 + } 28 + 29 + public function toArray(): array 30 + { 31 + return [ 32 + 'repos' => $this->repos->all(), 33 + 'cursor' => $this->cursor, 34 + ]; 35 + } 36 + }
+18 -4
src/Data/Responses/Atproto/Sync/ListReposResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Sync; 4 5 - class ListReposResponse 6 { 7 /** 8 - * @param array<array{did: string, head: string, rev: string, active?: bool, status?: string}> $repos 9 */ 10 public function __construct( 11 - public readonly array $repos, 12 public readonly ?string $cursor = null, 13 ) {} 14 15 public static function fromArray(array $data): self 16 { 17 return new self( 18 - repos: $data['repos'] ?? [], 19 cursor: $data['cursor'] ?? null, 20 ); 21 } 22 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Atproto\Sync; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 + 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class ListReposResponse implements Arrayable 12 { 13 /** 14 + * @param Collection<int, array{did: string, head: string, rev: string, active?: bool, status?: string}> $repos 15 */ 16 public function __construct( 17 + public readonly Collection $repos, 18 public readonly ?string $cursor = null, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + repos: collect($data['repos'] ?? []), 25 cursor: $data['cursor'] ?? null, 26 ); 27 + } 28 + 29 + public function toArray(): array 30 + { 31 + return [ 32 + 'repos' => $this->repos->all(), 33 + 'cursor' => $this->cursor, 34 + ]; 35 } 36 }
+17 -6
src/Data/Responses/Bsky/Actor/GetProfilesResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Actor; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileViewDetailed; 6 7 - class GetProfilesResponse 8 { 9 /** 10 - * @param array<ProfileViewDetailed> $profiles 11 */ 12 public function __construct( 13 - public readonly array $profiles, 14 ) {} 15 16 public static function fromArray(array $data): self 17 { 18 return new self( 19 - profiles: array_map( 20 - fn (array $profile) => ProfileViewDetailed::fromArray($profile), 21 - $data['profiles'] ?? [] 22 ), 23 ); 24 } 25 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Actor; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileViewDetailed; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetProfilesResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ProfileViewDetailed> $profiles 16 */ 17 public function __construct( 18 + public readonly Collection $profiles, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + profiles: collect($data['profiles'] ?? [])->map( 25 + fn (array $profile) => ProfileViewDetailed::fromArray($profile) 26 ), 27 ); 28 + } 29 + 30 + public function toArray(): array 31 + { 32 + return [ 33 + 'profiles' => $this->profiles->map(fn (ProfileViewDetailed $p) => $p->toArray())->all(), 34 + ]; 35 } 36 }
+18 -6
src/Data/Responses/Bsky/Actor/GetSuggestionsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Actor; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 6 7 - class GetSuggestionsResponse 8 { 9 /** 10 - * @param array<ProfileView> $actors 11 */ 12 public function __construct( 13 - public readonly array $actors, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - actors: array_map( 21 - fn (array $actor) => ProfileView::fromArray($actor), 22 - $data['actors'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Actor; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetSuggestionsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ProfileView> $actors 16 */ 17 public function __construct( 18 + public readonly Collection $actors, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + actors: collect($data['actors'] ?? [])->map( 26 + fn (array $actor) => ProfileView::fromArray($actor) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'actors' => $this->actors->map(fn (ProfileView $a) => $a->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+18 -6
src/Data/Responses/Bsky/Actor/SearchActorsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Actor; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 6 7 - class SearchActorsResponse 8 { 9 /** 10 - * @param array<ProfileView> $actors 11 */ 12 public function __construct( 13 - public readonly array $actors, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - actors: array_map( 21 - fn (array $actor) => ProfileView::fromArray($actor), 22 - $data['actors'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Actor; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class SearchActorsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ProfileView> $actors 16 */ 17 public function __construct( 18 + public readonly Collection $actors, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + actors: collect($data['actors'] ?? [])->map( 26 + fn (array $actor) => ProfileView::fromArray($actor) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'actors' => $this->actors->map(fn (ProfileView $a) => $a->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+17 -6
src/Data/Responses/Bsky/Actor/SearchActorsTypeaheadResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Actor; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileViewBasic; 6 7 - class SearchActorsTypeaheadResponse 8 { 9 /** 10 - * @param array<ProfileViewBasic> $actors 11 */ 12 public function __construct( 13 - public readonly array $actors, 14 ) {} 15 16 public static function fromArray(array $data): self 17 { 18 return new self( 19 - actors: array_map( 20 - fn (array $actor) => ProfileViewBasic::fromArray($actor), 21 - $data['actors'] ?? [] 22 ), 23 ); 24 } 25 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Actor; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileViewBasic; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class SearchActorsTypeaheadResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ProfileViewBasic> $actors 16 */ 17 public function __construct( 18 + public readonly Collection $actors, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + actors: collect($data['actors'] ?? [])->map( 25 + fn (array $actor) => ProfileViewBasic::fromArray($actor) 26 ), 27 ); 28 + } 29 + 30 + public function toArray(): array 31 + { 32 + return [ 33 + 'actors' => $this->actors->map(fn (ProfileViewBasic $a) => $a->toArray())->all(), 34 + ]; 35 } 36 }
+15 -1
src/Data/Responses/Bsky/Feed/DescribeFeedGeneratorResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 - class DescribeFeedGeneratorResponse 6 { 7 /** 8 * @param array<array{uri: string}> $feeds ··· 20 feeds: $data['feeds'] ?? [], 21 links: $data['links'] ?? null, 22 ); 23 } 24 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, mixed> 9 + */ 10 + class DescribeFeedGeneratorResponse implements Arrayable 11 { 12 /** 13 * @param array<array{uri: string}> $feeds ··· 25 feeds: $data['feeds'] ?? [], 26 links: $data['links'] ?? null, 27 ); 28 + } 29 + 30 + public function toArray(): array 31 + { 32 + return [ 33 + 'did' => $this->did, 34 + 'feeds' => $this->feeds, 35 + 'links' => $this->links, 36 + ]; 37 } 38 }
+18 -6
src/Data/Responses/Bsky/Feed/GetActorFeedsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\GeneratorView; 6 7 - class GetActorFeedsResponse 8 { 9 /** 10 - * @param array<GeneratorView> $feeds 11 */ 12 public function __construct( 13 - public readonly array $feeds, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - feeds: array_map( 21 - fn (array $feed) => GeneratorView::fromArray($feed), 22 - $data['feeds'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\GeneratorView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetActorFeedsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, GeneratorView> $feeds 16 */ 17 public function __construct( 18 + public readonly Collection $feeds, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + feeds: collect($data['feeds'] ?? [])->map( 26 + fn (array $feed) => GeneratorView::fromArray($feed) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'feeds' => $this->feeds->map(fn (GeneratorView $f) => $f->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+18 -6
src/Data/Responses/Bsky/Feed/GetActorLikesResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\FeedViewPost; 6 7 - class GetActorLikesResponse 8 { 9 /** 10 - * @param array<FeedViewPost> $feed 11 */ 12 public function __construct( 13 - public readonly array $feed, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - feed: array_map( 21 - fn (array $post) => FeedViewPost::fromArray($post), 22 - $data['feed'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\FeedViewPost; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetActorLikesResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, FeedViewPost> $feed 16 */ 17 public function __construct( 18 + public readonly Collection $feed, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + feed: collect($data['feed'] ?? [])->map( 26 + fn (array $post) => FeedViewPost::fromArray($post) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'feed' => $this->feed->map(fn (FeedViewPost $p) => $p->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+18 -6
src/Data/Responses/Bsky/Feed/GetAuthorFeedResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\FeedViewPost; 6 7 - class GetAuthorFeedResponse 8 { 9 /** 10 - * @param array<FeedViewPost> $feed 11 */ 12 public function __construct( 13 - public readonly array $feed, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - feed: array_map( 21 - fn (array $post) => FeedViewPost::fromArray($post), 22 - $data['feed'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\FeedViewPost; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetAuthorFeedResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, FeedViewPost> $feed 16 */ 17 public function __construct( 18 + public readonly Collection $feed, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + feed: collect($data['feed'] ?? [])->map( 26 + fn (array $post) => FeedViewPost::fromArray($post) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'feed' => $this->feed->map(fn (FeedViewPost $p) => $p->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+14 -1
src/Data/Responses/Bsky/Feed/GetFeedGeneratorResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\GeneratorView; 6 7 - class GetFeedGeneratorResponse 8 { 9 public function __construct( 10 public readonly GeneratorView $view, ··· 19 isOnline: $data['isOnline'], 20 isValid: $data['isValid'], 21 ); 22 } 23 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\GeneratorView; 7 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class GetFeedGeneratorResponse implements Arrayable 12 { 13 public function __construct( 14 public readonly GeneratorView $view, ··· 23 isOnline: $data['isOnline'], 24 isValid: $data['isValid'], 25 ); 26 + } 27 + 28 + public function toArray(): array 29 + { 30 + return [ 31 + 'view' => $this->view->toArray(), 32 + 'isOnline' => $this->isOnline, 33 + 'isValid' => $this->isValid, 34 + ]; 35 } 36 }
+17 -6
src/Data/Responses/Bsky/Feed/GetFeedGeneratorsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\GeneratorView; 6 7 - class GetFeedGeneratorsResponse 8 { 9 /** 10 - * @param array<GeneratorView> $feeds 11 */ 12 public function __construct( 13 - public readonly array $feeds, 14 ) {} 15 16 public static function fromArray(array $data): self 17 { 18 return new self( 19 - feeds: array_map( 20 - fn (array $feed) => GeneratorView::fromArray($feed), 21 - $data['feeds'] ?? [] 22 ), 23 ); 24 } 25 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\GeneratorView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetFeedGeneratorsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, GeneratorView> $feeds 16 */ 17 public function __construct( 18 + public readonly Collection $feeds, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + feeds: collect($data['feeds'] ?? [])->map( 25 + fn (array $feed) => GeneratorView::fromArray($feed) 26 ), 27 ); 28 + } 29 + 30 + public function toArray(): array 31 + { 32 + return [ 33 + 'feeds' => $this->feeds->map(fn (GeneratorView $f) => $f->toArray())->all(), 34 + ]; 35 } 36 }
+18 -6
src/Data/Responses/Bsky/Feed/GetFeedResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\FeedViewPost; 6 7 - class GetFeedResponse 8 { 9 /** 10 - * @param array<FeedViewPost> $feed 11 */ 12 public function __construct( 13 - public readonly array $feed, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - feed: array_map( 21 - fn (array $post) => FeedViewPost::fromArray($post), 22 - $data['feed'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\FeedViewPost; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetFeedResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, FeedViewPost> $feed 16 */ 17 public function __construct( 18 + public readonly Collection $feed, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + feed: collect($data['feed'] ?? [])->map( 26 + fn (array $post) => FeedViewPost::fromArray($post) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'feed' => $this->feed->map(fn (FeedViewPost $p) => $p->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+20 -6
src/Data/Responses/Bsky/Feed/GetLikesResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\GetLikes\Like; 6 7 - class GetLikesResponse 8 { 9 /** 10 - * @param array<Like> $likes 11 */ 12 public function __construct( 13 public readonly string $uri, 14 - public readonly array $likes, 15 public readonly ?string $cid = null, 16 public readonly ?string $cursor = null, 17 ) {} ··· 20 { 21 return new self( 22 uri: $data['uri'], 23 - likes: array_map( 24 - fn (array $like) => Like::fromArray($like), 25 - $data['likes'] ?? [] 26 ), 27 cid: $data['cid'] ?? null, 28 cursor: $data['cursor'] ?? null, 29 ); 30 } 31 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\GetLikes\Like; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetLikesResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, Like> $likes 16 */ 17 public function __construct( 18 public readonly string $uri, 19 + public readonly Collection $likes, 20 public readonly ?string $cid = null, 21 public readonly ?string $cursor = null, 22 ) {} ··· 25 { 26 return new self( 27 uri: $data['uri'], 28 + likes: collect($data['likes'] ?? [])->map( 29 + fn (array $like) => Like::fromArray($like) 30 ), 31 cid: $data['cid'] ?? null, 32 cursor: $data['cursor'] ?? null, 33 ); 34 + } 35 + 36 + public function toArray(): array 37 + { 38 + return [ 39 + 'uri' => $this->uri, 40 + 'likes' => $this->likes->map(fn (Like $l) => $l->toArray())->all(), 41 + 'cid' => $this->cid, 42 + 'cursor' => $this->cursor, 43 + ]; 44 } 45 }
+13 -1
src/Data/Responses/Bsky/Feed/GetPostThreadResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\ThreadViewPost; 6 7 - class GetPostThreadResponse 8 { 9 public function __construct( 10 public readonly ThreadViewPost $thread, ··· 17 thread: ThreadViewPost::fromArray($data['thread']), 18 threadgate: $data['threadgate'] ?? null, 19 ); 20 } 21 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\ThreadViewPost; 7 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class GetPostThreadResponse implements Arrayable 12 { 13 public function __construct( 14 public readonly ThreadViewPost $thread, ··· 21 thread: ThreadViewPost::fromArray($data['thread']), 22 threadgate: $data['threadgate'] ?? null, 23 ); 24 + } 25 + 26 + public function toArray(): array 27 + { 28 + return [ 29 + 'thread' => $this->thread->toArray(), 30 + 'threadgate' => $this->threadgate, 31 + ]; 32 } 33 }
+17 -6
src/Data/Responses/Bsky/Feed/GetPostsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\PostView; 6 7 - class GetPostsResponse 8 { 9 /** 10 - * @param array<PostView> $posts 11 */ 12 public function __construct( 13 - public readonly array $posts, 14 ) {} 15 16 public static function fromArray(array $data): self 17 { 18 return new self( 19 - posts: array_map( 20 - fn (array $post) => PostView::fromArray($post), 21 - $data['posts'] ?? [] 22 ), 23 ); 24 } 25 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\PostView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetPostsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, PostView> $posts 16 */ 17 public function __construct( 18 + public readonly Collection $posts, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + posts: collect($data['posts'] ?? [])->map( 25 + fn (array $post) => PostView::fromArray($post) 26 ), 27 ); 28 + } 29 + 30 + public function toArray(): array 31 + { 32 + return [ 33 + 'posts' => $this->posts->map(fn (PostView $p) => $p->toArray())->all(), 34 + ]; 35 } 36 }
+20 -6
src/Data/Responses/Bsky/Feed/GetQuotesResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\PostView; 6 7 - class GetQuotesResponse 8 { 9 /** 10 - * @param array<PostView> $posts 11 */ 12 public function __construct( 13 public readonly string $uri, 14 - public readonly array $posts, 15 public readonly ?string $cid = null, 16 public readonly ?string $cursor = null, 17 ) {} ··· 20 { 21 return new self( 22 uri: $data['uri'], 23 - posts: array_map( 24 - fn (array $post) => PostView::fromArray($post), 25 - $data['posts'] ?? [] 26 ), 27 cid: $data['cid'] ?? null, 28 cursor: $data['cursor'] ?? null, 29 ); 30 } 31 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\PostView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetQuotesResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, PostView> $posts 16 */ 17 public function __construct( 18 public readonly string $uri, 19 + public readonly Collection $posts, 20 public readonly ?string $cid = null, 21 public readonly ?string $cursor = null, 22 ) {} ··· 25 { 26 return new self( 27 uri: $data['uri'], 28 + posts: collect($data['posts'] ?? [])->map( 29 + fn (array $post) => PostView::fromArray($post) 30 ), 31 cid: $data['cid'] ?? null, 32 cursor: $data['cursor'] ?? null, 33 ); 34 + } 35 + 36 + public function toArray(): array 37 + { 38 + return [ 39 + 'uri' => $this->uri, 40 + 'posts' => $this->posts->map(fn (PostView $p) => $p->toArray())->all(), 41 + 'cid' => $this->cid, 42 + 'cursor' => $this->cursor, 43 + ]; 44 } 45 }
+20 -6
src/Data/Responses/Bsky/Feed/GetRepostedByResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 6 7 - class GetRepostedByResponse 8 { 9 /** 10 - * @param array<ProfileView> $repostedBy 11 */ 12 public function __construct( 13 public readonly string $uri, 14 - public readonly array $repostedBy, 15 public readonly ?string $cid = null, 16 public readonly ?string $cursor = null, 17 ) {} ··· 20 { 21 return new self( 22 uri: $data['uri'], 23 - repostedBy: array_map( 24 - fn (array $profile) => ProfileView::fromArray($profile), 25 - $data['repostedBy'] ?? [] 26 ), 27 cid: $data['cid'] ?? null, 28 cursor: $data['cursor'] ?? null, 29 ); 30 } 31 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetRepostedByResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ProfileView> $repostedBy 16 */ 17 public function __construct( 18 public readonly string $uri, 19 + public readonly Collection $repostedBy, 20 public readonly ?string $cid = null, 21 public readonly ?string $cursor = null, 22 ) {} ··· 25 { 26 return new self( 27 uri: $data['uri'], 28 + repostedBy: collect($data['repostedBy'] ?? [])->map( 29 + fn (array $profile) => ProfileView::fromArray($profile) 30 ), 31 cid: $data['cid'] ?? null, 32 cursor: $data['cursor'] ?? null, 33 ); 34 + } 35 + 36 + public function toArray(): array 37 + { 38 + return [ 39 + 'uri' => $this->uri, 40 + 'repostedBy' => $this->repostedBy->map(fn (ProfileView $p) => $p->toArray())->all(), 41 + 'cid' => $this->cid, 42 + 'cursor' => $this->cursor, 43 + ]; 44 } 45 }
+18 -6
src/Data/Responses/Bsky/Feed/GetSuggestedFeedsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\GeneratorView; 6 7 - class GetSuggestedFeedsResponse 8 { 9 /** 10 - * @param array<GeneratorView> $feeds 11 */ 12 public function __construct( 13 - public readonly array $feeds, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - feeds: array_map( 21 - fn (array $feed) => GeneratorView::fromArray($feed), 22 - $data['feeds'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\GeneratorView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetSuggestedFeedsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, GeneratorView> $feeds 16 */ 17 public function __construct( 18 + public readonly Collection $feeds, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + feeds: collect($data['feeds'] ?? [])->map( 26 + fn (array $feed) => GeneratorView::fromArray($feed) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'feeds' => $this->feeds->map(fn (GeneratorView $f) => $f->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+18 -6
src/Data/Responses/Bsky/Feed/GetTimelineResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\FeedViewPost; 6 7 - class GetTimelineResponse 8 { 9 /** 10 - * @param array<FeedViewPost> $feed 11 */ 12 public function __construct( 13 - public readonly array $feed, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - feed: array_map( 21 - fn (array $post) => FeedViewPost::fromArray($post), 22 - $data['feed'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\FeedViewPost; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetTimelineResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, FeedViewPost> $feed 16 */ 17 public function __construct( 18 + public readonly Collection $feed, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + feed: collect($data['feed'] ?? [])->map( 26 + fn (array $post) => FeedViewPost::fromArray($post) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'feed' => $this->feed->map(fn (FeedViewPost $p) => $p->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+19 -6
src/Data/Responses/Bsky/Feed/SearchPostsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\PostView; 6 7 - class SearchPostsResponse 8 { 9 /** 10 - * @param array<PostView> $posts 11 */ 12 public function __construct( 13 - public readonly array $posts, 14 public readonly ?string $cursor = null, 15 public readonly ?int $hitsTotal = null, 16 ) {} ··· 18 public static function fromArray(array $data): self 19 { 20 return new self( 21 - posts: array_map( 22 - fn (array $post) => PostView::fromArray($post), 23 - $data['posts'] ?? [] 24 ), 25 cursor: $data['cursor'] ?? null, 26 hitsTotal: $data['hitsTotal'] ?? null, 27 ); 28 } 29 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Feed; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\PostView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class SearchPostsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, PostView> $posts 16 */ 17 public function __construct( 18 + public readonly Collection $posts, 19 public readonly ?string $cursor = null, 20 public readonly ?int $hitsTotal = null, 21 ) {} ··· 23 public static function fromArray(array $data): self 24 { 25 return new self( 26 + posts: collect($data['posts'] ?? [])->map( 27 + fn (array $post) => PostView::fromArray($post) 28 ), 29 cursor: $data['cursor'] ?? null, 30 hitsTotal: $data['hitsTotal'] ?? null, 31 ); 32 + } 33 + 34 + public function toArray(): array 35 + { 36 + return [ 37 + 'posts' => $this->posts->map(fn (PostView $p) => $p->toArray())->all(), 38 + 'cursor' => $this->cursor, 39 + 'hitsTotal' => $this->hitsTotal, 40 + ]; 41 } 42 }
+19 -6
src/Data/Responses/Bsky/Graph/GetFollowersResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 6 7 - class GetFollowersResponse 8 { 9 /** 10 - * @param array<ProfileView> $followers 11 */ 12 public function __construct( 13 public readonly ProfileView $subject, 14 - public readonly array $followers, 15 public readonly ?string $cursor = null, 16 ) {} 17 ··· 19 { 20 return new self( 21 subject: ProfileView::fromArray($data['subject']), 22 - followers: array_map( 23 - fn (array $profile) => ProfileView::fromArray($profile), 24 - $data['followers'] ?? [] 25 ), 26 cursor: $data['cursor'] ?? null, 27 ); 28 } 29 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetFollowersResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ProfileView> $followers 16 */ 17 public function __construct( 18 public readonly ProfileView $subject, 19 + public readonly Collection $followers, 20 public readonly ?string $cursor = null, 21 ) {} 22 ··· 24 { 25 return new self( 26 subject: ProfileView::fromArray($data['subject']), 27 + followers: collect($data['followers'] ?? [])->map( 28 + fn (array $profile) => ProfileView::fromArray($profile) 29 ), 30 cursor: $data['cursor'] ?? null, 31 ); 32 + } 33 + 34 + public function toArray(): array 35 + { 36 + return [ 37 + 'subject' => $this->subject->toArray(), 38 + 'followers' => $this->followers->map(fn (ProfileView $p) => $p->toArray())->all(), 39 + 'cursor' => $this->cursor, 40 + ]; 41 } 42 }
+19 -6
src/Data/Responses/Bsky/Graph/GetFollowsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 6 7 - class GetFollowsResponse 8 { 9 /** 10 - * @param array<ProfileView> $follows 11 */ 12 public function __construct( 13 public readonly ProfileView $subject, 14 - public readonly array $follows, 15 public readonly ?string $cursor = null, 16 ) {} 17 ··· 19 { 20 return new self( 21 subject: ProfileView::fromArray($data['subject']), 22 - follows: array_map( 23 - fn (array $profile) => ProfileView::fromArray($profile), 24 - $data['follows'] ?? [] 25 ), 26 cursor: $data['cursor'] ?? null, 27 ); 28 } 29 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetFollowsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ProfileView> $follows 16 */ 17 public function __construct( 18 public readonly ProfileView $subject, 19 + public readonly Collection $follows, 20 public readonly ?string $cursor = null, 21 ) {} 22 ··· 24 { 25 return new self( 26 subject: ProfileView::fromArray($data['subject']), 27 + follows: collect($data['follows'] ?? [])->map( 28 + fn (array $profile) => ProfileView::fromArray($profile) 29 ), 30 cursor: $data['cursor'] ?? null, 31 ); 32 + } 33 + 34 + public function toArray(): array 35 + { 36 + return [ 37 + 'subject' => $this->subject->toArray(), 38 + 'follows' => $this->follows->map(fn (ProfileView $p) => $p->toArray())->all(), 39 + 'cursor' => $this->cursor, 40 + ]; 41 } 42 }
+19 -6
src/Data/Responses/Bsky/Graph/GetKnownFollowersResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 6 7 - class GetKnownFollowersResponse 8 { 9 /** 10 - * @param array<ProfileView> $followers 11 */ 12 public function __construct( 13 public readonly ProfileView $subject, 14 - public readonly array $followers, 15 public readonly ?string $cursor = null, 16 ) {} 17 ··· 19 { 20 return new self( 21 subject: ProfileView::fromArray($data['subject']), 22 - followers: array_map( 23 - fn (array $profile) => ProfileView::fromArray($profile), 24 - $data['followers'] ?? [] 25 ), 26 cursor: $data['cursor'] ?? null, 27 ); 28 } 29 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetKnownFollowersResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ProfileView> $followers 16 */ 17 public function __construct( 18 public readonly ProfileView $subject, 19 + public readonly Collection $followers, 20 public readonly ?string $cursor = null, 21 ) {} 22 ··· 24 { 25 return new self( 26 subject: ProfileView::fromArray($data['subject']), 27 + followers: collect($data['followers'] ?? [])->map( 28 + fn (array $profile) => ProfileView::fromArray($profile) 29 ), 30 cursor: $data['cursor'] ?? null, 31 ); 32 + } 33 + 34 + public function toArray(): array 35 + { 36 + return [ 37 + 'subject' => $this->subject->toArray(), 38 + 'followers' => $this->followers->map(fn (ProfileView $p) => $p->toArray())->all(), 39 + 'cursor' => $this->cursor, 40 + ]; 41 } 42 }
+19 -6
src/Data/Responses/Bsky/Graph/GetListResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\ListItemView; 6 use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\ListView; 7 8 - class GetListResponse 9 { 10 /** 11 - * @param array<ListItemView> $items 12 */ 13 public function __construct( 14 public readonly ListView $list, 15 - public readonly array $items, 16 public readonly ?string $cursor = null, 17 ) {} 18 ··· 20 { 21 return new self( 22 list: ListView::fromArray($data['list']), 23 - items: array_map( 24 - fn (array $item) => ListItemView::fromArray($item), 25 - $data['items'] ?? [] 26 ), 27 cursor: $data['cursor'] ?? null, 28 ); 29 } 30 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\ListItemView; 8 use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\ListView; 9 10 + /** 11 + * @implements Arrayable<string, mixed> 12 + */ 13 + class GetListResponse implements Arrayable 14 { 15 /** 16 + * @param Collection<int, ListItemView> $items 17 */ 18 public function __construct( 19 public readonly ListView $list, 20 + public readonly Collection $items, 21 public readonly ?string $cursor = null, 22 ) {} 23 ··· 25 { 26 return new self( 27 list: ListView::fromArray($data['list']), 28 + items: collect($data['items'] ?? [])->map( 29 + fn (array $item) => ListItemView::fromArray($item) 30 ), 31 cursor: $data['cursor'] ?? null, 32 ); 33 + } 34 + 35 + public function toArray(): array 36 + { 37 + return [ 38 + 'list' => $this->list->toArray(), 39 + 'items' => $this->items->map(fn (ListItemView $i) => $i->toArray())->all(), 40 + 'cursor' => $this->cursor, 41 + ]; 42 } 43 }
+18 -6
src/Data/Responses/Bsky/Graph/GetListsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\ListView; 6 7 - class GetListsResponse 8 { 9 /** 10 - * @param array<ListView> $lists 11 */ 12 public function __construct( 13 - public readonly array $lists, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - lists: array_map( 21 - fn (array $list) => ListView::fromArray($list), 22 - $data['lists'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\ListView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetListsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ListView> $lists 16 */ 17 public function __construct( 18 + public readonly Collection $lists, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + lists: collect($data['lists'] ?? [])->map( 26 + fn (array $list) => ListView::fromArray($list) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'lists' => $this->lists->map(fn (ListView $l) => $l->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+18 -4
src/Data/Responses/Bsky/Graph/GetRelationshipsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 - class GetRelationshipsResponse 6 { 7 /** 8 - * @param array<mixed> $relationships Array of Relationship or NotFoundActor objects 9 */ 10 public function __construct( 11 - public readonly array $relationships, 12 public readonly ?string $actor = null, 13 ) {} 14 15 public static function fromArray(array $data): self 16 { 17 return new self( 18 - relationships: $data['relationships'] ?? [], 19 actor: $data['actor'] ?? null, 20 ); 21 } 22 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 + 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class GetRelationshipsResponse implements Arrayable 12 { 13 /** 14 + * @param Collection<int, mixed> $relationships Collection of Relationship or NotFoundActor objects 15 */ 16 public function __construct( 17 + public readonly Collection $relationships, 18 public readonly ?string $actor = null, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + relationships: collect($data['relationships'] ?? []), 25 actor: $data['actor'] ?? null, 26 ); 27 + } 28 + 29 + public function toArray(): array 30 + { 31 + return [ 32 + 'relationships' => $this->relationships->all(), 33 + 'actor' => $this->actor, 34 + ]; 35 } 36 }
+17 -6
src/Data/Responses/Bsky/Graph/GetStarterPacksResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\StarterPackViewBasic; 6 7 - class GetStarterPacksResponse 8 { 9 /** 10 - * @param array<StarterPackViewBasic> $starterPacks 11 */ 12 public function __construct( 13 - public readonly array $starterPacks, 14 ) {} 15 16 public static function fromArray(array $data): self 17 { 18 return new self( 19 - starterPacks: array_map( 20 - fn (array $pack) => StarterPackViewBasic::fromArray($pack), 21 - $data['starterPacks'] ?? [] 22 ), 23 ); 24 } 25 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Defs\StarterPackViewBasic; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetStarterPacksResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, StarterPackViewBasic> $starterPacks 16 */ 17 public function __construct( 18 + public readonly Collection $starterPacks, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + starterPacks: collect($data['starterPacks'] ?? [])->map( 25 + fn (array $pack) => StarterPackViewBasic::fromArray($pack) 26 ), 27 ); 28 + } 29 + 30 + public function toArray(): array 31 + { 32 + return [ 33 + 'starterPacks' => $this->starterPacks->map(fn (StarterPackViewBasic $p) => $p->toArray())->all(), 34 + ]; 35 } 36 }
+18 -6
src/Data/Responses/Bsky/Graph/GetSuggestedFollowsByActorResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 6 7 - class GetSuggestedFollowsByActorResponse 8 { 9 /** 10 - * @param array<ProfileView> $suggestions 11 */ 12 public function __construct( 13 - public readonly array $suggestions, 14 public readonly ?bool $isFallback = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - suggestions: array_map( 21 - fn (array $profile) => ProfileView::fromArray($profile), 22 - $data['suggestions'] ?? [] 23 ), 24 isFallback: $data['isFallback'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Graph; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Actor\Defs\ProfileView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetSuggestedFollowsByActorResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ProfileView> $suggestions 16 */ 17 public function __construct( 18 + public readonly Collection $suggestions, 19 public readonly ?bool $isFallback = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + suggestions: collect($data['suggestions'] ?? [])->map( 26 + fn (array $profile) => ProfileView::fromArray($profile) 27 ), 28 isFallback: $data['isFallback'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'suggestions' => $this->suggestions->map(fn (ProfileView $p) => $p->toArray())->all(), 36 + 'isFallback' => $this->isFallback, 37 + ]; 38 } 39 }
+17 -6
src/Data/Responses/Bsky/Labeler/GetServicesResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Labeler; 4 5 use SocialDept\AtpSchema\Generated\App\Bsky\Labeler\Defs\LabelerView; 6 use SocialDept\AtpSchema\Generated\App\Bsky\Labeler\Defs\LabelerViewDetailed; 7 8 - class GetServicesResponse 9 { 10 /** 11 - * @param array<LabelerView|LabelerViewDetailed> $views 12 */ 13 public function __construct( 14 - public readonly array $views, 15 ) {} 16 17 public static function fromArray(array $data, bool $detailed = false): self 18 { 19 return new self( 20 - views: array_map( 21 fn (array $view) => $detailed 22 ? LabelerViewDetailed::fromArray($view) 23 - : LabelerView::fromArray($view), 24 - $data['views'] ?? [] 25 ), 26 ); 27 } 28 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Bsky\Labeler; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\App\Bsky\Labeler\Defs\LabelerView; 8 use SocialDept\AtpSchema\Generated\App\Bsky\Labeler\Defs\LabelerViewDetailed; 9 10 + /** 11 + * @implements Arrayable<string, mixed> 12 + */ 13 + class GetServicesResponse implements Arrayable 14 { 15 /** 16 + * @param Collection<int, LabelerView|LabelerViewDetailed> $views 17 */ 18 public function __construct( 19 + public readonly Collection $views, 20 ) {} 21 22 public static function fromArray(array $data, bool $detailed = false): self 23 { 24 return new self( 25 + views: collect($data['views'] ?? [])->map( 26 fn (array $view) => $detailed 27 ? LabelerViewDetailed::fromArray($view) 28 + : LabelerView::fromArray($view) 29 ), 30 ); 31 + } 32 + 33 + public function toArray(): array 34 + { 35 + return [ 36 + 'views' => $this->views->map(fn (LabelerView|LabelerViewDetailed $v) => $v->toArray())->all(), 37 + ]; 38 } 39 }
+18 -4
src/Data/Responses/Chat/Convo/GetLogResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 - class GetLogResponse 6 { 7 /** 8 - * @param array<mixed> $logs Array of log event objects (LogBeginConvo, LogCreateMessage, etc.) 9 */ 10 public function __construct( 11 - public readonly array $logs, 12 public readonly ?string $cursor = null, 13 ) {} 14 15 public static function fromArray(array $data): self 16 { 17 return new self( 18 - logs: $data['logs'] ?? [], 19 cursor: $data['cursor'] ?? null, 20 ); 21 } 22 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 + 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class GetLogResponse implements Arrayable 12 { 13 /** 14 + * @param Collection<int, mixed> $logs Collection of log event objects (LogBeginConvo, LogCreateMessage, etc.) 15 */ 16 public function __construct( 17 + public readonly Collection $logs, 18 public readonly ?string $cursor = null, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + logs: collect($data['logs'] ?? []), 25 cursor: $data['cursor'] ?? null, 26 ); 27 + } 28 + 29 + public function toArray(): array 30 + { 31 + return [ 32 + 'logs' => $this->logs->all(), 33 + 'cursor' => $this->cursor, 34 + ]; 35 } 36 }
+18 -6
src/Data/Responses/Chat/Convo/GetMessagesResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\DeletedMessageView; 6 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\MessageView; 7 8 - class GetMessagesResponse 9 { 10 /** 11 - * @param array<MessageView|DeletedMessageView> $messages 12 */ 13 public function __construct( 14 - public readonly array $messages, 15 public readonly ?string $cursor = null, 16 ) {} 17 18 public static function fromArray(array $data): self 19 { 20 return new self( 21 - messages: array_map( 22 function (array $message) { 23 if (isset($message['$type']) && $message['$type'] === 'chat.bsky.convo.defs#deletedMessageView') { 24 return DeletedMessageView::fromArray($message); 25 } 26 27 return MessageView::fromArray($message); 28 - }, 29 - $data['messages'] ?? [] 30 ), 31 cursor: $data['cursor'] ?? null, 32 ); 33 } 34 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\DeletedMessageView; 8 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\MessageView; 9 10 + /** 11 + * @implements Arrayable<string, mixed> 12 + */ 13 + class GetMessagesResponse implements Arrayable 14 { 15 /** 16 + * @param Collection<int, MessageView|DeletedMessageView> $messages 17 */ 18 public function __construct( 19 + public readonly Collection $messages, 20 public readonly ?string $cursor = null, 21 ) {} 22 23 public static function fromArray(array $data): self 24 { 25 return new self( 26 + messages: collect($data['messages'] ?? [])->map( 27 function (array $message) { 28 if (isset($message['$type']) && $message['$type'] === 'chat.bsky.convo.defs#deletedMessageView') { 29 return DeletedMessageView::fromArray($message); 30 } 31 32 return MessageView::fromArray($message); 33 + } 34 ), 35 cursor: $data['cursor'] ?? null, 36 ); 37 + } 38 + 39 + public function toArray(): array 40 + { 41 + return [ 42 + 'messages' => $this->messages->map(fn (MessageView|DeletedMessageView $m) => $m->toArray())->all(), 43 + 'cursor' => $this->cursor, 44 + ]; 45 } 46 }
+14 -1
src/Data/Responses/Chat/Convo/LeaveConvoResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 - class LeaveConvoResponse 6 { 7 public function __construct( 8 public readonly string $convoId, ··· 15 convoId: $data['convoId'], 16 rev: $data['rev'], 17 ); 18 } 19 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, mixed> 9 + */ 10 + class LeaveConvoResponse implements Arrayable 11 { 12 public function __construct( 13 public readonly string $convoId, ··· 20 convoId: $data['convoId'], 21 rev: $data['rev'], 22 ); 23 + } 24 + 25 + public function toArray(): array 26 + { 27 + return [ 28 + 'convoId' => $this->convoId, 29 + 'rev' => $this->rev, 30 + ]; 31 } 32 }
+18 -6
src/Data/Responses/Chat/Convo/ListConvosResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\ConvoView; 6 7 - class ListConvosResponse 8 { 9 /** 10 - * @param array<ConvoView> $convos 11 */ 12 public function __construct( 13 - public readonly array $convos, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - convos: array_map( 21 - fn (array $convo) => ConvoView::fromArray($convo), 22 - $data['convos'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\ConvoView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class ListConvosResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ConvoView> $convos 16 */ 17 public function __construct( 18 + public readonly Collection $convos, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + convos: collect($data['convos'] ?? [])->map( 26 + fn (array $convo) => ConvoView::fromArray($convo) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'convos' => $this->convos->map(fn (ConvoView $c) => $c->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+17 -6
src/Data/Responses/Chat/Convo/SendMessageBatchResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\MessageView; 6 7 - class SendMessageBatchResponse 8 { 9 /** 10 - * @param array<MessageView> $items 11 */ 12 public function __construct( 13 - public readonly array $items, 14 ) {} 15 16 public static function fromArray(array $data): self 17 { 18 return new self( 19 - items: array_map( 20 - fn (array $item) => MessageView::fromArray($item), 21 - $data['items'] ?? [] 22 ), 23 ); 24 } 25 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Chat\Convo; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\Chat\Bsky\Convo\Defs\MessageView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class SendMessageBatchResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, MessageView> $items 16 */ 17 public function __construct( 18 + public readonly Collection $items, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + items: collect($data['items'] ?? [])->map( 25 + fn (array $item) => MessageView::fromArray($item) 26 ), 27 ); 28 + } 29 + 30 + public function toArray(): array 31 + { 32 + return [ 33 + 'items' => $this->items->map(fn (MessageView $m) => $m->toArray())->all(), 34 + ]; 35 } 36 }
+25
src/Data/Responses/EmptyResponse.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Data\Responses; 4 + 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * Response class for endpoints that return empty objects. 9 + * 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class EmptyResponse implements Arrayable 13 + { 14 + public function __construct() {} 15 + 16 + public static function fromArray(array $data): self 17 + { 18 + return new self; 19 + } 20 + 21 + public function toArray(): array 22 + { 23 + return []; 24 + } 25 + }
+18 -6
src/Data/Responses/Ozone/Moderation/QueryEventsResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Moderation; 4 5 use SocialDept\AtpSchema\Generated\Tools\Ozone\Moderation\Defs\ModEventView; 6 7 - class QueryEventsResponse 8 { 9 /** 10 - * @param array<ModEventView> $events 11 */ 12 public function __construct( 13 - public readonly array $events, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - events: array_map( 21 - fn (array $event) => ModEventView::fromArray($event), 22 - $data['events'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Moderation; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\Tools\Ozone\Moderation\Defs\ModEventView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class QueryEventsResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, ModEventView> $events 16 */ 17 public function __construct( 18 + public readonly Collection $events, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + events: collect($data['events'] ?? [])->map( 26 + fn (array $event) => ModEventView::fromArray($event) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'events' => $this->events->map(fn (ModEventView $e) => $e->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+18 -6
src/Data/Responses/Ozone/Moderation/QueryStatusesResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Moderation; 4 5 use SocialDept\AtpSchema\Generated\Tools\Ozone\Moderation\Defs\SubjectStatusView; 6 7 - class QueryStatusesResponse 8 { 9 /** 10 - * @param array<SubjectStatusView> $subjectStatuses 11 */ 12 public function __construct( 13 - public readonly array $subjectStatuses, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - subjectStatuses: array_map( 21 - fn (array $status) => SubjectStatusView::fromArray($status), 22 - $data['subjectStatuses'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Moderation; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\Tools\Ozone\Moderation\Defs\SubjectStatusView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class QueryStatusesResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, SubjectStatusView> $subjectStatuses 16 */ 17 public function __construct( 18 + public readonly Collection $subjectStatuses, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + subjectStatuses: collect($data['subjectStatuses'] ?? [])->map( 26 + fn (array $status) => SubjectStatusView::fromArray($status) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'subjectStatuses' => $this->subjectStatuses->map(fn (SubjectStatusView $s) => $s->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+18 -6
src/Data/Responses/Ozone/Moderation/SearchReposResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Moderation; 4 5 use SocialDept\AtpSchema\Generated\Tools\Ozone\Moderation\Defs\RepoView; 6 7 - class SearchReposResponse 8 { 9 /** 10 - * @param array<RepoView> $repos 11 */ 12 public function __construct( 13 - public readonly array $repos, 14 public readonly ?string $cursor = null, 15 ) {} 16 17 public static function fromArray(array $data): self 18 { 19 return new self( 20 - repos: array_map( 21 - fn (array $repo) => RepoView::fromArray($repo), 22 - $data['repos'] ?? [] 23 ), 24 cursor: $data['cursor'] ?? null, 25 ); 26 } 27 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Moderation; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 use SocialDept\AtpSchema\Generated\Tools\Ozone\Moderation\Defs\RepoView; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class SearchReposResponse implements Arrayable 13 { 14 /** 15 + * @param Collection<int, RepoView> $repos 16 */ 17 public function __construct( 18 + public readonly Collection $repos, 19 public readonly ?string $cursor = null, 20 ) {} 21 22 public static function fromArray(array $data): self 23 { 24 return new self( 25 + repos: collect($data['repos'] ?? [])->map( 26 + fn (array $repo) => RepoView::fromArray($repo) 27 ), 28 cursor: $data['cursor'] ?? null, 29 ); 30 + } 31 + 32 + public function toArray(): array 33 + { 34 + return [ 35 + 'repos' => $this->repos->map(fn (RepoView $r) => $r->toArray())->all(), 36 + 'cursor' => $this->cursor, 37 + ]; 38 } 39 }
+17 -1
src/Data/Responses/Ozone/Server/GetConfigResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Server; 4 5 use SocialDept\AtpSchema\Generated\Tools\Ozone\Server\GetConfig\ServiceConfig; 6 use SocialDept\AtpSchema\Generated\Tools\Ozone\Server\GetConfig\ViewerConfig; 7 8 - class GetConfigResponse 9 { 10 public function __construct( 11 public readonly ?ServiceConfig $appview = null, ··· 26 viewer: isset($data['viewer']) ? ViewerConfig::fromArray($data['viewer']) : null, 27 verifierDid: $data['verifierDid'] ?? null, 28 ); 29 } 30 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Server; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 use SocialDept\AtpSchema\Generated\Tools\Ozone\Server\GetConfig\ServiceConfig; 7 use SocialDept\AtpSchema\Generated\Tools\Ozone\Server\GetConfig\ViewerConfig; 8 9 + /** 10 + * @implements Arrayable<string, mixed> 11 + */ 12 + class GetConfigResponse implements Arrayable 13 { 14 public function __construct( 15 public readonly ?ServiceConfig $appview = null, ··· 30 viewer: isset($data['viewer']) ? ViewerConfig::fromArray($data['viewer']) : null, 31 verifierDid: $data['verifierDid'] ?? null, 32 ); 33 + } 34 + 35 + public function toArray(): array 36 + { 37 + return [ 38 + 'appview' => $this->appview?->toArray(), 39 + 'pds' => $this->pds?->toArray(), 40 + 'blobDivert' => $this->blobDivert?->toArray(), 41 + 'chat' => $this->chat?->toArray(), 42 + 'viewer' => $this->viewer?->toArray(), 43 + 'verifierDid' => $this->verifierDid, 44 + ]; 45 } 46 }
+18 -4
src/Data/Responses/Ozone/Team/ListMembersResponse.php
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Team; 4 5 - class ListMembersResponse 6 { 7 /** 8 - * @param array<array<string, mixed>> $members Array of team member objects 9 */ 10 public function __construct( 11 - public readonly array $members, 12 public readonly ?string $cursor = null, 13 ) {} 14 15 public static function fromArray(array $data): self 16 { 17 return new self( 18 - members: $data['members'] ?? [], 19 cursor: $data['cursor'] ?? null, 20 ); 21 } 22 }
··· 2 3 namespace SocialDept\AtpClient\Data\Responses\Ozone\Team; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + use Illuminate\Support\Collection; 7 + 8 + /** 9 + * @implements Arrayable<string, mixed> 10 + */ 11 + class ListMembersResponse implements Arrayable 12 { 13 /** 14 + * @param Collection<int, array<string, mixed>> $members Collection of team member objects 15 */ 16 public function __construct( 17 + public readonly Collection $members, 18 public readonly ?string $cursor = null, 19 ) {} 20 21 public static function fromArray(array $data): self 22 { 23 return new self( 24 + members: collect($data['members'] ?? []), 25 cursor: $data['cursor'] ?? null, 26 ); 27 + } 28 + 29 + public function toArray(): array 30 + { 31 + return [ 32 + 'members' => $this->members->all(), 33 + 'cursor' => $this->cursor, 34 + ]; 35 } 36 }
+44
src/Data/Responses/Ozone/Team/MemberResponse.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Data\Responses\Ozone\Team; 4 + 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, mixed> 9 + */ 10 + class MemberResponse implements Arrayable 11 + { 12 + public function __construct( 13 + public readonly string $did, 14 + public readonly bool $disabled, 15 + public readonly ?string $role = null, 16 + public readonly ?string $createdAt = null, 17 + public readonly ?string $updatedAt = null, 18 + public readonly ?string $lastUpdatedBy = null, 19 + ) {} 20 + 21 + public static function fromArray(array $data): self 22 + { 23 + return new self( 24 + did: $data['did'], 25 + disabled: $data['disabled'] ?? false, 26 + role: $data['role'] ?? null, 27 + createdAt: $data['createdAt'] ?? null, 28 + updatedAt: $data['updatedAt'] ?? null, 29 + lastUpdatedBy: $data['lastUpdatedBy'] ?? null, 30 + ); 31 + } 32 + 33 + public function toArray(): array 34 + { 35 + return array_filter([ 36 + 'did' => $this->did, 37 + 'disabled' => $this->disabled, 38 + 'role' => $this->role, 39 + 'createdAt' => $this->createdAt, 40 + 'updatedAt' => $this->updatedAt, 41 + 'lastUpdatedBy' => $this->lastUpdatedBy, 42 + ], fn ($v) => $v !== null); 43 + } 44 + }
+6 -1
src/Data/StrongRef.php
··· 2 3 namespace SocialDept\AtpClient\Data; 4 5 - class StrongRef 6 { 7 public function __construct( 8 public readonly string $uri,
··· 2 3 namespace SocialDept\AtpClient\Data; 4 5 + use Illuminate\Contracts\Support\Arrayable; 6 + 7 + /** 8 + * @implements Arrayable<string, string> 9 + */ 10 + class StrongRef implements Arrayable 11 { 12 public function __construct( 13 public readonly string $uri,
+12
src/Enums/Nsid/AtprotoIdentity.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum AtprotoIdentity: string 8 + { 9 + use HasScopeHelpers; 10 + case ResolveHandle = 'com.atproto.identity.resolveHandle'; 11 + case UpdateHandle = 'com.atproto.identity.updateHandle'; 12 + }
+17
src/Enums/Nsid/AtprotoRepo.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum AtprotoRepo: string 8 + { 9 + use HasScopeHelpers; 10 + case CreateRecord = 'com.atproto.repo.createRecord'; 11 + case DeleteRecord = 'com.atproto.repo.deleteRecord'; 12 + case PutRecord = 'com.atproto.repo.putRecord'; 13 + case GetRecord = 'com.atproto.repo.getRecord'; 14 + case ListRecords = 'com.atproto.repo.listRecords'; 15 + case UploadBlob = 'com.atproto.repo.uploadBlob'; 16 + case DescribeRepo = 'com.atproto.repo.describeRepo'; 17 + }
+14
src/Enums/Nsid/AtprotoServer.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum AtprotoServer: string 8 + { 9 + use HasScopeHelpers; 10 + case CreateSession = 'com.atproto.server.createSession'; 11 + case RefreshSession = 'com.atproto.server.refreshSession'; 12 + case GetSession = 'com.atproto.server.getSession'; 13 + case DescribeServer = 'com.atproto.server.describeServer'; 14 + }
+19
src/Enums/Nsid/AtprotoSync.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum AtprotoSync: string 8 + { 9 + use HasScopeHelpers; 10 + case GetBlob = 'com.atproto.sync.getBlob'; 11 + case GetRepo = 'com.atproto.sync.getRepo'; 12 + case ListRepos = 'com.atproto.sync.listRepos'; 13 + case ListReposByCollection = 'com.atproto.sync.listReposByCollection'; 14 + case GetLatestCommit = 'com.atproto.sync.getLatestCommit'; 15 + case GetRecord = 'com.atproto.sync.getRecord'; 16 + case ListBlobs = 'com.atproto.sync.listBlobs'; 17 + case GetBlocks = 'com.atproto.sync.getBlocks'; 18 + case GetRepoStatus = 'com.atproto.sync.getRepoStatus'; 19 + }
+18
src/Enums/Nsid/BskyActor.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum BskyActor: string 8 + { 9 + use HasScopeHelpers; 10 + case GetProfile = 'app.bsky.actor.getProfile'; 11 + case GetProfiles = 'app.bsky.actor.getProfiles'; 12 + case GetSuggestions = 'app.bsky.actor.getSuggestions'; 13 + case SearchActors = 'app.bsky.actor.searchActors'; 14 + case SearchActorsTypeahead = 'app.bsky.actor.searchActorsTypeahead'; 15 + 16 + // Record type 17 + case Profile = 'app.bsky.actor.profile'; 18 + }
+29
src/Enums/Nsid/BskyFeed.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum BskyFeed: string 8 + { 9 + use HasScopeHelpers; 10 + case DescribeFeedGenerator = 'app.bsky.feed.describeFeedGenerator'; 11 + case GetAuthorFeed = 'app.bsky.feed.getAuthorFeed'; 12 + case GetActorFeeds = 'app.bsky.feed.getActorFeeds'; 13 + case GetActorLikes = 'app.bsky.feed.getActorLikes'; 14 + case GetFeed = 'app.bsky.feed.getFeed'; 15 + case GetFeedGenerator = 'app.bsky.feed.getFeedGenerator'; 16 + case GetFeedGenerators = 'app.bsky.feed.getFeedGenerators'; 17 + case GetLikes = 'app.bsky.feed.getLikes'; 18 + case GetPostThread = 'app.bsky.feed.getPostThread'; 19 + case GetPosts = 'app.bsky.feed.getPosts'; 20 + case GetQuotes = 'app.bsky.feed.getQuotes'; 21 + case GetRepostedBy = 'app.bsky.feed.getRepostedBy'; 22 + case GetSuggestedFeeds = 'app.bsky.feed.getSuggestedFeeds'; 23 + case GetTimeline = 'app.bsky.feed.getTimeline'; 24 + case SearchPosts = 'app.bsky.feed.searchPosts'; 25 + 26 + // Record types 27 + case Post = 'app.bsky.feed.post'; 28 + case Like = 'app.bsky.feed.like'; 29 + }
+22
src/Enums/Nsid/BskyGraph.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum BskyGraph: string 8 + { 9 + use HasScopeHelpers; 10 + case GetFollowers = 'app.bsky.graph.getFollowers'; 11 + case GetFollows = 'app.bsky.graph.getFollows'; 12 + case GetKnownFollowers = 'app.bsky.graph.getKnownFollowers'; 13 + case GetList = 'app.bsky.graph.getList'; 14 + case GetLists = 'app.bsky.graph.getLists'; 15 + case GetRelationships = 'app.bsky.graph.getRelationships'; 16 + case GetStarterPack = 'app.bsky.graph.getStarterPack'; 17 + case GetStarterPacks = 'app.bsky.graph.getStarterPacks'; 18 + case GetSuggestedFollowsByActor = 'app.bsky.graph.getSuggestedFollowsByActor'; 19 + 20 + // Record type 21 + case Follow = 'app.bsky.graph.follow'; 22 + }
+11
src/Enums/Nsid/BskyLabeler.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum BskyLabeler: string 8 + { 9 + use HasScopeHelpers; 10 + case GetServices = 'app.bsky.labeler.getServices'; 11 + }
+13
src/Enums/Nsid/ChatActor.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum ChatActor: string 8 + { 9 + use HasScopeHelpers; 10 + case GetActorMetadata = 'chat.bsky.actor.getActorMetadata'; 11 + case ExportAccountData = 'chat.bsky.actor.exportAccountData'; 12 + case DeleteAccount = 'chat.bsky.actor.deleteAccount'; 13 + }
+22
src/Enums/Nsid/ChatConvo.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum ChatConvo: string 8 + { 9 + use HasScopeHelpers; 10 + case GetConvo = 'chat.bsky.convo.getConvo'; 11 + case GetConvoForMembers = 'chat.bsky.convo.getConvoForMembers'; 12 + case ListConvos = 'chat.bsky.convo.listConvos'; 13 + case GetMessages = 'chat.bsky.convo.getMessages'; 14 + case SendMessage = 'chat.bsky.convo.sendMessage'; 15 + case SendMessageBatch = 'chat.bsky.convo.sendMessageBatch'; 16 + case DeleteMessageForSelf = 'chat.bsky.convo.deleteMessageForSelf'; 17 + case UpdateRead = 'chat.bsky.convo.updateRead'; 18 + case MuteConvo = 'chat.bsky.convo.muteConvo'; 19 + case UnmuteConvo = 'chat.bsky.convo.unmuteConvo'; 20 + case LeaveConvo = 'chat.bsky.convo.leaveConvo'; 21 + case GetLog = 'chat.bsky.convo.getLog'; 22 + }
+34
src/Enums/Nsid/Concerns/HasScopeHelpers.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid\Concerns; 4 + 5 + trait HasScopeHelpers 6 + { 7 + /** 8 + * Get the RPC scope format for this NSID. 9 + * 10 + * @example BskyActor::GetProfile->rpc() // "rpc:app.bsky.actor.getProfile" 11 + */ 12 + public function rpc(): string 13 + { 14 + return 'rpc:' . $this->value; 15 + } 16 + 17 + /** 18 + * Get the repo scope format for this NSID. 19 + * 20 + * @example BskyGraph::Follow->repo(['create']) // "repo:app.bsky.graph.follow?action=create" 21 + * @example BskyFeed::Post->repo(['create', 'delete']) // "repo:app.bsky.feed.post?action=create&action=delete" 22 + * @example BskyFeed::Post->repo() // "repo:app.bsky.feed.post" 23 + */ 24 + public function repo(array $actions = []): string 25 + { 26 + $scope = 'repo:' . $this->value; 27 + 28 + if (! empty($actions)) { 29 + $scope .= '?' . implode('&', array_map(fn ($action) => "action={$action}", $actions)); 30 + } 31 + 32 + return $scope; 33 + } 34 + }
+18
src/Enums/Nsid/OzoneModeration.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum OzoneModeration: string 8 + { 9 + use HasScopeHelpers; 10 + case GetEvent = 'tools.ozone.moderation.getEvent'; 11 + case GetEvents = 'tools.ozone.moderation.getEvents'; 12 + case GetRecord = 'tools.ozone.moderation.getRecord'; 13 + case GetRepo = 'tools.ozone.moderation.getRepo'; 14 + case QueryEvents = 'tools.ozone.moderation.queryEvents'; 15 + case QueryStatuses = 'tools.ozone.moderation.queryStatuses'; 16 + case SearchRepos = 'tools.ozone.moderation.searchRepos'; 17 + case EmitEvent = 'tools.ozone.moderation.emitEvent'; 18 + }
+12
src/Enums/Nsid/OzoneServer.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum OzoneServer: string 8 + { 9 + use HasScopeHelpers; 10 + case GetBlob = 'tools.ozone.server.getBlob'; 11 + case GetConfig = 'tools.ozone.server.getConfig'; 12 + }
+15
src/Enums/Nsid/OzoneTeam.php
···
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Enums\Nsid; 4 + 5 + use SocialDept\AtpClient\Enums\Nsid\Concerns\HasScopeHelpers; 6 + 7 + enum OzoneTeam: string 8 + { 9 + use HasScopeHelpers; 10 + case GetMember = 'tools.ozone.team.getMember'; 11 + case ListMembers = 'tools.ozone.team.listMembers'; 12 + case AddMember = 'tools.ozone.team.addMember'; 13 + case UpdateMember = 'tools.ozone.team.updateMember'; 14 + case DeleteMember = 'tools.ozone.team.deleteMember'; 15 + }
+10 -5
src/Enums/Scope.php
··· 2 3 namespace SocialDept\AtpClient\Enums; 4 5 enum Scope: string 6 { 7 // Transition scopes (current) ··· 13 /** 14 * Build a repo scope string for record operations. 15 * 16 - * @param string $collection The collection NSID (e.g., 'app.bsky.feed.post') 17 - * @param string|null $action The action (create, update, delete) 18 */ 19 - public static function repo(string $collection, ?string $action = null): string 20 { 21 $scope = "repo:{$collection}"; 22 23 - if ($action !== null) { 24 - $scope .= "?action={$action}"; 25 } 26 27 return $scope;
··· 2 3 namespace SocialDept\AtpClient\Enums; 4 5 + use BackedEnum; 6 + 7 enum Scope: string 8 { 9 // Transition scopes (current) ··· 15 /** 16 * Build a repo scope string for record operations. 17 * 18 + * @param string|BackedEnum $collection The collection NSID (e.g., 'app.bsky.feed.post') 19 + * @param array|null $actions The action (create, update, delete) 20 + * 21 + * @return string 22 */ 23 + public static function repo(string|BackedEnum $collection, ?array $actions = []): string 24 { 25 + $collection = $collection instanceof BackedEnum ? $collection->value : $collection; 26 $scope = "repo:{$collection}"; 27 28 + if (!empty($actions)) { 29 + $scope .= '?' . implode('&', array_map(fn ($action) => "action={$action}", $actions)); 30 } 31 32 return $scope;
+2 -3
src/Facades/Atp.php
··· 3 namespace SocialDept\AtpClient\Facades; 4 5 use Illuminate\Support\Facades\Facade; 6 use SocialDept\AtpClient\Auth\OAuthEngine; 7 - use SocialDept\AtpClient\Client\AtpClient; 8 - use SocialDept\AtpClient\Client\Public\AtpPublicClient; 9 use SocialDept\AtpClient\Contracts\CredentialProvider; 10 11 /** 12 * @method static AtpClient as(string $actor) 13 * @method static AtpClient login(string $actor, string $password) 14 * @method static OAuthEngine oauth() 15 - * @method static AtpPublicClient public(?string $service = null) 16 * @method static void setDefaultProvider(CredentialProvider $provider) 17 * 18 * @see \SocialDept\AtpClient\AtpClientServiceProvider
··· 3 namespace SocialDept\AtpClient\Facades; 4 5 use Illuminate\Support\Facades\Facade; 6 + use SocialDept\AtpClient\AtpClient; 7 use SocialDept\AtpClient\Auth\OAuthEngine; 8 use SocialDept\AtpClient\Contracts\CredentialProvider; 9 10 /** 11 * @method static AtpClient as(string $actor) 12 * @method static AtpClient login(string $actor, string $password) 13 * @method static OAuthEngine oauth() 14 + * @method static AtpClient public(?string $service = null) 15 * @method static void setDefaultProvider(CredentialProvider $provider) 16 * 17 * @see \SocialDept\AtpClient\AtpClientServiceProvider
+11 -8
src/Http/HasHttp.php
··· 2 3 namespace SocialDept\AtpClient\Http; 4 5 use Illuminate\Http\Client\Response as LaravelResponse; 6 use Illuminate\Support\Facades\Http; 7 use InvalidArgumentException; ··· 15 16 trait HasHttp 17 { 18 - protected SessionManager $sessions; 19 20 - protected string $did; 21 22 - protected DPoPClient $dpopClient; 23 24 protected ?ScopeChecker $scopeChecker = null; 25 ··· 27 * Make XRPC call 28 */ 29 protected function call( 30 - string $endpoint, 31 string $method, 32 ?array $params = null, 33 ?array $body = null 34 ): Response { 35 $session = $this->sessions->ensureValid($this->did); 36 $url = rtrim($session->pdsEndpoint(), '/').'/xrpc/'.$endpoint; 37 ··· 102 /** 103 * Make GET request 104 */ 105 - public function get(string $endpoint, array $params = []): Response 106 { 107 return $this->call($endpoint, 'GET', $params); 108 } ··· 110 /** 111 * Make POST request 112 */ 113 - public function post(string $endpoint, array $body = []): Response 114 { 115 return $this->call($endpoint, 'POST', null, $body); 116 } ··· 118 /** 119 * Make DELETE request 120 */ 121 - public function delete(string $endpoint, array $params = []): Response 122 { 123 return $this->call($endpoint, 'DELETE', $params); 124 } ··· 126 /** 127 * Make POST request with raw binary body (for blob uploads) 128 */ 129 - public function postBlob(string $endpoint, string $data, string $mimeType): Response 130 { 131 $session = $this->sessions->ensureValid($this->did); 132 $url = rtrim($session->pdsEndpoint(), '/').'/xrpc/'.$endpoint; 133
··· 2 3 namespace SocialDept\AtpClient\Http; 4 5 + use BackedEnum; 6 use Illuminate\Http\Client\Response as LaravelResponse; 7 use Illuminate\Support\Facades\Http; 8 use InvalidArgumentException; ··· 16 17 trait HasHttp 18 { 19 + protected ?SessionManager $sessions = null; 20 21 + protected ?string $did = null; 22 23 + protected ?DPoPClient $dpopClient = null; 24 25 protected ?ScopeChecker $scopeChecker = null; 26 ··· 28 * Make XRPC call 29 */ 30 protected function call( 31 + string|BackedEnum $endpoint, 32 string $method, 33 ?array $params = null, 34 ?array $body = null 35 ): Response { 36 + $endpoint = $endpoint instanceof BackedEnum ? $endpoint->value : $endpoint; 37 $session = $this->sessions->ensureValid($this->did); 38 $url = rtrim($session->pdsEndpoint(), '/').'/xrpc/'.$endpoint; 39 ··· 104 /** 105 * Make GET request 106 */ 107 + public function get(string|BackedEnum $endpoint, array $params = []): Response 108 { 109 return $this->call($endpoint, 'GET', $params); 110 } ··· 112 /** 113 * Make POST request 114 */ 115 + public function post(string|BackedEnum $endpoint, array $body = []): Response 116 { 117 return $this->call($endpoint, 'POST', null, $body); 118 } ··· 120 /** 121 * Make DELETE request 122 */ 123 + public function delete(string|BackedEnum $endpoint, array $params = []): Response 124 { 125 return $this->call($endpoint, 'DELETE', $params); 126 } ··· 128 /** 129 * Make POST request with raw binary body (for blob uploads) 130 */ 131 + public function postBlob(string|BackedEnum $endpoint, string $data, string $mimeType): Response 132 { 133 + $endpoint = $endpoint instanceof BackedEnum ? $endpoint->value : $endpoint; 134 $session = $this->sessions->ensureValid($this->did); 135 $url = rtrim($session->pdsEndpoint(), '/').'/xrpc/'.$endpoint; 136
+5 -186
src/RichText/TextBuilder.php
··· 2 3 namespace SocialDept\AtpClient\RichText; 4 5 - use SocialDept\AtpResolver\Facades\Resolver; 6 7 class TextBuilder 8 { 9 - protected string $text = ''; 10 - protected array $facets = []; 11 12 /** 13 * Create a new text builder instance 14 */ 15 public static function make(): self 16 { 17 - return new self(); 18 } 19 20 /** ··· 22 */ 23 public static function build(callable $callback): array 24 { 25 - $builder = new self(); 26 $callback($builder); 27 28 return $builder->toArray(); 29 } 30 31 /** 32 - * Add plain text 33 - */ 34 - public function text(string $text): self 35 - { 36 - $this->text .= $text; 37 - 38 - return $this; 39 - } 40 - 41 - /** 42 - * Add a new line 43 - */ 44 - public function newLine(): self 45 - { 46 - $this->text .= "\n"; 47 - 48 - return $this; 49 - } 50 - 51 - /** 52 - * Add mention (@handle) 53 - */ 54 - public function mention(string $handle, ?string $did = null): self 55 - { 56 - $handle = ltrim($handle, '@'); 57 - $start = $this->getBytePosition(); 58 - $this->text .= '@'.$handle; 59 - $end = $this->getBytePosition(); 60 - 61 - // Resolve DID if not provided 62 - if (! $did) { 63 - try { 64 - $did = Resolver::handleToDid($handle); 65 - } catch (\Exception $e) { 66 - // If resolution fails, still add the text but skip the facet 67 - return $this; 68 - } 69 - } 70 - 71 - $this->facets[] = [ 72 - 'index' => [ 73 - 'byteStart' => $start, 74 - 'byteEnd' => $end, 75 - ], 76 - 'features' => [ 77 - [ 78 - '$type' => 'app.bsky.richtext.facet#mention', 79 - 'did' => $did, 80 - ], 81 - ], 82 - ]; 83 - 84 - return $this; 85 - } 86 - 87 - /** 88 - * Add link with custom display text 89 - */ 90 - public function link(string $text, string $uri): self 91 - { 92 - $start = $this->getBytePosition(); 93 - $this->text .= $text; 94 - $end = $this->getBytePosition(); 95 - 96 - $this->facets[] = [ 97 - 'index' => [ 98 - 'byteStart' => $start, 99 - 'byteEnd' => $end, 100 - ], 101 - 'features' => [ 102 - [ 103 - '$type' => 'app.bsky.richtext.facet#link', 104 - 'uri' => $uri, 105 - ], 106 - ], 107 - ]; 108 - 109 - return $this; 110 - } 111 - 112 - /** 113 - * Add a URL (displayed as-is) 114 - */ 115 - public function url(string $url): self 116 - { 117 - return $this->link($url, $url); 118 - } 119 - 120 - /** 121 - * Add hashtag 122 - */ 123 - public function tag(string $tag): self 124 - { 125 - $tag = ltrim($tag, '#'); 126 - 127 - $start = $this->getBytePosition(); 128 - $this->text .= '#'.$tag; 129 - $end = $this->getBytePosition(); 130 - 131 - $this->facets[] = [ 132 - 'index' => [ 133 - 'byteStart' => $start, 134 - 'byteEnd' => $end, 135 - ], 136 - 'features' => [ 137 - [ 138 - '$type' => 'app.bsky.richtext.facet#tag', 139 - 'tag' => $tag, 140 - ], 141 - ], 142 - ]; 143 - 144 - return $this; 145 - } 146 - 147 - /** 148 - * Auto-detect and add facets from plain text 149 - */ 150 - public function autoDetect(string $text): self 151 - { 152 - $start = $this->getBytePosition(); 153 - $this->text .= $text; 154 - 155 - // Detect facets in the added text 156 - $detected = FacetDetector::detect($text); 157 - 158 - // Adjust byte positions to account for existing text 159 - foreach ($detected as $facet) { 160 - $facet['index']['byteStart'] += $start; 161 - $facet['index']['byteEnd'] += $start; 162 - $this->facets[] = $facet; 163 - } 164 - 165 - return $this; 166 - } 167 - 168 - /** 169 - * Get current byte position 170 - */ 171 - protected function getBytePosition(): int 172 - { 173 - return strlen($this->text); 174 - } 175 - 176 - /** 177 - * Get the text content 178 - */ 179 - public function getText(): string 180 - { 181 - return $this->text; 182 - } 183 - 184 - /** 185 - * Get the facets 186 - */ 187 - public function getFacets(): array 188 - { 189 - return $this->facets; 190 - } 191 - 192 - /** 193 * Build the final text and facets array 194 */ 195 public function toArray(): array 196 { 197 - return [ 198 - 'text' => $this->text, 199 - 'facets' => $this->facets, 200 - ]; 201 } 202 203 /** ··· 233 public function getByteCount(): int 234 { 235 return strlen($this->text); 236 - } 237 - 238 - /** 239 - * Check if text exceeds AT Protocol post limit (300 graphemes) 240 - */ 241 - public function exceedsLimit(int $limit = 300): bool 242 - { 243 - return $this->getGraphemeCount() > $limit; 244 - } 245 - 246 - /** 247 - * Get grapheme count (closest to what AT Protocol uses) 248 - */ 249 - public function getGraphemeCount(): int 250 - { 251 - return grapheme_strlen($this->text); 252 } 253 254 /**
··· 2 3 namespace SocialDept\AtpClient\RichText; 4 5 + use SocialDept\AtpClient\Builders\Concerns\BuildsRichText; 6 7 class TextBuilder 8 { 9 + use BuildsRichText; 10 11 /** 12 * Create a new text builder instance 13 */ 14 public static function make(): self 15 { 16 + return new self; 17 } 18 19 /** ··· 21 */ 22 public static function build(callable $callback): array 23 { 24 + $builder = new self; 25 $callback($builder); 26 27 return $builder->toArray(); 28 } 29 30 /** 31 * Build the final text and facets array 32 */ 33 public function toArray(): array 34 { 35 + return $this->getTextAndFacets(); 36 } 37 38 /** ··· 68 public function getByteCount(): int 69 { 70 return strlen($this->text); 71 } 72 73 /**