php-plug-web3/src/Farcaster/Read.php
Snider 79d206009c feat: extract Web3 providers from app/Plug/Web3
Bluesky, Farcaster, Lemmy, Mastodon, Nostr, Threads
providers with Core\Plug\Web3 namespace alignment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:28:07 +00:00

205 lines
6.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Core\Plug\Web3\Farcaster;
use Core\Plug\Concern\BuildsResponse;
use Core\Plug\Concern\ManagesTokens;
use Core\Plug\Concern\UsesHttp;
use Core\Plug\Contract\Readable;
use Core\Plug\Response;
/**
* Farcaster profile and cast reading.
*/
class Read implements Readable
{
use BuildsResponse;
use ManagesTokens;
use UsesHttp;
private const NEYNAR_API = 'https://api.neynar.com/v2/farcaster';
private string $apiKey = '';
private ?int $fid = null;
/**
* Set the Neynar API key.
*/
public function withApiKey(string $apiKey): self
{
$this->apiKey = $apiKey;
return $this;
}
/**
* Set the FID for user operations.
*/
public function forFid(int $fid): self
{
$this->fid = $fid;
return $this;
}
/**
* Get a specific cast by hash.
*/
public function get(string $id): Response
{
$response = $this->http()
->withHeaders(['api_key' => $this->apiKey])
->get(self::NEYNAR_API.'/cast', [
'identifier' => $id,
'type' => 'hash',
]);
return $this->fromHttp($response, fn ($data) => [
'hash' => $data['cast']['hash'],
'text' => $data['cast']['text'],
'author' => [
'fid' => $data['cast']['author']['fid'],
'username' => $data['cast']['author']['username'],
'display_name' => $data['cast']['author']['display_name'],
'pfp_url' => $data['cast']['author']['pfp_url'],
],
'timestamp' => $data['cast']['timestamp'],
'reactions' => [
'likes_count' => $data['cast']['reactions']['likes_count'] ?? 0,
'recasts_count' => $data['cast']['reactions']['recasts_count'] ?? 0,
],
'replies' => [
'count' => $data['cast']['replies']['count'] ?? 0,
],
]);
}
/**
* Get the authenticated user's profile by FID.
*/
public function me(): Response
{
if (! $this->fid) {
return $this->error('FID is required');
}
return $this->user($this->fid);
}
/**
* Get user by FID.
*/
public function user(int $fid): Response
{
$response = $this->http()
->withHeaders(['api_key' => $this->apiKey])
->get(self::NEYNAR_API.'/user/bulk', [
'fids' => $fid,
]);
return $this->fromHttp($response, function ($data) {
$user = $data['users'][0] ?? null;
if (! $user) {
return ['error' => 'User not found'];
}
return [
'fid' => $user['fid'],
'username' => $user['username'],
'name' => $user['display_name'],
'image' => $user['pfp_url'],
'bio' => $user['profile']['bio']['text'] ?? null,
'followers_count' => $user['follower_count'] ?? 0,
'following_count' => $user['following_count'] ?? 0,
'verified_addresses' => $user['verified_addresses'] ?? [],
'power_badge' => $user['power_badge'] ?? false,
];
});
}
/**
* Get user by username.
*/
public function userByUsername(string $username): Response
{
$response = $this->http()
->withHeaders(['api_key' => $this->apiKey])
->get(self::NEYNAR_API.'/user/by_username', [
'username' => $username,
]);
return $this->fromHttp($response, fn ($data) => [
'fid' => $data['user']['fid'],
'username' => $data['user']['username'],
'name' => $data['user']['display_name'],
'image' => $data['user']['pfp_url'],
'bio' => $data['user']['profile']['bio']['text'] ?? null,
'followers_count' => $data['user']['follower_count'] ?? 0,
'following_count' => $data['user']['following_count'] ?? 0,
]);
}
/**
* Get user's casts.
*/
public function list(array $params = []): Response
{
$fid = $params['fid'] ?? $this->fid;
if (! $fid) {
return $this->error('FID is required');
}
$response = $this->http()
->withHeaders(['api_key' => $this->apiKey])
->get(self::NEYNAR_API.'/feed/user/casts', [
'fid' => $fid,
'limit' => $params['limit'] ?? 25,
'cursor' => $params['cursor'] ?? null,
'include_replies' => $params['include_replies'] ?? false,
]);
return $this->fromHttp($response, fn ($data) => [
'casts' => array_map(fn ($cast) => [
'hash' => $cast['hash'],
'text' => $cast['text'],
'timestamp' => $cast['timestamp'],
'likes_count' => $cast['reactions']['likes_count'] ?? 0,
'recasts_count' => $cast['reactions']['recasts_count'] ?? 0,
'replies_count' => $cast['replies']['count'] ?? 0,
], $data['casts'] ?? []),
'cursor' => $data['next']['cursor'] ?? null,
]);
}
/**
* Get channel feed.
*/
public function channel(string $channelId, array $params = []): Response
{
$response = $this->http()
->withHeaders(['api_key' => $this->apiKey])
->get(self::NEYNAR_API.'/feed/channel', [
'channel_ids' => $channelId,
'limit' => $params['limit'] ?? 25,
'cursor' => $params['cursor'] ?? null,
]);
return $this->fromHttp($response, fn ($data) => [
'casts' => array_map(fn ($cast) => [
'hash' => $cast['hash'],
'text' => $cast['text'],
'author' => [
'fid' => $cast['author']['fid'],
'username' => $cast['author']['username'],
],
'timestamp' => $cast['timestamp'],
], $data['casts'] ?? []),
'cursor' => $data['next']['cursor'] ?? null,
]);
}
}