|
1 | 1 | import { |
2 | 2 | any, |
3 | 3 | array, |
4 | | - empty, |
5 | 4 | integer, |
6 | | - length, |
7 | 5 | literal, |
8 | 6 | minValue, |
9 | | - nonEmpty, |
10 | 7 | number, |
11 | 8 | object, |
12 | 9 | optional, |
13 | 10 | pipe, |
14 | 11 | safeParse, |
15 | 12 | string, |
16 | | - transform, |
17 | 13 | undefinedable, |
18 | | - union, |
19 | | - unknown |
| 14 | + union |
20 | 15 | } from 'valibot'; |
21 | 16 |
|
22 | 17 | import { type WebChatActivity } from '../types/WebChatActivity'; |
23 | 18 | import getOrgSchemaMessage from './getOrgSchemaMessage'; |
24 | 19 |
|
25 | 20 | const EMPTY_ARRAY = Object.freeze([]); |
26 | 21 |
|
| 22 | +const streamSequenceSchema = pipe(number(), integer(), minValue(1)); |
| 23 | + |
27 | 24 | type StreamData = { |
28 | 25 | streamId?: string; |
29 | 26 | streamSequence?: number; |
30 | 27 | streamType?: string; |
31 | 28 | }; |
32 | 29 |
|
33 | | -const streamSequenceSchema = pipe(number(), integer(), minValue(1)); |
| 30 | +const streamDataSchema = object({ |
| 31 | + streamId: optional(undefinedable(string())), |
| 32 | + streamSequence: streamSequenceSchema, |
| 33 | + streamType: union([literal('streaming'), literal('informative'), literal('final')]) |
| 34 | +}); |
34 | 35 |
|
35 | | -const streamInfoSchema = union([ |
| 36 | +const livestreamingActivitySchema = union([ |
| 37 | + // Interim. |
36 | 38 | object({ |
37 | | - // "streamId" is optional for the very first activity in the session. |
38 | | - streamId: optional(undefinedable(string())), |
39 | | - streamSequence: streamSequenceSchema, |
40 | | - streamType: union([literal('streaming'), literal('informative')]) |
| 39 | + attachments: optional(array(any()), EMPTY_ARRAY), |
| 40 | + channelData: any(), |
| 41 | + id: string(), |
| 42 | + // "text" is optional. If not set or empty, it presents a contentless activity. |
| 43 | + text: optional(undefinedable(string())), |
| 44 | + type: literal('typing'), |
| 45 | + entities: optional(array(any()), EMPTY_ARRAY) |
41 | 46 | }), |
| 47 | + // Informative message. |
42 | 48 | object({ |
43 | | - // "streamId" is required for the final activity in the session. |
44 | | - // The final activity must not be the sole activity in the session. |
45 | | - streamId: pipe(string(), nonEmpty()), |
46 | | - streamType: literal('final') |
| 49 | + attachments: optional(array(any()), EMPTY_ARRAY), |
| 50 | + channelData: any(), |
| 51 | + id: string(), |
| 52 | + // Informative may not have "text", but should have abstract instead (checked later) |
| 53 | + text: optional(undefinedable(string())), |
| 54 | + type: literal('typing'), |
| 55 | + entities: optional(array(any()), EMPTY_ARRAY) |
| 56 | + }), |
| 57 | + // Conclude with a message. |
| 58 | + object({ |
| 59 | + attachments: optional(array(any()), EMPTY_ARRAY), |
| 60 | + channelData: any(), |
| 61 | + id: string(), |
| 62 | + // If "text" is empty, it represents "regretting" the livestream. |
| 63 | + text: optional(undefinedable(string())), |
| 64 | + type: literal('message'), |
| 65 | + entities: optional(array(any()), EMPTY_ARRAY) |
| 66 | + }), |
| 67 | + // Conclude without a message. |
| 68 | + object({ |
| 69 | + attachments: optional(array(any()), EMPTY_ARRAY), |
| 70 | + channelData: any(), |
| 71 | + id: string(), |
| 72 | + // If "text" is not set or empty, it represents "regretting" the livestream. |
| 73 | + text: optional(undefinedable(literal(''))), |
| 74 | + type: literal('typing'), |
| 75 | + entities: optional(array(any()), EMPTY_ARRAY) |
47 | 76 | }) |
48 | 77 | ]); |
49 | 78 |
|
50 | | -const validEntitiesSchema = union([ |
51 | | - pipe( |
52 | | - array(streamInfoSchema), |
53 | | - length(1), |
54 | | - transform(data => data[0]) |
55 | | - ), |
56 | | - pipe( |
57 | | - array(unknown()), |
58 | | - empty(), |
59 | | - transform(() => undefined) |
60 | | - ) |
61 | | -]); |
62 | | - |
63 | | -const validChannelDataSchema = union([ |
64 | | - streamInfoSchema, |
65 | | - pipe( |
66 | | - object({ |
67 | | - webChat: object({ |
68 | | - receivedAt: number() |
69 | | - }) |
70 | | - }), |
71 | | - transform(() => undefined) |
72 | | - ) |
73 | | -]); |
74 | | - |
75 | | -const livestreamingActivitySchema = object({ |
76 | | - entities: validEntitiesSchema, |
77 | | - channelData: validChannelDataSchema, |
78 | | - attachments: optional(array(any()), EMPTY_ARRAY), |
79 | | - id: string(), |
80 | | - text: optional(undefinedable(string())), |
81 | | - type: union([literal('typing'), literal('message')]) |
82 | | -}); |
| 79 | +// const livestreamingActivitySchema = union([ |
| 80 | +// // Interim. |
| 81 | +// object({ |
| 82 | +// attachments: optional(array(any()), EMPTY_ARRAY), |
| 83 | +// channelData: object({ |
| 84 | +// // "streamId" is optional for the very first activity in the session. |
| 85 | +// streamId: optional(undefinedable(string())), |
| 86 | +// streamSequence: streamSequenceSchema, |
| 87 | +// streamType: literal('streaming') |
| 88 | +// }), |
| 89 | +// id: string(), |
| 90 | +// // "text" is optional. If not set or empty, it presents a contentless activity. |
| 91 | +// text: optional(undefinedable(string())), |
| 92 | +// type: literal('typing') |
| 93 | +// }), |
| 94 | +// // Informative message. |
| 95 | +// object({ |
| 96 | +// attachments: optional(array(any()), EMPTY_ARRAY), |
| 97 | +// channelData: object({ |
| 98 | +// // "streamId" is optional for the very first activity in the session. |
| 99 | +// streamId: optional(undefinedable(string())), |
| 100 | +// streamSequence: streamSequenceSchema, |
| 101 | +// streamType: literal('informative') |
| 102 | +// }), |
| 103 | +// id: string(), |
| 104 | +// // Informative may not have "text", but should have abstract instead (checked later) |
| 105 | +// text: optional(undefinedable(string())), |
| 106 | +// type: literal('typing'), |
| 107 | +// entities: optional(array(any()), EMPTY_ARRAY) |
| 108 | +// }), |
| 109 | +// // Conclude with a message. |
| 110 | +// object({ |
| 111 | +// attachments: optional(array(any()), EMPTY_ARRAY), |
| 112 | +// channelData: object({ |
| 113 | +// // "streamId" is required for the final activity in the session. |
| 114 | +// // The final activity must not be the sole activity in the session. |
| 115 | +// streamId: pipe(string(), nonEmpty()), |
| 116 | +// streamType: literal('final') |
| 117 | +// }), |
| 118 | +// id: string(), |
| 119 | +// // If "text" is empty, it represents "regretting" the livestream. |
| 120 | +// text: optional(undefinedable(string())), |
| 121 | +// type: literal('message') |
| 122 | +// }), |
| 123 | +// // Conclude without a message. |
| 124 | +// object({ |
| 125 | +// attachments: optional(array(any()), EMPTY_ARRAY), |
| 126 | +// channelData: object({ |
| 127 | +// // "streamId" is required for the final activity in the session. |
| 128 | +// // The final activity must not be the sole activity in the session. |
| 129 | +// streamId: pipe(string(), nonEmpty()), |
| 130 | +// streamType: literal('final') |
| 131 | +// }), |
| 132 | +// id: string(), |
| 133 | +// // If "text" is not set or empty, it represents "regretting" the livestream. |
| 134 | +// text: optional(undefinedable(literal(''))), |
| 135 | +// type: literal('typing') |
| 136 | +// }) |
| 137 | +// ]); |
83 | 138 |
|
84 | 139 | /** |
85 | 140 | * Gets the livestreaming metadata of the activity, or `undefined` if the activity is not participating in a livestreaming session. |
@@ -112,9 +167,9 @@ export default function getActivityLivestreamingMetadata(activity: WebChatActivi |
112 | 167 |
|
113 | 168 | let streamData: StreamData; |
114 | 169 |
|
115 | | - if (output.entities !== undefined) { |
116 | | - streamData = output.entities; |
117 | | - } else if (output.channelData !== undefined) { |
| 170 | + if (safeParse(streamDataSchema, output.entities[0]).success) { |
| 171 | + [streamData] = output.entities; |
| 172 | + } else if (safeParse(streamDataSchema, output.channelData).success) { |
118 | 173 | streamData = output.channelData; |
119 | 174 | } else { |
120 | 175 | return undefined; |
|
0 commit comments