diff --git a/.github/workflows/workflow.deploy.yml b/.github/workflows/deploy.yml similarity index 100% rename from .github/workflows/workflow.deploy.yml rename to .github/workflows/deploy.yml diff --git a/.github/workflows/workflow.release.yml b/.github/workflows/release.yml similarity index 100% rename from .github/workflows/workflow.release.yml rename to .github/workflows/release.yml diff --git a/.github/workflows/workflow.yml b/.github/workflows/trigger.yml similarity index 91% rename from .github/workflows/workflow.yml rename to .github/workflows/trigger.yml index 4ed4f7b..6a46a5f 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/trigger.yml @@ -9,7 +9,7 @@ on: jobs: deploy-development: if: github.ref == 'refs/heads/develop' - uses: ./.github/workflows/workflow.deploy.yml + uses: ./.github/workflows/deploy.yml secrets: APP_CLIENT_ID: ${{secrets.APP_CLIENT_ID}} APP_DOMAIN_NAME: ${{secrets.APP_DOMAIN_NAME}} @@ -23,7 +23,7 @@ jobs: environment: development deploy-production: if: github.ref == 'refs/heads/main' - uses: ./.github/workflows/workflow.deploy.yml + uses: ./.github/workflows/deploy.yml secrets: APP_CLIENT_ID: ${{secrets.APP_CLIENT_ID}} APP_DOMAIN_NAME: ${{secrets.APP_DOMAIN_NAME}} @@ -37,7 +37,7 @@ jobs: environment: production create-release: if: contains(github.ref, 'refs/tags/') - uses: ./.github/workflows/workflow.release.yml + uses: ./.github/workflows/release.yml secrets: APP_CLIENT_ID: ${{secrets.APP_CLIENT_ID}} APP_DOMAIN_NAME: ${{secrets.APP_DOMAIN_NAME}} diff --git a/source/server/Karamem0.Commistant.Bot/Bots/ActivityBot.cs b/source/server/Karamem0.Commistant.Bot/Bots/ActivityBot.cs index f355dd6..22f3e0c 100644 --- a/source/server/Karamem0.Commistant.Bot/Bots/ActivityBot.cs +++ b/source/server/Karamem0.Commistant.Bot/Bots/ActivityBot.cs @@ -104,27 +104,15 @@ protected override async Task OnMessageActivityAsync(ITurnContext>(async () => + var arguments = await this.openAIService.GetArgumentsAsync(command, cancellationToken: cancellationToken); + var result = arguments?.Type switch { - var message = await this.openAIService.ChatCompletionAsync(command, cancellationToken: cancellationToken); - if (message.FinishReason == ChatFinishReason.ToolCalls) - { - var arguments = message.ToolCalls.Select(item => item.FunctionArguments).First(); - var content = JsonSerializer.Deserialize(arguments?.ToString()); - return content?.Type switch - { - "会議開始後" => await dc.BeginDialogAsync(nameof(StartMeetingDialog), content, cancellationToken: cancellationToken), - "会議終了前" => await dc.BeginDialogAsync(nameof(EndMeetingDialog), content, cancellationToken: cancellationToken), - "会議中" => await dc.BeginDialogAsync(nameof(InMeetingDialog), content, cancellationToken: cancellationToken), - "初期化" => await dc.BeginDialogAsync(nameof(ResetDialog), cancellationToken: cancellationToken), - _ => null, - }; - } - else - { - return null; - } - })(); + "会議開始後" => await dc.BeginDialogAsync(nameof(StartMeetingDialog), arguments, cancellationToken: cancellationToken), + "会議終了前" => await dc.BeginDialogAsync(nameof(EndMeetingDialog), arguments, cancellationToken: cancellationToken), + "会議中" => await dc.BeginDialogAsync(nameof(InMeetingDialog), arguments, cancellationToken: cancellationToken), + "初期化" => await dc.BeginDialogAsync(nameof(ResetDialog), cancellationToken: cancellationToken), + _ => null, + }; if (result is null) { _ = await turnContext.SendActivityAsync( diff --git a/source/server/Karamem0.Commistant.Bot/Dialogs/EndMeetingDialog.cs b/source/server/Karamem0.Commistant.Bot/Dialogs/EndMeetingDialog.cs index c339b25..7c1c592 100644 --- a/source/server/Karamem0.Commistant.Bot/Dialogs/EndMeetingDialog.cs +++ b/source/server/Karamem0.Commistant.Bot/Dialogs/EndMeetingDialog.cs @@ -105,6 +105,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste new AdaptiveTextInput() { Id = "Message", + IsMultiline = true, Label = "メッセージ", Placeholder = "会議後に表示されるメッセージ", Style = AdaptiveTextInputStyle.Text, @@ -172,18 +173,12 @@ private async Task AfterConrifmAsync(WaterfallStepContext step property.EndMeetingMessage = value.Value("Message", null); property.EndMeetingUrl = value.Value("Url", null); this.logger.SettingsUpdated(stepContext.Context.Activity); - _ = await stepContext.Context.SendActivityAsync( - "設定を変更しました。", - cancellationToken: cancellationToken - ); + _ = await stepContext.Context.SendSettingsUpdatedAsync(cancellationToken); } if (value.Value("Button") == "Cancel") { this.logger.SettingsCancelled(stepContext.Context.Activity); - _ = await stepContext.Context.SendActivityAsync( - "キャンセルしました。設定は変更されていません。", - cancellationToken: cancellationToken - ); + _ = await stepContext.Context.SendSettingsCancelledAsync(cancellationToken); } if (stepContext.Context.Activity.ReplyToId is not null) { @@ -191,46 +186,116 @@ private async Task AfterConrifmAsync(WaterfallStepContext step { Body = [ - new AdaptiveFactSet() + new AdaptiveColumnSet() { - Facts = + Columns = [ - new() + new AdaptiveColumn() { - Title = "スケジュール", - Value = new Func(() => - property.EndMeetingSchedule switch + Items = + [ + new AdaptiveTextBlock() { - -1 => "なし", - 0 => "予定時刻", - _ => $"{property.EndMeetingSchedule} 分後" + Text = "スケジュール", + Weight = AdaptiveTextWeight.Bolder } - )() + ], + Width = "90px" }, - new() + new AdaptiveColumn() { - Title = "メッセージ", - Value = $"{property.EndMeetingMessage}" + Items = + [ + new AdaptiveTextBlock() + { + Text = property.EndMeetingSchedule switch + { + -1 => "なし", + 0 => "予定時刻", + _ => $"{property.EndMeetingSchedule} 分前" + } + } + ], + Width = "stretch" + } + ], + }, + new AdaptiveColumnSet() + { + Columns = + [ + new AdaptiveColumn() + { + Items = + [ + new AdaptiveTextBlock() + { + Text = "メッセージ", + Weight = AdaptiveTextWeight.Bolder + } + ], + Width = "90px" }, - new() + new AdaptiveColumn() { - Title = "URL", - Value = $"{property.EndMeetingUrl}" + Items = + [ + new AdaptiveTextBlock() + { + Text = $"{property.EndMeetingMessage}", + Wrap = true + } + ], + Width = "stretch" + } + ] + }, + new AdaptiveColumnSet() + { + Columns = + [ + new AdaptiveColumn() + { + Items = + [ + new AdaptiveTextBlock() + { + Text = "URL", + Weight = AdaptiveTextWeight.Bolder + } + ], + Width = "90px" + }, + new AdaptiveColumn() + { + Items = + [ + new AdaptiveTextBlock() + { + Text = $"{property.EndMeetingUrl}" + } + ], + Width = "stretch" } ] } ] }; - if (property.EndMeetingUrl is not null) + if (Uri.TryCreate(property.EndMeetingUrl, UriKind.Absolute, out var url)) { - var bytes = await this.qrCodeService.CreateAsync(property.EndMeetingUrl, cancellationToken); + var bytes = await this.qrCodeService.CreateAsync(url.ToString(), cancellationToken); var base64 = Convert.ToBase64String(bytes); card.Body.Add(new AdaptiveImage() { - AltText = property.EndMeetingUrl, - Size = AdaptiveImageSize.Stretch, + AltText = url.ToString(), + Size = AdaptiveImageSize.Large, Url = new Uri($"data:image/png;base64,{base64}") }); + card.Actions.Add(new AdaptiveOpenUrlAction() + { + Title = "URL を開く", + Url = url, + }); } var activity = MessageFactory.Attachment(new Attachment() { @@ -238,10 +303,7 @@ private async Task AfterConrifmAsync(WaterfallStepContext step Content = JsonConvert.DeserializeObject(card.ToJson()) }); activity.Id = stepContext.Context.Activity.ReplyToId; - _ = await stepContext.Context.UpdateActivityAsync( - activity, - cancellationToken: cancellationToken - ); + _ = await stepContext.Context.UpdateActivityAsync(activity, cancellationToken); } return await stepContext.EndDialogAsync(cancellationToken: cancellationToken); } diff --git a/source/server/Karamem0.Commistant.Bot/Dialogs/InMeetingDialog.cs b/source/server/Karamem0.Commistant.Bot/Dialogs/InMeetingDialog.cs index de5e7ae..eb167d2 100644 --- a/source/server/Karamem0.Commistant.Bot/Dialogs/InMeetingDialog.cs +++ b/source/server/Karamem0.Commistant.Bot/Dialogs/InMeetingDialog.cs @@ -110,6 +110,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste new AdaptiveTextInput() { Id = "Message", + IsMultiline = true, Label = "メッセージ", Placeholder = "会議中に表示されるメッセージ", Style = AdaptiveTextInputStyle.Text, @@ -177,10 +178,12 @@ private async Task AfterConrifmAsync(WaterfallStepContext step property.InMeetingMessage = value.Value("Message", null); property.InMeetingUrl = value.Value("Url", null); this.logger.SettingsUpdated(stepContext.Context.Activity); - _ = await stepContext.Context.SendActivityAsync( - "設定を変更しました。", - cancellationToken: cancellationToken - ); + _ = await stepContext.Context.SendSettingsUpdatedAsync(cancellationToken); + } + if (value.Value("Button") == "Cancel") + { + this.logger.SettingsCancelled(stepContext.Context.Activity); + _ = await stepContext.Context.SendSettingsCancelledAsync(cancellationToken); } if (stepContext.Context.Activity.ReplyToId is not null) { @@ -188,45 +191,116 @@ private async Task AfterConrifmAsync(WaterfallStepContext step { Body = [ - new AdaptiveFactSet() + new AdaptiveColumnSet() { - Facts = + Columns = [ - new() + new AdaptiveColumn() { - Title = "スケジュール", - Value = new Func(() => - property.InMeetingSchedule switch + Items = + [ + new AdaptiveTextBlock() { - -1 => "なし", - _ => $"{property.InMeetingSchedule} 分" + Text = "スケジュール", + Weight = AdaptiveTextWeight.Bolder } - )() + ], + Width = "90px" }, - new() + new AdaptiveColumn() { - Title = "メッセージ", - Value = $"{property.InMeetingMessage}" + Items = + [ + new AdaptiveTextBlock() + { + Text = property.InMeetingSchedule switch + { + -1 => "なし", + 0 => "予定時刻", + _ => $"{property.InMeetingSchedule} 分ごと" + } + } + ], + Width = "stretch" + } + ], + }, + new AdaptiveColumnSet() + { + Columns = + [ + new AdaptiveColumn() + { + Items = + [ + new AdaptiveTextBlock() + { + Text = "メッセージ", + Weight = AdaptiveTextWeight.Bolder + } + ], + Width = "90px" }, - new() + new AdaptiveColumn() { - Title = "URL", - Value = $"{property.InMeetingUrl}" + Items = + [ + new AdaptiveTextBlock() + { + Text = $"{property.InMeetingMessage}", + Wrap = true + } + ], + Width = "stretch" + } + ] + }, + new AdaptiveColumnSet() + { + Columns = + [ + new AdaptiveColumn() + { + Items = + [ + new AdaptiveTextBlock() + { + Text = "URL", + Weight = AdaptiveTextWeight.Bolder + } + ], + Width = "90px" + }, + new AdaptiveColumn() + { + Items = + [ + new AdaptiveTextBlock() + { + Text = $"{property.InMeetingUrl}" + } + ], + Width = "stretch" } ] } ] }; - if (property.InMeetingUrl is not null) + if (Uri.TryCreate(property.InMeetingUrl, UriKind.Absolute, out var url)) { - var bytes = await this.qrCodeService.CreateAsync(property.InMeetingUrl, cancellationToken); + var bytes = await this.qrCodeService.CreateAsync(url.ToString(), cancellationToken); var base64 = Convert.ToBase64String(bytes); card.Body.Add(new AdaptiveImage() { - AltText = property.InMeetingUrl, - Size = AdaptiveImageSize.Stretch, + AltText = url.ToString(), + Size = AdaptiveImageSize.Large, Url = new Uri($"data:image/png;base64,{base64}") }); + card.Actions.Add(new AdaptiveOpenUrlAction() + { + Title = "URL を開く", + Url = url, + }); } var activity = MessageFactory.Attachment(new Attachment() { @@ -234,10 +308,7 @@ private async Task AfterConrifmAsync(WaterfallStepContext step Content = JsonConvert.DeserializeObject(card.ToJson()) }); activity.Id = stepContext.Context.Activity.ReplyToId; - _ = await stepContext.Context.UpdateActivityAsync( - activity, - cancellationToken: cancellationToken - ); + _ = await stepContext.Context.UpdateActivityAsync(activity, cancellationToken); } return await stepContext.EndDialogAsync(cancellationToken: cancellationToken); } diff --git a/source/server/Karamem0.Commistant.Bot/Dialogs/ResetDialog.cs b/source/server/Karamem0.Commistant.Bot/Dialogs/ResetDialog.cs index 3d2191d..33a53b5 100644 --- a/source/server/Karamem0.Commistant.Bot/Dialogs/ResetDialog.cs +++ b/source/server/Karamem0.Commistant.Bot/Dialogs/ResetDialog.cs @@ -7,6 +7,7 @@ // using AdaptiveCards; +using Karamem0.Commistant.Extensions; using Karamem0.Commistant.Logging; using Karamem0.Commistant.Models; using Karamem0.Commistant.Validators; @@ -106,20 +107,15 @@ private async Task AfterConrifmAsync(WaterfallStepContext step } if (value.Value("Button") == "Yes") { - this.logger.SettingsReseted(stepContext.Context.Activity); var accessor = this.conversationState.CreateProperty(nameof(ConversationProperty)); await accessor.SetAsync(stepContext.Context, new(), cancellationToken); - _ = await stepContext.Context.SendActivityAsync( - "設定を初期化しました。", - cancellationToken: cancellationToken - ); + this.logger.SettingsReseted(stepContext.Context.Activity); + _ = await stepContext.Context.SendSettingsResetedAsync(cancellationToken); } if (value.Value("Button") == "No") { this.logger.SettingsCancelled(stepContext.Context.Activity); - _ = await stepContext.Context.SendActivityAsync( - "キャンセルしました。設定は変更されていません。", - cancellationToken: cancellationToken); + _ = await stepContext.Context.SendSettingsCancelledAsync(cancellationToken); } if (stepContext.Context.Activity.ReplyToId is not null) { @@ -134,14 +130,12 @@ private async Task AfterConrifmAsync(WaterfallStepContext step new() { Title = "応答", - Value = new Func(() => - value.Value("Button") switch - { - "Yes" => "はい", - "No" => "いいえ", - _ => "" - } - )() + Value = value.Value("Button") switch + { + "Yes" => "はい", + "No" => "いいえ", + _ => "" + } } ] } @@ -153,10 +147,7 @@ private async Task AfterConrifmAsync(WaterfallStepContext step Content = JsonConvert.DeserializeObject(card.ToJson()) }); activity.Id = stepContext.Context.Activity.ReplyToId; - _ = await stepContext.Context.UpdateActivityAsync( - activity, - cancellationToken: cancellationToken - ); + _ = await stepContext.Context.UpdateActivityAsync(activity, cancellationToken); } return await stepContext.EndDialogAsync(cancellationToken: cancellationToken); } diff --git a/source/server/Karamem0.Commistant.Bot/Dialogs/StartMeetingDialog.cs b/source/server/Karamem0.Commistant.Bot/Dialogs/StartMeetingDialog.cs index 5594a77..dcd1f4c 100644 --- a/source/server/Karamem0.Commistant.Bot/Dialogs/StartMeetingDialog.cs +++ b/source/server/Karamem0.Commistant.Bot/Dialogs/StartMeetingDialog.cs @@ -105,6 +105,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste new AdaptiveTextInput() { Id = "Message", + IsMultiline = true, Label = "メッセージ", Placeholder = "開始後に表示されるメッセージ", Style = AdaptiveTextInputStyle.Text, @@ -172,18 +173,12 @@ private async Task AfterConrifmAsync(WaterfallStepContext step property.StartMeetingMessage = value.Value("Message", null); property.StartMeetingUrl = value.Value("Url", null); this.logger.SettingsUpdated(stepContext.Context.Activity); - _ = await stepContext.Context.SendActivityAsync( - "設定を変更しました。", - cancellationToken: cancellationToken - ); + _ = await stepContext.Context.SendSettingsUpdatedAsync(cancellationToken); } if (value.Value("Button") == "Cancel") { this.logger.SettingsCancelled(stepContext.Context.Activity); - _ = await stepContext.Context.SendActivityAsync( - "キャンセルしました。設定は変更されていません。", - cancellationToken: cancellationToken - ); + _ = await stepContext.Context.SendSettingsCancelledAsync(cancellationToken); } if (stepContext.Context.Activity.ReplyToId is not null) { @@ -191,46 +186,116 @@ private async Task AfterConrifmAsync(WaterfallStepContext step { Body = [ - new AdaptiveFactSet() + new AdaptiveColumnSet() { - Facts = + Columns = [ - new() + new AdaptiveColumn() { - Title = "スケジュール", - Value = new Func(() => - property.StartMeetingSchedule switch + Items = + [ + new AdaptiveTextBlock() { - -1 => "なし", - 0 => "予定時刻", - _ => $"{property.StartMeetingSchedule} 分後" + Text = "スケジュール", + Weight = AdaptiveTextWeight.Bolder } - )() + ], + Width = "90px" }, - new() + new AdaptiveColumn() { - Title = "メッセージ", - Value = $"{property.StartMeetingMessage}" + Items = + [ + new AdaptiveTextBlock() + { + Text = property.StartMeetingSchedule switch + { + -1 => "なし", + 0 => "予定時刻", + _ => $"{property.StartMeetingSchedule} 分後" + } + } + ], + Width = "stretch" + } + ], + }, + new AdaptiveColumnSet() + { + Columns = + [ + new AdaptiveColumn() + { + Items = + [ + new AdaptiveTextBlock() + { + Text = "メッセージ", + Weight = AdaptiveTextWeight.Bolder + } + ], + Width = "90px" }, - new() + new AdaptiveColumn() { - Title = "URL", - Value = $"{property.StartMeetingUrl}" + Items = + [ + new AdaptiveTextBlock() + { + Text = $"{property.StartMeetingMessage}", + Wrap = true + } + ], + Width = "stretch" + } + ] + }, + new AdaptiveColumnSet() + { + Columns = + [ + new AdaptiveColumn() + { + Items = + [ + new AdaptiveTextBlock() + { + Text = "URL", + Weight = AdaptiveTextWeight.Bolder + } + ], + Width = "90px" + }, + new AdaptiveColumn() + { + Items = + [ + new AdaptiveTextBlock() + { + Text = $"{property.StartMeetingUrl}" + } + ], + Width = "stretch" } ] } ] }; - if (property.StartMeetingUrl is not null) + if (Uri.TryCreate(property.StartMeetingUrl, UriKind.Absolute, out var url)) { - var bytes = await this.qrCodeService.CreateAsync(property.StartMeetingUrl, cancellationToken); + var bytes = await this.qrCodeService.CreateAsync(url.ToString(), cancellationToken); var base64 = Convert.ToBase64String(bytes); card.Body.Add(new AdaptiveImage() { - AltText = property.StartMeetingUrl, - Size = AdaptiveImageSize.Stretch, + AltText = url.ToString(), + Size = AdaptiveImageSize.Large, Url = new Uri($"data:image/png;base64,{base64}") }); + card.Actions.Add(new AdaptiveOpenUrlAction() + { + Title = "URL を開く", + Url = url, + }); } var activity = MessageFactory.Attachment(new Attachment() { @@ -238,10 +303,7 @@ private async Task AfterConrifmAsync(WaterfallStepContext step Content = JsonConvert.DeserializeObject(card.ToJson()) }); activity.Id = stepContext.Context.Activity.ReplyToId; - _ = await stepContext.Context.UpdateActivityAsync( - activity, - cancellationToken: cancellationToken - ); + _ = await stepContext.Context.UpdateActivityAsync(activity, cancellationToken); } return await stepContext.EndDialogAsync(cancellationToken: cancellationToken); } diff --git a/source/server/Karamem0.Commistant.Bot/Extensions/TurnContextExtensions.cs b/source/server/Karamem0.Commistant.Bot/Extensions/TurnContextExtensions.cs new file mode 100644 index 0000000..459ba79 --- /dev/null +++ b/source/server/Karamem0.Commistant.Bot/Extensions/TurnContextExtensions.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) 2022-2024 karamem0 +// +// This software is released under the MIT License. +// +// https://github.com/karamem0/commistant/blob/main/LICENSE +// + +using AdaptiveCards; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Dialogs; +using Microsoft.Bot.Schema; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Karamem0.Commistant.Extensions; + +public static class TurnContextExtensions +{ + + public static async Task SendSettingsCancelledAsync(this ITurnContext target, CancellationToken cancellationToken = default) + { + return await target.SendActivityAsync( + "キャンセルしました。設定は変更されていません。", + cancellationToken: cancellationToken + ); + } + + public static async Task SendSettingsResetedAsync(this ITurnContext target, CancellationToken cancellationToken = default) + { + return await target.SendActivityAsync( + "設定を初期化しました。", + cancellationToken: cancellationToken + ); + } + + public static async Task SendSettingsUpdatedAsync(this ITurnContext target, CancellationToken cancellationToken = default) + { + return await target.SendActivityAsync( + "設定を変更しました。", + cancellationToken: cancellationToken + ); + } + +} diff --git a/source/server/Karamem0.Commistant.Bot/Mappings/AutoMapperProfile.cs b/source/server/Karamem0.Commistant.Bot/Mappings/AutoMapperProfile.cs index c28b6ad..80bb00a 100644 --- a/source/server/Karamem0.Commistant.Bot/Mappings/AutoMapperProfile.cs +++ b/source/server/Karamem0.Commistant.Bot/Mappings/AutoMapperProfile.cs @@ -33,19 +33,19 @@ public AutoMapperProfile() o => { o.Condition(s => s.Type == "会議開始後"); - o.MapFrom((s, _) => + o.MapFrom((s, d) => { if (s.Value is null) { - return -1; + return d.StartMeetingSchedule; } if (s.Value.Enabled is false) { - return -1; + return d.StartMeetingSchedule; } if (Array.TrueForAll(StartMeetingSchedules, _ => _ != s.Value.Schedule)) { - return -1; + return d.StartMeetingSchedule; } return s.Value.Schedule; }); @@ -56,19 +56,19 @@ public AutoMapperProfile() o => { o.Condition(s => s.Type == "会議開始後"); - o.MapFrom((s, _) => + o.MapFrom((s, d) => { if (s.Value is null) { - return null; + return d.StartMeetingMessage; } if (s.Value.Enabled is false) { - return null; + return d.StartMeetingMessage; } if (string.IsNullOrEmpty(s.Value.Message)) { - return null; + return d.StartMeetingMessage; } return s.Value.Message; }); @@ -79,19 +79,19 @@ public AutoMapperProfile() o => { o.Condition(s => s.Type == "会議開始後"); - o.MapFrom((s, _) => + o.MapFrom((s, d) => { if (s.Value is null) { - return null; + return d.StartMeetingUrl; } if (s.Value.Enabled is false) { - return null; + return d.StartMeetingUrl; } if (string.IsNullOrEmpty(s.Value.Url)) { - return null; + return d.StartMeetingUrl; } return s.Value.Url; }); @@ -102,19 +102,19 @@ public AutoMapperProfile() o => { o.Condition(s => s.Type == "会議終了前"); - o.MapFrom((s, _) => + o.MapFrom((s, d) => { if (s.Value is null) { - return -1; + return d.EndMeetingSchedule; } if (s.Value.Enabled is false) { - return -1; + return d.EndMeetingSchedule; } if (Array.TrueForAll(EndMeetingSchedules, _ => _ != s.Value.Schedule)) { - return -1; + return d.EndMeetingSchedule; } return s.Value.Schedule; }); @@ -125,19 +125,19 @@ public AutoMapperProfile() o => { o.Condition(s => s.Type == "会議終了前"); - o.MapFrom((s, _) => + o.MapFrom((s, d) => { if (s.Value is null) { - return null; + return d.EndMeetingMessage; } if (s.Value.Enabled is false) { - return null; + return d.EndMeetingMessage; } if (string.IsNullOrEmpty(s.Value.Message)) { - return null; + return d.EndMeetingMessage; } return s.Value.Message; }); @@ -148,19 +148,19 @@ public AutoMapperProfile() o => { o.Condition(s => s.Type == "会議終了前"); - o.MapFrom((s, _) => + o.MapFrom((s, d) => { if (s.Value is null) { - return null; + return d.EndMeetingUrl; } if (s.Value.Enabled is false) { - return null; + return d.EndMeetingUrl; } if (string.IsNullOrEmpty(s.Value.Url)) { - return null; + return d.EndMeetingUrl; } return s.Value.Url; }); @@ -171,19 +171,19 @@ public AutoMapperProfile() o => { o.Condition(s => s.Type == "会議中"); - o.MapFrom((s, _) => + o.MapFrom((s, d) => { if (s.Value is null) { - return -1; + return d.InMeetingSchedule; } if (s.Value.Enabled is false) { - return -1; + return d.InMeetingSchedule; } if (Array.TrueForAll(InMeetingSchedules, _ => _ != s.Value.Schedule)) { - return -1; + return d.InMeetingSchedule; } return s.Value.Schedule; }); @@ -194,19 +194,19 @@ public AutoMapperProfile() o => { o.Condition(s => s.Type == "会議中"); - o.MapFrom((s, _) => + o.MapFrom((s, d) => { if (s.Value is null) { - return null; + return d.InMeetingMessage; } if (s.Value.Enabled is false) { - return null; + return d.InMeetingMessage; } if (string.IsNullOrEmpty(s.Value.Message)) { - return null; + return d.InMeetingMessage; } return s.Value.Message; }); @@ -217,19 +217,19 @@ public AutoMapperProfile() o => { o.Condition(s => s.Type == "会議中"); - o.MapFrom((s, _) => + o.MapFrom((s, d) => { if (s.Value is null) { - return null; + return d.InMeetingUrl; } if (s.Value.Enabled is false) { - return null; + return d.InMeetingUrl; } if (string.IsNullOrEmpty(s.Value.Url)) { - return null; + return d.InMeetingUrl; } return s.Value.Url; }); diff --git a/source/server/Karamem0.Commistant.Common/Extensions/BlobClientExtension.cs b/source/server/Karamem0.Commistant.Common/Extensions/BlobClientExtension.cs index d6e2d46..5919a4f 100644 --- a/source/server/Karamem0.Commistant.Common/Extensions/BlobClientExtension.cs +++ b/source/server/Karamem0.Commistant.Common/Extensions/BlobClientExtension.cs @@ -37,9 +37,9 @@ public static async Task> GetObjectAsync(this BlobClient targe public static async Task SetObjectAsync(this BlobClient target, BlobContent value) { - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(value.Data))); + var content = BinaryData.FromString(JsonSerializer.Serialize(value.Data)); _ = await target.UploadAsync( - stream, + content, new BlobUploadOptions() { Conditions = new BlobRequestConditions() diff --git a/source/server/Karamem0.Commistant.Common/Serialization/JsonSerializer.cs b/source/server/Karamem0.Commistant.Common/Serialization/JsonSerializer.cs index 9c6f4ca..cbbb636 100644 --- a/source/server/Karamem0.Commistant.Common/Serialization/JsonSerializer.cs +++ b/source/server/Karamem0.Commistant.Common/Serialization/JsonSerializer.cs @@ -25,19 +25,13 @@ public static class JsonSerializer public static T? Deserialize(string? value) { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } + _ = value ?? throw new ArgumentNullException(nameof(value)); return JsonConvert.DeserializeObject(value, settings); } public static string Serialize(T? value) { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } + _ = value ?? throw new ArgumentNullException(nameof(value)); return JsonConvert.SerializeObject(value, settings); } diff --git a/source/server/Karamem0.Commistant.Common/Services/OpenAIService.cs b/source/server/Karamem0.Commistant.Common/Services/OpenAIService.cs index b0d91be..2d61619 100644 --- a/source/server/Karamem0.Commistant.Common/Services/OpenAIService.cs +++ b/source/server/Karamem0.Commistant.Common/Services/OpenAIService.cs @@ -7,7 +7,9 @@ // using Azure.AI.OpenAI; +using Karamem0.Commistant.Models; using Karamem0.Commistant.Resources; +using Newtonsoft.Json; using OpenAI; using OpenAI.Chat; using System; @@ -22,7 +24,7 @@ namespace Karamem0.Commistant.Services; public interface IOpenAIService { - Task ChatCompletionAsync(string text, CancellationToken cancellationToken = default); + Task GetArgumentsAsync(string text, CancellationToken cancellationToken = default); } @@ -33,7 +35,7 @@ public class OpenAIService(OpenAIClient openAIClient, string openAIModelName) : private readonly string openAIModelName = openAIModelName; - public async Task ChatCompletionAsync(string text, CancellationToken cancellationToken = default) + public async Task GetArgumentsAsync(string text, CancellationToken cancellationToken = default) { var chatCompletionsOptions = new ChatCompletionOptions() { @@ -69,7 +71,16 @@ public async Task ChatCompletionAsync(string text, CancellationT chatCompletionsOptions, cancellationToken ); - return chatCompletion.Value; + if (chatCompletion.Value.FinishReason == ChatFinishReason.ToolCalls) + { + return JsonConvert.DeserializeObject( + chatCompletion.Value.ToolCalls + .Select(item => item.FunctionArguments) + .First() + .ToString() + ); + } + return null; } } diff --git a/source/server/Karamem0.Commistant.Functions/Commands/EndMeetingCommand.cs b/source/server/Karamem0.Commistant.Functions/Commands/EndMeetingCommand.cs index 6d00d89..3d17624 100644 --- a/source/server/Karamem0.Commistant.Functions/Commands/EndMeetingCommand.cs +++ b/source/server/Karamem0.Commistant.Functions/Commands/EndMeetingCommand.cs @@ -48,7 +48,7 @@ public override async Task ExecuteAsync( CancellationToken cancellationToken = default ) { - if (property.InMeeting is not true) + if (property.InMeeting is false) { return; } @@ -79,7 +79,7 @@ public override async Task ExecuteAsync( { this.logger.EndMeetingMessageNotifying(reference, property); var card = new AdaptiveCard("1.3"); - if (string.IsNullOrEmpty(property.EndMeetingMessage) is not true) + if (string.IsNullOrEmpty(property.EndMeetingMessage) is false) { card.Body.Add(new AdaptiveTextBlock() { @@ -87,16 +87,21 @@ public override async Task ExecuteAsync( Wrap = true }); } - if (string.IsNullOrEmpty(property.EndMeetingUrl) is not true) + if (Uri.TryCreate(property.EndMeetingUrl, UriKind.Absolute, out var url)) { - var bytes = await this.qrCodeService.CreateAsync(property.EndMeetingUrl, cancellationToken); + var bytes = await this.qrCodeService.CreateAsync(url.ToString(), cancellationToken); var base64 = Convert.ToBase64String(bytes); card.Body.Add(new AdaptiveImage() { - AltText = property.InMeetingUrl, + AltText = url.ToString(), Size = AdaptiveImageSize.Stretch, Url = new Uri($"data:image/png;base64,{base64}") }); + card.Actions.Add(new AdaptiveOpenUrlAction() + { + Title = "URL を開く", + Url = url, + }); } var activity = MessageFactory.Attachment(new Attachment() { @@ -109,7 +114,7 @@ public override async Task ExecuteAsync( _ = await this.connectorClientService.SendActivityAsync( new Uri(reference.ServiceUrl), (Activity)activity, - cancellationToken: cancellationToken + cancellationToken ); } finally diff --git a/source/server/Karamem0.Commistant.Functions/Commands/InMeetingCommand.cs b/source/server/Karamem0.Commistant.Functions/Commands/InMeetingCommand.cs index c5cf7cf..3e22f6d 100644 --- a/source/server/Karamem0.Commistant.Functions/Commands/InMeetingCommand.cs +++ b/source/server/Karamem0.Commistant.Functions/Commands/InMeetingCommand.cs @@ -48,7 +48,7 @@ public override async Task ExecuteAsync( CancellationToken cancellationToken = default ) { - if (property.InMeeting is not true) + if (property.InMeeting is false) { return; } @@ -75,7 +75,7 @@ public override async Task ExecuteAsync( { this.logger.InMeetingMessageNotifying(reference, property); var card = new AdaptiveCard("1.3"); - if (string.IsNullOrEmpty(property.InMeetingMessage) is not true) + if (string.IsNullOrEmpty(property.InMeetingMessage) is false) { card.Body.Add(new AdaptiveTextBlock() { @@ -83,16 +83,21 @@ public override async Task ExecuteAsync( Wrap = true }); } - if (string.IsNullOrEmpty(property.InMeetingUrl) is not true) + if (Uri.TryCreate(property.InMeetingUrl, UriKind.Absolute, out var url)) { - var bytes = await this.qrCodeService.CreateAsync(property.InMeetingUrl); + var bytes = await this.qrCodeService.CreateAsync(url.ToString(), cancellationToken); var base64 = Convert.ToBase64String(bytes); card.Body.Add(new AdaptiveImage() { - AltText = property.InMeetingUrl, + AltText = url.ToString(), Size = AdaptiveImageSize.Stretch, Url = new Uri($"data:image/png;base64,{base64}") }); + card.Actions.Add(new AdaptiveOpenUrlAction() + { + Title = "URL を開く", + Url = url, + }); } var activity = MessageFactory.Attachment(new Attachment() { @@ -105,7 +110,7 @@ public override async Task ExecuteAsync( _ = await this.connectorClientService.SendActivityAsync( new Uri(reference.ServiceUrl), (Activity)activity, - cancellationToken: cancellationToken + cancellationToken ); } finally diff --git a/source/server/Karamem0.Commistant.Functions/Commands/StartMeetingCommand.cs b/source/server/Karamem0.Commistant.Functions/Commands/StartMeetingCommand.cs index b946203..8ddaf2b 100644 --- a/source/server/Karamem0.Commistant.Functions/Commands/StartMeetingCommand.cs +++ b/source/server/Karamem0.Commistant.Functions/Commands/StartMeetingCommand.cs @@ -48,7 +48,7 @@ public override async Task ExecuteAsync( CancellationToken cancellationToken = default ) { - if (property.InMeeting is not true) + if (property.InMeeting is false) { return; } @@ -79,7 +79,7 @@ public override async Task ExecuteAsync( { this.logger.StartMeetingMessageNotifying(reference, property); var card = new AdaptiveCard("1.3"); - if (string.IsNullOrEmpty(property.StartMeetingMessage) is not true) + if (string.IsNullOrEmpty(property.StartMeetingMessage) is false) { card.Body.Add(new AdaptiveTextBlock() { @@ -87,16 +87,21 @@ public override async Task ExecuteAsync( Wrap = true }); } - if (string.IsNullOrEmpty(property.StartMeetingUrl) is not true) + if (Uri.TryCreate(property.StartMeetingUrl, UriKind.Absolute, out var url)) { - var bytes = await this.qrCodeService.CreateAsync(property.StartMeetingUrl); + var bytes = await this.qrCodeService.CreateAsync(url.ToString(), cancellationToken); var base64 = Convert.ToBase64String(bytes); card.Body.Add(new AdaptiveImage() { - AltText = property.InMeetingUrl, + AltText = url.ToString(), Size = AdaptiveImageSize.Stretch, Url = new Uri($"data:image/png;base64,{base64}") }); + card.Actions.Add(new AdaptiveOpenUrlAction() + { + Title = "URL を開く", + Url = url, + }); } var activity = MessageFactory.Attachment(new Attachment() { @@ -109,7 +114,7 @@ public override async Task ExecuteAsync( _ = await this.connectorClientService.SendActivityAsync( new Uri(reference.ServiceUrl), (Activity)activity, - cancellationToken: cancellationToken + cancellationToken ); } finally diff --git a/source/server/Karamem0.Commistant.Tests/Functions/Commands/EndMeetingCommandTests.cs b/source/server/Karamem0.Commistant.Tests/Functions/Commands/EndMeetingCommandTests.cs index 47cfe04..a1975e4 100644 --- a/source/server/Karamem0.Commistant.Tests/Functions/Commands/EndMeetingCommandTests.cs +++ b/source/server/Karamem0.Commistant.Tests/Functions/Commands/EndMeetingCommandTests.cs @@ -36,7 +36,7 @@ public async Task EndMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -46,16 +46,16 @@ public async Task EndMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() EndMeetingSended = false, EndMeetingSchedule = 10, EndMeetingMessage = "", - EndMeetingUrl = "https://www.example.com", + EndMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 20, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); @@ -75,10 +75,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.EndMeetingSended, Is.EqualTo(true)); _ = connectorClientService .Received() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .Received() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -91,7 +91,7 @@ public async Task EndMeetingCommand_ExecuteAsync_Succeeded_AfterSchedule() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -101,16 +101,16 @@ public async Task EndMeetingCommand_ExecuteAsync_Succeeded_AfterSchedule() EndMeetingSended = false, EndMeetingSchedule = 10, EndMeetingMessage = "", - EndMeetingUrl = "https://www.example.com", + EndMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 25, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); @@ -130,10 +130,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.EndMeetingSended, Is.EqualTo(true)); _ = connectorClientService .Received() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .Received() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -146,7 +146,7 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_BeforeSchedule() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -156,7 +156,7 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_BeforeSchedule() EndMeetingSended = false, EndMeetingSchedule = 10, EndMeetingMessage = "", - EndMeetingUrl = "https://www.example.com", + EndMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService @@ -164,10 +164,10 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_BeforeSchedule() .Returns(new DateTime(2000, 1, 1, 9, 15, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); _ = connectorClientService - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); @@ -187,10 +187,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.EndMeetingSended, Is.EqualTo(false)); _ = connectorClientService .DidNotReceive() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .DidNotReceive() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -203,7 +203,7 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -213,7 +213,7 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() EndMeetingSended = true, EndMeetingSchedule = 10, EndMeetingMessage = "", - EndMeetingUrl = "https://www.example.com", + EndMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService @@ -221,11 +221,11 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() .Returns(new DateTime(2000, 1, 1, 9, 20, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); _ = connectorClientService - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); _ = qrCodeService - .CreateAsync("https://www.example.com") + .CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); @@ -245,10 +245,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.EndMeetingSended, Is.EqualTo(true)); _ = connectorClientService .DidNotReceive() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .DidNotReceive() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -261,7 +261,7 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_AfterSended() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -271,7 +271,7 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_AfterSended() EndMeetingSended = true, EndMeetingSchedule = 10, EndMeetingMessage = "", - EndMeetingUrl = "https://www.example.com", + EndMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService @@ -279,11 +279,11 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_AfterSended() .Returns(new DateTime(2000, 1, 1, 9, 20, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); _ = connectorClientService - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); _ = qrCodeService - .CreateAsync("https://www.example.com") + .CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); @@ -303,10 +303,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.EndMeetingSended, Is.EqualTo(true)); _ = connectorClientService .DidNotReceive() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .DidNotReceive() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } } diff --git a/source/server/Karamem0.Commistant.Tests/Functions/Commands/InMeetingCommandTests.cs b/source/server/Karamem0.Commistant.Tests/Functions/Commands/InMeetingCommandTests.cs index f3a0ee9..6e67a62 100644 --- a/source/server/Karamem0.Commistant.Tests/Functions/Commands/InMeetingCommandTests.cs +++ b/source/server/Karamem0.Commistant.Tests/Functions/Commands/InMeetingCommandTests.cs @@ -36,7 +36,7 @@ public async Task InMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -45,16 +45,16 @@ public async Task InMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() ScheduledEndTime = new DateTime(2000, 1, 1, 9, 30, 0, DateTimeKind.Utc), InMeetingSchedule = 10, InMeetingMessage = "", - InMeetingUrl = "https://www.example.com", + InMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 20, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.InMeetingMessageNotifying(conversationReference, conversationProperty); @@ -73,10 +73,10 @@ await command.ExecuteAsync( // Assert _ = connectorClientService .Received() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .Received() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -89,7 +89,7 @@ public async Task InMeetingCommand_ExecuteAsync_Skipped_OffSchedule() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -98,16 +98,16 @@ public async Task InMeetingCommand_ExecuteAsync_Skipped_OffSchedule() ScheduledEndTime = new DateTime(2000, 1, 1, 9, 30, 0, DateTimeKind.Utc), InMeetingSchedule = 10, InMeetingMessage = "", - InMeetingUrl = "https://www.example.com", + InMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 25, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.InMeetingMessageNotifying(conversationReference, conversationProperty); @@ -126,10 +126,10 @@ await command.ExecuteAsync( // Assert _ = connectorClientService .DidNotReceive() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .DidNotReceive() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -142,7 +142,7 @@ public async Task InMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -151,16 +151,16 @@ public async Task InMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() ScheduledEndTime = new DateTime(2000, 1, 1, 9, 30, 0, DateTimeKind.Utc), InMeetingSchedule = 10, InMeetingMessage = "", - InMeetingUrl = "https://www.example.com", + InMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 20, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.InMeetingMessageNotifying(conversationReference, conversationProperty); @@ -179,10 +179,10 @@ await command.ExecuteAsync( // Assert _ = connectorClientService .DidNotReceive() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .DidNotReceive() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } } diff --git a/source/server/Karamem0.Commistant.Tests/Functions/Commands/StartMeetingCommandTests.cs b/source/server/Karamem0.Commistant.Tests/Functions/Commands/StartMeetingCommandTests.cs index 3c8fc85..f74cbdd 100644 --- a/source/server/Karamem0.Commistant.Tests/Functions/Commands/StartMeetingCommandTests.cs +++ b/source/server/Karamem0.Commistant.Tests/Functions/Commands/StartMeetingCommandTests.cs @@ -36,7 +36,7 @@ public async Task StartMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -46,16 +46,16 @@ public async Task StartMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() StartMeetingSended = false, StartMeetingSchedule = 10, StartMeetingMessage = "", - StartMeetingUrl = "https://www.example.com", + StartMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 10, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); @@ -75,10 +75,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.StartMeetingSended, Is.EqualTo(true)); _ = connectorClientService .Received() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .Received() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -91,7 +91,7 @@ public async Task StartMeetingCommand_ExecuteAsync_Succeeded_AfterSchedule() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -101,16 +101,16 @@ public async Task StartMeetingCommand_ExecuteAsync_Succeeded_AfterSchedule() StartMeetingSended = false, StartMeetingSchedule = 10, StartMeetingMessage = "", - StartMeetingUrl = "https://www.example.com", + StartMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 15, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); @@ -130,10 +130,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.StartMeetingSended, Is.EqualTo(true)); _ = connectorClientService .Received() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .Received() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -146,7 +146,7 @@ public async Task StartMeetingCommand_ExecuteAsync_Skipped_BeforeSchedule() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -156,16 +156,16 @@ public async Task StartMeetingCommand_ExecuteAsync_Skipped_BeforeSchedule() StartMeetingSended = false, StartMeetingSchedule = 10, StartMeetingMessage = "", - StartMeetingUrl = "https://www.example.com", + StartMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 5, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); @@ -185,10 +185,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.StartMeetingSended, Is.EqualTo(false)); _ = connectorClientService .DidNotReceive() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .DidNotReceive() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -201,7 +201,7 @@ public async Task StartMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -211,16 +211,16 @@ public async Task StartMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() StartMeetingSended = false, StartMeetingSchedule = 10, StartMeetingMessage = "", - StartMeetingUrl = "https://www.example.com", + StartMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 10, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); @@ -240,10 +240,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.StartMeetingSended, Is.EqualTo(false)); _ = connectorClientService .DidNotReceive() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .DidNotReceive() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } [Test()] @@ -256,7 +256,7 @@ public async Task StartMeetingCommand_ExecuteAsync_Skipped_AfterSended() { Id = "1234567890", }, - ServiceUrl = "https://www.example.com", + ServiceUrl = "https://www.example.com/", }; var conversationProperty = new ConversationProperty() { @@ -266,16 +266,16 @@ public async Task StartMeetingCommand_ExecuteAsync_Skipped_AfterSended() StartMeetingSended = true, StartMeetingSchedule = 10, StartMeetingMessage = "", - StartMeetingUrl = "https://www.example.com", + StartMeetingUrl = "https://www.example.com/", }; var dateTimeService = Substitute.For(); _ = dateTimeService.GetCurrentDateTime() .Returns(new DateTime(2000, 1, 1, 9, 10, 0, DateTimeKind.Utc)); var connectorClientService = Substitute.For(); - _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()) + _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); var qrCodeService = Substitute.For(); - _ = qrCodeService.CreateAsync("https://www.example.com") + _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); @@ -295,10 +295,10 @@ await command.ExecuteAsync( Assert.That(conversationProperty.StartMeetingSended, Is.EqualTo(true)); _ = connectorClientService .DidNotReceive() - .SendActivityAsync(new Uri("https://www.example.com"), Arg.Any()); + .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()); _ = qrCodeService .DidNotReceive() - .CreateAsync("https://www.example.com"); + .CreateAsync("https://www.example.com/"); } }