471 lines
23 KiB
JavaScript
471 lines
23 KiB
JavaScript
// ==UserScript==
|
|
// @name NGA优化摸鱼体验插件-自动同步数据
|
|
// @namespace https://github.com/lifegpc/userscript/tree/master/NGA_BBS_plugins/AutoSyncData
|
|
// @version 1.0.3
|
|
// @author lifegpc
|
|
// @description 通过WebDAV自动同步数据
|
|
// @license MIT
|
|
// @match *://bbs.nga.cn/*
|
|
// @match *://ngabbs.com/*
|
|
// @match *://nga.178.com/*
|
|
// @match *://g.nga.cn/*
|
|
// @require https://cdn.staticfile.org/blueimp-md5/2.19.0/js/md5.min.js
|
|
// @grant unsafeWindow
|
|
// @grant GM_xmlhttpRequest
|
|
// @run-at document-start
|
|
// @inject-into content
|
|
// ==/UserScript==
|
|
|
|
(function (registerPlugin) {
|
|
'use strict';
|
|
const AutoSyncData = {
|
|
name: 'AutoSyncData',
|
|
title: '自动同步数据',
|
|
desc: '通过WebDAV自动同步数据',
|
|
settings: [{
|
|
key: 'url',
|
|
title: 'WebDAV地址',
|
|
default: ''
|
|
}, {
|
|
key: 'username',
|
|
title: 'WebDAV账号',
|
|
default: ''
|
|
}, {
|
|
key: 'password',
|
|
title: 'WebDAV密码',
|
|
default: ''
|
|
}, {
|
|
key: 'backupKeywordsList',
|
|
title: '备份关键词列表',
|
|
default: true
|
|
}, {
|
|
key: 'keywordsListFileName',
|
|
title: '关键词列表文件名',
|
|
default: 'nga_bbs_script_data_keywords_list.json'
|
|
}, {
|
|
key: 'backupBanList',
|
|
title: '备份黑名单列表',
|
|
default: true
|
|
}, {
|
|
key: 'banListFileName',
|
|
title: '黑名单列表文件名',
|
|
default: 'nga_bbs_script_data_ban_list.json'
|
|
}, {
|
|
key: 'backupMarkList',
|
|
title: '备份标记名单列表',
|
|
default: true
|
|
}, {
|
|
key: 'markListFileName',
|
|
title: '标记名单列表文件名',
|
|
default: 'nga_bbs_script_data_mark_list.json'
|
|
}, {
|
|
key: 'metaFileName',
|
|
title: '元数据文件名',
|
|
default: 'nga_bbs_script_autosync_meta.json'
|
|
}, {
|
|
key: 'checkInterval',
|
|
title: '检查间隔(单位:分)',
|
|
default: 5
|
|
}],
|
|
buttons: [{
|
|
title: '检查连接',
|
|
action: 'testConnections'
|
|
}, {
|
|
title: '备份',
|
|
action: 'backup'
|
|
}, {
|
|
title: '还原',
|
|
action: 'restore'
|
|
}],
|
|
lastCheckInfo: {},
|
|
beforeSaveSettingFunc(settings) {
|
|
if (settings['checkInterval'] < 0) {
|
|
return '检查间隔不能小于0。'
|
|
}
|
|
if (settings['keywordsListFileName'] == '') {
|
|
return '关键词列表文件名不能为空。'
|
|
}
|
|
if (settings['banListFileName'] == '') {
|
|
return '黑名单列表文件名不能为空。'
|
|
}
|
|
if (settings['markListFileName'] == '') {
|
|
return '标记名单列表文件名不能为空。'
|
|
}
|
|
if (settings['metaFileName'] == '') {
|
|
return '元数据文件名不能为空。'
|
|
}
|
|
},
|
|
initFunc() {
|
|
if (typeof this.pluginSettings['backupKeywordsList'] === 'string') {
|
|
this.pluginSettings['backupKeywordsList'] = true
|
|
}
|
|
if (typeof this.pluginSettings['keywordsListFileName'] !== 'string' || this.pluginSettings['keywordsListFileName'] === '') {
|
|
this.pluginSettings['keywordsListFileName'] = 'nga_bbs_script_data_keywords_list.json'
|
|
}
|
|
if (typeof this.pluginSettings['backupBanList'] === 'string') {
|
|
this.pluginSettings['backupBanList'] = true
|
|
}
|
|
if (typeof this.pluginSettings['banListFileName'] !== 'string' || this.pluginSettings['banListFileName'] === '') {
|
|
this.pluginSettings['banListFileName'] = 'nga_bbs_script_data_ban_list.json'
|
|
}
|
|
if (typeof this.pluginSettings['backupMarkList'] === 'string') {
|
|
this.pluginSettings['backupMarkList'] = true
|
|
}
|
|
if (typeof this.pluginSettings['markListFileName'] !== 'string' || this.pluginSettings['markListFileName'] === '') {
|
|
this.pluginSettings['markListFileName'] = 'nga_bbs_script_data_mark_list.json'
|
|
}
|
|
if (typeof this.pluginSettings['metaFileName'] !== 'string' || this.pluginSettings['metaFileName'] === '') {
|
|
this.pluginSettings['metaFileName'] = 'nga_bbs_script_autosync_meta.json'
|
|
}
|
|
if (typeof this.pluginSettings['checkInterval'] !== 'number' || this.pluginSettings['checkInterval'] < 0) {
|
|
this.pluginSettings['checkInterval'] = 5
|
|
}
|
|
try {
|
|
this.lastCheckInfo = JSON.parse(this.mainScript.getValue("lifegpc__AutoSyncData_lastCheckInfo") ?? "{}");
|
|
} catch (e) {
|
|
this.printLog('读取上次检查信息失败', 'err');
|
|
console.error(e);
|
|
}
|
|
},
|
|
postProcFunc() {
|
|
if (this.pluginInputs['url'].val()) {
|
|
const _this = this;
|
|
const handler = async () => {
|
|
const lastCheckTime = _this.lastCheckInfo['lastCheckTime'];
|
|
let lastCheckInfoChanged = false;
|
|
try {
|
|
if (typeof lastCheckTime !== 'number' || lastCheckTime + _this.pluginSettings['checkInterval'] * 60 * 1000 < Date.now()) {
|
|
const re = await _this.request({
|
|
method: 'GET',
|
|
path: _this.pluginSettings['metaFileName']
|
|
});
|
|
if (re == null) {
|
|
await _this.backup(true);
|
|
return;
|
|
}
|
|
const latestMeta = JSON.parse(re.responseText);
|
|
const localMeta = _this.lastCheckInfo['meta'] || {};
|
|
if (latestMeta.backupBanList) {
|
|
if (latestMeta.banListMd5 !== localMeta.banListMd5) {
|
|
const filename = latestMeta.banListFileName;
|
|
const banListStr = (await _this.request({
|
|
method: 'GET',
|
|
path: filename
|
|
})).responseText;
|
|
const banList = JSON.parse(banListStr);
|
|
_this.mainScript.getModule('MarkAndBan').banList = banList;
|
|
_this.mainScript.setValue("hld__NGA_ban_list", banListStr);
|
|
_this.mainScript.popNotification('黑名单列表已更新');
|
|
lastCheckInfoChanged = true;
|
|
}
|
|
}
|
|
if (latestMeta.backupKeywordsList) {
|
|
if (latestMeta.keywordsListMd5 !== localMeta.keywordsListMd5) {
|
|
const filename = latestMeta.keywordsListFileName;
|
|
const keywordsListStr = (await _this.request({
|
|
method: 'GET',
|
|
path: filename
|
|
})).responseText;
|
|
const keywordsList = JSON.parse(keywordsListStr);
|
|
_this.mainScript.getModule('KeywordsBlock').keywordsList = keywordsList;
|
|
_this.mainScript.setValue("hld__NGA_keywords_list", keywordsListStr);
|
|
_this.mainScript.popNotification('关键词列表已更新');
|
|
lastCheckInfoChanged = true;
|
|
}
|
|
}
|
|
if (latestMeta.backupMarkList) {
|
|
if (latestMeta.markListMd5 !== localMeta.markListMd5) {
|
|
const filename = latestMeta.markListFileName;
|
|
const markListStr = (await _this.request({
|
|
method: 'GET',
|
|
path: filename
|
|
})).responseText;
|
|
const markList = JSON.parse(markListStr);
|
|
_this.mainScript.getModule('MarkAndBan').markList = markList;
|
|
_this.mainScript.setValue("hld__NGA_mark_list", markListStr);
|
|
_this.mainScript.popNotification('标记名单列表已更新');
|
|
lastCheckInfoChanged = true;
|
|
}
|
|
}
|
|
_this.lastCheckInfo['meta'] = latestMeta;
|
|
lastCheckInfoChanged = true;
|
|
_this.lastCheckInfo['lastCheckTime'] = Date.now();
|
|
}
|
|
let metaChanged = false;
|
|
if (_this.pluginSettings['backupBanList']) {
|
|
const banList = _this.mainScript.getModule('MarkAndBan').banList;
|
|
const banListStr = JSON.stringify(banList);
|
|
const filename = _this.pluginSettings['banListFileName'];
|
|
const md5Sum = md5(banListStr);
|
|
const meta = _this.lastCheckInfo['meta'] || {};
|
|
if (md5Sum !== meta['banListMd5']) {
|
|
await _this.request({
|
|
method: 'PUT',
|
|
path: filename,
|
|
data: banListStr
|
|
})
|
|
meta['banListMd5'] = md5Sum;
|
|
meta['banListModifiedTime'] = Date.now();
|
|
lastCheckInfoChanged = true;
|
|
metaChanged = true;
|
|
_this.mainScript.popNotification('已备份黑名单列表')
|
|
}
|
|
}
|
|
if (_this.pluginSettings['backupKeywordsList']) {
|
|
const keywordsList = _this.mainScript.getModule('KeywordsBlock').keywordsList;
|
|
const keywordsListStr = JSON.stringify(keywordsList);
|
|
const filename = _this.pluginSettings['keywordsListFileName'];
|
|
const md5Sum = md5(keywordsListStr);
|
|
const meta = _this.lastCheckInfo['meta'] || {};
|
|
if (md5Sum !== meta['keywordsListMd5']) {
|
|
await _this.request({
|
|
method: 'PUT',
|
|
path: filename,
|
|
data: keywordsListStr
|
|
})
|
|
meta['keywordsListMd5'] = md5Sum;
|
|
meta['keywordsListModifiedTime'] = Date.now();
|
|
lastCheckInfoChanged = true;
|
|
metaChanged = true;
|
|
_this.mainScript.popNotification('已备份关键词列表')
|
|
}
|
|
}
|
|
if (_this.pluginSettings['backupMarkList']) {
|
|
const markList = _this.mainScript.getModule('MarkAndBan').markList;
|
|
const markListStr = JSON.stringify(markList);
|
|
const filename = _this.pluginSettings['markListFileName'];
|
|
const md5Sum = md5(markListStr);
|
|
const meta = _this.lastCheckInfo['meta'] || {};
|
|
if (md5Sum !== meta['markListMd5']) {
|
|
await _this.request({
|
|
method: 'PUT',
|
|
path: filename,
|
|
data: markListStr
|
|
})
|
|
meta['markListMd5'] = md5Sum;
|
|
meta['markListModifiedTime'] = Date.now();
|
|
lastCheckInfoChanged = true;
|
|
metaChanged = true;
|
|
_this.mainScript.popNotification('已备份标记名单列表')
|
|
}
|
|
}
|
|
if (metaChanged) {
|
|
await _this.request({
|
|
method: 'PUT',
|
|
path: _this.pluginSettings['metaFileName'],
|
|
data: JSON.stringify(_this.lastCheckInfo['meta'])
|
|
});
|
|
_this.mainScript.popNotification('数据已同步');
|
|
}
|
|
} catch (e) {
|
|
_this.mainScript.printLog('检查连接失败');
|
|
console.log(e);
|
|
} finally {
|
|
try {
|
|
if (lastCheckInfoChanged) {
|
|
_this.mainScript.setValue("lifegpc__AutoSyncData_lastCheckInfo", JSON.stringify(_this.lastCheckInfo));
|
|
}
|
|
} catch (e) {
|
|
_this.mainScript.printLog('保存检查信息失败');
|
|
console.error(e);
|
|
}
|
|
setTimeout(handler, 1000);
|
|
}
|
|
}
|
|
this.timeout = setTimeout(handler, 1000);
|
|
}
|
|
},
|
|
// 请求构造
|
|
request({ method, path = '', headers, ...config }) {
|
|
// 获取输入框的当前的值
|
|
let url = this.pluginInputs['url'].val().trim()
|
|
url[url.length - 1] !== '/' && (url += '/')
|
|
const username = this.pluginInputs['username'].val().trim()
|
|
const password = this.pluginInputs['password'].val().trim()
|
|
const methodDict = {
|
|
PROPFIND: 207,
|
|
GET: 200,
|
|
PUT: 201,
|
|
DELETE: 204
|
|
}
|
|
this.buttons.forEach(button => button.$el.attr('disabled', true))
|
|
return new Promise((resolve, reject) => {
|
|
GM_xmlhttpRequest({
|
|
method,
|
|
url: url + path,
|
|
headers: {
|
|
authorization: 'Basic ' + btoa(`${username}:${password}`),
|
|
'Cache-control': 'no-cache',
|
|
...headers
|
|
},
|
|
...config,
|
|
onload: response => {
|
|
this.buttons.forEach(button => button.$el.removeAttr('disabled'))
|
|
if (response.status === methodDict[method]) {
|
|
resolve(response)
|
|
} else {
|
|
if (method == 'GET' && response.status == 404) {
|
|
resolve(null);
|
|
return;
|
|
}
|
|
this.mainScript.popMsg(`WebDAV请求失败! 状态码: ${response.status} ${response.statusText}`, 'err')
|
|
}
|
|
},
|
|
onerror: error => {
|
|
reject(error);
|
|
this.buttons.forEach(button => button.$el.removeAttr('disabled'))
|
|
this.mainScript.popMsg(`WebDAV请求失败!${error}`);
|
|
}
|
|
})
|
|
})
|
|
},
|
|
// 获取文件列表
|
|
getFileList() {
|
|
return new Promise((resolve, reject) => {
|
|
this.request({
|
|
method: 'PROPFIND',
|
|
headers: { depth: 1 }
|
|
})
|
|
.then(res => {
|
|
let files = []
|
|
let path = res.responseText.match(/(?<=<d:href>).*?(?=<\/d:href>)/gi)
|
|
path.forEach(p => {
|
|
const filename = p.split('/').pop();
|
|
files.push(filename);
|
|
})
|
|
resolve(files)
|
|
})
|
|
})
|
|
},
|
|
// 测试连通性
|
|
async testConnections() {
|
|
await this.getFileList()
|
|
this.mainScript.popMsg('连接成功!同步配置看起来没问题')
|
|
},
|
|
// 备份配置
|
|
async backup(saveMeta = false) {
|
|
const meta = {
|
|
backupBanList: this.pluginSettings['backupBanList'],
|
|
backupKeywordsList: this.pluginSettings['backupKeywordsList'],
|
|
backupMarkList: this.pluginSettings['backupMarkList'],
|
|
banListFileName: this.pluginSettings['banListFileName'],
|
|
banListMd5: '',
|
|
keywordsListFileName: this.pluginSettings['keywordsListFileName'],
|
|
keywordsListMd5: '',
|
|
markListFileName: this.pluginSettings['markListFileName'],
|
|
markListMd5: '',
|
|
backupTime: Date.now(),
|
|
};
|
|
const bmeta = this.lastCheckInfo['meta'] || {};
|
|
if (meta.backupBanList) {
|
|
const banList = this.mainScript.getModule('MarkAndBan').banList;
|
|
const banListStr = JSON.stringify(banList);
|
|
const filename = meta.banListFileName;
|
|
meta.banListMd5 = md5(banListStr);
|
|
meta.banListModifiedTime = bmeta['banListModifiedTime'] || Date.now();
|
|
await this.request({
|
|
method: 'PUT',
|
|
path: filename,
|
|
data: banListStr
|
|
})
|
|
}
|
|
if (meta.backupKeywordsList) {
|
|
const keywordsList = this.mainScript.getModule('KeywordsBlock').keywordsList;
|
|
const keywordsListStr = JSON.stringify(keywordsList);
|
|
const filename = meta.keywordsListFileName;
|
|
meta.keywordsListMd5 = md5(keywordsListStr);
|
|
meta.keywordsListModifiedTime = bmeta['keywordsListModifiedTime'] || Date.now();
|
|
await this.request({
|
|
method: 'PUT',
|
|
path: filename,
|
|
data: keywordsListStr
|
|
})
|
|
}
|
|
if (meta.backupMarkList) {
|
|
const markList = this.mainScript.getModule('MarkAndBan').markList;
|
|
const markListStr = JSON.stringify(markList);
|
|
const filename = meta.markListFileName;
|
|
meta.markListMd5 = md5(markListStr);
|
|
meta.markListModifiedTime = bmeta['markListModifiedTime'] || Date.now();
|
|
await this.request({
|
|
method: 'PUT',
|
|
path: filename,
|
|
data: markListStr
|
|
})
|
|
}
|
|
const metaStr = JSON.stringify(meta);
|
|
const filename = this.pluginSettings['metaFileName'];
|
|
await this.request({
|
|
method: 'PUT',
|
|
path: filename,
|
|
data: metaStr
|
|
})
|
|
this.mainScript.popMsg(`备份成功`)
|
|
if (saveMeta) {
|
|
this.lastCheckInfo['meta'] = meta;
|
|
this.lastCheckInfo['lastCheckTime'] = Date.now();
|
|
this.mainScript.setValue("lifegpc__AutoSyncData_lastCheckInfo", JSON.stringify(this.lastCheckInfo));
|
|
}
|
|
},
|
|
async restore() {
|
|
const filename = this.pluginSettings['metaFileName'];
|
|
const meta = JSON.parse((await this.request({
|
|
method: 'GET',
|
|
path: filename
|
|
})).responseText);
|
|
if (meta.backupBanList) {
|
|
const banList = this.mainScript.getModule('MarkAndBan').banList;
|
|
const banListStr = JSON.stringify(banList);
|
|
if (meta.banListMd5 !== md5(banListStr)) {
|
|
const filename = meta.banListFileName;
|
|
const banListStr = (await this.request({
|
|
method: 'GET',
|
|
path: filename
|
|
})).responseText;
|
|
const banList = JSON.parse(banListStr);
|
|
this.mainScript.getModule('MarkAndBan').banList = banList;
|
|
this.mainScript.setValue("hld__NGA_ban_list", banListStr);
|
|
this.mainScript.popNotification('黑名单列表已还原');
|
|
}
|
|
}
|
|
if (meta.backupKeywordsList) {
|
|
const keywordsList = this.mainScript.getModule('KeywordsBlock').keywordsList;
|
|
const keywordsListStr = JSON.stringify(keywordsList);
|
|
if (meta.keywordsListMd5 !== md5(keywordsListStr)) {
|
|
const filename = meta.keywordsListFileName;
|
|
const keywordsListStr = (await this.request({
|
|
method: 'GET',
|
|
path: filename
|
|
})).responseText;
|
|
const keywordsList = JSON.parse(keywordsListStr);
|
|
this.mainScript.getModule('KeywordsBlock').keywordsList = keywordsList;
|
|
this.mainScript.setValue("hld__NGA_keywords_list", keywordsListStr);
|
|
this.mainScript.popNotification('关键词列表已还原');
|
|
}
|
|
}
|
|
if (meta.backupMarkList) {
|
|
const markList = this.mainScript.getModule('MarkAndBan').markList;
|
|
const markListStr = JSON.stringify(markList);
|
|
if (meta.markListMd5 !== md5(markListStr)) {
|
|
const filename = meta.markListFileName;
|
|
const markListStr = (await this.request({
|
|
method: 'GET',
|
|
path: filename
|
|
})).responseText;
|
|
const markList = JSON.parse(markListStr);
|
|
this.mainScript.getModule('MarkAndBan').markList = markList;
|
|
this.mainScript.setValue("hld__NGA_mark_list", markListStr);
|
|
this.mainScript.popNotification('标记名单列表已还原');
|
|
}
|
|
}
|
|
this.mainScript.popMsg(`还原成功`)
|
|
}
|
|
}
|
|
registerPlugin(AutoSyncData)
|
|
|
|
})(function (plugin) {
|
|
plugin.meta = GM_info.script
|
|
unsafeWindow.ngaScriptPlugins = unsafeWindow.ngaScriptPlugins || []
|
|
unsafeWindow.ngaScriptPlugins.push(plugin)
|
|
});
|