opp-radar 0.1.0__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.
- opp_radar-0.1.0/LICENSE +21 -0
- opp_radar-0.1.0/PKG-INFO +89 -0
- opp_radar-0.1.0/README.md +63 -0
- opp_radar-0.1.0/opp_radar/__init__.py +3 -0
- opp_radar-0.1.0/opp_radar/cli.py +44 -0
- opp_radar-0.1.0/opp_radar/publish.py +40 -0
- opp_radar-0.1.0/opp_radar/py.typed +0 -0
- opp_radar-0.1.0/opp_radar/render.py +49 -0
- opp_radar-0.1.0/opp_radar.egg-info/PKG-INFO +89 -0
- opp_radar-0.1.0/opp_radar.egg-info/SOURCES.txt +15 -0
- opp_radar-0.1.0/opp_radar.egg-info/dependency_links.txt +1 -0
- opp_radar-0.1.0/opp_radar.egg-info/entry_points.txt +2 -0
- opp_radar-0.1.0/opp_radar.egg-info/requires.txt +2 -0
- opp_radar-0.1.0/opp_radar.egg-info/top_level.txt +1 -0
- opp_radar-0.1.0/pyproject.toml +44 -0
- opp_radar-0.1.0/setup.cfg +4 -0
- opp_radar-0.1.0/tests/test_smoke.py +17 -0
opp_radar-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 程序员白大力
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
opp_radar-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opp-radar
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Opportunity radar for indie devs: render a WeChat-style article from Markdown and push it to your Official Account draft box.
|
|
5
|
+
Author: 程序员白大力
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/baiyigali/opp_radar
|
|
8
|
+
Project-URL: Repository, https://github.com/baiyigali/opp_radar
|
|
9
|
+
Keywords: wechat,wechat-mp,公众号,markdown,wechat-formatter,publish,indie-dev,opportunity
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Office/Business
|
|
19
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: wechat-formatter
|
|
24
|
+
Requires-Dist: wechat-publish
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# opp-radar
|
|
28
|
+
|
|
29
|
+
面向个人开发者 / 技术创业者的"机会雷达":从公开技术信号里筛出小团队能做的中小产品机会,自动写成公众号专栏,渲染排版后推到草稿箱(发表由人工在后台完成)。
|
|
30
|
+
|
|
31
|
+
专栏名:**开发者机会观察**(公众号:程序员白大力)。觉得有用欢迎关注。
|
|
32
|
+
|
|
33
|
+
## 目录结构
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
opp-radar/
|
|
37
|
+
opp_radar/ # 安装后的 Python 包(render / publish / cli)
|
|
38
|
+
render.py · publish.py · cli.py · __init__.py · py.typed
|
|
39
|
+
prompts/
|
|
40
|
+
dev.md # 开发者机会观察(dev 领域)完整提示词
|
|
41
|
+
# 以后加 marketing.md、xxx.md 等其他领域,复用 opp_radar/
|
|
42
|
+
scripts/
|
|
43
|
+
render.py # 薄壳:转发到 opp_radar render(兼容旧调用,无需安装)
|
|
44
|
+
publish.py # 薄壳:转发到 opp_radar publish
|
|
45
|
+
examples/
|
|
46
|
+
开发者机会观察-第5期.md/.png/.html # 一篇成稿样例
|
|
47
|
+
tests/ # 冒烟测试
|
|
48
|
+
.github/workflows/publish.yml # CI:构建 + TestPyPI/PyPI(OIDC 免密)
|
|
49
|
+
pyproject.toml # 打包配置,提供 `opp-radar` 命令
|
|
50
|
+
issues/ # 各期产出,gitignore,只在本地(用于去重)
|
|
51
|
+
config.json # 微信凭据,gitignore,不进版本库
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 跑一期
|
|
55
|
+
|
|
56
|
+
安装(开发模式):
|
|
57
|
+
```bash
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
然后:
|
|
61
|
+
1. 按 `prompts/dev.md` 的流程:找信号 → 反向验证 → 写 `.md` → 出 `.png`(都落在 `issues/`);
|
|
62
|
+
2. 渲染:`opp-radar render "开发者机会观察-第N期"`;
|
|
63
|
+
3. 推草稿:`opp-radar publish "开发者机会观察-第N期" "<标题>" "<摘要>"`,再去公众号后台草稿箱点"发表"。
|
|
64
|
+
|
|
65
|
+
> 不想安装时也可直接用薄壳脚本,效果一样:
|
|
66
|
+
> `python3 scripts/render.py "..."` / `python3 scripts/publish.py "..."`。
|
|
67
|
+
|
|
68
|
+
## 样例
|
|
69
|
+
|
|
70
|
+
看 `examples/开发者机会观察-第5期.html` 可直接在浏览器打开预览最终排版效果(科技风、经典蓝 #2563eb)。
|
|
71
|
+
|
|
72
|
+
## 依赖
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install -e .
|
|
76
|
+
# 或手动:pip install wechat-formatter wechat-publish
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 本地凭据
|
|
80
|
+
|
|
81
|
+
根目录 `config.json`:
|
|
82
|
+
```json
|
|
83
|
+
{ "appid": "...", "secret": "...", "author": "..." }
|
|
84
|
+
```
|
|
85
|
+
该文件已在 `.gitignore` 中,**不要提交到公开仓库**。
|
|
86
|
+
|
|
87
|
+
## 铁律
|
|
88
|
+
|
|
89
|
+
推草稿脚本只执行一次,无论返回成功/报错/超时都绝不重试,避免重复建草稿。
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# opp-radar
|
|
2
|
+
|
|
3
|
+
面向个人开发者 / 技术创业者的"机会雷达":从公开技术信号里筛出小团队能做的中小产品机会,自动写成公众号专栏,渲染排版后推到草稿箱(发表由人工在后台完成)。
|
|
4
|
+
|
|
5
|
+
专栏名:**开发者机会观察**(公众号:程序员白大力)。觉得有用欢迎关注。
|
|
6
|
+
|
|
7
|
+
## 目录结构
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
opp-radar/
|
|
11
|
+
opp_radar/ # 安装后的 Python 包(render / publish / cli)
|
|
12
|
+
render.py · publish.py · cli.py · __init__.py · py.typed
|
|
13
|
+
prompts/
|
|
14
|
+
dev.md # 开发者机会观察(dev 领域)完整提示词
|
|
15
|
+
# 以后加 marketing.md、xxx.md 等其他领域,复用 opp_radar/
|
|
16
|
+
scripts/
|
|
17
|
+
render.py # 薄壳:转发到 opp_radar render(兼容旧调用,无需安装)
|
|
18
|
+
publish.py # 薄壳:转发到 opp_radar publish
|
|
19
|
+
examples/
|
|
20
|
+
开发者机会观察-第5期.md/.png/.html # 一篇成稿样例
|
|
21
|
+
tests/ # 冒烟测试
|
|
22
|
+
.github/workflows/publish.yml # CI:构建 + TestPyPI/PyPI(OIDC 免密)
|
|
23
|
+
pyproject.toml # 打包配置,提供 `opp-radar` 命令
|
|
24
|
+
issues/ # 各期产出,gitignore,只在本地(用于去重)
|
|
25
|
+
config.json # 微信凭据,gitignore,不进版本库
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 跑一期
|
|
29
|
+
|
|
30
|
+
安装(开发模式):
|
|
31
|
+
```bash
|
|
32
|
+
pip install -e .
|
|
33
|
+
```
|
|
34
|
+
然后:
|
|
35
|
+
1. 按 `prompts/dev.md` 的流程:找信号 → 反向验证 → 写 `.md` → 出 `.png`(都落在 `issues/`);
|
|
36
|
+
2. 渲染:`opp-radar render "开发者机会观察-第N期"`;
|
|
37
|
+
3. 推草稿:`opp-radar publish "开发者机会观察-第N期" "<标题>" "<摘要>"`,再去公众号后台草稿箱点"发表"。
|
|
38
|
+
|
|
39
|
+
> 不想安装时也可直接用薄壳脚本,效果一样:
|
|
40
|
+
> `python3 scripts/render.py "..."` / `python3 scripts/publish.py "..."`。
|
|
41
|
+
|
|
42
|
+
## 样例
|
|
43
|
+
|
|
44
|
+
看 `examples/开发者机会观察-第5期.html` 可直接在浏览器打开预览最终排版效果(科技风、经典蓝 #2563eb)。
|
|
45
|
+
|
|
46
|
+
## 依赖
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install -e .
|
|
50
|
+
# 或手动:pip install wechat-formatter wechat-publish
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 本地凭据
|
|
54
|
+
|
|
55
|
+
根目录 `config.json`:
|
|
56
|
+
```json
|
|
57
|
+
{ "appid": "...", "secret": "...", "author": "..." }
|
|
58
|
+
```
|
|
59
|
+
该文件已在 `.gitignore` 中,**不要提交到公开仓库**。
|
|
60
|
+
|
|
61
|
+
## 铁律
|
|
62
|
+
|
|
63
|
+
推草稿脚本只执行一次,无论返回成功/报错/超时都绝不重试,避免重复建草稿。
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""opp-radar 命令行入口。
|
|
2
|
+
|
|
3
|
+
安装后提供两个子命令:
|
|
4
|
+
opp-radar render "<主名>" # issues/<主名>.md -> .html
|
|
5
|
+
opp-radar publish "<主名>" "<标题>" "<摘要>" # 推到公众号草稿箱
|
|
6
|
+
两者都在当前工作目录下读写 issues/ 与 config.json。
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main() -> int:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
prog="opp-radar",
|
|
17
|
+
description="开发者机会观察:渲染公众号 HTML 并推草稿箱。",
|
|
18
|
+
)
|
|
19
|
+
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
20
|
+
|
|
21
|
+
pr = sub.add_parser("render", help="把 issues/<主名>.md 渲染成公众号 HTML")
|
|
22
|
+
pr.add_argument("base", help="文件名主名,如 开发者机会观察-第6期")
|
|
23
|
+
|
|
24
|
+
pp = sub.add_parser("publish", help="把 issues/<主名>.html+.png 推到公众号草稿箱")
|
|
25
|
+
pp.add_argument("base", help="文件名主名")
|
|
26
|
+
pp.add_argument("title", help="文章标题")
|
|
27
|
+
pp.add_argument("digest", help="摘要(120 字内)")
|
|
28
|
+
|
|
29
|
+
args = parser.parse_args()
|
|
30
|
+
|
|
31
|
+
if args.cmd == "render":
|
|
32
|
+
from .render import render
|
|
33
|
+
render(args.base)
|
|
34
|
+
return 0
|
|
35
|
+
if args.cmd == "publish":
|
|
36
|
+
from .publish import push_draft
|
|
37
|
+
push_draft(args.base, args.title, args.digest)
|
|
38
|
+
return 0
|
|
39
|
+
parser.error("未知子命令")
|
|
40
|
+
return 2
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""把本期 HTML + 封面推到微信公众号草稿箱(只存草稿,不群发)。"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from wechat_publish import WeChatAPIError, push_articles
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def push_draft(
|
|
11
|
+
base: str,
|
|
12
|
+
title: str,
|
|
13
|
+
digest: str,
|
|
14
|
+
config_path: str = "config.json",
|
|
15
|
+
issues_dir: str = "issues",
|
|
16
|
+
) -> str | None:
|
|
17
|
+
"""读 config.json 凭据,建草稿。无论成功/报错都只调用一次,绝不重试。"""
|
|
18
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
19
|
+
cfg = json.load(f)
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
media_id = push_articles(
|
|
23
|
+
appid=cfg["appid"],
|
|
24
|
+
secret=cfg["secret"],
|
|
25
|
+
articles=[{
|
|
26
|
+
"html_path": os.path.join(issues_dir, f"{base}.html"),
|
|
27
|
+
"title": title,
|
|
28
|
+
"cover_path": os.path.join(issues_dir, f"{base}.png"),
|
|
29
|
+
"digest": digest,
|
|
30
|
+
}],
|
|
31
|
+
author=cfg["author"],
|
|
32
|
+
open_comment=False,
|
|
33
|
+
publish_now=False, # 个人未认证号无 freepublish 发布权限,发表由人工完成
|
|
34
|
+
)
|
|
35
|
+
print("DRAFT_MEDIA_ID:", media_id)
|
|
36
|
+
return media_id
|
|
37
|
+
except WeChatAPIError as e:
|
|
38
|
+
# 常见 40164 = 出口 IP 未加白。无论什么错都不重试。
|
|
39
|
+
print("WECHAT_ERROR:", e)
|
|
40
|
+
return None
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""把 <主名>.md 用 wechat_formatter 渲染成公众号内联样式 HTML。"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from wechat_formatter import FormatTweaks, get_template, render_article
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def render(base: str, issues_dir: str = "issues") -> str:
|
|
10
|
+
"""读取 issues_dir/<base>.md,写出 issues_dir/<base>.html,返回 html 路径。"""
|
|
11
|
+
md_path = os.path.join(issues_dir, f"{base}.md")
|
|
12
|
+
html_path = os.path.join(issues_dir, f"{base}.html")
|
|
13
|
+
|
|
14
|
+
with open(md_path, "r", encoding="utf-8") as f:
|
|
15
|
+
md = f.read()
|
|
16
|
+
|
|
17
|
+
template = get_template("科技风", "经典") # 科技风 + 经典蓝 #2563eb
|
|
18
|
+
tweaks = FormatTweaks(
|
|
19
|
+
fontSize=15,
|
|
20
|
+
lineHeight=1.8,
|
|
21
|
+
paragraphSpacing=18,
|
|
22
|
+
imageRadius=6,
|
|
23
|
+
)
|
|
24
|
+
body = render_article(md, template, tweaks)
|
|
25
|
+
|
|
26
|
+
html = f"""<!DOCTYPE html>
|
|
27
|
+
<html lang="zh-CN">
|
|
28
|
+
<head>
|
|
29
|
+
<meta charset="utf-8">
|
|
30
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
31
|
+
<title>{base}</title>
|
|
32
|
+
<style>
|
|
33
|
+
body {{ margin:0; background:#f2f3f5; padding:24px 0; }}
|
|
34
|
+
.wechat-article {{ max-width:677px; margin:0 auto; background:#fff;
|
|
35
|
+
padding:24px 16px; box-sizing:border-box; }}
|
|
36
|
+
</style>
|
|
37
|
+
</head>
|
|
38
|
+
<body>
|
|
39
|
+
<div class="wechat-article">
|
|
40
|
+
{body}
|
|
41
|
+
</div>
|
|
42
|
+
</body>
|
|
43
|
+
</html>"""
|
|
44
|
+
|
|
45
|
+
with open(html_path, "w", encoding="utf-8") as f:
|
|
46
|
+
f.write(html)
|
|
47
|
+
|
|
48
|
+
print("HTML_OK:", html_path)
|
|
49
|
+
return html_path
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opp-radar
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Opportunity radar for indie devs: render a WeChat-style article from Markdown and push it to your Official Account draft box.
|
|
5
|
+
Author: 程序员白大力
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/baiyigali/opp_radar
|
|
8
|
+
Project-URL: Repository, https://github.com/baiyigali/opp_radar
|
|
9
|
+
Keywords: wechat,wechat-mp,公众号,markdown,wechat-formatter,publish,indie-dev,opportunity
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Office/Business
|
|
19
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: wechat-formatter
|
|
24
|
+
Requires-Dist: wechat-publish
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# opp-radar
|
|
28
|
+
|
|
29
|
+
面向个人开发者 / 技术创业者的"机会雷达":从公开技术信号里筛出小团队能做的中小产品机会,自动写成公众号专栏,渲染排版后推到草稿箱(发表由人工在后台完成)。
|
|
30
|
+
|
|
31
|
+
专栏名:**开发者机会观察**(公众号:程序员白大力)。觉得有用欢迎关注。
|
|
32
|
+
|
|
33
|
+
## 目录结构
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
opp-radar/
|
|
37
|
+
opp_radar/ # 安装后的 Python 包(render / publish / cli)
|
|
38
|
+
render.py · publish.py · cli.py · __init__.py · py.typed
|
|
39
|
+
prompts/
|
|
40
|
+
dev.md # 开发者机会观察(dev 领域)完整提示词
|
|
41
|
+
# 以后加 marketing.md、xxx.md 等其他领域,复用 opp_radar/
|
|
42
|
+
scripts/
|
|
43
|
+
render.py # 薄壳:转发到 opp_radar render(兼容旧调用,无需安装)
|
|
44
|
+
publish.py # 薄壳:转发到 opp_radar publish
|
|
45
|
+
examples/
|
|
46
|
+
开发者机会观察-第5期.md/.png/.html # 一篇成稿样例
|
|
47
|
+
tests/ # 冒烟测试
|
|
48
|
+
.github/workflows/publish.yml # CI:构建 + TestPyPI/PyPI(OIDC 免密)
|
|
49
|
+
pyproject.toml # 打包配置,提供 `opp-radar` 命令
|
|
50
|
+
issues/ # 各期产出,gitignore,只在本地(用于去重)
|
|
51
|
+
config.json # 微信凭据,gitignore,不进版本库
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 跑一期
|
|
55
|
+
|
|
56
|
+
安装(开发模式):
|
|
57
|
+
```bash
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
然后:
|
|
61
|
+
1. 按 `prompts/dev.md` 的流程:找信号 → 反向验证 → 写 `.md` → 出 `.png`(都落在 `issues/`);
|
|
62
|
+
2. 渲染:`opp-radar render "开发者机会观察-第N期"`;
|
|
63
|
+
3. 推草稿:`opp-radar publish "开发者机会观察-第N期" "<标题>" "<摘要>"`,再去公众号后台草稿箱点"发表"。
|
|
64
|
+
|
|
65
|
+
> 不想安装时也可直接用薄壳脚本,效果一样:
|
|
66
|
+
> `python3 scripts/render.py "..."` / `python3 scripts/publish.py "..."`。
|
|
67
|
+
|
|
68
|
+
## 样例
|
|
69
|
+
|
|
70
|
+
看 `examples/开发者机会观察-第5期.html` 可直接在浏览器打开预览最终排版效果(科技风、经典蓝 #2563eb)。
|
|
71
|
+
|
|
72
|
+
## 依赖
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install -e .
|
|
76
|
+
# 或手动:pip install wechat-formatter wechat-publish
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 本地凭据
|
|
80
|
+
|
|
81
|
+
根目录 `config.json`:
|
|
82
|
+
```json
|
|
83
|
+
{ "appid": "...", "secret": "...", "author": "..." }
|
|
84
|
+
```
|
|
85
|
+
该文件已在 `.gitignore` 中,**不要提交到公开仓库**。
|
|
86
|
+
|
|
87
|
+
## 铁律
|
|
88
|
+
|
|
89
|
+
推草稿脚本只执行一次,无论返回成功/报错/超时都绝不重试,避免重复建草稿。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
opp_radar/__init__.py
|
|
5
|
+
opp_radar/cli.py
|
|
6
|
+
opp_radar/publish.py
|
|
7
|
+
opp_radar/py.typed
|
|
8
|
+
opp_radar/render.py
|
|
9
|
+
opp_radar.egg-info/PKG-INFO
|
|
10
|
+
opp_radar.egg-info/SOURCES.txt
|
|
11
|
+
opp_radar.egg-info/dependency_links.txt
|
|
12
|
+
opp_radar.egg-info/entry_points.txt
|
|
13
|
+
opp_radar.egg-info/requires.txt
|
|
14
|
+
opp_radar.egg-info/top_level.txt
|
|
15
|
+
tests/test_smoke.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
opp_radar
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "opp-radar"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Opportunity radar for indie devs: render a WeChat-style article from Markdown and push it to your Official Account draft box."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "程序员白大力" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["wechat", "wechat-mp", "公众号", "markdown", "wechat-formatter", "publish", "indie-dev", "opportunity"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Office/Business",
|
|
26
|
+
"Topic :: Text Processing :: Markup",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"wechat-formatter",
|
|
30
|
+
"wechat-publish",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
opp-radar = "opp_radar.cli:main"
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://github.com/baiyigali/opp_radar"
|
|
38
|
+
Repository = "https://github.com/baiyigali/opp_radar"
|
|
39
|
+
|
|
40
|
+
[tool.setuptools]
|
|
41
|
+
packages = ["opp_radar"]
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.package-data]
|
|
44
|
+
opp_radar = ["py.typed"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
def test_import_package():
|
|
2
|
+
import opp_radar
|
|
3
|
+
assert opp_radar.__version__
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_cli_help():
|
|
7
|
+
from opp_radar.cli import main
|
|
8
|
+
import sys
|
|
9
|
+
argv = sys.argv
|
|
10
|
+
try:
|
|
11
|
+
sys.argv = ["opp-radar", "--help"]
|
|
12
|
+
try:
|
|
13
|
+
main()
|
|
14
|
+
except SystemExit as e:
|
|
15
|
+
assert e.code == 0
|
|
16
|
+
finally:
|
|
17
|
+
sys.argv = argv
|