mrstack 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mrstack/__init__.py +4 -0
- mrstack/_data/config/com.mrstack.claude-telegram.plist +25 -0
- mrstack/_data/config/mcp-config.example.json +23 -0
- mrstack/_data/config/start-daemon.sh +53 -0
- mrstack/_data/config/start.sh +29 -0
- mrstack/_data/schedulers/manage-jobs.sh +87 -0
- mrstack/_data/schedulers/morning-briefing.sh +29 -0
- mrstack/_data/schedulers/register-jobs.py +182 -0
- mrstack/_data/schedulers/run-threads-briefing.sh +36 -0
- mrstack/_data/schedulers/weekly-review.sh +26 -0
- mrstack/_data/templates/DESIGN-GUIDE.md +160 -0
- mrstack/_data/templates/alert.md +56 -0
- mrstack/_data/templates/evening-summary.md +73 -0
- mrstack/_data/templates/jarvis-alert.md +64 -0
- mrstack/_data/templates/morning-briefing.md +53 -0
- mrstack/_data/templates/weekly-review.md +79 -0
- mrstack/_overlay/api/dashboard.py +223 -0
- mrstack/_overlay/api/templates/dashboard.html +328 -0
- mrstack/_overlay/bot/handlers/callback.py +1432 -0
- mrstack/_overlay/bot/handlers/command.py +1541 -0
- mrstack/_overlay/bot/utils/keyboards.py +125 -0
- mrstack/_overlay/bot/utils/ui_components.py +166 -0
- mrstack/_overlay/claude/session.py +341 -0
- mrstack/_overlay/jarvis/__init__.py +77 -0
- mrstack/_overlay/jarvis/coach.py +122 -0
- mrstack/_overlay/jarvis/context_engine.py +463 -0
- mrstack/_overlay/jarvis/pattern_learner.py +255 -0
- mrstack/_overlay/jarvis/persona.py +84 -0
- mrstack/_overlay/jarvis/platform.py +182 -0
- mrstack/_overlay/knowledge/__init__.py +6 -0
- mrstack/_overlay/knowledge/manager.py +464 -0
- mrstack/_overlay/knowledge/memory_index.py +180 -0
- mrstack/cli.py +330 -0
- mrstack/constants.py +77 -0
- mrstack/daemon.py +325 -0
- mrstack/patcher.py +169 -0
- mrstack/wizard.py +271 -0
- mrstack-1.1.0.dist-info/METADATA +640 -0
- mrstack-1.1.0.dist-info/RECORD +42 -0
- mrstack-1.1.0.dist-info/WHEEL +4 -0
- mrstack-1.1.0.dist-info/entry_points.txt +2 -0
- mrstack-1.1.0.dist-info/licenses/LICENSE +21 -0
mrstack/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>Label</key>
|
|
6
|
+
<string>com.mrstack.claude-telegram</string>
|
|
7
|
+
<key>ProgramArguments</key>
|
|
8
|
+
<array>
|
|
9
|
+
<string>/bin/bash</string>
|
|
10
|
+
<string>$HOME/claude-telegram/start-daemon.sh</string>
|
|
11
|
+
</array>
|
|
12
|
+
<key>RunAtLoad</key>
|
|
13
|
+
<true/>
|
|
14
|
+
<key>KeepAlive</key>
|
|
15
|
+
<true/>
|
|
16
|
+
<key>ThrottleInterval</key>
|
|
17
|
+
<integer>30</integer>
|
|
18
|
+
<key>StandardOutPath</key>
|
|
19
|
+
<string>$HOME/claude-telegram/logs/daemon-stdout.log</string>
|
|
20
|
+
<key>StandardErrorPath</key>
|
|
21
|
+
<string>$HOME/claude-telegram/logs/daemon-stderr.log</string>
|
|
22
|
+
<key>WorkingDirectory</key>
|
|
23
|
+
<string>$HOME/claude-telegram</string>
|
|
24
|
+
</dict>
|
|
25
|
+
</plist>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"google-calendar": {
|
|
4
|
+
"type": "http",
|
|
5
|
+
"url": "https://gcal.mcp.claude.com/mcp"
|
|
6
|
+
},
|
|
7
|
+
"notion": {
|
|
8
|
+
"command": "npx",
|
|
9
|
+
"args": ["-y", "@notionhq/notion-mcp-server"],
|
|
10
|
+
"env": {
|
|
11
|
+
"OPENAPI_MCP_HEADERS": "{\"Authorization\": \"Bearer YOUR_NOTION_API_KEY\", \"Notion-Version\": \"2022-06-28\"}"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"playwright": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "@playwright/mcp"]
|
|
17
|
+
},
|
|
18
|
+
"applescript": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "@peakmojo/applescript-mcp"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Daemon startup script for launchd (foreground execution)
|
|
3
|
+
# Use start.sh for manual/tmux execution instead
|
|
4
|
+
|
|
5
|
+
cd "$HOME/claude-telegram"
|
|
6
|
+
|
|
7
|
+
# Load environment variables
|
|
8
|
+
set -a
|
|
9
|
+
source .env
|
|
10
|
+
set +a
|
|
11
|
+
|
|
12
|
+
# Ensure claude CLI and other tools are in PATH
|
|
13
|
+
export PATH="$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
14
|
+
|
|
15
|
+
# Sleep prevention: only on AC power
|
|
16
|
+
# Bot always runs; caffeinate runs alongside only when on AC.
|
|
17
|
+
# A background loop checks power state every 60s and toggles caffeinate.
|
|
18
|
+
|
|
19
|
+
CAFF_PID=""
|
|
20
|
+
|
|
21
|
+
cleanup() {
|
|
22
|
+
[ -n "$CAFF_PID" ] && kill "$CAFF_PID" 2>/dev/null
|
|
23
|
+
kill "$BOT_PID" 2>/dev/null
|
|
24
|
+
wait
|
|
25
|
+
exit 0
|
|
26
|
+
}
|
|
27
|
+
trap cleanup SIGTERM SIGINT
|
|
28
|
+
|
|
29
|
+
is_on_ac() {
|
|
30
|
+
pmset -g batt | grep -q "AC Power"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Start the bot in background
|
|
34
|
+
claude-telegram-bot &
|
|
35
|
+
BOT_PID=$!
|
|
36
|
+
|
|
37
|
+
# Power monitor loop
|
|
38
|
+
while kill -0 "$BOT_PID" 2>/dev/null; do
|
|
39
|
+
if is_on_ac; then
|
|
40
|
+
if [ -z "$CAFF_PID" ] || ! kill -0 "$CAFF_PID" 2>/dev/null; then
|
|
41
|
+
caffeinate -s -i -w "$BOT_PID" &
|
|
42
|
+
CAFF_PID=$!
|
|
43
|
+
fi
|
|
44
|
+
else
|
|
45
|
+
if [ -n "$CAFF_PID" ] && kill -0 "$CAFF_PID" 2>/dev/null; then
|
|
46
|
+
kill "$CAFF_PID" 2>/dev/null
|
|
47
|
+
CAFF_PID=""
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
sleep 60
|
|
51
|
+
done
|
|
52
|
+
|
|
53
|
+
wait "$BOT_PID"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
cd "$HOME/claude-telegram"
|
|
3
|
+
|
|
4
|
+
# Load env for webhook secret
|
|
5
|
+
set -a
|
|
6
|
+
source .env
|
|
7
|
+
set +a
|
|
8
|
+
|
|
9
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
10
|
+
|
|
11
|
+
# Start bot in background, wait for health check, then notify
|
|
12
|
+
claude-telegram-bot &
|
|
13
|
+
BOT_PID=$!
|
|
14
|
+
|
|
15
|
+
# Wait for API server to be ready (max 30 seconds)
|
|
16
|
+
for i in $(seq 1 30); do
|
|
17
|
+
if curl -s http://localhost:8080/health >/dev/null 2>&1; then
|
|
18
|
+
# Send startup notification via webhook
|
|
19
|
+
curl -s -X POST http://localhost:8080/webhooks/system \
|
|
20
|
+
-H "Authorization: Bearer $WEBHOOK_API_SECRET" \
|
|
21
|
+
-H "Content-Type: application/json" \
|
|
22
|
+
-d '{"event": "bot_started", "message": "Bot online and ready."}' >/dev/null 2>&1
|
|
23
|
+
break
|
|
24
|
+
fi
|
|
25
|
+
sleep 1
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
# Wait for bot process
|
|
29
|
+
wait $BOT_PID
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Manage scheduled jobs in the bot's SQLite database
|
|
3
|
+
DB="$HOME/claude-telegram/data/bot.db"
|
|
4
|
+
|
|
5
|
+
usage() {
|
|
6
|
+
echo "Usage: $0 {list|add|remove|toggle|update-cron}"
|
|
7
|
+
echo ""
|
|
8
|
+
echo " list List all registered jobs"
|
|
9
|
+
echo " add <name> <cron> <prompt> Add a new job"
|
|
10
|
+
echo " remove <job-name> Remove a job by name"
|
|
11
|
+
echo " toggle <job-name> Toggle job active/inactive"
|
|
12
|
+
echo " update-cron <job-name> <cron> Update a job's cron expression"
|
|
13
|
+
echo ""
|
|
14
|
+
echo "Note: Restart bot after changes for scheduler to reload."
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
case "${1:-}" in
|
|
18
|
+
list)
|
|
19
|
+
echo "=== Scheduled Jobs ==="
|
|
20
|
+
sqlite3 -header -column "$DB" \
|
|
21
|
+
"SELECT job_name, cron_expression, is_active,
|
|
22
|
+
substr(prompt, 1, 50) || '...' as prompt_preview
|
|
23
|
+
FROM scheduled_jobs
|
|
24
|
+
ORDER BY job_name"
|
|
25
|
+
echo ""
|
|
26
|
+
echo "Total: $(sqlite3 "$DB" "SELECT COUNT(*) FROM scheduled_jobs") jobs"
|
|
27
|
+
echo "Active: $(sqlite3 "$DB" "SELECT COUNT(*) FROM scheduled_jobs WHERE is_active=1") jobs"
|
|
28
|
+
;;
|
|
29
|
+
|
|
30
|
+
add)
|
|
31
|
+
if [ $# -lt 4 ]; then
|
|
32
|
+
echo "Usage: $0 add <name> <cron> <prompt>"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
JOB_NAME="$2"
|
|
36
|
+
CRON="$3"
|
|
37
|
+
PROMPT="$4"
|
|
38
|
+
JOB_ID=$(python3 -c "import uuid; print(uuid.uuid4())")
|
|
39
|
+
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
40
|
+
|
|
41
|
+
sqlite3 "$DB" "INSERT INTO scheduled_jobs
|
|
42
|
+
(job_id, job_name, cron_expression, prompt, target_chat_ids, working_directory, is_active, created_at, updated_at)
|
|
43
|
+
VALUES ('$JOB_ID', '$JOB_NAME', '$CRON', '$PROMPT', 'YOUR_CHAT_ID', '$HOME', 1, '$NOW', '$NOW')"
|
|
44
|
+
echo "Created: $JOB_NAME ($CRON) -> $JOB_ID"
|
|
45
|
+
echo "Restart bot to apply."
|
|
46
|
+
;;
|
|
47
|
+
|
|
48
|
+
remove)
|
|
49
|
+
if [ $# -lt 2 ]; then
|
|
50
|
+
echo "Usage: $0 remove <job-name>"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
JOB_NAME="$2"
|
|
54
|
+
sqlite3 "$DB" "DELETE FROM scheduled_jobs WHERE job_name='$JOB_NAME'"
|
|
55
|
+
echo "Removed: $JOB_NAME"
|
|
56
|
+
echo "Restart bot to apply."
|
|
57
|
+
;;
|
|
58
|
+
|
|
59
|
+
toggle)
|
|
60
|
+
if [ $# -lt 2 ]; then
|
|
61
|
+
echo "Usage: $0 toggle <job-name>"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
JOB_NAME="$2"
|
|
65
|
+
sqlite3 "$DB" "UPDATE scheduled_jobs SET is_active = NOT is_active WHERE job_name='$JOB_NAME'"
|
|
66
|
+
STATUS=$(sqlite3 "$DB" "SELECT CASE WHEN is_active THEN 'ACTIVE' ELSE 'INACTIVE' END FROM scheduled_jobs WHERE job_name='$JOB_NAME'")
|
|
67
|
+
echo "$JOB_NAME -> $STATUS"
|
|
68
|
+
echo "Restart bot to apply."
|
|
69
|
+
;;
|
|
70
|
+
|
|
71
|
+
update-cron)
|
|
72
|
+
if [ $# -lt 3 ]; then
|
|
73
|
+
echo "Usage: $0 update-cron <job-name> <new-cron>"
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
JOB_NAME="$2"
|
|
77
|
+
NEW_CRON="$3"
|
|
78
|
+
sqlite3 "$DB" "UPDATE scheduled_jobs SET cron_expression='$NEW_CRON' WHERE job_name='$JOB_NAME'"
|
|
79
|
+
echo "$JOB_NAME cron -> $NEW_CRON"
|
|
80
|
+
echo "Restart bot to apply."
|
|
81
|
+
;;
|
|
82
|
+
|
|
83
|
+
*)
|
|
84
|
+
usage
|
|
85
|
+
exit 1
|
|
86
|
+
;;
|
|
87
|
+
esac
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
export PATH="$HOME/.local/bin:/opt/homebrew/bin:$PATH"
|
|
3
|
+
|
|
4
|
+
set -a
|
|
5
|
+
source "$HOME/claude-telegram/.env"
|
|
6
|
+
set +a
|
|
7
|
+
|
|
8
|
+
# Use Claude Code to generate briefing
|
|
9
|
+
CLAUDECODE= claude --print --output-format text "
|
|
10
|
+
You are DD's personal assistant. Generate a morning briefing.
|
|
11
|
+
Check:
|
|
12
|
+
1. Today's calendar events (if Google Calendar MCP available)
|
|
13
|
+
2. Unread important emails (if Gmail MCP available)
|
|
14
|
+
3. Pending issues/tasks (if Linear/Jira MCP available)
|
|
15
|
+
4. Unread Slack mentions (if Slack MCP available)
|
|
16
|
+
5. Latest AI news from ~/claude-telegram/scrapers/threads/output/
|
|
17
|
+
|
|
18
|
+
Format as a concise Telegram message in Korean.
|
|
19
|
+
Start with: 🌅 오전 브리핑 (date)
|
|
20
|
+
" 2>/dev/null > /tmp/morning-briefing.txt
|
|
21
|
+
|
|
22
|
+
# Send via Telegram
|
|
23
|
+
MSG=$(cat /tmp/morning-briefing.txt)
|
|
24
|
+
if [ -n "$MSG" ]; then
|
|
25
|
+
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
|
26
|
+
-H "Content-Type: application/json" \
|
|
27
|
+
-d "{\"chat_id\":\"${NOTIFICATION_CHAT_IDS}\",\"text\":$(echo "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')}" \
|
|
28
|
+
> /dev/null
|
|
29
|
+
fi
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Register scheduled jobs into the bot's SQLite database."""
|
|
3
|
+
|
|
4
|
+
import sqlite3
|
|
5
|
+
import uuid
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
DB_PATH = os.path.expanduser("~/claude-telegram/data/bot.db")
|
|
11
|
+
CHAT_ID = os.environ.get("NOTIFICATION_CHAT_IDS", "YOUR_CHAT_ID")
|
|
12
|
+
WORKING_DIR = os.path.expanduser("~")
|
|
13
|
+
|
|
14
|
+
# Model routing:
|
|
15
|
+
# "sonnet" — simple lookups, status checks, no creative work
|
|
16
|
+
# "opus" — complex analysis, writing, multi-step reasoning
|
|
17
|
+
# None — inherit CLI default (opus)
|
|
18
|
+
|
|
19
|
+
JOBS = [
|
|
20
|
+
{
|
|
21
|
+
"job_name": "morning-briefing",
|
|
22
|
+
"cron_expression": "0 8 * * 1-5",
|
|
23
|
+
"model": "sonnet",
|
|
24
|
+
"prompt": """아침 브리핑:
|
|
25
|
+
1. 오늘 날짜/요일
|
|
26
|
+
2. Google Calendar MCP로 오늘 일정
|
|
27
|
+
3. ~/claude-telegram/memory/ 어제 daily 요약 + pending
|
|
28
|
+
4. ~/claude-telegram/scrapers/threads/output/ 최신 AI 뉴스 (1파일)
|
|
29
|
+
5. 오늘 할 일 제안
|
|
30
|
+
|
|
31
|
+
~/claude-telegram/templates/morning-briefing.md 템플릿 참고.""",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"job_name": "evening-summary",
|
|
35
|
+
"cron_expression": "0 21 * * *",
|
|
36
|
+
"model": "opus",
|
|
37
|
+
"prompt": """일일 요약 작성 + 저장:
|
|
38
|
+
1. 오늘 대화 핵심 정리
|
|
39
|
+
2. 결정사항, 완료 작업, 새 정보
|
|
40
|
+
3. 내일 할 일
|
|
41
|
+
4. ~/claude-telegram/memory/daily/YYYY-MM-DD.md 저장
|
|
42
|
+
5. decisions/log.md 업데이트 (있으면)
|
|
43
|
+
6. projects/ 해당 파일 업데이트 (있으면)
|
|
44
|
+
7. [Coach] ~/claude-telegram/memory/patterns/interactions.jsonl 분석:
|
|
45
|
+
- 총 요청 수, 유형 분포, 피크 시간대
|
|
46
|
+
- 디버깅 비율 지적, 컨텍스트 전환 패턴
|
|
47
|
+
- 코칭 포인트 2-3개 (interactions.jsonl 없으면 건너뛰기)
|
|
48
|
+
|
|
49
|
+
파일 저장 후 텔레그램으로 요약 전송. ~/claude-telegram/templates/morning-briefing.md 형식 참고.""",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"job_name": "weekly-review",
|
|
53
|
+
"cron_expression": "0 18 * * 5",
|
|
54
|
+
"model": "opus",
|
|
55
|
+
"prompt": """주간 회고:
|
|
56
|
+
1. ~/claude-telegram/memory/daily/ 이번 주 파일들
|
|
57
|
+
2. 성과 요약
|
|
58
|
+
3. 주요 결정사항 + 이유
|
|
59
|
+
4. 배운 것 / 개선점
|
|
60
|
+
5. 다음 주 계획
|
|
61
|
+
|
|
62
|
+
~/claude-telegram/templates/weekly-review.md 템플릿 참고.""",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"job_name": "calendar-check",
|
|
66
|
+
"cron_expression": "0 9,12,15,18 * * 1-5",
|
|
67
|
+
"model": "haiku",
|
|
68
|
+
"prompt": """Google Calendar MCP로 3시간 내 일정 확인.
|
|
69
|
+
있으면: 시간, 제목, 장소, 준비사항.
|
|
70
|
+
없으면 빈 응답.""",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"job_name": "threads-notify",
|
|
74
|
+
"cron_expression": "30 8,20 * * *",
|
|
75
|
+
"model": "sonnet",
|
|
76
|
+
"prompt": """~/claude-telegram/scrapers/threads/output/ 최신 JSON 분석:
|
|
77
|
+
1. 최근 1파일의 AI/테크 포스트 요약 (최대 5개)
|
|
78
|
+
2. 포스트별: 주제, 핵심 1-2줄, 링크
|
|
79
|
+
3. 트렌드 한줄 요약
|
|
80
|
+
4. Notion MCP → "AI 업무 로그" DB(data_source_id: 14e322c2-110e-4cce-9a22-f2c529abb54e) 저장:
|
|
81
|
+
제목: "[Threads] YYYY-MM-DD 오전/오후 AI 뉴스", 카테고리: "학습"
|
|
82
|
+
(Notion 불가시 건너뛰기)
|
|
83
|
+
|
|
84
|
+
새 파일 없거나 이미 브리핑한 파일이면 빈 응답.""",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"job_name": "memory-sync",
|
|
88
|
+
"cron_expression": "0 */3 * * *",
|
|
89
|
+
"model": "sonnet",
|
|
90
|
+
"prompt": """메모리 동기화 — 최근 대화에서 추출:
|
|
91
|
+
- 새 사람 → ~/claude-telegram/memory/people/{name}.md
|
|
92
|
+
- 프로젝트 → projects/{name}.md
|
|
93
|
+
- 선호도 → preferences/
|
|
94
|
+
- 지식 → knowledge/
|
|
95
|
+
- 결정 → decisions/log.md""",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"job_name": "notion-sync",
|
|
99
|
+
"cron_expression": "0 22 * * *",
|
|
100
|
+
"model": "sonnet",
|
|
101
|
+
"prompt": """오늘 중요 대화/결정을 Notion 저장:
|
|
102
|
+
1. 기술 결정, 진행상황, 학습 내용 추출
|
|
103
|
+
2. Notion MCP → "AI 업무 로그" DB(data_source_id: 14e322c2-110e-4cce-9a22-f2c529abb54e)
|
|
104
|
+
3. properties: 제목, 카테고리(결정/진행/학습/이슈/아이디어), 날짜, 요약
|
|
105
|
+
4. Notion 불가시 ~/claude-telegram/memory/daily/ 기록 + 상태 알림""",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"job_name": "github-check",
|
|
109
|
+
"cron_expression": "0 */2 * * *",
|
|
110
|
+
"model": "haiku",
|
|
111
|
+
"prompt": """`gh api notifications` 로 읽지 않은 알림 확인.
|
|
112
|
+
새 PR/이슈/코멘트 요약. PR review, 멘션, CI 실패 강조.""",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"job_name": "token-check",
|
|
116
|
+
"cron_expression": "0 10 * * *",
|
|
117
|
+
"model": "haiku",
|
|
118
|
+
"prompt": """~/.claude/.credentials.json 토큰 확인.
|
|
119
|
+
만료 7일 이내면 경고. 만료/파일없음이면 "claude CLI 실행하여 갱신" 알림.""",
|
|
120
|
+
},
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def register():
|
|
125
|
+
conn = sqlite3.connect(DB_PATH)
|
|
126
|
+
cursor = conn.cursor()
|
|
127
|
+
|
|
128
|
+
for job in JOBS:
|
|
129
|
+
# Check if job already exists
|
|
130
|
+
cursor.execute(
|
|
131
|
+
"SELECT job_id FROM scheduled_jobs WHERE job_name = ?",
|
|
132
|
+
(job["job_name"],),
|
|
133
|
+
)
|
|
134
|
+
existing = cursor.fetchone()
|
|
135
|
+
|
|
136
|
+
if existing:
|
|
137
|
+
# Update existing job
|
|
138
|
+
cursor.execute(
|
|
139
|
+
"""UPDATE scheduled_jobs
|
|
140
|
+
SET cron_expression = ?, prompt = ?, target_chat_ids = ?,
|
|
141
|
+
working_directory = ?, model = ?, is_active = 1, updated_at = ?
|
|
142
|
+
WHERE job_name = ?""",
|
|
143
|
+
(
|
|
144
|
+
job["cron_expression"],
|
|
145
|
+
job["prompt"],
|
|
146
|
+
CHAT_ID,
|
|
147
|
+
WORKING_DIR,
|
|
148
|
+
job.get("model"),
|
|
149
|
+
datetime.now().isoformat(),
|
|
150
|
+
job["job_name"],
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
print(f"Updated: {job['job_name']} ({job['cron_expression']}) model={job.get('model', 'default')}")
|
|
154
|
+
else:
|
|
155
|
+
# Insert new job
|
|
156
|
+
job_id = str(uuid.uuid4())
|
|
157
|
+
cursor.execute(
|
|
158
|
+
"""INSERT INTO scheduled_jobs
|
|
159
|
+
(job_id, job_name, cron_expression, prompt, target_chat_ids,
|
|
160
|
+
working_directory, model, is_active, created_at, updated_at)
|
|
161
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?)""",
|
|
162
|
+
(
|
|
163
|
+
job_id,
|
|
164
|
+
job["job_name"],
|
|
165
|
+
job["cron_expression"],
|
|
166
|
+
job["prompt"],
|
|
167
|
+
CHAT_ID,
|
|
168
|
+
WORKING_DIR,
|
|
169
|
+
job.get("model"),
|
|
170
|
+
datetime.now().isoformat(),
|
|
171
|
+
datetime.now().isoformat(),
|
|
172
|
+
),
|
|
173
|
+
)
|
|
174
|
+
print(f"Created: {job['job_name']} ({job['cron_expression']}) model={job.get('model', 'default')} -> {job_id}")
|
|
175
|
+
|
|
176
|
+
conn.commit()
|
|
177
|
+
conn.close()
|
|
178
|
+
print(f"\nDone. {len(JOBS)} jobs registered.")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
register()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
export PATH="$HOME/.local/bin:/opt/homebrew/bin:$PATH"
|
|
3
|
+
cd "$HOME/claude-telegram/scrapers/threads"
|
|
4
|
+
|
|
5
|
+
# Source env
|
|
6
|
+
set -a
|
|
7
|
+
source "$HOME/claude-telegram/.env"
|
|
8
|
+
set +a
|
|
9
|
+
|
|
10
|
+
# Run scraper
|
|
11
|
+
node scraper.js 2>&1 | tee -a "$HOME/claude-telegram/logs/threads.log"
|
|
12
|
+
|
|
13
|
+
# Get latest output file
|
|
14
|
+
LATEST=$(ls -t output/*.json 2>/dev/null | head -1)
|
|
15
|
+
|
|
16
|
+
if [ -n "$LATEST" ]; then
|
|
17
|
+
POST_COUNT=$(python3 -c "import json; print(len(json.load(open('$LATEST'))))" 2>/dev/null || echo "0")
|
|
18
|
+
|
|
19
|
+
# Send to bot's webhook API for Claude-powered analysis
|
|
20
|
+
WEBHOOK_RESULT=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
21
|
+
-X POST http://localhost:8080/webhooks/threads \
|
|
22
|
+
-H "Authorization: Bearer $WEBHOOK_API_SECRET" \
|
|
23
|
+
-H "Content-Type: application/json" \
|
|
24
|
+
-d "{\"file\": \"$LATEST\", \"count\": $POST_COUNT}" 2>/dev/null)
|
|
25
|
+
|
|
26
|
+
if [ "$WEBHOOK_RESULT" = "200" ] || [ "$WEBHOOK_RESULT" = "202" ]; then
|
|
27
|
+
echo "Webhook sent successfully (HTTP $WEBHOOK_RESULT)" | tee -a "$HOME/claude-telegram/logs/threads.log"
|
|
28
|
+
else
|
|
29
|
+
echo "Webhook failed (HTTP $WEBHOOK_RESULT), falling back to direct briefing" | tee -a "$HOME/claude-telegram/logs/threads.log"
|
|
30
|
+
# Fallback: direct briefing
|
|
31
|
+
node briefing.js "$LATEST" 2>&1 | tee -a "$HOME/claude-telegram/logs/threads.log"
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Save to Notion via Claude Code
|
|
35
|
+
bash "$HOME/claude-telegram/scrapers/threads/save-to-notion.sh" "$LATEST" 2>&1 | tee -a "$HOME/claude-telegram/logs/threads.log"
|
|
36
|
+
fi
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
export PATH="$HOME/.local/bin:/opt/homebrew/bin:$PATH"
|
|
3
|
+
|
|
4
|
+
set -a
|
|
5
|
+
source "$HOME/claude-telegram/.env"
|
|
6
|
+
set +a
|
|
7
|
+
|
|
8
|
+
CLAUDECODE= claude --print --output-format text "
|
|
9
|
+
You are DD's personal assistant. Generate a weekly review.
|
|
10
|
+
Check:
|
|
11
|
+
1. This week's completed tasks and accomplishments
|
|
12
|
+
2. Next week's scheduled events from Google Calendar
|
|
13
|
+
3. AI trends summary from ~/claude-telegram/scrapers/threads/output/ (this week's files)
|
|
14
|
+
4. Any pending items that need attention
|
|
15
|
+
|
|
16
|
+
Format as a concise Telegram message in Korean.
|
|
17
|
+
Start with: 📊 주간 회고 (date range)
|
|
18
|
+
" 2>/dev/null > /tmp/weekly-review.txt
|
|
19
|
+
|
|
20
|
+
MSG=$(cat /tmp/weekly-review.txt)
|
|
21
|
+
if [ -n "$MSG" ]; then
|
|
22
|
+
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
|
23
|
+
-H "Content-Type: application/json" \
|
|
24
|
+
-d "{\"chat_id\":\"${NOTIFICATION_CHAT_IDS}\",\"text\":$(echo "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')}" \
|
|
25
|
+
> /dev/null
|
|
26
|
+
fi
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Mr.Stack Message Design Guide
|
|
2
|
+
|
|
3
|
+
Mr.Stack 텔레그램 봇의 메시지 디자인 시스템 규칙.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 메시지 구조
|
|
8
|
+
|
|
9
|
+
### 헤더/푸터 구분선
|
|
10
|
+
- 상단: `━━━ Mr.Stack {제목} ━━━`
|
|
11
|
+
- 하단: `━━━━━━━━━━━━━━━━━━━━` (━ x 20)
|
|
12
|
+
- 문자: ━ (U+2501, BOX DRAWINGS HEAVY HORIZONTAL)
|
|
13
|
+
- Jarvis 알림은 구분선 없이 `[Mr.Stack]` prefix만 사용
|
|
14
|
+
|
|
15
|
+
### 섹션 prefix
|
|
16
|
+
- `▸` (U+25B8, BLACK RIGHT-POINTING SMALL TRIANGLE)
|
|
17
|
+
- 섹션 제목 앞에 사용: `▸ 오늘 일정`
|
|
18
|
+
- 텔레그램 HTML: `<b>▸ 섹션 제목</b>`
|
|
19
|
+
|
|
20
|
+
### 들여쓰기
|
|
21
|
+
- 섹션 내용은 공백 2칸 들여쓰기
|
|
22
|
+
- 일정/태스크 항목 정렬 유지
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2. 프로그레스 바
|
|
27
|
+
|
|
28
|
+
### 기본 규칙
|
|
29
|
+
- 총 10칸 고정
|
|
30
|
+
- 채워진 칸: █ (U+2588, FULL BLOCK)
|
|
31
|
+
- 빈 칸: ░ (U+2591, LIGHT SHADE)
|
|
32
|
+
|
|
33
|
+
### 변환 공식
|
|
34
|
+
```
|
|
35
|
+
filled = round(value / max_value * 10)
|
|
36
|
+
empty = 10 - filled
|
|
37
|
+
bar = "█" * filled + "░" * empty
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 예시
|
|
41
|
+
| 값 | 바 | 표기 |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| 0% | ░░░░░░░░░░ | 0/10 |
|
|
44
|
+
| 30% | ███░░░░░░░ | 3/10 |
|
|
45
|
+
| 50% | █████░░░░░ | 5/10 |
|
|
46
|
+
| 80% | ████████░░ | 8/10 |
|
|
47
|
+
| 100% | ██████████ | 10/10 |
|
|
48
|
+
|
|
49
|
+
### 용도별
|
|
50
|
+
- **생산성 스코어**: value=score, max=10
|
|
51
|
+
- **시간 분석**: value=hours, max=total_working_hours
|
|
52
|
+
- **프로젝트 비율**: value=percentage, max=100
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 3. 상태 이모지 매핑
|
|
57
|
+
|
|
58
|
+
| 기호 | 의미 | 용도 |
|
|
59
|
+
|------|------|------|
|
|
60
|
+
| ✓ | 완료/성공 | 완료된 태스크, 성공한 작업 |
|
|
61
|
+
| ✗ | 실패/에러 | 실패한 빌드, 에러 발생 |
|
|
62
|
+
| ◻ | 미완료/대기 | Pending 태스크, 체크리스트 |
|
|
63
|
+
| · | 정보/항목 | 뉴스, 리스트 아이템, 일반 나열 |
|
|
64
|
+
| — | 없음/구분 | "일정 없음", 설명 구분 |
|
|
65
|
+
|
|
66
|
+
### 알림 등급 이모지
|
|
67
|
+
|
|
68
|
+
| 이모지 | 등급 | 사용 상황 |
|
|
69
|
+
|--------|------|-----------|
|
|
70
|
+
| 🔴 | 긴급 | 즉시 조치 (서비스 장애, 보안 이슈, 배터리 < 10%) |
|
|
71
|
+
| 🟡 | 주의 | 확인 필요 (성능 저하, 이상 패턴, 배터리 < 30%) |
|
|
72
|
+
| 🟢 | 정보 | 참고용 (작업 완료, 상태 변경) |
|
|
73
|
+
| ⚪ | 시스템 | 내부 상태 (토큰 갱신, 동기화, 메모리 업데이트) |
|
|
74
|
+
|
|
75
|
+
### 카테고리 이모지
|
|
76
|
+
|
|
77
|
+
| 이모지 | 용도 |
|
|
78
|
+
|--------|------|
|
|
79
|
+
| 📅 | 날짜, 일정 |
|
|
80
|
+
| 📊 | 통계, 분석, 리포트 |
|
|
81
|
+
| 💡 | 코칭, 인사이트, 팁 |
|
|
82
|
+
| 🏷️ | 키워드, 태그 |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 4. Jarvis 선제 알림 포맷
|
|
87
|
+
|
|
88
|
+
### 구조
|
|
89
|
+
```
|
|
90
|
+
[Mr.Stack] {메시지}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 규칙
|
|
94
|
+
- prefix: 반드시 `[Mr.Stack]`으로 시작
|
|
95
|
+
- 길이: 한 줄, 최대 100자
|
|
96
|
+
- 조치 필요 시: `—` (em dash) 뒤에 구체적 행동 제안
|
|
97
|
+
- 존댓말 사용, 간결하게
|
|
98
|
+
- 이모지 사용 금지 (prefix가 브랜딩 역할)
|
|
99
|
+
|
|
100
|
+
### 텔레그램 HTML
|
|
101
|
+
```html
|
|
102
|
+
<b>[Mr.Stack]</b> {메시지}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
강조가 필요한 수치:
|
|
106
|
+
```html
|
|
107
|
+
<b>[Mr.Stack]</b> 배터리 <b>12%</b> — 저장하시고 충전하세요.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 알림음 정책
|
|
111
|
+
- 긴급 (배터리 < 10%, 장애): `disable_notification=false`
|
|
112
|
+
- 일반 (정보, 상태): `disable_notification=true`
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 5. 텔레그램 HTML 호환 태그
|
|
117
|
+
|
|
118
|
+
텔레그램 Bot API `parse_mode=HTML`에서 지원하는 태그:
|
|
119
|
+
|
|
120
|
+
| 태그 | 용도 | 예시 |
|
|
121
|
+
|------|------|------|
|
|
122
|
+
| `<b>text</b>` | 굵게 | 섹션 제목, 강조 수치 |
|
|
123
|
+
| `<i>text</i>` | 기울임 | 코칭 라인, 키워드 |
|
|
124
|
+
| `<code>text</code>` | 인라인 코드 | 변수명, 명령어, 브랜치명 |
|
|
125
|
+
| `<pre>text</pre>` | 코드 블록 | 멀티라인 코드 |
|
|
126
|
+
| `<a href="url">text</a>` | 링크 | 외부 URL |
|
|
127
|
+
| `<s>text</s>` | 취소선 | 취소된 항목 |
|
|
128
|
+
| `<u>text</u>` | 밑줄 | 특별 강조 (절제해서 사용) |
|
|
129
|
+
| `<tg-spoiler>text</tg-spoiler>` | 스포일러 | 숨김 처리 |
|
|
130
|
+
| `<blockquote>text</blockquote>` | 인용 | 인용문 |
|
|
131
|
+
|
|
132
|
+
### 주의사항
|
|
133
|
+
- `<`, `>`, `&` 문자는 반드시 이스케이프: `<`, `>`, `&`
|
|
134
|
+
- 지원하지 않는 태그 사용 시 메시지 전송 실패
|
|
135
|
+
- 태그 중첩 가능하나 최소한으로 사용
|
|
136
|
+
- Markdown 파싱 모드와 혼용 불가 — HTML 모드만 사용
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 6. 템플릿 목록
|
|
141
|
+
|
|
142
|
+
| 파일 | 용도 | 발송 시점 |
|
|
143
|
+
|------|------|-----------|
|
|
144
|
+
| morning-briefing.md | 모닝 브리핑 | 평일 08:00 |
|
|
145
|
+
| evening-summary.md | 데일리 리포트 | 매일 21:00 |
|
|
146
|
+
| weekly-review.md | 주간 회고 | 금요일 18:00 |
|
|
147
|
+
| alert.md | 시스템/서비스 알림 | 이벤트 발생 시 |
|
|
148
|
+
| jarvis-alert.md | Jarvis 선제 알림 | ContextEngine 트리거 시 |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 7. 일반 원칙
|
|
153
|
+
|
|
154
|
+
1. **일관성**: 모든 정기 메시지는 구분선 + 섹션 prefix 패턴 사용
|
|
155
|
+
2. **간결성**: 불필요한 수식어 제거, 데이터 중심
|
|
156
|
+
3. **실행 가능성**: 알림에는 반드시 구체적 행동 제안 포함
|
|
157
|
+
4. **연결성**: evening-summary의 "내일 제안" → morning-briefing의 "코칭" 라인으로 이어짐
|
|
158
|
+
5. **한국어 기본**: 기술 용어, 브랜치명, 코드는 영어 유지
|
|
159
|
+
6. **최대 길이 준수**: 각 템플릿별 글자 수 제한 엄수
|
|
160
|
+
7. **이모지 절제**: 카테고리 이모지만 사용, 장식용 이모지 금지
|