-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.js
335 lines (288 loc) · 9.2 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
const Max = require('max-api');
const google = require("./src/google_functions");
const dbQuery = require("./src/db_functions")
const fs = require('fs');
// =========================================================
// ========================= Setup =========================
// =========================================================
const IDs = {
youtubeId: parseVideoId(process.argv[2]),
// youtubeId: parseVideoId('https://youtu.be/hHW1oY26kxQ')
}
const loopArgs = {
waitInterval: parseInt(process.argv[3]),
isRunning: true
}
//get the live chat id and assign it when it comes back
getStreamDetails(IDs.youtubeId).then(id => {
IDs.liveChatId = id;
});
//get the video_id of the video. Either a new id or existing.
getVideoId(IDs.youtubeId).then(id => {
IDs.videoId = id;
});
//setup handlers for Max.
const handlers = {
query: (toggle) => {
if (toggle == 1) {
loopArgs.isRunning = true;
mainLoop();
} else if (toggle == 0) {
loopArgs.isRunning = false;
} else {
console.log("Please enter either a 0 or 1.");
}
},
//nothing else for now...
}
Max.addHandlers(handlers);
// =========================================================
// ========================== Logs =========================
// =========================================================
let startTime, endTime;
function log_StartTimer() {
startTime = new Date();
};
function log_EndTimer() {
endTime = new Date();
let timeDiff = endTime - startTime; //in ms
// strip the ms
timeDiff /= 1000;
let log = `It's been ${timeDiff} since last query.\n`;
//Log it
fs.appendFile('query.log', log, function (err) {
if (err) throw err;
});
}
// =========================================================
// ========================== Main =========================
// =========================================================
/**
* mainLoop makes sure things are set and calls the process function
*/
function mainLoop() {
//stuff for logs
log_EndTimer();
log_StartTimer();
if(loopArgs.isRunning) {
setTimeout(() => {
//if everything is set, run it!
if(IDs.youtubeId != null && IDs.videoId != null) {
processLiveChat();
} else {
//It's possible that we're waiting on some async. Give it a second to return.
try {
setTimeout(() => processLiveChat(), 1000);
} catch(e) {
console.log("Looks like an ID is not set...");
}
}
}, loopArgs.waitInterval);
}
}
/**
* processLiveChat() - runs the main processing functions. Calls the main loop upon success
*/
async function processLiveChat() {
const output = {};
const chatQuery = new google.ChatQuery(IDs.liveChatId);
const videoId = IDs.videoId;
const messageData = await chatQuery.getLiveChatData();
const newMessages = await checkResultsForNewMessages(messageData.messageData, videoId);
const newUsersGoogleIds = await checkNewMessagesForNewUsers(newMessages);
//write any new users to the db. return the ids for kicks and giggles i guess...
if (newUsersGoogleIds.length > 0) {
const newUserIds = await writeNew(newMessages, 'user').then( (newIds) => {
return newIds;
});
}
//get new message user google_ids into an array so we can search for them
const newMessageUserGoogleIds = newMessages.map( (message) => message.author.authorId);
//get google ids and user ids
const newMessageUserIds = await getUsersByGoogleId(newMessageUserGoogleIds);
//write new message
const writeNewMessageArgs = {
videoId: videoId,
userIds: newMessageUserIds,
messages: newMessages
}
const newMessageIds = await writeNew(writeNewMessageArgs, 'message').then( (newIds) => {
return newIds;
});
//get the new message content
const newlyAddedMessages = await getNewAddedMessages(newMessageIds);
//output to Max
output.new_messages = newlyAddedMessages
// console.log(output);
const outputString = JSON.stringify(output); //There seems to be a bug (or a feature) where you can't output directly an object.
Max.outlet(outputString);
//order the next round
mainLoop();
}
// =========================================================
// ======================== Helpers ========================
// =========================================================
/**
* parseVideoId() - takes a url and strips out the google video id
*
* @param {string} url must be a youtube url
* @returns {string} the google id
*/
function parseVideoId(url) {
const regExp = /^.*(youtu\.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
const match = url.match(regExp);
if (match && match[2].length == 11) {
return match[2];
} else {
console.log("Cannot parse YouTube id.")
}
}
/**
* getStreamDetails() - querys google to get liveChatId
*
* @param {string} youtubeId
* @returns {promise string}
*/
function getStreamDetails(youtubeId) {
return new Promise((resolve, reject) => {
try {
let streamDetails = new google.StreamDetails(youtubeId);
let chatId = streamDetails.getChatId();
resolve(chatId);
} catch(e) {
reject(e);
}
});
}
/**
* getVideoId - querys the db and returns the video_id
*
* @param {string} youtubeId
* @returns {int} video_id
*/
async function getVideoId(youtubeId) {
const db = new dbQuery.DBQuery;
return await db.checkVideo(youtubeId);
}
/**
* checkResultsForNewMessages
*
* @param {object} results the google messages
* @param {int} videoId
* @return {array} array of new messages || empty if nothing new
*/
async function checkResultsForNewMessages(results, videoId) {
const db = new dbQuery.DBQuery;
const allMessages = await db.getMessagesForVideo(videoId);
return sniffNew(allMessages, results, 'chat');
}
/**
* checkNewMessagesForNewUsers
*
* @param {array} newMessages array of new messages
* @return {array} google ids of new messages || empty if nothing new
*/
async function checkNewMessagesForNewUsers(newMessages) {
const db = new dbQuery.DBQuery;
const allUsers = await db.getAllUsers();
const newUsers = sniffNew(allUsers, newMessages, 'user');
const newUsersGoogleIds = newUsers.map( (user) => {
return user.author.authorId;
});
return newUsersGoogleIds;
}
/**
* getUsersByGoogleId
*
* @param {array} newMessageUserGoogleIds array of google ids
* @return {array} result of user_ids and google_ids
*/
async function getUsersByGoogleId(newMessageUserGoogleIds) {
const db = new dbQuery.DBQuery;
return await db.getUsersByGoogleId(newMessageUserGoogleIds);
}
/**
* getNewAddedMessages
*
* @param {array} newMessageIds array of comment ids
* @return {array} result of comment_id and content
*/
async function getNewAddedMessages(newMessageIds) {
const db = new dbQuery.DBQuery;
return await db.getMessagesById(newMessageIds);
}
// =========================================================
// ========================= Logic =========================
// =========================================================
/**
* writeNewUsers - writes new users to db. returns an array of new ids
*
* @param {any} newItemsToSave the something you want to save
* @return {array} an array of new ids
*/
function writeNew(newItemsToSave, type) {
const db = new dbQuery.DBQuery;
let newIds;
switch(type) {
case "user":
newIds = newItemsToSave.map( async (item) => {
let googleId = item.author.authorId;
let moderator = item.author.moderator;
let display = item.author.display;
let userId = await db.newUser(googleId, display, moderator);
return userId;
});
break;
case "message":
newIds = newItemsToSave.messages.map( async (message) => {
//match the user_id with the google user id for message
let user_id;
newItemsToSave.userIds.forEach( (user) => {
if(user.google_id == message.author.authorId) {
user_id = user.user_id;
}
});
//write the message to the db
let messageId = await db.newMessage(message.message, newItemsToSave.videoId, user_id);
return messageId;
});
break
}
return Promise.all(newIds);
}
/**
* sniffNew - loop through stuff and find new stuff
*
* @param {array} allKnown all things known to exist
* @param {array} results the things returned by google
* @param {string} type
* @return {object} object containg an array of known ids and new ids
*/
function sniffNew(allKnown, results, type) {
let newItems = [];
results.forEach( (potentialNewItem) => {
let knownItem = false;
let potentialNewItemId;
//determine the google_id based on type
switch(type) {
case 'chat':
potentialNewItemId = potentialNewItem.message.id;
break;
case 'user':
potentialNewItemId = potentialNewItem.author.authorId;
break;
}
//loop through the established items; use a classic for so we can break if we find a match
for(i=0; i<allKnown.length; i++) {
if (allKnown[i].google_id === potentialNewItemId) {
knownItem = true;
break;
}
}
//if we don't know the item
if (!knownItem) {
newItems.push(potentialNewItem)
}
});
return newItems
}