lolipop 邮箱自动删除邮件工具
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. const { chromium } = require('playwright');
  2. const express = require('express');
  3. const path = require('path');
  4. const app = express();
  5. const port = 3120;
  6. app.use(express.json());
  7. app.use(express.static(path.join(__dirname)));
  8. let clients = [];
  9. function sendToAllClients(message, paginationInfo = null) {
  10. const data = JSON.stringify({
  11. message: message,
  12. paginationInfo: paginationInfo
  13. });
  14. console.log(`Sending to ${clients.length} clients: ${message}`);
  15. clients.forEach(client => {
  16. try {
  17. client.res.write(`data: ${data}\n\n`);
  18. } catch (error) {
  19. console.error(`Error sending to client ${client.id}:`, error);
  20. // Remove problematic client
  21. clients = clients.filter(c => c.id !== client.id);
  22. }
  23. });
  24. }
  25. app.get('/delete-progress', (req, res) => {
  26. res.setHeader('Content-Type', 'text/event-stream');
  27. res.setHeader('Cache-Control', 'no-cache');
  28. res.setHeader('Connection', 'keep-alive');
  29. res.setHeader('Access-Control-Allow-Origin', '*');
  30. res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  31. const clientId = Date.now();
  32. clients.push({
  33. id: clientId,
  34. res
  35. });
  36. // Send initial connection confirmation
  37. res.write(`data: ${JSON.stringify({message: '事件流连接已建立'})}\n\n`);
  38. console.log(`Client ${clientId} connected to event stream`);
  39. req.on('close', () => {
  40. clients = clients.filter(client => client.id !== clientId);
  41. console.log(`Client ${clientId} disconnected from event stream`);
  42. });
  43. });
  44. async function deleteEmails(email, password, startPage, endPage) {
  45. let browser = null;
  46. let context = null;
  47. let page = null;
  48. console.log(`deleteEmails called with: email=${email}, startPage=${startPage}, endPage=${endPage || 'not specified'}`);
  49. try {
  50. console.log('Launching browser...');
  51. sendToAllClients('正在启动浏览器...');
  52. try {
  53. browser = await chromium.launch({
  54. headless: true,
  55. channel: 'chrome',
  56. args: ['--no-sandbox', '--disable-setuid-sandbox']
  57. });
  58. console.log('Browser launched successfully');
  59. } catch (browserError) {
  60. console.error('Failed to launch browser:', browserError);
  61. sendToAllClients(`浏览器启动失败: ${browserError.message}`);
  62. return;
  63. }
  64. try {
  65. context = await browser.newContext();
  66. page = await context.newPage();
  67. console.log('Browser context and page created');
  68. } catch (contextError) {
  69. console.error('Failed to create browser context or page:', contextError);
  70. sendToAllClients(`浏览器上下文创建失败: ${contextError.message}`);
  71. return;
  72. }
  73. console.log('Navigating to login page...');
  74. sendToAllClients('正在登录...');
  75. await page.goto('https://webmail.lolipop.jp/login');
  76. await page.fill('input[type="email"]', email);
  77. await page.fill('input[type="password"]', password);
  78. await page.click('button[type="submit"]');
  79. await page.waitForSelector('.css-1f8bwsm', { timeout: 10000 });
  80. sendToAllClients('登录成功');
  81. let currentPage = startPage;
  82. let totalProcessed = 0;
  83. let continueDeleting = true;
  84. while (continueDeleting) {
  85. // Navigate to specific page
  86. await page.goto(`https://webmail.lolipop.jp/mail/INBOX?p=${currentPage}`);
  87. await page.waitForSelector('.css-1f8bwsm', { timeout: 100000 });
  88. // Select all emails on current page
  89. const emailsToDelete = await page.evaluate(() => {
  90. const checkboxes = document.querySelectorAll('.css-1f8bwsm .css-1rlbz42 .css-1m9pwf3');
  91. let count = 0;
  92. checkboxes.forEach((checkbox) => {
  93. checkbox.click();
  94. count++;
  95. });
  96. return count;
  97. });
  98. if (emailsToDelete > 0) {
  99. totalProcessed += emailsToDelete;
  100. sendToAllClients(`当前页面选中 ${emailsToDelete} 封邮件`);
  101. // Delete selected emails
  102. await page.click('.css-i9gxme .css-zy67up .css-1yxmbwk[aria-label="削除"]');
  103. await page.waitForTimeout(1000);
  104. sendToAllClients(`已删除第 ${currentPage} 页的 ${emailsToDelete} 封邮件`);
  105. }
  106. // Check if we should continue to next page
  107. if (endPage && currentPage >= endPage) {
  108. continueDeleting = false;
  109. } else {
  110. // Check if there is a next page
  111. const nextPageButton = await page.$('.MuiBox-root.css-0 .css-1yxmbwk:nth-child(3)');
  112. const isNextPageDisabled = await nextPageButton.evaluate(button =>
  113. button.disabled || button.classList.contains('disabled')
  114. );
  115. if (isNextPageDisabled) {
  116. continueDeleting = false;
  117. } else {
  118. currentPage++;
  119. sendToAllClients(`进入第 ${currentPage} 页`);
  120. }
  121. }
  122. }
  123. sendToAllClients(`删除完成,共处理 ${totalProcessed} 封邮件`);
  124. } catch (error) {
  125. sendToAllClients(`发生错误: ${error.message}`);
  126. console.error('Delete emails error:', error);
  127. } finally {
  128. try {
  129. if (page) await page.close().catch(e => console.error('Error closing page:', e));
  130. if (context) await context.close().catch(e => console.error('Error closing context:', e));
  131. if (browser) await browser.close().catch(e => console.error('Error closing browser:', e));
  132. } catch (closeError) {
  133. console.error('Error in cleanup:', closeError);
  134. }
  135. }
  136. }
  137. app.post('/start-delete', async (req, res) => {
  138. const { email, password, startPage, endPage } = req.body;
  139. console.log('Received delete request:', { email, startPage, endPage });
  140. if (!email || !password || !startPage) {
  141. console.error('Missing required parameters');
  142. return res.status(400).json({ status: 'error', message: '缺少必要参数' });
  143. }
  144. // Send response immediately
  145. res.json({ status: 'started' });
  146. console.log('Starting delete operation...');
  147. // Set timeout to allow response to be sent before potentially long operation
  148. setTimeout(() => {
  149. console.log('Executing deleteEmails function...');
  150. // Call the function without await to not block
  151. deleteEmails(email, password, parseInt(startPage), endPage ? parseInt(endPage) : null)
  152. .then(() => console.log('Delete emails operation completed'))
  153. .catch(err => console.error('Delete emails operation failed:', err));
  154. }, 100);
  155. });
  156. async function getLastPageDate() {
  157. let browser = null;
  158. let context = null;
  159. let page = null;
  160. try {
  161. browser = await chromium.launch({
  162. headless: true,
  163. channel: 'chrome',
  164. args: ['--no-sandbox', '--disable-setuid-sandbox']
  165. });
  166. context = await browser.newContext();
  167. page = await context.newPage();
  168. await page.goto('https://webmail.lolipop.jp/login');
  169. await page.fill('input[type="email"]', 'spdrakuten@spdsystem.com');
  170. await page.fill('input[type="password"]', 'YzFiMTJlYT2a4-4a');
  171. await page.click('button[type="submit"]');
  172. await page.waitForSelector('.css-1f8bwsm', { timeout: 10000 });
  173. // 点击最后一页按钮
  174. await page.waitForSelector('.MuiBox-root.css-0 .css-1yxmbwk');
  175. await page.click('.MuiBox-root.css-0 .css-1yxmbwk:nth-child(4)');
  176. await page.waitForTimeout(1000);
  177. // 获取分页信息和最后一封邮件的日期
  178. const lastDate = await page.evaluate(() => {
  179. const dates = document.querySelectorAll('.css-1e7qmj6');
  180. if (dates.length > 0) {
  181. const lastDateStr = dates[dates.length - 1].textContent.replace(/:\d+0*$/, '');
  182. return lastDateStr;
  183. }
  184. return null;
  185. });
  186. return lastDate;
  187. } catch (error) {
  188. console.error('Error getting last date:', error);
  189. return null;
  190. } finally {
  191. try {
  192. if (page) await page.close().catch(e => console.error('Error closing page:', e));
  193. if (context) await context.close().catch(e => console.error('Error closing context:', e));
  194. if (browser) await browser.close().catch(e => console.error('Error closing browser:', e));
  195. } catch (closeError) {
  196. console.error('Error in cleanup:', closeError);
  197. }
  198. }
  199. }
  200. app.get('/get-last-date', async (req, res) => {
  201. const lastDate = await getLastPageDate();
  202. res.json({ lastDate });
  203. });
  204. // Add a test endpoint
  205. app.get('/test-event', (req, res) => {
  206. console.log('Current clients:', clients.length);
  207. clients.forEach((client, index) => {
  208. console.log(`Client ${index} (ID: ${client.id}): ${typeof client.res}`);
  209. });
  210. sendToAllClients('测试事件 - ' + new Date().toISOString());
  211. res.json({
  212. status: 'Test event sent to ' + clients.length + ' clients',
  213. clientInfo: clients.map(c => ({ id: c.id }))
  214. });
  215. });
  216. app.listen(port, () => {
  217. console.log(`Server running at http://localhost:${port}`);
  218. });