// ==UserScript==
// @name CookieCloud
// @namespace http://tampermonkey.net/
// @version v0.13
// @description CookieCloud的tampermonkey版本,目前仅支持上传cookie,兼容移动端gear浏览器;
// @author tomato
// @icon https://store-images.s-microsoft.com/image/apps.63473.a0ccb631-d5e7-422b-bcc7-c0405274114b.be044f83-1292-4e84-a65d-e0527d895863.05fc1666-519a-4d36-8b67-8110c70b45cc?mode=scale&h=64&q=90&w=64
// @match *://*/*
// @grant GM_cookie
// @grant GM_xmlhttpRequest
// @grant GM_notification
// @connect *
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js
// @run-at document-start
// @license MIT
// ==/UserScript==
/* global $, jQuery, CryptoJS */
(function() {
'use strict';
const configStoreKey = '_cookieCloudConfig';
async function init() {
const btnContainer = document.createElement('section');
const asyncBtn = document.createElement('button');
const asyncConfigBtn = document.createElement('button');
asyncBtn.innerText = '上传';
asyncConfigBtn.innerText = '配置';
const btnContainerStyles = {
position: 'fixed',
bottom: '200px',
left: '20px',
fontSize: '14px',
zIndex: 1000,
};
const btnStyles = {
display: 'block',
width: '50px',
height: '50px',
borderRadius: '50%',
backgroundColor: '#87CEEB',
border: 'none',
color: '#fff',
marginBottom: '10px',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.3)'
}
Object.assign(btnContainer.style, btnContainerStyles);
Object.assign(asyncBtn.style, btnStyles);
Object.assign(asyncConfigBtn.style, btnStyles);
btnContainer.appendChild(asyncBtn);
btnContainer.appendChild(asyncConfigBtn);
document.body.appendChild(btnContainer);
asyncConfigBtn.onclick = function() {
const modal = initConfigForm();
document.body.appendChild(modal);
}
// 为按钮添加点击事件
asyncBtn.onclick = async function(event) {
event.stopPropagation();
const config = localStorage.getItem(configStoreKey);
if (!config) {
msg('请填写配置');
return;
};
const {url, uuid, password, domain = location.host} = JSON.parse(config);
if (!url) {
msg('请填写服务器地址');
return;
};
if (!uuid) {
msg('请填写uuid');
return;
};
if (!password) {
msg('请填写密码');
return;
};
const cookies = await getCookie(domain);
const encryptCookies = cookie_encrypt(uuid, password, cookies);
const payload = {
uuid,
encrypted: encryptCookies
};
const res = await syncCookie(url, payload);
try {
const resData = JSON.parse(res.response)
console.log('resData:', resData);
if (resData.action === 'done') {
msg('同步成功')
} else {
throw('错误')
}
} catch(e) {
msg(String(e))
}
};
}
const msg = (function () {
const originalAlert = window.alert;
return (title) => {
originalAlert(title);
}
})();
function initConfigForm() {
// 创建遮罩层
const overlay = document.createElement('div');
overlay.style.position = 'fixed';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
overlay.style.zIndex = '1001';
// 创建弹框(Modal)容器
const modal = document.createElement('div');
const modalStyles = {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '95%',
maxWidth: '400px',
backgroundColor: '#fff',
padding: '20px',
boxShadow: '0 4px 10px rgba(0, 0, 0, 0.3)',
zIndex: '1000',
borderRadius: '8px',
};
Object.assign(modal.style, modalStyles);
// 创建表单
const form = document.createElement('form');
const inputStyles = {
width: '100%',
padding: '8px',
boxSizing: 'border-box',
border: '1px solid #ddd',
outline: 'none',
borderRadius: '8px',
marginBottom: '10px'
};
// 创建同步域名关键词·默认当前域名
const domainEle = document.createElement('input');
domainEle.type = 'text';
domainEle.placeholder = '同步域名关键词·默认当前域名';
Object.assign(domainEle.style, inputStyles);
// 创建输入框 服务器地址
const urlEle = document.createElement('input');
urlEle.type = 'text';
urlEle.placeholder = '服务器地址';
Object.assign(urlEle.style, inputStyles);
// 创建输入框 端对端加密密码
const pwdEle = document.createElement('input');
pwdEle.type = 'text';
pwdEle.placeholder = '输入框 端对端加密密码';
Object.assign(pwdEle.style, inputStyles);
// 创建输入框 用户KEY · UUID
const uuieEle = document.createElement('input');
uuieEle.type = 'text';
uuieEle.placeholder = '用户KEY · UUID';
Object.assign(uuieEle.style, inputStyles);
// 创建保存按钮
const saveButton = document.createElement('button');
saveButton.type = 'submit';
saveButton.innerText = '保存';
saveButton.style.width = '100%';
saveButton.style.padding = '10px';
saveButton.style.backgroundColor = '#87CEEB';
saveButton.style.border = 'none';
saveButton.style.color = '#fff';
saveButton.style.cursor = 'pointer';
saveButton.style.fontSize = '16px';
saveButton.style.borderRadius = '4px';
const config = localStorage.getItem(configStoreKey);
if (config) {
const {url, uuid, password, domain = ''} = JSON.parse(config);
urlEle.value = url;
pwdEle.value = password;
uuieEle.value = uuid;
domainEle.value = domain;
};
saveButton.onclick = function () {
const configStr = JSON.stringify({
url: urlEle.value,
password: pwdEle.value,
uuid: uuieEle.value,
domain: domainEle.value
});
localStorage.setItem(configStoreKey, configStr);
overlay.remove();
}
modal.onclick = function (event) {
event.stopPropagation();
}
overlay.onclick = function () {
overlay.remove();
}
// 将输入框和保存按钮添加到表单
form.appendChild(domainEle);
form.appendChild(urlEle);
form.appendChild(pwdEle);
form.appendChild(uuieEle);
form.appendChild(saveButton);
// 将表单添加到弹框中
modal.appendChild(form);
overlay.appendChild(modal);
return overlay;
}
// 用aes对cookie进行加密
function cookie_encrypt( uuid, password, cookies ) {
const the_key = CryptoJS.MD5(uuid+'-'+password).toString().substring(0,16);
const data_to_encrypt = JSON.stringify({"cookie_data":cookies,"update_time":new Date()});
return CryptoJS.AES.encrypt(data_to_encrypt, the_key).toString();
}
async function getCookie(domain) {
return new Promise((res, rej) => {
GM_cookie.list({}, function(cookies, error) {
if (!error) {
const ret_cookies = {};
for( const cookie of cookies ) {
if( cookie.domain?.includes(domain) ) {
if (!ret_cookies[domain]) {
ret_cookies[domain] = [cookie];
} else {
ret_cookies[domain].push(cookie);
}
}
}
res(ret_cookies);
} else {
console.error(error);
rej(error)
}
});
})
}
// 上传cookie
async function syncCookie(url, body) {
return new Promise((res, rej) => {
GM_xmlhttpRequest({
method: 'POST',
url: url+'/update',
data: JSON.stringify(body),
headers: {
'Content-Type': 'application/json',
},
onload: function(response) {
console.log('Response:', response.responseText);
res(response);
},
onerror: function(error) {
console.error('Error:', error);
rej(error);
}
});
})
}
window.addEventListener('load', init);
})();