Greasy Fork is available in English.

Duoshuo Admin

更加强大的多说评论管理脚本,可以自动分析所有有评论的页面,方便地修改thread_key,避免评论丢失。

// ==UserScript==
// @name Duoshuo Admin
// @name:zh-CN 多说评论管理
// @namespace http://gerald.top
// @homepage https://gerald.top/code/DuoshuoAdmin
// @author Gerald <i@gerald.top>
// @icon	http://cn.gravatar.com/avatar/a0ad718d86d21262ccd6ff271ece08a3?s=80
// @description 更加强大的多说评论管理脚本,可以自动分析所有有评论的页面,方便地修改thread_key,避免评论丢失。
// @version 0.3
// @match http://*.duoshuo.com/admin/*
// @grant none
// ==/UserScript==

var threads;
try {
	threads = JSON.parse(localStorage.threadData);
} catch(e) {
	threads = [];
}
function save() {
	localStorage.threadData = JSON.stringify(threads);
}

$('<style>')
.html(`
.dsa-popup{background:white;position:fixed;top:70px;right:70px;padding:10px;border-radius:3px;box-shadow:0 0 5px rgba(0,0,0,.1);}
.dsa-dialog{background:white;position:absolute;top:50px;right:600px;width:400px;z-index:10;box-shadow:0 0 5px rgba(0,0,0,.1);border:1px solid gray;padding:10px;border-radius:3px;}
.dsa-dialog label{display:block;}
.dsa-dialog input[type=text],.dsa-dialog input[type=url]{display:block;width:100%;}
.dsa-panel{background:white;position:fixed;top:50px;right:0;bottom:0;width:600px;box-shadow:-2px 0 5px gray;}
.dsa-panel-body{position:absolute;top:30px;left:10px;right:10px;bottom:10px;overflow:auto;}
.dsa-panel table{width:100%;border-spacing:5px;border-collapse:separate;table-layout:fixed;}
.dsa-panel td>*{padding:5px;}
.dsa-panel .key{cursor:pointer;}
.dsa-panel .key:hover{background:olive;color:white;}
`)
.appendTo(document.head);

const menu = $('<ul class="dropdown-menu">');
$('<li class="dropdown">')
.prependTo('.navbar-nav')
.html('<a href class="dropdown-toggle" data-toggle="dropdown">评论管理<b class="caret"></b>')
.append(menu);

$('<li><a href="https://gerald.top/code/DuoshuoAdmin" target=_blank>使用须知</a></li>')
.appendTo(menu);

