import PostalMime from 'postal-mime'; export async function get_access_token(tokenInfo: any, client_id: string, clientSecret: string) { // 检查token是否过期(提前5分钟刷新) const now = Date.now(); const expiryTime = tokenInfo.timestamp + (tokenInfo.expires_in * 1000); const shouldRefresh = now >= (expiryTime - 300000); // 5分钟 = 300000毫秒 if (!shouldRefresh) { return tokenInfo.access_token; } const response = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ 'client_id': client_id, 'client_secret': clientSecret, 'grant_type': 'refresh_token', 'refresh_token': tokenInfo.refresh_token }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status}, response: ${errorText}`); } const data = await response.json() as any; return data.access_token; } //https://learn.microsoft.com/zh-cn/graph/api/resources/mail-api-overview?view=graph-rest-1.0 /** * 获取邮件列表 */ interface ParsedEmail { id: string; subject: string; from: string; to: string[]; date: { created: string; received: string; sent: string; modified: string; }; text: string; html: string; importance: string; isRead: boolean; isDraft: boolean; hasAttachments: boolean; webLink: string; preview: string; categories: string[]; internetMessageId: string; metadata: { platform?: string; browser?: string; ip?: string; location?: string; }; } async function parseEmail(email: any): Promise { const parser = new PostalMime(); let content = ''; try { if (email.body.content) { content = email.body.content; } else { const response = await fetch(email.body.contentUrl); content = await response.text(); } const parsed = await parser.parse(content); // 从HTML内容中提取元数据 const htmlContent = email.body.content || ''; const platformMatch = htmlContent.match(/Platform:\s*([^<\r\n]+)/); const browserMatch = htmlContent.match(/Browser:\s*([^<\r\n]+)/); const ipMatch = htmlContent.match(/IP address:\s*([^<\r\n]+)/); const locationMatch = htmlContent.match(/Country\/region:\s*([^<\r\n]+)/); return { id: email.id, subject: email.subject, from: email.from.emailAddress.address, to: email.toRecipients.map((r: any) => r.emailAddress.address), date: { created: email.createdDateTime, received: email.receivedDateTime, sent: email.sentDateTime, modified: email.lastModifiedDateTime }, text: parsed.text || email.bodyPreview || '', html: parsed.html || email.body.content || '', importance: email.importance, isRead: email.isRead, isDraft: email.isDraft, hasAttachments: email.hasAttachments, webLink: email.webLink, preview: email.bodyPreview, categories: email.categories, internetMessageId: email.internetMessageId, metadata: { platform: platformMatch?.[1]?.trim(), browser: browserMatch?.[1]?.trim(), ip: ipMatch?.[1]?.trim(), location: locationMatch?.[1]?.trim(), } }; } catch (error) { console.error('解析邮件失败:', error); return { id: email.id, subject: email.subject, from: email.from.emailAddress.address, to: email.toRecipients.map((r: any) => r.emailAddress.address), date: { created: email.createdDateTime, received: email.receivedDateTime, sent: email.sentDateTime, modified: email.lastModifiedDateTime }, text: email.bodyPreview || '', html: email.body.content || '', importance: email.importance, isRead: email.isRead, isDraft: email.isDraft, hasAttachments: email.hasAttachments, webLink: email.webLink, preview: email.bodyPreview, categories: email.categories, internetMessageId: email.internetMessageId, metadata: {} }; } } export async function getEmails(accessToken: string, limit = 50): Promise { const endpoint = 'https://graph.microsoft.com/v1.0/me/messages'; const response = await fetch(`${endpoint}?$top=${limit}`, { method: 'GET', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' } }); const data = await response.json() as any; if (!response.ok) { throw new Error(`获取邮件失败: ${data.error?.message}`); } const emails = data.value; const parsedEmails = await Promise.all(emails.map(parseEmail)); return parsedEmails; } /** * 删除单个邮件 */ export async function deleteEmail(accessToken: string, emailId: string): Promise { const endpoint = `https://graph.microsoft.com/v1.0/me/messages/${emailId}`; const response = await fetch(endpoint, { method: 'DELETE', headers: { 'Authorization': `Bearer ${accessToken}` } }); if (!response.ok) { const errorData = await response.json() as any; throw new Error(`删除邮件失败: ${errorData.error?.message}`); } } /** * 清空邮箱 (批量删除邮件) */ export async function emptyMailbox(accessToken: string, batchSize = 50): Promise { let hasMoreEmails = true; let totalDeleted = 0; while (hasMoreEmails) { // 获取一批邮件 const emails = await getEmails(accessToken, batchSize); if (emails.length === 0) { hasMoreEmails = false; break; } // 删除该批次的每封邮件 const deletePromises = emails.map(email => deleteEmail(accessToken, email.id)); await Promise.all(deletePromises); totalDeleted += emails.length; console.log(`已删除 ${emails.length} 封邮件,累计: ${totalDeleted}`); } console.log('邮箱已清空'); } /** * 发送邮件 */ export async function sendEmail( accessToken: string, to: string[], subject: string, body: string, isHtml = false ): Promise { const endpoint = 'https://graph.microsoft.com/v1.0/me/sendMail'; const emailData = { message: { subject, body: { contentType: isHtml ? 'HTML' : 'Text', content: body }, toRecipients: to.map(recipient => ({ emailAddress: { address: recipient } })) } }; const response = await fetch(endpoint, { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }); if (!response.ok) { const errorData = await response.json() as any; throw new Error(`发送邮件失败: ${errorData.error?.message}`); } }