Skip to content

Commit

Permalink
feat: support hashtags (#2)
Browse files Browse the repository at this point in the history
Co-authored-by: Enzo Innocenzi <enzo@innocenzi.dev>
  • Loading branch information
paulhennell and innocenzi authored Aug 14, 2024
1 parent a57fdf4 commit 1192336
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/Facets/DefaultFacetsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function resolve(BlueskyService $bluesky, BlueskyPost $post): array
return [
...self::detectMentions($text, $bluesky->getClient()),
...self::detectLinks($text),
...self::detectTags($text),
];
}

Expand Down Expand Up @@ -68,4 +69,32 @@ private static function detectLinks(string $text): array
);
}, $matches[1]);
}

/** @return array<Facet> */
private static function detectTags(string $text): array
{
$tagRegexp = '/(?:^|\\s)(#[^\\d\\s]\\S*)(?=\\s)?/';

preg_match_all($tagRegexp, $text, $matches, \PREG_OFFSET_CAPTURE);

return array_filter(array_map(function (array $match) {
[$tag, $position] = $match;

$tag = preg_replace('/\\p{P}+$/u', '', $tag);

if (\strlen($tag) > 66) {
return null;
}

return new Facet(
range: [
$position,
$position + mb_strlen($tag),
],
features: [
new Tag(tag: substr($tag, 1)),
],
);
}, $matches[1]));
}
}
16 changes: 16 additions & 0 deletions src/Facets/Tag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace NotificationChannels\Bluesky\Facets;

final class Tag extends Feature
{
public function __construct(
public readonly string $tag,
) {
}

public function getType(): string
{
return 'app.bsky.richtext.facet#tag';
}
}
84 changes: 84 additions & 0 deletions tests/DefaultFacetsResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,87 @@
]),
);
});

it('detects tags', function (string $text, array $positions, string $tag) {
/** @var FacetsResolver */
$resolver = resolve(FacetsResolver::class);
$facets = $resolver->resolve(
bluesky: resolve(BlueskyService::class),
post: BlueskyPost::make()->text($text),
);

expect($facets)->sequence(
fn (Expectation $facet) => $facet->toArray()->toBe([
'$type' => 'app.bsky.richtext.facet',
'index' => [
'byteStart' => $positions[0],
'byteEnd' => $positions[1],
],
'features' => [
[
'$type' => 'app.bsky.richtext.facet#tag',
'tag' => $tag,
],
],
]),
);
})->with([
['A post with a #tag inline', [14, 18], 'tag'],
['#tag starting the post', [0, 4], 'tag'],
['a post ended by a #tag', [18, 22], 'tag'],
['a tag that ends #with! punctuation', [16, 21], 'with'],
['a post with #anextremelylongtagwhichisjustunderblueskys64charactertaglimit!', [12, 74], 'anextremelylongtagwhichisjustunderblueskys64charactertaglimit'],
]);

it('doesnt detect non tags', function (string $text) {
/** @var FacetsResolver */
$resolver = resolve(FacetsResolver::class);
$facets = $resolver->resolve(
bluesky: resolve(BlueskyService::class),
post: BlueskyPost::make()->text($text),
);

expect($facets)->toBe([]);
})->with([
['A real#tag must start with space'],
['a #1tag can not have a number'],
['an #anextremelylongtagwhichisabsolutlyoverblueskys64charactertaglimitisnotaddedasatag'],
]);

it('detects multiple tags', function () {
/** @var FacetsResolver */
$resolver = resolve(FacetsResolver::class);
$facets = $resolver->resolve(
bluesky: resolve(BlueskyService::class),
post: BlueskyPost::make()->text('this post has #two #tags'),
);

expect($facets)->sequence(
fn (Expectation $facet) => $facet->toArray()->toBe([
'$type' => 'app.bsky.richtext.facet',
'index' => [
'byteStart' => 14,
'byteEnd' => 18,
],
'features' => [
[
'$type' => 'app.bsky.richtext.facet#tag',
'tag' => 'two',
],
],
]),
fn (Expectation $facet) => $facet->toArray()->toBe([
'$type' => 'app.bsky.richtext.facet',
'index' => [
'byteStart' => 19,
'byteEnd' => 24,
],
'features' => [
[
'$type' => 'app.bsky.richtext.facet#tag',
'tag' => 'tags',
],
],
]),
);
});

0 comments on commit 1192336

Please sign in to comment.