Skip to content

Instantly share code, notes, and snippets.

@raarts
Last active July 20, 2020 11:02
Show Gist options
  • Save raarts/de06767d21e8edc9ea530dbc0ba994f1 to your computer and use it in GitHub Desktop.
Save raarts/de06767d21e8edc9ea530dbc0ba994f1 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
const debug = console.log;
let neemopGuards;
const buildGuards = (a) => a;
let neemopActions;
const buildActions = (a) => a;
let neemopServices;
const buildServices = (a) => a;
let neemopDelays;
const buildDelays = (a) => a;
let neemopContext;
const buildContext = (a) => a;
let neemopConfig;
const buildConfig = (a) => a;
let neemopOptions;
const buildOptions = (a) => a;
const play = async (context, prompt) => {
debug(`${prompt} (${prompt.length*200/1000}s)`);
return new Promise(function (resolve) {
setTimeout(resolve.bind(null, context), prompt.length * 200);
});
};
// eslint-disable-next-line prefer-const
neemopGuards = {
noNumber: (context) => context.number === '',
invalidNumber: (context) => (context.number && context.number.length > 0 && context.number.length < 10) || false,
maxRetry: (context) => (context.retry && context.retry >= 3) || false,
isMorning: () => true,
};
const guardList = buildGuards(neemopGuards);
// eslint-disable-next-line prefer-const
neemopActions = {
incRetry: assign({ retry: (context) => context.retry + 1 }),
clearRetry: assign({ retry: () => 0 }),
addDigit: assign({ number: (context, event) => context.number + event.value }),
clearNumber: assign({ number: () => '' }),
savePlaybackId: assign({ playbackId: (context, event) => event.value }),
clearPlaybackId: assign({ playbackId: () => '' }),
stopPlayback: (context) => {
if (context.playbackId !== '') {
ari.playbacks
.stop({ playbackId: context.playbackId || '' })
.then(() => {
// ignore this
})
.catch((e) => {
debug('canceling playback:', e);
});
}
},
hangupChannel: (context) => ari.channels.hangup({ channelId: context.channel?.id || '' }).then(),
onEntryOffline: (context) => {
context.channel?.on('ChannelDtmfReceived', ChannelDtmfReceived);
context.channel?.on('ChannelStateChange', ChannelStateChange);
context.channel?.on('ChannelHangupRequest', ChannelHangupRequest);
context.channel?.answer().then();
},
logDialing: (context) => debug('dialing:', context.number),
logEvent: (context, event) => debug('event:', event),
};
const actionList = buildActions(neemopActions);
// eslint-disable-next-line prefer-const
neemopServices = {
playWelcome: (context) => play(context, 'neemop/Goedemorgen'),
playEnterNumberToDial: (context) => play(context, 'neemop/toets_het_telefoonnr_dat_u_wilt_bellen'),
playNoValidInputReceived: (context) => play(context, 'neemop/geen_geldige_invoer_ontvangen'),
playNumberCannotBeDialed: (context) => play(context, 'neemop/dit_nummer_kan_niet_worden_gebeld'),
playLineWillBeDisconnected: (context) => play(context, 'neemop/de_verbinding_wordt_nu_verbroken'),
};
const serviceList = buildServices(neemopServices);
// eslint-disable-next-line prefer-const
neemopDelays = {
DIGIT_TIMEOUT: 200,
RESPONSE_TIMEOUT: 5000,
DIAL_TIMEOUT: 60000,
ERROR_TIMEOUT: 60000,
};
const delayList = buildDelays(neemopDelays);
// eslint-disable-next-line prefer-const
neemopContext = {
channel: null,
args: null,
playbackId: '',
number: '',
retry: 0,
};
const context = buildContext(neemopContext);
// eslint-disable-next-line prefer-const
neemopOptions = {
guards: guardList,
actions: actionList,
services: serviceList,
delays: delayList,
};
const options = buildOptions(neemopOptions);
// eslint-disable-next-line prefer-const
neemopConfig = {
id: 'neemop',
context,
initial: 'offline',
states: {
offline: {
entry: ['onEntryOffline'],
on: {
ANSWER: 'online',
},
},
done: {
id: 'done',
type: 'final',
},
online: {
initial: 'welcome',
states: {
welcome: {
invoke: {
src: 'playWelcome',
},
on: {
PLAYBACK_FINISHED: { target: '#getnumbergroup', actions: 'clearPlaybackId' },
DIGIT: {
actions: ['stopPlayback', 'addDigit'],
},
HASH: {
actions: ['stopPlayback'],
},
STAR: {
actions: ['stopPlayback'],
},
},
after: {
ERROR_TIMEOUT: '#telldisconnect',
},
},
getnumbergroup: {
id: 'getnumbergroup',
initial: 'getnumberwithprompt',
states: {
novalidinputreceived: {
id: 'novalidinputreceived',
entry: ['clearNumber'],
invoke: { src: 'playNoValidInputReceived' },
on: {
DIGIT: {
actions: ['stopPlayback', 'addDigit'],
target: '#getnumberwithoutprompt',
},
HASH: {
actions: ['stopPlayback'],
},
STAR: {
actions: ['stopPlayback', 'clearNumber'],
},
PLAYBACK_FINISHED: [
{
cond: 'maxRetry',
target: '#telldisconnect',
},
{
actions: 'clearPlaybackId',
target: '#getnumberwithprompt',
},
],
},
after: {
ERROR_TIMEOUT: '#telldisconnect',
},
},
thisnumbercannotbedialed: {
id: 'thisnumbercannotbedialed',
entry: ['clearNumber'],
invoke: { src: 'playNumberCannotBeDialed' },
on: {
DIGIT: {
actions: ['stopPlayback', 'addDigit'],
target: '#getnumberwithoutprompt',
},
HASH: {
actions: ['stopPlayback'],
},
STAR: {
actions: ['stopPlayback', 'clearNumber'],
},
PLAYBACK_FINISHED: [
{
cond: 'maxRetry',
target: '#telldisconnect',
},
{
actions: 'clearPlaybackId',
target: '#getnumberwithprompt',
},
],
},
after: {
ERROR_TIMEOUT: '#telldisconnect',
},
},
getnumberwithprompt: {
id: 'getnumberwithprompt',
invoke: { src: 'playEnterNumberToDial' },
on: {
DIGIT: {
actions: ['stopPlayback', 'addDigit'],
target: '#getnumberwithoutprompt',
},
HASH: {
actions: ['incRetry'],
target: '#novalidinputreceived',
},
PLAYBACK_FINISHED: { target: '#getnumberwithoutprompt', actions: 'clearPlaybackId' },
},
after: {
ERROR_TIMEOUT: '#telldisconnect',
},
},
getnumberwithoutprompt: {
id: 'getnumberwithoutprompt',
on: {
DIGIT: {
actions: ['addDigit'],
target: '#getnumberwithoutprompt',
},
HASH: [
{
cond: 'noNumber',
actions: ['incRetry'],
target: '#novalidinputreceived',
},
{
cond: 'invalidNumber',
actions: ['incRetry'],
target: '#thisnumbercannotbedialed',
},
{
target: '#dialing',
},
],
STAR: {
actions: ['clearNumber'],
target: '#getnumberwithprompt',
},
PLAYBACK_FINISHED: { target: '#getnumberwithoutprompt', actions: 'clearPlaybackId' },
},
after: {
RESPONSE_TIMEOUT: [
{
cond: 'maxRetry',
actions: ['playNoValidInputReceived'],
target: '#telldisconnect',
},
{
cond: 'noNumber',
actions: ['incRetry'],
target: '#novalidinputreceived',
},
{
cond: 'invalidNumber',
actions: ['incRetry'],
target: '#thisnumbercannotbedialed',
},
{
target: '#dialing',
},
],
},
},
},
},
dialing: {
id: 'dialing',
entry: ['clearRetry', 'logDialing'],
on: {
ANSWER: 'incall',
UNREACHABLE: '#thisnumbercannotbedialed',
},
after: {
DIAL_TIMEOUT: '#telldisconnect',
},
},
incall: {
initial: 'call',
states: {
call: {
on: {
LASTMINUTEWARN: 'lastminute',
},
},
lastminute: {
on: {
LAST30SECWARN: 'last30secs',
},
},
last30secs: {
on: {
NOMONEYLEFT: '#telldisconnect',
},
},
},
},
telldisconnect: {
id: 'telldisconnect',
invoke: { src: 'playLineWillBeDisconnected' },
on: {
PLAYBACK_FINISHED: { actions: 'hangupChannel' },
},
after: {
ERROR_TIMEOUT: { actions: 'hangupChannel' },
},
},
},
on: {
PLAYBACK_STARTED: { actions: 'savePlaybackId' },
HANGUP: '#done',
},
},
},
};
const config = buildConfig(neemopConfig);
const neemopIncomingMachine = Machine(config, options);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment