您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Extracts M-Pesa messages from messages.google.com and creates a CSV
// ==UserScript== // @name M-Pesa CSV Extractor // @namespace https://openai.com // @version 1.0 // @description Extracts M-Pesa messages from messages.google.com and creates a CSV // @match https://messages.google.com/web/* // @grant none // @run-at document-idle // ==/UserScript== (function () { 'use strict'; function createFloatingButton() { const btn = document.createElement('button'); btn.id = 'mpesa-monkey-button'; btn.textContent = '🐒'; btn.title = 'Save M-Pesa Transactions'; Object.assign(btn.style, { position: 'fixed', bottom: '20px', right: '20px', width: '48px', height: '48px', borderRadius: '50%', border: 'none', background: '#1a73e8', color: 'white', fontSize: '24px', boxShadow: '0 2px 6px rgba(0,0,0,0.3)', zIndex: '9999', cursor: 'pointer' }); const style = document.createElement('style'); style.textContent = ` @keyframes mpesa-jump { 0% { transform: translateY(0); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0); } } `; document.head.appendChild(style); btn.onclick = () => { btn.style.animation = 'mpesa-jump 0.4s ease'; setTimeout(() => btn.style.animation = '', 400); openDateModal(); }; document.body.appendChild(btn); } function openDateModal() { const existing = document.getElementById('mpesa-modal'); if (existing) existing.remove(); const modal = document.createElement('div'); modal.id = 'mpesa-modal'; Object.assign(modal.style, { position: 'fixed', top: '0', left: '0', right: '0', bottom: '0', backgroundColor: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: '10000' }); const content = document.createElement('div'); Object.assign(content.style, { background: 'white', padding: '20px', borderRadius: '8px', textAlign: 'center', display: 'flex', flexDirection: 'column', gap: '10px', maxWidth: '300px' }); const title = document.createElement('h3'); title.textContent = 'Select start date'; const reminder = document.createElement('p'); reminder.style.fontSize = '12px'; reminder.style.color = '#666'; reminder.textContent = 'Reminder: Scroll to load all messages before extracting.'; const input = document.createElement('input'); input.type = 'date'; input.id = 'mpesa-date'; const buttons = document.createElement('div'); buttons.style.display = 'flex'; buttons.style.justifyContent = 'space-between'; buttons.style.gap = '10px'; const submit = document.createElement('button'); submit.textContent = 'Extract'; submit.onclick = () => { const dateValue = input.value; modal.remove(); if (dateValue) processMessages(new Date(dateValue)); }; const cancel = document.createElement('button'); cancel.textContent = 'Cancel'; cancel.onclick = () => modal.remove(); buttons.appendChild(submit); buttons.appendChild(cancel); content.appendChild(title); content.appendChild(reminder); content.appendChild(input); content.appendChild(buttons); modal.appendChild(content); document.body.appendChild(modal); } function showToast(message) { const toast = document.createElement('div'); toast.textContent = message; Object.assign(toast.style, { position: 'fixed', bottom: '80px', right: '20px', background: '#333', color: '#fff', padding: '10px 15px', borderRadius: '5px', zIndex: '10001', fontSize: '14px', opacity: '0', transition: 'opacity 0.3s ease' }); document.body.appendChild(toast); setTimeout(() => toast.style.opacity = '1', 10); setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 3000); }; function normalizeAmount(str) { if (!str) return "0.00"; return parseFloat(str.replace(/,/g, '').replace(/[^\d.]/g, '')).toFixed(2); } function processMessages(startDate) { const rawMessages = Array.from(document.querySelectorAll('mws-text-message-part')) .map(el => el.getAttribute('aria-label')?.trim()) .filter(Boolean); const rows = []; for (const message of rawMessages) { const dateMatch = message.match(/Received on (.+?) at/); if (!dateMatch) continue; const msgDate = new Date(dateMatch[1]); const startOfDay = new Date(startDate); startOfDay.setHours(0, 0, 0, 0); if (msgDate < startOfDay) continue; const reversalMatch = message.match(/.*?factura\s+(\w{11}).*?revertido/i); if (reversalMatch) { const code = reversalMatch[1]; const index = rows.findIndex(row => row[1] === code); if (index !== -1) rows.splice(index, 1); continue; } const transfer = message.match( /Confirmado\s+([A-Z0-9]{11,12})[\s\S]*?Transferiste\s+([\d.,]+)MT[\s\S]*?taxa\s+foi\s+de\s+([\d.,]+)MT[\s\S]*?aos\s+(\d{1,2})\/(\d{1,2})\/(\d{2})/i ); if (transfer) { const [_, code, value, fee, day, month, year] = transfer; const formattedDate = `${day.padStart(2, '0')}/${month.padStart(2, '0')}/20${year}`; rows.push([rows.length + 1, code, normalizeAmount(value), formattedDate, normalizeAmount(fee)]); continue; } const compra = message.match( /Confirmado\s+([A-Z0-9]{11,12})[\s\S]*?operacao de compra[\s\S]*?([\d.,]+)MT[\s\S]*?aos\s+(\d{1,2})\/(\d{1,2})\/(\d{2})/i ); if (compra) { const [_, code, value, day, month, year] = compra; const formattedDate = `${day.padStart(2, '0')}/${month.padStart(2, '0')}/20${year}`; console.log(value) rows.push([rows.length + 1, code, normalizeAmount(value), formattedDate, '0.00']); continue; } } if (rows.length === 0) { showToast("No transactions found"); return; } const csvContent = [ ['Numero', 'Codigo M-Pesa', 'Valor', 'Data', 'Taxa'], ...rows ].map(row => row.map(field => `"${field}"`).join(';')).join('\r\n'); const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'mpesa_export.csv'; link.click(); } window.addEventListener('load', () => { setTimeout(createFloatingButton, 1500); document.addEventListener('keydown', e => { if (e.altKey && e.key.toLowerCase() === 'm') { e.preventDefault(); openDateModal(); } }); }); })();