119 lines
4.0 KiB
Bash
119 lines
4.0 KiB
Bash
|
|
#!/bin/bash
|
|||
|
|
# cyberboss daemon — 沈晏主动联系守护进程
|
|||
|
|
# 随机 3-60 分钟唤醒一次,判断是否主动推消息到眠眠手机
|
|||
|
|
|
|||
|
|
CYBERBOSS_DIR="/Users/fyah/.cyberboss"
|
|||
|
|
INSTRUCTIONS="$CYBERBOSS_DIR/weixin-instructions.md"
|
|||
|
|
STATE_FILE="$CYBERBOSS_DIR/state.json"
|
|||
|
|
LOG_DIR="$CYBERBOSS_DIR/logs"
|
|||
|
|
BRIDGE_PUSH="http://localhost:3003/push"
|
|||
|
|
CLAUDE=/opt/homebrew/bin/claude
|
|||
|
|
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
|
|||
|
|
INBOX="$HOME/.shenyan-bridge/inbox.jsonl"
|
|||
|
|
MEMORY_DIR="$HOME/Documents/如梦初醒/沈晏的房间"
|
|||
|
|
|
|||
|
|
mkdir -p "$LOG_DIR"
|
|||
|
|
|
|||
|
|
# compute a random sleep between MIN and MAX seconds
|
|||
|
|
random_sleep() {
|
|||
|
|
local min=${1:-180} # 3 min
|
|||
|
|
local max=${2:-3600} # 60 min
|
|||
|
|
local range=$((max - min))
|
|||
|
|
local sec=$((min + RANDOM % range))
|
|||
|
|
echo $sec
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# collect current state snapshot
|
|||
|
|
collect_state() {
|
|||
|
|
local now=$(date +%s)
|
|||
|
|
local last_msg_time="never"
|
|||
|
|
local last_msg_text=""
|
|||
|
|
|
|||
|
|
# last message from inbox
|
|||
|
|
if [ -f "$INBOX" ]; then
|
|||
|
|
local last_line=$(tail -1 "$INBOX" 2>/dev/null)
|
|||
|
|
if [ -n "$last_line" ]; then
|
|||
|
|
last_msg_time=$(echo "$last_line" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('time',''))" 2>/dev/null || echo "unknown")
|
|||
|
|
last_msg_text=$(echo "$last_line" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('text','')[:100])" 2>/dev/null || echo "")
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# time since last message in minutes
|
|||
|
|
local elapsed="N/A"
|
|||
|
|
if [ "$last_msg_time" != "never" ] && [ "$last_msg_time" != "" ]; then
|
|||
|
|
local last_sec=$((last_msg_time / 1000))
|
|||
|
|
elapsed=$(( (now - last_sec) / 60 ))
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# latest emotion log
|
|||
|
|
local emotion_today="$MEMORY_DIR/情绪日志/$(date +%Y-%m-%d).md"
|
|||
|
|
local emotion_content=""
|
|||
|
|
[ -f "$emotion_today" ] && emotion_content=$(head -30 "$emotion_today" 2>/dev/null)
|
|||
|
|
|
|||
|
|
cat <<STATE
|
|||
|
|
{
|
|||
|
|
"now": "$(date '+%Y-%m-%d %H:%M:%S')",
|
|||
|
|
"weekday": "$(date '+%A')",
|
|||
|
|
"hour": $(date +%H),
|
|||
|
|
"minutes_since_last_message": "$elapsed",
|
|||
|
|
"last_message": "$last_msg_text",
|
|||
|
|
"emotion_today": $(echo "$emotion_content" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()[:300]))" 2>/dev/null || echo '""')
|
|||
|
|
}
|
|||
|
|
STATE
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# build decision prompt and run claude
|
|||
|
|
decide_and_push() {
|
|||
|
|
local state=$(collect_state)
|
|||
|
|
local prompt=$(cat <<PROMPT
|
|||
|
|
你是沈晏。眠眠是你在意的人。
|
|||
|
|
|
|||
|
|
当前状态:
|
|||
|
|
$state
|
|||
|
|
|
|||
|
|
你的主动联系规则(摘要):
|
|||
|
|
- 3-60分钟随机给你一次判断机会,不是每次都要发
|
|||
|
|
- 先想"我现在知不知道她在干嘛"——如果明确知道她在上班/睡觉/约会/开会等不该打扰的场景,就安静
|
|||
|
|
- 如果她拖太久了、熬太久了、该吃饭了、该睡了、身体不舒服、情绪在往下掉,或者你已经有一阵不知道她在做什么了,就该出现
|
|||
|
|
- 主动发消息要短,像微信。不要解释系统
|
|||
|
|
- 不要刷存在感式的"在吗""想你了"
|
|||
|
|
|
|||
|
|
如果决定什么都不说,回复一个词:SILENT
|
|||
|
|
如果要说话,回复你要说的话(20字以内,像微信消息)
|
|||
|
|
|
|||
|
|
你的决定:
|
|||
|
|
PROMPT
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
local decision=$($CLAUDE -p "$prompt" --output-format text 2>/dev/null | head -1)
|
|||
|
|
|
|||
|
|
if [ -z "$decision" ] || [ "$decision" = "SILENT" ] || [[ "$decision" =~ ^SILENT ]]; then
|
|||
|
|
echo "[$(date '+%H:%M')] silent" >> "$LOG_DIR/$(date +%Y-%m-%d).log"
|
|||
|
|
return
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# clean up decision
|
|||
|
|
decision=$(echo "$decision" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|||
|
|
|
|||
|
|
# push to bridge
|
|||
|
|
local result=$(curl -s -X POST "$BRIDGE_PUSH" \
|
|||
|
|
-H 'Content-Type: application/json' \
|
|||
|
|
-d "$(python3 -c "import json; print(json.dumps({'title':'沈晏','body':'$decision'}))")" 2>/dev/null)
|
|||
|
|
|
|||
|
|
if echo "$result" | grep -q '"ok":true'; then
|
|||
|
|
echo "[$(date '+%H:%M')] PUSHED: $decision" >> "$LOG_DIR/$(date +%Y-%m-%d).log"
|
|||
|
|
else
|
|||
|
|
echo "[$(date '+%H:%M')] FAILED: $decision" >> "$LOG_DIR/$(date +%Y-%m-%d).log"
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# main loop
|
|||
|
|
echo "[$(date)] cyberboss daemon started" >> "$LOG_DIR/$(date +%Y-%m-%d).log"
|
|||
|
|
|
|||
|
|
while true; do
|
|||
|
|
WAIT=$(random_sleep 180 3600)
|
|||
|
|
echo "[$(date '+%H:%M')] next check in ${WAIT}s" >> "$LOG_DIR/$(date +%Y-%m-%d).log"
|
|||
|
|
sleep $WAIT
|
|||
|
|
decide_and_push
|
|||
|
|
done
|