oauth2 方式获取outlook邮箱收件箱(python)

news2024/12/29 9:50:28

1.在Azure 门户注册应用程序

  微软文档地址

  重定向的地址配置(微软地址):  https://login.microsoftonline.com/common/oauth2/nativeclient

注册应用地址

2.程序代码

#安装包以及需要的驱动
pip3 install playwright

playwright install
import base64
import json
import logging
from io import BytesIO

from playwright.sync_api import sync_playwright
import time
import urllib.parse
import requests

from pia.utils.cache import get_redis_value, set_redis_expire
from pia.utils.constants import OUTLOOK_CLIENT_ID, OUTLOOK_TENANT_ID, OUTLOOK_CLIENT_SECRET, OUTLOOK_REDIRECT_URI, \
    COS_OUTLOOK_DIR, OUTLOOK_TOP
from pia.utils.cos_upload import upload_stream_to_cos, check_exists
from pia.utils.reids_key import OUTLOOK_TOKEN

log = logging.getLogger(__name__)

#账号登录 官方文档 https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
def get_authorization_code(_user_name, _pass_word):
    with sync_playwright() as play_wright:
        browser = play_wright.chromium.launch(headless=True)
        context = browser.new_context(locale="zh-CN", accept_downloads=True)
        page = context.new_page()
        url = f"https://login.microsoftonline.com/{OUTLOOK_TENANT_ID}/oauth2/v2.0/authorize?client_id={OUTLOOK_CLIENT_ID}&response_type=code&redirect_uri={OUTLOOK_REDIRECT_URI}&response_mode=query&scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read&state=12345"
        page.goto(url)

        page.click("[placeholder=\"电子邮件、电话或\\ Skype\"]")

        page.fill("[placeholder=\"电子邮件、电话或\\ Skype\"]", _user_name)

        with page.expect_navigation():
            page.click("text=下一步")

        page.click("[placeholder=\"密码\"]")

        page.fill("[placeholder=\"密码\"]", _pass_word)

        with page.expect_navigation():
            page.click("text=登录")

        page.click("text=是")

        time.sleep(3)
        query = dict(urllib.parse.parse_qsl(urllib.parse.urlsplit(page.url).query))
        authorization_code = query.get('code')
        context.close()
        browser.close()
        return authorization_code


def get_token(authorization_code):
    param = {'client_id': OUTLOOK_CLIENT_ID,
             'code': authorization_code,
             'redirect_uri': OUTLOOK_REDIRECT_URI,
             'grant_type': 'authorization_code',
             'client_secret': OUTLOOK_CLIENT_SECRET
             }
    token_headers = {"Content-Type": "application/x-www-form-urlencoded"}
    token_url = f'https://login.microsoftonline.com/{OUTLOOK_TENANT_ID}/oauth2/v2.0/token'

    res = requests.post(url=token_url, headers=token_headers, data=param)
    access_token = json.loads(res.text).get('access_token')
    return f'Bearer {access_token}'

