Skip to content

Commit c60d012

Browse files
yingduyingduYing Du
andauthored
support message edit, message soft delete, message undelete events to bot (#6564)
Co-authored-by: Ying Du <yingdu@microsoft.com>
1 parent 55eb4f1 commit c60d012

File tree

4 files changed

+371
-0
lines changed

4 files changed

+371
-0
lines changed

libraries/Microsoft.Bot.Builder/ActivityHandler.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public class ActivityHandler : IBot
4141
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> method.
4242
/// </remarks>
4343
/// <seealso cref="OnMessageActivityAsync(ITurnContext{IMessageActivity}, CancellationToken)"/>
44+
/// <seealso cref="OnMessageUpdateActivityAsync(ITurnContext{IMessageUpdateActivity}, CancellationToken)"/>
45+
/// <seealso cref="OnMessageDeleteActivityAsync(ITurnContext{IMessageDeleteActivity}, CancellationToken)"/>
4446
/// <seealso cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
4547
/// <seealso cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
4648
/// <seealso cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
@@ -69,6 +71,14 @@ public class ActivityHandler : IBot
6971
case ActivityTypes.Message:
7072
await OnMessageActivityAsync(new DelegatingTurnContext<IMessageActivity>(turnContext), cancellationToken).ConfigureAwait(false);
7173
break;
74+
75+
case ActivityTypes.MessageUpdate:
76+
await OnMessageUpdateActivityAsync(new DelegatingTurnContext<IMessageUpdateActivity>(turnContext), cancellationToken).ConfigureAwait(false);
77+
break;
78+
79+
case ActivityTypes.MessageDelete:
80+
await OnMessageDeleteActivityAsync(new DelegatingTurnContext<IMessageDeleteActivity>(turnContext), cancellationToken).ConfigureAwait(false);
81+
break;
7282

7383
case ActivityTypes.ConversationUpdate:
7484
await OnConversationUpdateActivityAsync(new DelegatingTurnContext<IConversationUpdateActivity>(turnContext), cancellationToken).ConfigureAwait(false);
@@ -147,6 +157,42 @@ protected virtual Task OnMessageActivityAsync(ITurnContext<IMessageActivity> tur
147157
return Task.CompletedTask;
148158
}
149159

160+
/// <summary>
161+
/// Override this in a derived class to provide logic specific to
162+
/// <see cref="ActivityTypes.MessageUpdate"/> activities, such as the conversational logic.
163+
/// </summary>
164+
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
165+
/// <param name="cancellationToken">A cancellation token that can be used by other objects
166+
/// or threads to receive notice of cancellation.</param>
167+
/// <returns>A task that represents the work queued to execute.</returns>
168+
/// <remarks>
169+
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
170+
/// method receives a message update activity, it calls this method.
171+
/// </remarks>
172+
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
173+
protected virtual Task OnMessageUpdateActivityAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
174+
{
175+
return Task.CompletedTask;
176+
}
177+
178+
/// <summary>
179+
/// Override this in a derived class to provide logic specific to
180+
/// <see cref="ActivityTypes.MessageDelete"/> activities, such as the conversational logic.
181+
/// </summary>
182+
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
183+
/// <param name="cancellationToken">A cancellation token that can be used by other objects
184+
/// or threads to receive notice of cancellation.</param>
185+
/// <returns>A task that represents the work queued to execute.</returns>
186+
/// <remarks>
187+
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
188+
/// method receives a message delete activity, it calls this method.
189+
/// </remarks>
190+
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
191+
protected virtual Task OnMessageDeleteActivityAsync(ITurnContext<IMessageDeleteActivity> turnContext, CancellationToken cancellationToken)
192+
{
193+
return Task.CompletedTask;
194+
}
195+
150196
/// <summary>
151197
/// Invoked when a conversation update activity is received from the channel when the base behavior of
152198
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> is used.

libraries/Microsoft.Bot.Builder/Teams/TeamsActivityHandler.cs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,111 @@ protected virtual Task OnTeamsReadReceiptAsync(ReadReceiptInfo readReceiptInfo,
825825
return Task.CompletedTask;
826826
}
827827

828+
/// <summary>
829+
/// Invoked when an message update activity is received.
830+
/// <see cref="ActivityTypes.MessageUpdate"/> activities, such as the conversational logic.
831+
/// </summary>
832+
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
833+
/// <param name="cancellationToken">A cancellation token that can be used by other objects
834+
/// or threads to receive notice of cancellation.</param>
835+
/// <returns>A task that represents the work queued to execute.</returns>
836+
/// <remarks>
837+
/// In a derived class, override this method to add logic that applies to all message update activities.
838+
/// </remarks>
839+
protected override Task OnMessageUpdateActivityAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
840+
{
841+
if (turnContext.Activity.ChannelId == Channels.Msteams)
842+
{
843+
var channelData = turnContext.Activity.GetChannelData<TeamsChannelData>();
844+
845+
if (channelData != null)
846+
{
847+
switch (channelData.EventType)
848+
{
849+
case "editMessage":
850+
return OnTeamsMessageEditAsync(turnContext, cancellationToken);
851+
852+
case "undeleteMessage":
853+
return OnTeamsMessageUndeleteAsync(turnContext, cancellationToken);
854+
855+
default:
856+
return base.OnMessageUpdateActivityAsync(turnContext, cancellationToken);
857+
}
858+
}
859+
}
860+
861+
return base.OnMessageUpdateActivityAsync(turnContext, cancellationToken);
862+
}
863+
864+
/// <summary>
865+
/// Invoked when an message delete activity is received.
866+
/// <see cref="ActivityTypes.MessageDelete"/> activities, such as the conversational logic.
867+
/// </summary>
868+
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
869+
/// <param name="cancellationToken">A cancellation token that can be used by other objects
870+
/// or threads to receive notice of cancellation.</param>
871+
/// <returns>A task that represents the work queued to execute.</returns>
872+
/// <remarks>
873+
/// In a derived class, override this method to add logic that applies to all message update activities.
874+
/// </remarks>
875+
protected override Task OnMessageDeleteActivityAsync(ITurnContext<IMessageDeleteActivity> turnContext, CancellationToken cancellationToken)
876+
{
877+
if (turnContext.Activity.ChannelId == Channels.Msteams)
878+
{
879+
var channelData = turnContext.Activity.GetChannelData<TeamsChannelData>();
880+
881+
if (channelData != null)
882+
{
883+
switch (channelData.EventType)
884+
{
885+
case "softDeleteMessage":
886+
return OnTeamsMessageSoftDeleteAsync(turnContext, cancellationToken);
887+
888+
default:
889+
return base.OnMessageDeleteActivityAsync(turnContext, cancellationToken);
890+
}
891+
}
892+
}
893+
894+
return base.OnMessageDeleteActivityAsync(turnContext, cancellationToken);
895+
}
896+
897+
/// <summary>
898+
/// Invoked when a edit message event activity is received.
899+
/// </summary>
900+
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
901+
/// <param name="cancellationToken">A cancellation token that can be used by other objects
902+
/// or threads to receive notice of cancellation.</param>
903+
/// <returns>A task that represents the work queued to execute.</returns>
904+
protected virtual Task OnTeamsMessageEditAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
905+
{
906+
return Task.CompletedTask;
907+
}
908+
909+
/// <summary>
910+
/// Invoked when a undo soft delete message event activity is received.
911+
/// </summary>
912+
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
913+
/// <param name="cancellationToken">A cancellation token that can be used by other objects
914+
/// or threads to receive notice of cancellation.</param>
915+
/// <returns>A task that represents the work queued to execute.</returns>
916+
protected virtual Task OnTeamsMessageUndeleteAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
917+
{
918+
return Task.CompletedTask;
919+
}
920+
921+
/// <summary>
922+
/// Invoked when a soft delete message event activity is received.
923+
/// </summary>
924+
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
925+
/// <param name="cancellationToken">A cancellation token that can be used by other objects
926+
/// or threads to receive notice of cancellation.</param>
927+
/// <returns>A task that represents the work queued to execute.</returns>
928+
protected virtual Task OnTeamsMessageSoftDeleteAsync(ITurnContext<IMessageDeleteActivity> turnContext, CancellationToken cancellationToken)
929+
{
930+
return Task.CompletedTask;
931+
}
932+
828933
/// <summary>
829934
/// Safely casts an object to an object of type <typeparamref name="T"/> .
830935
/// </summary>

tests/Microsoft.Bot.Builder.Tests/ActivityHandlerTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,38 @@ public async Task TestMessageActivity()
3838
Assert.Equal("OnMessageActivityAsync", bot.Record[0]);
3939
}
4040

