* Added Teams meeting start/end

* Switched to using OffsetDateTime instead of String for Teams meeting models

* Accept case insensitive properties in JSON

* Corrected unit test after case insensitive JSON change

* Corrected MeetingEndEventDetails superclass

* Added TeamsInfo.getMeetingInfo overload to allow optional meetingId
This commit is contained in:
tracyboehrer 2021-06-25 08:39:57 -05:00 коммит произвёл GitHub
Родитель 5a9d1cf104
Коммит 1961ccab23
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 532 добавлений и 249 удалений

Просмотреть файл

@ -9,6 +9,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.bot.builder.ActivityHandler;
import com.microsoft.bot.builder.InvokeResponse;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.connector.Async;
import com.microsoft.bot.connector.Channels;
import com.microsoft.bot.connector.rest.ErrorResponseException;
import com.microsoft.bot.schema.ChannelAccount;
import com.microsoft.bot.schema.Error;
@ -18,6 +20,8 @@ import com.microsoft.bot.schema.Serialization;
import com.microsoft.bot.schema.teams.AppBasedLinkQuery;
import com.microsoft.bot.schema.teams.ChannelInfo;
import com.microsoft.bot.schema.teams.FileConsentCardResponse;
import com.microsoft.bot.schema.teams.MeetingEndEventDetails;
import com.microsoft.bot.schema.teams.MeetingStartEventDetails;
import com.microsoft.bot.schema.teams.MessagingExtensionAction;
import com.microsoft.bot.schema.teams.MessagingExtensionActionResponse;
import com.microsoft.bot.schema.teams.MessagingExtensionQuery;
@ -45,7 +49,7 @@ import java.util.concurrent.CompletionException;
* Pre and post processing of Activities can be plugged in by deriving and
* calling the base class implementation.
*/
@SuppressWarnings({ "checkstyle:JavadocMethod", "checkstyle:DesignForExtension", "checkstyle:MethodLength" })
@SuppressWarnings({"checkstyle:JavadocMethod", "checkstyle:DesignForExtension", "checkstyle:MethodLength"})
public class TeamsActivityHandler extends ActivityHandler {
/**
* Invoked when an invoke activity is received from the connector when the base
@ -59,87 +63,66 @@ public class TeamsActivityHandler extends ActivityHandler {
CompletableFuture<InvokeResponse> result;
try {
if (
turnContext.getActivity().getName() == null
&& turnContext.getActivity().isTeamsActivity()
) {
if (turnContext.getActivity().getName() == null && turnContext.getActivity().isTeamsActivity()) {
result = onTeamsCardActionInvoke(turnContext);
} else {
switch (turnContext.getActivity().getName()) {
case "fileConsent/invoke":
result = onTeamsFileConsent(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
FileConsentCardResponse.class
)
Serialization.safeGetAs(turnContext.getActivity().getValue(), FileConsentCardResponse.class)
);
break;
case "actionableMessage/executeAction":
result = onTeamsO365ConnectorCardAction(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
O365ConnectorCardActionQuery.class
)
Serialization
.safeGetAs(turnContext.getActivity().getValue(), O365ConnectorCardActionQuery.class)
).thenApply(aVoid -> createInvokeResponse(null));
break;
case "composeExtension/queryLink":
result = onTeamsAppBasedLinkQuery(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
AppBasedLinkQuery.class
)
Serialization.safeGetAs(turnContext.getActivity().getValue(), AppBasedLinkQuery.class)
).thenApply(ActivityHandler::createInvokeResponse);
break;
case "composeExtension/query":
result = onTeamsMessagingExtensionQuery(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
MessagingExtensionQuery.class
)
Serialization.safeGetAs(turnContext.getActivity().getValue(), MessagingExtensionQuery.class)
).thenApply(ActivityHandler::createInvokeResponse);
break;
case "composeExtension/selectItem":
result = onTeamsMessagingExtensionSelectItem(
turnContext,
turnContext.getActivity().getValue()
).thenApply(ActivityHandler::createInvokeResponse);
result = onTeamsMessagingExtensionSelectItem(turnContext, turnContext.getActivity().getValue())
.thenApply(ActivityHandler::createInvokeResponse);
break;
case "composeExtension/submitAction":
result = onTeamsMessagingExtensionSubmitActionDispatch(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
MessagingExtensionAction.class
)
).thenApply(ActivityHandler::createInvokeResponse);
result =
onTeamsMessagingExtensionSubmitActionDispatch(
turnContext,
Serialization
.safeGetAs(turnContext.getActivity().getValue(), MessagingExtensionAction.class)
).thenApply(ActivityHandler::createInvokeResponse);
break;
case "composeExtension/fetchTask":
result = onTeamsMessagingExtensionFetchTask(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
MessagingExtensionAction.class
)
).thenApply(ActivityHandler::createInvokeResponse);
result =
onTeamsMessagingExtensionFetchTask(
turnContext,
Serialization
.safeGetAs(turnContext.getActivity().getValue(), MessagingExtensionAction.class)
).thenApply(ActivityHandler::createInvokeResponse);
break;
case "composeExtension/querySettingUrl":
result = onTeamsMessagingExtensionConfigurationQuerySettingUrl(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
MessagingExtensionQuery.class
)
Serialization.safeGetAs(turnContext.getActivity().getValue(), MessagingExtensionQuery.class)
).thenApply(ActivityHandler::createInvokeResponse);
break;
@ -160,40 +143,28 @@ public class TeamsActivityHandler extends ActivityHandler {
case "task/fetch":
result = onTeamsTaskModuleFetch(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
TaskModuleRequest.class
)
Serialization.safeGetAs(turnContext.getActivity().getValue(), TaskModuleRequest.class)
).thenApply(ActivityHandler::createInvokeResponse);
break;
case "task/submit":
result = onTeamsTaskModuleSubmit(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
TaskModuleRequest.class
)
Serialization.safeGetAs(turnContext.getActivity().getValue(), TaskModuleRequest.class)
).thenApply(ActivityHandler::createInvokeResponse);
break;
case "tab/fetch":
result = onTeamsTabFetch(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
TabRequest.class
)
Serialization.safeGetAs(turnContext.getActivity().getValue(), TabRequest.class)
).thenApply(ActivityHandler::createInvokeResponse);
break;
case "tab/submit":
result = onTeamsTabSubmit(
turnContext,
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
TabSubmit.class
)
Serialization.safeGetAs(turnContext.getActivity().getValue(), TabSubmit.class)
).thenApply(ActivityHandler::createInvokeResponse);
break;
@ -208,17 +179,12 @@ public class TeamsActivityHandler extends ActivityHandler {
}
return result.exceptionally(e -> {
if (
e instanceof CompletionException && e.getCause() instanceof InvokeResponseException
) {
if (e instanceof CompletionException && e.getCause() instanceof InvokeResponseException) {
return ((InvokeResponseException) e.getCause()).createInvokeResponse();
} else if (e instanceof InvokeResponseException) {
return ((InvokeResponseException) e).createInvokeResponse();
}
return new InvokeResponse(
HttpURLConnection.HTTP_INTERNAL_ERROR,
e.getLocalizedMessage()
);
return new InvokeResponse(HttpURLConnection.HTTP_INTERNAL_ERROR, e.getLocalizedMessage());
});
}
@ -255,9 +221,10 @@ public class TeamsActivityHandler extends ActivityHandler {
/**
* Invoked when a file consent card activity is received from the connector.
*
* @param turnContext The current TurnContext.
* @param fileConsentCardResponse The response representing the value of the invoke activity sent
* when the user acts on a file consent card.
* @param turnContext The current TurnContext.
* @param fileConsentCardResponse The response representing the value of the
* invoke activity sent when the user acts on a
* file consent card.
* @return An InvokeResponse depending on the action of the file consent card.
*/
protected CompletableFuture<InvokeResponse> onTeamsFileConsent(
@ -266,14 +233,12 @@ public class TeamsActivityHandler extends ActivityHandler {
) {
switch (fileConsentCardResponse.getAction()) {
case "accept":
return onTeamsFileConsentAccept(turnContext, fileConsentCardResponse).thenApply(
aVoid -> createInvokeResponse(null)
);
return onTeamsFileConsentAccept(turnContext, fileConsentCardResponse)
.thenApply(aVoid -> createInvokeResponse(null));
case "decline":
return onTeamsFileConsentDecline(turnContext, fileConsentCardResponse).thenApply(
aVoid -> createInvokeResponse(null)
);
return onTeamsFileConsentDecline(turnContext, fileConsentCardResponse)
.thenApply(aVoid -> createInvokeResponse(null));
default:
CompletableFuture<InvokeResponse> result = new CompletableFuture<>();
@ -290,9 +255,10 @@ public class TeamsActivityHandler extends ActivityHandler {
/**
* Invoked when a file consent card is accepted by the user.
*
* @param turnContext The current TurnContext.
* @param fileConsentCardResponse The response representing the value of the invoke activity sent
* when the user accepts a file consent card.
* @param turnContext The current TurnContext.
* @param fileConsentCardResponse The response representing the value of the
* invoke activity sent when the user accepts a
* file consent card.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsFileConsentAccept(
@ -305,9 +271,10 @@ public class TeamsActivityHandler extends ActivityHandler {
/**
* Invoked when a file consent card is declined by the user.
*
* @param turnContext The current TurnContext.
* @param fileConsentCardResponse The response representing the value of the invoke activity sent
* when the user declines a file consent card.
* @param turnContext The current TurnContext.
* @param fileConsentCardResponse The response representing the value of the
* invoke activity sent when the user declines a
* file consent card.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsFileConsentDecline(
@ -318,10 +285,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a Messaging Extension Query activity is received from the connector.
* Invoked when a Messaging Extension Query activity is received from the
* connector.
*
* @param turnContext The current TurnContext.
* @param query The query for the search command.
* @param query The query for the search command.
* @return The Messaging Extension Response for the query.
*/
protected CompletableFuture<MessagingExtensionResponse> onTeamsMessagingExtensionQuery(
@ -332,10 +300,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a O365 Connector Card Action activity is received from the connector.
* Invoked when a O365 Connector Card Action activity is received from the
* connector.
*
* @param turnContext The current TurnContext.
* @param query The O365 connector card HttpPOST invoke query.
* @param query The O365 connector card HttpPOST invoke query.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsO365ConnectorCardAction(
@ -349,7 +318,7 @@ public class TeamsActivityHandler extends ActivityHandler {
* Invoked when an app based link query activity is received from the connector.
*
* @param turnContext The current TurnContext.
* @param query The invoke request body type for app-based link query.
* @param query The invoke request body type for app-based link query.
* @return The Messaging Extension Response for the query.
*/
protected CompletableFuture<MessagingExtensionResponse> onTeamsAppBasedLinkQuery(
@ -360,10 +329,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a messaging extension select item activity is received from the connector.
* Invoked when a messaging extension select item activity is received from the
* connector.
*
* @param turnContext The current TurnContext.
* @param query The object representing the query.
* @param query The object representing the query.
* @return The Messaging Extension Response for the query.
*/
protected CompletableFuture<MessagingExtensionResponse> onTeamsMessagingExtensionSelectItem(
@ -374,10 +344,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a Messaging Extension Fetch activity is received from the connector.
* Invoked when a Messaging Extension Fetch activity is received from the
* connector.
*
* @param turnContext The current TurnContext.
* @param action The messaging extension action.
* @param action The messaging extension action.
* @return The Messaging Extension Action Response for the action.
*/
protected CompletableFuture<MessagingExtensionActionResponse> onTeamsMessagingExtensionFetchTask(
@ -388,10 +359,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a messaging extension submit action dispatch activity is received from the connector.
* Invoked when a messaging extension submit action dispatch activity is
* received from the connector.
*
* @param turnContext The current TurnContext.
* @param action The messaging extension action.
* @param action The messaging extension action.
* @return The Messaging Extension Action Response for the action.
*/
protected CompletableFuture<MessagingExtensionActionResponse> onTeamsMessagingExtensionSubmitActionDispatch(
@ -411,8 +383,7 @@ public class TeamsActivityHandler extends ActivityHandler {
result.completeExceptionally(
new InvokeResponseException(
HttpURLConnection.HTTP_BAD_REQUEST,
action.getBotMessagePreviewAction()
+ " is not a support BotMessagePreviewAction"
action.getBotMessagePreviewAction() + " is not a support BotMessagePreviewAction"
)
);
return result;
@ -423,10 +394,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a messaging extension submit action activity is received from the connector.
* Invoked when a messaging extension submit action activity is received from
* the connector.
*
* @param turnContext The current TurnContext.
* @param action The messaging extension action.
* @param action The messaging extension action.
* @return The Messaging Extension Action Response for the action.
*/
protected CompletableFuture<MessagingExtensionActionResponse> onTeamsMessagingExtensionSubmitAction(
@ -437,10 +409,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a messaging extension bot message preview edit activity is received from the connector.
* Invoked when a messaging extension bot message preview edit activity is
* received from the connector.
*
* @param turnContext The current TurnContext.
* @param action The messaging extension action.
* @param action The messaging extension action.
* @return The Messaging Extension Action Response for the action.
*/
protected CompletableFuture<MessagingExtensionActionResponse> onTeamsMessagingExtensionBotMessagePreviewEdit(
@ -451,10 +424,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a messaging extension bot message preview send activity is received from the connector.
* Invoked when a messaging extension bot message preview send activity is
* received from the connector.
*
* @param turnContext The current TurnContext.
* @param action The messaging extension action.
* @param action The messaging extension action.
* @return The Messaging Extension Action Response for the action.
*/
protected CompletableFuture<MessagingExtensionActionResponse> onTeamsMessagingExtensionBotMessagePreviewSend(
@ -465,10 +439,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a messaging extension configuration query setting url activity is received from the connector.
* Invoked when a messaging extension configuration query setting url activity
* is received from the connector.
*
* @param turnContext The current TurnContext.
* @param query The Messaging extension query.
* @param query The Messaging extension query.
* @return The Messaging Extension Response for the query.
*/
protected CompletableFuture<MessagingExtensionResponse> onTeamsMessagingExtensionConfigurationQuerySettingUrl(
@ -479,10 +454,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Override this in a derived class to provide logic for when a configuration is set for a messaging extension.
* Override this in a derived class to provide logic for when a configuration is
* set for a messaging extension.
*
* @param turnContext The current TurnContext.
* @param settings Object representing the configuration settings.
* @param settings Object representing the configuration settings.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsMessagingExtensionConfigurationSetting(
@ -493,9 +469,10 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Override this in a derived class to provide logic for when a task module is fetched.
* Override this in a derived class to provide logic for when a task module is
* fetched.
*
* @param turnContext The current TurnContext.
* @param turnContext The current TurnContext.
* @param taskModuleRequest The task module invoke request value payload.
* @return A Task Module Response for the request.
*/
@ -507,11 +484,11 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Override this in a derived class to provide logic for when a card button
* is clicked in a messaging extension.
* Override this in a derived class to provide logic for when a card button is
* clicked in a messaging extension.
*
* @param turnContext The current TurnContext.
* @param cardData Object representing the card data.
* @param cardData Object representing the card data.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsMessagingExtensionCardButtonClicked(
@ -522,9 +499,10 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Override this in a derived class to provide logic for when a task module is submited.
* Override this in a derived class to provide logic for when a task module is
* submited.
*
* @param turnContext The current TurnContext.
* @param turnContext The current TurnContext.
* @param taskModuleRequest The task module invoke request value payload.
* @return A Task Module Response for the request.
*/
@ -537,18 +515,17 @@ public class TeamsActivityHandler extends ActivityHandler {
/**
* Invoked when a conversation update activity is received from the channel.
* Conversation update activities are useful when it comes to responding to users being added to
* or removed from the channel.
* For example, a bot could respond to a user being added by greeting the user.
* Conversation update activities are useful when it comes to responding to
* users being added to or removed from the channel. For example, a bot could
* respond to a user being added by greeting the user.
*
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onConversationUpdateActivity(TurnContext turnContext) {
if (turnContext.getActivity().isTeamsActivity()) {
ResultPair<TeamsChannelData> channelData = turnContext.getActivity().tryGetChannelData(
TeamsChannelData.class
);
ResultPair<TeamsChannelData> channelData =
turnContext.getActivity().tryGetChannelData(TeamsChannelData.class);
if (turnContext.getActivity().getMembersAdded() != null) {
return onTeamsMembersAddedDispatch(
@ -648,14 +625,14 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Override this in a derived class to provide logic for when members other than the bot
* join the channel, such as your bot's welcome logic.
* It will get the associated members with the provided accounts.
* Override this in a derived class to provide logic for when members other than
* the bot join the channel, such as your bot's welcome logic. It will get the
* associated members with the provided accounts.
*
* @param membersAdded A list of all the accounts added to the channel, as described by
* the conversation update activity.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @param membersAdded A list of all the accounts added to the channel, as
* described by the conversation update activity.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsMembersAddedDispatch(
@ -694,7 +671,7 @@ public class TeamsActivityHandler extends ActivityHandler {
throw ex;
}
}
} else {
} else {
throw ex;
}
}
@ -716,14 +693,14 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Override this in a derived class to provide logic for when members other than the bot
* leave the channel, such as your bot's good-bye logic.
* It will get the associated members with the provided accounts.
* Override this in a derived class to provide logic for when members other than
* the bot leave the channel, such as your bot's good-bye logic. It will get the
* associated members with the provided accounts.
*
* @param membersRemoved A list of all the accounts removed from the channel, as described by
* the conversation update activity.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @param membersRemoved A list of all the accounts removed from the channel, as
* described by the conversation update activity.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsMembersRemovedDispatch(
@ -748,13 +725,13 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Override this in a derived class to provide logic for when members other than the bot
* join the channel, such as your bot's welcome logic.
* Override this in a derived class to provide logic for when members other than
* the bot join the channel, such as your bot's welcome logic.
*
* @param membersAdded A list of all the members added to the channel, as described by
* the conversation update activity.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @param membersAdded A list of all the members added to the channel, as
* described by the conversation update activity.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsMembersAdded(
@ -766,13 +743,13 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Override this in a derived class to provide logic for when members other than the bot
* leave the channel, such as your bot's good-bye logic.
* Override this in a derived class to provide logic for when members other than
* the bot leave the channel, such as your bot's good-bye logic.
*
* @param membersRemoved A list of all the members removed from the channel, as described by
* the conversation update activity.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @param membersRemoved A list of all the members removed from the channel, as
* described by the conversation update activity.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsMembersRemoved(
@ -788,7 +765,7 @@ public class TeamsActivityHandler extends ActivityHandler {
* Channel Created correspond to the user creating a new channel.
*
* @param channelInfo The channel info object which describes the channel.
* @param teamInfo The team info object representing the team.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
@ -805,7 +782,7 @@ public class TeamsActivityHandler extends ActivityHandler {
* Channel Deleted correspond to the user deleting an existing channel.
*
* @param channelInfo The channel info object which describes the channel.
* @param teamInfo The team info object representing the team.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
@ -822,7 +799,7 @@ public class TeamsActivityHandler extends ActivityHandler {
* Channel Renamed correspond to the user renaming an existing channel.
*
* @param channelInfo The channel info object which describes the channel.
* @param teamInfo The team info object representing the team.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
@ -835,11 +812,12 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a Channel Restored event activity is received from the connector.
* Channel Restored correspond to the user restoring a previously deleted channel.
* Invoked when a Channel Restored event activity is received from the
* connector. Channel Restored correspond to the user restoring a previously
* deleted channel.
*
* @param channelInfo The channel info object which describes the channel.
* @param teamInfo The team info object representing the team.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
@ -882,8 +860,8 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Invoked when a Team Hard Deleted event activity is received from the connector.
* Team Hard Deleted correspond to the user hard-deleting a team.
* Invoked when a Team Hard Deleted event activity is received from the
* connector. Team Hard Deleted correspond to the user hard-deleting a team.
*
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
@ -901,7 +879,7 @@ public class TeamsActivityHandler extends ActivityHandler {
* Channel Renamed correspond to the user renaming an existing channel.
*
* @param channelInfo The channel info object which describes the channel.
* @param teamInfo The team info object representing the team.
* @param teamInfo The team info object representing the team.
* @param turnContext The current TurnContext.
* @return A task that represents the work queued to execute.
*/
@ -945,8 +923,9 @@ public class TeamsActivityHandler extends ActivityHandler {
/**
* Override this in a derived class to provide logic for when a tab is fetched.
*
* @param turnContext The context object for this turn.
* @param tabRequest The tab invoke request value payload.
* @param tabRequest The tab invoke request value payload.
* @return A Tab Response for the request.
*/
protected CompletableFuture<TabResponse> onTeamsTabFetch(TurnContext turnContext, TabRequest tabRequest) {
@ -954,19 +933,101 @@ public class TeamsActivityHandler extends ActivityHandler {
}
/**
* Override this in a derived class to provide logic for when a tab is submitted.
* Override this in a derived class to provide logic for when a tab is
* submitted.
*
* @param turnContext The context object for this turn.
* @param tabSubmit The tab submit invoke request value payload.
* @param tabSubmit The tab submit invoke request value payload.
* @return A Tab Response for the request.
*/
protected CompletableFuture<TabResponse> onTeamsTabSubmit(TurnContext turnContext, TabSubmit tabSubmit) {
return withException(new InvokeResponseException(HttpURLConnection.HTTP_NOT_IMPLEMENTED));
}
/**
* Invoked when a "tokens/response" event is received when the base behavior of
* {@link #onEventActivity(TurnContext)} is used.
*
* <p>
* If using an OAuthPrompt, override this method to forward this
* {@link com.microsoft.bot.schema.Activity} to the current dialog.
* </p>
*
* <p>
* By default, this method does nothing.
* </p>
* <p>
* When the {@link #onEventActivity(TurnContext)} method receives an event with
* a {@link com.microsoft.bot.schema.Activity#getName()} of `tokens/response`, it calls this method.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
@Override
protected CompletableFuture<Void> onEventActivity(TurnContext turnContext) {
if (StringUtils.equals(turnContext.getActivity().getChannelId(), Channels.MSTEAMS)) {
try {
switch (turnContext.getActivity().getName()) {
case "application/vnd.microsoft.meetingStart":
return onTeamsMeetingStart(
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
MeetingStartEventDetails.class
),
turnContext
);
case "application/vnd.microsoft.meetingEnd":
return onTeamsMeetingEnd(
Serialization.safeGetAs(
turnContext.getActivity().getValue(),
MeetingEndEventDetails.class
),
turnContext
);
default:
break;
}
} catch (Throwable t) {
return Async.completeExceptionally(t);
}
}
return super.onEventActivity(turnContext);
}
/**
* Invoked when a Teams Meeting Start event activity is received from the
* connector. Override this in a derived class to provide logic for when a
* meeting is started.
*
* @param meeting The details of the meeting.
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsMeetingStart(MeetingStartEventDetails meeting, TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
/**
* Invoked when a Teams Meeting End event activity is received from the
* connector. Override this in a derived class to provide logic for when a
* meeting is ended.
*
* @param meeting The details of the meeting.
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onTeamsMeetingEnd(MeetingEndEventDetails meeting, TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
/**
* Invoke a new InvokeResponseException with a HTTP 501 code status.
*
* @return true if this invocation caused this CompletableFuture to transition to a completed state, else false
* @return true if this invocation caused this CompletableFuture to transition
* to a completed state, else false
*/
protected <T> CompletableFuture<T> notImplemented() {
return notImplemented(null);
@ -976,13 +1037,12 @@ public class TeamsActivityHandler extends ActivityHandler {
* Invoke a new InvokeResponseException with a HTTP 501 code status.
*
* @param body The body for the InvokeResponseException.
* @return true if this invocation caused this CompletableFuture to transition to a completed state, else false
* @return true if this invocation caused this CompletableFuture to transition
* to a completed state, else false
*/
protected <T> CompletableFuture<T> notImplemented(String body) {
CompletableFuture<T> result = new CompletableFuture<>();
result.completeExceptionally(
new InvokeResponseException(HttpURLConnection.HTTP_NOT_IMPLEMENTED, body)
);
result.completeExceptionally(new InvokeResponseException(HttpURLConnection.HTTP_NOT_IMPLEMENTED, body));
return result;
}

Просмотреть файл

@ -270,6 +270,15 @@ public final class TeamsInfo {
);
}
/**
* Gets the information for the given meeting id.
* @param turnContext Turn context.
* @return Meeting Details.
*/
public static CompletableFuture<MeetingInfo> getMeetingInfo(TurnContext turnContext) {
return getMeetingInfo(turnContext, null);
}
/**
* Gets the information for the given meeting id.
* @param turnContext Turn context.

Просмотреть файл

@ -24,10 +24,13 @@ import com.microsoft.bot.schema.ConversationParameters;
import com.microsoft.bot.schema.ConversationReference;
import com.microsoft.bot.schema.ConversationResourceResponse;
import com.microsoft.bot.schema.ResourceResponse;
import com.microsoft.bot.schema.Serialization;
import com.microsoft.bot.schema.teams.AppBasedLinkQuery;
import com.microsoft.bot.schema.teams.ChannelInfo;
import com.microsoft.bot.schema.teams.FileConsentCardResponse;
import com.microsoft.bot.schema.teams.FileUploadInfo;
import com.microsoft.bot.schema.teams.MeetingEndEventDetails;
import com.microsoft.bot.schema.teams.MeetingStartEventDetails;
import com.microsoft.bot.schema.teams.MessagingExtensionAction;
import com.microsoft.bot.schema.teams.MessagingExtensionActionResponse;
import com.microsoft.bot.schema.teams.MessagingExtensionQuery;
@ -39,6 +42,7 @@ import com.microsoft.bot.schema.teams.TaskModuleResponse;
import com.microsoft.bot.schema.teams.TeamInfo;
import com.microsoft.bot.schema.teams.TeamsChannelAccount;
import com.microsoft.bot.schema.teams.TeamsChannelData;
import java.io.IOException;
import org.apache.commons.lang3.NotImplementedException;
import org.junit.Assert;
import org.junit.Test;
@ -857,6 +861,80 @@ public class TeamsActivityHandlerTests {
);
}
@Test
public void TestOnEventActivity() {
// Arrange
Activity activity = new Activity(ActivityTypes.EVENT);
activity.setChannelId(Channels.DIRECTLINE);
TurnContext turnContext = new TurnContextImpl(new SimpleAdapter(), activity);
// Act
TestActivityHandler bot = new TestActivityHandler();
bot.onTurn(turnContext).join();
// Assert
Assert.assertEquals(1, bot.record.size());
Assert.assertEquals("onEventActivity", bot.record.get(0));
}
@Test
public void TestMeetingStartEvent() throws IOException {
// Arrange
Activity activity = new Activity(ActivityTypes.EVENT);
activity.setChannelId(Channels.MSTEAMS);
activity.setName("application/vnd.microsoft.meetingStart");
activity.setValue(Serialization.jsonToTree("{\"StartTime\": \"2021-06-05T00:01:02.0Z\"}"));
AtomicReference<List<Activity>> activitiesToSend = new AtomicReference<>();
TurnContext turnContext = new TurnContextImpl(
new SimpleAdapter(activitiesToSend::set),
activity
);
// Act
TestActivityHandler bot = new TestActivityHandler();
bot.onTurn(turnContext).join();
// Assert
Assert.assertEquals(2, bot.record.size());
Assert.assertEquals("onEventActivity", bot.record.get(0));
Assert.assertEquals("onTeamsMeetingStart", bot.record.get(1));
Assert.assertNotNull(activitiesToSend.get());
Assert.assertEquals(1, activitiesToSend.get().size());
Assert.assertTrue(activitiesToSend.get().get(0).getText().contains("00:01:02"));
}
@Test
public void TestMeetingEndEvent() throws IOException {
// Arrange
Activity activity = new Activity(ActivityTypes.EVENT);
activity.setChannelId(Channels.MSTEAMS);
activity.setName("application/vnd.microsoft.meetingEnd");
activity.setValue(Serialization.jsonToTree("{\"EndTime\": \"2021-06-05T01:02:03.0Z\"}"));
AtomicReference<List<Activity>> activitiesToSend = new AtomicReference<>();
TurnContext turnContext = new TurnContextImpl(
new SimpleAdapter(activitiesToSend::set),
activity
);
// Act
TestActivityHandler bot = new TestActivityHandler();
bot.onTurn(turnContext).join();
// Assert
Assert.assertEquals(2, bot.record.size());
Assert.assertEquals("onEventActivity", bot.record.get(0));
Assert.assertEquals("onTeamsMeetingEnd", bot.record.get(1));
Assert.assertNotNull(activitiesToSend.get());
Assert.assertEquals(1, activitiesToSend.get().size());
Assert.assertTrue(activitiesToSend.get().get(0).getText().contains("1:02:03"));
}
private static class NotImplementedAdapter extends BotAdapter {
@Override
@ -1209,6 +1287,34 @@ public class TeamsActivityHandlerTests {
record.add("onTeamsTeamUnarchived");
return super.onTeamsTeamUnarchived(channelInfo, teamInfo, turnContext);
}
@Override
protected CompletableFuture<Void> onEventActivity(
TurnContext turnContext
) {
record.add("onEventActivity");
return super.onEventActivity(turnContext);
}
@Override
protected CompletableFuture<Void> onTeamsMeetingStart(
MeetingStartEventDetails meeting,
TurnContext turnContext
) {
record.add("onTeamsMeetingStart");
return turnContext.sendActivity(meeting.getStartTime().toString())
.thenCompose(resourceResponse -> super.onTeamsMeetingStart(meeting, turnContext));
}
@Override
protected CompletableFuture<Void> onTeamsMeetingEnd(
MeetingEndEventDetails meeting,
TurnContext turnContext
) {
record.add("onTeamsMeetingEnd");
return turnContext.sendActivity(meeting.getEndTime().toString())
.thenCompose(resourceResponse -> super.onTeamsMeetingEnd(meeting, turnContext));
}
}
private static ConnectorClient getConnectorClient(String baseUri, AppCredentials credentials) {

Просмотреть файл

@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.TypeBindings;
@ -143,7 +144,8 @@ public class JacksonAdapter implements SerializerAdapter<ObjectMapper> {
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())

Просмотреть файл

@ -6,6 +6,7 @@ package com.microsoft.bot.schema;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
@ -27,6 +28,7 @@ public final class Serialization {
static {
objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
objectMapper.findAndRegisterModules();
// NOTE: Undetermined if we should accommodate non-public fields. The normal

Просмотреть файл

@ -4,59 +4,27 @@
package com.microsoft.bot.schema.teams;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.OffsetDateTime;
/**
* Specific details of a Teams meeting.
*/
public class MeetingDetails {
@JsonProperty(value = "id")
private String id;
public class MeetingDetails extends MeetingDetailsBase {
@JsonProperty(value = "msGraphResourceId")
private String msGraphResourceId;
@JsonProperty(value = "scheduledStartTime")
private String scheduledStartTime;
private OffsetDateTime scheduledStartTime;
@JsonProperty(value = "scheduledEndTime")
private String scheduledEndTime;
@JsonProperty(value = "joinUrl")
private String joinUrl;
@JsonProperty(value = "title")
private String title;
private OffsetDateTime scheduledEndTime;
@JsonProperty(value = "type")
private String type;
/**
* Initializes a new instance.
*/
public MeetingDetails() {
}
/**
* Gets the meeting's Id, encoded as a BASE64 String.
*
* @return The meeting's Id, encoded as a BASE64 String.
*/
public String getId() {
return id;
}
/**
* Sets the meeting's Id, encoded as a BASE64 String.
*
* @param withId The meeting's Id, encoded as a BASE64 String.
*/
public void setId(String withId) {
id = withId;
}
/**
* Gets the MsGraphResourceId, used specifically for MS Graph API calls.
*
*
* @return The MsGraphResourceId, used specifically for MS Graph API calls.
*/
public String getMsGraphResourceId() {
@ -65,7 +33,7 @@ public class MeetingDetails {
/**
* Sets the MsGraphResourceId, used specifically for MS Graph API calls.
*
*
* @param withMsGraphResourceId The MsGraphResourceId, used specifically for MS
* Graph API calls.
*/
@ -75,79 +43,43 @@ public class MeetingDetails {
/**
* Gets the meeting's scheduled start time, in UTC.
*
*
* @return The meeting's scheduled start time, in UTC.
*/
public String getScheduledStartTime() {
public OffsetDateTime getScheduledStartTime() {
return scheduledStartTime;
}
/**
* Sets the meeting's scheduled start time, in UTC.
*
*
* @param withScheduledStartTime The meeting's scheduled start time, in UTC.
*/
public void setScheduledStartTime(String withScheduledStartTime) {
public void setScheduledStartTime(OffsetDateTime withScheduledStartTime) {
scheduledStartTime = withScheduledStartTime;
}
/**
* Gets the meeting's scheduled end time, in UTC.
*
*
* @return The meeting's scheduled end time, in UTC.
*/
public String getScheduledEndTime() {
public OffsetDateTime getScheduledEndTime() {
return scheduledEndTime;
}
/**
* Sets the meeting's scheduled end time, in UTC.
*
*
* @param withScheduledEndTime The meeting's scheduled end time, in UTC.
*/
public void setScheduledEndTime(String withScheduledEndTime) {
public void setScheduledEndTime(OffsetDateTime withScheduledEndTime) {
scheduledEndTime = withScheduledEndTime;
}
/**
* Gets the URL used to join the meeting.
*
* @return The URL used to join the meeting.
*/
public String getJoinUrl() {
return joinUrl;
}
/**
* Sets the URL used to join the meeting.
*
* @param withJoinUrl The URL used to join the meeting.
*/
public void setJoinUrl(String withJoinUrl) {
joinUrl = withJoinUrl;
}
/**
* Gets the title of the meeting.
*
* @return The title of the meeting.
*/
public String getTitle() {
return title;
}
/**
* Sets the title of the meeting.
*
* @param withTitle The title of the meeting.
*/
public void setTitle(String withTitle) {
title = withTitle;
}
/**
* Gets the meeting's type.
*
*
* @return The meeting's type.
*/
public String getType() {
@ -156,7 +88,7 @@ public class MeetingDetails {
/**
* Sets the meeting's type.
*
*
* @param withType The meeting's type.
*/
public void setType(String withType) {

Просмотреть файл

@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.schema.teams;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Specific details of a Teams meeting.
*/
public class MeetingDetailsBase {
@JsonProperty(value = "id")
private String id;
@JsonProperty(value = "joinUrl")
private String joinUrl;
@JsonProperty(value = "title")
private String title;
/**
* Initializes a new instance.
*/
public MeetingDetailsBase() {
}
/**
* Gets the meeting's Id, encoded as a BASE64 String.
*
* @return The meeting's Id, encoded as a BASE64 String.
*/
public String getId() {
return id;
}
/**
* Sets the meeting's Id, encoded as a BASE64 String.
*
* @param withId The meeting's Id, encoded as a BASE64 String.
*/
public void setId(String withId) {
id = withId;
}
/**
* Gets the URL used to join the meeting.
*
* @return The URL used to join the meeting.
*/
public String getJoinUrl() {
return joinUrl;
}
/**
* Sets the URL used to join the meeting.
*
* @param withJoinUrl The URL used to join the meeting.
*/
public void setJoinUrl(String withJoinUrl) {
joinUrl = withJoinUrl;
}
/**
* Gets the title of the meeting.
*
* @return The title of the meeting.
*/
public String getTitle() {
return title;
}
/**
* Sets the title of the meeting.
*
* @param withTitle The title of the meeting.
*/
public void setTitle(String withTitle) {
title = withTitle;
}
}

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.schema.teams;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.OffsetDateTime;
/**
* Specific details of a Teams meeting end event.
*/
public class MeetingEndEventDetails extends MeetingEventDetails {
@JsonProperty(value = "EndTime")
private OffsetDateTime endTime;
/**
* Gets the meeting's end time, in UTC.
* @return The meeting's end time, in UTC.
*/
public OffsetDateTime getEndTime() {
return endTime;
}
/**
* Sets the meeting's end time, in UTC.
* @param withEndTime The meeting's end time, in UTC.
*/
public void setEndTime(OffsetDateTime withEndTime) {
endTime = withEndTime;
}
}

Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.schema.teams;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Specific details of a Teams meeting.
*/
public class MeetingEventDetails extends MeetingDetailsBase {
@JsonProperty(value = "MeetingType")
private String meetingType;
/**
* Gets the meeting's type.
* @return The meeting's type.
*/
public String getMeetingType() {
return meetingType;
}
/**
* Sets the meeting's type.
* @param withMeetingType The meeting's type.
*/
public void setMeetingType(String withMeetingType) {
meetingType = withMeetingType;
}
}

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.schema.teams;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.OffsetDateTime;
/**
* Specific details of a Teams meeting start event.
*/
public class MeetingStartEventDetails extends MeetingEventDetails {
@JsonProperty(value = "StartTime")
private OffsetDateTime startTime;
/**
* Gets the meeting's start time, in UTC.
* @return The meeting's start time, in UTC.
*/
public OffsetDateTime getStartTime() {
return startTime;
}
/**
* Sets the meeting's start time, in UTC.
* @param withStartTime The meeting's start time, in UTC.
*/
public void setStartTime(OffsetDateTime withStartTime) {
startTime = withStartTime;
}
}

Просмотреть файл

@ -177,13 +177,13 @@ public class SerializationTest {
ChannelAccount account1 = new ChannelAccount();
account1.setId("ChannelAccount_Id_1");
account1.setName("ChannelAccount_Name_1");
account1.setProperties("Name", JsonNodeFactory.instance.objectNode().put("Name", "Value"));
account1.setProperties("TestName", JsonNodeFactory.instance.objectNode().put("Name", "Value"));
account1.setRole(RoleTypes.USER);
ChannelAccount account2 = new ChannelAccount();
account2.setId("ChannelAccount_Id_2");
account2.setName("ChannelAccount_Name_2");
account2.setProperties("Name", JsonNodeFactory.instance.objectNode().put("Name", "Value"));
account2.setProperties("TestName", JsonNodeFactory.instance.objectNode().put("Name", "Value"));
account2.setRole(RoleTypes.USER);
ConversationAccount conversationAccount = new ConversationAccount();
@ -191,7 +191,7 @@ public class SerializationTest {
conversationAccount.setId("123");
conversationAccount.setIsGroup(true);
conversationAccount.setName("Name");
conversationAccount.setProperties("Name", JsonNodeFactory.instance.objectNode().put("Name", "Value"));
conversationAccount.setProperties("TestName", JsonNodeFactory.instance.objectNode().put("Name", "Value"));
Activity activity = new Activity();
activity.setId("123");