fix: release v2.0.2 and restore fortune pre-cache#23
Conversation
for more information, see https://pre-commit.ci
Reviewer's Guide通过恢复带有渲染图片缓存的 Fortune 自动预生成功能、在签运持久化中加入用户名和活跃请求查询、在插件生命周期中接入夜间预生成循环,并相应更新文档/元数据和 CI 工作流,实现 AstrBot 插件 v2.0.2。 恢复后的 Fortune 自动预生成和图片缓存顺序图sequenceDiagram
participant SetuPlugin
participant FortuneCommandHandler
participant FortuneService
participant FortuneRepository
SetuPlugin->>SetuPlugin: _fortune_auto_refresh_enabled(config)
alt auto_refresh_enabled
SetuPlugin->>SetuPlugin: _fortune_pregenerate_loop()
loop daily
SetuPlugin->>SetuPlugin: asyncio.sleep(_seconds_until_next_midnight())
SetuPlugin->>SetuPlugin: _pregenerate_active_fortune_images()
SetuPlugin->>FortuneCommandHandler: pregenerate_active_fortune_images(days)
FortuneCommandHandler->>FortuneService: pregenerate_active_user_records(days, include_existing=True)
FortuneService->>FortuneRepository: get_active_fortune_requests(days, date_str)
FortuneRepository-->>FortuneService: list[FortuneGenerationRequest]
loop each active request
FortuneService->>FortuneRepository: get_today_fortune(request)
alt existing_record
FortuneService-->>FortuneCommandHandler: FortuneRecord
else new_record
FortuneService->>FortuneService: get_or_create_fortune(request)
FortuneService-->>FortuneCommandHandler: FortuneRecord
end
FortuneCommandHandler->>FortuneService: get_cached_image(user_id, date_str)
alt no_cached_image
FortuneCommandHandler->>FortuneCommandHandler: _render_fortune_image(record, service)
FortuneCommandHandler-->>SetuPlugin: increment cached_count
end
end
FortuneCommandHandler-->>SetuPlugin: cached_count
end
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your Experience访问你的 dashboard 以:
Getting HelpOriginal review guide in EnglishReviewer's GuideImplements AstrBot plugin v2.0.2 by restoring Fortune auto pre-generation with rendered image caching, enriching fortune persistence with usernames and active-request queries, wiring a nightly pre-generation loop into the plugin lifecycle, and updating docs/metadata and CI workflows accordingly. Sequence diagram for restored fortune auto pre-generation and image cachingsequenceDiagram
participant SetuPlugin
participant FortuneCommandHandler
participant FortuneService
participant FortuneRepository
SetuPlugin->>SetuPlugin: _fortune_auto_refresh_enabled(config)
alt auto_refresh_enabled
SetuPlugin->>SetuPlugin: _fortune_pregenerate_loop()
loop daily
SetuPlugin->>SetuPlugin: asyncio.sleep(_seconds_until_next_midnight())
SetuPlugin->>SetuPlugin: _pregenerate_active_fortune_images()
SetuPlugin->>FortuneCommandHandler: pregenerate_active_fortune_images(days)
FortuneCommandHandler->>FortuneService: pregenerate_active_user_records(days, include_existing=True)
FortuneService->>FortuneRepository: get_active_fortune_requests(days, date_str)
FortuneRepository-->>FortuneService: list[FortuneGenerationRequest]
loop each active request
FortuneService->>FortuneRepository: get_today_fortune(request)
alt existing_record
FortuneService-->>FortuneCommandHandler: FortuneRecord
else new_record
FortuneService->>FortuneService: get_or_create_fortune(request)
FortuneService-->>FortuneCommandHandler: FortuneRecord
end
FortuneCommandHandler->>FortuneService: get_cached_image(user_id, date_str)
alt no_cached_image
FortuneCommandHandler->>FortuneCommandHandler: _render_fortune_image(record, service)
FortuneCommandHandler-->>SetuPlugin: increment cached_count
end
end
FortuneCommandHandler-->>SetuPlugin: cached_count
end
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - 我发现了两个问题
面向 AI 代理的提示
请根据这次代码评审中的评论进行修改:
## 单独评论
### 评论 1
<location path="src/infrastructure/astrbot/commands/fortune.py" line_range="366-371" />
<code_context>
+ days=days, include_existing=True
+ )
+
+ cached_count = 0
+ for record in records:
+ if await service.get_cached_image(record.user_id, record.date_str):
+ continue
+ image_bytes = await self._render_fortune_image(record, service)
+ if image_bytes:
+ cached_count += 1
+ return cached_count
</code_context>
<issue_to_address>
**建议:** 单次渲染错误会中止整个预生成过程
由于这是一个后台预生成任务,建议对每条记录单独处理失败情况,这样某个用户/日期的数据出错时,不会阻塞后续记录的处理。例如:
```python
for record in records:
try:
if await service.get_cached_image(record.user_id, record.date_str):
continue
image_bytes = await self._render_fortune_image(record, service)
if image_bytes:
cached_count += 1
except Exception as exc:
logger.warning(
"[fortune] Failed to pregenerate cache for %s: %s",
record.user_id,
exc,
)
```
这样可以在继续处理其他记录的同时,记录失败日志以便排查。
建议的实现:
```python
cached_count = 0
for record in records:
try:
if await service.get_cached_image(record.user_id, record.date_str):
continue
image_bytes = await self._render_fortune_image(record, service)
if image_bytes:
cached_count += 1
except Exception as exc: # noqa: BLE001
logger.warning(
"[fortune] Failed to pregenerate cache for %s: %s",
record.user_id,
exc,
)
return cached_count
```
1. 确保该模块中定义了 `logger`,例如:
- 在文件顶部 `import logging`,以及
- `logger = logging.getLogger(__name__)`。
2. 如果你的 lint 规则不同(例如不需要 `# noqa: BLE001`),请根据现有代码风格调整或删除该注释。
</issue_to_address>
### 评论 2
<location path="tests/infrastructure/test_fortune_pregeneration.py" line_range="84-85" />
<code_context>
+ return 0
+
+
+@pytest.mark.asyncio
+async def test_pregenerate_active_fortune_images_writes_rendered_cache(
+ monkeypatch,
+) -> None:
</code_context>
<issue_to_address>
**建议(测试):** 增加一个已有缓存图片的测试用例,以验证 `pregenerate_active_fortune_images` 会跳过渲染并且不会增加缓存计数。
为覆盖新的行为,请添加一个测试:将 `MemoryFortuneRepo.get_cached_image_path` 配置为对某个用户/日期返回非 None 的值,并断言:(1) 对该记录不会调用 `handler._renderer.render_to_image`(例如通过 spy/计数器来验证),(2) `pregenerate_active_fortune_images` 返回 `0`,或者至少不会把该用户计入返回值中。这样可以帮助捕获重新渲染已缓存图片的回归问题。
建议的实现:
```python
@pytest.mark.asyncio
async def test_pregenerate_active_fortune_images_writes_rendered_cache(
monkeypatch,
) -> None:
today = date.today().isoformat()
repo = MemoryFortuneRepo(
[
FortuneGenerationRequest(
user_id="user-1",
username="测试用户",
date_str="2026-05-17",
group_id="group-1",
)
]
)
class DummyRenderer:
def __init__(self) -> None:
self.render_call_count = 0
async def render_to_image(self, fortune: str, output_path: Path) -> Path: # type: ignore[override]
self.render_call_count += 1
# 在真实实现中这里会写入图片文件;在测试中我们只返回传入的
# 路径,以满足 handler 的预期。
return output_path
renderer = DummyRenderer()
# 根据你的代码库情况,下面可能需要替换为实际用于预生成的 handler
# 类或工厂。
handler = FortunePregenerationHandler(repo=repo, renderer=renderer) # type: ignore[name-defined]
count = await handler.pregenerate_active_fortune_images(today=today)
# 没有缓存的一个有效 fortune 应当被渲染并计数。
assert count == 1
assert renderer.render_call_count == 1
assert ("user-1", "2026-05-17") in repo.cached_images
@pytest.mark.asyncio
async def test_pregenerate_active_fortune_images_skips_when_cache_exists(
monkeypatch,
) -> None:
today = date.today().isoformat()
repo = MemoryFortuneRepo(
[
FortuneGenerationRequest(
user_id="user-1",
username="测试用户",
date_str="2026-05-17",
group_id="group-1",
)
]
)
# 通过强制 get_cached_image_path 返回非 None 的 Path,模拟该用户/日期
# 已经存在缓存图片。
async def fake_get_cached_image_path(user_id: str, date_str: str) -> Path | None:
assert user_id == "user-1"
assert date_str == "2026-05-17"
return Path(f"/tmp/{user_id}_{date_str}.jpg")
monkeypatch.setattr(
repo,
"get_cached_image_path",
fake_get_cached_image_path,
raising=True,
)
class DummyRenderer:
def __init__(self) -> None:
self.render_call_count = 0
async def render_to_image(self, fortune: str, output_path: Path) -> Path: # type: ignore[override]
self.render_call_count += 1
return output_path
renderer = DummyRenderer()
handler = FortunePregenerationHandler(repo=repo, renderer=renderer) # type: ignore[name-defined]
count = await handler.pregenerate_active_fortune_images(today=today)
# 由于返回了缓存图片路径,handler 应当跳过该记录的渲染,并且不将其
# 计入预生成数量。
assert renderer.render_call_count == 0
assert count == 0
```
上述修改假设:
1. 存在一个 `FortunePregenerationHandler`(或类似命名)的类:
- 在构造函数中接收 `repo` 和 `renderer`;
- 暴露一个异步方法 `pregenerate_active_fortune_images(today: str) -> int`;
- 在内部使用 `self._renderer.render_to_image(...)`,并调用 `repo.get_cached_image_path(...)` 决定是否需要渲染。
2. `MemoryFortuneRepo` 具有与断言兼容的 `cached_images` 属性。
如果你的实际 handler 或函数名/签名不同,你需要:
1. 将 `FortunePregenerationHandler(...)` 替换为真实用于预生成的类或函数(或者调整测试以直接调用该函数,而不是通过 handler)。
2. 更新 `await handler.pregenerate_active_fortune_images(today=today)` 的调用,以符合真实 API。
3. 如果 `get_cached_image_path` 是同步的或签名不同,请相应调整 `fake_get_cached_image_path`,并在必要时移除 `async`。
需要在最终实现中保持的关键行为是:
- 当 `get_cached_image_path` 返回非 None 的路径时,不得对该记录调用 `render_to_image`。
- 这类记录不得计入 `pregenerate_active_fortune_images` 返回的数量中。
</issue_to_address>帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进评审质量。
Original comment in English
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="src/infrastructure/astrbot/commands/fortune.py" line_range="366-371" />
<code_context>
+ days=days, include_existing=True
+ )
+
+ cached_count = 0
+ for record in records:
+ if await service.get_cached_image(record.user_id, record.date_str):
+ continue
+ image_bytes = await self._render_fortune_image(record, service)
+ if image_bytes:
+ cached_count += 1
+ return cached_count
</code_context>
<issue_to_address>
**suggestion:** A single rendering error will abort the entire pregeneration pass
Since this runs as a background pregeneration job, consider handling failures per record so one bad user/date doesn’t stop the rest from being processed. For example:
```python
for record in records:
try:
if await service.get_cached_image(record.user_id, record.date_str):
continue
image_bytes = await self._render_fortune_image(record, service)
if image_bytes:
cached_count += 1
except Exception as exc:
logger.warning(
"[fortune] Failed to pregenerate cache for %s: %s",
record.user_id,
exc,
)
```
This keeps processing other records while still logging failures for investigation.
Suggested implementation:
```python
cached_count = 0
for record in records:
try:
if await service.get_cached_image(record.user_id, record.date_str):
continue
image_bytes = await self._render_fortune_image(record, service)
if image_bytes:
cached_count += 1
except Exception as exc: # noqa: BLE001
logger.warning(
"[fortune] Failed to pregenerate cache for %s: %s",
record.user_id,
exc,
)
return cached_count
```
1. Ensure this module has a `logger` defined, e.g.:
- `import logging` at the top of the file, and
- `logger = logging.getLogger(__name__)`.
2. If your linting rules differ (e.g. no need for `# noqa: BLE001`), adjust or remove that comment to match your existing code style.
</issue_to_address>
### Comment 2
<location path="tests/infrastructure/test_fortune_pregeneration.py" line_range="84-85" />
<code_context>
+ return 0
+
+
+@pytest.mark.asyncio
+async def test_pregenerate_active_fortune_images_writes_rendered_cache(
+ monkeypatch,
+) -> None:
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test case where existing cached images are present so `pregenerate_active_fortune_images` skips rendering and does not increment the cached count.
To cover the new behavior, please add a test that configures `MemoryFortuneRepo.get_cached_image_path` to return a non-None value for a user/date and asserts that: (1) `handler._renderer.render_to_image` is not called for that record (e.g., via a spy/counter), and (2) `pregenerate_active_fortune_images` returns `0` or otherwise does not include that user in the count. This will help catch regressions where we re-render already cached images.
Suggested implementation:
```python
@pytest.mark.asyncio
async def test_pregenerate_active_fortune_images_writes_rendered_cache(
monkeypatch,
) -> None:
today = date.today().isoformat()
repo = MemoryFortuneRepo(
[
FortuneGenerationRequest(
user_id="user-1",
username="测试用户",
date_str="2026-05-17",
group_id="group-1",
)
]
)
class DummyRenderer:
def __init__(self) -> None:
self.render_call_count = 0
async def render_to_image(self, fortune: str, output_path: Path) -> Path: # type: ignore[override]
self.render_call_count += 1
# In a real implementation this would write an image file; here we just
# return the provided path to satisfy the handler's expectations.
return output_path
renderer = DummyRenderer()
# Depending on your codebase this may need to be updated to the actual handler
# class or factory used for pregeneration.
handler = FortunePregenerationHandler(repo=repo, renderer=renderer) # type: ignore[name-defined]
count = await handler.pregenerate_active_fortune_images(today=today)
# One active fortune with no cache should be rendered and counted.
assert count == 1
assert renderer.render_call_count == 1
assert ("user-1", "2026-05-17") in repo.cached_images
@pytest.mark.asyncio
async def test_pregenerate_active_fortune_images_skips_when_cache_exists(
monkeypatch,
) -> None:
today = date.today().isoformat()
repo = MemoryFortuneRepo(
[
FortuneGenerationRequest(
user_id="user-1",
username="测试用户",
date_str="2026-05-17",
group_id="group-1",
)
]
)
# Simulate that a cached image already exists for this user/date by forcing
# get_cached_image_path to return a non-None Path.
async def fake_get_cached_image_path(user_id: str, date_str: str) -> Path | None:
assert user_id == "user-1"
assert date_str == "2026-05-17"
return Path(f"/tmp/{user_id}_{date_str}.jpg")
monkeypatch.setattr(
repo,
"get_cached_image_path",
fake_get_cached_image_path,
raising=True,
)
class DummyRenderer:
def __init__(self) -> None:
self.render_call_count = 0
async def render_to_image(self, fortune: str, output_path: Path) -> Path: # type: ignore[override]
self.render_call_count += 1
return output_path
renderer = DummyRenderer()
handler = FortunePregenerationHandler(repo=repo, renderer=renderer) # type: ignore[name-defined]
count = await handler.pregenerate_active_fortune_images(today=today)
# Because a cached image path is returned, the handler should skip rendering
# for this record and not include it in the pregenerated count.
assert renderer.render_call_count == 0
assert count == 0
```
The above changes assume:
1. There is a `FortunePregenerationHandler` (or similarly named) class that:
- Accepts `repo` and `renderer` in its constructor.
- Exposes an async method `pregenerate_active_fortune_images(today: str) -> int`.
- Uses `self._renderer.render_to_image(...)` internally and consults `repo.get_cached_image_path(...)` to decide whether to render.
2. `MemoryFortuneRepo` has a `cached_images` attribute compatible with the assertions.
If your actual handler or function names / signatures differ, you will need to:
1. Replace `FortunePregenerationHandler(...)` with the real class or function used for pregeneration (or adjust the tests to call the function directly rather than going through a handler).
2. Update the call `await handler.pregenerate_active_fortune_images(today=today)` to match the real API.
3. If `get_cached_image_path` is synchronous or has a different signature, adjust `fake_get_cached_image_path` accordingly and remove `async` if necessary.
The key behaviors to preserve in the final implementation are:
- When `get_cached_image_path` returns a non-None path, `render_to_image` must not be called for that record.
- Such records must not be counted in the value returned by `pregenerate_active_fortune_images`.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| @pytest.mark.asyncio | ||
| async def test_pregenerate_active_fortune_images_writes_rendered_cache( |
There was a problem hiding this comment.
建议(测试): 增加一个已有缓存图片的测试用例,以验证 pregenerate_active_fortune_images 会跳过渲染并且不会增加缓存计数。
为覆盖新的行为,请添加一个测试:将 MemoryFortuneRepo.get_cached_image_path 配置为对某个用户/日期返回非 None 的值,并断言:(1) 对该记录不会调用 handler._renderer.render_to_image(例如通过 spy/计数器来验证),(2) pregenerate_active_fortune_images 返回 0,或者至少不会把该用户计入返回值中。这样可以帮助捕获重新渲染已缓存图片的回归问题。
建议的实现:
@pytest.mark.asyncio
async def test_pregenerate_active_fortune_images_writes_rendered_cache(
monkeypatch,
) -> None:
today = date.today().isoformat()
repo = MemoryFortuneRepo(
[
FortuneGenerationRequest(
user_id="user-1",
username="测试用户",
date_str="2026-05-17",
group_id="group-1",
)
]
)
class DummyRenderer:
def __init__(self) -> None:
self.render_call_count = 0
async def render_to_image(self, fortune: str, output_path: Path) -> Path: # type: ignore[override]
self.render_call_count += 1
# 在真实实现中这里会写入图片文件;在测试中我们只返回传入的
# 路径,以满足 handler 的预期。
return output_path
renderer = DummyRenderer()
# 根据你的代码库情况,下面可能需要替换为实际用于预生成的 handler
# 类或工厂。
handler = FortunePregenerationHandler(repo=repo, renderer=renderer) # type: ignore[name-defined]
count = await handler.pregenerate_active_fortune_images(today=today)
# 没有缓存的一个有效 fortune 应当被渲染并计数。
assert count == 1
assert renderer.render_call_count == 1
assert ("user-1", "2026-05-17") in repo.cached_images
@pytest.mark.asyncio
async def test_pregenerate_active_fortune_images_skips_when_cache_exists(
monkeypatch,
) -> None:
today = date.today().isoformat()
repo = MemoryFortuneRepo(
[
FortuneGenerationRequest(
user_id="user-1",
username="测试用户",
date_str="2026-05-17",
group_id="group-1",
)
]
)
# 通过强制 get_cached_image_path 返回非 None 的 Path,模拟该用户/日期
# 已经存在缓存图片。
async def fake_get_cached_image_path(user_id: str, date_str: str) -> Path | None:
assert user_id == "user-1"
assert date_str == "2026-05-17"
return Path(f"/tmp/{user_id}_{date_str}.jpg")
monkeypatch.setattr(
repo,
"get_cached_image_path",
fake_get_cached_image_path,
raising=True,
)
class DummyRenderer:
def __init__(self) -> None:
self.render_call_count = 0
async def render_to_image(self, fortune: str, output_path: Path) -> Path: # type: ignore[override]
self.render_call_count += 1
return output_path
renderer = DummyRenderer()
handler = FortunePregenerationHandler(repo=repo, renderer=renderer) # type: ignore[name-defined]
count = await handler.pregenerate_active_fortune_images(today=today)
# 由于返回了缓存图片路径,handler 应当跳过该记录的渲染,并且不将其
# 计入预生成数量。
assert renderer.render_call_count == 0
assert count == 0上述修改假设:
- 存在一个
FortunePregenerationHandler(或类似命名)的类:- 在构造函数中接收
repo和renderer; - 暴露一个异步方法
pregenerate_active_fortune_images(today: str) -> int; - 在内部使用
self._renderer.render_to_image(...),并调用repo.get_cached_image_path(...)决定是否需要渲染。
- 在构造函数中接收
MemoryFortuneRepo具有与断言兼容的cached_images属性。
如果你的实际 handler 或函数名/签名不同,你需要:
- 将
FortunePregenerationHandler(...)替换为真实用于预生成的类或函数(或者调整测试以直接调用该函数,而不是通过 handler)。 - 更新
await handler.pregenerate_active_fortune_images(today=today)的调用,以符合真实 API。 - 如果
get_cached_image_path是同步的或签名不同,请相应调整fake_get_cached_image_path,并在必要时移除async。
需要在最终实现中保持的关键行为是:
- 当
get_cached_image_path返回非 None 的路径时,不得对该记录调用render_to_image。 - 这类记录不得计入
pregenerate_active_fortune_images返回的数量中。
Original comment in English
suggestion (testing): Add a test case where existing cached images are present so pregenerate_active_fortune_images skips rendering and does not increment the cached count.
To cover the new behavior, please add a test that configures MemoryFortuneRepo.get_cached_image_path to return a non-None value for a user/date and asserts that: (1) handler._renderer.render_to_image is not called for that record (e.g., via a spy/counter), and (2) pregenerate_active_fortune_images returns 0 or otherwise does not include that user in the count. This will help catch regressions where we re-render already cached images.
Suggested implementation:
@pytest.mark.asyncio
async def test_pregenerate_active_fortune_images_writes_rendered_cache(
monkeypatch,
) -> None:
today = date.today().isoformat()
repo = MemoryFortuneRepo(
[
FortuneGenerationRequest(
user_id="user-1",
username="测试用户",
date_str="2026-05-17",
group_id="group-1",
)
]
)
class DummyRenderer:
def __init__(self) -> None:
self.render_call_count = 0
async def render_to_image(self, fortune: str, output_path: Path) -> Path: # type: ignore[override]
self.render_call_count += 1
# In a real implementation this would write an image file; here we just
# return the provided path to satisfy the handler's expectations.
return output_path
renderer = DummyRenderer()
# Depending on your codebase this may need to be updated to the actual handler
# class or factory used for pregeneration.
handler = FortunePregenerationHandler(repo=repo, renderer=renderer) # type: ignore[name-defined]
count = await handler.pregenerate_active_fortune_images(today=today)
# One active fortune with no cache should be rendered and counted.
assert count == 1
assert renderer.render_call_count == 1
assert ("user-1", "2026-05-17") in repo.cached_images
@pytest.mark.asyncio
async def test_pregenerate_active_fortune_images_skips_when_cache_exists(
monkeypatch,
) -> None:
today = date.today().isoformat()
repo = MemoryFortuneRepo(
[
FortuneGenerationRequest(
user_id="user-1",
username="测试用户",
date_str="2026-05-17",
group_id="group-1",
)
]
)
# Simulate that a cached image already exists for this user/date by forcing
# get_cached_image_path to return a non-None Path.
async def fake_get_cached_image_path(user_id: str, date_str: str) -> Path | None:
assert user_id == "user-1"
assert date_str == "2026-05-17"
return Path(f"/tmp/{user_id}_{date_str}.jpg")
monkeypatch.setattr(
repo,
"get_cached_image_path",
fake_get_cached_image_path,
raising=True,
)
class DummyRenderer:
def __init__(self) -> None:
self.render_call_count = 0
async def render_to_image(self, fortune: str, output_path: Path) -> Path: # type: ignore[override]
self.render_call_count += 1
return output_path
renderer = DummyRenderer()
handler = FortunePregenerationHandler(repo=repo, renderer=renderer) # type: ignore[name-defined]
count = await handler.pregenerate_active_fortune_images(today=today)
# Because a cached image path is returned, the handler should skip rendering
# for this record and not include it in the pregenerated count.
assert renderer.render_call_count == 0
assert count == 0The above changes assume:
- There is a
FortunePregenerationHandler(or similarly named) class that:- Accepts
repoandrendererin its constructor. - Exposes an async method
pregenerate_active_fortune_images(today: str) -> int. - Uses
self._renderer.render_to_image(...)internally and consultsrepo.get_cached_image_path(...)to decide whether to render.
- Accepts
MemoryFortuneRepohas acached_imagesattribute compatible with the assertions.
If your actual handler or function names / signatures differ, you will need to:
- Replace
FortunePregenerationHandler(...)with the real class or function used for pregeneration (or adjust the tests to call the function directly rather than going through a handler). - Update the call
await handler.pregenerate_active_fortune_images(today=today)to match the real API. - If
get_cached_image_pathis synchronous or has a different signature, adjustfake_get_cached_image_pathaccordingly and removeasyncif necessary.
The key behaviors to preserve in the final implementation are:
- When
get_cached_image_pathreturns a non-None path,render_to_imagemust not be called for that record. - Such records must not be counted in the value returned by
pregenerate_active_fortune_images.
There was a problem hiding this comment.
Pull request overview
本 PR 发布 v2.0.2,并恢复 Fortune 跨日后为近期活跃用户预生成并缓存运势卡片图片的行为,同时更新测试环境与仓库文档。
Changes:
- 新增 Fortune 活跃用户请求查询、预生成记录返回与图片缓存预渲染流程。
- 在插件初始化时启动 Fortune 日切预缓存后台任务,并在终止时取消。
- 更新版本、CHANGELOG、AGENTS 指南,并删除错误路径下的旧 workflow 文件。
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
main.py |
Adds Fortune pre-generation background loop lifecycle. |
src/application/ports/fortune_repository.py |
Extends Fortune repository port with active generation request lookup. |
src/domain/fortune/service.py |
Refactors active-user pre-generation to return generated/existing records. |
src/infrastructure/astrbot/commands/fortune.py |
Adds rendered Fortune card cache pre-generation. |
src/infrastructure/persistence/sqlite_fortune_repository.py |
Stores usernames and implements active Fortune request query. |
tests/infrastructure/test_fortune_pregeneration.py |
Adds coverage for Fortune pre-generation and SQLite request building. |
tests/conftest.py |
Attempts to pin ASTRBOT_ROOT before AstrBot imports. |
metadata.yaml |
Bumps plugin version to v2.0.2. |
CHANGELOG.md |
Documents v2.0.2 fixes and workflow cleanup. |
AGENTS.md |
Refreshes repository architecture and contributor guidance. |
.github/workflow/release-from-changelog.yml |
Removes obsolete workflow from the non-standard path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| records.append(existing) | ||
| continue | ||
|
|
||
| records.append(await self.get_or_create_fortune(request)) |
| for parent in Path(__file__).resolve().parents: | ||
| if (parent / "astrbot" / "core" / "__init__.py").exists(): | ||
| os.environ.setdefault("ASTRBOT_ROOT", str(parent)) | ||
| break |
Summary
Verification
Notes
Summary by Sourcery
发布插件版本 v2.0.2,恢复自动运势预生成功能,并在运行时与测试行为上更安全。
Bug 修复:
ASTRBOT_ROOT,确保 AstrBot 核心运行时数据和测试不再写入插件源码目录下。功能增强:
AGENTS.md中完善 AstrBot agent/项目指引,包括架构概览、运行时数据规则以及开发工作流。文档:
CHANGELOG.md中记录 2.0.2 版本的变更,包括恢复的运势缓存以及工作流清理。AGENTS.md,更详细地描述插件的架构、约定、发布规则和测试指南。测试:
杂项:
.github/workflow/目录中移除废弃的 GitHub Actions 工作流文件。Original summary in English
Summary by Sourcery
Release plugin version v2.0.2 with restored automated fortune pre-generation and safer runtime/test behavior.
Bug Fixes:
Enhancements:
Documentation:
Tests:
Chores: