diff --git a/packages/Teams/dotnet/Actions/GetMeetingInfo.cs b/packages/Teams/dotnet/Actions/GetMeetingInfo.cs new file mode 100644 index 0000000000..a95eaa52a5 --- /dev/null +++ b/packages/Teams/dotnet/Actions/GetMeetingInfo.cs @@ -0,0 +1,104 @@ +// Licensed under the MIT License. +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using AdaptiveExpressions.Properties; +using Microsoft.Bot.Builder.Dialogs; +using Microsoft.Bot.Builder.Teams; +using Microsoft.Bot.Connector; +using Newtonsoft.Json; + +namespace Microsoft.Bot.Components.Teams.Actions +{ + /// + /// Calls TeamsInfo.GetMeetingInfoAsync and sets the result to a memory property. + /// + public class GetMeetingInfo : Dialog + { + /// + /// Class identifier. + /// + [JsonProperty("$kind")] + public const string Kind = "Teams.GetMeetingInfo"; + + /// + /// Initializes a new instance of the class. + /// + /// Optional, source file full path. + /// Optional, line number in source file. + [JsonConstructor] + public GetMeetingInfo([CallerFilePath] string callerPath = "", [CallerLineNumber] int callerLine = 0) + : base() + { + RegisterSourceLocation(callerPath, callerLine); + } + + /// + /// Gets or sets an optional expression which if is true will disable this action. + /// + /// + /// "user.age > 18". + /// + /// + /// A boolean expression. + /// + [JsonProperty("disabled")] + public BoolExpression Disabled { get; set; } + + /// + /// Gets or sets property path to put the value in. + /// + /// + /// Property path to put the value in. + /// + [JsonProperty("property")] + public StringExpression Property { get; set; } + + /// + /// Gets or sets the expression to get the value to use for meeting id. + /// + /// + /// The expression to get the value to use for meeting id. Default value is turn.activity.channelData.meeting.id. + /// + [JsonProperty("meetingId")] + public StringExpression MeetingId { get; set; } = "=turn.activity.channelData.meeting.id"; + + /// + public override async Task BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (options is CancellationToken) + { + throw new ArgumentException($"{nameof(options)} cannot be a cancellation token"); + } + + if (Disabled != null && Disabled.GetValue(dc.State)) + { + return await dc.EndDialogAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + } + + if (dc.Context.Activity.ChannelId != Channels.Msteams) + { + throw new InvalidOperationException($"{Kind} works only on the Teams channel."); + } + + string meetingId = MeetingId.GetValueOrNull(dc.State); + var result = await TeamsInfo.GetMeetingInfoAsync(dc.Context, meetingId, cancellationToken: cancellationToken).ConfigureAwait(false); + + if (Property != null) + { + dc.State.SetValue(Property.GetValue(dc.State), result); + } + + return await dc.EndDialogAsync(result, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + protected override string OnComputeId() + { + return $"{GetType().Name}[{MeetingId?.ToString() ?? string.Empty},{Property?.ToString() ?? string.Empty}]"; + } + } +} diff --git a/packages/Teams/dotnet/Schemas/Actions/Teams.GetMeetingInfo.schema b/packages/Teams/dotnet/Schemas/Actions/Teams.GetMeetingInfo.schema new file mode 100644 index 0000000000..39d3fedb45 --- /dev/null +++ b/packages/Teams/dotnet/Schemas/Actions/Teams.GetMeetingInfo.schema @@ -0,0 +1,38 @@ +{ + "$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema", + "$role": "implements(Microsoft.IDialog)", + "title": "Get meeting information", + "description": "Get teams meeting information.", + "type": "object", + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Optional id for the dialog" + }, + "property": { + "$ref": "schema:#/definitions/stringExpression", + "title": "Property", + "description": "Property (named location to store information).", + "examples": [ + "dialog.meetingInfo" + ] + }, + "meetingId": { + "$ref": "schema:#/definitions/stringExpression", + "title": "Meeting id", + "description": "Meeting Id or expression to a meetingId to use to get the meeting information. Default value is the current turn.activity.channelData.meeting.id.", + "examples": [ + "=turn.activity.channelData.meeting.id" + ] + }, + "disabled": { + "$ref": "schema:#/definitions/booleanExpression", + "title": "Disabled", + "description": "Optional condition which if true will disable this action.", + "examples": [ + "=user.age > 3" + ] + } + } +} \ No newline at end of file diff --git a/packages/Teams/dotnet/Schemas/Actions/Teams.GetMeetingInfo.uischema b/packages/Teams/dotnet/Schemas/Actions/Teams.GetMeetingInfo.uischema new file mode 100644 index 0000000000..9f79a1de99 --- /dev/null +++ b/packages/Teams/dotnet/Schemas/Actions/Teams.GetMeetingInfo.uischema @@ -0,0 +1,6 @@ +{ + "$schema": "https://schemas.botframework.com/schemas/ui/v1.0/ui.schema", + "menu": { + "submenu": ["Microsoft Teams", "Get Teams Info"] + } +} \ No newline at end of file diff --git a/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingEnd.schema b/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingEnd.schema new file mode 100644 index 0000000000..e339419fb0 --- /dev/null +++ b/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingEnd.schema @@ -0,0 +1,9 @@ +{ + "$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema", + "$role": [ "implements(Microsoft.ITrigger)", "extends(Microsoft.OnCondition)" ], + "title": "On meeting end", + "description": "Actions triggered when a Teams Meeting is ended", + "type": "object", + "required": [ + ] +} diff --git a/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingEnd.uischema b/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingEnd.uischema new file mode 100644 index 0000000000..98cfcc870a --- /dev/null +++ b/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingEnd.uischema @@ -0,0 +1,7 @@ +{ + "$schema": "https://schemas.botframework.com/schemas/ui/v1.0/ui.schema", + "trigger": { + "submenu": "Microsoft Teams", + "label": "On meeting end" + } +} diff --git a/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingStart.schema b/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingStart.schema new file mode 100644 index 0000000000..e30ddfff41 --- /dev/null +++ b/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingStart.schema @@ -0,0 +1,9 @@ +{ + "$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema", + "$role": [ "implements(Microsoft.ITrigger)", "extends(Microsoft.OnCondition)" ], + "title": "On meeting start", + "description": "Actions triggered when a Teams Meeting is started", + "type": "object", + "required": [ + ] +} diff --git a/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingStart.uischema b/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingStart.uischema new file mode 100644 index 0000000000..440af4b889 --- /dev/null +++ b/packages/Teams/dotnet/Schemas/TriggerConditions/Teams.OnMeetingStart.uischema @@ -0,0 +1,7 @@ +{ + "$schema": "https://schemas.botframework.com/schemas/ui/v1.0/ui.schema", + "trigger": { + "submenu": "Microsoft Teams", + "label": "On meeting start" + } +} diff --git a/packages/Teams/dotnet/TeamsBotComponent.cs b/packages/Teams/dotnet/TeamsBotComponent.cs index cf2cd3cc6e..1eb4cef0aa 100644 --- a/packages/Teams/dotnet/TeamsBotComponent.cs +++ b/packages/Teams/dotnet/TeamsBotComponent.cs @@ -42,6 +42,8 @@ public override void ConfigureServices(IServiceCollection services, IConfigurati services.AddSingleton(sp => new DeclarativeType(OnTeamsChannelRenamed.Kind)); services.AddSingleton(sp => new DeclarativeType(OnTeamsChannelRestored.Kind)); services.AddSingleton(sp => new DeclarativeType(OnTeamsFileConsent.Kind)); + services.AddSingleton(sp => new DeclarativeType(OnTeamsMeetingStart.Kind)); + services.AddSingleton(sp => new DeclarativeType(OnTeamsMeetingEnd.Kind)); services.AddSingleton(sp => new DeclarativeType(OnTeamsMECardButtonClicked.Kind)); services.AddSingleton(sp => new DeclarativeType(OnTeamsMEConfigQuerySettingUrl.Kind)); @@ -68,6 +70,7 @@ public override void ConfigureServices(IServiceCollection services, IConfigurati // Actions + services.AddSingleton(sp => new DeclarativeType(GetMeetingInfo.Kind)); services.AddSingleton(sp => new DeclarativeType(GetMeetingParticipant.Kind)); services.AddSingleton(sp => new DeclarativeType(GetMember.Kind)); services.AddSingleton(sp => new DeclarativeType(GetPagedMembers.Kind)); diff --git a/packages/Teams/dotnet/TriggerConditions/OnTeamsMeetingEnd.cs b/packages/Teams/dotnet/TriggerConditions/OnTeamsMeetingEnd.cs new file mode 100644 index 0000000000..c99285ab01 --- /dev/null +++ b/packages/Teams/dotnet/TriggerConditions/OnTeamsMeetingEnd.cs @@ -0,0 +1,37 @@ +// Licensed under the MIT License. +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using AdaptiveExpressions; +using Microsoft.Bot.Builder.Dialogs; +using Microsoft.Bot.Builder.Dialogs.Adaptive.Conditions; +using Microsoft.Bot.Connector; +using Newtonsoft.Json; + +namespace Microsoft.Bot.Components.Teams.Conditions +{ + /// + /// Actions triggered when a Teams Meeting End event is received. + /// + /// + /// turn.activity.value has meeting data. + /// + public class OnTeamsMeetingEnd : OnEventActivity + { + [JsonProperty("$kind")] + public new const string Kind = "Teams.OnMeetingEnd"; + + [JsonConstructor] + public OnTeamsMeetingEnd(List actions = null, string condition = null, [CallerFilePath] string callerPath = "", [CallerLineNumber] int callerLine = 0) + : base(actions: actions, condition: condition, callerPath: callerPath, callerLine: callerLine) + { + } + + /// + protected override Expression CreateExpression() + { + return Expression.AndExpression(Expression.Parse($"{TurnPath.Activity}.channelId == '{Channels.Msteams}' && {TurnPath.Activity}.name == 'application/vnd.microsoft.meetingEnd'"), base.CreateExpression()); + } + } +} diff --git a/packages/Teams/dotnet/TriggerConditions/OnTeamsMeetingStart.cs b/packages/Teams/dotnet/TriggerConditions/OnTeamsMeetingStart.cs new file mode 100644 index 0000000000..921f0a58d4 --- /dev/null +++ b/packages/Teams/dotnet/TriggerConditions/OnTeamsMeetingStart.cs @@ -0,0 +1,37 @@ +// Licensed under the MIT License. +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using AdaptiveExpressions; +using Microsoft.Bot.Builder.Dialogs; +using Microsoft.Bot.Builder.Dialogs.Adaptive.Conditions; +using Microsoft.Bot.Connector; +using Newtonsoft.Json; + +namespace Microsoft.Bot.Components.Teams.Conditions +{ + /// + /// Actions triggered when a Teams Meeting Start event is received. + /// + /// + /// turn.activity.value has meeting data. + /// + public class OnTeamsMeetingStart : OnEventActivity + { + [JsonProperty("$kind")] + public new const string Kind = "Teams.OnMeetingStart"; + + [JsonConstructor] + public OnTeamsMeetingStart(List actions = null, string condition = null, [CallerFilePath] string callerPath = "", [CallerLineNumber] int callerLine = 0) + : base(actions: actions, condition: condition, callerPath: callerPath, callerLine: callerLine) + { + } + + /// + protected override Expression CreateExpression() + { + return Expression.AndExpression(Expression.Parse($"{TurnPath.Activity}.channelId == '{Channels.Msteams}' && {TurnPath.Activity}.name == 'application/vnd.microsoft.meetingStart'"), base.CreateExpression()); + } + } +}