'use strict'; const Posts = require(__dirname+'/../../db/posts.js') , Boards = require(__dirname+'/../../db/boards.js') , quoteRegex = />>(?\d+)/g , crossQuoteRegex = />>>/(?\w+)(?:/(?\d*))?/gm , catalogSearchQuoteRegex = />>>#/(?\w+)//gm; module.exports = { quoteRegex, crossQuoteRegex, catalogSearchQuoteRegex, process: async (board, text, thread) => { //get the matches const quotes = text.match(quoteRegex); const crossQuotes = text.match(crossQuoteRegex); const catalogSearchQuotes = text.match(catalogSearchQuoteRegex); if (!quotes && !crossQuotes && !catalogSearchQuotes) { return { quotedMessage: text, threadQuotes: [], crossQuotes: [] }; } //make query for db including crossquotes const postQueryOrs = [] const boardQueryIns = [] const crossQuoteMap = {}; if (quotes && board) { const quoteIds = [...new Set(quotes.map(q => { return Number(q.substring(8)) }))]; postQueryOrs.push({ 'board': board, 'postId': { '$in': quoteIds } }); } if (crossQuotes) { for (let i = 0; i < crossQuotes.length; i++) { const crossQuote = crossQuotes[i].split('/'); const crossQuoteBoard = crossQuote[1]; const crossQuotePostId = +crossQuote[2]; if (!crossQuoteMap[crossQuoteBoard]) { crossQuoteMap[crossQuoteBoard] = []; } if (!isNaN(crossQuotePostId) && crossQuotePostId > 0) { crossQuoteMap[crossQuoteBoard].push(crossQuotePostId); } } const crossQuoteBoards = Object.keys(crossQuoteMap) for (let i = 0; i < crossQuoteBoards.length; i++) { const crossQuoteBoard = crossQuoteBoards[i]; boardQueryIns.push(crossQuoteBoard); const crossQuoteBoardPostIds = crossQuoteMap[crossQuoteBoard]; if (crossQuoteBoardPostIds.length > 0) { postQueryOrs.push({ 'board': crossQuoteBoard, 'postId': { '$in': crossQuoteBoardPostIds } }); } } } //get all the posts from quotes const postThreadIdMap = {}; if (crossQuotes || quotes) { const [ posts, boards ] = await Promise.all([ postQueryOrs.length > 0 ? Posts.getPostsForQuotes(postQueryOrs) : [], boardQueryIns.length > 0 ? Boards.db.find({ '_id': { '$in': boardQueryIns } }, { projection: { '_id': 1 } }).toArray() : [] ]); //turn the result into a map of postId => threadId/postId for (let i = 0; i < posts.length; i++) { const post = posts[i]; if (!postThreadIdMap[post.board]) { postThreadIdMap[post.board] = {}; } postThreadIdMap[post.board][post.postId] = { '_id': post._id, 'thread': post.thread || post.postId, 'postId': post.postId }; } for (let i = 0; i < boards.length; i++) { const boardName = boards[i]._id; if (!postThreadIdMap[boardName]) { postThreadIdMap[boardName] = {}; } } } //then replace the quotes with only ones that exist const threadQuotes = new Set(); const nonThreadQuotes = new Set(); if (quotes) { text = text.replace(quoteRegex, (match, quotenum) => { if (postThreadIdMap[board] && postThreadIdMap[board][quotenum]) { if (postThreadIdMap[board][quotenum].thread === thread) { threadQuotes.add(postThreadIdMap[board][quotenum]); } else { nonThreadQuotes.add(postThreadIdMap[board][quotenum]); } return `>>${quotenum}${postThreadIdMap[board][quotenum].postId == thread ? ' (OP) ' : ''}`; } return `>>${quotenum}`; }); } if (crossQuotes) { text = text.replace(crossQuoteRegex, (match, quoteboard, quotenum) => { if (postThreadIdMap[quoteboard]) { if (!quotenum) { return `>>>/${quoteboard}/`; } else if (!isNaN(quotenum) && quotenum > 0 && postThreadIdMap[quoteboard][quotenum]) { return `>>>/${quoteboard}/${quotenum}`; } } return `>>>/${quoteboard}/${quotenum || ''}`; }); } if (catalogSearchQuotes) { text = text.replace(catalogSearchQuoteRegex, (match, search) => { return `>>>#/${search}/`; }); } return { quotedMessage: text, threadQuotes: [...threadQuotes], crossQuotes: [...nonThreadQuotes] }; }, };