Skip to content

Commit 9284c82

Browse files
committed
Add labeler getServices
Add moderation createReport
1 parent e38ca88 commit 9284c82

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2216
-32
lines changed

docs/docs/conversationsAndMessages.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,44 @@ You can send multiple messages into multiple conversations using `SendMessageBat
6565

6666
To delete a message in a conversation use `DeleteMessageForSelf()`, passing the conversation id and the message id.
6767

68+
## <a name="reacting">Message reactions</a>
69+
70+
Bluesky allows for simple message reactions in conversations. The `MessageView` you get from `getMessages` has a `Reactions` property which is a collection of `ReactionView`. To display reactions
71+
you might adjust the code above for display a message to include reactions like this:
72+
73+
```
74+
foreach (MessageViewBase message in getMessages.Result)
75+
{
76+
if (message is MessageView view)
77+
{
78+
var sender = getConversation.Result.Members.FirstOrDefault(m => m.Did == view.Sender.Did) ??
79+
throw new InvalidOperationException("Cannot find message sender in conversation view");
80+
81+
Console.WriteLine($"{sender}: {view.Text} {view.SentAt:g}");
82+
83+
foreach (ReactionView reaction in view.Reactions)
84+
{
85+
var reactionSender = getConversation.Result.Members.FirstOrDefault(m => m.Did == view.Sender.Did) ??
86+
throw new InvalidOperationException("Cannot find message sender in conversation view");
87+
88+
Console.WriteLine($"{reactionSender}: {reaction.Value} {reaction.CreatedAt:g}");
89+
}
90+
}
91+
else if (message is DeletedMessageView _)
92+
{
93+
Console.WriteLine("Deleted Message");
94+
}
95+
}
96+
```
97+
98+
To add a reaction to a message call `AddReaction()`. This requires the conversation id and the message id, and the reaction you want to add. A reaction is an single emoji grapheme.
99+
To delete a reaction call `RemoveReaction()` with the same parameters with which you added a reaction.
100+
101+
68102
## <a name="creating">Starting a conversation</a>
69103

70-
To start a conversation you will need the DIDs of the conversation members, which you pass a collection to `GetConversationForMembers()`. If the user has left a conversation with these DIDs it will be restored in the direct message list.
104+
To start a conversation you will need the DIDs of the conversation members, which you pass a collection to `GetConversationForMembers()`. If the user has left a conversation with these DIDs
105+
it will be restored in the direct message list.
71106

72107
```c#
73108
var memberDid = await agent.ResolveHandle("example.invalid.handle", cancellationToken);

