AltHeroes Bot v2
1using AltBot.Core.Models;
2using AltBot.Data;
3using AltBot.ServiceDefaults;
4using Duende.IdentityModel.OidcClient;
5using idunno.AtProto;
6using idunno.Bluesky;
7using idunno.Bluesky.Actor;
8using idunno.Bluesky.Feed;
9using idunno.Bluesky.Graph;
10using idunno.Bluesky.Record;
11using Microsoft.EntityFrameworkCore;
12using Cid = AltBot.Core.Models.Cid;
13using Did = AltBot.Core.Models.Did;
14using Like = idunno.Bluesky.Record.Like;
15
16namespace AltBot.Api;
17
18public static class StartupExtensions
19{
20 // DTOs
21 record SubscriberCreate(Did Did, string? Handle);
22 record SubscriberUpdate(string? Handle, bool Active, string Rkey);
23 record PostCreate(Did Did, Cid Cid, string Rkey);
24
25 public static void MapApplicationEndpoints(this WebApplication app)
26 {
27 // Subscribers endpoints
28 app.MapGet("/subscribers", async (DataContext db) =>
29 {
30 var agent = new BlueskyAgent();
31
32
33 var likesResult = await agent.GetLikes(
34 new AtUri("at://did:plc:yb2gz6yxpebbzlundrrfkv4d/app.bsky.labeler.service/self"));
35
36
37 if (!likesResult.Succeeded || likesResult.Result.Count == 0)
38 {
39 return Results.NotFound();
40 }
41
42 var list = new List<ProfileView>();
43 list.AddRange(likesResult.Result.Select(x => x.Actor));
44
45 while (!string.IsNullOrEmpty(likesResult.Result.Cursor))
46 {
47 likesResult = await agent.GetLikes(
48 new AtUri("at://did:plc:yb2gz6yxpebbzlundrrfkv4d/app.bsky.labeler.service/self"),
49 cursor: likesResult.Result.Cursor);
50
51 list.AddRange(likesResult.Result.Select(x => x.Actor));
52 }
53
54 var x = list.Select(x => $"{x.Handle.Value} - {x.Did}");
55
56 return Results.Ok(x);
57
58
59
60 //var followersResult = await agent.GetFollowers(AtIdentifier.Create("did:plc:yb2gz6yxpebbzlundrrfkv4d"));
61
62 //var list = new List<Followers>();
63
64 //if (followersResult.Succeeded && followersResult.Result.Count > 0)
65 //{
66
67 //}
68
69 //var sfsda = new List<Followers>();
70
71 //do
72 //{
73 // var results = followers.Result.Select(follow => $"{follow.Handle.Value} - {follow.Did}");
74 //}
75 //while (!string.IsNullOrEmpty(followers.Result.Cursor))
76
77
78
79 //return Results.Ok(results);
80
81
82
83 var subscribers = await db.Subscribers
84 .Select(s => new
85 {
86 s.Did,
87 s.Handle,
88 s.Active,
89 s.SeenAt,
90 s.Label
91 })
92 .ToListAsync();
93 return Results.Ok(subscribers);
94 })
95 .WithName("GetSubscribers")
96 .WithOpenApi();
97
98 app.MapGet("/subscribers/{did}", async (Did did, DataContext db) =>
99 {
100 var subscriber = await db.Subscribers.FirstOrDefaultAsync(s => s.Did == did);
101 return subscriber == null ? Results.NotFound() : Results.Ok(subscriber);
102 })
103 .WithName("GetSubscriber")
104 .WithOpenApi();
105
106 app.MapGet("subscribers/{did}/posts", async (Did did, DataContext db) =>
107 {
108 var posts = await db.Posts
109 .Where(x => x.Did == did)
110 .Select(p => new
111 {
112 p.Cid,
113 p.Rkey,
114 p.SeenAt
115 })
116 .ToListAsync();
117 return Results.Ok(posts);
118 })
119 .WithName("GetPosts")
120 .WithOpenApi();
121
122 app.MapPost("/subscribers", async (SubscriberCreate request, DataContext db) =>
123 {
124 if (await db.Subscribers.AnyAsync(s => s.Did == request.Did))
125 {
126 return Results.Conflict("Subscriber already exists");
127 }
128
129 var subscriber = new Subscriber
130 {
131 Did = request.Did,
132 Handle = request.Handle,
133 Active = true,
134 SeenAt = DateTime.UtcNow
135 };
136
137 db.Subscribers.Add(subscriber);
138 await db.SaveChangesAsync();
139
140 return Results.Created($"/subscribers/{subscriber.Did}", subscriber);
141 })
142 .WithName("CreateSubscriber")
143 .WithOpenApi();
144
145 app.MapPut("/subscribers/{did}", async (Did did, SubscriberUpdate request, DataContext db) =>
146 {
147 var subscriber = await db.Subscribers.FindAsync(did);
148 if (subscriber == null)
149 {
150 return Results.NotFound();
151 }
152
153 subscriber.Handle = request.Handle ?? subscriber.Handle;
154 subscriber.Active = request.Active;
155 subscriber.Rkey = request.Rkey;
156
157 await db.SaveChangesAsync();
158 return Results.Ok(subscriber);
159 })
160 .WithName("UpdateSubscriber")
161 .WithOpenApi();
162
163 app.MapDelete("/subscribers/{did}", async (Did did, DataContext db) =>
164 {
165 var subscriber = await db.Subscribers.FindAsync(did);
166 if (subscriber == null)
167 {
168 return Results.NotFound();
169 }
170
171 db.Subscribers.Remove(subscriber);
172 await db.SaveChangesAsync();
173
174 return Results.NoContent();
175 })
176 .WithName("DeleteSubscriber")
177 .WithOpenApi();
178
179
180 // Posts endpoints
181 app.MapPost("/posts", async (PostCreate request, DataContext db) =>
182 {
183 if (!await db.Subscribers.AnyAsync(s => s.Did == request.Did))
184 {
185 return Results.NotFound("Subscriber not found");
186 }
187
188 var post = new ImagePost
189 {
190 Did = request.Did,
191 Cid = request.Cid,
192 Rkey = request.Rkey,
193 SeenAt = DateTime.UtcNow
194 };
195
196 db.Posts.Add(post);
197 await db.SaveChangesAsync();
198
199 return Results.Created($"/subscribers/{post.Did}/posts/{post.Cid}", post);
200 })
201 .WithName("CreatePost")
202 .WithOpenApi();
203
204 app.MapDelete("/posts/{did}/{cid}/{rKey}", async (Did did, Cid cid, string rKey, DataContext db) =>
205 {
206 var post = await db.Posts.FindAsync(did, cid);
207 if (post == null)
208 {
209 return Results.NotFound();
210 }
211
212 db.Posts.Remove(post);
213 await db.SaveChangesAsync();
214
215 return Results.NoContent();
216 })
217 .WithName("DeletePost")
218 .WithOpenApi();
219
220 app.MapDefaultEndpoints();
221
222 app.Run();
223 }
224}