Vai al contenuto principale

Cos’è TextBuilder

TextBuilder è una classe che permette di comporre tramite metodi concatenati i facets (annotazioni rich text) definiti da AT Protocol di Bluesky. Il corpo dei post Bluesky è testo semplice, ma per visualizzare menzioni, link e hashtag è necessario inviare, insieme al testo, un array facets che indichi posizione (offset in byte) e tipo. TextBuilder calcola automaticamente questi offset e costruisce l’array.

Creazione di testo base

TextBuilder::make()

Con TextBuilder::make() generi un’istanza specificando il testo iniziale. Il testo iniziale è opzionale.
use Revolution\Bluesky\RichText\TextBuilder;

$builder = TextBuilder::make(text: 'Hello Bluesky');

text()

Con text() aggiungi testo in coda.
$builder = TextBuilder::make()
    ->text('Hello ')
    ->text('Bluesky');

// $builder->text === 'Hello Bluesky'

newLine()

Aggiunge un a capo. Con count puoi indicare il numero di righe (default: 1).
$builder = TextBuilder::make('Prima riga')
    ->newLine()
    ->text('Seconda riga')
    ->newLine(count: 2)
    ->text('Quarta riga');

toPost()

Converte l’istanza di TextBuilder in un record Post. Puoi passarlo direttamente a Bluesky::post().
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('Un post semplice')->toPost();

$response = Bluesky::withToken()->post($post);

Post::build()

Puoi anche passare una closure a Post::build(). Il valore di ritorno è un Post.
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;

$post = Post::build(function (TextBuilder $builder) {
    $builder->text('Hello Bluesky');
});

$response = Bluesky::withToken()->post($post);

Aggiungere una menzione (@mention)

Con mention() aggiungi un facet di menzione.
$builder->mention(text: '@alice.bsky.social');

Risoluzione automatica del DID

Se ometti did, viene risolto automaticamente dall’handle (viene chiamato Bluesky::resolveHandle()).
// Risoluzione automatica del DID (viene fatta una chiamata API)
$builder->mention('@alice.bsky.social');

Specifica esplicita del DID

Se conosci già il DID, puoi passarlo esplicitamente per evitare la chiamata API.
// Specifica diretta del DID (consigliato: nessuna chiamata API)
$builder->mention(text: '@alice.bsky.social', did: 'did:plc:xxxxxxxxxxxxxxxxxxxx');
Se in produzione usi molte menzioni, mettere in cache i DID riduce il numero di chiamate API.
Con link() aggiungi un facet di link.
// Usa l'URL stesso come testo visualizzato
$builder->link('https://laravel.com');

// Specifica testo visualizzato e URL separatamente
$builder->link(text: 'Sito ufficiale di Laravel', uri: 'https://laravel.com');
Se ometti uri, viene usato text come URI.

Aggiungere un hashtag

Con tag() aggiungi un facet di hashtag.
// Passando testo che inizia con # il tag viene estratto automaticamente
$builder->tag('#Laravel');

// Specifica esplicita della stringa del tag (senza #)
$builder->tag(text: '#Laravel', tag: 'Laravel');

Composizione di testo complesso

Puoi combinare più facet per creare testi post ricchi.
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('Ho pubblicato un nuovo articolo!')
    ->newLine(count: 2)
    ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
    ->text(' spero lo leggerai anche tu.')
    ->newLine()
    ->link(text: 'Leggi l\'articolo', uri: 'https://example.com/article/1')
    ->newLine()
    ->tag('#Laravel')
    ->text(' ')
    ->tag('#PHP')
    ->toPost();

$response = Bluesky::withToken()->post($post);

Scrittura con Post::build()

use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;

$post = Post::build(function (TextBuilder $builder) {
    $builder->text('Ho pubblicato un nuovo articolo!')
            ->newLine(count: 2)
            ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
            ->text(' spero lo leggerai anche tu.')
            ->newLine()
            ->link(text: 'Leggi l\'articolo', uri: 'https://example.com/article/1')
            ->newLine()
            ->tag('#Laravel')
            ->text(' ')
            ->tag('#PHP');
});

