PromptDialog.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
38 using Microsoft.Bot.Connector;
39 using System;
40 using System.Collections.Generic;
41 using System.Linq;
42 using System.Threading.Tasks;
43 
44 namespace Microsoft.Bot.Builder.Dialogs
45 {
49  public enum PromptStyle
50  {
54  Auto,
55 
64  Keyboard,
65 
70  AutoText,
71 
75  Inline,
76 
80  PerLine,
81 
85  None
86  }
87 
92  [Serializable]
93  public class PromptOptions<T>
94  {
98  public readonly string Prompt;
99 
103  public readonly string Retry;
104 
108  public readonly IReadOnlyList<T> Options;
109 
117  public readonly IReadOnlyList<string> Descriptions;
118 
122  public readonly string TooManyAttempts;
123 
127  public int Attempts { set; get; }
128 
132  public readonly PromptStyler PromptStyler;
133 
137  public string DefaultRetry { get; set; }
138 
142  protected string DefaultTooManyAttempts
143  {
144  get { return Resources.TooManyAttempts; }
145  }
146 
157  public PromptOptions(string prompt, string retry = null, string tooManyAttempts = null, IReadOnlyList<T> options = null, int attempts = 3, PromptStyler promptStyler = null, IReadOnlyList<string> descriptions = null)
158  {
159  SetField.NotNull(out this.Prompt, nameof(this.Prompt), prompt);
160  this.Retry = retry;
161  this.TooManyAttempts = tooManyAttempts ?? this.DefaultTooManyAttempts;
162  this.Attempts = attempts;
163  this.Options = options;
164  this.Descriptions = descriptions;
165  this.DefaultRetry = prompt;
166  if (promptStyler == null)
167  {
168  promptStyler = new PromptStyler();
169  }
170  this.PromptStyler = promptStyler;
171  }
172  }
173 
177  [Serializable]
178  public class PromptStyler
179  {
183  public readonly PromptStyle PromptStyle;
184 
185  public PromptStyler(PromptStyle promptStyle = PromptStyle.Auto)
186  {
187  this.PromptStyle = promptStyle;
188  }
189 
199  public static void Apply<T>(ref IMessageActivity message, string prompt, IReadOnlyList<T> options, PromptStyle promptStyle, IReadOnlyList<string> descriptions = null)
200  {
201  var styler = new PromptStyler(promptStyle);
202  styler.Apply(ref message, prompt, options, descriptions);
203  }
204 
210  public virtual void Apply(ref IMessageActivity message, string prompt)
211  {
212  SetField.CheckNull(nameof(prompt), prompt);
213  message.Text = prompt;
214  }
215 
227  public virtual void Apply<T>(ref IMessageActivity message, string prompt, IReadOnlyList<T> options, IReadOnlyList<string> descriptions = null)
228  {
229  SetField.CheckNull(nameof(prompt), prompt);
230  SetField.CheckNull(nameof(options), options);
231  if (descriptions == null)
232  {
233  descriptions = (from option in options select option.ToString()).ToList();
234  }
235  switch (PromptStyle)
236  {
237  case PromptStyle.Auto:
238  case PromptStyle.Keyboard:
239  if (options != null && options.Any())
240  {
241  if (PromptStyle == PromptStyle.Keyboard)
242  {
243  message.AddKeyboardCard(prompt, options, descriptions);
244  }
245  else
246  {
247  message.AddHeroCard(prompt, options, descriptions);
248  }
249  }
250  else
251  {
252  message.Text = prompt;
253  }
254  break;
255  case PromptStyle.AutoText:
256  Apply(ref message, prompt, options, options?.Count() > 4 ? PromptStyle.PerLine : PromptStyle.Inline, descriptions);
257  break;
258  case PromptStyle.Inline:
259  //TODO: Refactor buildlist function to a more generic namespace when changing prompt to use recognizers.
260  message.Text = $"{prompt} {FormFlow.Advanced.Language.BuildList(descriptions, Resources.DefaultChoiceSeparator, Resources.DefaultChoiceLastSeparator)}";
261  break;
262  case PromptStyle.PerLine:
263  message.Text = $"{prompt}{Environment.NewLine}{FormFlow.Advanced.Language.BuildList(descriptions.Select(description => $"* {description}"), Environment.NewLine, Environment.NewLine)}";
264  break;
265  case PromptStyle.None:
266  default:
267  message.Text = prompt;
268  break;
269  }
270  }
271  }
272 
275  public class PromptDialog
276  {
283  public static void Text(IDialogContext context, ResumeAfter<string> resume, string prompt, string retry = null, int attempts = 3)
284  {
285  var child = new PromptString(prompt, retry, attempts);
286  context.Call<string>(child, resume);
287  }
288 
296  public static void Confirm(IDialogContext context, ResumeAfter<bool> resume, string prompt, string retry = null, int attempts = 3, PromptStyle promptStyle = PromptStyle.Auto)
297  {
298  Confirm(context, resume, new PromptOptions<string>(prompt, retry, attempts: attempts, options: PromptConfirm.Options.ToList(), promptStyler: new PromptStyler(promptStyle: promptStyle)));
299  }
300 
307  public static void Confirm(IDialogContext context, ResumeAfter<bool> resume, PromptOptions<string> promptOptions)
308  {
309  var child = new PromptConfirm(promptOptions);
310  context.Call<bool>(child, resume);
311  }
312 
319  public static void Number(IDialogContext context, ResumeAfter<long> resume, string prompt, string retry = null, int attempts = 3)
320  {
321  var child = new PromptInt64(prompt, retry, attempts);
322  context.Call<long>(child, resume);
323  }
324 
331  public static void Number(IDialogContext context, ResumeAfter<double> resume, string prompt, string retry = null, int attempts = 3)
332  {
333  var child = new PromptDouble(prompt, retry, attempts);
334  context.Call<double>(child, resume);
335  }
336 
346  public static void Choice<T>(IDialogContext context, ResumeAfter<T> resume, IEnumerable<T> options, string prompt, string retry = null, int attempts = 3, PromptStyle promptStyle = PromptStyle.Auto, IEnumerable<string> descriptions = null)
347  {
348  Choice(context, resume, new PromptOptions<T>(prompt, retry, attempts: attempts, options: options.ToList(), promptStyler: new PromptStyler(promptStyle), descriptions: descriptions?.ToList()));
349  }
350 
359  public static void Choice<T>(IDialogContext context, ResumeAfter<T> resume, PromptOptions<T> promptOptions)
360  {
361  var child = new PromptChoice<T>(promptOptions);
362  context.Call<T>(child, resume);
363  }
364 
374  public static void Attachment(IDialogContext context, ResumeAfter<IEnumerable<Attachment>> resume, string prompt, IEnumerable<string> contentTypes = null, string retry = null, int attempts = 3)
375  {
376  var child = new PromptAttachment(prompt, retry, attempts, contentTypes);
377  context.Call<IEnumerable<Attachment>>(child, resume);
378  }
379 
382  [Serializable]
383  public sealed class PromptString : Prompt<string, string>
384  {
389  public PromptString(string prompt, string retry, int attempts)
390  : this(new PromptOptions<string>(prompt, retry, attempts: attempts)) { }
391 
394  public PromptString(PromptOptions<string> promptOptions)
395  : base(promptOptions)
396  {
397  this.promptOptions.DefaultRetry = this.DefaultRetry;
398  }
399 
400  protected override bool TryParse(IMessageActivity message, out string result)
401  {
402  if (!string.IsNullOrWhiteSpace(message.Text))
403  {
404  result = message.Text;
405  return true;
406  }
407  else
408  {
409  result = null;
410  return false;
411  }
412  }
413 
414  public string DefaultRetry
415  {
416  get
417  {
418  return Resources.PromptRetry + Environment.NewLine + this.promptOptions.Prompt;
419  }
420  }
421  }
422 
425  [Serializable]
426  public sealed class PromptConfirm : Prompt<bool, string>
427  {
431  public const int Yes = 0;
432 
436  public const int No = 1;
437 
441  public static string[] Options { set; get; } = { Resources.MatchYes.SplitList().First(), Resources.MatchNo.SplitList().First() };
442 
446  public static string[][] Patterns { get; set; } = { Resources.MatchYes.SplitList(), Resources.MatchNo.SplitList() };
447 
453  public PromptConfirm(string prompt, string retry, int attempts, PromptStyle promptStyle = PromptStyle.Auto)
454  : this(new PromptOptions<string>(prompt, retry, attempts: attempts, options: Options.ToList(), promptStyler: new PromptStyler(promptStyle)))
455  {
456  }
457 
462  public PromptConfirm(PromptOptions<string> promptOptions)
463  : base(promptOptions)
464  {
465  this.promptOptions.DefaultRetry = this.DefaultRetry;
466  }
467 
468 
469  protected override bool TryParse(IMessageActivity message, out bool result)
470  {
471  var found = false;
472  result = false;
473  if (message.Text != null)
474  {
475  var term = message.Text.Trim().ToLower();
476  if ((from r in Patterns[Yes] select r.ToLower()).Contains(term))
477  {
478  result = true;
479  found = true;
480  }
481  else if ((from r in Patterns[No] select r.ToLower()).Contains(term))
482  {
483  result = false;
484  found = true;
485  }
486  }
487  return found;
488  }
489 
490  public string DefaultRetry
491  {
492  get
493  {
494  return Resources.PromptRetry + Environment.NewLine + this.promptOptions.Prompt;
495  }
496  }
497  }
498 
501  [Serializable]
502  public sealed class PromptInt64 : Prompt<long, long>
503  {
508  public PromptInt64(string prompt, string retry, int attempts)
509  : this(new PromptOptions<long>(prompt, retry, attempts: attempts)) { }
510 
513  public PromptInt64(PromptOptions<long> promptOptions)
514  : base(promptOptions) { }
515 
516  protected override bool TryParse(IMessageActivity message, out Int64 result)
517  {
518  return Int64.TryParse(message.Text, out result);
519  }
520  }
521 
524  [Serializable]
525  public sealed class PromptDouble : Prompt<double, double>
526  {
531  public PromptDouble(string prompt, string retry, int attempts)
532  : this(new PromptOptions<double>(prompt, retry, attempts: attempts)) { }
533 
536  public PromptDouble(PromptOptions<double> promptOptions)
537  : base(promptOptions) { }
538 
539  protected override bool TryParse(IMessageActivity message, out double result)
540  {
541  return double.TryParse(message.Text, out result);
542  }
543  }
544 
547  [Serializable]
548  public class PromptChoice<T> : Prompt<T, T>
549  {
557  public PromptChoice(IEnumerable<T> options, string prompt, string retry, int attempts, PromptStyle promptStyle = PromptStyle.Auto, IEnumerable<string> descriptions = null)
558  : this(new PromptOptions<T>(prompt, retry, options: options.ToList(), attempts: attempts, promptStyler: new PromptStyler(promptStyle), descriptions: descriptions?.ToList()))
559  {
560  }
561 
566  public PromptChoice(PromptOptions<T> promptOptions)
567  : base(promptOptions)
568  {
569  SetField.CheckNull(nameof(promptOptions.Options), promptOptions.Options);
570  }
571 
572  public virtual Tuple<bool, int> ScoreMatch(T option, string input)
573  {
574  var trimmed = input.Trim();
575  var text = option.ToString();
576  bool occurs = text.IndexOf(trimmed, StringComparison.CurrentCultureIgnoreCase) >= 0;
577  bool equals = text == trimmed;
578  return occurs
579  ? Tuple.Create(equals, trimmed.Length)
580  : null;
581  }
582 
583  protected override bool TryParse(IMessageActivity message, out T result)
584  {
585  if (!string.IsNullOrWhiteSpace(message.Text))
586  {
587  var scores = from option in this.promptOptions.Options
588  let score = ScoreMatch(option, message.Text)
589  select new { score, option };
590 
591  var winner = scores.MaxBy(s => s.score);
592  if (winner.score != null)
593  {
594  result = winner.option;
595  return true;
596  }
597  }
598 
599  result = default(T);
600  return false;
601  }
602  }
603 
606  [Serializable]
607  public sealed class PromptAttachment : Prompt<IEnumerable<Attachment>, Attachment>
608  {
609  public IEnumerable<string> ContentTypes
610  {
611  get;
612  private set;
613  }
614 
620  public PromptAttachment(string prompt, string retry, int attempts, IEnumerable<string> contentTypes = null)
621  : base(new PromptOptions<Attachment>(prompt, retry, attempts: attempts))
622  {
623  this.ContentTypes = contentTypes ?? new List<string>();
624  }
625 
626  protected override bool TryParse(IMessageActivity message, out IEnumerable<Attachment> result)
627  {
628  if (message.Attachments != null && message.Attachments.Any())
629  {
630  // Retrieve attachments corresponding to content types if any
631  result = ContentTypes.Any() ? message.Attachments.Join(ContentTypes, a => a.ContentType, c => c, (a, c) => a)
632  : message.Attachments;
633  return result != null && result.Any();
634  }
635  else
636  {
637  result = null;
638  return false;
639  }
640  }
641  }
642 
643  }
644 
645  public static partial class Extensions
646  {
658  public static void AddHeroCard<T>(this IMessageActivity message, string text, IEnumerable<T> options, IEnumerable<string> descriptions = null)
659  {
660  message.AttachmentLayout = AttachmentLayoutTypes.List;
661  message.Attachments = options.GenerateHeroCard(text, descriptions);
662  }
663 
675  public static void AddKeyboardCard<T>(this IMessageActivity message, string text, IEnumerable<T> options,
676  IEnumerable<string> descriptions = null)
677  {
678  message.AttachmentLayout = AttachmentLayoutTypes.List;
679  message.Attachments = options.GenerateKeyboardCard(text, descriptions);
680  }
681 
682  internal static IList<Attachment> GenerateHeroCard<T>(this IEnumerable<T> options, string text, IEnumerable<string> descriptions = null)
683  {
684  var attachments = new List<Attachment>
685  {
686  new HeroCard(text: text, buttons: GenerateButtons(options, descriptions)).ToAttachment()
687  };
688 
689  return attachments;
690  }
691 
692  internal static IList<Attachment> GenerateKeyboardCard<T>(this IEnumerable<T> options, string text, IEnumerable<string> descriptions = null)
693  {
694  var attachments = new List<Attachment>
695  {
696  new KeyboardCard(text: text, buttons: GenerateButtons(options, descriptions)).ToAttachment()
697  };
698 
699  return attachments;
700  }
701 
702  internal static IList<CardAction> GenerateButtons<T>(IEnumerable<T> options,
703  IEnumerable<string> descriptions = null)
704  {
705  var actions = new List<CardAction>();
706  int i = 0;
707  var adescriptions = descriptions?.ToArray();
708  foreach (var option in options)
709  {
710  var title = (adescriptions == null ? option.ToString() : adescriptions[i]);
711  actions.Add(new CardAction
712  {
713  Title = title,
714  Type = ActionTypes.ImBack,
715  Value = option.ToString()
716  });
717  ++i;
718  }
719  return actions;
720  }
721  }
722 }
723 
724 namespace Microsoft.Bot.Builder.Dialogs.Internals
725 {
726 
727  [Serializable]
728  public abstract class Prompt<T, U> : IDialog<T>
729  {
730  protected readonly PromptOptions<U> promptOptions;
731 
732  public Prompt(PromptOptions<U> promptOptions)
733  {
734  SetField.NotNull(out this.promptOptions, nameof(promptOptions), promptOptions);
735 
736  }
737 
738  async Task IDialog<T>.StartAsync(IDialogContext context)
739  {
740  await context.PostAsync(this.MakePrompt(context, promptOptions.Prompt, promptOptions.Options, promptOptions.Descriptions));
741  context.Wait(MessageReceivedAsync);
742  }
743 
744  protected virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> message)
745  {
746  T result;
747  if (this.TryParse(await message, out result))
748  {
749  context.Done(result);
750  }
751  else
752  {
753  --promptOptions.Attempts;
754  if (promptOptions.Attempts >= 0)
755  {
756  await context.PostAsync(this.MakePrompt(context, promptOptions.Retry ?? promptOptions.DefaultRetry, promptOptions.Options, promptOptions.Descriptions));
757  context.Wait(MessageReceivedAsync);
758  }
759  else
760  {
761  //too many attempts, throw.
762  await context.PostAsync(this.MakePrompt(context, promptOptions.TooManyAttempts));
763  throw new TooManyAttemptsException(promptOptions.TooManyAttempts);
764  }
765  }
766  }
767 
768  protected abstract bool TryParse(IMessageActivity message, out T result);
769 
770  protected virtual IMessageActivity MakePrompt(IDialogContext context, string prompt, IReadOnlyList<U> options = null, IReadOnlyList<string> descriptions = null)
771  {
772  var msg = context.MakeMessage();
773  if (options != null && options.Count > 0)
774  {
775  promptOptions.PromptStyler.Apply(ref msg, prompt, options, descriptions);
776  }
777  else
778  {
779  promptOptions.PromptStyler.Apply(ref msg, prompt);
780  }
781  return msg;
782  }
783  }
784 }
Task StartAsync(IDialogContext context)
The start of the code that represents the conversational dialog.
static void Number(IDialogContext context, ResumeAfter< long > resume, string prompt, string retry=null, int attempts=3)
Prompt for a long.
static void Attachment(IDialogContext context, ResumeAfter< IEnumerable< Attachment >> resume, string prompt, IEnumerable< string > contentTypes=null, string retry=null, int attempts=3)
Prompt for an attachment
The context for the execution of a dialog&#39;s conversational process.
PromptStyler(PromptStyle promptStyle=PromptStyle.Auto)
A strongly-typed resource class, for looking up localized strings, etc.
virtual Tuple< bool, int > ScoreMatch(T option, string input)
Namespace for Microsoft Bot Builder SDK extensions to the Microsoft Bot Connector SDK...
Definition: KeyboardCard.cs:40
static string TooManyAttempts
Looks up a localized string similar to too many attempts.
Namespace for the Microsoft Bot Connector SDK.
readonly IReadOnlyList< T > Options
The choices to be returned when selected.
static string PromptRetry
Looks up a localized string similar to I didn't understand. Say something in reply..
string Text
Content for the message
static void Text(IDialogContext context, ResumeAfter< string > resume, string prompt, string retry=null, int attempts=3)
Prompt for a string.
readonly string Prompt
The prompt.
Definition: PromptDialog.cs:98
Explicit interface to support the compiling of async/await.
Definition: Awaitable.cs:55
readonly string TooManyAttempts
What to display when user didn&#39;t say a valid response after Attempts.
PromptChoice(PromptOptions< T > promptOptions)
Constructs a choice dialog.
Do not show possible choices in the prompt
The exception representing too many attempts by the user to answer the question asked by the prompt...
virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable< IMessageActivity > message)
string DefaultRetry
Default retry prompt that is used if Retry is null.
static void Confirm(IDialogContext context, ResumeAfter< bool > resume, string prompt, string retry=null, int attempts=3, PromptStyle promptStyle=PromptStyle.Auto)
Ask a yes/no question.
Prompt for a choice from a set of choices.
PromptString(PromptOptions< string > promptOptions)
Constructor for a prompt string dialog.
virtual void Apply(ref IMessageActivity message, string prompt)
Style a prompt and populate the IMessageActivity.Text.
Namespace for internal machinery that is not useful for most developers and may change in the future...
PromptString(string prompt, string retry, int attempts)
Constructor for a prompt string dialog.
A IDialog<TResult> is a suspendable conversational process that produces a result of type TResult ...
Definition: IDialog.cs:47
Namespace for resources.
Generate keyboard card for choices that will be mapped to a HeroCard or a keyboard, e.g. Facebook quick replies
static string[] Options
The yes, no options for confirmation prompt
Task PostAsync(IMessageActivity message, CancellationToken cancellationToken=default(CancellationToken))
Post a message to be sent to the user.
override bool TryParse(IMessageActivity message, out string result)
const string ImBack
Client will post message to bot, so all other participants will see that was posted to the bot and wh...
Definition: ActionTypes.cs:19
override bool TryParse(IMessageActivity message, out Int64 result)
PromptAttachment(string prompt, string retry, int attempts, IEnumerable< string > contentTypes=null)
Constructor for a prompt attachment dialog.
Generate buttons for choices and let connector generate the right style based on channel capabilities...
PromptStyle
The style of generated prompt
Definition: PromptDialog.cs:49
Namespace for internal Dialogs machinery that is not useful for most developers and may change in the...
static string MatchYes
Looks up a localized string similar to Yes;y;sure;ok;yep;1.
static void Number(IDialogContext context, ResumeAfter< double > resume, string prompt, string retry=null, int attempts=3)
Prompt for a double.
IList< Attachment > Attachments
Attachments
PromptDouble(PromptOptions< double > promptOptions)
Constructor for a prompt double dialog.
PromptConfirm(PromptOptions< string > promptOptions)
Constructor for a prompt confirmation dialog.
An attachment within an activity
Definition: Attachment.cs:17
readonly string Retry
What to display on retry.
PromptConfirm(string prompt, string retry, int attempts, PromptStyle promptStyle=PromptStyle.Auto)
Constructor for a prompt confirmation dialog.
readonly PromptStyle PromptStyle
Style of the prompt Dialogs.PromptStyle.
readonly IReadOnlyList< string > Descriptions
The description of each possible option.
virtual IMessageActivity MakePrompt(IDialogContext context, string prompt, IReadOnlyList< U > options=null, IReadOnlyList< string > descriptions=null)
PromptInt64(string prompt, string retry, int attempts)
Constructor for a prompt int64 dialog.
IMessageActivity MakeMessage()
Make a message.
PromptChoice(IEnumerable< T > options, string prompt, string retry, int attempts, PromptStyle promptStyle=PromptStyle.Auto, IEnumerable< string > descriptions=null)
Constructor for a prompt choice dialog.
readonly PromptOptions< U > promptOptions
override bool TryParse(IMessageActivity message, out IEnumerable< Attachment > result)
override bool TryParse(IMessageActivity message, out bool result)
override bool TryParse(IMessageActivity message, out T result)
override bool TryParse(IMessageActivity message, out double result)
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
Core namespace for Dialogs and associated infrastructure.
PromptInt64(PromptOptions< long > promptOptions)
Constructor for a prompt int64 dialog.
Dialog factory for simple prompts.
PromptOptions(string prompt, string retry=null, string tooManyAttempts=null, IReadOnlyList< T > options=null, int attempts=3, PromptStyler promptStyler=null, IReadOnlyList< string > descriptions=null)
Constructs the prompt options.
A Hero card (card with a single, large image)
Prompt(PromptOptions< U > promptOptions)
readonly PromptStyler PromptStyler
Styler of the prompt Dialogs.PromptStyler.
Root namespace for the Microsoft Bot Builder SDK.
PromptDouble(string prompt, string retry, int attempts)
Constructor for a prompt double dialog.
static void Confirm(IDialogContext context, ResumeAfter< bool > resume, PromptOptions< string > promptOptions)
Ask a yes/no questions.
static string MatchNo
Looks up a localized string similar to No;n;nope;2.