ErisPulse 1.1.13__tar.gz → 1.1.14.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- erispulse-1.1.14.dev1/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
- erispulse-1.1.14.dev1/.github/ISSUE_TEMPLATE/feature_request.md +42 -0
- erispulse-1.1.14.dev1/.github/assets/erispulse_logo.png +0 -0
- erispulse-1.1.14.dev1/.github/config/notify.json +11 -0
- erispulse-1.1.14.dev1/.github/tools/update-api-docs.py +76 -0
- erispulse-1.1.14.dev1/.github/workflows/notifications.yml +80 -0
- erispulse-1.1.14.dev1/.github/workflows/pypi-publish.yml +114 -0
- erispulse-1.1.14.dev1/.github/workflows/send-email/action.yml +41 -0
- erispulse-1.1.14.dev1/.github/workflows/send-email/send_mail.py +64 -0
- erispulse-1.1.14.dev1/.github/workflows/send-email/send_mail_old.py +29 -0
- erispulse-1.1.14.dev1/.gitignore +199 -0
- erispulse-1.1.14.dev1/.python-version +1 -0
- erispulse-1.1.14.dev1/PKG-INFO +165 -0
- erispulse-1.1.14.dev1/README.md +122 -0
- erispulse-1.1.14.dev1/devs/test.py +425 -0
- erispulse-1.1.14.dev1/devs/test_adapter.py +133 -0
- erispulse-1.1.14.dev1/devs/test_files/test.docx +0 -0
- erispulse-1.1.14.dev1/devs/test_files/test.jpg +0 -0
- erispulse-1.1.14.dev1/devs/test_files/test.mp4 +0 -0
- erispulse-1.1.14.dev1/docs/CHANGELOG.md +327 -0
- erispulse-1.1.14.dev1/docs/CLI.md +49 -0
- erispulse-1.1.14.dev1/docs/DEVELOPMENT.md +402 -0
- erispulse-1.1.14.dev1/docs/ORIGIN.md +82 -0
- erispulse-1.1.14.dev1/docs/REFERENCE.md +343 -0
- erispulse-1.1.14.dev1/docs/quick-start.md +153 -0
- {erispulse-1.1.13 → erispulse-1.1.14.dev1}/pyproject.toml +18 -13
- {erispulse-1.1.13 → erispulse-1.1.14.dev1/src}/ErisPulse/__init__.py +40 -2
- {erispulse-1.1.13 → erispulse-1.1.14.dev1/src}/ErisPulse/__main__.py +45 -1
- {erispulse-1.1.13 → erispulse-1.1.14.dev1/src}/ErisPulse/adapter.py +47 -0
- erispulse-1.1.14.dev1/src/ErisPulse/db.py +411 -0
- {erispulse-1.1.13 → erispulse-1.1.14.dev1/src}/ErisPulse/logger.py +47 -1
- {erispulse-1.1.13 → erispulse-1.1.14.dev1/src}/ErisPulse/mods.py +38 -0
- {erispulse-1.1.13 → erispulse-1.1.14.dev1/src}/ErisPulse/raiserr.py +36 -1
- {erispulse-1.1.13 → erispulse-1.1.14.dev1/src}/ErisPulse/util.py +45 -1
- erispulse-1.1.13/ErisPulse/db.py +0 -130
- erispulse-1.1.13/ErisPulse.egg-info/PKG-INFO +0 -79
- erispulse-1.1.13/ErisPulse.egg-info/SOURCES.txt +0 -17
- erispulse-1.1.13/ErisPulse.egg-info/dependency_links.txt +0 -1
- erispulse-1.1.13/ErisPulse.egg-info/entry_points.txt +0 -5
- erispulse-1.1.13/ErisPulse.egg-info/requires.txt +0 -2
- erispulse-1.1.13/ErisPulse.egg-info/top_level.txt +0 -1
- erispulse-1.1.13/PKG-INFO +0 -79
- erispulse-1.1.13/README.md +0 -54
- erispulse-1.1.13/setup.cfg +0 -4
- {erispulse-1.1.13 → erispulse-1.1.14.dev1}/LICENSE +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: 提升问题反馈质量
|
|
4
|
+
title: ''
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: runoneall, wsu2059q
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## SDK 版本
|
|
11
|
+
SDK Vision: [ ]
|
|
12
|
+
|
|
13
|
+
## 附加信息
|
|
14
|
+
- Python 版本:
|
|
15
|
+
- 操作系统:
|
|
16
|
+
- 是否使用虚拟环境?[是 / 否]
|
|
17
|
+
- 其他相关依赖版本:
|
|
18
|
+
|
|
19
|
+
## 描述问题
|
|
20
|
+
请简要描述你遇到的问题
|
|
21
|
+
|
|
22
|
+
## 复现步骤
|
|
23
|
+
1.
|
|
24
|
+
2.
|
|
25
|
+
3.
|
|
26
|
+
|
|
27
|
+
## 预期行为
|
|
28
|
+
期望的结果是什么?
|
|
29
|
+
|
|
30
|
+
## 实际行为
|
|
31
|
+
实际发生了什么?是否抛出错误?如果有,请粘贴完整 traceback:
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## 日志输出
|
|
35
|
+
(可选)请提供关键日志输出或截图:
|
|
36
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: 提出你的完美想法
|
|
4
|
+
title: ''
|
|
5
|
+
labels: ''
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
## 功能名称
|
|
10
|
+
请填写你想建议的新功能名称
|
|
11
|
+
|
|
12
|
+
## 功能描述
|
|
13
|
+
请详细描述你希望新增的功能及其用途
|
|
14
|
+
|
|
15
|
+
## 使用场景
|
|
16
|
+
请说明你打算在哪些场景下使用这个功能
|
|
17
|
+
|
|
18
|
+
## 当前限制
|
|
19
|
+
目前是否有替代方案?为什么它不能满足你的需求?
|
|
20
|
+
|
|
21
|
+
## 建议实现方式
|
|
22
|
+
(可选)你希望如何实现这个功能?可以包括接口设计、调用方式等
|
|
23
|
+
|
|
24
|
+
## 影响范围
|
|
25
|
+
这个功能是否会影响现有模块?例如性能、兼容性、API 变更等
|
|
26
|
+
|
|
27
|
+
## 示例代码
|
|
28
|
+
(可选)如果适用,请提供示例调用方式或伪代码:
|
|
29
|
+
```python
|
|
30
|
+
# 示例代码
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### 文件位置结构建议
|
|
36
|
+
```
|
|
37
|
+
.github/
|
|
38
|
+
└── ISSUE_TEMPLATE/
|
|
39
|
+
├── bug_report.md
|
|
40
|
+
└── feature_request.md
|
|
41
|
+
```
|
|
42
|
+
|
|
Binary file
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
def extract_module_docs(file_path):
|
|
6
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
7
|
+
content = f.read()
|
|
8
|
+
|
|
9
|
+
match = re.search(r'^\"\"\"(.*?)\"\"\"', content, re.DOTALL)
|
|
10
|
+
if not match:
|
|
11
|
+
return None
|
|
12
|
+
|
|
13
|
+
docstring = match.group(1).strip()
|
|
14
|
+
return docstring
|
|
15
|
+
|
|
16
|
+
def update_reference_docs(module_name, docs, reference_path, module_path):
|
|
17
|
+
if not docs:
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
reference_path.parent.mkdir(exist_ok=True)
|
|
21
|
+
|
|
22
|
+
if not reference_path.exists():
|
|
23
|
+
with open(reference_path, 'w', encoding='utf-8') as f:
|
|
24
|
+
f.write("# API Reference Documentation\n\n")
|
|
25
|
+
|
|
26
|
+
with open(reference_path, 'r', encoding='utf-8') as f:
|
|
27
|
+
content = f.read()
|
|
28
|
+
|
|
29
|
+
github_base_url = "https://raw.githubusercontent.com/ErisPulse/ErisPulse/refs/heads/main/"
|
|
30
|
+
github_source_url = github_base_url + module_path.replace('\\', '/')
|
|
31
|
+
|
|
32
|
+
section_header = f"## {module_name} (source: [{module_path}]({github_source_url}))"
|
|
33
|
+
|
|
34
|
+
section_pattern = re.escape(section_header)
|
|
35
|
+
match = re.search(section_pattern, content, re.IGNORECASE)
|
|
36
|
+
|
|
37
|
+
if match:
|
|
38
|
+
section_start = match.start()
|
|
39
|
+
next_section = re.search(r'## ', content[section_start + 1:])
|
|
40
|
+
section_end = section_start + next_section.start() if next_section else len(content)
|
|
41
|
+
|
|
42
|
+
if docs.strip() in content[section_start:section_end]:
|
|
43
|
+
print(f"Docs for {module_name} already up to date")
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
updated_content = (
|
|
47
|
+
content[:section_start] +
|
|
48
|
+
section_header + '\n\n' +
|
|
49
|
+
docs + '\n\n' +
|
|
50
|
+
content[section_end:]
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
updated_content = content + section_header + '\n\n' + docs + '\n\n'
|
|
54
|
+
|
|
55
|
+
with open(reference_path, 'w', encoding='utf-8') as f:
|
|
56
|
+
f.write(updated_content)
|
|
57
|
+
print(f"Updated docs for {module_name} in REFERENCE.md")
|
|
58
|
+
|
|
59
|
+
def main():
|
|
60
|
+
module_dir = Path('ErisPulse')
|
|
61
|
+
reference_path = Path('docs/REFERENCE.md')
|
|
62
|
+
|
|
63
|
+
modules = ['__init__', '__main__', 'adapter', 'db', 'logger', 'mods', 'raiserr', 'util']
|
|
64
|
+
|
|
65
|
+
for module in modules:
|
|
66
|
+
py_file = module_dir / f'{module}.py'
|
|
67
|
+
if not py_file.exists():
|
|
68
|
+
print(f"Warning: {py_file} not found")
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
docs = extract_module_docs(py_file)
|
|
72
|
+
if docs:
|
|
73
|
+
update_reference_docs(module, docs, reference_path, f"ErisPulse/{module}.py")
|
|
74
|
+
|
|
75
|
+
if __name__ == '__main__':
|
|
76
|
+
main()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: 🎀 艾莉丝的统一通知服务 ~
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [opened]
|
|
6
|
+
pull_request:
|
|
7
|
+
types: [opened]
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
notify:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: 检出代码
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: 获取事件信息
|
|
19
|
+
id: event_info
|
|
20
|
+
run: |
|
|
21
|
+
if [ "${{ github.event_name }}" = "issues" ]; then
|
|
22
|
+
title="${{ github.event.issue.title }}"
|
|
23
|
+
body="${{ github.event.issue.body }}"
|
|
24
|
+
url="${{ github.event.issue.html_url }}"
|
|
25
|
+
type="issue"
|
|
26
|
+
elif [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
27
|
+
title="${{ github.event.pull_request.title }}"
|
|
28
|
+
body="${{ github.event.pull_request.body }}"
|
|
29
|
+
url="${{ github.event.pull_request.html_url }}"
|
|
30
|
+
type="PR"
|
|
31
|
+
else
|
|
32
|
+
title="${{ github.event.pusher.name }} 提交到 ${{ github.ref_name }}"
|
|
33
|
+
body=$(git log -1 --pretty=%B)
|
|
34
|
+
url="https://github.com/${{ github.repository }}/compare/${{ github.event.before }}...${{ github.event.after }}?expand=1"
|
|
35
|
+
type="push"
|
|
36
|
+
fi
|
|
37
|
+
echo "title=$title" >> $GITHUB_OUTPUT
|
|
38
|
+
echo "body=$body" >> $GITHUB_OUTPUT
|
|
39
|
+
echo "url=$url" >> $GITHUB_OUTPUT
|
|
40
|
+
echo "type=$type" >> $GITHUB_OUTPUT
|
|
41
|
+
|
|
42
|
+
- name: 读取收件人列表
|
|
43
|
+
id: get_emails
|
|
44
|
+
run: |
|
|
45
|
+
emails=$(cat .github/config/notify.json | jq -c '.emails')
|
|
46
|
+
echo "emails=$emails" >> $GITHUB_OUTPUT
|
|
47
|
+
|
|
48
|
+
- name: 发送邮件通知
|
|
49
|
+
uses: ./.github/workflows/send-email
|
|
50
|
+
with:
|
|
51
|
+
subject: |
|
|
52
|
+
${{
|
|
53
|
+
github.event_name == 'issues' && '[艾莉丝快报] 有新issue被创建!' ||
|
|
54
|
+
github.event_name == 'pull_request' && '[艾莉丝快报] 有新PR被创建!' ||
|
|
55
|
+
'[艾莉丝快报] 开发分支有更新'
|
|
56
|
+
}}
|
|
57
|
+
content: |
|
|
58
|
+
<div style="font-family: 'Segoe UI', sans-serif; background-color: #2a2a3c; color: #f5f5f5; padding: 20px; border-radius: 8px;">
|
|
59
|
+
<h2 style="color: #ff6ec7;">
|
|
60
|
+
${{
|
|
61
|
+
github.event_name == 'issues' && '新Issue' ||
|
|
62
|
+
github.event_name == 'pull_request' && '新PR' ||
|
|
63
|
+
'开发分支更新'
|
|
64
|
+
}}: ${{ steps.event_info.outputs.title }}
|
|
65
|
+
</h2>
|
|
66
|
+
<p><strong>提交者:</strong> ${{
|
|
67
|
+
github.event_name == 'push' && github.event.pusher.name ||
|
|
68
|
+
github.event.issue.user.login ||
|
|
69
|
+
github.event.pull_request.user.login
|
|
70
|
+
}}</p>
|
|
71
|
+
<p><strong>内容:</strong></p>
|
|
72
|
+
<pre style="background-color: #3b3b4f; padding: 10px; border-radius: 4px; white-space: pre-wrap;">${{ steps.event_info.outputs.body }}</pre>
|
|
73
|
+
<p><strong>地址:</strong><br/>
|
|
74
|
+
<a href="${{ steps.event_info.outputs.url }}" style="color: #7acbf7; text-decoration: none;">
|
|
75
|
+
查看详情
|
|
76
|
+
</a></p>
|
|
77
|
+
</div>
|
|
78
|
+
recipients: ${{ steps.get_emails.outputs.emails }}
|
|
79
|
+
resend_api_key: ${{ secrets.RESEND_API_KEY }}
|
|
80
|
+
feishu_smtp_password: ${{ secrets.FEISHU_SMTP_PASSWORD }}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
name: 🎀 艾莉丝的PyPI包裹快递服务 ~
|
|
2
|
+
|
|
3
|
+
# 艾莉丝今天也要努力发包呢!
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch: {} # 手动触发
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
release-build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: 检出代码
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
|
|
20
|
+
- name: 版本检查
|
|
21
|
+
id: version
|
|
22
|
+
run: |
|
|
23
|
+
CURRENT_TAG=${GITHUB_REF#refs/tags/}
|
|
24
|
+
echo "✨ 当前版本: $CURRENT_TAG"
|
|
25
|
+
|
|
26
|
+
if [[ $CURRENT_TAG =~ \.dev|\.alpha|\.beta ]]; then
|
|
27
|
+
BASE_VERSION=$(echo $CURRENT_TAG | sed -E 's/([0-9]+)$//')
|
|
28
|
+
PREV_TAG=$(git tag --sort=-v:refname | grep "^$BASE_VERSION" | grep -v "$CURRENT_TAG" | head -n 1)
|
|
29
|
+
|
|
30
|
+
if [ -z "$PREV_TAG" ]; then
|
|
31
|
+
echo "💢 找不到上一个版本!"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
echo "🎀 发现上一个试验版本: $PREV_TAG"
|
|
35
|
+
git checkout $PREV_TAG
|
|
36
|
+
echo "use_current=false" >> $GITHUB_OUTPUT
|
|
37
|
+
else
|
|
38
|
+
echo "🌟 正式版本~"
|
|
39
|
+
echo "use_current=true" >> $GITHUB_OUTPUT
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
- name: 环境准备
|
|
43
|
+
uses: actions/setup-python@v5
|
|
44
|
+
with:
|
|
45
|
+
python-version: "3.x"
|
|
46
|
+
|
|
47
|
+
- name: 打包构建
|
|
48
|
+
run: |
|
|
49
|
+
python -m pip install --upgrade pip
|
|
50
|
+
python -m pip install build
|
|
51
|
+
python -m build
|
|
52
|
+
|
|
53
|
+
- name: 暂存包裹
|
|
54
|
+
uses: actions/upload-artifact@v4
|
|
55
|
+
with:
|
|
56
|
+
name: release-dists
|
|
57
|
+
path: dist/
|
|
58
|
+
|
|
59
|
+
pypi-publish:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
needs: [release-build]
|
|
62
|
+
permissions:
|
|
63
|
+
id-token: write
|
|
64
|
+
|
|
65
|
+
environment:
|
|
66
|
+
name: pypi
|
|
67
|
+
url: https://pypi.org/p/erispulse
|
|
68
|
+
|
|
69
|
+
steps:
|
|
70
|
+
# 👇 新增这一步:检出代码,使 send-email 可用
|
|
71
|
+
- name: 检出代码
|
|
72
|
+
uses: actions/checkout@v4
|
|
73
|
+
|
|
74
|
+
- name: 获取包裹
|
|
75
|
+
uses: actions/download-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: release-dists
|
|
78
|
+
path: dist/
|
|
79
|
+
|
|
80
|
+
- name: 发布上传
|
|
81
|
+
id: publish
|
|
82
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
83
|
+
with:
|
|
84
|
+
packages-dir: dist/
|
|
85
|
+
verbose: true
|
|
86
|
+
|
|
87
|
+
- name: 读取收件人列表
|
|
88
|
+
id: get_emails
|
|
89
|
+
run: |
|
|
90
|
+
emails=$(cat .github/config/notify.json | jq -c '.emails')
|
|
91
|
+
echo "emails=$emails" >> $GITHUB_OUTPUT
|
|
92
|
+
|
|
93
|
+
- name: 发送邮件通知
|
|
94
|
+
uses: ./.github/workflows/send-email
|
|
95
|
+
with:
|
|
96
|
+
subject: "[艾莉丝快报] 新版本 ${{ steps.version.outputs.CURRENT_TAG }} 已发布至 PyPI!✨"
|
|
97
|
+
content: |
|
|
98
|
+
🎀 您好主人~
|
|
99
|
+
|
|
100
|
+
我是艾莉丝,负责为您播报最新消息!
|
|
101
|
+
今天我们的项目又更新啦~快去看看吧!
|
|
102
|
+
|
|
103
|
+
🔖 版本号:${{ steps.version.outputs.CURRENT_TAG }}
|
|
104
|
+
📦 包地址:
|
|
105
|
+
https://pypi.org/project/erispulse/${{ steps.version.outputs.CURRENT_TAG }}
|
|
106
|
+
|
|
107
|
+
📝 更新内容:
|
|
108
|
+
${{ steps.version.outputs.CURRENT_TAG }}
|
|
109
|
+
|
|
110
|
+
祝您每天都有好心情~ 💖
|
|
111
|
+
—— 艾莉丝
|
|
112
|
+
recipients: ${{ steps.get_emails.outputs.emails }}
|
|
113
|
+
resend_api_key: ${{ secrets.RESEND_API_KEY }}
|
|
114
|
+
feishu_smtp_password: ${{ secrets.FEISHU_SMTP_PASSWORD }}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: "🎀 艾莉丝的魔法邮件服务 ~"
|
|
2
|
+
description: "通过 Resend 发送魔法电子邮件通知"
|
|
3
|
+
inputs:
|
|
4
|
+
subject:
|
|
5
|
+
type: string
|
|
6
|
+
default: "[艾莉丝快报] 默认主题"
|
|
7
|
+
description: "邮件主题"
|
|
8
|
+
content:
|
|
9
|
+
type: string
|
|
10
|
+
required: true
|
|
11
|
+
description: "邮件正文内容"
|
|
12
|
+
recipients:
|
|
13
|
+
type: json
|
|
14
|
+
required: true
|
|
15
|
+
description: "收件人列表 JSON 数组"
|
|
16
|
+
resend_api_key:
|
|
17
|
+
type: string
|
|
18
|
+
required: true
|
|
19
|
+
description: "Resend API 密钥"
|
|
20
|
+
feishu_smtp_password:
|
|
21
|
+
type: string
|
|
22
|
+
required: true
|
|
23
|
+
description: "飞书 SMTP 密码"
|
|
24
|
+
|
|
25
|
+
runs:
|
|
26
|
+
using: "composite"
|
|
27
|
+
steps:
|
|
28
|
+
- name: 安装依赖
|
|
29
|
+
run: python -m pip install requests
|
|
30
|
+
shell: bash
|
|
31
|
+
|
|
32
|
+
- name: 发送邮件
|
|
33
|
+
env:
|
|
34
|
+
RECIPIENTS_JSON: ${{ inputs.recipients }}
|
|
35
|
+
SUBJECT: ${{ inputs.subject }}
|
|
36
|
+
CONTENT: ${{ inputs.content }}
|
|
37
|
+
RESEND_API_KEY: ${{ inputs.resend_api_key }}
|
|
38
|
+
FEISHU_SMTP_PASSWORD: ${{ inputs.feishu_smtp_password }}
|
|
39
|
+
run: |
|
|
40
|
+
python ${{ github.action_path }}/send_mail.py
|
|
41
|
+
shell: bash
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import smtplib
|
|
4
|
+
from email.mime.text import MIMEText
|
|
5
|
+
from email.mime.multipart import MIMEMultipart
|
|
6
|
+
from email.header import Header
|
|
7
|
+
from email.utils import formataddr, formatdate
|
|
8
|
+
|
|
9
|
+
# 邮箱配置信息
|
|
10
|
+
EMAIL_HOST = "smtp.feishu.cn" # SMTP服务器地址
|
|
11
|
+
EMAIL_PORT_SSL = 465 # SSL加密端口
|
|
12
|
+
USERNAME = "noreply@ns1.loc.cc" # 邮箱地址
|
|
13
|
+
PASSWORD = os.getenv("FEISHU_SMTP_PASSWORD") # IMAP/SMTP密码
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def send_email(
|
|
17
|
+
subject, content, receivers, content_type="html", sender_name="ErisPulse通知"
|
|
18
|
+
):
|
|
19
|
+
"""
|
|
20
|
+
发送邮件到多个收件人,支持HTML格式
|
|
21
|
+
|
|
22
|
+
参数:
|
|
23
|
+
:param subject: 邮件主题
|
|
24
|
+
:param content: 邮件内容(HTML或纯文本)
|
|
25
|
+
:param receivers: 收件人列表,如 ["user1@example.com", "user2@example.com"]
|
|
26
|
+
:param content_type: 邮件类型,"html" 或 "plain"
|
|
27
|
+
:param sender_name: 发件人显示名称
|
|
28
|
+
"""
|
|
29
|
+
# 创建邮件对象
|
|
30
|
+
msg = MIMEMultipart()
|
|
31
|
+
msg["Subject"] = Header(subject, "utf-8")
|
|
32
|
+
msg["From"] = formataddr((str(Header(sender_name, "utf-8")), USERNAME))
|
|
33
|
+
msg["To"] = ", ".join(receivers) # 多个收件人用逗号分隔
|
|
34
|
+
msg["Date"] = formatdate(localtime=True)
|
|
35
|
+
|
|
36
|
+
# 添加邮件正文
|
|
37
|
+
if content_type == "html":
|
|
38
|
+
body = MIMEText(content, "html", "utf-8")
|
|
39
|
+
else:
|
|
40
|
+
body = MIMEText(content, "plain", "utf-8")
|
|
41
|
+
msg.attach(body)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
# 创建SSL加密连接
|
|
45
|
+
with smtplib.SMTP_SSL(EMAIL_HOST, EMAIL_PORT_SSL) as server:
|
|
46
|
+
server.login(USERNAME, PASSWORD)
|
|
47
|
+
server.sendmail(USERNAME, receivers, msg.as_string())
|
|
48
|
+
print(f"邮件成功发送给 {len(receivers)} 位收件人")
|
|
49
|
+
return True
|
|
50
|
+
except Exception as e:
|
|
51
|
+
print(f"邮件发送失败: {str(e)}")
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if __name__ == "__main__":
|
|
56
|
+
receivers = json.loads(os.getenv("RECIPIENTS_JSON"))
|
|
57
|
+
print(f"收件人列表: {receivers}")
|
|
58
|
+
|
|
59
|
+
send_email(
|
|
60
|
+
subject=os.getenv("SUBJECT"),
|
|
61
|
+
content=os.getenv("CONTENT"),
|
|
62
|
+
receivers=receivers,
|
|
63
|
+
content_type="html",
|
|
64
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
recipients = json.loads(os.getenv("RECIPIENTS_JSON"))
|
|
6
|
+
resend_api_key = os.getenv("RESEND_API_KEY")
|
|
7
|
+
feishu_smtp_password = os.getenv("FEISHU_SMTP_PASSWORD")
|
|
8
|
+
subject = os.getenv("SUBJECT")
|
|
9
|
+
content = os.getenv("CONTENT")
|
|
10
|
+
print(f"📬 收件人列表: {recipients}")
|
|
11
|
+
response = requests.post(
|
|
12
|
+
"https://api.resend.com/emails",
|
|
13
|
+
headers={
|
|
14
|
+
"Authorization": f"Bearer {resend_api_key}",
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
},
|
|
17
|
+
json={
|
|
18
|
+
"from": "ErisPulse <noreply@anran.xyz>",
|
|
19
|
+
"to": recipients,
|
|
20
|
+
"subject": subject,
|
|
21
|
+
"html": content,
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
if response.status_code == 200:
|
|
26
|
+
print("💌 魔法信件已成功寄出!")
|
|
27
|
+
else:
|
|
28
|
+
print(f"❌ 发送失败: {response.text}")
|
|
29
|
+
exit(1)
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# dev gnore ignore file for Python projects
|
|
2
|
+
env.py
|
|
3
|
+
uv.lock
|
|
4
|
+
snapshots/
|
|
5
|
+
config.db*
|
|
6
|
+
.website
|
|
7
|
+
|
|
8
|
+
# Byte-compiled / optimized / DLL files
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
*$py.class
|
|
12
|
+
|
|
13
|
+
# C extensions
|
|
14
|
+
*.so
|
|
15
|
+
|
|
16
|
+
# Distribution / packaging
|
|
17
|
+
.Python
|
|
18
|
+
.vscode/
|
|
19
|
+
build/
|
|
20
|
+
develop-eggs/
|
|
21
|
+
dist/
|
|
22
|
+
downloads/
|
|
23
|
+
eggs/
|
|
24
|
+
.eggs/
|
|
25
|
+
lib/
|
|
26
|
+
lib64/
|
|
27
|
+
parts/
|
|
28
|
+
sdist/
|
|
29
|
+
var/
|
|
30
|
+
wheels/
|
|
31
|
+
share/python-wheels/
|
|
32
|
+
*.egg-info/
|
|
33
|
+
.installed.cfg
|
|
34
|
+
*.egg
|
|
35
|
+
MANIFEST
|
|
36
|
+
|
|
37
|
+
# PyInstaller
|
|
38
|
+
# Usually these files are written by a python script from a template
|
|
39
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
40
|
+
*.manifest
|
|
41
|
+
*.spec
|
|
42
|
+
|
|
43
|
+
# Installer logs
|
|
44
|
+
pip-log.txt
|
|
45
|
+
pip-delete-this-directory.txt
|
|
46
|
+
|
|
47
|
+
# Unit test / coverage reports
|
|
48
|
+
htmlcov/
|
|
49
|
+
.tox/
|
|
50
|
+
.nox/
|
|
51
|
+
.coverage
|
|
52
|
+
.coverage.*
|
|
53
|
+
.cache
|
|
54
|
+
nosetests.xml
|
|
55
|
+
coverage.xml
|
|
56
|
+
*.cover
|
|
57
|
+
*.py,cover
|
|
58
|
+
.hypothesis/
|
|
59
|
+
.pytest_cache/
|
|
60
|
+
cover/
|
|
61
|
+
|
|
62
|
+
# Translations
|
|
63
|
+
*.mo
|
|
64
|
+
*.pot
|
|
65
|
+
|
|
66
|
+
# Django stuff:
|
|
67
|
+
*.log
|
|
68
|
+
local_settings.py
|
|
69
|
+
db.sqlite3
|
|
70
|
+
db.sqlite3-journal
|
|
71
|
+
|
|
72
|
+
# Flask stuff:
|
|
73
|
+
instance/
|
|
74
|
+
.webassets-cache
|
|
75
|
+
|
|
76
|
+
# Scrapy stuff:
|
|
77
|
+
.scrapy
|
|
78
|
+
|
|
79
|
+
# Sphinx documentation
|
|
80
|
+
docs/_build/
|
|
81
|
+
|
|
82
|
+
# PyBuilder
|
|
83
|
+
.pybuilder/
|
|
84
|
+
target/
|
|
85
|
+
|
|
86
|
+
# Jupyter Notebook
|
|
87
|
+
.ipynb_checkpoints
|
|
88
|
+
|
|
89
|
+
# IPython
|
|
90
|
+
profile_default/
|
|
91
|
+
ipython_config.py
|
|
92
|
+
|
|
93
|
+
# pyenv
|
|
94
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
95
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
96
|
+
# .python-version
|
|
97
|
+
|
|
98
|
+
# pipenv
|
|
99
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
100
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
101
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
102
|
+
# install all needed dependencies.
|
|
103
|
+
#Pipfile.lock
|
|
104
|
+
|
|
105
|
+
# UV
|
|
106
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
107
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
108
|
+
# commonly ignored for libraries.
|
|
109
|
+
#uv.lock
|
|
110
|
+
|
|
111
|
+
# poetry
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
113
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
114
|
+
# commonly ignored for libraries.
|
|
115
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
116
|
+
#poetry.lock
|
|
117
|
+
|
|
118
|
+
# pdm
|
|
119
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
120
|
+
#pdm.lock
|
|
121
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
122
|
+
# in version control.
|
|
123
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
124
|
+
.pdm.toml
|
|
125
|
+
.pdm-python
|
|
126
|
+
.pdm-build/
|
|
127
|
+
|
|
128
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
129
|
+
__pypackages__/
|
|
130
|
+
|
|
131
|
+
# Celery stuff
|
|
132
|
+
celerybeat-schedule
|
|
133
|
+
celerybeat.pid
|
|
134
|
+
|
|
135
|
+
# SageMath parsed files
|
|
136
|
+
*.sage.py
|
|
137
|
+
|
|
138
|
+
# Environments
|
|
139
|
+
.env
|
|
140
|
+
.venv
|
|
141
|
+
env/
|
|
142
|
+
venv/
|
|
143
|
+
ENV/
|
|
144
|
+
env.bak/
|
|
145
|
+
venv.bak/
|
|
146
|
+
|
|
147
|
+
# Spyder project settings
|
|
148
|
+
.spyderproject
|
|
149
|
+
.spyproject
|
|
150
|
+
|
|
151
|
+
# Rope project settings
|
|
152
|
+
.ropeproject
|
|
153
|
+
|
|
154
|
+
# mkdocs documentation
|
|
155
|
+
/site
|
|
156
|
+
|
|
157
|
+
# mypy
|
|
158
|
+
.mypy_cache/
|
|
159
|
+
.dmypy.json
|
|
160
|
+
dmypy.json
|
|
161
|
+
|
|
162
|
+
# Pyre type checker
|
|
163
|
+
.pyre/
|
|
164
|
+
|
|
165
|
+
# pytype static type analyzer
|
|
166
|
+
.pytype/
|
|
167
|
+
|
|
168
|
+
# Cython debug symbols
|
|
169
|
+
cython_debug/
|
|
170
|
+
|
|
171
|
+
# PyCharm
|
|
172
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
173
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
174
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
175
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
176
|
+
#.idea/
|
|
177
|
+
|
|
178
|
+
# Visual Studio Code
|
|
179
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
180
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
181
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
182
|
+
# you could uncomment the following to ignore the enitre vscode folder
|
|
183
|
+
# .vscode/
|
|
184
|
+
|
|
185
|
+
# Ruff stuff:
|
|
186
|
+
.ruff_cache/
|
|
187
|
+
|
|
188
|
+
# PyPI configuration file
|
|
189
|
+
.pypirc
|
|
190
|
+
|
|
191
|
+
# Cursor
|
|
192
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
193
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
194
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
195
|
+
.cursorignore
|
|
196
|
+
.cursorindexingignore
|
|
197
|
+
|
|
198
|
+
# osx
|
|
199
|
+
.DS_Store
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|