# api官方文档 https://learn.microsoft.com/en-us/graph/api/mailfolder-list-messages?view=graph-rest-1.0
def get_inbox(authorization, str_start, str_end):
    condition = '?$filter = '
    if str_start:
        condition += f'ReceivedDateTime ge {str_start} and '
    condition += f'receivedDateTime lt {str_end}'
    # 获取收件箱里面的 邮件
    endpoint = f"https://graph.microsoft.com/v1.0/me/mailFolders/inbox/messages{condition}&$top={OUTLOOK_TOP}"
    http_headers = {'Authorization': authorization,
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'}
    data = requests.get(endpoint, headers=http_headers, stream=False).json()
    return data


def get_email_attachments(authorization, email_id):
    # 获取收件箱里面的邮件附件
    endpoint = f"https://graph.microsoft.com/v1.0/me/messages/{email_id}/attachments"
    http_headers = {'Authorization': authorization,
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'}
    data = requests.get(endpoint, headers=http_headers, stream=False).json()
    return data

#程序入口
def deal_user_email(_user_name, _pass_word, str_start, str_end) -> list:
    result = []
    redis_key = f'{OUTLOOK_TOKEN}:{_user_name}'
    # 缓存
    authorization = get_redis_value(redis_key)
    if authorization:
        pass
    else:
        authorization_code = get_authorization_code(_user_name, _pass_word)
        authorization = get_token(authorization_code)
        if authorization:
            set_redis_expire(redis_key, authorization, 60 * 60)

    if authorization:
        email = get_inbox(authorization, str_start, str_end)
        if email:
            email_values = email.get("value")
            if email_values:
                for value in email_values:
                    # 邮件 id
                    email_id = value.get("id")
                    # 是否存在附件 True/False
                    has_attachments = value.get("hasAttachments")
                    value.update({"attachments": {}})
                    if has_attachments and email_id:
                        attachment_dict = upload_attachment_to_cos(authorization, email_id, _user_name)
                        value.update({"attachments": attachment_dict})
                    result.append(value)

    else:
        log.error(f"outlook user_name: {_user_name} Authorization Failed")

    return result


'''
附件上传到cos
'''


def upload_attachment_to_cos(authorization, email_id, _user_name):
    attachment_dict = {}
    attachments = get_email_attachments(authorization, email_id)
    if attachments:
        attachment_values = attachments.get("value")
        if attachment_values:
            for _value in attachment_values:
                # 附件 name
                attachment_name = _value.get("name")
                # 附件 内容
                attachment_content = _value.get("contentBytes")
                # Step 1: 解码 Base64 字符串
                decoded_data = base64.b64decode(attachment_content)
                # Step 2: 创建一个 BytesIO 对象作为文件流
                file_stream = BytesIO(decoded_data)
                object_name = f'{COS_OUTLOOK_DIR}/{_user_name}/{email_id}/{attachment_name}'
                is_exists, url = check_exists(object_name)
                if not is_exists:
                    url = upload_stream_to_cos(file_stream, object_name)
                attachment_dict.update({attachment_name: url})

    return attachment_dict
import traceback
from datetime import datetime, timedelta

from django.core.management import BaseCommand
import logging
import uuid

from django.db import transaction

from pia.models import PiaOutLookTask, PiaOutLookData
from pia.utils.aes import decrypted
from pia.utils.outlook import deal_user_email

logging = logging.getLogger('task')

#任务的方式拉取
class Command(BaseCommand):

    def add_arguments(self, parser):
        parser.add_argument('trace_id', type=str)

    def handle(self, *args, **options):
        trace_id = options["trace_id"]
        if not trace_id:
            trace_id = str(uuid.uuid4())
        self.trace_id = trace_id

        self.writeLog('outlook email start')
        outlook_task = PiaOutLookTask.objects.values("id", "user_name", "pwd", "execution_time")
        for x in outlook_task:

            _id = x.get("id")
            user_name = x.get("user_name")
            self.writeLog(f'############## outlook email user_name:{user_name} start ##############')
            pwd = x.get("pwd")
            execution_time = x.get("execution_time")
            # 获取当前时间
            current_time = datetime.now()
            # 格式化为 YYYY-MM-DD
            end_time = current_time.strftime('%Y-%m-%dT%H:%M:%S')
            try:
                if user_name and pwd:
                    _pwd = decrypted(pwd)
                    result = deal_user_email(user_name, _pwd, f'{execution_time}Z', F'{end_time}Z')
                    with transaction.atomic():
                        PiaOutLookTask.objects.filter(id=_id).update(status=0, execution_time=end_time)
                        outlook_data = PiaOutLookData.objects.filter(outlook_task_id=_id, start_time=execution_time,
                                                                     end_time=end_time)
                        if outlook_data:
                            outlook_data.update(content=result)
                        else:
                            PiaOutLookData.objects.create(outlook_task_id=_id, start_time=execution_time,
                                                          end_time=end_time, content=result)
                self.writeLog(f'############## outlook email user_name:{user_name} end ##############')
            except Exception:
                PiaOutLookTask.objects.filter(id=_id).update(status=1, execution_time=end_time)
                self.writeLog(
                    f'##############  outlook email user_name:{user_name} execution failed::::{traceback.format_exc()}')
        self.writeLog('outlook email end')

    def writeLog(self, msg: str):
        logging.info(f'[{self.trace_id}] {msg}')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2111965.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服务器模型 Reactor 和 Proactor

Proactor 具体流程如下: 处理器发起异步操作,并关注 IO 完成事件;事件分离器等待操作完成事件;分离器等待过程中,内核并行执行实际的 IO 操作,并将结果存储入用户自定义的缓冲区,最后通知事件分…

零工市场小程序:自由职业者的日常工具

零工市场小程序多功能且便捷,提供了前所未有的灵活性和工作效率。这类小程序不仅改变了自由职业者的工作方式,也重塑了劳动力市场的格局。 一、零工市场小程序的特点 即时匹配:利用先进的数据算法,零工市场小程序能够快速匹配自由…

ASP.Net Core 因集成WebSocket导致Swagger UI显示错误

文章目录 前言一、ApiExplorerSettings二、解决Swagger UI显示问题 前言 Swagger UI 本身并不支持直接展示或测试 WebSocket 端点。Swagger(现在称为 OpenAPI)及其 UI 实现主要是为 RESTful API 设计的,这些 API 基于 HTTP 请求/响应模型。W…

IDEA加载工程报错Error Loading Project: Cannot load module demo.iml解决

spring boot工程由于工程名字为demo不太好,直接更改了这个工程的名字,主要操作了包括重命名项目文件夹、修改IDEA中的项目名称、模块名称、包名称、以及相关的配置文件等。 然后再打开工程,报错Error Loading Project: Cannot load module de…

MyBatis ——在java层面对MySQL数据库进行操作

目录 MyBatis 是一款优秀的 持久层框架,用于简化JDBC(java操作数据库)的开发; 使用MyBatis 查询所有用户数据的过程 Lombok是一个实用的]ava类库,能通过注解的形式 简化 JavaBean的代码 注解: 引入Myb…

Oracle之用TO_CHAR函数将日期格式转化为不带前导零的月份和日

要求: 1、日期格式转化成字符串格式,月和日前面的0需要去掉,如日期2024-09-06需要转化成2024-9-6; 2、如果用截取拼接函数写法就会复杂,最好用TO_CHAR函数格式化实现。 正确写法: SELECT TO_CHAR(SYSDAT…

使用cage工具包生成验证码

目录 1. 导入依赖2. 控制类3. 测试 1. 导入依赖 <!-- 验证码工具 --><dependency><groupId>com.github.cage</groupId><artifactId>cage</artifactId><version>1.0</version></dependency>2. 控制类 RestControl…

SD-WAN解决外贸企业网络搭建问题

在全球化浪潮下&#xff0c;外贸企业日益感受到互联网和数字化技术对业务发展的重要性。作为一种创新的网络解决方案&#xff0c;SD-WAN&#xff08;软件定义广域网&#xff09;正成为外贸企业网络升级的重要助力。本文将深入剖析SD-WAN如何有效解决外贸企业网络搭建中的关键问…

ubuntu java

1、安装&#xff1a;https://zhuanlan.zhihu.com/p/565274672 遇到 sudo kill 8544 2、然后可以正常安装 编写hello world: https://blog.csdn.net/qq_33523925/article/details/92410963 安装IDEA Linux系统安装IDEA保姆级教程_linux安装idea-CSDN博客 3、idea如何设置…

【第0006页 · 数组】寻找重复数

【前言】本文以及之后的一些题解都会陆续整理到目录中&#xff0c;若想了解全部题解整理&#xff0c;请看这里&#xff1a; 第0006页 寻找重复数 今天想讨论的一道题在 LeetCode 上评论也是颇为“不错”。有一说一&#xff0c;是道好题&#xff0c;不过我们还是得先理解了它才…

北京理工大学开设仓颉实践课程,培养特色化软件人才

为响应国家软件发展战略&#xff0c;培养满足产业发展需求的特色化软件人才&#xff0c;北京理工大学信息技术创新学院率先把仓颉语言引入到大二暑期的实践课程中。在今年暑期为期3周的实践课中&#xff0c;信息技术创新学院教研中心副主任徐礼文老师开设了“仓颉语言HarmonyOS…

Git 撤回commit

上一篇&#xff0c;Git撤销add&#xff0c;其实已经讲了用reset命令可以取消commit&#xff0c;这里再啰嗦下。先看&#xff1a; git如何撤回已经commit • Worktile社区 首先明确一点&#xff0c;无论是commit还是撤销commit&#xff0c;都是在本地暂存区操作&#xff0c;而…

CTFHub技能树-Git泄漏-Stash

目录 一、前提知识 1.什么是git stash 2.git文件目录结构 3.git中对象指向 二、解题过程 方法一&#xff1a;使用GitHack 方法二&#xff1a;使用Git_Extract工具&#xff0c;这个是自动解析不用git stash等操作&#xff0c;直接得到flag 当前大量开发人员使用git进行版本…

C/C++:C语言中的__FILE__、__LINE__等几种C标准用法

C语言中的几种特殊标准定义 __FILE__ &#xff1a;正在编译文件的文件名__LINE__ &#xff1a;正在编译文件的行号__DATE__&#xff1a;编译时刻的日期字符串 如“Sep 22 2020”__TIME__&#xff1a;编译时刻的时间字符串 如”10:00:00“__STDC__&#xff1a;判断该文件是不是…

网关功能介绍

在微服务架构中&#xff0c;网关&#xff08;API Gateway&#xff09;扮演着至关重要的角色&#xff0c;它作为客户端和微服务之间的中介&#xff0c;负责路由、过滤、认证、限流等职责。以下是一些常见的网关实现&#xff1a; Spring Cloud Gateway&#xff1a; Spring Cloud …

【佳学基因检测】网站加密证书失效后,如何移除并为新的证书安装准备环境?

【佳学基因检测】网站加密证书失效后&#xff0c;如何移除并为新的证书安装准备环境&#xff1f; 当WoTrus DV Server CA证书失效后&#xff0c;你需要确保你的Nginx配置中不再引用该证书&#xff0c;并且移除或替换相关的证书文件。以下是具体步骤&#xff1a; 1. 确认Nginx…

如何在 cPanel 中使用 PuTTY SSH

cPanel & WHM 的直观网页界面使管理服务器和虚拟主机账户变得轻松。然而&#xff0c;有时在服务器命令行上工作也是很有用的。PuTTY SSH客户端允许您通过加密连接登录到服务器的命令行界面&#xff0c;从而确保敏感数据不会在互联网上暴露。Hostease 提供高性能的服务器&am…

Python | Leetcode Python题解之第391题完美矩形

题目&#xff1a; 题解&#xff1a; class Solution:def isRectangleCover(self, rectangles: List[List[int]]) -> bool:area, minX, minY, maxX, maxY 0, rectangles[0][0], rectangles[0][1], rectangles[0][2], rectangles[0][3]cnt defaultdict(int)for rect in rec…

【区块链 + 物联网】区块链边缘计算网关设备 | FISCO BCOS应用案例

目前边缘端设备主要以人工智能应用为主&#xff0c;或以数据采集网络设备为主&#xff0c;还未有区块链边缘计算网关设备&#xff0c;难以 在依托终端设备的传统行业中进行区块链 应用。本项目研制区块链边缘计算网关&#xff0c;将区块链、计算与网络集成 在一起&#xff0c;…

docker-compose安装mysql8集群

我这里一主两从mysql数据库集群,mysql镜像版本是8.0.39 如下 如下&#xff1a; [rootVM-20-8-centos mysqlData]# docker-compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS mysql-master …