enhance shift function
This commit is contained in:
202
addCheckboxes.js
202
addCheckboxes.js
@@ -1,122 +1,152 @@
|
||||
console.log('addCheckboxes.js loaded');
|
||||
console.log("addCheckboxes.js loaded");
|
||||
|
||||
// 查找带有特定选择器的祖先元素
|
||||
function findAncestorWithCheckbox(el, selector) {
|
||||
while ((el = el.parentElement) && !el.querySelector(selector));
|
||||
return el;
|
||||
// Create a new checkbox element
|
||||
function createCheckbox(index) {
|
||||
const checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.className = CHECKBOX_CLASS;
|
||||
checkbox.dataset.index = index;
|
||||
checkbox.addEventListener("click", preventEventPropagation);
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
// 切换复选框的选中状态
|
||||
function toggleCheckbox(event) {
|
||||
// 阻止事件的默认行为(例如链接跳转)
|
||||
event.preventDefault();
|
||||
// 阻止事件冒泡到父元素
|
||||
// Add click event listener to an element
|
||||
function addClickEventListener(element) {
|
||||
const handleTitleClick = (event) => {
|
||||
toggleCheckbox(event);
|
||||
event.stopPropagation();
|
||||
};
|
||||
element.addEventListener("click", handleTitleClick);
|
||||
element.dataset.hasClickListener = "true";
|
||||
}
|
||||
|
||||
const parentElement = findAncestorWithCheckbox(event.currentTarget, `.${CHECKBOX_CLASS}`);
|
||||
const checkbox = parentElement ? parentElement.querySelector(`.${CHECKBOX_CLASS}`) : null;
|
||||
if (checkbox) {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
checkPreviousCheckboxes(checkbox);
|
||||
function findAncestorWithCheckbox(el, selector) {
|
||||
while ((el = el.parentElement) && !el.querySelector(selector));
|
||||
return el;
|
||||
}
|
||||
|
||||
// Toggle the checkbox's checked state
|
||||
function toggleCheckbox(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const parentElement = findAncestorWithCheckbox(
|
||||
event.currentTarget,
|
||||
`.${CHECKBOX_CLASS}`
|
||||
);
|
||||
const checkbox = parentElement
|
||||
? parentElement.querySelector(`.${CHECKBOX_CLASS}`)
|
||||
: null;
|
||||
if (checkbox) {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
checkPreviousCheckboxes(checkbox);
|
||||
|
||||
// 更新最后选中的复选框
|
||||
if (checkbox.checked) {
|
||||
window.lastCheckedCheckbox = checkbox;
|
||||
}
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
function handleCheckboxClick(event) {
|
||||
// 阻止事件冒泡
|
||||
event.stopPropagation();
|
||||
event.stopPropagation();
|
||||
|
||||
checkPreviousCheckboxes(event.target);
|
||||
const clickedCheckbox = event.target;
|
||||
checkPreviousCheckboxes(clickedCheckbox);
|
||||
|
||||
// 更新最后选中的复选框
|
||||
window.lastCheckedCheckbox = clickedCheckbox;
|
||||
}
|
||||
|
||||
function checkPreviousCheckboxes(clickedCheckbox) {
|
||||
if (shiftPressed && clickedCheckbox.checked) {
|
||||
const allCheckboxes = Array.from(document.querySelectorAll(`.${CHECKBOX_CLASS}`));
|
||||
const previousCheckboxes = allCheckboxes.slice(0, allCheckboxes.indexOf(clickedCheckbox));
|
||||
if (window.shiftPressed && window.lastCheckedCheckbox) {
|
||||
const allCheckboxes = Array.from(
|
||||
document.querySelectorAll(`.${CHECKBOX_CLASS}`)
|
||||
);
|
||||
const start = allCheckboxes.indexOf(window.lastCheckedCheckbox);
|
||||
const end = allCheckboxes.indexOf(clickedCheckbox);
|
||||
|
||||
let index = previousCheckboxes.length - 1;
|
||||
while (index >= 0 && !previousCheckboxes[index].checked) {
|
||||
index--;
|
||||
}
|
||||
const [lower, upper] = start < end ? [start, end] : [end, start];
|
||||
|
||||
// A negative index means no previous checkbox is checked
|
||||
if (index >= 0) {
|
||||
previousCheckboxes.slice(index).forEach((checkbox) => {
|
||||
checkbox.checked = true;
|
||||
});
|
||||
}
|
||||
for (let i = lower; i <= upper; i++) {
|
||||
allCheckboxes[i].checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addShiftKeyEventListeners() {
|
||||
console.log('Adding Shift key event listeners...');
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Shift') {
|
||||
shiftPressed = true;
|
||||
}
|
||||
});
|
||||
console.log("Adding Shift key event listeners...");
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Shift") {
|
||||
console.log("Shift key pressed");
|
||||
window.shiftPressed = true;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', (event) => {
|
||||
if (event.key === 'Shift') {
|
||||
shiftPressed = false;
|
||||
}
|
||||
});
|
||||
document.addEventListener("keyup", (event) => {
|
||||
if (event.key === "Shift") {
|
||||
console.log("Shift key released");
|
||||
window.shiftPressed = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 添加复选框到每个对话
|
||||
function addCheckboxes() {
|
||||
console.log('Adding checkboxes to conversations...', Selectors);
|
||||
const conversations = document.querySelectorAll(Selectors.CONVERSATION_SELECTOR);
|
||||
console.log("Adding checkboxes to conversations...", Selectors);
|
||||
const conversations = document.querySelectorAll(
|
||||
Selectors.CONVERSATION_SELECTOR
|
||||
);
|
||||
|
||||
conversations.forEach((conversation, index) => {
|
||||
let existingCheckbox = conversation.querySelector(`.${CHECKBOX_CLASS}`);
|
||||
conversations.forEach((conversation, index) => {
|
||||
let existingCheckbox = conversation.querySelector(`.${CHECKBOX_CLASS}`);
|
||||
|
||||
// 如果复选框已存在,获取其选中状态并移除它
|
||||
let isChecked = existingCheckbox ? existingCheckbox.checked : false;
|
||||
if (existingCheckbox) {
|
||||
existingCheckbox.remove();
|
||||
}
|
||||
// 如果复选框已存在,获取其选中状态并移除它
|
||||
let isChecked = existingCheckbox ? existingCheckbox.checked : false;
|
||||
if (existingCheckbox) {
|
||||
existingCheckbox.remove();
|
||||
}
|
||||
|
||||
// 创建新的复选框并设置其属性
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.className = CHECKBOX_CLASS;
|
||||
checkbox.dataset.index = index;
|
||||
checkbox.checked = isChecked;
|
||||
checkbox.addEventListener('click', handleCheckboxClick);
|
||||
conversation.insertAdjacentElement('afterbegin', checkbox);
|
||||
// 创建新的复选框并设置其属性
|
||||
const checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.className = CHECKBOX_CLASS;
|
||||
checkbox.dataset.index = index;
|
||||
checkbox.checked = isChecked;
|
||||
checkbox.addEventListener("click", handleCheckboxClick);
|
||||
conversation.insertAdjacentElement("afterbegin", checkbox);
|
||||
|
||||
// 为对话标题添加点击事件
|
||||
const titleElement = conversation.querySelector(Selectors.TITLE_SELECTOR);
|
||||
if (titleElement) {
|
||||
titleElement.style.cursor = 'default';
|
||||
// add click event listener to the title element
|
||||
const titleElement = conversation.querySelector(Selectors.TITLE_SELECTOR);
|
||||
if (titleElement) {
|
||||
titleElement.style.cursor = "default";
|
||||
|
||||
// 获取 titleElement 的父元素
|
||||
const parentElement = titleElement.parentElement;
|
||||
// get the parent element of titleElement
|
||||
const parentElement = titleElement.parentElement;
|
||||
|
||||
// 定义一个通用的事件处理函数
|
||||
const handleTitleClick = (event) => {
|
||||
toggleCheckbox(event);
|
||||
event.stopPropagation(); // 防止事件冒泡
|
||||
};
|
||||
// define a common event handler
|
||||
const handleTitleClick = (event) => {
|
||||
toggleCheckbox(event);
|
||||
event.stopPropagation(); // prevent event propagation
|
||||
};
|
||||
|
||||
// 为 titleElement 添加点击事件
|
||||
if (!titleElement.dataset.hasClickListener) {
|
||||
titleElement.addEventListener('click', handleTitleClick);
|
||||
titleElement.dataset.hasClickListener = 'true';
|
||||
}
|
||||
// add click event listener to titleElement
|
||||
if (!titleElement.dataset.hasClickListener) {
|
||||
titleElement.addEventListener("click", handleTitleClick);
|
||||
titleElement.dataset.hasClickListener = "true";
|
||||
}
|
||||
|
||||
// 为 titleElement 的父元素添加点击事件
|
||||
if (parentElement && !parentElement.dataset.hasClickListener) {
|
||||
parentElement.style.cursor = 'default';
|
||||
parentElement.addEventListener('click', handleTitleClick);
|
||||
parentElement.dataset.hasClickListener = 'true';
|
||||
}
|
||||
}
|
||||
});
|
||||
// add click event listener to the parent element of titleElement
|
||||
if (parentElement && !parentElement.dataset.hasClickListener) {
|
||||
parentElement.style.cursor = "default";
|
||||
parentElement.addEventListener("click", handleTitleClick);
|
||||
parentElement.dataset.hasClickListener = "true";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addShiftKeyEventListeners();
|
||||
addShiftKeyEventListeners();
|
||||
}
|
||||
|
||||
// 执行主函数
|
||||
// run the main function
|
||||
addCheckboxes();
|
||||
|
||||
21
background.js
Normal file
21
background.js
Normal file
@@ -0,0 +1,21 @@
|
||||
console.log("Background script loaded");
|
||||
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
console.log("Extension installed");
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.action === "getUserInfo") {
|
||||
chrome.identity.getProfileUserInfo({ accountStatus: "ANY" }, (userInfo) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(chrome.runtime.lastError);
|
||||
sendResponse({ error: chrome.runtime.lastError.message });
|
||||
} else {
|
||||
sendResponse({ userInfo: userInfo });
|
||||
}
|
||||
});
|
||||
return true; // Will respond asynchronously
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Background script setup complete");
|
||||
106
bulkArchiveConversations.js
Normal file
106
bulkArchiveConversations.js
Normal file
@@ -0,0 +1,106 @@
|
||||
console.log("bulkArchiveConversations.js loaded");
|
||||
|
||||
async function bulkArchiveConversations() {
|
||||
const selectedConversations = getSelectedConversations();
|
||||
|
||||
if (selectedConversations.length === 0) {
|
||||
console.log("No conversations to archive.");
|
||||
removeAllCheckboxes();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Selected Conversations:", selectedConversations);
|
||||
|
||||
for (const element of selectedConversations) {
|
||||
await archiveConversation(element);
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedConversations() {
|
||||
return [...document.querySelectorAll(Selectors.conversationsCheckbox)];
|
||||
}
|
||||
|
||||
function removeAllCheckboxes() {
|
||||
const allCheckboxes = document.querySelectorAll(`.${CHECKBOX_CLASS}`);
|
||||
allCheckboxes.forEach((checkbox) => checkbox.remove());
|
||||
}
|
||||
|
||||
async function archiveConversation(checkbox) {
|
||||
await delay(100);
|
||||
|
||||
const conversationElement = checkbox.parentElement;
|
||||
const hoverEvent = new MouseEvent("mouseover", {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
console.log("1. Hovering over conversation...", conversationElement);
|
||||
conversationElement.dispatchEvent(hoverEvent);
|
||||
await delay(200);
|
||||
|
||||
const pointerDownEvent = new PointerEvent("pointerdown", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
pointerType: "mouse",
|
||||
});
|
||||
const threeDotButton = await waitForElement(
|
||||
Selectors.threeDotButton,
|
||||
conversationElement.parentElement
|
||||
);
|
||||
console.log("2. Clicking three dot button...", threeDotButton);
|
||||
threeDotButton.dispatchEvent(pointerDownEvent);
|
||||
await delay(300);
|
||||
|
||||
const archiveButton = await waitForArchiveButton();
|
||||
|
||||
if (archiveButton) {
|
||||
console.log("3. Clicking archive button...", archiveButton);
|
||||
archiveButton.click();
|
||||
await delay(500);
|
||||
}
|
||||
|
||||
console.log("4. Archiving completed.");
|
||||
}
|
||||
|
||||
async function waitForArchiveButton(parent = document, timeout = 2000) {
|
||||
const selector = 'div[role="menuitem"]';
|
||||
const textContent = "Archive";
|
||||
const textContentSimplifiedChinese = "归档";
|
||||
const textContentTraditionalChinese = "封存";
|
||||
|
||||
const startedAt = Date.now();
|
||||
|
||||
while (Date.now() - startedAt < timeout) {
|
||||
const elements = parent.querySelectorAll(selector);
|
||||
const element = Array.from(elements).find(
|
||||
(el) =>
|
||||
el.textContent.trim() === textContent ||
|
||||
el.textContent.trim() === textContentSimplifiedChinese ||
|
||||
el.textContent.trim() === textContentTraditionalChinese
|
||||
);
|
||||
if (element) return element;
|
||||
await delay(100);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function waitForElement(selector, parent = document, timeout = 2000) {
|
||||
const startedAt = Date.now();
|
||||
while (Date.now() - startedAt < timeout) {
|
||||
const element = parent.querySelector(selector);
|
||||
if (element) return element;
|
||||
await delay(100);
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Element ${selector} not found within ${timeout}ms in the specified parent`
|
||||
);
|
||||
}
|
||||
|
||||
bulkArchiveConversations();
|
||||
@@ -1,4 +1,4 @@
|
||||
console.log('bulkDeleteConversations.js loaded');
|
||||
console.log("bulkDeleteConversations.js loaded");
|
||||
|
||||
async function bulkDeleteConversations() {
|
||||
const selectedConversations = getSelectedConversations();
|
||||
@@ -11,6 +11,8 @@ async function bulkDeleteConversations() {
|
||||
|
||||
console.log("Selected Conversations:", selectedConversations);
|
||||
|
||||
sendEventAsync(selectedConversations.length);
|
||||
|
||||
for (const element of selectedConversations) {
|
||||
await deleteConversation(element);
|
||||
}
|
||||
@@ -21,38 +23,36 @@ function getSelectedConversations() {
|
||||
}
|
||||
|
||||
function removeAllCheckboxes() {
|
||||
const allCheckboxes = document.querySelectorAll(Selectors.conversationsCheckbox);
|
||||
allCheckboxes.forEach(checkbox => checkbox.remove());
|
||||
const allCheckboxes = document.querySelectorAll(`.${CHECKBOX_CLASS}`);
|
||||
allCheckboxes.forEach((checkbox) => checkbox.remove());
|
||||
}
|
||||
|
||||
async function deleteConversation(checkbox) {
|
||||
const conversationElement = checkbox.parentElement;
|
||||
await delay(100);
|
||||
// console.log("1. Clicking conversation...", conversationElement);
|
||||
// conversationElement.click();
|
||||
// 将 click 替换为悬停
|
||||
|
||||
// 创建一个鼠标悬停事件
|
||||
const hoverEvent = new MouseEvent('mouseover', {
|
||||
const conversationElement = checkbox.parentElement;
|
||||
const hoverEvent = new MouseEvent("mouseover", {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
console.log("1. Hovering over conversation...", conversationElement);
|
||||
// 触发鼠标悬停事件
|
||||
conversationElement.dispatchEvent(hoverEvent);
|
||||
conversationElement.dispatchEvent(hoverEvent);
|
||||
await delay(200);
|
||||
|
||||
const pointerDownEvent = new PointerEvent('pointerdown', {
|
||||
const pointerDownEvent = new PointerEvent("pointerdown", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
pointerType: 'mouse'
|
||||
pointerType: "mouse",
|
||||
});
|
||||
const threeDotButton = await waitForElement(Selectors.threeDotButton, conversationElement.parentElement);
|
||||
const threeDotButton = await waitForElement(
|
||||
Selectors.threeDotButton,
|
||||
conversationElement.parentElement
|
||||
);
|
||||
console.log("2. Clicking three dot button...", threeDotButton);
|
||||
threeDotButton.dispatchEvent(pointerDownEvent);
|
||||
await delay(300);
|
||||
console.log("2. Clicking three dot button...", threeDotButton);
|
||||
|
||||
const deleteButton = await waitForDeleteButton();
|
||||
|
||||
@@ -60,8 +60,8 @@ async function deleteConversation(checkbox) {
|
||||
console.log("3. Clicking delete button...", deleteButton);
|
||||
|
||||
deleteButton.click();
|
||||
const confirmButton = await waitForElement(Selectors.confirmDeleteButton);
|
||||
|
||||
const confirmButton = await waitForElement(Selectors.confirmDeleteButton);
|
||||
if (confirmButton) {
|
||||
console.log("4. Clicking confirm button...");
|
||||
confirmButton.click();
|
||||
@@ -74,49 +74,83 @@ async function deleteConversation(checkbox) {
|
||||
}
|
||||
|
||||
async function waitForDeleteButton(parent = document, timeout = 2000) {
|
||||
const selector = 'div[role="menuitem"]'; // 设定好选择器
|
||||
const textContent = "Delete"; // 指定文本内容
|
||||
const selector = 'div[role="menuitem"]';
|
||||
const textContent = "Delete";
|
||||
const startedAt = Date.now();
|
||||
|
||||
while ((Date.now() - startedAt) < timeout) {
|
||||
while (Date.now() - startedAt < timeout) {
|
||||
const elements = parent.querySelectorAll(selector);
|
||||
const element = Array.from(elements).find(el => el.textContent.trim() === textContent);
|
||||
if (element) return element; // 返回找到的元素
|
||||
const element = Array.from(elements).find(
|
||||
(el) =>
|
||||
el.textContent.trim() === textContent ||
|
||||
el.querySelector(".text-token-text-error")
|
||||
);
|
||||
if (element) return element;
|
||||
await delay(100);
|
||||
}
|
||||
|
||||
console.log(`Delete button not found within ${timeout}ms`);
|
||||
throw new Error(`Delete button not found within ${timeout}ms`);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Helper function to create a delay
|
||||
function delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function waitForElement(selector, parent = document, timeout = 2000) {
|
||||
const startedAt = Date.now();
|
||||
while ((Date.now() - startedAt) < timeout) {
|
||||
while (Date.now() - startedAt < timeout) {
|
||||
const element = parent.querySelector(selector);
|
||||
if (element) return element; // 返回找到的元素
|
||||
if (element) return element;
|
||||
await delay(100);
|
||||
}
|
||||
console.log(`Element ${selector} not found within ${timeout}ms in the specified parent`);
|
||||
throw new Error(`Element ${selector} not found within ${timeout}ms in the specified parent`);
|
||||
|
||||
throw new Error(
|
||||
`Element ${selector} not found within ${timeout}ms in the specified parent`
|
||||
);
|
||||
}
|
||||
|
||||
async function waitForElementToDisappear(selector, timeout = 2000) {
|
||||
const startedAt = Date.now();
|
||||
while ((Date.now() - startedAt) < timeout) {
|
||||
while (Date.now() - startedAt < timeout) {
|
||||
const element = document.querySelector(selector);
|
||||
if (!element) return;
|
||||
await delay(100);
|
||||
}
|
||||
|
||||
throw new Error(`Element ${selector} did not disappear within ${timeout}ms`);
|
||||
}
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
async function sendEventAsync(count) {
|
||||
try {
|
||||
const userInfo = await getUserInfo();
|
||||
const timestamp = new Date().toISOString().replace("T", " ").substr(0, 19);
|
||||
|
||||
const data = {
|
||||
user_id: userInfo.id || "unknown",
|
||||
timestamp: timestamp,
|
||||
action: "delete",
|
||||
count: count,
|
||||
};
|
||||
|
||||
const response = await fetch(
|
||||
"https://bulk-delete-chatgpt-worker.qcrao.com/send-event",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
console.log("Event sent successfully");
|
||||
} catch (error) {
|
||||
console.error("Error sending event:", error);
|
||||
}
|
||||
}
|
||||
|
||||
bulkDeleteConversations();
|
||||
|
||||
52
globals.js
52
globals.js
@@ -1,23 +1,39 @@
|
||||
if (typeof window.globalsLoaded === 'undefined') {
|
||||
console.log('globals.js loaded');
|
||||
if (typeof window.globalsLoaded === "undefined") {
|
||||
console.log("globals.js loaded");
|
||||
|
||||
window.globalsLoaded = true;
|
||||
window.globalsLoaded = true;
|
||||
|
||||
let Selectors = {
|
||||
// Plus 用户的选择器
|
||||
conversationsCheckbox: '.conversation-checkbox:checked',
|
||||
confirmDeleteButton: 'button.btn.btn-danger',
|
||||
threeDotButton: '[id^="radix-"]',
|
||||
// 其他 Plus 用户选择器...
|
||||
CONVERSATION_SELECTOR: 'div > div > div > div > div > div > nav > div > div > div > div > ol > li > div > a',
|
||||
TITLE_SELECTOR: '.relative.grow.overflow-hidden.whitespace-nowrap',
|
||||
};
|
||||
const Selectors = {
|
||||
conversationsCheckbox: ".conversation-checkbox:checked",
|
||||
confirmDeleteButton: "button.btn.btn-danger",
|
||||
threeDotButton: '[id^="radix-"]',
|
||||
CONVERSATION_SELECTOR:
|
||||
"div > div > div > div > div > div > nav > div > div > div > div > ol > li > div > a",
|
||||
TITLE_SELECTOR: ".relative.grow.overflow-hidden.whitespace-nowrap",
|
||||
};
|
||||
|
||||
let CHECKBOX_CLASS = 'conversation-checkbox';
|
||||
const CHECKBOX_CLASS = "conversation-checkbox";
|
||||
|
||||
window.shiftPressed = false;
|
||||
window.Selectors = Selectors;
|
||||
window.CHECKBOX_CLASS = CHECKBOX_CLASS;
|
||||
// Define getUserInfo function
|
||||
function getUserInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.runtime.sendMessage({ action: "getUserInfo" }, (response) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
reject(chrome.runtime.lastError);
|
||||
} else if (response.error) {
|
||||
reject(new Error(response.error));
|
||||
} else {
|
||||
resolve(response.userInfo);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Expose variables to the global scope
|
||||
window.Selectors = Selectors;
|
||||
window.shiftPressed = false;
|
||||
window.lastCheckedCheckbox = null;
|
||||
window.CHECKBOX_CLASS = CHECKBOX_CLASS;
|
||||
} else {
|
||||
console.log('globals.js already loaded, skipping re-initialization');
|
||||
}
|
||||
console.log("globals.js already loaded, skipping re-initialization");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "ChatGPT Bulk Delete",
|
||||
"version": "4.5",
|
||||
"version": "5.2",
|
||||
"description": "A Chrome extension to bulk delete ChatGPT conversations",
|
||||
"icons": {
|
||||
"48": "icon48.png"
|
||||
@@ -11,19 +11,20 @@
|
||||
"default_popup": "popup.html",
|
||||
"default_title": "Bulk Delete Conversations"
|
||||
},
|
||||
"permissions": [
|
||||
"scripting",
|
||||
"activeTab"
|
||||
],
|
||||
"permissions": ["scripting", "activeTab", "identity", "identity.email"],
|
||||
"host_permissions": ["https://bulk-delete-chatgpt-worker.qcrao.com/*"],
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"*://chat.openai.com/*"
|
||||
],
|
||||
"matches": ["*://chat.openai.com/*"],
|
||||
"js": [
|
||||
"globals.js",
|
||||
"utils.js",
|
||||
"addCheckboxes.js",
|
||||
"bulkDeleteConversations.js"
|
||||
"bulkDeleteConversations.js",
|
||||
"bulkArchiveConversations.js"
|
||||
],
|
||||
"run_at": "document_idle"
|
||||
}
|
||||
|
||||
276
popup.css
276
popup.css
@@ -1,75 +1,133 @@
|
||||
body {
|
||||
width: 240px;
|
||||
padding: 15px;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 280px;
|
||||
padding: 20px;
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: #f0f2f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #e1e4e8;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.buttons-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
background-color: #1ABC9C;
|
||||
color: white;
|
||||
border: 1px solid #16A085;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition-duration: 0.4s;
|
||||
margin: 0; /* 去掉左右外边距 */
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #16A085;
|
||||
border-color: #14967F;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
button#bulk-delete, button#toggle-checkboxes {
|
||||
font-size: 16px;
|
||||
background-color: #e74c3c;
|
||||
border-color: #c0392b;
|
||||
margin-top: 10px;
|
||||
width: calc(200px + 40px); /* bulk-delete 宽度等于 body 的宽度减去左右内边距再加上两者之间的距离 */
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.buttons-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.buttons-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 15px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
outline: none;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
opacity: 0;
|
||||
border-radius: 100%;
|
||||
transform: scale(1, 1) translate(-50%);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 7px 14px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
button:hover::after {
|
||||
animation: ripple 1s ease-out;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
0% {
|
||||
transform: scale(0, 0);
|
||||
opacity: 0.5;
|
||||
}
|
||||
20% {
|
||||
transform: scale(25, 25);
|
||||
opacity: 0.3;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scale(40, 40);
|
||||
}
|
||||
}
|
||||
|
||||
.buttons-row button {
|
||||
width: 120px; /* 将 remove-checkboxes 和 add-checkboxes 按钮的宽度增加到 120px */
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.buttons-row button:first-child {
|
||||
margin-right: 10px; /* 添加一个右外边距来增加两个按钮之间的距离 */
|
||||
button#bulk-delete,
|
||||
button#toggle-checkboxes,
|
||||
button#bulk-archive {
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
padding: 12px 15px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
button#bulk-archive span {
|
||||
position: absolute;
|
||||
left: 75px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
button#add-checkboxes {
|
||||
background-color: #2ecc71;
|
||||
}
|
||||
button#remove-checkboxes {
|
||||
background-color: #e74c3c;
|
||||
}
|
||||
button#toggle-checkboxes {
|
||||
background-color: #f39c12;
|
||||
}
|
||||
button#bulk-delete {
|
||||
background-color: #e74c3c;
|
||||
}
|
||||
button#bulk-archive {
|
||||
background-color: #9b59b6;
|
||||
}
|
||||
|
||||
.footer {
|
||||
@@ -77,41 +135,117 @@ button#bulk-delete, button#toggle-checkboxes {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #ccc;
|
||||
padding-top: 10px;
|
||||
display: flex; /* 添加这一行使得.footer下的直接子元素并排排列 */
|
||||
justify-content: center; /* 使得子元素在中心对齐 */
|
||||
gap: 10px; /* 如果需要,可以添加这一行以在子元素之间创建一些空间 */
|
||||
}
|
||||
|
||||
button#remove-checkboxes {
|
||||
background-color: #badb34;
|
||||
border-color: #b6b929;
|
||||
}
|
||||
|
||||
button#toggle-checkboxes {
|
||||
background-color: #2287da;
|
||||
border-color: #2758ab;
|
||||
border-top: 2px solid #e1e4e8;
|
||||
padding-top: 15px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
#sponsorLink {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
background-color: #383E46;
|
||||
border-color: #78838F;
|
||||
padding: 8px 15px;
|
||||
background-color: #34495e;
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#sponsorLink:hover {
|
||||
background-color: #2c3e50;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#sponsorLink .icon-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#sponsorLink:hover {
|
||||
background-color: #993366;
|
||||
}
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 5% auto;
|
||||
padding: 0;
|
||||
border-radius: 10px;
|
||||
width: 260px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal h2 {
|
||||
margin: 0;
|
||||
padding: 15px 0;
|
||||
background-color: #4a4a4a;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal p {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin: 20px 15px;
|
||||
}
|
||||
|
||||
.modal-separator {
|
||||
height: 1px;
|
||||
background-color: #e0e0e0;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.modal-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.modal-button {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.modal-button-primary {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal-button-primary:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
#modalCancel {
|
||||
background-color: #f8f9fa;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#modalCancel:hover {
|
||||
background-color: #e2e6ea;
|
||||
}
|
||||
|
||||
85
popup.html
85
popup.html
@@ -1,34 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ChatGPT Bulk Delete</title>
|
||||
<link rel="stylesheet" href="popup.css">
|
||||
</head>
|
||||
<body>
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>bulk-delete-chatGPT</h1>
|
||||
<h1>ChatGPT Bulk Delete</h1>
|
||||
</div>
|
||||
<div class="buttons-wrapper">
|
||||
<div class="buttons-row">
|
||||
<button id="add-checkboxes"><span>Add</span><br><span>Checkboxes</span></button>
|
||||
<button id="remove-checkboxes"><span>Remove</span><br><span>Checkboxes</span></button>
|
||||
</div>
|
||||
<button id="toggle-checkboxes">Toggle Checkboxes</button>
|
||||
<button id="bulk-delete">Bulk Delete</button>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<a href="https://github.com/sponsors/qcrao" target="_blank" id="sponsorLink">
|
||||
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-heart icon-sponsor mr-1 v-align-middle color-fg-sponsors anim-pulse-in" style="fill: #BC6796; vertical-align: middle;">
|
||||
<path d="m8 14.25.345.666a.75.75 0 0 1-.69 0l-.008-.004-.018-.01a7.152 7.152 0 0 1-.31-.17 22.055 22.055 0 0 1-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.066 22.066 0 0 1-3.744 2.584l-.018.01-.006.003h-.002ZM4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.58 20.58 0 0 0 8 13.393a20.58 20.58 0 0 0 3.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.749.749 0 0 1-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5Z"></path>
|
||||
</svg>
|
||||
Sponsor
|
||||
</a>
|
||||
<p id="copyright"></p>
|
||||
<div class="buttons-row">
|
||||
<button id="add-checkboxes">
|
||||
<span>Add</span><br /><span>Checkboxes</span>
|
||||
</button>
|
||||
<button id="remove-checkboxes">
|
||||
<span>Remove</span><br /><span>Checkboxes</span>
|
||||
</button>
|
||||
</div>
|
||||
<button id="toggle-checkboxes">Toggle Checkboxes</button>
|
||||
<button id="bulk-archive" class="locked">
|
||||
<span>🔒</span> Bulk Archive
|
||||
</button>
|
||||
<button id="bulk-delete">Bulk Delete</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer">
|
||||
<a
|
||||
href="https://github.com/sponsors/qcrao"
|
||||
target="_blank"
|
||||
id="sponsorLink">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="16"
|
||||
data-view-component="true"
|
||||
class="octicon octicon-heart icon-sponsor mr-1 v-align-middle color-fg-sponsors anim-pulse-in"
|
||||
style="fill: #bc6796; vertical-align: middle">
|
||||
<path
|
||||
d="m8 14.25.345.666a.75.75 0 0 1-.69 0l-.008-.004-.018-.01a7.152 7.152 0 0 1-.31-.17 22.055 22.055 0 0 1-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.066 22.066 0 0 1-3.744 2.584l-.018.01-.006.003h-.002ZM4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.58 20.58 0 0 0 8 13.393a20.58 20.58 0 0 0 3.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.749.749 0 0 1-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5Z"></path>
|
||||
</svg>
|
||||
Sponsor
|
||||
</a>
|
||||
<p id="copyright"></p>
|
||||
</div>
|
||||
|
||||
<div id="customModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h2>Buy Bulk Archive</h2>
|
||||
<p>One-time payment of $0.99 USD to purchase access.</p>
|
||||
<p>Do you want to continue?</p>
|
||||
<div class="modal-separator"></div>
|
||||
<div class="modal-buttons">
|
||||
<button id="modalCancel" class="modal-button">Cancel</button>
|
||||
<button id="modalOK" class="modal-button modal-button-primary">
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
<script src="utils.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
177
popup.js
177
popup.js
@@ -1,17 +1,20 @@
|
||||
function loadGlobalsThenExecute(tabId, secondaryScript) {
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId: tabId },
|
||||
files: ['globals.js']
|
||||
}, () => {
|
||||
chrome.scripting.executeScript({
|
||||
chrome.scripting.executeScript(
|
||||
{
|
||||
target: { tabId: tabId },
|
||||
files: [secondaryScript]
|
||||
});
|
||||
});
|
||||
files: ["globals.js"],
|
||||
},
|
||||
() => {
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId: tabId },
|
||||
files: [secondaryScript],
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function addButtonListener(buttonId, scriptName) {
|
||||
document.getElementById(buttonId).addEventListener('click', () => {
|
||||
document.getElementById(buttonId).addEventListener("click", () => {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
|
||||
if (tab) {
|
||||
loadGlobalsThenExecute(tab.id, scriptName);
|
||||
@@ -21,17 +24,157 @@ function addButtonListener(buttonId, scriptName) {
|
||||
}
|
||||
|
||||
function initializeButtons() {
|
||||
addButtonListener('add-checkboxes', 'addCheckboxes.js');
|
||||
addButtonListener('bulk-delete', 'bulkDeleteConversations.js');
|
||||
addButtonListener('toggle-checkboxes', 'toggleCheckboxes.js');
|
||||
addButtonListener('remove-checkboxes', 'removeCheckboxes.js');
|
||||
addButtonListener("add-checkboxes", "addCheckboxes.js");
|
||||
addButtonListener("bulk-delete", "bulkDeleteConversations.js");
|
||||
addButtonListener("toggle-checkboxes", "toggleCheckboxes.js");
|
||||
addButtonListener("remove-checkboxes", "removeCheckboxes.js");
|
||||
|
||||
const bulkArchiveButton = document.getElementById("bulk-archive");
|
||||
bulkArchiveButton.addEventListener("click", handleBulkArchive);
|
||||
}
|
||||
|
||||
const storageKey = "BulkDeleteChatGPT_isPaid";
|
||||
async function checkMembershipStatus() {
|
||||
const localIsPaid = localStorage.getItem(storageKey) === "true";
|
||||
|
||||
updateBulkArchiveButton(localIsPaid);
|
||||
|
||||
const userInfo = await getUserInfo();
|
||||
if (!userInfo) {
|
||||
console.error("Unable to get user info");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://bulk-delete-chatgpt-worker.qcrao.com/check-payment-status?user_id=${encodeURIComponent(
|
||||
userInfo.id
|
||||
)}`
|
||||
);
|
||||
const data = await response.json();
|
||||
|
||||
// 更新本地存储和按钮状态
|
||||
localStorage.setItem(storageKey, data.isPaid);
|
||||
updateBulkArchiveButton(data.isPaid);
|
||||
} catch (error) {
|
||||
console.error("Error checking membership status:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateBulkArchiveButton(isPaid) {
|
||||
const bulkArchiveButton = document.getElementById("bulk-archive");
|
||||
if (isPaid) {
|
||||
bulkArchiveButton.classList.remove("locked");
|
||||
bulkArchiveButton.querySelector("span").textContent = "";
|
||||
} else {
|
||||
bulkArchiveButton.classList.add("locked");
|
||||
bulkArchiveButton.querySelector("span").textContent = "🔒";
|
||||
}
|
||||
}
|
||||
|
||||
async function handleBulkArchive() {
|
||||
const localIsPaid = localStorage.getItem(storageKey) === "true";
|
||||
if (localIsPaid) {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
|
||||
if (tab) {
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId: tab.id },
|
||||
files: ["bulkArchiveConversations.js"],
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const userInfo = await getUserInfo();
|
||||
if (!userInfo) {
|
||||
console.error("Unable to get user info");
|
||||
alert("Unable to verify user. Please try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://bulk-delete-chatgpt-worker.qcrao.com/check-payment-status?user_id=${encodeURIComponent(
|
||||
userInfo.id
|
||||
)}`
|
||||
);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.isPaid) {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
|
||||
if (tab) {
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId: tab.id },
|
||||
files: ["bulkArchiveConversations.js"],
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showModal().then(async (result) => {
|
||||
if (result) {
|
||||
const payResponse = await fetch(
|
||||
`https://bulk-delete-chatgpt-worker.qcrao.com/pay-bulk-archive?user_id=${encodeURIComponent(
|
||||
userInfo.id
|
||||
)}`,
|
||||
{ method: "POST" }
|
||||
);
|
||||
const payData = await payResponse.json();
|
||||
console.log("payData", payData);
|
||||
if (payData.paymentUrl) {
|
||||
window.open(payData.paymentUrl, "_blank");
|
||||
} else {
|
||||
alert("Failed to get payment link. Please try again later.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function showModal() {
|
||||
return new Promise((resolve) => {
|
||||
const modal = document.getElementById("customModal");
|
||||
const okButton = document.getElementById("modalOK");
|
||||
const cancelButton = document.getElementById("modalCancel");
|
||||
|
||||
modal.style.display = "block";
|
||||
|
||||
okButton.onclick = () => {
|
||||
modal.style.display = "none";
|
||||
resolve(true);
|
||||
};
|
||||
|
||||
cancelButton.onclick = () => {
|
||||
modal.style.display = "none";
|
||||
resolve(false);
|
||||
};
|
||||
|
||||
window.onclick = (event) => {
|
||||
if (event.target == modal) {
|
||||
modal.style.display = "none";
|
||||
resolve(false);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function updateCopyrightYear() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
document.getElementById('copyright').innerHTML =
|
||||
`© ${currentYear} <a href="https://github.com/qcrao/bulk-delete-chatGPT" target="_blank">qcrao@GitHub</a>`;
|
||||
document.getElementById(
|
||||
"copyright"
|
||||
).innerHTML = `© ${currentYear} <a href="https://github.com/qcrao/bulk-delete-chatGPT" target="_blank">qcrao@GitHub</a>`;
|
||||
}
|
||||
|
||||
initializeButtons();
|
||||
updateCopyrightYear();
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initializeButtons();
|
||||
updateCopyrightYear();
|
||||
checkMembershipStatus();
|
||||
});
|
||||
|
||||
// 每次打开popup时检查会员状态
|
||||
chrome.runtime.onConnect.addListener(function (port) {
|
||||
if (port.name === "popup") {
|
||||
port.onDisconnect.addListener(function () {
|
||||
checkMembershipStatus();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(function() {
|
||||
const removeConversationCheckboxes = document.querySelectorAll('.conversation-checkbox');
|
||||
removeConversationCheckboxes.forEach(checkbox => {
|
||||
checkbox.remove();
|
||||
});
|
||||
// 在移除所有复选框后刷新页面
|
||||
location.reload();
|
||||
})();
|
||||
|
||||
function removeCheckboxesAndReload() {
|
||||
const checkboxes = document.querySelectorAll(`.${CHECKBOX_CLASS}`);
|
||||
checkboxes.forEach(checkbox => checkbox.remove());
|
||||
|
||||
// Refresh the page after removing all checkboxes
|
||||
location.reload();
|
||||
}
|
||||
|
||||
removeCheckboxesAndReload();
|
||||
@@ -1,9 +1,9 @@
|
||||
function toggleCheckboxes() {
|
||||
const conversations = document.querySelectorAll(".conversation-checkbox");
|
||||
const conversations = document.querySelectorAll(`.${CHECKBOX_CLASS}`);
|
||||
|
||||
conversations.forEach((checkbox) => {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
});
|
||||
}
|
||||
|
||||
toggleCheckboxes();
|
||||
toggleCheckboxes();
|
||||
14
utils.js
Normal file
14
utils.js
Normal file
@@ -0,0 +1,14 @@
|
||||
function getUserInfo() {
|
||||
return new Promise((resolve) => {
|
||||
chrome.identity.getProfileUserInfo({ accountStatus: "ANY" }, (userInfo) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(chrome.runtime.lastError);
|
||||
resolve(null);
|
||||
} else {
|
||||
resolve(userInfo);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
window.getUserInfo = getUserInfo;
|
||||
Reference in New Issue
Block a user