41+
[Fact]
42+
public async Task TestMessageUpdateActivity()
43+
{
44+
// Arrange
45+
var activity = new Activity { Type = ActivityTypes.MessageUpdate };
46+
var turnContext = new TurnContext(new NotImplementedAdapter(), activity);
47+
48+
// Act
49+
var bot = new TestActivityHandler();
50+
await ((IBot)bot).OnTurnAsync(turnContext);
51+
52+
// Assert
53+
Assert.Single(bot.Record);
54+
Assert.Equal("OnMessageUpdateActivityAsync", bot.Record[0]);
55+
}
56+
57+
[Fact]
58+
public async Task TestMessageDeleteActivity()
59+
{
60+
// Arrange
61+
var activity = new Activity { Type = ActivityTypes.MessageDelete };
62+
var turnContext = new TurnContext(new NotImplementedAdapter(), activity);
63+
64+
// Act
65+
var bot = new TestActivityHandler();
66+
await ((IBot)bot).OnTurnAsync(turnContext);
67+
68+
// Assert
69+
Assert.Single(bot.Record);
70+
Assert.Equal("OnMessageDeleteActivityAsync", bot.Record[0]);
71+
}
72+
4173
[Fact]
4274
public async Task TestEndOfConversationActivity()
4375
{
@@ -798,6 +830,18 @@ protected override Task OnMessageActivityAsync(ITurnContext<IMessageActivity> tu
798830
return base.OnMessageActivityAsync(turnContext, cancellationToken);
799831
}
800832

833+
protected override Task OnMessageUpdateActivityAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
834+
{
835+
Record.Add(MethodBase.GetCurrentMethod().Name);
836+
return base.OnMessageUpdateActivityAsync(turnContext, cancellationToken);
837+
}
838+
839+
protected override Task OnMessageDeleteActivityAsync(ITurnContext<IMessageDeleteActivity> turnContext, CancellationToken cancellationToken)
840+
{
841+
Record.Add(MethodBase.GetCurrentMethod().Name);
842+
return base.OnMessageDeleteActivityAsync(turnContext, cancellationToken);
843+
}
844+
801845
protected override Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
802846
{
803847
Record.Add(MethodBase.GetCurrentMethod().Name);

0 commit comments

Comments
 (0)