Laravel AT Protocol Client (alpha & unstable)
1<?php
2
3namespace SocialDept\AtpClient\Client\Records;
4
5use DateTimeInterface;
6use SocialDept\AtpClient\Attributes\ScopedEndpoint;
7use SocialDept\AtpClient\Builders\PostBuilder;
8use SocialDept\AtpClient\Client\Requests\Request;
9use SocialDept\AtpClient\Contracts\Recordable;
10use SocialDept\AtpClient\Data\Record;
11use SocialDept\AtpClient\Data\Responses\Atproto\Repo\CreateRecordResponse;
12use SocialDept\AtpClient\Data\Responses\Atproto\Repo\DeleteRecordResponse;
13use SocialDept\AtpClient\Data\Responses\Atproto\Repo\PutRecordResponse;
14use SocialDept\AtpClient\Data\StrongRef;
15use SocialDept\AtpClient\Enums\Nsid\BskyFeed;
16use SocialDept\AtpClient\Enums\Scope;
17use SocialDept\AtpClient\RichText\TextBuilder;
18use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Defs\PostView;
19
20class 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,
40 ?array $embed = 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 = [
48 'text' => $content,
49 'facets' => $facets ?? TextBuilder::parse($content)['facets'],
50 ];
51 } elseif ($content instanceof Recordable) {
52 $record = $content->toArray();
53 } else {
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 /**
86 * Update a post
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 /**
107 * Delete a post
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 }
120
121 /**
122 * Get a post
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}