using AltBot.Core.Models; using AltBot.Data; using AltBot.ServiceDefaults; using Duende.IdentityModel.OidcClient; using idunno.AtProto; using idunno.Bluesky; using idunno.Bluesky.Actor; using idunno.Bluesky.Feed; using idunno.Bluesky.Graph; using idunno.Bluesky.Record; using Microsoft.EntityFrameworkCore; using Cid = AltBot.Core.Models.Cid; using Did = AltBot.Core.Models.Did; using Like = idunno.Bluesky.Record.Like; namespace AltBot.Api; public static class StartupExtensions { // DTOs record SubscriberCreate(Did Did, string? Handle); record SubscriberUpdate(string? Handle, bool Active, string Rkey); record PostCreate(Did Did, Cid Cid, string Rkey); public static void MapApplicationEndpoints(this WebApplication app) { // Subscribers endpoints app.MapGet("/subscribers", async (DataContext db) => { var agent = new BlueskyAgent(); var likesResult = await agent.GetLikes( new AtUri("at://did:plc:yb2gz6yxpebbzlundrrfkv4d/app.bsky.labeler.service/self")); if (!likesResult.Succeeded || likesResult.Result.Count == 0) { return Results.NotFound(); } var list = new List(); list.AddRange(likesResult.Result.Select(x => x.Actor)); while (!string.IsNullOrEmpty(likesResult.Result.Cursor)) { likesResult = await agent.GetLikes( new AtUri("at://did:plc:yb2gz6yxpebbzlundrrfkv4d/app.bsky.labeler.service/self"), cursor: likesResult.Result.Cursor); list.AddRange(likesResult.Result.Select(x => x.Actor)); } var x = list.Select(x => $"{x.Handle.Value} - {x.Did}"); return Results.Ok(x); //var followersResult = await agent.GetFollowers(AtIdentifier.Create("did:plc:yb2gz6yxpebbzlundrrfkv4d")); //var list = new List(); //if (followersResult.Succeeded && followersResult.Result.Count > 0) //{ //} //var sfsda = new List(); //do //{ // var results = followers.Result.Select(follow => $"{follow.Handle.Value} - {follow.Did}"); //} //while (!string.IsNullOrEmpty(followers.Result.Cursor)) //return Results.Ok(results); var subscribers = await db.Subscribers .Select(s => new { s.Did, s.Handle, s.Active, s.SeenAt, s.Label }) .ToListAsync(); return Results.Ok(subscribers); }) .WithName("GetSubscribers") .WithOpenApi(); app.MapGet("/subscribers/{did}", async (Did did, DataContext db) => { var subscriber = await db.Subscribers.FirstOrDefaultAsync(s => s.Did == did); return subscriber == null ? Results.NotFound() : Results.Ok(subscriber); }) .WithName("GetSubscriber") .WithOpenApi(); app.MapGet("subscribers/{did}/posts", async (Did did, DataContext db) => { var posts = await db.Posts .Where(x => x.Did == did) .Select(p => new { p.Cid, p.Rkey, p.SeenAt }) .ToListAsync(); return Results.Ok(posts); }) .WithName("GetPosts") .WithOpenApi(); app.MapPost("/subscribers", async (SubscriberCreate request, DataContext db) => { if (await db.Subscribers.AnyAsync(s => s.Did == request.Did)) { return Results.Conflict("Subscriber already exists"); } var subscriber = new Subscriber { Did = request.Did, Handle = request.Handle, Active = true, SeenAt = DateTime.UtcNow }; db.Subscribers.Add(subscriber); await db.SaveChangesAsync(); return Results.Created($"/subscribers/{subscriber.Did}", subscriber); }) .WithName("CreateSubscriber") .WithOpenApi(); app.MapPut("/subscribers/{did}", async (Did did, SubscriberUpdate request, DataContext db) => { var subscriber = await db.Subscribers.FindAsync(did); if (subscriber == null) { return Results.NotFound(); } subscriber.Handle = request.Handle ?? subscriber.Handle; subscriber.Active = request.Active; subscriber.Rkey = request.Rkey; await db.SaveChangesAsync(); return Results.Ok(subscriber); }) .WithName("UpdateSubscriber") .WithOpenApi(); app.MapDelete("/subscribers/{did}", async (Did did, DataContext db) => { var subscriber = await db.Subscribers.FindAsync(did); if (subscriber == null) { return Results.NotFound(); } db.Subscribers.Remove(subscriber); await db.SaveChangesAsync(); return Results.NoContent(); }) .WithName("DeleteSubscriber") .WithOpenApi(); // Posts endpoints app.MapPost("/posts", async (PostCreate request, DataContext db) => { if (!await db.Subscribers.AnyAsync(s => s.Did == request.Did)) { return Results.NotFound("Subscriber not found"); } var post = new ImagePost { Did = request.Did, Cid = request.Cid, Rkey = request.Rkey, SeenAt = DateTime.UtcNow }; db.Posts.Add(post); await db.SaveChangesAsync(); return Results.Created($"/subscribers/{post.Did}/posts/{post.Cid}", post); }) .WithName("CreatePost") .WithOpenApi(); app.MapDelete("/posts/{did}/{cid}/{rKey}", async (Did did, Cid cid, string rKey, DataContext db) => { var post = await db.Posts.FindAsync(did, cid); if (post == null) { return Results.NotFound(); } db.Posts.Remove(post); await db.SaveChangesAsync(); return Results.NoContent(); }) .WithName("DeletePost") .WithOpenApi(); app.MapDefaultEndpoints(); app.Run(); } }