From 083971a94ac8c530fd6284b76c5ef8ebecfa3851 Mon Sep 17 00:00:00 2001 From: wanjia Date: Mon, 7 Apr 2025 15:07:20 +0800 Subject: [PATCH] =?UTF-8?q?gmail=E6=B7=BB=E5=8A=A0=E9=99=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gmail/attachments/test2.txt | 10 ++++ gmail/email_conversations.json | 27 ++++++++-- gmail/email_conversations.txt | 76 +++++++++++++++++++--------- gmail/quickstart.py | 92 +++++++++++++++++++++++++++++++--- gmail/storage.json | 2 +- 5 files changed, 171 insertions(+), 36 deletions(-) create mode 100644 gmail/attachments/test2.txt diff --git a/gmail/attachments/test2.txt b/gmail/attachments/test2.txt new file mode 100644 index 00000000..b16ac8e5 --- /dev/null +++ b/gmail/attachments/test2.txt @@ -0,0 +1,10 @@ +本期节目内容简介 +在参加各类比赛时,肯定会遇到竞争对手。英语单词 rival、opponent、competitor 和 contestant 的含义相似,都可以用来指“与他人之间存在竞争关系的人或团队”。在本集《你问我答》节目中,我们将通过和体育比赛有关的实例来为大家阐释这四个近义词之间的区别和用法。 + +欢迎你加入并和我们一起讨论英语学习的方方面面。请通过微博“BBC英语教学”或邮件与我们取得联系。我们的邮箱地址是 questions.chinaelt@bbc.co.uk。 + +文字稿 +(关于台词的备注: 请注意这不是广播节目的逐字稿件。本文稿可能没有体现录制、编辑过程中对节目做出的改变。) + +Feifei +大家好,欢迎收听 BBC 英语教学的《你问我答》节目,我是冯菲菲。每集节目中,我们会回答大家在英语学习时遇到的一个问题。本集的问题来自 Adela。我们来听一下她的问题。 diff --git a/gmail/email_conversations.json b/gmail/email_conversations.json index 2756391a..beded6f6 100644 --- a/gmail/email_conversations.json +++ b/gmail/email_conversations.json @@ -1,20 +1,41 @@ [ { + "id": "195d5c6094b7e85f", "subject": "这是主题", "from": "crush wds ", "date": "2025-03-27 12:04:29", - "body": "你好呀\r\n" + "body": "你好呀\r\n", + "attachments": [] }, { + "id": "195d5c72dbafcc13", "subject": "", "from": "wds crush ", "date": "2025-03-27 12:05:54", - "body": "你那里天气怎么样\r\n" + "body": "你那里天气怎么样\r\n", + "attachments": [] }, { + "id": "195d604a5ad5fabf", "subject": "", "from": "wds crush ", "date": "2025-03-27 13:13:03", - "body": "吃饭了吗\r\n" + "body": "吃饭了吗\r\n", + "attachments": [] + }, + { + "id": "1960eec066745682", + "subject": "Re: 这是主题", + "from": "wds crush ", + "date": "2025-04-07 14:24:28", + "body": "测试附件内容\r\n\r\n\r\ncrush wds 于2025年3月27日周四 12:04写道:\r\n\r\n> 你好呀\r\n>\r\n", + "attachments": [ + { + "filename": "test2.txt", + "mimeType": "text/plain", + "size": 996, + "attachmentId": "ANGjdJ-f7-vJBk-5aJnh1zPE0rwbH0PXFzJHsXv9XL5LavJ0r4yQAayw1dw1pQ0AUhgwWyTP_hxcROnZO2C5W3MhBGGFmrcVpjwWE-yFr5BYA-I1-6sn24oEPFqX8Hem4dZxcYd2h6RgSfUfq3BqOe0AtZru0GWvUxRK7kadSCjpP2kigFkVhxvl18MiNmeR5B9aLkkjB6kwSCsTekw8uNTZxh01ZCkjOB1tRiCJnchmh4-TPfOrcZBfV0WNFcbend0DfK9tMRmcs3Gv5aWzFe0mF6eXE5dTAACMEkLwwOrvP-aXRq526DN43RFVasGMvq7-E81pjtcZN_8xPWNs" + } + ] } ] \ No newline at end of file diff --git a/gmail/email_conversations.txt b/gmail/email_conversations.txt index 578ce5be..8b10c07e 100644 --- a/gmail/email_conversations.txt +++ b/gmail/email_conversations.txt @@ -1,25 +1,51 @@ -================================================== -记录时间: 2025-03-27 13:15:18 -================================================== - -时间: 2025-03-27 12:04:29 -发件人: crush wds -主题: 这是主题 -内容: -你好呀 - --------------------------------------------------- -时间: 2025-03-27 12:05:54 -发件人: wds crush -主题: -内容: -你那里天气怎么样 - --------------------------------------------------- -时间: 2025-03-27 13:13:03 -发件人: wds crush -主题: -内容: -吃饭了吗 - --------------------------------------------------- +================================================== +记录时间: 2025-04-07 14:24:38 +================================================== + +时间: 2025-03-27 12:04:29 +发件人: crush wds +主题: 这是主题 +内容: +你好呀 + + +-------------------------------------------------- +时间: 2025-03-27 12:05:54 +发件人: wds crush +主题: +内容: +你那里天气怎么样 + + +-------------------------------------------------- +时间: 2025-03-27 13:13:03 +发件人: wds crush +主题: +内容: +吃饭了吗 + + +-------------------------------------------------- +时间: 2025-04-07 14:24:28 +发件人: wds crush +主题: Re: 这是主题 +内容: +测试附件内容 + + + + + +crush wds 于2025年3月27日周四 12:04写道: + + + +> 你好呀 + +> + + + +附件: + - test2.txt (text/plain, 996 字节) +-------------------------------------------------- diff --git a/gmail/quickstart.py b/gmail/quickstart.py index dd2c5e07..eda5118f 100644 --- a/gmail/quickstart.py +++ b/gmail/quickstart.py @@ -21,18 +21,47 @@ if not creds or creds.invalid: creds = tools.run_flow(flow, store) GMAIL = discovery.build('gmail', 'v1', http=creds.authorize(Http())) +def download_attachment(message_id, attachment_id, filename): + """下载邮件附件""" + try: + attachment = GMAIL.users().messages().attachments().get( + userId='me', + messageId=message_id, + id=attachment_id + ).execute() + + data = attachment['data'] + file_data = base64.urlsafe_b64decode(data) + + # 创建附件目录 + if not os.path.exists('attachments'): + os.makedirs('attachments') + + # 保存附件 + filepath = os.path.join('attachments', filename) + with open(filepath, 'wb') as f: + f.write(file_data) + + return filepath + except Exception as e: + print(f"Error downloading attachment: {str(e)}") + return None + def get_email_content(message): """提取邮件内容""" try: + message_id = message['id'] # 获取邮件ID payload = message['payload'] headers = payload['headers'] # 获取邮件基本信息 email_data = { + 'id': message_id, # 保存邮件ID 'subject': '', 'from': '', 'date': '', - 'body': '' + 'body': '', + 'attachments': [] # 新增附件列表 } # 提取头部信息 @@ -45,16 +74,37 @@ def get_email_content(message): date = parser.parse(header['value']) email_data['date'] = date.strftime('%Y-%m-%d %H:%M:%S') - # 提取邮件正文 - if 'parts' in payload: - parts = payload['parts'] + # 定义一个递归函数来处理所有部分和附件 + def process_parts(parts): for part in parts: - if part['mimeType'] == 'text/plain': + # 检查是否是附件 + if 'filename' in part and part['filename']: + attachment = { + 'filename': part['filename'], + 'mimeType': part['mimeType'], + 'size': part['body'].get('size', 0) + } + + # 如果有附件内容数据,可以获取附件ID + if 'attachmentId' in part['body']: + attachment['attachmentId'] = part['body']['attachmentId'] + + email_data['attachments'].append(attachment) + + # 处理文本内容 + if part['mimeType'] == 'text/plain' and not email_data['body']: data = part['body'].get('data', '') if data: text = base64.urlsafe_b64decode(data).decode('utf-8') email_data['body'] = text - break + + # 递归处理多部分内容 + if 'parts' in part: + process_parts(part['parts']) + + # 处理邮件正文和附件 + if 'parts' in payload: + process_parts(payload['parts']) elif 'body' in payload: data = payload['body'].get('data', '') if data: @@ -123,6 +173,13 @@ def save_conversations(conversations, output_file): f.write(f"主题: {msg['subject']}\n") f.write("内容:\n") f.write(f"{msg['body']}\n") + + # 添加附件信息 + if msg['attachments']: + f.write("\n附件:\n") + for att in msg['attachments']: + f.write(f" - {att['filename']} ({att['mimeType']}, {att['size']} 字节)\n") + f.write("-" * 50 + "\n") print(f"对话记录已保存到: {output_file}") @@ -152,18 +209,39 @@ def main(): if conversations: print(f"找到 {len(conversations)} 条对话记录") - # 保存对话记录(追加模式) + # 统计附件 + total_attachments = 0 + for msg in conversations: + total_attachments += len(msg['attachments']) + + # 保存对话记录 save_conversations(conversations, output_file) # 打印对话统计 print("\n对话统计:") print(f"总消息数: {len(conversations)}") + print(f"总附件数: {total_attachments}") senders = {} for msg in conversations: sender = msg['from'] senders[sender] = senders.get(sender, 0) + 1 for sender, count in senders.items(): print(f"{sender}: {count} 条消息") + + # 提示用户是否下载附件 + if total_attachments > 0: + download_choice = input(f"\n发现 {total_attachments} 个附件,是否下载? (y/n): ") + if download_choice.lower() == 'y': + print("\n开始下载附件...") + downloaded = 0 + for msg in conversations: + for att in msg['attachments']: + if 'attachmentId' in att: + filepath = download_attachment(msg['id'], att['attachmentId'], att['filename']) + if filepath: + downloaded += 1 + print(f"已下载: {att['filename']} -> {filepath}") + print(f"\n完成! 成功下载了 {downloaded}/{total_attachments} 个附件到 'attachments' 目录") else: print("未找到对话记录") diff --git a/gmail/storage.json b/gmail/storage.json index a53fb2fe..efc6c8c4 100644 --- a/gmail/storage.json +++ b/gmail/storage.json @@ -1 +1 @@ -{"access_token": "ya29.a0AeXRPp7jfizXj6NfUavzABcDOEH-PD7TckkeCwQWFksLZwkFXqAaVe1gCeZZIWtP4hMD3F2-K7-hdSQAzCiyP7Z_72YPr_r3UDfflr6XOV7czJ937QknW_236kLJK1DDggpB4BFCB1cXtSJa6V4pbyez4lkka021mZWegOdGaCgYKATkSARISFQHGX2Mi7Ua3f90G-JLtvzu2YJgvoA0175", "client_id": "240872828479-570luspoc31259l1faab6kmjmpcsa9n9.apps.googleusercontent.com", "client_secret": "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", "refresh_token": "1//0eUNnePCc4hEKCgYIARAAGA4SNwF-L9IrIRfxg1JwnMu0WHZAu-uCWT7cx1PncId3bPZr4x53Mo57xGXr2WjiF4szuSQQkeL4hNU", "token_expiry": "2025-03-27T06:13:12Z", "token_uri": "https://oauth2.googleapis.com/token", "user_agent": null, "revoke_uri": "https://oauth2.googleapis.com/revoke", "id_token": null, "id_token_jwt": null, "token_response": {"access_token": "ya29.a0AeXRPp7jfizXj6NfUavzABcDOEH-PD7TckkeCwQWFksLZwkFXqAaVe1gCeZZIWtP4hMD3F2-K7-hdSQAzCiyP7Z_72YPr_r3UDfflr6XOV7czJ937QknW_236kLJK1DDggpB4BFCB1cXtSJa6V4pbyez4lkka021mZWegOdGaCgYKATkSARISFQHGX2Mi7Ua3f90G-JLtvzu2YJgvoA0175", "expires_in": 3599, "scope": "https://www.googleapis.com/auth/gmail.modify", "token_type": "Bearer", "refresh_token_expires_in": 599654}, "scopes": ["https://www.googleapis.com/auth/gmail.modify"], "token_info_uri": "https://oauth2.googleapis.com/tokeninfo", "invalid": false, "_class": "OAuth2Credentials", "_module": "oauth2client.client"} \ No newline at end of file +{"access_token": "ya29.a0AZYkNZjEUAU0zL5oOFtnyDomfMZSzeAVMn5tvmHziV8CaMb4LaK3FAwZ_YC3itk2XlUQlIX0jZ4LQbqZZvdgk7fuwlimFzOHIrfkLt2nd6TPt-L3OqJbmYbYsJRgZ6twrlkX4-nvDsvK0br-WByC5WZj4Ih2FevlMb_-n6J-aCgYKAUUSARISFQHGX2MiYlBZMAAMwlcMNHHm-HnGiA0175", "client_id": "240872828479-570luspoc31259l1faab6kmjmpcsa9n9.apps.googleusercontent.com", "client_secret": "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", "refresh_token": "1//0e45xAmjtJtxpCgYIARAAGA4SNwF-L9Ir7GKiW_h2fGY6auAsSxOtmpocidE68QpFfuUDtwPhSJYHhS_1ZfYDNu8pRcD85X1Kh_Y", "token_expiry": "2025-04-07T07:20:36Z", "token_uri": "https://oauth2.googleapis.com/token", "user_agent": null, "revoke_uri": "https://oauth2.googleapis.com/revoke", "id_token": null, "id_token_jwt": null, "token_response": {"access_token": "ya29.a0AZYkNZjEUAU0zL5oOFtnyDomfMZSzeAVMn5tvmHziV8CaMb4LaK3FAwZ_YC3itk2XlUQlIX0jZ4LQbqZZvdgk7fuwlimFzOHIrfkLt2nd6TPt-L3OqJbmYbYsJRgZ6twrlkX4-nvDsvK0br-WByC5WZj4Ih2FevlMb_-n6J-aCgYKAUUSARISFQHGX2MiYlBZMAAMwlcMNHHm-HnGiA0175", "expires_in": 3599, "refresh_token": "1//0e45xAmjtJtxpCgYIARAAGA4SNwF-L9Ir7GKiW_h2fGY6auAsSxOtmpocidE68QpFfuUDtwPhSJYHhS_1ZfYDNu8pRcD85X1Kh_Y", "scope": "https://www.googleapis.com/auth/gmail.readonly", "token_type": "Bearer", "refresh_token_expires_in": 604799}, "scopes": ["https://www.googleapis.com/auth/gmail.readonly"], "token_info_uri": "https://oauth2.googleapis.com/tokeninfo", "invalid": false, "_class": "OAuth2Credentials", "_module": "oauth2client.client"} \ No newline at end of file