function EditPanel(thread) {
	this.thread = thread;
	[
		'onSubmit',
		'onClose',
		'onVisitURL',
		'onSolveConflict',
	].forEach(key => {
		this[key] = this[key].bind(this);
	});
	this.dialog = $('<form class="dsa-dialog">').appendTo(document.body)
	.html(`
	<h3>修改Thread</h3>
	<label>
		thread_key:
		<input type=text data-id=thread_key>
	</label>
	<label>
		title:
		<input type=text data-id=title>
	</label>
	<label>
		url: <a href="#" data-id=visit_url>[Go]</a>
		<input type=url data-id=url>
	</label>
	<label>
		<input type=checkbox data-id=post_enable>
		post_enable
	</label>
	<button class="btn btn-primary" type="submit">保存</button>
	<button class="btn btn-default" type="button" data-id=cancel>关闭</button>
	<button class="btn btn-default" data-id=conflict style="display:none">修改冲突</button>
	<div data-id=msg></div>
	`)
	.submit(this.onSubmit)
	.on('click', '[data-id=visit_url]', this.onVisitURL)
	.on('click', '[data-id=cancel]', this.onClose)
	.on('click', '[data-id=conflict]', this.onSolveConflict)
	;
	this.find('thread_key').val(thread.thread_key);
	this.find('title').val(thread.title);
	this.find('url').val(thread.url);
	this.find('post_enable').prop('checked', thread.post_enable);
}
EditPanel.panels = {};
EditPanel.open = function (thread) {
	var panel = EditPanel.panels[thread.thread_id];
	if (!panel) {
		panel = EditPanel.panels[thread.thread_id] = new EditPanel(thread);
	}
	panel.dialog.appendTo(document.body);
	return panel;
};
EditPanel.prototype.find = function (key) {
	return this.dialog.find(`[data-id=${JSON.stringify(key)}]`);
};
EditPanel.prototype.onSubmit = function (e) {
	e.preventDefault();
	$.post('/api/threads/update.json', {
		thread_id: this.thread.thread_id,
		thread_key: this.find('thread_key').val(),
		title: this.find('title').val(),
		url: this.find('url').val(),
		post_enable: this.find('post_enable').prop('checked') ? 1 : 0,
	}, r => {
		const thread = r.response;
		if (thread) {
			this.thread = thread;
			const i = threads.findIndex(_thread => _thread.thread_id === thread.thread_id);
			if (~i) threads[i] = thread;
			save();
			this.find('msg').html('修改成功!');
		} else {
			this.find('msg').html('<span style="color:red">修改失败!</span>');
			const conflict = this.find('conflict').show();
			this.find('thread_key').one('change', () => conflict.hide());
		}
	});
};
EditPanel.prototype.onVisitURL = function (e) {
	window.open(this.find('url').val());
};
EditPanel.prototype.onSolveConflict = function (e) {
	$.get('/api/threads/listPosts.json', {
		thread_key: this.find('thread_key').val(),
		limit: 1,
	}, r => {
		EditPanel.open(r.thread);
	});
};
EditPanel.prototype.onClose = function (e) {
	this.dialog.remove();
};

function getThreadByKey(key, cb) {
}
var panel=$('<div class="dsa-panel">').appendTo(document.body).hide();
$('<a href>关闭</a>').appendTo(panel).css({
	position:'absolute',
	top:10,
	right:20,
}).click(function(e){
	e.preventDefault();
	panel.hide();
});
panel.area=$('<div class="dsa-panel-body">').appendTo(panel);
$('<a href>显示文章列表</a>')
.appendTo($('<li>').appendTo(menu))
.click(function(e){
	e.preventDefault();
	panel.area.html('');
	var table=$('<table>').appendTo(panel.area);
	$('<colgroup><col width=100><col width=200><col></colgroup>').appendTo(table);
	$('<tr><th>thread_key</th><th>title</th><th>url</th></tr>').appendTo(table);
	threads.forEach(thread => {
		const row = $('<tr>').appendTo(table);
		$('<span class="key">')
		.text(thread.thread_key)
		.click(() => EditPanel.open(thread))
		.wrap('<td>').parent().appendTo(row);
		$('<td>').text(thread.title).appendTo(row);
		$('<a target=_blank>')
		.attr('href', thread.url)
		.text(thread.url)
		.wrap('<td>').parent().appendTo(row);
	});
	panel.show();
});

$('<a href>自动分析评论</a>')
.appendTo($('<li>').appendTo(menu))
.click(function(e){
	e.preventDefault();
	var dthreads={};
	var limit=100,n=~~(DUOSHUO.site.comments/limit),i;
	var finished=0;
	var popup=$('<div class="dsa-popup">').html('正在分析评论:<span></span>/'+n).appendTo(document.body);
	var prog=popup.find('span').html(finished);
	for(i=0;i<n;i++)
		$.get('/api/posts/list.json',{
			order:'desc',
			source:'duoshuo,repost',
			max_depth:1,
			limit:limit,
			'related[]':'thread',
			nonce:DUOSHUO.nonce,
			status:'all',
			page:i+1,
		},function(r){
			$.each(r.response,function(i,p){
				var t=p.thread;
				dthreads[t.thread_id]=t;
			});
			prog.html(++finished);
			if(finished==n) {
				popup.html('评论分析完成!');
				popup.fadeOut(5000,function(){
					popup.remove();
				});
				threads=[];
				for(var i in dthreads) threads.push(dthreads[i]);
				threads.sort(function(a,b){return a.thread_id>b.thread_id;});
				save();
			}
		});
});