botframework-webchat-core 4.13.0 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.yml +4 -198
- package/.prettierrc.yml +1 -1
- package/lib/actions/setNotification.js +1 -1
- package/lib/createPromiseQueue.js +1 -1
- package/lib/createStore.d.ts +3 -2
- package/lib/createStore.d.ts.map +1 -0
- package/lib/createStore.js +5 -5
- package/lib/index.d.ts +50 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +34 -6
- package/lib/reducer.d.ts +1 -0
- package/lib/reducer.d.ts.map +1 -0
- package/lib/reducers/activities.js +1 -1
- package/lib/reducers/clockSkewAdjustment.js +1 -1
- package/lib/reducers/lastTypingAt.js +1 -1
- package/lib/reducers/notifications.js +21 -14
- package/lib/reducers/typing.js +1 -1
- package/lib/sagas/connectSaga.js +14 -8
- package/lib/sagas/connectionStatusToNotificationSaga.js +1 -1
- package/lib/sagas/effects/forkPut.js +1 -1
- package/lib/sagas/markAllAsSpokenOnStopSpeakActivitySaga.js +1 -1
- package/lib/sagas/observeActivitySaga.js +1 -1
- package/lib/sagas/postActivitySaga.js +39 -32
- package/lib/sagas/queueIncomingActivitySaga.js +7 -3
- package/lib/sagas/sendMessageBackToPostActivitySaga.js +4 -4
- package/lib/sagas/sendPostBackToPostActivitySaga.js +4 -4
- package/lib/sagas/sendTypingIndicatorOnSetSendBoxSaga.js +1 -1
- package/lib/sagas/speakActivityAndStartDictateOnIncomingActivityFromOthersSaga.js +1 -1
- package/lib/sagas/startSpeakActivityOnPostActivitySaga.js +1 -1
- package/lib/sagas/stopSpeakingActivityOnInputSaga.js +1 -1
- package/lib/sagas/submitSendBoxSaga.js +1 -1
- package/lib/selectors/combineSelectors.js +8 -3
- package/lib/types/OneOrMany.d.ts +3 -0
- package/lib/types/OneOrMany.d.ts.map +1 -0
- package/lib/types/OneOrMany.js +2 -0
- package/lib/types/external/DirectLineActivity.d.ts +3 -0
- package/lib/types/external/DirectLineActivity.d.ts.map +1 -0
- package/lib/types/external/DirectLineActivity.js +2 -0
- package/lib/types/external/DirectLineAnimationCard.d.ts +3 -0
- package/lib/types/external/DirectLineAnimationCard.d.ts.map +1 -0
- package/lib/types/external/DirectLineAnimationCard.js +2 -0
- package/lib/types/external/DirectLineAttachment.d.ts +3 -0
- package/lib/types/external/DirectLineAttachment.d.ts.map +1 -0
- package/lib/types/external/DirectLineAttachment.js +2 -0
- package/lib/types/external/DirectLineAudioCard.d.ts +3 -0
- package/lib/types/external/DirectLineAudioCard.d.ts.map +1 -0
- package/lib/types/external/DirectLineAudioCard.js +2 -0
- package/lib/types/external/DirectLineCardAction.d.ts +110 -0
- package/lib/types/external/DirectLineCardAction.d.ts.map +1 -0
- package/lib/types/external/DirectLineCardAction.js +2 -0
- package/lib/types/external/DirectLineHeroCard.d.ts +3 -0
- package/lib/types/external/DirectLineHeroCard.d.ts.map +1 -0
- package/lib/types/external/DirectLineHeroCard.js +2 -0
- package/lib/types/external/DirectLineJSBotConnection.d.ts +3 -0
- package/lib/types/external/DirectLineJSBotConnection.d.ts.map +1 -0
- package/lib/types/external/DirectLineJSBotConnection.js +2 -0
- package/lib/types/external/DirectLineOAuthCard.d.ts +3 -0
- package/lib/types/external/DirectLineOAuthCard.d.ts.map +1 -0
- package/lib/types/external/DirectLineOAuthCard.js +2 -0
- package/lib/types/external/DirectLineReceiptCard.d.ts +3 -0
- package/lib/types/external/DirectLineReceiptCard.d.ts.map +1 -0
- package/lib/types/external/DirectLineReceiptCard.js +2 -0
- package/lib/types/external/DirectLineSignInCard.d.ts +3 -0
- package/lib/types/external/DirectLineSignInCard.d.ts.map +1 -0
- package/lib/types/external/DirectLineSignInCard.js +2 -0
- package/lib/types/external/DirectLineSuggestedAction.d.ts +3 -0
- package/lib/types/external/DirectLineSuggestedAction.d.ts.map +1 -0
- package/lib/types/external/DirectLineSuggestedAction.js +2 -0
- package/lib/types/external/DirectLineThumbnailCard.d.ts +3 -0
- package/lib/types/external/DirectLineThumbnailCard.d.ts.map +1 -0
- package/lib/types/external/DirectLineThumbnailCard.js +2 -0
- package/lib/types/external/DirectLineVideoCard.d.ts +3 -0
- package/lib/types/external/DirectLineVideoCard.d.ts.map +1 -0
- package/lib/types/external/DirectLineVideoCard.js +2 -0
- package/lib/utils/dateToLocaleISOString.js +37 -0
- package/lib/utils/deleteKey.js +2 -3
- package/lib/utils/isForbiddenPropertyName.d.ts +2 -0
- package/lib/utils/isForbiddenPropertyName.d.ts.map +1 -0
- package/lib/utils/isForbiddenPropertyName.js +21 -0
- package/lib/utils/mime-wrapper.js +2 -4
- package/lib/utils/once.d.ts +2 -0
- package/lib/utils/once.d.ts.map +1 -0
- package/lib/utils/once.js +17 -0
- package/lib/utils/singleToArray.d.ts +2 -0
- package/lib/utils/singleToArray.d.ts.map +1 -0
- package/lib/utils/singleToArray.js +11 -0
- package/lib/utils/uniqueID.js +1 -1
- package/lib/utils/warnOnce.d.ts +2 -0
- package/lib/utils/warnOnce.d.ts.map +1 -0
- package/lib/utils/warnOnce.js +17 -0
- package/package.json +19 -22
- package/src/__tests__/detectSlowConnectionSaga.spec.js +1 -1
- package/src/__tests__/observeOnce.spec.js +3 -3
- package/src/actions/setNotification.js +8 -2
- package/src/createStore.ts +6 -6
- package/src/{index.js → index.ts} +40 -4
- package/src/reducers/notifications.js +22 -16
- package/src/sagas/connectSaga.js +9 -2
- package/src/sagas/connectionStatusToNotificationSaga.js +1 -1
- package/src/sagas/postActivitySaga.js +11 -4
- package/src/sagas/queueIncomingActivitySaga.js +40 -39
- package/src/sagas/sendMessageBackToPostActivitySaga.js +2 -3
- package/src/sagas/sendPostBackToPostActivitySaga.js +2 -3
- package/src/sagas/sendTypingIndicatorOnSetSendBoxSaga.js +1 -1
- package/src/sagas/speakActivityAndStartDictateOnIncomingActivityFromOthersSaga.js +1 -1
- package/src/sagas/startSpeakActivityOnPostActivitySaga.js +1 -1
- package/src/sagas/stopSpeakingActivityOnInputSaga.js +1 -1
- package/src/sagas/submitSendBoxSaga.js +1 -1
- package/src/selectors/combineSelectors.js +9 -1
- package/src/tsconfig.json +7 -3
- package/src/types/OneOrMany.ts +3 -0
- package/src/types/external/DirectLineActivity.ts +7 -0
- package/src/types/external/DirectLineAnimationCard.ts +5 -0
- package/src/types/external/DirectLineAttachment.ts +5 -0
- package/src/types/external/DirectLineAudioCard.ts +5 -0
- package/src/types/external/DirectLineCardAction.ts +128 -0
- package/src/types/external/DirectLineHeroCard.ts +5 -0
- package/src/types/external/DirectLineJSBotConnection.ts +5 -0
- package/src/types/external/DirectLineOAuthCard.ts +5 -0
- package/src/types/external/DirectLineReceiptCard.ts +5 -0
- package/src/types/external/DirectLineSignInCard.ts +5 -0
- package/src/types/external/DirectLineSuggestedAction.ts +5 -0
- package/src/types/external/DirectLineThumbnailCard.ts +5 -0
- package/src/types/external/DirectLineVideoCard.ts +5 -0
- package/src/utils/dateToLocaleISOString.chatham.spec.js +14 -0
- package/src/utils/dateToLocaleISOString.japan.spec.js +14 -0
- package/src/utils/dateToLocaleISOString.js +34 -0
- package/src/utils/dateToLocaleISOString.pacific.spec.js +14 -0
- package/src/utils/dateToLocaleISOString.utc.spec.js +22 -0
- package/src/utils/deleteKey.js +1 -3
- package/src/utils/isForbiddenPropertyName.spec.js +6 -0
- package/src/utils/isForbiddenPropertyName.ts +33 -0
- package/src/utils/mime-wrapper.js +1 -2
- package/src/utils/once.ts +10 -0
- package/src/utils/singleToArray.ts +3 -0
- package/src/utils/uniqueID.js +1 -6
- package/src/utils/warnOnce.ts +5 -0
- package/.eslintignore +0 -9
|
@@ -50,51 +50,52 @@ function* waitForActivityId(replyToId, initialActivities) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function* queueIncomingActivity({ userID }) {
|
|
53
|
-
yield takeEveryAndSelect(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
53
|
+
yield takeEveryAndSelect(
|
|
54
|
+
QUEUE_INCOMING_ACTIVITY,
|
|
55
|
+
activitiesSelector,
|
|
56
|
+
function* queueIncomingActivity({ payload: { activity } }, initialActivities) {
|
|
57
|
+
// This is for resolving an accessibility issue.
|
|
58
|
+
// If the incoming activity has "replyToId" field, hold on it until the activity replied to is in the transcript, then release this one.
|
|
59
|
+
const { replyToId } = activity;
|
|
60
|
+
const initialBotActivities = initialActivities.filter(({ from: { role } }) => role === 'bot');
|
|
61
|
+
|
|
62
|
+
// To speed up the first activity render time, we do not delay the first activity from the bot.
|
|
63
|
+
// Even if it is the first activity from the bot, the bot might be "replying" to the "conversationUpdate" event.
|
|
64
|
+
// Thus, the "replyToId" will always be there even it is the first activity in the conversation.
|
|
65
|
+
if (replyToId && initialBotActivities.length) {
|
|
66
|
+
// Either the activity replied to is in the transcript or after timeout.
|
|
67
|
+
const result = yield race({
|
|
68
|
+
_: waitForActivityId(replyToId, initialActivities),
|
|
69
|
+
timeout: call(sleep, REPLY_TIMEOUT)
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if ('timeout' in result) {
|
|
73
|
+
console.warn(
|
|
74
|
+
`botframework-webchat: Timed out while waiting for activity "${replyToId}" which activity "${activity.id}" is replying to.`,
|
|
75
|
+
{
|
|
76
|
+
activity,
|
|
77
|
+
replyToId
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
}
|
|
80
81
|
}
|
|
81
|
-
}
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
yield put(incomingActivity(activity));
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
// Update suggested actions
|
|
86
|
+
// TODO: [P3] We could put this logic inside reducer to minimize number of actions dispatched.
|
|
87
|
+
const messageActivities = yield select(activitiesOfType('message'));
|
|
88
|
+
const lastMessageActivity = messageActivities[messageActivities.length - 1];
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
if (activityFromBot(lastMessageActivity)) {
|
|
91
|
+
const { suggestedActions: { actions, to } = {} } = lastMessageActivity;
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
// If suggested actions is not destined to anyone, or is destined to the user, show it.
|
|
94
|
+
// In other words, if suggested actions is destined to someone else, don't show it.
|
|
95
|
+
yield put(setSuggestedActions(to && to.length && !to.includes(userID) ? null : actions));
|
|
96
|
+
}
|
|
96
97
|
}
|
|
97
|
-
|
|
98
|
+
);
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
export default function* queueIncomingActivitySaga() {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { put, takeEvery } from 'redux-saga/effects';
|
|
2
|
-
|
|
3
|
-
import { SEND_MESSAGE_BACK } from '../actions/sendMessageBack';
|
|
4
2
|
import postActivity from '../actions/postActivity';
|
|
3
|
+
import { SEND_MESSAGE_BACK } from '../actions/sendMessageBack';
|
|
5
4
|
import whileConnected from './effects/whileConnected';
|
|
6
5
|
|
|
7
|
-
// https://github.com/microsoft/botframework-sdk/blob/
|
|
6
|
+
// https://github.com/microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#message-back
|
|
8
7
|
function* postActivityWithMessageBack({ payload: { displayText, text, value } }) {
|
|
9
8
|
yield put(
|
|
10
9
|
postActivity({
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
/* eslint no-undefined: "off" */
|
|
3
3
|
|
|
4
4
|
import { put, takeEvery } from 'redux-saga/effects';
|
|
5
|
-
|
|
6
|
-
import { SEND_POST_BACK } from '../actions/sendPostBack';
|
|
7
5
|
import postActivity from '../actions/postActivity';
|
|
6
|
+
import { SEND_POST_BACK } from '../actions/sendPostBack';
|
|
8
7
|
import whileConnected from './effects/whileConnected';
|
|
9
8
|
|
|
10
|
-
// https://github.com/microsoft/botframework-sdk/blob/
|
|
9
|
+
// https://github.com/microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#post-back
|
|
11
10
|
function* postActivityWithPostBack({ payload: { value } }) {
|
|
12
11
|
yield put(
|
|
13
12
|
postActivity({
|
|
@@ -34,7 +34,7 @@ function* sendTypingIndicatorOnSetSendBox() {
|
|
|
34
34
|
// When the user type, and then post the activity at t = 1500, we still have a pending typing indicator at t = 3000.
|
|
35
35
|
// This code is to cancel the typing indicator at t = 3000.
|
|
36
36
|
(type === POST_ACTIVITY && payload.activity.type !== 'typing'),
|
|
37
|
-
function*({ payload, type }) {
|
|
37
|
+
function* ({ payload, type }) {
|
|
38
38
|
if (type === SET_SEND_BOX) {
|
|
39
39
|
const interval = SEND_INTERVAL - Date.now() + lastSend;
|
|
40
40
|
|
|
@@ -16,7 +16,7 @@ function* speakActivityAndStartDictateOnIncomingActivityFromOthers({ userID }) {
|
|
|
16
16
|
// In Direct Line Speech, we do not know the user ID, but "role" is filled with "bot" or "user".
|
|
17
17
|
// Here, we do two checks: the speakable activity must not have user ID, and must not have role === 'user'
|
|
18
18
|
type === INCOMING_ACTIVITY && payload.activity.from.id !== userID && payload.activity.from.role !== 'user',
|
|
19
|
-
function*({ payload: { activity } }) {
|
|
19
|
+
function* ({ payload: { activity } }) {
|
|
20
20
|
const shouldSpeakIncomingActivity = yield select(shouldSpeakIncomingActivitySelector);
|
|
21
21
|
const shouldSpeak = speakableActivity(activity) && shouldSpeakIncomingActivity;
|
|
22
22
|
|
|
@@ -8,7 +8,7 @@ function* startSpeakActivityOnPostActivity() {
|
|
|
8
8
|
yield takeEvery(
|
|
9
9
|
({ meta, payload, type }) =>
|
|
10
10
|
type === POST_ACTIVITY_PENDING && meta.method === 'speech' && payload.activity.type === 'message',
|
|
11
|
-
function*() {
|
|
11
|
+
function* () {
|
|
12
12
|
yield put(startSpeakingActivity());
|
|
13
13
|
}
|
|
14
14
|
);
|
|
@@ -14,7 +14,7 @@ function* stopSpeakingActivityOnInput() {
|
|
|
14
14
|
// So, right now, we are using best-effort by listening to POST_ACTIVITY_PENDING with a "message" event
|
|
15
15
|
// We filter out speech because we will call startSpeakingActivity() for POST_ACTIVITY_PENDING dispatched by speech
|
|
16
16
|
(type === POST_ACTIVITY_PENDING && meta.method !== 'speech' && payload.activity.type === 'message'),
|
|
17
|
-
function*() {
|
|
17
|
+
function* () {
|
|
18
18
|
yield put(stopSpeakingActivity());
|
|
19
19
|
}
|
|
20
20
|
);
|
|
@@ -7,7 +7,7 @@ import setSendBox from '../actions/setSendBox';
|
|
|
7
7
|
import whileConnected from './effects/whileConnected';
|
|
8
8
|
|
|
9
9
|
function* submitSendBox() {
|
|
10
|
-
yield takeEvery(SUBMIT_SEND_BOX, function*({ payload: { channelData, method } }) {
|
|
10
|
+
yield takeEvery(SUBMIT_SEND_BOX, function* ({ payload: { channelData, method } }) {
|
|
11
11
|
const sendBoxValue = yield select(sendBoxValueSelector);
|
|
12
12
|
|
|
13
13
|
if (sendBoxValue) {
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
import isForbiddenPropertyName from '../utils/isForbiddenPropertyName';
|
|
2
|
+
|
|
1
3
|
export default function combineSelectors(selectors) {
|
|
2
4
|
if (Array.isArray(selectors)) {
|
|
3
5
|
return state => selectors.reduce((combinedState, selector) => [...combinedState, selector(state)], []);
|
|
4
6
|
}
|
|
5
7
|
|
|
6
8
|
return state =>
|
|
7
|
-
Object.keys(selectors).reduce(
|
|
9
|
+
Object.keys(selectors).reduce(
|
|
10
|
+
(combinedState, key) =>
|
|
11
|
+
// Mitigated through denylisting.
|
|
12
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
13
|
+
isForbiddenPropertyName(key) ? combinedState : { ...combinedState, [key]: selectors[key](state) },
|
|
14
|
+
{}
|
|
15
|
+
);
|
|
8
16
|
}
|
package/src/tsconfig.json
CHANGED
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
"allowSyntheticDefaultImports": true,
|
|
4
4
|
"declaration": true,
|
|
5
5
|
"declarationDir": "../lib",
|
|
6
|
+
"declarationMap": true,
|
|
7
|
+
"downlevelIteration": true,
|
|
6
8
|
"emitDeclarationOnly": true,
|
|
7
|
-
"isolatedModules": true,
|
|
8
9
|
"jsx": "react",
|
|
9
10
|
"preserveWatchOutput": true,
|
|
10
11
|
"pretty": true,
|
|
11
|
-
"
|
|
12
|
-
|
|
12
|
+
"resolveJsonModule": true, // Required for localization files
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"sourceMap": true
|
|
15
|
+
},
|
|
16
|
+
"files": ["index.ts"]
|
|
13
17
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// TODO: [P1] #3953 We should fully type it out.
|
|
2
|
+
|
|
3
|
+
// Until we fully typed out DirectLineActivity, we need to use "any" here.
|
|
4
|
+
// We only know the DirectLineActivity must be a map, and not other primitive types.
|
|
5
|
+
type DirectLineActivity = Exclude<any, [] | boolean | Function | number | string>;
|
|
6
|
+
|
|
7
|
+
export default DirectLineActivity;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
type CardActionWithImageAndTitle =
|
|
2
|
+
| { image: string }
|
|
3
|
+
| { title: string }
|
|
4
|
+
| {
|
|
5
|
+
image: string;
|
|
6
|
+
title: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A `call` action represents a telephone number that may be called.
|
|
11
|
+
*
|
|
12
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#call
|
|
13
|
+
*/
|
|
14
|
+
type CallCardAction = CardActionWithImageAndTitle & {
|
|
15
|
+
type: 'call';
|
|
16
|
+
value: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A `downloadFile` action represents a hyperlink to be downloaded.
|
|
21
|
+
*
|
|
22
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#download-file-actions
|
|
23
|
+
*/
|
|
24
|
+
type DownloadFileCardAction = CardActionWithImageAndTitle & {
|
|
25
|
+
type: 'downloadFile';
|
|
26
|
+
value: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* An `imBack` action represents a text response that is added to the chat feed.
|
|
31
|
+
*
|
|
32
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#im-back
|
|
33
|
+
*/
|
|
34
|
+
type IMBackCardAction = CardActionWithImageAndTitle & {
|
|
35
|
+
type: 'imBack';
|
|
36
|
+
value: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A `messageBack` action represents a text response to be sent via the chat system.
|
|
41
|
+
*
|
|
42
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#message-back
|
|
43
|
+
*/
|
|
44
|
+
type MessageBackCardAction = CardActionWithImageAndTitle & {
|
|
45
|
+
displayText?: string;
|
|
46
|
+
text?: string;
|
|
47
|
+
type: 'messageBack';
|
|
48
|
+
value?: { [key: string]: any };
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* An `openUrl` action represents a hyperlink to be handled by the client.
|
|
53
|
+
*
|
|
54
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#open-url-actions
|
|
55
|
+
*/
|
|
56
|
+
type OpenURLCardAction = CardActionWithImageAndTitle & {
|
|
57
|
+
type: 'openUrl';
|
|
58
|
+
value: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A `playAudio` action represents audio media that may be played.
|
|
63
|
+
*
|
|
64
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#play-audio
|
|
65
|
+
*/
|
|
66
|
+
type PlayAudioCardAction = CardActionWithImageAndTitle & {
|
|
67
|
+
type: 'playAudio';
|
|
68
|
+
value: string;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A `playVideo` action represents video media that may be played.
|
|
73
|
+
*
|
|
74
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#play-video
|
|
75
|
+
*/
|
|
76
|
+
type PlayVideoCardAction = CardActionWithImageAndTitle & {
|
|
77
|
+
type: 'playVideo';
|
|
78
|
+
value: string;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* A `postBack` action represents a text response that is not added to the chat feed.
|
|
83
|
+
*
|
|
84
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#post-back
|
|
85
|
+
*/
|
|
86
|
+
type PostBackCardAction = CardActionWithImageAndTitle & {
|
|
87
|
+
type: 'postBack';
|
|
88
|
+
value: any; // For legacy reason, postBack support any.
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A `showImage` action represents an image that may be displayed.
|
|
93
|
+
*
|
|
94
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#show-image-file-actions
|
|
95
|
+
*/
|
|
96
|
+
type ShowImageCardAction = CardActionWithImageAndTitle & {
|
|
97
|
+
type: 'showImage';
|
|
98
|
+
value: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A `signin` action represents a hyperlink to be handled by the client's signin system.
|
|
103
|
+
*
|
|
104
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#signin
|
|
105
|
+
*/
|
|
106
|
+
type SignInCardAction = CardActionWithImageAndTitle & {
|
|
107
|
+
type: 'signin';
|
|
108
|
+
value: string;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* A card action represents a clickable or interactive button for use within cards or as suggested actions. They are used to solicit input from users. Despite their name, card actions are not limited to use solely on cards.
|
|
113
|
+
*
|
|
114
|
+
* https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#card-action
|
|
115
|
+
*/
|
|
116
|
+
type DirectLineCardAction =
|
|
117
|
+
| CallCardAction
|
|
118
|
+
| DownloadFileCardAction
|
|
119
|
+
| IMBackCardAction
|
|
120
|
+
| MessageBackCardAction
|
|
121
|
+
| OpenURLCardAction
|
|
122
|
+
| PlayAudioCardAction
|
|
123
|
+
| PlayVideoCardAction
|
|
124
|
+
| PostBackCardAction
|
|
125
|
+
| ShowImageCardAction
|
|
126
|
+
| SignInCardAction;
|
|
127
|
+
|
|
128
|
+
export default DirectLineCardAction;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment ../../../__tests__/setup/jestNodeEnvironmentWithTimezone.js
|
|
3
|
+
* @timezone Pacific/Chatham
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import dateToLocaleISOString from './dateToLocaleISOString';
|
|
7
|
+
|
|
8
|
+
test('formatting a time in Chatham Islands timezone', () => {
|
|
9
|
+
// eslint-disable-next-line no-magic-numbers
|
|
10
|
+
const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 567));
|
|
11
|
+
const actual = dateToLocaleISOString(date);
|
|
12
|
+
|
|
13
|
+
expect(actual).toMatchInlineSnapshot(`"2000-01-01T13:57:34.567+13:45"`);
|
|
14
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment ../../../__tests__/setup/jestNodeEnvironmentWithTimezone.js
|
|
3
|
+
* @timezone Asia/Tokyo
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import dateToLocaleISOString from './dateToLocaleISOString';
|
|
7
|
+
|
|
8
|
+
test('formatting a time in Japan timezone', () => {
|
|
9
|
+
// eslint-disable-next-line no-magic-numbers
|
|
10
|
+
const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 567));
|
|
11
|
+
const actual = dateToLocaleISOString(date);
|
|
12
|
+
|
|
13
|
+
expect(actual).toMatchInlineSnapshot(`"2000-01-01T09:12:34.567+09:00"`);
|
|
14
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* eslint no-magic-numbers: ["off", { "ignore": [1, 2, 3, 60] }] */
|
|
2
|
+
|
|
3
|
+
function pad(value, count = 2) {
|
|
4
|
+
if (typeof value !== 'number') {
|
|
5
|
+
throw new Error('First argument must be a number');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
value += '';
|
|
9
|
+
|
|
10
|
+
while (value.length < count) {
|
|
11
|
+
value = '0' + value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Adopted from https://stackoverflow.com/questions/17415579/how-to-iso-8601-format-a-date-with-timezone-offset-in-javascript.
|
|
18
|
+
export default function dateToLocaleISOString(date) {
|
|
19
|
+
if (!(date instanceof Date)) {
|
|
20
|
+
throw new Error('First argument must be a Date object');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const timezoneOffset = -date.getTimezoneOffset();
|
|
24
|
+
const timezoneSign = timezoneOffset < 0 ? '-' : '+';
|
|
25
|
+
|
|
26
|
+
// "yyyy-MM-DDTHH:mm:ss.fff+08:00" for GMT+08
|
|
27
|
+
// "yyyy-MM-DDTHH:mm:ss.fffZ" for UTC
|
|
28
|
+
|
|
29
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(
|
|
30
|
+
date.getMinutes()
|
|
31
|
+
)}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}${
|
|
32
|
+
timezoneOffset ? `${timezoneSign}${pad(~~(Math.abs(timezoneOffset) / 60))}:${pad(timezoneOffset % 60)}` : 'Z'
|
|
33
|
+
}`;
|
|
34
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment ../../../__tests__/setup/jestNodeEnvironmentWithTimezone.js
|
|
3
|
+
* @timezone America/Los_Angeles
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import dateToLocaleISOString from './dateToLocaleISOString';
|
|
7
|
+
|
|
8
|
+
test('formatting a time in Pacific Standard Time timezone', () => {
|
|
9
|
+
// eslint-disable-next-line no-magic-numbers
|
|
10
|
+
const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 567));
|
|
11
|
+
const actual = dateToLocaleISOString(date);
|
|
12
|
+
|
|
13
|
+
expect(actual).toMatchInlineSnapshot(`"1999-12-31T16:12:34.567-08:00"`);
|
|
14
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment ../../../__tests__/setup/jestNodeEnvironmentWithTimezone.js
|
|
3
|
+
* @timezone Etc/UTC
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import dateToLocaleISOString from './dateToLocaleISOString';
|
|
7
|
+
|
|
8
|
+
test('formatting a time in UTC timezone', () => {
|
|
9
|
+
// eslint-disable-next-line no-magic-numbers
|
|
10
|
+
const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 567));
|
|
11
|
+
const actual = dateToLocaleISOString(date);
|
|
12
|
+
|
|
13
|
+
expect(actual).toMatchInlineSnapshot(`"2000-01-01T00:12:34.567Z"`);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('formatting a time in UTC timezone with zero milliseconds', () => {
|
|
17
|
+
// eslint-disable-next-line no-magic-numbers
|
|
18
|
+
const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 0));
|
|
19
|
+
const actual = dateToLocaleISOString(date);
|
|
20
|
+
|
|
21
|
+
expect(actual).toMatchInlineSnapshot(`"2000-01-01T00:12:34.000Z"`);
|
|
22
|
+
});
|
package/src/utils/deleteKey.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^deleted$" }] */
|
|
2
|
-
|
|
3
1
|
export default function deleteKey(map, key) {
|
|
4
2
|
if (!map) {
|
|
5
3
|
return map;
|
|
6
4
|
}
|
|
7
5
|
|
|
8
|
-
const { [key]:
|
|
6
|
+
const { [key]: _deleted, ...nextMap } = map;
|
|
9
7
|
|
|
10
8
|
return nextMap;
|
|
11
9
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import isForbiddenPropertyName from './isForbiddenPropertyName';
|
|
2
|
+
|
|
3
|
+
test('should forbid "__proto__"', () => expect(isForbiddenPropertyName('__proto__')).toBeTruthy());
|
|
4
|
+
test('should forbid "constructor"', () => expect(isForbiddenPropertyName('constructor')).toBeTruthy());
|
|
5
|
+
test('should forbid "prototype"', () => expect(isForbiddenPropertyName('prototype')).toBeTruthy());
|
|
6
|
+
test('should not forbid "abc"', () => expect(isForbiddenPropertyName('abc')).toBeFalsy());
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
let FORBIDDEN_PROPERTY_NAMES;
|
|
2
|
+
|
|
3
|
+
function getForbiddenPropertyNames(): string[] {
|
|
4
|
+
return (
|
|
5
|
+
FORBIDDEN_PROPERTY_NAMES ||
|
|
6
|
+
(FORBIDDEN_PROPERTY_NAMES = Object.freeze(
|
|
7
|
+
Array.from(
|
|
8
|
+
new Set([
|
|
9
|
+
// As-of writing, `Object.prototype` includes:
|
|
10
|
+
// __defineGetter__
|
|
11
|
+
// __defineSetter__
|
|
12
|
+
// __lookupGetter__
|
|
13
|
+
// __lookupSetter
|
|
14
|
+
// __proto__
|
|
15
|
+
// constructor
|
|
16
|
+
// hasOwnProperty
|
|
17
|
+
// isPrototypeOf
|
|
18
|
+
// propertyIsEnumerable
|
|
19
|
+
// toLocaleString
|
|
20
|
+
// toString
|
|
21
|
+
// valueOf
|
|
22
|
+
...Object.getOwnPropertyNames(Object.prototype),
|
|
23
|
+
|
|
24
|
+
'prototype'
|
|
25
|
+
])
|
|
26
|
+
)
|
|
27
|
+
))
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function isForbiddenPropertyName(propertyName: string): boolean {
|
|
32
|
+
return getForbiddenPropertyNames().includes(propertyName);
|
|
33
|
+
}
|
package/src/utils/uniqueID.js
CHANGED