NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name U2 Sticky Manager (Forum) // @namespace https://onee3.org/ // @version 0.94 // @description Easier to manage the sticky torrents. Only for moderators. // @include *://u2.dmhy.org/forums.php?action=viewtopic&* // @copyright 2014+, Frederick888 // @require https://onee3.org/libs/jquery/3.2.1/jquery.min.js // @grant none // @license GPL-3.0-or-later // ==/UserScript== var D = 0; var A = 60; var duration = 3.5; var processed = false; console.log('Using jQuery v' + $.fn.jquery); // these obey the order shown in page (deduplicated based on post id + torrent id) var $apps, app_ids, app_applicants, app_pids, app_ranks; // items to be added (html, random order) var items = []; // sorted items with styles (jQuery elements) var sortedItems = []; // torrent id => torrent data var torrentData = {}; function nextSame() { var $currTr = $(this).closest('tr.manage-item'); var seq = $currTr.data('origin-seq'); var next = -1, pos = seq + 1; while (pos != seq) { if (app_ids[pos] == app_ids[seq]) { next = pos; break; } else { pos++; if (pos > app_ids.length) pos = 0; } } if (next != -1) { $currTr.data('origin-seq', next); $currTr.find('td.applicant').html(app_applicants[next]); $currTr.find('button.pid').html(app_pids[next]); } } function modified_time(pid) { $postHeader = $('table#pid' + pid); postCreatedTime = Date.parse($postHeader.find('time').attr('title') === undefined ? $postHeader.find('time').html() : $postHeader.find('time').attr('title')); $postBody = $postHeader.parent().next('table.main'); if ($postBody.find('time').length > 0) { return Date.parse($postBody.find('time').attr('title') === undefined ? $postBody.find('time').html() : $postBody.find('time').attr('title')); } else { return postCreatedTime; } } function sortManger() { sortedItems = []; // sort the items, order: rank, modified date, post id, torrent id var j, k, currTid, currPid; for (j = 0; j < items.length; j++) { sortedItems.push($(items[j])); // since item is not ordered, we need to find the origin order currTid = parseInt(sortedItems[j].find('.torrent-id').html()); currPid = parseInt(sortedItems[j].find('.pid').html()); for (k = 0; k < app_ids.length; k++) { if (app_ids[k] == currTid && app_pids[k] == currPid) { sortedItems[j].data('origin-seq', k); } } } sortedItems.sort(function ($a, $b) { aSeq = $a.data('origin-seq'); aRank = app_ranks[aSeq]; aTid = parseInt($a.find('.torrent-id').html()); aPid = parseInt($a.find('.pid').html()); aTime = modified_time(aPid); bSeq = $b.data('origin-seq'); bRank = app_ranks[bSeq]; bTid = parseInt($b.find('.torrent-id').html()); bPid = parseInt($b.find('.pid').html()); bTime = modified_time(bPid); if (aRank != bRank) { return aRank - bRank; } else if (aTime != bTime) { return aTime - bTime; } else if (aPid != bPid) { return aPid - bPid; } else if (aTid != bTid) { return aTid - bTid; } return 0; }); // Set the styles for (j = 0; j < sortedItems.length; j++) { currTid = parseInt(sortedItems[j].find('.torrent-id').html()); // If the torrent is not deleted or recycled or ... if (torrentData[currTid].data !== undefined && torrentData[currTid].data.status == 'p') { // Currently not sticky if (torrentData[currTid].data.pos_state == 'normal') { sortedItems[j].find('.to-stick').removeAttr('disabled'); } else { // Currently sticky sortedItems[j].find('.to-stick').removeAttr('disabled'); sortedItems[j].find('.to-stick').prop('checked', true); sortedItems[j].find('.to-stick').attr('previous', 't'); sortedItems[j].find('.to-remove').removeAttr('disabled'); sortedItems[j].css('background-color', 'skyblue'); } } // Enable PID button if the torrent is repeatedly applied if (app_ids.indexOf(currTid) != app_ids.lastIndexOf(currTid)) { sortedItems[j].find('.pid').removeAttr('disabled'); sortedItems[j].find('.pid').click(nextSame); } } var rank = 0; for (j = 0; j < sortedItems.length; j++) { currTid = parseInt(sortedItems[j].find('.torrent-id').html()); var toAdd = true; for (k = j + 1; k < sortedItems.length; k++) { if (parseInt(sortedItems[k].find('.torrent-id').html()) == currTid) { toAdd = false; break; } } if (toAdd) { if (app_ranks[sortedItems[j].data('origin-seq')] > rank) { // add delimiters based on ranks $('#manager>tbody>tr:last-child').before('<tr style="height:10px"></tr>'); rank = app_ranks[sortedItems[j].data('origin-seq')]; } $('#manager>tbody>tr:last-child').before(sortedItems[j]); } } // set sequence number column var $seqTd = $('.seq'); for (var seq = 1; seq <= $seqTd.length; seq++) { $($seqTd[seq - 1]).html(seq); } $('input.to-stick, input.to-remove').click(function () { if ($(this).prop('disabled') === false) { if ($(this).attr('previous') == 't') { $(this).prop('checked', false); $(this).attr('previous', 'f'); } else { $(this).parent().find('input').each(function () { $(this).attr('previous', 'f'); }); $(this).attr('previous', 't'); } } }); } function recalculate() { A = parseFloat($('#value-a').val()); duration = parseFloat($('#value-duration').val()); $('.manage-item').each(function () { var size = parseFloat($(this).find('.torrent-size').html()); $(this).find('.torrent-cost').html((A * D * size / 7 * duration).toFixed(3)); }); $('#process-sticky').removeAttr('disabled'); } function addItem(data, applicant, pid) { var name, size, finished, seeder, leecher, cost; var torrentId = data.data.id; if (data.code === 0) { size = parseInt(data.data.size) / 1024 / 1024 / 1024; size = (size).toFixed(2); name = data.data.name; finished = parseInt(data.data.times_completed); seeder = parseInt(data.data.seeders); leecher = parseInt(data.data.leechers); cost = (A * D * size / 7 * duration).toFixed(3); } else { name = data.err_msg; size = 'x'; finished = 'x'; seeder = 'x'; leecher = 'x'; cost = 'x'; } var addRow = '<tr class="manage-item" id="manage' + torrentId + '"><td class="torrent-checked"><input type="radio" class="to-stick" name="choice-' + torrentId + '" value="' + torrentId + '" disabled/><input type="radio" class="to-remove" name="choice-' + torrentId + '" value="' + torrentId + '" disabled/></td><td class="seq"></td><td class="applicant" style="min-width:100px;max-width:100px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;">' + applicant + '</td><td><button type="button" class="pid" disabled>' + pid + '</button></td><td class="torrent-name" style="max-width:900px"><a target="_blank" href="details.php?id=' + torrentId + '">' + name + '</a></td><td style="min-width:50px;text-align:right;" title="Check Log">' + '<a target="_blank" class="torrent-id" href="log.php?search=all&action=dailylog&query=details.php%3Fid%3D' + torrentId + '">' + torrentId + '</a></td><td class="torrent-size" style="min-width:50px;text-align:right;">' + size + '</td><td class="torrent-cost" style="min-width:70px;text-align:right;">' + cost + '</td><td class="torrent-seeder" style="min-width:20px;text-align:right;">' + seeder + '</td><td class="torrent-leecher" style="min-width:20px;text-align:right;">' + leecher + '</td><td class="torrent-finished" style="min-width:20px;text-align:right;">' + finished + '</td></tr>'; items.push(addRow); } function process() { var toStick = [], toRemove = [], i; $('#manager .to-stick').each(function () { if (this.checked) { toStick.push($(this).val()); } }); $('#manager .to-remove').each(function () { if (this.checked) { toRemove.push($(this).val()); } }); var pid, cost, size, message; // UC POST to forumpostadmin.php // markdes={add|sub}&mark={amount}&reason={URLEncodedMessage}&admintype=mark&pid={PostID} // Posting POST to forums.php?action=post // id={ArticleID}&type=reply&body={URLEncodedMessage} // Sticky GET httpapi_torrentstick.php?sticky={1|0}&id={torrentid} // Comment POST to forumpostadmin.php // admintype=comment&pid={PostId}&comment={Comment} for (i = 0; i < toStick.length; i++) { pid = parseInt($('.manage-item#manage' + toStick[i]).find('.pid').html()); cost = parseFloat($('tr#manage' + toStick[i] + '>.torrent-cost').html()); size = parseFloat($('tr#manage' + toStick[i] + '>.torrent-size').html()); message = encodeURIComponent('Torrent #' + toStick[i] + ': CHARGE, A × D × Size × Duration = ' + A + ' × ' + D + ' × ' + size + ' × (' + duration + ' ÷ 7) = ' + cost); $.ajax({ url: 'forumpostadmin.php', method: 'post', aPid: pid, aTid: toStick[i], data: 'markdes=sub&mark=' + cost + '&reason=' + message + '&admintype=mark&pid=' + pid, error: function (xhr, stat, err) { console.log('Failed to manage PID#' + this.aPid + ' with Torrent#' + this.aTid); }, success: function (data) { console.log('Succeeded in managing PID#' + this.aPid + ' with Torrent#' + this.aTid); $.get('httpapi_torrentstick.php', { sticky: 1, id: this.aTid }); } }); } for (i = 0; i < toRemove.length; i++) { pid = parseInt($('.manage-item#manage' + toRemove[i]).find('.pid').html()); message = encodeURIComponent('Torrent #' + toRemove[i] + ': REMOVE'); $.ajax({ url: 'forumpostadmin.php', method: 'post', aPid: pid, aTid: toRemove[i], data: 'admintype=comment&comment=' + message + '&pid=' + pid, error: function (xhr, stat, err) { console.log('Failed to manage PID#' + this.aPid + ' with Torrent#' + this.aTid); }, success: function (data) { console.log('Succeeded in managing PID#' + this.aPid + ' with Torrent#' + this.aTid); $.get('httpapi_torrentstick.php', { sticky: 0, id: this.aTid }); } }); } if (toStick.length == 0 && toRemove.length == 0) { if (confirm('Reply with marker only?')) { reply(''); } } processed = true; } // Checkbox Name ID Size Cost ↑ ↓ √ function addManager() { console.log('Starting manager'); processed = false; var $enableBtn = $('.enable-manager'); $enableBtn.attr('disabled', 'disabled'); $enableBtn.html('Just a moment...<span class="spinner"><img src="https://onee3.org/libs/loading_spinner_wedges.gif" width="15" height="15" style="vertical-align:middle;"/></spin>'); $.ajax({ url: 'httpapi_globalconstant.php?constant=D', dataType: 'json', error: function (xhr, stat, err) { alert('Failed to get D'); }, success: function (data) { if (data.code !== 0) { alert('Failed to get D'); return; } D = parseFloat(data.data); console.log('D=' + D); } }).done(function () { $apps = $('div.post-body a.faqlink'); for (var h = $apps.length - 1; h >= 0; h--) { if (!(/\bdetails\.php\b/.test($($apps[h]).attr('href')))) { $apps.splice(h, 1); } } app_ids = []; app_applicants = []; app_pids = []; app_ranks = []; var rank = 0; $apps.each(function () { // Ignore posts with manager tag "ignore" if (/MANAGER_TAG_IGNORE/.test($(this).parent().html())) { rank++; return; } if (/details\.php\?.*id=(\d+)/.test($(this).attr('href'))) { var currId = parseInt(RegExp.$1); var currPid = /pid\d+/i.exec($(this).closest('.post-body').attr('id'))[0]; currPid = parseInt(/\d+/.exec(currPid)[0]); // Add all applications but only one for same applications in one post var toAdd = true; for (var z = 0; z < app_ids.length; z++) { if (app_ids[z] == currId && app_pids[z] == currPid) { toAdd = false; break; } } if (toAdd) { app_ids.push(currId); app_pids.push(currPid); app_ranks.push(rank); var applicant = $('#pid' + currPid + ' a[href^="userdetails.php"]')[0].outerHTML; applicant = applicant.substring(0, 3) + 'target="_blank"' + applicant.substring(2); app_applicants.push(applicant); } } }); $('td.outer').append('<table id="manager"><tbody><tr><td colspan="20" style="text-align:right"><label for="value-a">A: </label>' + '<input type="number" id="value-a" value="' + A + '" style="margin-right:15px;width:100px;"><label for="value-d">D: </label><input type="number" id="value-d" disabled value="' + D.toFixed(4) + '" style="margin-right:15px;width:100px;"><label for="value-duration">Duration: </label><input type="number" id="value-duration" value="' + duration + '" style="margin-right:15px;width:100px;"><button type="button" id="re-cal">Re-calculate</button></td></tr><tr><td>Ch/Rm</td><td>#</td>' + '<td>Applicant</td><td>Post</td><td>Name</td><td style="text-align:right">ID</td><td style="text-align:right">Size</td>' + '<td style="text-align:right">Cost</td><td style="text-align:right">↑</td><td style="text-align:right">↓</td>' + '<td style="text-align:right">√</td></tr><tr><td colspan="11" style="text-align:right">' + '<button type="button" id="process-sticky">Process!</button></td></tr></tbody></table>'); $('#process-sticky').on('click', process); $('#value-a, #value-duration').change(function () { $('#process-sticky').attr('disabled', 'disabled'); }); $('#re-cal').click(recalculate); for (var i = 0; i < app_ids.length; i++) { if (isNaN(torrentData[app_ids[i]])) { // httpapi_torrentinfo.php?tid=xxx // status: p=normal; d=recycled; w=offer // pos_state: normal/sticky $.ajax({ url: 'httpapi_torrentinfo.php?tid=' + app_ids[i], applicant: app_applicants[i], pid: app_pids[i], tid: app_ids[i], dataType: 'json', error: function (xhr, stat, err) { alert('Failed to get info of #' + this.tid); }, success: function (data, status, xhr) { torrentData[this.tid] = data; addItem(data, this.applicant, this.pid); } }); } else { addItem(torrentData[app_ids[i]], app_applicants[i], app_pids[i]); } } }); } function reply(message) { message = "MANAGER_TAG_IGNORE\n" + message; $('#compose>textarea').text(message); $('#qr').trigger('click'); } $(document).ajaxStop(function () { console.log('stopped'); if (!processed) { $('.enable-manager').html('Enabled'); sortManger(); location.hash = '#manager'; } else { console.log('all done'); var replyMsg = "D=" + D + "\n\n"; var toStick = [], toRemove = [], i; $('#manager .to-stick').each(function () { if (this.checked) { toStick.push($(this).val()); } }); $('#manager .to-remove').each(function () { if (this.checked) { toRemove.push($(this).val()); } }); var dataRow, nameColumn, nameColor, nameText; for (i = 0; i < toStick.length; i++) { dataRow = $('tr#manage' + toStick[i]); nameColumn = $(dataRow.find('a')[0]); if (/color:(#[a-z0-9]+)/i.test(nameColumn.attr('style'))) { nameColor = RegExp.$1; } else { nameColor = 'black'; } (/<b><bdo .*?>(.*)<\/bdo><\/b>/).test(nameColumn.html()); nameText = RegExp.$1.replace(/^<i>/, "[i]").replace(/<\/i>$/, "[/i]"); replyMsg += '[url=' + nameColumn.attr('href') + '][color=' + nameColor + '][b]' + nameText + '[/b][/color][/url]' + "\n"; replyMsg += $(dataRow.find('a')[1]).html() + "\n"; replyMsg += 'https://u2.dmhy.org/' + $(dataRow.find('a')[1]).attr('href') + "\n"; replyMsg += 'Operation: Stick Size: ' + dataRow.children('.torrent-size').html() + 'GB Cost: ' + dataRow.children('.torrent-cost').html(); if (i < toStick.length - 1 || toRemove.length > 0) { replyMsg += "\n\n"; } } for (i = 0; i < toRemove.length; i++) { dataRow = $('tr#manage' + toRemove[i]); nameColumn = $(dataRow.find('a')[0]); if (/color:(#[a-z0-9]+)/i.test(nameColumn.attr('style'))) { nameColor = RegExp.$1; } else { nameColor = 'black'; } (/<b><bdo .*?>(.*)<\/bdo><\/b>/).test(nameColumn.html()); nameText = RegExp.$1.replace(/^<i>/, "[i]").replace(/<\/i>$/, "[/i]"); replyMsg += '[url=' + nameColumn.attr('href') + '][color=' + nameColor + '][b]' + nameText + '[/b][/color][/url]' + "\n"; replyMsg += $(dataRow.find('a')[1]).html() + "\n"; replyMsg += 'https://u2.dmhy.org/' + $(dataRow.find('a')[1]).attr('href') + "\n"; replyMsg += 'Operation: Remove'; if (i < toRemove.length - 1) { replyMsg += "\n\n"; } } reply(replyMsg); } }); $('td.outer>table.main>tbody').append('<tr><td style="text-align:center"><button type="button" class="enable-manager">Enable Sticky Manager<div></div></button></td></tr>'); $('.enable-manager').on('click', addManager); if (window.location.hash.indexOf('manager') >= 0) addManager();