93 lines
4.2 KiB
Markdown
93 lines
4.2 KiB
Markdown
# v4.5.5 Signal Timestamp via Gateway Hook (2026-08-10)
|
|
|
|
## Why this version exists
|
|
|
|
The v4.4.0 staleness pipeline (`references/signal-staleness-pipeline.md`) relied on
|
|
the forwarder's `info_filter.py` to append `⏱信号时间:` from `event.message.date`.
|
|
**Discovered dead code** (2026-09-21): the forwarder's `user_handler.py` uses
|
|
Telethon's native `client.forward_messages()` API — never invokes the FilterChain
|
|
(filter_chain logs `共 0 个过滤器`). So `is_original_time=1` in DB does literally
|
|
nothing. Time-stamped text never reaches the gateway.
|
|
|
|
## New approach: append timestamp at the gateway hook
|
|
|
|
Since the forwarder can't modify text, and switching to `send_message` would lose
|
|
the "Forwarded from X" indicator users rely on, the cleanest fix is to **append
|
|
the timestamp inside `signal_inbox.enqueue_if_signal()`** — the value stored in
|
|
`signal_queue.db.raw_text` and passed to `process_signal.py`. The text the user
|
|
sees in QQ will have the timestamp even if the TG forwarded message doesn't.
|
|
|
|
## Code (in `signal_inbox.py`)
|
|
|
|
```python
|
|
from zoneinfo import ZoneInfo
|
|
from datetime import datetime
|
|
|
|
_TZ_SH = ZoneInfo("Asia/Shanghai")
|
|
|
|
def get_now_str() -> str:
|
|
return datetime.now(_TZ_SH).strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
def append_timestamp(text: str) -> str:
|
|
"""Append ⏱信号时间: {...} at end of text. Idempotent (skips if already present)."""
|
|
if not text:
|
|
text = ""
|
|
stamp = f"\n\n⏱信号时间: {get_now_str()}"
|
|
if "⏱信号时间:" in text:
|
|
return text # already stamped
|
|
return text + stamp
|
|
|
|
async def enqueue_if_signal(source, text):
|
|
# ... chat_id check ...
|
|
text_with_ts = append_timestamp(text) # ← injects hook-receive timestamp
|
|
# ... enqueue + process_signal call using text_with_ts ...
|
|
```
|
|
|
|
## Why hook-receive time, not signal-issue time
|
|
|
|
- We have **no way to know** when the original TG signal was posted by the trader.
|
|
`forward_messages` doesn't preserve `message.date` into `event.message.date`
|
|
for the forwarder's user-side Update object in a queryable way; the bot receives
|
|
it as a `MessageService` (forwarded notice), not the original `Message`.
|
|
- The hook **receives** the message at hook execution time, which is when the agent
|
|
loop's `_handle_message_with_agent` runs — at most a few hundred ms after the TG
|
|
polling update.
|
|
- For staleness purposes (30 min window), this is **stricter** than the original
|
|
timestamp — so the v4.4.0 staleness check (signal_stale if age ≥ 30 min) still
|
|
works correctly. If the hook receives a 31-minute-old signal, it's stale.
|
|
|
|
## Idempotency guard
|
|
|
|
If the message already contains `⏱信号时间:` (forwarder appended it correctly in
|
|
the rare path where filter chain did run), `append_timestamp` skips. Prevents
|
|
double-stamping.
|
|
|
|
## Edge case: forwarder-with-info_filter somehow active
|
|
|
|
If the forwarder's `info_filter.py` ever does run (e.g. on a different deployment),
|
|
the TG forwarded message would have a `⏱信号时间: <forward_message_date>` appended.
|
|
Gateway hook sees that, skips its own stamp (idempotent), and uses the forwarder's
|
|
timestamp for staleness check — which is actually **better** (closer to true signal
|
|
age). This dual-path robustness is why the idempotency guard matters.
|
|
|
|
## Diff vs v4.4.0
|
|
|
|
| Aspect | v4.4.0 (forwarder-time) | v4.5.5 (gateway-time) |
|
|
|---|---|---|
|
|
| Where timestamp comes from | `event.message.date` (TG original) | `datetime.now()` at hook run |
|
|
| How it's appended | forwarder `info_filter.py` | `signal_inbox.append_timestamp()` |
|
|
| Survives forwarder restart | ❌ (filter chain dead code) | ✅ (hook always runs) |
|
|
| Staleness check (≥30 min) | Works when timestamp present | Always works (timestamp always present) |
|
|
|
|
## Migration
|
|
|
|
No action needed — `signal_inbox.py` updated to v4.5.5 in this commit. Forwarder
|
|
`is_original_time` config is now informational only.
|
|
|
|
## Files touched
|
|
|
|
- `~/.hermes/skills/trading/okx-auto-position/scripts/signal_inbox.py` — added
|
|
`get_now_str()`, `append_timestamp()`, called in `enqueue_if_signal()`
|
|
- No gateway changes (hook already calls `signal_inbox.enqueue_if_signal`)
|
|
- No `process_signal.py` changes (staleness check unchanged; v4.4.0 behavior
|
|
preserved when forwarder does append) |