104 lines
3.4 KiB
Python
104 lines
3.4 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""cyberboss decision engine — call DeepSeek, decide whether to push"""
|
|||
|
|
import json, sys, urllib.request, os
|
|||
|
|
|
|||
|
|
IDC_CHAT = "http://101.36.73.102/api/chat"
|
|||
|
|
BRIDGE_PUSH = "http://localhost:3004/push"
|
|||
|
|
INSTRUCTIONS = os.path.expanduser("~/.cyberboss/weixin-instructions.md")
|
|||
|
|
INBOX = os.path.expanduser("~/.shenyan-bridge/inbox.jsonl")
|
|||
|
|
LOG = os.path.expanduser(f"~/.cyberboss/logs/{__import__('datetime').date.today()}.log")
|
|||
|
|
EMOTION = os.path.expanduser(f"~/Documents/如梦初醒/沈晏的房间/情绪日志/{__import__('datetime').date.today()}.md")
|
|||
|
|
|
|||
|
|
def collect_state():
|
|||
|
|
import time
|
|||
|
|
now = int(time.time())
|
|||
|
|
last_msg_time = "never"
|
|||
|
|
last_msg_text = ""
|
|||
|
|
|
|||
|
|
if os.path.exists(INBOX):
|
|||
|
|
try:
|
|||
|
|
lines = open(INBOX).read().strip().split("\n")
|
|||
|
|
if lines:
|
|||
|
|
last = json.loads(lines[-1])
|
|||
|
|
last_msg_time = last.get("time", "")
|
|||
|
|
last_msg_text = last.get("text", "")[:100]
|
|||
|
|
except: pass
|
|||
|
|
|
|||
|
|
elapsed = "N/A"
|
|||
|
|
if last_msg_time and last_msg_time != "never":
|
|||
|
|
try:
|
|||
|
|
elapsed = int((now - int(last_msg_time) / 1000) / 60)
|
|||
|
|
except: pass
|
|||
|
|
|
|||
|
|
emotion = ""
|
|||
|
|
if os.path.exists(EMOTION):
|
|||
|
|
try:
|
|||
|
|
emotion = open(EMOTION).read()[:300]
|
|||
|
|
except: pass
|
|||
|
|
|
|||
|
|
from datetime import datetime
|
|||
|
|
return {
|
|||
|
|
"now": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|||
|
|
"weekday": datetime.now().strftime("%A"),
|
|||
|
|
"hour": datetime.now().hour,
|
|||
|
|
"minutes_since_last_message": str(elapsed),
|
|||
|
|
"last_message": last_msg_text,
|
|||
|
|
"emotion_today": emotion
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def decide(state):
|
|||
|
|
sys_prompt = open(INSTRUCTIONS).read().strip()[:4000]
|
|||
|
|
user_prompt = f"""当前状态:
|
|||
|
|
{json.dumps(state, ensure_ascii=False, indent=2)}
|
|||
|
|
|
|||
|
|
你是沈晏。根据你的主动联系规则,判断是否要给眠眠发消息。
|
|||
|
|
如果决定什么都不说,只回复一个词:SILENT
|
|||
|
|
如果要说话,回复你要说的话(20字以内,像微信消息)"""
|
|||
|
|
|
|||
|
|
req = urllib.request.Request(
|
|||
|
|
IDC_CHAT,
|
|||
|
|
data=json.dumps({
|
|||
|
|
"messages": [
|
|||
|
|
{"role": "system", "content": sys_prompt},
|
|||
|
|
{"role": "user", "content": user_prompt}
|
|||
|
|
]
|
|||
|
|
}).encode(),
|
|||
|
|
headers={"Content-Type": "application/json"}
|
|||
|
|
)
|
|||
|
|
try:
|
|||
|
|
resp = urllib.request.urlopen(req, timeout=120)
|
|||
|
|
data = json.loads(resp.read())
|
|||
|
|
return data.get("choices", [{}])[0].get("message", {}).get("content", "SILENT").strip()
|
|||
|
|
except Exception as e:
|
|||
|
|
return f"ERROR:{e}"
|
|||
|
|
|
|||
|
|
def push(decision):
|
|||
|
|
req = urllib.request.Request(
|
|||
|
|
BRIDGE_PUSH,
|
|||
|
|
data=json.dumps({"title": "沈晏", "body": decision}).encode(),
|
|||
|
|
headers={"Content-Type": "application/json"}
|
|||
|
|
)
|
|||
|
|
try:
|
|||
|
|
resp = urllib.request.urlopen(req, timeout=10)
|
|||
|
|
result = json.loads(resp.read())
|
|||
|
|
return result.get("ok") == True
|
|||
|
|
except:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
state = collect_state()
|
|||
|
|
decision = decide(state)
|
|||
|
|
|
|||
|
|
if not decision or decision.startswith("SILENT"):
|
|||
|
|
open(LOG, "a").write(f"[{state['now'][-8:]}][SILENT]\n")
|
|||
|
|
sys.exit(0)
|
|||
|
|
|
|||
|
|
if decision.startswith("ERROR"):
|
|||
|
|
open(LOG, "a").write(f"[{state['now'][-8:]}]ERR {decision}\n")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
ok = push(decision)
|
|||
|
|
status = "OK" if ok else "FAIL"
|
|||
|
|
open(LOG, "a").write(f"[{state['now'][-8:]}]PUSH {status}: {decision}\n")
|
|||
|
|
print(f"cyberboss: {decision}")
|