最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Outlook graph V1.0 API 批量发送邮件时邮件在队列中等待

网站源码admin39浏览0评论

Outlook graph V1.0 API 批量发送邮件时邮件在队列中等待

Outlook graph V1.0 API 批量发送邮件时邮件在队列中等待

我们正在使用 outlook graph v1.0 API 在我们的 Node js 服务器中发送电子邮件。为了发送批量电子邮件,我们将电子邮件分成多个块,每个块包含 20 个具有不同消息的收件人。

  • 现在考虑一下,我一次发送大量 1000 个收件人,其中包含不同的消息。
  • 它分成 50 个块,每块 20 条消息。
  • 然后我尝试点击 batch API。
  • 现在我将单独过滤状态为429的失败响应,并在我在失败响应中获得的重试超时时间内使用相同的消息重试。
  • 在这段时间内,如果任何其他用户试图点击sendMail API,则需要一些时间(20 分钟、30 分钟)才能在收件人的收件箱中收到邮件。但是在我们的服务器中,我们在访问 API 时得到了成功响应。 我们在生活环境中正面临这个问题。

下面附上我为 outlook 邮件服务尝试的代码参考。

从下面的批量电子邮件发送代码开始,我正在使用 sendBulkMail 函数,之后对于我正在使用 sendSingleMail 函数的任何单个电子邮件。

我可以得到任何解决方案吗?

let tokenConfig = {
    access_token: '',
    token_type: '',
    expires_in: 0
};

const isTokenValid = () => {
    try {
        if (tokenConfig.access_token) {
            return (tokenConfig.expires_in > moment().toDate().getTime());
        }
        return false;
    } catch (error) {
        return false;
    }
};

const getToken = async () => {
    try {
        if (isTokenValid()) {
            return tokenConfig;
        }
        tokenConfig = {
            access_token: '',
            token_type: '',
            expires_in: 0
        };
        const tokenUrl = `${config.mail.smtp.azureAadEndpoint}/${config.mail.smtp.tenantId}/oauth2/token`;
        const requestBody = {
            grant_type: 'client_credentials',
            client_id: config.mail.smtp.clientId,
            client_secret: config.mail.smtp.clientSecret,
            resource: config.mail.smtp.azureGraphEndpoint
        };
        const bodyPayload = Object.keys(requestBody).map((key) => `${key}=${encodeURIComponent(requestBody[key])}`).join('&');
        const headersConfig = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } };
        const tokenResp = await request.post('', tokenUrl, bodyPayload, headersConfig);
        tokenConfig = {
            access_token: tokenResp.data.access_token,
            token_type: tokenResp.data.token_type,
            expires_in: moment().add(tokenResp.data.expires_in, 'seconds').toDate().getTime(),
        };
        return tokenConfig;
    } catch (error) {
        tokenConfig = {
            access_token: '',
            token_type: '',
            expires_in: 0
        };
        return tokenConfig;
    }
};

const sendMail = async (mailBody, batch = false) => {
    try {
        const { access_token, token_type } = await getToken();

        if (batch) {
            const sendMailUrl = `${config.mail.smtp.azureGraphEndpoint}/${config.mail.smtp.azureGraphEndpointVersion}/$batch`;

            const headersConfig = {
                headers: {
                    'Content-Type': 'application/json',
                    Authorization: `${token_type} ${access_token}`
                }
            };
            const mailSendResp = await timeouutSendMail(sendMailUrl, mailBody, headersConfig);
            return mailSendResp;
        } else {
            const sendMailUrl = `${config.mail.smtp.azureGraphEndpoint}/${config.mail.smtp.azureGraphEndpointVersion}/users/${config.mail.fromMailId}/sendMail`;

            const headersConfig = {
                headers: {
                    'Content-Type': 'application/json',
                    Authorization: `${token_type} ${access_token}`
                }
            };
            const mailSendResp = await timeouutSendMail(sendMailUrl, mailBody, headersConfig);
            return mailSendResp;
        }
    } catch (error) {
        return Promise.reject(error);
    }
};

const retryMailTimeout = (reqBody, batch = false, retrySec = 0) => {
    return new Promise((resolve, reject) => {
        setTimeout(async () => {
            try {
                const outlookSendResp = await sendMail(reqBody, batch);
                resolve(outlookSendResp);
            } catch (error) {
                reject(error);
            }
        }, (retrySec + 1) * 1000);
    });
};