$response = Bluesky::withToken()->post($post);

Integrazione con i post

Combinazione con Bluesky::post()

use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('test')
    ->newLine()
    ->link('https://bsky.app/')
    ->toPost();

$response = Bluesky::withToken()->post($post);

Combinazione con il canale Notification

Nel metodo toBluesky() di BlueskyChannel puoi usare Post::build().
use Illuminate\Notifications\Notification;
use Revolution\Bluesky\Notifications\BlueskyChannel;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;

class DeployedNotification extends Notification
{
    public function __construct(
        private string $url,
    ) {}

    public function via(object $notifiable): array
    {
        return [BlueskyChannel::class];
    }

    public function toBluesky(object $notifiable): Post
    {
        return Post::build(function (TextBuilder $builder) {
            $builder->text('Deploy completato')
                    ->newLine()
                    ->link(text: 'Verifica', uri: $this->url)
                    ->newLine()
                    ->tag('#Laravel');
        });
    }
}

Rilevamento automatico dei facets

Puoi anche far rilevare automaticamente @mention, URL e #hashtag nel testo e impostare i facets.
use Revolution\Bluesky\RichText\TextBuilder;

$builder = TextBuilder::make('@alice.bsky.social test https://example.com #alice')
    ->detectFacets();

// Puoi aggiungere altri facets anche dopo detectFacets()
$builder->newLine()->tag('#bob');

$post = $builder->toPost();
detectFacets() è un rilevamento basato su espressioni regolari. Se vuoi certezza del link, usa esplicitamente link(), mention() o tag().

Aggiungere un facet personalizzato

Con il metodo facet() puoi aggiungere direttamente un array di facet arbitrario.
use Revolution\Bluesky\RichText\TextBuilder;

$builder = TextBuilder::make();

$builder->facet([
    'index' => [
        'byteStart' => 0,
        'byteEnd' => 5,
    ],
    'features' => [
        [
            '$type' => 'app.bsky.richtext.facet#link',
            'uri' => 'https://example.com',
        ],
    ],
]);

Limiti di lunghezza e attenzioni

Offset in byte e grapheme

Gli indici dei facet in AT Protocol si specificano in offset UTF-8 in byte. TextBuilder calcola internamente il numero di byte usando strlen(). Caratteri multibyte come giapponese ed emoji occupano più byte anche se rappresentano un solo carattere, quindi gli offset sono determinati dai byte, non dal numero di caratteri.
// 'Hello' occupa 5 byte in UTF-8 (1 carattere = 1 byte)
// '日本語' occupa 9 byte in UTF-8 (1 carattere = 3 byte)
$builder = TextBuilder::make('日本語');
// strlen($builder->text) === 9

Limite di caratteri del post

I post di Bluesky hanno un limite di 300 caratteri in grapheme (caratteri visualizzati). Il limite è espresso in grapheme e non in byte, quindi puoi scrivere fino a 300 caratteri anche in giapponese.
use Illuminate\Support\Str;

$text = 'Questo è il testo del post.';

// Verifica del numero di caratteri in grapheme
$length = Str::length($text); // equivalente a mb_strlen()
TextBuilder di per sé non verifica il numero di caratteri. Se invii un post che supera i 300 grapheme, l’API di AT Protocol restituisce un errore.

Elenco dei metodi

MetodoDescrizione
TextBuilder::make(string $text = '')Crea un’istanza
text(string $text)Aggiunge testo in coda
newLine(int $count = 1)Aggiunge a capo
mention(string $text, ?string $did = null)Aggiunge un facet di menzione
link(string $text, ?string $uri = null)Aggiunge un facet di link
tag(string $text, ?string $tag = null)Aggiunge un facet di hashtag
detectFacets()Rileva automaticamente i facets dal testo
facet(array $facet)Aggiunge un facet personalizzato
resetFacets()Resetta tutti i facets
toPost()Converte in record Post
toArray()Converte in array ['text' => ..., 'facets' => ...]
Ultima modifica il 13 luglio 2026