v0.2.1: merge-wait debounce, time-awareness, persistent heartbeat, red-state crash fix, pitfall #19
This commit is contained in:
@@ -16,3 +16,9 @@
|
|||||||
## v0.1.x (历史)
|
## v0.1.x (历史)
|
||||||
|
|
||||||
macOS 视觉自动化技能初版:vision.py + 单文件 SKILL.md(坐标绝不落盘、每步验证、朋友圈发布全流程案例)。
|
macOS 视觉自动化技能初版:vision.py + 单文件 SKILL.md(坐标绝不落盘、每步验证、朋友圈发布全流程案例)。
|
||||||
|
|
||||||
|
## v0.2.1 (2026-09-29)
|
||||||
|
|
||||||
|
- agent.py: 消息合并去抖(merge_wait)、AI时间感知(当前时间注入prompt)、心跳时间持久化(重启不重发)、修复 Python3 推导式变量泄漏导致的红点状态复位崩溃
|
||||||
|
- pitfalls-linux.md: 第19条(发文件需先聚焦输入区,否则 Send 点击被吞)
|
||||||
|
- config-template: 新增 merge_wait
|
||||||
|
|||||||
@@ -99,3 +99,11 @@ Qt 渲染管线崩掉——窗口 Map State 仍是 IsViewable、wmctrl 一切正
|
|||||||
②自愈的透明壳重启前,先看进程存活时长——刚登录几分钟就"渲染死亡"的,
|
②自愈的透明壳重启前,先看进程存活时长——刚登录几分钟就"渲染死亡"的,
|
||||||
大概率是这条坑,宁可等待观察也不要立即杀;
|
大概率是这条坑,宁可等待观察也不要立即杀;
|
||||||
③同样的坑没人知道微信哪版会修,升级微信后应重测。
|
③同样的坑没人知道微信哪版会修,升级微信后应重测。
|
||||||
|
|
||||||
|
## 19. 发文件时 Send 按钮点击无效(输入区必须先有焦点)
|
||||||
|
|
||||||
|
Qt 文件选择器的 File name 栏直接粘贴完整路径回车即可挂载文件,这步很稳;
|
||||||
|
但挂载后点 Send 按钮若输入区从未获得过焦点,点击会被无声吞掉(连按两次都没用)。
|
||||||
|
|
||||||
|
**对策**:先点一下输入区任意位置(如文本光标处)让输入区拿到焦点,再点 Send。
|
||||||
|
若仍未发出,补一个 Return(焦点在输入区时回车可发送文件卡片)。
|
||||||
|
|||||||
+14
-5
@@ -296,7 +296,8 @@ def compose_reply(info):
|
|||||||
history = "\n".join(("对方: " if m["from"] == "them" else "我: ") + m["text"]
|
history = "\n".join(("对方: " if m["from"] == "them" else "我: ") + m["text"]
|
||||||
for m in info["messages"])
|
for m in info["messages"])
|
||||||
prompt = (CFG["persona"] +
|
prompt = (CFG["persona"] +
|
||||||
"\n\n当前聊天对象: " + info.get("chat_name", "未知") +
|
"\n\n当前时间: " + datetime.now().strftime("%Y-%m-%d %H:%M %A") + # AI时间感知(学自 WXAUTO_SE)
|
||||||
|
"\n当前聊天对象: " + info.get("chat_name", "未知") +
|
||||||
"\n最近对话:\n" + history +
|
"\n最近对话:\n" + history +
|
||||||
"\n\n请以「我」的身份回复对方最后一条消息。只输出回复正文,不要任何解释、引号或表情符号堆砌。")
|
"\n\n请以「我」的身份回复对方最后一条消息。只输出回复正文,不要任何解释、引号或表情符号堆砌。")
|
||||||
reply = think(prompt).strip().strip('"')
|
reply = think(prompt).strip().strip('"')
|
||||||
@@ -345,6 +346,8 @@ def handle_trigger(slots, img):
|
|||||||
if not brain_ready():
|
if not brain_ready():
|
||||||
log.info("观察模式,无大脑,不操作: %s" % name)
|
log.info("观察模式,无大脑,不操作: %s" % name)
|
||||||
continue
|
continue
|
||||||
|
# 消息合并去抖(学自 WXAUTO_SE):等突发的一串消息到齐再读,避免逐条回复
|
||||||
|
time.sleep(CFG.get("merge_wait", 8))
|
||||||
y = c["slot_y0"] + s * c["slot_dy"]
|
y = c["slot_y0"] + s * c["slot_dy"]
|
||||||
click(c["slot_click_x"], y, 1.5) # 点开该会话
|
click(c["slot_click_x"], y, 1.5) # 点开该会话
|
||||||
info = read_conversation()
|
info = read_conversation()
|
||||||
@@ -431,6 +434,14 @@ def heartbeat():
|
|||||||
hb = CFG.get("heartbeat_chat")
|
hb = CFG.get("heartbeat_chat")
|
||||||
if not hb or not brain_ready():
|
if not hb or not brain_ready():
|
||||||
return # 没有大脑时无法核验目标,不发,防误发
|
return # 没有大脑时无法核验目标,不发,防误发
|
||||||
|
hb_file = os.path.join(BASE, "last_hb")
|
||||||
|
try:
|
||||||
|
since = time.time() - os.path.getmtime(hb_file)
|
||||||
|
except OSError:
|
||||||
|
since = CFG["heartbeat_interval_min"] * 60
|
||||||
|
if since < CFG["heartbeat_interval_min"] * 60 - 120:
|
||||||
|
return # 未到期。上次心跳时间持久化到文件,
|
||||||
|
# 服务重启不再重发(实测重启曾连发3条骚扰)
|
||||||
if not ensure_window():
|
if not ensure_window():
|
||||||
return
|
return
|
||||||
img = scrot()
|
img = scrot()
|
||||||
@@ -450,6 +461,7 @@ def heartbeat():
|
|||||||
return
|
return
|
||||||
free = sh("free -m | awk '/Mem:/{print $7}'").stdout.decode().strip()
|
free = sh("free -m | awk '/Mem:/{print $7}'").stdout.decode().strip()
|
||||||
send_text("〔自动心跳〕Agent在线 ✓ 可用内存%sMB 时间%s" % (free, datetime.now().strftime("%m-%d %H:%M")))
|
send_text("〔自动心跳〕Agent在线 ✓ 可用内存%sMB 时间%s" % (free, datetime.now().strftime("%m-%d %H:%M")))
|
||||||
|
open(hb_file, "w").write(datetime.now().isoformat())
|
||||||
log.info("心跳已发")
|
log.info("心跳已发")
|
||||||
|
|
||||||
# ---------------- 主循环 ----------------
|
# ---------------- 主循环 ----------------
|
||||||
@@ -457,7 +469,6 @@ def main():
|
|||||||
ensure_window()
|
ensure_window()
|
||||||
prev = None
|
prev = None
|
||||||
red_state = {}
|
red_state = {}
|
||||||
last_hb = time.time() - CFG["heartbeat_interval_min"] * 60 + 90 # 启动90秒后先发一次心跳
|
|
||||||
log.info("Agent启动 mode=%s brain=%s" % (CFG["mode"], "GLM" if brain_ready() else "无(观察)"))
|
log.info("Agent启动 mode=%s brain=%s" % (CFG["mode"], "GLM" if brain_ready() else "无(观察)"))
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@@ -486,9 +497,7 @@ def main():
|
|||||||
prev = cur
|
prev = cur
|
||||||
elif prev is None:
|
elif prev is None:
|
||||||
prev = cur
|
prev = cur
|
||||||
if time.time() - last_hb > CFG["heartbeat_interval_min"] * 60:
|
heartbeat() # 内部按 last_hb 文件到期判定,重启不重发
|
||||||
heartbeat()
|
|
||||||
last_hb = time.time()
|
|
||||||
except Exception:
|
except Exception:
|
||||||
log.exception("循环异常")
|
log.exception("循环异常")
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"display": ":1",
|
"display": ":1",
|
||||||
"poll_interval": 8,
|
"poll_interval": 8,
|
||||||
|
"merge_wait": 8,
|
||||||
"mode": "draft",
|
"mode": "draft",
|
||||||
"whitelist": [
|
"whitelist": [
|
||||||
"联系人A",
|
"联系人A",
|
||||||
|
|||||||
Reference in New Issue
Block a user