Laravel AT Protocol Client (alpha & unstable)
1<?php
2
3namespace SocialDept\AtpClient\Client\Records;
4
5use SocialDept\AtpClient\Attributes\ScopedEndpoint;
6use SocialDept\AtpClient\Client\Requests\Request;
7use SocialDept\AtpClient\Data\Record;
8use SocialDept\AtpClient\Data\Responses\Atproto\Repo\PutRecordResponse;
9use SocialDept\AtpClient\Enums\Nsid\BskyActor;
10use SocialDept\AtpClient\Enums\Scope;
11
12class ProfileRecordClient extends Request
13{
14 /**
15 * Update profile
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 /**
36 * Get current profile
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 /**
53 * Update display name
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;
63
64 return $this->update($profile);
65 }
66
67 /**
68 * Update description/bio
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;
78
79 return $this->update($profile);
80 }
81
82 /**
83 * Update avatar
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;
93
94 return $this->update($profile);
95 }
96
97 /**
98 * Update banner
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;
108
109 return $this->update($profile);
110 }
111
112 /**
113 * Get profile or create empty one if doesn't exist
114 */
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 }
126}