Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature request] Automatic facet parsing #68

Closed
NotNite opened this issue Oct 27, 2024 · 3 comments
Closed

[Feature request] Automatic facet parsing #68

NotNite opened this issue Oct 27, 2024 · 3 comments
Labels
enhancement New feature or request

Comments

@NotNite
Copy link

NotNite commented Oct 27, 2024

I'm working on an app for Bluesky. My code path receives a string content and then I directly post that, but of course the facets are missing from the post, so any links/hashtags/mentions/etc don't work. It would be nice to have an API like:

string content = "Hello, @notnite.com! #hashtag";
Facet[] facets = Facet.ParseFacets(content);
await AtProtocol.Repo.CreatePostAsync(content, facets: facets);

so I can automatically detect facets in the post text without having to implement my own parser.

@drasticactions drasticactions added the enhancement New feature or request label Oct 28, 2024
@drasticactions
Copy link
Owner

For Hashtags and base-level URLs (where you have a raw URL in the post, but don't want to embed it inside of other text for an anchor link), then a generic ParseFacets could be done, but for Mentions, it's tricky. Those need to be ATDid values, and need to be fetched (unless you already know them).

So, it would need to be Async for that and include the ATProtocol client itself since it needs to be able to fetch those values. Those values can be fetched with BlueskyActor.GetProfilesAsync and require authentication. Having network calls complicates it enough that it could be too complex to maintain.

That said, a generic Facet.ForHashtags or Facet.ForBaseUris could be straightforward, and those could be extra static methods on top of the existing ones for creating the facets.

/// <summary>
/// Creates a facet with a link feature.
/// </summary>
/// <param name="start">The start index of the link.</param>
/// <param name="end">The end index of the link.</param>
/// <param name="uri">The URI of the link.</param>
/// <returns>The created facet.</returns>
public static Facet CreateFacetLink(int start, int end, string uri)
=> new(new FacetIndex(start, end), new FacetFeature[] { FacetFeature.CreateLink(uri) });
/// <summary>
/// Creates a facet with a hashtag feature.
/// </summary>
/// <param name="start">The start index of the hashtag.</param>
/// <param name="end">The end index of the hashtag.</param>
/// <param name="hashtag">The hashtag value.</param>
/// <returns>The created facet.</returns>
public static Facet CreateFacetHashtag(int start, int end, string hashtag)
=> new(new FacetIndex(start, end), new FacetFeature[] { FacetFeature.CreateHashtag(hashtag) });
/// <summary>
/// Creates a facet with a mention feature.
/// </summary>
/// <param name="start">The start index of the mention.</param>
/// <param name="end">The end index of the mention.</param>
/// <param name="mention">The mention value.</param>
/// <returns>The created facet.</returns>
public static Facet CreateFacetMention(int start, int end, ATDid mention)

For handles, I think it's best left to the developer to figure out how to get the ATDid values for your app. For example, you could use BlueskyActor.SearchActorsTypeaheadAsync to show type-ahead values for the mentions, including the ATDid. That could be stored, and then a Facet could be created.

For creating the Facets themselves, I can see a generic Facet.ForHandle(string post, string handle, ATDid did) where it would return the FacetFeature(s) for that handle, attached with the ATDid you already fetched. So you don't have to deal with byte placement for that too.

All that said, if you have an implementation for your idea in mind, you (or anyone else!) are welcome to PR it and I'll be happy to review.

@NotNite
Copy link
Author

NotNite commented Oct 28, 2024

Looks like the ATProto TypeScript SDK parses the handles and requires you to fetch your own DIDs: https://github.com/bluesky-social/atproto/blob/main/packages/api/src/rich-text/detection.ts - so makes sense. I don't really have much of an impl in mind, but it's possible that this somewhat-canonical impl can be ported.

@drasticactions
Copy link
Owner

drasticactions commented Oct 29, 2024

@NotNite I made some changes with #73 to add more helper methods

var postText = "@drasticactions.dev This is a #test #test of #testing the #FishyFlip #API. https://github.com/drasticactions DAHome. @drasticactions.jp https://github.com/drasticactions/FishyFlip @drasticactions.dev Weee!";
var postHandles = ATHandle.FromPostText(postText);
var feedProfiles = (await atProtocol.Actor.GetProfilesAsync(postHandles)).HandleResult();
var handleFacets = Facet.ForMentions(postText, feedProfiles!.Profiles!);
var hashtagFacets = Facet.ForHashtags(postText);
var uriFacets = Facet.ForUris(postText);
var baseUriFacets = Facet.ForUris(postText, "DAHome", "https://github.com/drasticactions");
var facets = handleFacets.Concat(hashtagFacets).Concat(uriFacets).Concat(baseUriFacets).ToArray();
var result = (await atProtocol.Repo.CreatePostAsync(postText, facets)).HandleResult();

In short, take your post text and pass it through the various Facet helper methods.

For Handles, you can generate them with either an array of FeedProfile, ActorProfile, or create your own FacetActorIdentifier with the ATHandle and ATDid if you know what they are.

https://www.nuget.org/packages/FishyFlip/2.1.0-alpha.20

Try it out and let me know what you think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants