const jsdom = require('jsdom'); const mysql = require('mysql'); const FormData = require('form-data'); module.exports = Self => { Self.remoteMethodCtx('closeTicket', { description: 'Close tickets without response from the user', accessType: 'READ', returns: { type: 'object', root: true }, http: { path: `/closeTicket`, verb: 'POST' } }); Self.closeTicket = async ctx => { const models = Self.app.models; const config = await models.OsTicketConfig.findOne(); const ostUri = `${config.host}/login.php`; if (!config.user || !config.password || !config.userDb || !config.passwordDb) return false; const con = mysql.createConnection({ host: config.hostDb, user: config.userDb, password: config.passwordDb, port: config.portDb }); const sql = `SELECT ot.ticket_id, ot.number FROM osticket.ost_ticket ot JOIN osticket.ost_ticket_status ots ON ots.id = ot.status_id JOIN osticket.ost_thread ot2 ON ot2.object_id = ot.ticket_id AND ot2.object_type = 'T' JOIN ( SELECT ote.thread_id, MAX(ote.created) created, MAX(ote.updated) updated FROM osticket.ost_thread_entry ote WHERE ote.staff_id AND ote.type = 'R' GROUP BY ote.thread_id ) sub ON sub.thread_id = ot2.id WHERE ot.isanswered AND ots.state = ? AND IF(sub.updated > sub.created, sub.updated, sub.created) < DATE_SUB(CURDATE(), INTERVAL ? DAY)`; const ticketsId = []; con.connect(err => { if (err) throw err; con.query(sql, [config.oldStatus, config.day], (err, results) => { if (err) throw err; for (const result of results) ticketsId.push(result.ticket_id); }); }); await getRequestToken(); async function getRequestToken() { const response = await fetch(ostUri); const result = response.headers.get('set-cookie'); const [firtHeader] = result.split(' '); const firtCookie = firtHeader.substring(0, firtHeader.length - 1); const body = await response.text(); const dom = new jsdom.JSDOM(body); const token = dom.window.document.querySelector('[name="__CSRFToken__"]').value; await login(token, firtCookie); } async function login(token, firtCookie) { const data = { __CSRFToken__: token, do: 'scplogin', userid: config.user, passwd: config.password, ajax: 1 }; const params = { method: 'POST', body: new URLSearchParams(data), headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Cookie': firtCookie } }; const response = await fetch(ostUri, params); const result = response.headers.get('set-cookie'); const [firtHeader] = result.split(' '); const secondCookie = firtHeader.substring(0, firtHeader.length - 1); await close(token, secondCookie); } async function close(token, secondCookie) { for (const ticketId of ticketsId) { try { const lockCode = await getLockCode(token, secondCookie, ticketId); if (lockCode == false) continue; let form = new FormData(); form.append('__CSRFToken__', token); form.append('id', ticketId); form.append('a', config.responseType); form.append('lockCode', lockCode); form.append('from_email_id', config.fromEmailId); form.append('reply-to', config.replyTo); form.append('cannedResp', 0); form.append('response', config.comment); form.append('signature', 'none'); form.append('reply_status_id', config.newStatusId); const ostUri = `${config.host}/tickets.php?id=${ticketId}`; const params = { method: 'POST', body: form, headers: { 'Cookie': secondCookie } }; await fetch(ostUri, params); } catch (e) { const err = new Error(`${ticketId} Ticket close failed: ${e.message}`); err.stack += e.stack; throw err; } } } async function getLockCode(token, secondCookie, ticketId) { const ostUri = `${config.host}/ajax.php/lock/ticket/${ticketId}`; const params = { method: 'POST', headers: { 'X-CSRFToken': token, 'Cookie': secondCookie } }; const response = await fetch(ostUri, params); const body = await response.text(); const json = JSON.parse(body); return json.code || json.retry; } }; };