Passer au contenu principal

Qu’est-ce que TextBuilder ?

TextBuilder est une classe qui permet de composer par chaînage de méthodes les facets (annotations de texte enrichi) définis par l’AT Protocol de Bluesky. Le corps d’une publication Bluesky est du texte brut, mais pour afficher des mentions, liens et hashtags, il faut envoyer un tableau facets indiquant la position (offset en octets) et le type dans le texte. TextBuilder effectue automatiquement ce calcul d’offset et la construction du tableau.

Création de texte de base

TextBuilder::make()

Utilisez TextBuilder::make() pour créer une instance avec un texte initial. Le texte initial est optionnel.
use Revolution\Bluesky\RichText\TextBuilder;

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

text()

text() ajoute du texte à la fin.
$builder = TextBuilder::make()
    ->text('Hello ')
    ->text('Bluesky');

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

newLine()

Ajoute un saut de ligne. Vous pouvez spécifier le nombre de lignes avec count (par défaut : 1).
$builder = TextBuilder::make('Ligne 1')
    ->newLine()
    ->text('Ligne 2')
    ->newLine(count: 2)
    ->text('Ligne 4');

toPost()

Convertit l’instance TextBuilder en enregistrement Post. Peut être transmis directement à Bluesky::post().
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('Publication simple')->toPost();

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

Post::build()

Vous pouvez également utiliser Post::build() en lui passant une closure. La valeur de retour est 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);

Ajout de mentions (@mention)

mention() ajoute un facet de mention.
$builder->mention(text: '@alice.bsky.social');

Résolution automatique du DID

Si vous omettez did, le DID sera automatiquement résolu depuis le handle (Bluesky::resolveHandle() est appelée).
// Résolution automatique du DID (un appel API se produit)
$builder->mention('@alice.bsky.social');

Spécification explicite du DID

Si le DID est déjà connu, vous pouvez le passer explicitement pour éviter l’appel API.
// Spécifier directement le DID (recommandé : pas d'appel API)
$builder->mention(text: '@alice.bsky.social', did: 'did:plc:xxxxxxxxxxxxxxxxxxxx');
Si vous utilisez beaucoup de mentions en production, mettre le DID en cache permet de réduire les appels API.

Intégration de liens (URL)

link() ajoute un facet de lien.
// L'URL elle-même sert de texte affiché
$builder->link('https://laravel.com');

// Spécifier séparément le texte affiché et l'URL
$builder->link(text: 'Site officiel Laravel', uri: 'https://laravel.com');
Si uri est omis, text sera utilisé tel quel comme URI.

Ajout de hashtags

tag() ajoute un facet de hashtag.
// Passer un texte commençant par # extrait automatiquement le tag
$builder->tag('#Laravel');

// Spécifier explicitement la chaîne du tag (sans #)
$builder->tag(text: '#Laravel', tag: 'Laravel');

Construction de texte composite

Vous pouvez combiner plusieurs facets pour créer un texte de publication enrichi.
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('J\'ai publié un nouvel article !')
    ->newLine(count: 2)
    ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
    ->text(' j\'aimerais que vous le lisiez aussi.')
    ->newLine()
    ->link(text: 'Lire l\'article', uri: 'https://example.com/article/1')
    ->newLine()
    ->tag('#Laravel')
    ->text(' ')
    ->tag('#PHP')
    ->toPost();

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

Écriture avec 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('J\'ai publié un nouvel article !')
            ->newLine(count: 2)
            ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
            ->text(' j\'aimerais que vous le lisiez aussi.')
            ->newLine()
            ->link(text: 'Lire l\'article', uri: 'https://example.com/article/1')
            ->newLine()
            ->tag('#Laravel')
            ->text(' ')
            ->tag('#PHP');
});

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

Intégration avec les publications

Combinaison avec 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);

Combinaison avec le canal Notification

Vous pouvez utiliser Post::build() dans la méthode toBluesky() de BlueskyChannel.
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('Le déploiement est terminé')
                    ->newLine()
                    ->link(text: 'Vérifier', uri: $this->url)
                    ->newLine()
                    ->tag('#Laravel');
        });
    }
}

Détection automatique des facets

Vous pouvez également détecter automatiquement les @mention, URL et #hashtag du texte pour configurer les facets.
use Revolution\Bluesky\RichText\TextBuilder;

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

// Il est possible d'ajouter d'autres facets après detectFacets()
$builder->newLine()->tag('#bob');

$post = $builder->toPost();
detectFacets() fonctionne par expressions régulières. Si vous voulez garantir la liaison, il est plus sûr d’utiliser explicitement link(), mention() et tag().

Ajout de facets personnalisés

La méthode facet() permet d’ajouter directement n’importe quel tableau de facet.
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',
        ],
    ],
]);

Limites de caractères et considérations

Offset en octets et grapheme

Les index de facet d’AT Protocol sont spécifiés en offsets d’octets UTF-8. TextBuilder utilise en interne strlen() pour calculer le nombre d’octets. Les caractères multi-octets comme le japonais ou les emojis consomment plusieurs octets par caractère, donc l’offset est déterminé en nombre d’octets et non en nombre de caractères.
// 'Hello' fait 5 octets en UTF-8 (1 caractère = 1 octet)
// '日本語' fait 9 octets en UTF-8 (1 caractère = 3 octets)
$builder = TextBuilder::make('日本語');
// strlen($builder->text) === 9

Limite de caractères des publications

Les publications Bluesky sont limitées à 300 caractères en grapheme (caractères affichés) maximum. La limite étant en grapheme et non en octets, vous pouvez écrire 300 caractères même en japonais.
use Illuminate\Support\Str;

$text = 'Voici le texte de la publication.';

// Vérifier le nombre de caractères en grapheme
$length = Str::length($text); // équivalent à mb_strlen()
TextBuilder lui-même ne vérifie pas la longueur. Si vous envoyez une publication dépassant 300 graphemes, l’API AT Protocol retournera une erreur.

Liste des méthodes

MéthodeDescription
TextBuilder::make(string $text = '')Créer une instance
text(string $text)Ajouter du texte à la fin
newLine(int $count = 1)Ajouter un saut de ligne
mention(string $text, ?string $did = null)Ajouter un facet de mention
link(string $text, ?string $uri = null)Ajouter un facet de lien
tag(string $text, ?string $tag = null)Ajouter un facet de hashtag
detectFacets()Détecter automatiquement les facets à partir du texte
facet(array $facet)Ajouter un facet personnalisé
resetFacets()Réinitialiser tous les facets
toPost()Convertir en enregistrement Post
toArray()Convertir en tableau ['text' => ..., 'facets' => ...]
Dernière modification le 13 juillet 2026