Located posts: local only, rounded, matched against their own radius
A post given a latitude and longitude becomes LocalGeo: its position is rounded to two decimals (about a kilometre) and stored as a 2dsphere point, its radius clamped to 1-50 km, and it is local only - no Create, no delivery, no outbox, never in the Mastodon API or on a profile page. /clientapi/post/nearby takes the viewer's position for the query only (it is neither stored nor logged), runs $geoNear and keeps posts within each post's own radius, minus authors the viewer blocks or mutes. A located post cannot be direct or in a group. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
This commit is contained in:
1 parent
28b581b6b1
commit
c00865d805
8 files changed
+150
-3
No files matched your search
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Entities;
|
||||
|
||||
using PrivaPub.ClientModels;
|
||||
@@ -27,6 +28,7 @@ namespace PrivaPub.Services
|
||||
Task<WebResult> InsertPost(string rootUserId, InsertPostForm form, CancellationToken token);
|
||||
Task<WebResult> DeletePost(string rootUserId, DeletePostForm form, CancellationToken token);
|
||||
Task<WebResult> UpdatePost(string rootUserId, UpdatePostForm form, CancellationToken token);
|
||||
Task<WebResult> Nearby(string rootUserId, string avatarId, double latitude, double longitude, CancellationToken token);
|
||||
Task<WebResult> GetPosts(string rootUserId, string avatarId, string groupId, CancellationToken token);
|
||||
Task<WebResult> InsertDm(string rootUserId, InsertDmForm form, CancellationToken token);
|
||||
Task<WebResult> GetDms(string rootUserId, string avatarId, string dmGroupId, CancellationToken token);
|
||||
@@ -68,7 +70,10 @@ namespace PrivaPub.Services
|
||||
Sensitive = form.HasContentWarning,
|
||||
Visibility = LocalVisibility(form.Visibility),
|
||||
InReplyTo = form.AnsweringToPostId,
|
||||
GroupId = form.GroupId
|
||||
GroupId = form.GroupId,
|
||||
Latitude = form.Latitude,
|
||||
Longitude = form.Longitude,
|
||||
RangeKm = form.RangeKm
|
||||
}, token), nameof(InsertPost));
|
||||
}
|
||||
|
||||
@@ -193,6 +198,35 @@ namespace PrivaPub.Services
|
||||
|
||||
|
||||
|
||||
public async Task<WebResult> Nearby(string rootUserId, string avatarId, double latitude, double longitude, CancellationToken token)
|
||||
{
|
||||
var result = new WebResult();
|
||||
var viewer = await OwnedAvatar(rootUserId, avatarId, token);
|
||||
if (viewer == default)
|
||||
return result.Invalidate(_localizer["Avatar not found."], StatusCodes.Status404NotFound);
|
||||
if (latitude is < -90 or > 90 || longitude is < -180 or > 180)
|
||||
return result.Invalidate(_localizer["Invalid model."]);
|
||||
|
||||
var query = new MongoDB.Bson.BsonDocument
|
||||
{
|
||||
["Visibility"] = (int)PostVisibility.LocalGeo,
|
||||
["DeletedAt"] = MongoDB.Bson.BsonNull.Value
|
||||
};
|
||||
var candidates = await DB.Default.GeoNear<PostEntity>(new Coordinates2D(longitude, latitude), p => p.Distance, true, 50_000, default, default, query)
|
||||
.Limit(200)
|
||||
.ToListAsync(token);
|
||||
var near = candidates.Where(p => p.Distance <= p.RangeKm * 1000).ToList();
|
||||
var hidden = await Domain.Relationships.Hidden.AuthorsHiddenFrom(viewer.Id, near.Select(p => p.ActorURI), forNotifications: false, token);
|
||||
result.Data = near.Where(p => !hidden.Contains(p.ActorURI)).Select(p =>
|
||||
{
|
||||
var view = ToView(p);
|
||||
view.RangeKm = p.RangeKm;
|
||||
view.DistanceKm = Math.Round(p.Distance.Value / 1000, 1);
|
||||
return view;
|
||||
}).ToList();
|
||||
return result;
|
||||
}
|
||||
|
||||
async Task<WebResult> Answer(Func<Task<StatusOutcome>> action, string operation)
|
||||
{
|
||||
var result = new WebResult();
|
||||
|
||||
Reference in new issue
Block a user