drasticactions / FishyFlip

Fishyflip - a .NET ATProtocol/Bluesky Library
MIT License
62 stars 7 forks source link

[Feature request] Automatic facet parsing #68

Open NotNite opened 3 weeks ago

NotNite commented 3 weeks ago

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 commented 3 weeks ago

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.

https://github.com/drasticactions/FishyFlip/blob/822c3197efae6499e6859e83ce13e4425b0713d4/src/FishyFlip/Models/Facet.cs#L55-L82

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 commented 3 weeks ago

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 commented 3 weeks ago

@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.