docs/docs/endpointStatus.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@
4747
| | [app.bsky.graph.unmuteActorList](https://docs.bsky.app/docs/api/app-bsky-graph-unmute-actor-list) | `BlueskyAgent.UnmuteActorList()` ||
4848
| | [app.bsky.graph.unmuteActor](https://docs.bsky.app/docs/api/app-bsky-graph-unmute-actor) | `BlueskyAgent.UmnuteActor()` ||
4949
| | [app.bsky.graph.unmuteThread](https://docs.bsky.app/docs/api/app-bsky-graph-unmute-thread) | `BlueskyAgent.UnmuteThread()` ||
50+
| **Labelers** | [app.bsky.labeler.getServices](https://docs.bsky.app/docs/api/app-bsky-labeler-get-services) | `BlueskyAgent.GetLabelerServices()` ||
5051
| **Notifications** | [app.bsky.notification.getUnreadCount](https://docs.bsky.app/docs/api/app-bsky-notification-get-unread-count) | `BlueskyAgent.GetNotificationUnreadCount()` ||
5152
| | [app.bsky.notification.listNotifications](https://docs.bsky.app/docs/api/app-bsky-notification-list-notifications) | `BlueskyAgent.ListNotifications()` ||
5253
| | [app.bsky.notification.updateSeen](https://docs.bsky.app/docs/api/app-bsky-notification-update-seen) | `BlueskyAgent.UpdateNotificationSeenAt()` ||
53-
| **Direct Messages** | [chat.bsky.convo.deleteMessageForSelf](https://docs.bsky.app/docs/api/chat-bsky-convo-delete-message-for-self) | `BlueskyAgent.DeleteMessageForSelf()` ||
54+
| **Direct Messages** | chat.bsky.convo.AddReaction | `BlueskyAgent.AddReaction()` ||
55+
| | [chat.bsky.convo.deleteMessageForSelf](https://docs.bsky.app/docs/api/chat-bsky-convo-delete-message-for-self) | `BlueskyAgent.DeleteMessageForSelf()` ||
5456
| | [chat.bsky.convo.getConvoForMembers](https://docs.bsky.app/docs/api/chat-bsky-convo-get-convo-for-members) | `BlueskyAgent.GetConversationForMembers()` ||
5557
| | [chat.bsky.convo.getConvo](https://docs.bsky.app/docs/api/chat-bsky-convo-get-convo) | `BlueskyAgent.GetConversation()` ||
5658
| | [chat.bsky.convo.getLog](https://docs.bsky.app/docs/api/chat-bsky-convo-get-log) | `BlueskyAgent.GetConversationLog()` ||
@@ -60,6 +62,7 @@
6062
| | [chat.bsky.convo.muteConvos](https://docs.bsky.app/docs/api/chat-bsky-convo-mute-convo) | `BlueskyAgent.MuteConversation()` ||
6163
| | [chat.bsky.convo.sendMessageBatch](https://docs.bsky.app/docs/api/chat-bsky-convo-send-message-batch) | `BlueskyAgent.SendMessageBatch()` ||
6264
| | [chat.bsky.convo.sendMessage](https://docs.bsky.app/docs/api/chat-bsky-convo-send-message) | `BlueskyAgent.SendMessage()` ||
65+
| | chat.bsky.convo.RemoveReaction | `BlueskyAgent.AddReaction()` ||
6366
| | [chat.bsky.convo.unmuteConvos](https://docs.bsky.app/docs/api/chat-bsky-convo-unmute-convo) | `BlueskyAgent.UnmuteConversation()` ||
6467
| | [chat.bsky.convo.updateAllRead](https://docs.bsky.app/docs/api/chat-bsky-convo-update-all-read) | `BlueskyAgent.UpdateRead()` ||
6568
| | [chat.bsky.convo.updateRead](https://docs.bsky.app/docs/api/chat-bsky-convo-update-read) | `BlueskyAgent.UpdateRead()` ||
@@ -69,6 +72,7 @@
6972
| ------------ | ------------------------------------------------------------ | ----------------------------- | ------ |
7073
| **Identity** | * _Uses DNS and .well-known endpoint resolution not the API | `AtProtoAgent.ResolveHandle()` ||
7174
| **Labels** | [com.atproto.label.queryLabels](https://docs.bsky.app/docs/api/com-atproto-label-query-labels) | `AtProtoAgent.QueryLabels()` ||
75+
| **Moderation** | [com.atproto.moderation.createReport](https://docs.bsky.app/docs/api/com-atproto-moderation-create-report) | `AtProtoAgent.CreateModerationReport()` ||
7276
| **Repo** | [com.atproto.repo.applyWrites](https://docs.bsky.app/docs/api/com-atproto-repo-apply-writes) | `AtProtoAgent.ApplyWrites()` ||
7377
| | [com.atproto.repo.createRecord](https://docs.bsky.app/docs/api/com-atproto-repo-create-record) | `AtProtoAgent.CreateRecord()` ||
7478
| | [com.atproto.repo.deleteRecord](https://docs.bsky.app/docs/api/com-atproto-repo-delete-record) | `AtProtoAgent.DeleteRecord()` ||

docs/docs/labels.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,27 @@ From there, you use the `SubscribedLabelers` property and pass that into any API
2929
For example, to get a user's notifications with labels applied:
3030

3131
```c#
32-
AtProtoHttpResult<NotificationsList> notificationsList =
32+
var notificationsList =
3333
await agent.ListNotifications(
3434
limit: pageSize,
3535
cursor: cursor,
36-
subscribedLabelers: userPreferences.SubscribedLabelers).ConfigureAwait(false);
36+
subscribedLabelers: userPreferences.SubscribedLabelers);
3737
```
3838

39-
For notifications labels are applied to the `Author` property and the `PostView` property. You can iterate through the collection, and act on it
39+
For notifications labels are applied to the `Author` property and the `PostView` property. You can iterate through the collection, and act on it.
40+
For example, to display labels applied to the author of a notification:
4041

4142
```c#
4243
foreach (Notification notification in notificationsList.Result)
4344
{
4445
StringBuilder labelDisplayBuilder = new ();
45-
foreach (Label label in author.Labels)
46+
foreach (var label in notification.Author.Labels)
4647
{
4748
labelDisplayBuilder.Append(CultureInfo.InvariantCulture, $"[{label.Value}] ");
4849
}
4950
labelDisplayBuilder.Length--;
5051

51-
Console.WriteLine($"Author: {author} {labelDisplayBuilder}");
52+
Console.WriteLine($"Author: {notification.Author} {labelDisplayBuilder}");
5253
}
5354
```
5455

samples/Samples.ConsoleShell/Program.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
using Samples.Common;
1313
using idunno.AtProto;
14+
using idunno.AtProto.Repo;
15+
using idunno.Bluesky.Moderation;
1416

1517
namespace Samples.ConsoleShell
1618
{
@@ -33,7 +35,7 @@ static async Task PerformOperations(string? handle, string? password, string? au
3335
ArgumentException.ThrowIfNullOrEmpty(password);
3436

3537
// Uncomment the next line to route all requests through Fiddler Everywhere
36-
// proxyUri = new Uri("http://localhost:8866");
38+
proxyUri = new Uri("http://localhost:8866");
3739

3840
// Uncomment the next line to route all requests through Fiddler Classic
3941
// proxyUri = new Uri("http://localhost:8888");
@@ -105,6 +107,16 @@ static async Task PerformOperations(string? handle, string? password, string? au
105107
}
106108
// END-AUTHENTICATION
107109

110+
var post = await agent.GetPost("at://did:plc:g2p3bnbzcvcbohfpvjxfazqv/app.bsky.feed.post/3ln3n4uaipc2h", cancellationToken: cancellationToken);
111+
post.EnsureSucceeded();
112+
113+
var reportResult = await agent.CreateModerationReport(
114+
labelerDid: "did:plc:newitj5jo3uel7o4mnf3vj2o",
115+
subject: post.Result.StrongReference,
116+
reportType: ReportType.Other,
117+
reason: "Bluesky not twitter",
118+
cancellationToken: cancellationToken);
119+
108120
Debugger.Break();
109121

110122
}

samples/Samples.DirectMessages/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ static async Task PerformOperations(string? handle, string? password, string? au
3636
ArgumentException.ThrowIfNullOrEmpty(password);
3737

3838
// Uncomment the next line to route all requests through Fiddler Everywhere
39-
proxyUri = new Uri("http://localhost:8866");
39+
// proxyUri = new Uri("http://localhost:8866");
4040

4141
// Uncomment the next line to route all requests through Fiddler Classic
4242
// proxyUri = new Uri("http://localhost:8888");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) Barry Dorrans. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using idunno.AtProto.Moderation;
5+
6+
namespace idunno.AtProto.Admin
7+
{
8+
/// <summary>
9+
/// Defines a reference to a repository.
10+
/// </summary>
11+
public sealed record RepoReference : SubjectType
12+
{
13+
/// <summary>
14+
/// The <see cref="AtProto.Did"/> of the repository.
15+
/// </summary>
16+
public required Did Did { get; init; }
17+
}
18+
}

src/idunno.AtProto/Labels/AtProtoServer.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static partial class AtProtoServer
3232
/// <param name="loggerFactory">An instance of <see cref="ILoggerFactory"/> to use to create a logger.</param>
3333
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
3434
/// <returns>The task object representing the asynchronous operation.</returns>
35-
/// <exception cref="ArgumentNullException">Thrown when <paramref name="uriPatterns" /> is null</exception>
35+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="uriPatterns" />, <paramref name="service"/> or <paramref name="httpClient"/> is null</exception>
3636
/// <exception cref="ArgumentOutOfRangeException">Thrown <paramref name="uriPatterns"/> is empty or if <paramref name="limit"/> is &lt;1 or &gt;250.</exception>
3737
[UnconditionalSuppressMessage(
3838
"Trimming",
@@ -54,6 +54,8 @@ public static async Task<AtProtoHttpResult<PagedReadOnlyCollection<Label>>> Quer
5454
CancellationToken cancellationToken = default)
5555
{
5656
ArgumentNullException.ThrowIfNull(uriPatterns);
57+
ArgumentNullException.ThrowIfNull(service);
58+
ArgumentNullException.ThrowIfNull(httpClient);
5759

5860
var uriPatternList = new List<string>(uriPatterns);
5961
if (uriPatternList.Count == 0)
@@ -87,7 +89,6 @@ public static async Task<AtProtoHttpResult<PagedReadOnlyCollection<Label>>> Quer
8789
queryStringBuilder.Append(CultureInfo.InvariantCulture, $"&cursor={Uri.EscapeDataString(cursor)}");
8890
}
8991

90-
9192
AtProtoHttpClient<QueryLabelsResponse> request = new(loggerFactory);
9293

9394
AtProtoHttpResult<QueryLabelsResponse> response = await request.Get(

src/idunno.AtProto/Labels/Label.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Diagnostics;
55
using System.Diagnostics.CodeAnalysis;
66
using System.Text.Json.Serialization;
7+
78
using idunno.AtProto.Repo;
89

910
namespace idunno.AtProto.Labels
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright (c) Barry Dorrans. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Text.Json.Serialization;
6+
7+
namespace idunno.AtProto.Labels
8+
{
9+
/// <summary>
10+
/// Encapsulates a label value and its expected interpretations and behaviors.
11+
/// </summary>
12+
public sealed record LabelValueDefinition
13+
{
14+
/// <summary>
15+
/// Gets the identifer of the label
16+
/// </summary>
17+
public required string Identifier { get; init; }
18+
19+
/// <summary>
20+
/// Gets an indication of how a client visually convey this label.
21+
///
22+
/// 'inform' means neutral and informational;
23+
/// 'alert' means negative and warning;
24+
/// 'none' means show nothing.
25+
///
26+
/// Other values may occur.
27+
/// </summary>
28+
public required string Severity { get; init; }
29+
30+
/// <summary>
31+
/// Gets an indication of how a client should hide entities who have the the label applied.
32+
///
33+
/// 'inform' means neutral and informational;
34+
///
35+
/// 'alert' means negative and warning;
36+
///
37+
/// 'none' means show nothing.
38+
///
39+
/// Other values may occur.
40+
/// </summary>
41+
public required string Blurs { get; init; }
42+
43+
/// <summary>
44+
/// Gets the default setting for the label, if any.
45+
/// </summary>
46+
public string DefaultSetting { get; init; } = "warn";
47+
48+
/// <summary>
49+
/// Gets a flag indicating if the user needs to have adult content enabled to configure the label.
50+
/// </summary>
51+
public bool AdultOnly { get; init; }
52+
53+
/// <summary>
54+
/// A collection of strings which describe the label in the UI, localized into a specific language.
55+
/// </summary>
56+
public ICollection<LabelValueDefinitionStrings> Locales { get; init; } = [];
57+
}
58+
59+
60+
/// <summary>
61+
/// Strings which describe the label in the UI, localized into a specific language.
62+
/// </summary>
63+
public sealed record LabelValueDefinitionStrings
64+
{
65+
/// <summary>
66+
/// Gets the code of the language these strings are written in.
67+
/// </summary>
68+
public required string Lang { get; init; }
69+
70+
/// <summary>
71+
/// Gets a short human-readable name for the label.
72+
/// </summary>
73+
public required string Name { get; init; }
74+
75+
/// <summary>
76+
/// Gets a longer description of what the label means and why it might be applied.
77+
/// </summary>
78+
public required string Description { get; init; }
79+
}
80+
}

src/idunno.AtProto/Logger.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.Extensions.Logging;
77

88
using idunno.AtProto.Repo;
9-
using System.Text.Json;
109

1110
namespace idunno.AtProto
1211
{

0 commit comments

Comments
 (0)