티스토리 뷰
지난번 Part5에 이어서 향상된 샌드위치 주문하기를 만들어 보도록 하겠습니다. 지난번 샌드 위치 주문은 다들 잘 하셨는지 궁금하군요 ㅋㅋ
Part2 : Azure에 배포하고, Bot Framework에 등록하기
지난 시간에 사용했던 페이지에서 계속 이어 갑니다.
http://docs.botframework.com/sdkreference/csharp/forms.html
1. 뭘 향상 시킬까요?
1) 문자열과 날짜을 추가합니다.
2) 여러가지 속성을 추가합니다. (설명, 약관, 프롬프트, 템플릿)
3) Switching from fields to properties to incorporate business logic
4) 메시지를 추가하고, 흐름을 컨트롤 할 수 있습니다.
2. 추가 가능한 속성은 뭐가 있나요?
1) Describe
필드 또는 값이 텍스트로 표시되는 방식을 변경합니다.
2) Numeric
숫자 필드에 허용된 값에 대한 제한을 걸 수 있습니다.
3) Optional
하나의 선택 값을 제공하지 않는 것 같은 선택적 필드를 만들 수 있습니다.
4) Prompt
필드에 입력을 요구하는 프로프트를 정의 할 수 있습니다.
5) Template
프롬프트에서 사용되는 템플릿을 정의 합니다.
6) Terms
필드나 값과 일치하는 입력 조건을 정의합니다.
3. 정리를 좀 해보죠
그러니까 샌드위치를 주문할 때 "Please select a sandwich"라고 이야기하는 것과 "What kind of sandwich would you like?"라고 이야기하는 것 중 어떤 것이 더 좋겠나요? 당근 후자겠죠? 아니면, 한글로 "어떤 종류의 샌드위치를 주문하시겠어요?"라고 한다면, 금상첨화 일 것 같네요
KakiSandwichOrder.cs에서 아래 내용을 수정 하도록 하겠습니다.
[Prompt("어떤 종류의 {&}로 주문을 도와드릴까요? {||}")]
[Describe("메뉴")]
public KakiMenuOptions? Menu;
여기서 한가지 재미있는 문자열이 보이는데요~ "{&}"와 "{||}" 입니다. Pattern Language라고 하는데, 이 부분에 문자열이 대체되어서 생성이 됩니다. "{&}" 부분은 필드의 설명이 들어가고, "{||}" 부분은 선택 가능한 목록이 나열됩니다. 그 외 다른 녀석들에 대한 설명은 뒤에서 하도록 하고, 실행을 해서 결과를 확인 합니다.
메뉴라는 글씨와 3개의 선택 가능 내용이 리스트업이 됩니다. 그렇다면 "{||}"을 삭제하면 어떻게 될까요??
정답은 아무것도 출력되지 않는다~입니다. 으흠..그럼 뭘 주문할지 어떻게 알죠? 알 수 있는 방법은 ?를 입력하시면 알 수 있습니다.
음음.. 아무래도 나오는 것이 더 보기 좋은 듯하네요~ 흐흐;;
그렇다면 왜 Pattern Language를 사용하는 것일까? 하는 의문이 생깁니다. 각 단계마다 프롬프트를 추가해주면 될 듯한데 왜?? 굳이??
이유는~ Pattern Language를 이용해서 Template로 하나의 문장을 만들어 놓으면 모든 프롬프트가 변경되기 때문입니다. 다음과 같이 수정을 해 보도록 하겠습니다.
KakiSandwichOrder.cs 클래스 선언 위에 아래와 같은 속성을 추가합니다.
[Serializable]
[Template(TemplateUsage.EnumSelectOne, "고객님의 샌드위치에 어떤 종류의 {&}을(를) 선택 하시겠습니까?{||}",
ChoiceStyle = ChoiceStyleOptions.PerLine)]
public class KakiSandwichOrder
TemplateUsage.EnumSelectOne 부분은 Enum으로 만들어진 녀석들 중 단일 선택인 필드에 적용할 문자열을 미리 정의하는 것입니다. 추가적으로, 필드들에 Describe을 추가해서 출력될 문자열을 이쁘게 만듭니다.
[Describe("빵")]
public KakiBreadOptions? Bread;
[Describe("추가재료")]
public KakiAdditionOptions? Addition;
이제 실행해서 결과를 확인 합니다.
영어로 나오는 것에 비해서 확실히 부드럽다는 생각이 드네요.
4. 세트는 싫어요~
역시가 저도 세트를 꼭 먹어야 한다고 생각하지는 않습니다. 으흐흐..고객의 선택인거죠? 이렇게 선택적인 필드는 어떻게 처리할까요?
Optional이라는 속성을 필드에 추가하면 간단하게 해결할 수 있습니다.
[Optional]
public List<KakiSetOptions> Set;
실행을 해보면 아래와 같이 표시됩니다.
no라고 입력하니 다음으로 넘어가는 군요 후후.. current choice라는 글씨도 한글로 바꾸고 싶어지내요;;;
5. 똑같은 맨트는 싫어요~
주문을 하는 동안 잘못된 단어를 이야기 했을 때 동일한 단어를 반복적으로 대답하면 싫겠죠? 다음과 같이 수정합니다.
public class KakiSandwichOrder
[Template(TemplateUsage.EnumSelectOne, "고객님의 샌드위치에 어떤 종류의 {&}을(를) 선택 하시겠습니까?{||}",
ChoiceStyle = ChoiceStyleOptions.PerLine)]
[Template(TemplateUsage.NotUnderstood, "이야기하신 단어를 이해하지 못 했습니다.\"{0}\".", "제가 난독증 일까요?\"{0}\".", "머라카노? 자꾸 그럴끼가?\"{0}\"")]
public class KakiSandwichOrder
입력한 단어를 처리할 수 없는 경우 3가지 경우 중에 하나를 반환 합니다. 음..도움말을 보여줄 때도 있으니 총 4가지 형태가 되겠네요
6. 대충 이야기해도 알아 들었으면 좋겠넹~
지금 카키 샌드위치에는 적용을 하기가 좀 어렵지만, Terms 속성에 대해 설명을 하도록 하겠습니다.
1) 대소문자가 변하거나 _가 있는 경우 단어로 인정
2) 쵀대 길이까지 구문 생성;;;
3) 단어 끝에 s?가 붙으면 복수형을 지원
예를 들어
[Terms(@"rotis\w* style chicken", MaxPhrase = 3)]RotisserieStyleChicken
이렇게 속성이 들어가있다고 하면, 기본적으로 대소문자가 변하는 부분이 3부분이니까 3개의 단어가 있다고 생각하고, rotis로 대충 시작하면 Rotisserie로 인식해라~라는 것과 최대 3개의 단어로 구성된다..뭐 그런 내용입니다.
7. 비지니스 로직을 추가해 보죠
어떤 고객님이 오셔서 난 모든 야채를 다 넣고 싶어~라고 한다면, 네~ 그렇다면 1,2,3,4,5,6....을 모두 입력하세요~라고 이야기를 해야겠죠?? 쿨럭;; 하지만, 고객이 왕인데..그렇게 이야기하면 싫어할 것 같네요. 대신, 고객님~ 전체를 선택하시면 됩니다~라고 안내를 해주세요~
먼저, 야채 부분을 약간 손대 보죠
public enum KakiVegetableOptions
{
[Terms("모두", "몽땅", "다", "전부")]전체 = 1,
양상추, 토마토, 오이, 피망, 양파, 피클, 올리브, 할라피뇨
}
여기서 Terms라는 속성을 이용해서 전체라는 단어를 여러가지 다른 단어로도 입력 할 수 있도록 했습니다. 그럼 1번을 선택하면 양상추, 토마토, 오이, 피망, 양파, 피클, 올리브, 할라피뇨가 모두 선택이 되어야 겠죠?
다음을 진행하기 위해서는 LINQ를 사용해야 하는데, 프로젝트에 System.Xml.Linq가 추가되어야 할 것 같내요.
KakiSandwichOrder.cs 상단에
using System.Linq;
문을 추가한 후에 아래 코드를 입력합니다.
private List<KakiVegetableOptions> _vegetable;
[Describe("야채")]
public List<KakiVegetableOptions> Vegetable
{
get { return _vegetable; }
set
{
if (value != null && value.Contains(KakiVegetableOptions.전체))
{
_vegetable = (from KakiVegetableOptions kkk in Enum.GetValues(typeof(KakiVegetableOptions))
where kkk != KakiVegetableOptions.전체
select kkk).ToList();
}
else
{
_vegetable = value;
}
}
}
위 코드는 '전체'가 선택이 되어있는 경우 '전체'를 제외한 나머지 항목을 모두 선택하는 것 입니다.
그런데.. 이렇게 하고나면..야채 선택하는 메시지가 제일 아래에서 출력이 되네요..이런 이런;;
8. Form Builder을 이용해서 최종 주문 폼을 만들어 보죠
폼 빌더를 이용해서 좀더 사용자 친화적인 대화 환경을 조성할 수 있습니다. 아래 코드를 볼까요?
public static IForm<KakiSandwichOrder> BuildForm()
{
OnCompletionAsyncDelegate<KakiSandwichOrder> processOrder =
async (context, state) => { await context.PostAsync("현재 고객님의 샌드위치를 만들고 있습니다. 준비되면 알려 드리겠습니다."); };
return new FormBuilder<KakiSandwichOrder>()
.Message("안녕하세요 카키 샌드위치 주문 봇입니다. help 혹은 ? 를 입력하시면 도움말을 보실 수 있습니다.")
.Field(nameof(Menu))
.Field(nameof(Bread))
.Field(nameof(Addition))
.Field(nameof(Vegetable))
.Message("아래의 야채들을 선택 하셨습니다.{Vegetable}")
.Field(nameof(Source))
.Field(nameof(Set))
.Field(nameof(OrderCount))
.Field(new FieldReflector<KakiSandwichOrder>(nameof(Specials))
.SetType(null)
.SetActive(state => state.Menu == KakiMenuOptions.Menu30cm)
.SetDefine(async (state, field) =>
{
field
.AddDescription("콜라", "무료 콜라")
.AddTerms("콜라", "무료 콜라")
.AddDescription("음료", "큰 음료 무료")
.AddTerms("음료", "무료 음료");
return true;
}))
.Confirm(async state =>
{
var cost = 0.0;
switch (state.Menu)
{
case KakiMenuOptions.Menu15cm:
cost = 5000*state.OrderCount;
break;
case KakiMenuOptions.Menu30cm:
cost = 6500*state.OrderCount;
break;
case KakiMenuOptions.찹샐러드:
cost = 7500*state.OrderCount;
break;
}
return new PromptAttribute($"고객님이 주문하신 총 금액은 {cost:N0}원 입니다. 맞습니까?(yes/no)");
})
.Field(nameof(DeliveryAddress),
validate:async (state, response) =>
{
var result = new ValidateResult {IsValid = true, Value = response };
var address = (response as string).Trim();
if (address.Length == 0)
{
result.Feedback = "주소를 입력하세요.";
result.IsValid = false;
}
return result;
})
.Field(nameof(DeliveryTime), "언제까지 배달해 드릴까요? 시간:분으로 입력해 주세요.{||}")
.Confirm("최종 주문 확인 하겠습니다. {Menu} 메뉴에 빵은 {Bread}, 추가재료는 {Addition}, 야채는 {Vegetable}, 소스는 {Source}를 선택하신 샌드위치 {OrderCount}개를 주문하셨으며, 배송지는 {DeliveryAddress} {?at {DeliveryTime:t}} 입니다. 주문하신 내용이 맞습니까?")
.AddRemainingFields()
.Message("주문해주셔서 감사합니다!")
.OnCompletionAsync(processOrder)
.Build();
}
컥 갑자기 내용이 늘었다고 긴장하실 필요는 없습니다. 제가 이미 삽질 다해서 파악을 해 놓았으니 설명들어보시면 쉽게 이해가 되실 것이라고 생각합니다. 보시다가 이해가 어려운 부분은 리플 남겨주시면 추가 설명을 하도록 하겠습니다.
* Dynamically Defined Fields, Confirmations and Messages
주문을 하는 중에 유동적으로 필드를 생성하거나, 확인 메시지를 출력하기 위한 경우에 대해서 설명을 드립니다.
state는 현재 주문 진행 중인 KakiSandwichOrder를 이야기 합니다.
[Optional] [Template(TemplateUsage.NoPreference, "None")]
[Describe("스페셜")]
public string Specials;
.Field(new FieldReflector<KakiSandwichOrder>(nameof(Specials))
.SetType(null)
.SetActive(state => state.Menu == KakiMenuOptions.Menu30cm)
.SetDefine(async (state, field) =>
{
field
.AddDescription("콜라", "무료 콜라")
.AddTerms("콜라", "무료 콜라")
.AddDescription("음료", "큰 음료 무료")
.AddTerms("음료", "무료 음료");
return true;
}))
스페셜이라는 필드가 있습니다. 이 필드는 메뉴(맨 처음 선택하는 녀석 3가지) 중 Menu30cm를 선택하는 경우에만 출력되는 녀석입니다. 콜라와 음료를 무료로 제공하는 녀석이죠.
.Confirm(async state =>
{
var cost = 0.0;
switch (state.Menu)
{
case KakiMenuOptions.Menu15cm:
cost = 5000*state.OrderCount;
break;
case KakiMenuOptions.Menu30cm:
cost = 6500*state.OrderCount;
break;
case KakiMenuOptions.찹샐러드:
cost = 7500*state.OrderCount;
break;
}
return new PromptAttribute($"고객님이 주문하신 총 금액은 {cost:N0}원 입니다. 맞습니까?(yes/no)");
})
이 녀석은 금액을 계산해 주고 있습니다. 현재 주문에서 선택한 Menu가 어떤 것인지 확인하고, 주문 수량 곱해서 금액을 출력해 줍니다.
이 내용을 입력하면 코드에 경고가 출력될 수 있습니다. 음..제가 사옹하고 있는 툴에서 나온 경고인지, 비주얼 스튜디오 경고인지 확실하지는 않치만.. async 뒤에 구문 때문에 경고가 출력이 되는데, 무시하셔도 됩니다. 그래도, 신경이 쓰이신다면,
파일 맨 위에 아래와 같은 코드를 추가해서 경고 메시지가 출력되지 않도록 처리할 수 있습니다.
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
9. MessagesController.cs 도 약간 수정해 줍니다.
internal static IDialog<KakiSandwichOrder> KakiMakeRootDialog()
{
return Chain.From(() => FormDialog.FromForm(KakiSandwichOrder.BuildForm))
.Do(async (context, order) =>
{
try
{
var completed = await order;
if (completed == null) return;
// Actually process the sandwich order...
await context.PostAsync("Processed your order!");
}
catch (FormCanceledException<KakiSandwichOrder> e)
{
string reply;
if (e.InnerException == null)
{
reply = $"You quit on {e.Last}--maybe you can finish next time!";
}
else
{
reply = "Sorry, I've had a short circuit. Please try again.";
}
await context.PostAsync(reply);
}
});
}
모든 주문이 끝나면 completed에 주문완료된 내용이 반환되면, 이 주문 내용을 DB에 저장하고 매장에 알려서 만들고 배달을 시키면 되겠죠?
10. 소스
'Bot Framework' 카테고리의 다른 글
Bot Framework V3 Upgrade - 테스트 가능 (1) | 2016.07.11 |
---|---|
Microsoft Band 데이터와 Bot Framework를 사용해서 Health Bot 만들기 (0) | 2016.07.05 |
Bot Framework 시작하기 Part5 - 카키 샌드위치 주문하기 (2) | 2016.05.31 |
Bot Framework 시작하기 Part4 - 다이얼로그 처리 (0) | 2016.05.25 |
Bot Framework 시작하기 Part3 - Facebook 메신저와 연동하기 (3) | 2016.05.08 |
- Total
- Today
- Yesterday
- #uwp
- WPF
- Behavior
- ef core
- PRISM
- C#
- #prism
- .net
- Always Encrypted
- LINQ
- UWP
- MVVM
- .net 5.0
- kiosk
- ComboBox
- Windows 10
- Cross-platform
- Build 2016
- uno-platform
- #Windows Template Studio
- #MVVM
- Bot Framework
- IOT
- XAML
- uno platform
- Microsoft
- dotNETconf
- visual studio 2019
- windows 11
- Visual Studio 2022
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |