ari icon indicating copy to clipboard operation
ari copied to clipboard

Prompt() with options

Open vieridipaola opened this issue 9 months ago • 5 comments

Hi,

I don't know if this is the right place for general usage questions. First time using this library.

I've tried the example here with play() and it works OK:

https://github.com/CyCoreSystems/ari/blob/main/_examples/play/main.go

Now I'd like to replace Play() with Prompt(), but I'd also like to limit the "interrupt DTMF keys" to a subset of AllDTMF as seen here:

https://github.com/CyCoreSystems/ari/blob/main/ext/play/play.go

say, "01234#". In other words, if there's a match, playback should be immediately interrupted and I should be able to know which digit was pressed.

I think I need to define an Options struct as seen here:

https://github.com/CyCoreSystems/ari/blob/main/ext/play/options.go

and then pass it to Prompt() as 3rd parameter. Then I should be able to get the pressed digit in play.Result.DTMF.

Sorry for the rookie question, but a quick example on how to set the options, pass them to Prompt() and read the pressed DTMF would be very useful.

Regards

[EDIT]

I tried this snippet:

	res, err := play.Prompt(ctx, h, play.URI("sound:googletts/d6fe29440ba3ac62606c8c3e5f93943e"),
		play.MatchHash(), // match any digits until hash
		play.Replays(3),  // repeat prompt up to three times, if no match
	).Result()

	if err != nil {
		log.Error("failed to play sound", "error", err)
		return
	}

	if res.MatchResult == play.Complete {
		log.Info("Got valid, terminated DTMF entry", res.DTMF)
		// hash is automatically trimmed from res.DTMF
	}

I think I need to use MatchDiscrete() for a custom list of digits, right?

[EDIT]

I tried this:

	res, err := play.Prompt(ctx, h,
		play.URI("sound:mysounbd"),
		play.MatchDiscrete([]string{"0", "1", "2", "3", "4", "5", "#"}),
	).Result()

If I press any one of those digits during audio playback, all's well. However, if I hit '9' which isn't in the list the audio playback is interrupted (when it shouldn't according to https://github.com/CyCoreSystems/ari/blob/a3f6a369a00cb6f8a1c569928716680fdffbd7f4/ext/play/options.go#L390) and I get 0 / nil for res.MatchResult.

[EDIT]

Either I'm misusing/misunderstanding the code or there's a bug:

	list := []string{"0", "1", "2", "3", "4", "5", "#"}

	res, err := play.Prompt(ctx, h,
		play.URI("sound:mysound"),
		play.MatchFunc(func(pat string) (string, play.MatchResult) {
			var maxLen int

			for _, t := range list {
				if t == pat {
					return pat, play.Complete
				}

				if len(t) > maxLen {
					maxLen = len(t)
				}
			}

			if len(pat) > maxLen {
				return pat, play.Invalid
			}
			log.Info("MatchFunc will return incomplete or 0", "incomplete", play.Incomplete)
			return pat, 0
			//return pat, play.Incomplete
		}),
	).Result()

The match function returns 0 if I press a digit that does not match "list" (eg. "9"). I'm expecting the audio playback to continue when this happens. Instead, the playback is interrupted. Why is the playback of "sound:mysound" interrupted if MatchFunc returns 0 or play.Incomplete?

vieridipaola avatar Apr 03 '25 17:04 vieridipaola

Prompt() will always terminate the audio when the prompt terminates. If you need to simply ignore digits not of a set, you'll want to use MatchFunc and implement your own system. You could also (with your example, using a '#' terminator), post-process the matched DTMF string to elide the ignored digits.

If I were you, though, I would just implement my own MatchFunc (func(string) (string, MatchResult))).

Ulexus avatar Apr 19 '25 16:04 Ulexus

If I were you, though, I would just implement my own MatchFunc (func(string) (string, MatchResult))).

I did that in my last "EDIT" of my first post. I redefined play.MatchFunc(func(pat string) (string, play.MatchResult) for Prompt().

What should MatchFunc return if I don't want Prompt() to terminate (thus terminating the audio)?

Thanks

vieridipaola avatar Apr 27 '25 21:04 vieridipaola

play.Incomplete: https://pkg.go.dev/github.com/CyCoreSystems/ari/ext/play#MatchResult

This indicates a match is not yet found, so Prompt should not yet terminate.

Ulexus avatar Apr 28 '25 13:04 Ulexus

play.Incomplete is 0, right?

If so, this is why I was asking the following question in my first post:

Why is the playback of "sound:mysound" interrupted if MatchFunc returns 0 or play.Incomplete?

I'm repasting the code here for clarity:

	list := []string{"0", "1", "2", "3", "4", "5", "#"}

	res, err := play.Prompt(ctx, h,
		play.URI("sound:mysound"),
		play.MatchFunc(func(pat string) (string, play.MatchResult) {
			var maxLen int

			for _, t := range list {
				if t == pat {
					return pat, play.Complete
				}

				if len(t) > maxLen {
					maxLen = len(t)
				}
			}

			if len(pat) > maxLen {
				return pat, play.Invalid
			}
			log.Info("MatchFunc will return incomplete or 0", "incomplete", play.Incomplete)
			return pat, 0
			//return pat, play.Incomplete
		}),
	).Result()

vieridipaola avatar May 01 '25 17:05 vieridipaola

Hmm... yeah, that's definitely how it is supposed to work: play.Incomplete means that the playback sequence does not get signaled to stop the playback, so it should continue playing.

For a sanity check, can you play a sequence of multiple files, use play.MatchNone(), and see what happens?

Ulexus avatar May 01 '25 19:05 Ulexus