1 | "use strict";
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4 | return new (P || (P = Promise))(function (resolve, reject) {
|
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9 | });
|
10 | };
|
11 | Object.defineProperty(exports, "__esModule", { value: true });
|
12 | exports.FileTranscriptStore = void 0;
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | const path_1 = require("path");
|
21 | const fs_extra_1 = require("fs-extra");
|
22 |
|
23 | const filenamify = require('filenamify');
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | const epochTicks = 621355968000000000;
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | const ticksPerMillisecond = 10000;
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | function getTicks(timestamp) {
|
39 | const ticks = epochTicks + timestamp.getTime() * ticksPerMillisecond;
|
40 | return ticks.toString(16);
|
41 | }
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | function readDate(ticks) {
|
47 | const t = Math.round((parseInt(ticks, 16) - epochTicks) / ticksPerMillisecond);
|
48 | return new Date(t);
|
49 | }
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | function withDateFilter(date, fileName) {
|
56 | if (!date) {
|
57 | return true;
|
58 | }
|
59 | const ticks = fileName.split('-')[0];
|
60 | return readDate(ticks) >= date;
|
61 | }
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | function includeWhen(expression) {
|
67 | let shouldInclude = false;
|
68 | return (item) => {
|
69 | return shouldInclude || (shouldInclude = expression(item));
|
70 | };
|
71 | }
|
72 |
|
73 |
|
74 |
|
75 |
|
76 | function parseActivity(json) {
|
77 | const activity = JSON.parse(json);
|
78 | activity.timestamp = new Date(activity.timestamp);
|
79 | return activity;
|
80 | }
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 | class FileTranscriptStore {
|
97 | |
98 |
|
99 |
|
100 |
|
101 | constructor(folder) {
|
102 | if (!folder) {
|
103 | throw new Error('Missing folder.');
|
104 | }
|
105 | this.rootFolder = folder;
|
106 | }
|
107 | |
108 |
|
109 |
|
110 |
|
111 | logActivity(activity) {
|
112 | return __awaiter(this, void 0, void 0, function* () {
|
113 | if (!activity) {
|
114 | throw new Error('activity cannot be null for logActivity()');
|
115 | }
|
116 | const conversationFolder = this.getTranscriptFolder(activity.channelId, activity.conversation.id);
|
117 | const activityFileName = this.getActivityFilename(activity);
|
118 | return this.saveActivity(activity, conversationFolder, activityFileName);
|
119 | });
|
120 | }
|
121 | |
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 | getTranscriptActivities(channelId, conversationId, continuationToken, startDate) {
|
129 | return __awaiter(this, void 0, void 0, function* () {
|
130 | if (!channelId) {
|
131 | throw new Error('Missing channelId');
|
132 | }
|
133 | if (!conversationId) {
|
134 | throw new Error('Missing conversationId');
|
135 | }
|
136 | const pagedResult = { items: [], continuationToken: undefined };
|
137 | const transcriptFolder = this.getTranscriptFolder(channelId, conversationId);
|
138 | const exists = yield fs_extra_1.pathExists(transcriptFolder);
|
139 | if (!exists) {
|
140 | return pagedResult;
|
141 | }
|
142 | const transcriptFolderContents = yield fs_extra_1.readdir(transcriptFolder);
|
143 | const include = includeWhen((fileName) => !continuationToken || path_1.parse(fileName).name === continuationToken);
|
144 | const items = transcriptFolderContents.filter((transcript) => transcript.endsWith('.json') && withDateFilter(startDate, transcript) && include(transcript));
|
145 | pagedResult.items = yield Promise.all(items
|
146 | .slice(0, FileTranscriptStore.PageSize)
|
147 | .sort()
|
148 | .map((activityFilename) => __awaiter(this, void 0, void 0, function* () {
|
149 | const json = yield fs_extra_1.readFile(path_1.join(transcriptFolder, activityFilename), 'utf8');
|
150 | return parseActivity(json);
|
151 | })));
|
152 | const { length } = pagedResult.items;
|
153 | if (length === FileTranscriptStore.PageSize && items[length]) {
|
154 | pagedResult.continuationToken = path_1.parse(items[length]).name;
|
155 | }
|
156 | return pagedResult;
|
157 | });
|
158 | }
|
159 | |
160 |
|
161 |
|
162 |
|
163 |
|
164 | listTranscripts(channelId, continuationToken) {
|
165 | return __awaiter(this, void 0, void 0, function* () {
|
166 | if (!channelId) {
|
167 | throw new Error('Missing channelId');
|
168 | }
|
169 | const pagedResult = { items: [], continuationToken: undefined };
|
170 | const channelFolder = this.getChannelFolder(channelId);
|
171 | const exists = yield fs_extra_1.pathExists(channelFolder);
|
172 | if (!exists) {
|
173 | return pagedResult;
|
174 | }
|
175 | const channels = yield fs_extra_1.readdir(channelFolder);
|
176 | const items = channels.filter(includeWhen((di) => !continuationToken || di === continuationToken));
|
177 | pagedResult.items = items
|
178 | .slice(0, FileTranscriptStore.PageSize)
|
179 | .map((i) => ({ channelId: channelId, id: i, created: null }));
|
180 | const { length } = pagedResult.items;
|
181 | if (length === FileTranscriptStore.PageSize && items[length]) {
|
182 | pagedResult.continuationToken = items[length];
|
183 | }
|
184 | return pagedResult;
|
185 | });
|
186 | }
|
187 | |
188 |
|
189 |
|
190 |
|
191 |
|
192 | deleteTranscript(channelId, conversationId) {
|
193 | return __awaiter(this, void 0, void 0, function* () {
|
194 | if (!channelId) {
|
195 | throw new Error('Missing channelId');
|
196 | }
|
197 | if (!conversationId) {
|
198 | throw new Error('Missing conversationId');
|
199 | }
|
200 | const transcriptFolder = this.getTranscriptFolder(channelId, conversationId);
|
201 | return fs_extra_1.remove(transcriptFolder);
|
202 | });
|
203 | }
|
204 | |
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 | saveActivity(activity, transcriptPath, activityFilename) {
|
211 | return __awaiter(this, void 0, void 0, function* () {
|
212 | const json = JSON.stringify(activity, null, '\t');
|
213 | const exists = yield fs_extra_1.pathExists(transcriptPath);
|
214 | if (!exists) {
|
215 | yield fs_extra_1.mkdirp(transcriptPath);
|
216 | }
|
217 | return fs_extra_1.writeFile(path_1.join(transcriptPath, activityFilename), json, 'utf8');
|
218 | });
|
219 | }
|
220 | |
221 |
|
222 |
|
223 | getActivityFilename(activity) {
|
224 | return `${getTicks(activity.timestamp)}-${this.sanitizeKey(activity.id)}.json`;
|
225 | }
|
226 | |
227 |
|
228 |
|
229 | getChannelFolder(channelId) {
|
230 | return path_1.join(this.rootFolder, this.sanitizeKey(channelId));
|
231 | }
|
232 | |
233 |
|
234 |
|
235 | getTranscriptFolder(channelId, conversationId) {
|
236 | return path_1.join(this.rootFolder, this.sanitizeKey(channelId), this.sanitizeKey(conversationId));
|
237 | }
|
238 | |
239 |
|
240 |
|
241 | sanitizeKey(key) {
|
242 | return filenamify(key);
|
243 | }
|
244 | }
|
245 | exports.FileTranscriptStore = FileTranscriptStore;
|
246 | FileTranscriptStore.PageSize = 20;
|
247 |
|
\ | No newline at end of file |