const retryMailTillSuccess = async (messages, retrySec = 0) => {
    try {
        let allMailResponses = [];
        const splitRetryMail = async (innerMessages = []) => {
            if (!innerMessages.length) { innerMessages = messages; }
            let returnResponses = [];
            const msgSplits = _.chunk(innerMessages, 20);
            for (const msgData of msgSplits) {
                const outlookSendResp = await retryMailTimeout({ requests: msgData }, true, (retrySec + 1));
                returnResponses = [...returnResponses, ...outlookSendResp.data.responses];
            }
            const retryResponses = _.filter(returnResponses, { status: 429 }).map(innerData => {
                innerData.id = Number(innerData.id);
                innerData.headers['Retry-After'] = Number(innerData.headers['Retry-After']);
                return innerData;
            });

            const maxRetrySec = Math.max(...retryResponses.map(o => o.headers['Retry-After']));

            const retryMessages = retryResponses.map(retryData => _.find(innerMessages, { id: retryData.id }));
            allMailResponses = [...allMailResponses, ..._.filter(returnResponses, respData => { return (respData.status != 429); })].filter((v, i, a) => a.findIndex(v2 => (v2.id === v.id)) === i);
            if (retryMessages.length) {
                const outlookSendResp = await splitRetryMail(retryMessages, maxRetrySec);
                return outlookSendResp;
            }
            return returnResponses;
        };
        await splitRetryMail();
        return allMailResponses;
    } catch (error) {
        return Promise.reject(error);
    }
};

const sendBulkMail = async (messages = []) => {
    try {
        if (messages.length) {
            messages = messages.map((innerData, idx) => {
                innerData.from = config.mail.fromMailId;
                if (innerData.htmlTemplate) { innerData.html = innerData.htmlTemplate; }
                const mailBody = {
                    message: {
                        subject: innerData.subject,
                        body: {
                            contentType: ((innerData.htmlTemplate || innerData.html) ? 'HTML' : 'Text'),
                            content: (innerData.text || (innerData.htmlTemplate || innerData.html))
                        },
                        toRecipients: [{
                            emailAddress: {
                                address: innerData.to
                            }
                        }]
                    },
                    saveToSentItems: 'false'
                };
                if (innerData.mailAttachments && innerData.mailAttachments.length) {
                    mailBody.message.attachments = innerData.mailAttachments.map(attachementData => {
                        return {
                            '@odata.type': '#microsoft.graph.fileAttachment',
                            name: attachementData.filename,
                            contentType: attachementData.type,
                            contentBytes: attachementData.content
                        };
                    });
                }

                return {
                    id: idx,
                    url: `/users/${config.mail.fromMailId}/sendMail`,
                    method: 'POST',
                    body: mailBody,
                    headers: {
                        'Content-Type': 'application/json'
                    }
                };
            });

            const returnResponses = await retryMailTillSuccess(messages, 1);
            return returnResponses;
        }
        return [];
    } catch (error) {
        return Promise.reject(error);
    }
};

const sendSingleMail = async (toMailId, mailSubject, mailText, mailHtml, mailAttachments) => {
    try {
        if (typeof toMailId == 'string') {
            toMailId = [toMailId];
        }

        const mailBody = {
            message: {
                subject: mailSubject,
                body: {
                    contentType: (mailHtml ? 'HTML' : 'Text'),
                    content: (mailText || mailHtml)
                },
                toRecipients: toMailId.map(mailId => ({
                    emailAddress: {
                        address: mailId
                    }
                }))
            },
            saveToSentItems: 'false'
        };
        if (mailAttachments && mailAttachments.length) {
            mailBody.message.attachments = mailAttachments.map(attachementData => {
                return {
                    '@odata.type': '#microsoft.graph.fileAttachment',
                    name: attachementData.filename,
                    contentType: attachementData.type,
                    contentBytes: attachementData.content
                };
            });
        }
        const outlookSendResp = await sendMail(mailBody);
        return outlookSendResp;

    } catch (error) {
        console.log('\n mail send error...', error);
        return Promise.reject(error);
    }
};
回答如下:
发布评论

评论列表(0)

  1. 暂无评论