|
import PostalMime from 'postal-mime'; |
|
|
|
export async function get_access_token(tokenInfo: any, client_id: string, clientSecret: string) { |
|
|
|
const now = Date.now(); |
|
const expiryTime = tokenInfo.timestamp + (tokenInfo.expires_in * 1000); |
|
const shouldRefresh = now >= (expiryTime - 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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
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<ParsedEmail> { |
|
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); |
|
|
|
|
|
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<ParsedEmail[]> { |
|
const endpoint = 'https://graph.microsoft.com/v1.0/me/messages'; |
|
|
|
|
|
console.log(accessToken) |
|
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; |
|
console.log(emails) |
|
const parsedEmails = await Promise.all(emails.map(parseEmail)); |
|
return parsedEmails; |
|
} |
|
|
|
|
|
|
|
|
|
export async function deleteEmail(accessToken: string, emailId: string): Promise<void> { |
|
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<void> { |
|
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<void> { |
|
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}`); |
|
} |
|
} |