初始提交 - Gmail集成功能优化
This commit is contained in:
parent
083971a94a
commit
45d49d4b4a
@ -1 +1 @@
|
||||
{"installed":{"client_id":"240872828479-570luspoc31259l1faab6kmjmpcsa9n9.apps.googleusercontent.com","project_id":"first-renderer-454910-c1","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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","redirect_uris":["http://localhost"]}}
|
||||
{"installed":{"client_id":"266164728215-v84lngbp3vgr4ulql01sqkg5vaigf4a5.apps.googleusercontent.com","project_id":"knowledge-454905","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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","redirect_uris":["http://localhost"]}}
|
@ -34,8 +34,88 @@
|
||||
"filename": "test2.txt",
|
||||
"mimeType": "text/plain",
|
||||
"size": 996,
|
||||
"attachmentId": "ANGjdJ-f7-vJBk-5aJnh1zPE0rwbH0PXFzJHsXv9XL5LavJ0r4yQAayw1dw1pQ0AUhgwWyTP_hxcROnZO2C5W3MhBGGFmrcVpjwWE-yFr5BYA-I1-6sn24oEPFqX8Hem4dZxcYd2h6RgSfUfq3BqOe0AtZru0GWvUxRK7kadSCjpP2kigFkVhxvl18MiNmeR5B9aLkkjB6kwSCsTekw8uNTZxh01ZCkjOB1tRiCJnchmh4-TPfOrcZBfV0WNFcbend0DfK9tMRmcs3Gv5aWzFe0mF6eXE5dTAACMEkLwwOrvP-aXRq526DN43RFVasGMvq7-E81pjtcZN_8xPWNs"
|
||||
"attachmentId": "ANGjdJ8O1uGve4uPqFSC2C4sMeC5jXJ3DGilhB1By705ZLGbOF30m6uITRtJHWsnB7yREKVslhYRdu4GKKvrrkWw-63ogqaZPGgi0WuSoB0OxIWXwbQSjaayUOPvc3P8y1g9A2mMAm5k0DkjH_LP0QMmulhXMg8ZgUcm4CZR7-HTBYztZSfWOoUeYkNugdzV5_2ax3GT34P5uaGHh3i4Ge6y-XDN-TDq3i1w9u_eTjZowoyVzjTj48uaQmzmFU36TQxg8ncovHk0sNZEygzCE1gbHbS1uM9N1tUHOOa6FWEpajHgz2aQwLh65SsMRqKe-LognFES-J_082IHddWs"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1961a1981e915eb3",
|
||||
"subject": "",
|
||||
"from": "crush wds <ardonisierni@gmail.com>",
|
||||
"date": "2025-04-09 18:29:51",
|
||||
"body": "测试角色1\r\n",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"id": "1961a19cc997f933",
|
||||
"subject": "",
|
||||
"from": "wds crush <crushwds@gmail.com>",
|
||||
"date": "2025-04-09 18:30:21",
|
||||
"body": "测试角色2\r\n",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"id": "1961de790e0b39e6",
|
||||
"subject": "",
|
||||
"from": "crushwds@gmail.com",
|
||||
"date": "2025-04-10 00:13:57",
|
||||
"body": "您好,关于我们之前讨论的产品,我想确认一些细节...",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"id": "1961dee46652dc86",
|
||||
"subject": "",
|
||||
"from": "crushwds@gmail.com",
|
||||
"date": "2025-04-10 00:21:17",
|
||||
"body": "您好,关于我们之前讨论的产品,我想确认一些细节...",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"id": "1961dfd7d36bf92c",
|
||||
"subject": "",
|
||||
"from": "crushwds@gmail.com",
|
||||
"date": "2025-04-10 00:37:54",
|
||||
"body": "您好,关于我们之前讨论的产品,我想确认一些细节...",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"id": "1961e39ad02da5f0",
|
||||
"subject": "",
|
||||
"from": "crushwds@gmail.com",
|
||||
"date": "2025-04-10 01:43:38",
|
||||
"body": "您好,关于我们之前讨论的产品,我想确认一些细节...",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"id": "1961df3bd554a4af",
|
||||
"subject": "",
|
||||
"from": "crush wds <ardonisierni@gmail.com>",
|
||||
"date": "2025-04-10 12:27:03",
|
||||
"body": "yes\r\n",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"id": "1961e3944c8aab04",
|
||||
"subject": "",
|
||||
"from": "crush wds <ardonisierni@gmail.com>",
|
||||
"date": "2025-04-10 13:42:58",
|
||||
"body": "1\r\n",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"id": "1961e39660feebd9",
|
||||
"subject": "",
|
||||
"from": "crush wds <ardonisierni@gmail.com>",
|
||||
"date": "2025-04-10 13:43:07",
|
||||
"body": "2\r\n",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"id": "1961ec2028c16037",
|
||||
"subject": "",
|
||||
"from": "crush wds <ardonisierni@gmail.com>",
|
||||
"date": "2025-04-10 16:12:20",
|
||||
"body": "你好\r\n",
|
||||
"attachments": []
|
||||
}
|
||||
]
|
@ -1,5 +1,5 @@
|
||||
==================================================
|
||||
记录时间: 2025-04-07 14:24:38
|
||||
记录时间: 2025-04-10 16:52:49
|
||||
==================================================
|
||||
|
||||
时间: 2025-03-27 12:04:29
|
||||
@ -8,7 +8,6 @@
|
||||
内容:
|
||||
你好呀
|
||||
|
||||
|
||||
--------------------------------------------------
|
||||
时间: 2025-03-27 12:05:54
|
||||
发件人: wds crush <crushwds@gmail.com>
|
||||
@ -16,7 +15,6 @@
|
||||
内容:
|
||||
你那里天气怎么样
|
||||
|
||||
|
||||
--------------------------------------------------
|
||||
时间: 2025-03-27 13:13:03
|
||||
发件人: wds crush <crushwds@gmail.com>
|
||||
@ -24,7 +22,6 @@
|
||||
内容:
|
||||
吃饭了吗
|
||||
|
||||
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-07 14:24:28
|
||||
发件人: wds crush <crushwds@gmail.com>
|
||||
@ -33,19 +30,78 @@
|
||||
测试附件内容
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
crush wds <ardonisierni@gmail.com> 于2025年3月27日周四 12:04写道:
|
||||
|
||||
|
||||
|
||||
> 你好呀
|
||||
|
||||
>
|
||||
|
||||
|
||||
|
||||
附件:
|
||||
- test2.txt (text/plain, 996 字节)
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-09 18:29:51
|
||||
发件人: crush wds <ardonisierni@gmail.com>
|
||||
主题:
|
||||
内容:
|
||||
测试角色1
|
||||
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-09 18:30:21
|
||||
发件人: wds crush <crushwds@gmail.com>
|
||||
主题:
|
||||
内容:
|
||||
测试角色2
|
||||
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-10 00:13:57
|
||||
发件人: crushwds@gmail.com
|
||||
主题:
|
||||
内容:
|
||||
您好,关于我们之前讨论的产品,我想确认一些细节...
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-10 00:21:17
|
||||
发件人: crushwds@gmail.com
|
||||
主题:
|
||||
内容:
|
||||
您好,关于我们之前讨论的产品,我想确认一些细节...
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-10 00:37:54
|
||||
发件人: crushwds@gmail.com
|
||||
主题:
|
||||
内容:
|
||||
您好,关于我们之前讨论的产品,我想确认一些细节...
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-10 01:43:38
|
||||
发件人: crushwds@gmail.com
|
||||
主题:
|
||||
内容:
|
||||
您好,关于我们之前讨论的产品,我想确认一些细节...
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-10 12:27:03
|
||||
发件人: crush wds <ardonisierni@gmail.com>
|
||||
主题:
|
||||
内容:
|
||||
yes
|
||||
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-10 13:42:58
|
||||
发件人: crush wds <ardonisierni@gmail.com>
|
||||
主题:
|
||||
内容:
|
||||
1
|
||||
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-10 13:43:07
|
||||
发件人: crush wds <ardonisierni@gmail.com>
|
||||
主题:
|
||||
内容:
|
||||
2
|
||||
|
||||
--------------------------------------------------
|
||||
时间: 2025-04-10 16:12:20
|
||||
发件人: crush wds <ardonisierni@gmail.com>
|
||||
主题:
|
||||
内容:
|
||||
你好
|
||||
|
||||
--------------------------------------------------
|
||||
|
@ -13,7 +13,7 @@ os.environ['HTTP_PROXY'] = 'http://127.0.0.1:7890'
|
||||
os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890'
|
||||
|
||||
# Gmail API 认证
|
||||
SCOPES = 'https://www.googleapis.com/auth/gmail.readonly'
|
||||
SCOPES = ['https://mail.google.com/']
|
||||
store = file.Storage('storage.json')
|
||||
creds = store.get()
|
||||
if not creds or creds.invalid:
|
||||
|
@ -1 +1 @@
|
||||
{"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"}
|
||||
{"access_token": "ya29.a0AZYkNZga-tjDnp1lsXRohu1Tji-eVV88RaLnPjxr3HpYuBDW_6boys1aqnRnete1pT-E7ygZ5drpb0Hhbt9o15ryqbfeaKqS4HTDG_iIVvFn3npNNLSqIdvsf98burhBOnR-Nf6ty7xCsPLyFaO15bG2LybRgGL1mubVNMXSaCgYKAdQSARISFQHGX2MicVi2eoShd196_WeptFDUZg0175", "client_id": "266164728215-v84lngbp3vgr4ulql01sqkg5vaigf4a5.apps.googleusercontent.com", "client_secret": "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", "refresh_token": "1//0eAXpVapw8WjjCgYIARAAGA4SNwF-L9Irm0iHkQzqzM7Hn39nctE-DOWKTsm89Ge3nG0bfdfqloRvLMiN4YWHEKcDpLdPIuZel0Q", "token_expiry": "2025-04-10T09:51:34Z", "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.a0AZYkNZga-tjDnp1lsXRohu1Tji-eVV88RaLnPjxr3HpYuBDW_6boys1aqnRnete1pT-E7ygZ5drpb0Hhbt9o15ryqbfeaKqS4HTDG_iIVvFn3npNNLSqIdvsf98burhBOnR-Nf6ty7xCsPLyFaO15bG2LybRgGL1mubVNMXSaCgYKAdQSARISFQHGX2MicVi2eoShd196_WeptFDUZg0175", "expires_in": 3599, "refresh_token": "1//0eAXpVapw8WjjCgYIARAAGA4SNwF-L9Irm0iHkQzqzM7Hn39nctE-DOWKTsm89Ge3nG0bfdfqloRvLMiN4YWHEKcDpLdPIuZel0Q", "scope": "https://mail.google.com/", "token_type": "Bearer", "refresh_token_expires_in": 604799}, "scopes": ["https://mail.google.com/"], "token_info_uri": "https://oauth2.googleapis.com/tokeninfo", "invalid": false, "_class": "OAuth2Credentials", "_module": "oauth2client.client"}
|
@ -1 +0,0 @@
|
||||
{"token": "ya29.a0AeXRPp5MvarP9U4JZ3ZlV_xpIeHNL7IK1HEQLX6qhY-thS5IOFsdHrcNwbdNqkYrClFm4yGiMCXzWO-IilVbdSrDfEDw-Qfs7V9oTsZS2RYjbgcSR9rLUtZlL8ObnaljQie5VVzQZysHPZcMSdWiqabcQq2UsU5TNuOWo4J7_waCgYKAQESARISFQHGX2MiAR1O_HNYd0F6z0Rj-vSybA0177", "refresh_token": "1//0e1Usc30XxlXRCgYIARAAGA4SNwF-L9IrOOjEN7EkOSatDMHzTy0KaNdp_jf1XtLn7a5pFSljo3IKnrDdvVnxTO_FX5Asj_UDiAw", "token_uri": "https://oauth2.googleapis.com/token", "client_id": "240872828479-570luspoc31259l1faab6kmjmpcsa9n9.apps.googleusercontent.com", "client_secret": "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", "scopes": ["https://www.googleapis.com/auth/gmail.readonly"], "universe_domain": "googleapis.com", "account": "", "expiry": "2025-03-27T04:39:31.274041Z"}
|
10
gmail_attachments/1960eec066745682_test2.txt
Normal file
10
gmail_attachments/1960eec066745682_test2.txt
Normal file
@ -0,0 +1,10 @@
|
||||
本期节目内容简介
|
||||
在参加各类比赛时,肯定会遇到竞争对手。英语单词 rival、opponent、competitor 和 contestant 的含义相似,都可以用来指“与他人之间存在竞争关系的人或团队”。在本集《你问我答》节目中,我们将通过和体育比赛有关的实例来为大家阐释这四个近义词之间的区别和用法。
|
||||
|
||||
欢迎你加入并和我们一起讨论英语学习的方方面面。请通过微博“BBC英语教学”或邮件与我们取得联系。我们的邮箱地址是 questions.chinaelt@bbc.co.uk。
|
||||
|
||||
文字稿
|
||||
(关于台词的备注: 请注意这不是广播节目的逐字稿件。本文稿可能没有体现录制、编辑过程中对节目做出的改变。)
|
||||
|
||||
Feifei
|
||||
大家好,欢迎收听 BBC 英语教学的《你问我答》节目,我是冯菲菲。每集节目中,我们会回答大家在英语学习时遇到的一个问题。本集的问题来自 Adela。我们来听一下她的问题。
|
@ -0,0 +1 @@
|
||||
{"access_token": "ya29.a0AZYkNZga-tjDnp1lsXRohu1Tji-eVV88RaLnPjxr3HpYuBDW_6boys1aqnRnete1pT-E7ygZ5drpb0Hhbt9o15ryqbfeaKqS4HTDG_iIVvFn3npNNLSqIdvsf98burhBOnR-Nf6ty7xCsPLyFaO15bG2LybRgGL1mubVNMXSaCgYKAdQSARISFQHGX2MicVi2eoShd196_WeptFDUZg0175", "client_id": "266164728215-v84lngbp3vgr4ulql01sqkg5vaigf4a5.apps.googleusercontent.com", "client_secret": "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", "refresh_token": "1//0eAXpVapw8WjjCgYIARAAGA4SNwF-L9Irm0iHkQzqzM7Hn39nctE-DOWKTsm89Ge3nG0bfdfqloRvLMiN4YWHEKcDpLdPIuZel0Q", "token_expiry": "2025-04-10T09:51:34Z", "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.a0AZYkNZga-tjDnp1lsXRohu1Tji-eVV88RaLnPjxr3HpYuBDW_6boys1aqnRnete1pT-E7ygZ5drpb0Hhbt9o15ryqbfeaKqS4HTDG_iIVvFn3npNNLSqIdvsf98burhBOnR-Nf6ty7xCsPLyFaO15bG2LybRgGL1mubVNMXSaCgYKAdQSARISFQHGX2MicVi2eoShd196_WeptFDUZg0175", "expires_in": 3599, "refresh_token": "1//0eAXpVapw8WjjCgYIARAAGA4SNwF-L9Irm0iHkQzqzM7Hn39nctE-DOWKTsm89Ge3nG0bfdfqloRvLMiN4YWHEKcDpLdPIuZel0Q", "scope": "https://mail.google.com/", "token_type": "Bearer", "refresh_token_expires_in": 604799}, "scopes": ["https://mail.google.com/"], "token_info_uri": "https://oauth2.googleapis.com/tokeninfo", "invalid": false, "_class": "OAuth2Credentials", "_module": "oauth2client.client"}
|
@ -142,7 +142,7 @@ TIME_ZONE = 'Asia/Shanghai'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = False
|
||||
USE_TZ = True # 将此项设置为True以启用时区支持
|
||||
|
||||
|
||||
|
||||
@ -294,3 +294,26 @@ REST_FRAMEWORK = {
|
||||
'rest_framework.parsers.MultiPartParser'
|
||||
],
|
||||
}
|
||||
|
||||
# Gmail API配置
|
||||
GOOGLE_CLOUD_PROJECT = 'knowledge-454905' # 更新为当前使用的项目ID
|
||||
GMAIL_API_SCOPES = ['https://mail.google.com/']
|
||||
GMAIL_TOPIC_NAME = 'gmail-watch-topic'
|
||||
|
||||
# Gmail webhook地址 (开发环境使用本机内网穿透地址)
|
||||
GMAIL_WEBHOOK_URL = 'https://a7a4-116-227-35-74.ngrok-free.app/api/user/gmail/webhook/'
|
||||
|
||||
# 如果在生产环境,使用以下固定地址
|
||||
# GMAIL_WEBHOOK_URL = 'https://你的域名/api/user/gmail/webhook/'
|
||||
|
||||
# 媒体文件目录
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
# 时区设置
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True # 将此项设置为True以启用时区支持
|
||||
|
||||
|
||||
|
@ -18,6 +18,7 @@ from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from user_management.views import gmail_webhook # 直接导入视图函数
|
||||
|
||||
urlpatterns = [
|
||||
# 管理后台
|
||||
@ -26,6 +27,10 @@ urlpatterns = [
|
||||
# API路由
|
||||
path('api/', include('user_management.urls')),
|
||||
|
||||
# 专用Gmail Webhook路由 - 直接匹配根路径
|
||||
path('api/user/gmail/webhook/', gmail_webhook, name='root_gmail_webhook'), # 修改为正确路径
|
||||
path('gmail/webhook/', gmail_webhook, name='alt_gmail_webhook'), # 添加备用路径
|
||||
|
||||
# 媒体文件服务
|
||||
*static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
|
||||
|
||||
|
1
temp_client_secret.json
Normal file
1
temp_client_secret.json
Normal file
@ -0,0 +1 @@
|
||||
{"installed": {"client_id": "240872828479-570luspoc31259l1faab6kmjmpcsa9n9.apps.googleusercontent.com", "project_id": "first-renderer-454910-c1", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_secret": "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", "redirect_uris": ["http://localhost"]}}
|
2187
user_management/gmail_integration.py
Normal file
2187
user_management/gmail_integration.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
||||
# 管理命令包
|
@ -0,0 +1 @@
|
||||
# Gmail管理命令
|
@ -0,0 +1,80 @@
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from user_management.models import GmailCredential, User
|
||||
from user_management.gmail_integration import GmailIntegration
|
||||
import logging
|
||||
import pickle
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '更新Gmail凭证中的邮箱信息'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--email', type=str, help='指定用户邮箱')
|
||||
parser.add_argument('--gmail', type=str, help='指定要设置的Gmail邮箱')
|
||||
parser.add_argument('--all', action='store_true', help='更新所有凭证')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
email = options.get('email')
|
||||
gmail = options.get('gmail')
|
||||
update_all = options.get('all')
|
||||
|
||||
if update_all:
|
||||
# 更新所有凭证
|
||||
credentials = GmailCredential.objects.filter(is_active=True)
|
||||
self.stdout.write(f"找到 {credentials.count()} 个活跃的Gmail凭证")
|
||||
|
||||
for credential in credentials:
|
||||
self._update_credential(credential, gmail)
|
||||
elif email:
|
||||
# 更新指定用户的凭证
|
||||
try:
|
||||
user = User.objects.get(email=email)
|
||||
credentials = GmailCredential.objects.filter(user=user, is_active=True)
|
||||
|
||||
if not credentials.exists():
|
||||
raise CommandError(f"未找到用户 {email} 的Gmail凭证")
|
||||
|
||||
for credential in credentials:
|
||||
self._update_credential(credential, gmail)
|
||||
except User.DoesNotExist:
|
||||
raise CommandError(f"未找到用户 {email}")
|
||||
else:
|
||||
self.stdout.write("请提供--email参数或--all参数")
|
||||
|
||||
def _update_credential(self, credential, gmail=None):
|
||||
"""更新单个凭证"""
|
||||
user = credential.user
|
||||
self.stdout.write(f"正在更新用户 {user.email} 的Gmail凭证...")
|
||||
|
||||
if gmail:
|
||||
# 如果指定了Gmail邮箱,直接使用
|
||||
credential.gmail_email = gmail
|
||||
credential.save()
|
||||
self.stdout.write(self.style.SUCCESS(f"已手动设置Gmail邮箱为: {gmail}"))
|
||||
return
|
||||
|
||||
# 尝试使用API获取Gmail邮箱
|
||||
try:
|
||||
# 从凭证数据中恢复服务
|
||||
creds = pickle.loads(credential.credentials)
|
||||
|
||||
if creds and not creds.invalid:
|
||||
# 创建Gmail集成实例
|
||||
integration = GmailIntegration(user=user)
|
||||
integration.credentials = creds
|
||||
|
||||
# 尝试调用API获取用户资料
|
||||
profile = integration.gmail_service.users().getProfile(userId='me').execute()
|
||||
gmail_email = profile.get('emailAddress')
|
||||
|
||||
if gmail_email:
|
||||
credential.gmail_email = gmail_email
|
||||
credential.save()
|
||||
self.stdout.write(self.style.SUCCESS(f"已更新Gmail邮箱为: {gmail_email}"))
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING("无法从API获取Gmail邮箱"))
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR("凭证无效,请重新授权"))
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.ERROR(f"更新失败: {str(e)}"))
|
@ -0,0 +1,71 @@
|
||||
# Generated by Django 5.1.5 on 2025-04-09 15:16
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('user_management', '0004_knowledgebasedocument'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='GmailAttachment',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('gmail_message_id', models.CharField(max_length=100, verbose_name='Gmail消息ID')),
|
||||
('filename', models.CharField(max_length=255, verbose_name='文件名')),
|
||||
('filepath', models.CharField(max_length=500, verbose_name='文件路径')),
|
||||
('mimetype', models.CharField(max_length=100, verbose_name='MIME类型')),
|
||||
('filesize', models.IntegerField(default=0, verbose_name='文件大小')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('chat_message', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='gmail_attachments', to='user_management.chathistory')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Gmail附件',
|
||||
'verbose_name_plural': 'Gmail附件',
|
||||
'db_table': 'gmail_attachments',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GmailCredential',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('token_path', models.CharField(max_length=255, verbose_name='Token存储路径')),
|
||||
('last_history_id', models.CharField(blank=True, max_length=100, null=True, verbose_name='上次同步历史ID')),
|
||||
('watch_expiration', models.DateTimeField(blank=True, null=True, verbose_name='监听过期时间')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='是否激活')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='gmail_credentials', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Gmail认证凭据',
|
||||
'verbose_name_plural': 'Gmail认证凭据',
|
||||
'db_table': 'gmail_credentials',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GmailTalentMapping',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('talent_email', models.EmailField(max_length=254, verbose_name='达人邮箱')),
|
||||
('conversation_id', models.CharField(max_length=100, verbose_name='对话ID')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='是否激活')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('knowledge_base', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='gmail_mappings', to='user_management.knowledgebase')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='gmail_talent_mappings', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Gmail达人映射',
|
||||
'verbose_name_plural': 'Gmail达人映射',
|
||||
'db_table': 'gmail_talent_mappings',
|
||||
'unique_together': {('user', 'talent_email')},
|
||||
},
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.5 on 2025-04-09 16:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('user_management', '0005_gmailattachment_gmailcredential_gmailtalentmapping'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='gmailcredential',
|
||||
name='credentials',
|
||||
field=models.BinaryField(blank=True, null=True, verbose_name='序列化的凭证数据'),
|
||||
),
|
||||
]
|
@ -0,0 +1,57 @@
|
||||
# Generated by Django 5.1.5 on 2025-04-10 09:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('user_management', '0006_gmailcredential_credentials'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='gmailcredential',
|
||||
options={},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gmailcredential',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gmailcredential',
|
||||
name='credentials',
|
||||
field=models.BinaryField(default=b''),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gmailcredential',
|
||||
name='is_active',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gmailcredential',
|
||||
name='last_history_id',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gmailcredential',
|
||||
name='token_path',
|
||||
field=models.CharField(max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gmailcredential',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gmailcredential',
|
||||
name='watch_expiration',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
migrations.AlterModelTable(
|
||||
name='gmailcredential',
|
||||
table='gmail_credential',
|
||||
),
|
||||
]
|
@ -707,3 +707,61 @@ class KnowledgeBaseDocument(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.knowledge_base.name} - {self.document_name}"
|
||||
|
||||
class GmailCredential(models.Model):
|
||||
"""Gmail认证信息"""
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='gmail_credentials')
|
||||
gmail_email = models.EmailField(max_length=255, null=True, blank=True, help_text="实际授权的Gmail账号,可能与user.email不同")
|
||||
credentials = models.BinaryField() # 序列化的凭证对象
|
||||
token_path = models.CharField(max_length=255) # token存储路径
|
||||
last_history_id = models.CharField(max_length=255, null=True, blank=True) # 最后处理的historyId
|
||||
watch_expiration = models.DateTimeField(null=True, blank=True) # 监听过期时间
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
db_table = 'gmail_credential'
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user.username}的Gmail认证"
|
||||
|
||||
class GmailTalentMapping(models.Model):
|
||||
"""Gmail达人映射关系模型"""
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='gmail_talent_mappings')
|
||||
talent_email = models.EmailField(verbose_name='达人邮箱')
|
||||
knowledge_base = models.ForeignKey(KnowledgeBase, on_delete=models.CASCADE, related_name='gmail_mappings')
|
||||
conversation_id = models.CharField(max_length=100, verbose_name='对话ID')
|
||||
is_active = models.BooleanField(default=True, verbose_name='是否激活')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'gmail_talent_mappings'
|
||||
unique_together = ['user', 'talent_email']
|
||||
verbose_name = 'Gmail达人映射'
|
||||
verbose_name_plural = 'Gmail达人映射'
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user.username} - {self.talent_email}"
|
||||
|
||||
class GmailAttachment(models.Model):
|
||||
"""Gmail附件模型"""
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
chat_message = models.ForeignKey(ChatHistory, on_delete=models.CASCADE, related_name='gmail_attachments')
|
||||
gmail_message_id = models.CharField(max_length=100, verbose_name='Gmail消息ID')
|
||||
filename = models.CharField(max_length=255, verbose_name='文件名')
|
||||
filepath = models.CharField(max_length=500, verbose_name='文件路径')
|
||||
mimetype = models.CharField(max_length=100, verbose_name='MIME类型')
|
||||
filesize = models.IntegerField(default=0, verbose_name='文件大小')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'gmail_attachments'
|
||||
verbose_name = 'Gmail附件'
|
||||
verbose_name_plural = 'Gmail附件'
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.filename} ({self.gmail_message_id})"
|
||||
|
@ -13,7 +13,19 @@ from .views import (
|
||||
RegisterView,
|
||||
LoginView,
|
||||
LogoutView,
|
||||
ChatHistoryViewSet
|
||||
ChatHistoryViewSet,
|
||||
user_profile,
|
||||
user_register,
|
||||
setup_gmail_integration,
|
||||
send_gmail_message,
|
||||
gmail_webhook,
|
||||
get_gmail_attachments,
|
||||
download_gmail_attachment,
|
||||
get_gmail_talents,
|
||||
refresh_gmail_watch,
|
||||
check_gmail_auth,
|
||||
import_gmail_from_sender,
|
||||
sync_talent_emails
|
||||
)
|
||||
|
||||
# 创建路由器
|
||||
@ -42,4 +54,16 @@ urlpatterns = [
|
||||
path('users/<str:pk>/', user_detail, name='user-detail'),
|
||||
path('users/<str:pk>/update/', user_update, name='user-update'),
|
||||
path('users/<str:pk>/delete/', user_delete, name='user-delete'),
|
||||
|
||||
# Gmail集成API
|
||||
path('gmail/setup/', setup_gmail_integration, name='setup_gmail_integration'),
|
||||
path('gmail/send/', send_gmail_message, name='send_gmail_message'),
|
||||
path('gmail/webhook/', gmail_webhook, name='gmail_webhook'),
|
||||
path('gmail/attachments/', get_gmail_attachments, name='get_gmail_attachments'),
|
||||
path('gmail/download/', download_gmail_attachment, name='download_gmail_attachment'),
|
||||
path('gmail/talents/', get_gmail_talents, name='get_gmail_talents'),
|
||||
path('gmail/refresh-watch/', refresh_gmail_watch, name='refresh_gmail_watch'),
|
||||
path('gmail/check-auth/', check_gmail_auth, name='check_gmail_auth'),
|
||||
path('gmail/import-from-sender/', import_gmail_from_sender, name='import_gmail_from_sender'),
|
||||
path('gmail/sync-talent/', sync_talent_emails, name='sync_talent_emails'),
|
||||
]
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user