tool-moyu.sh 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. #!/bin/bash
  2. # =============================================================
  3. # tool-moyu.sh — AI「摸鱼」模拟器
  4. #
  5. # 作用:读取一段内容(默认本会话日志,或任意文件/stdin),
  6. # 循环「假装在处理」,模拟 AI 工作的样子。
  7. # 适合:需要看起来在干活、又不想真干活的时候 😏
  8. #
  9. # 用法:
  10. # ./tool-moyu.sh # 用内置内容摸鱼(默认)
  11. # ./tool-moyu.sh -n 3 # 循环 3 遍
  12. # ./tool-moyu.sh [文件] # 从文件读取(覆盖内置内容)
  13. # ./tool-moyu.sh --help # 帮助
  14. #
  15. # 选项:
  16. # -n N 循环 N 次(默认无限)
  17. # -d MS 每行间隔毫秒(默认随机 80-400)
  18. # -t 逐个字符「打字」效果
  19. # -s 静默模式:不打印假状态行,只循环内容
  20. # =============================================================
  21. set -euo pipefail
  22. LOOP_TIMES=0 # 0 = 无限
  23. DELAY_MS=0 # 0 = 随机 80-400
  24. TYPE_EFFECT=0
  25. SILENT=0
  26. show_usage() {
  27. sed -n '1,20p' "$0"
  28. exit 0
  29. }
  30. while [ $# -gt 0 ]; do
  31. case "$1" in
  32. -n)
  33. LOOP_TIMES="${2:?}"
  34. shift 2
  35. ;;
  36. -d)
  37. DELAY_MS="${2:?}"
  38. shift 2
  39. ;;
  40. -t)
  41. TYPE_EFFECT=1
  42. shift
  43. ;;
  44. -s)
  45. SILENT=1
  46. shift
  47. ;;
  48. --help | -h) show_usage ;;
  49. -*)
  50. echo "Unknown option: $1" >&2
  51. exit 1
  52. ;;
  53. *) break ;;
  54. esac
  55. done
  56. # ---- 颜色自动检测:仅 TTY 或 FORCE_COLOR=1 才上色;NO_COLOR 显式禁用 ----
  57. # 文件模式不上色(见 run_once),此处仅决定随机内容是否套用 ANSI 主题色
  58. COLOR=0
  59. if [ -z "${NO_COLOR:-}" ] && { [ "${FORCE_COLOR:-0}" = "1" ] || [ -t 1 ]; }; then
  60. COLOR=1
  61. fi
  62. # ---- 命令与对应输出(成对,命令后紧跟其输出更真实) ----
  63. COMMANDS=(
  64. "go vet ./..."
  65. "go test ./..."
  66. "CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o admin_server ./cmd/server"
  67. "pnpm typecheck"
  68. "pnpm build"
  69. "rsync -az --delete release/frontend/ root@18.220.34.224:/www/wwwroot/household/"
  70. "ssh -p 10022 root@18.220.34.224 'systemctl restart backend-admin'"
  71. "mysql -h127.0.0.1 -uhousehold -p**** household -e 'SHOW TABLES;'"
  72. "curl -s -o /dev/null -w 'HTTP %{http_code}\\n' http://127.0.0.1:8081/"
  73. "journalctl -u backend-admin -n 30 --no-pager"
  74. "systemctl status backend-app"
  75. "git log --oneline -5"
  76. )
  77. COMMAND_OUT=(
  78. "ok 0.231s"
  79. "ok git.ooo.ink/household/app 0.532s"
  80. "[build] release/backend/admin/admin_server (ELF, linux/amd64)"
  81. "no type errors found"
  82. "✓ built in 26.39s"
  83. "sent 2.4MB, 1.1MB/s"
  84. "systemd[1]: Started backend-admin.service - Household Backend Admin."
  85. "Tables: center_admins center_menus center_roles ... (19 total)"
  86. "HTTP 200"
  87. "[GIN-debug] Listening and serving HTTP on :8081"
  88. "Active: active (running)"
  89. "f37eec3 docs(deploy): align backend directory with actual test-machine paths"
  90. )
  91. # ---- Standalone lines (tool returns / logs / a few AI actions) ----
  92. STANDALONE=(
  93. "[read] docs/deploy-aaPanel.md 168 lines (2.1 kB, 14 sections, 3 code blocks) · reviewed the Nginx config and systemd service sections, ready to follow the manual"
  94. "[read] internal/pkg/database/run_migrations.go 87 lines → located the RunMigrations entry · confirmed FIFO order + idempotent dedup, auto-rollback on failure"
  95. "[search] migrations → 3 results (run_migrations.go, migrate.go, main.go) · all are startup migration paths, no duplicate registration"
  96. "[search] CenterAdmin → 14 results · hit in admin routes/service/handler, test files excluded to avoid noise"
  97. "[trace] RunMigrations → cmd/server/main.go:41 ← internal/pkg/database.RunMigrations · called once from main only, no bypass path"
  98. "[trace] ParseToken → 6 callers: auth middleware ×1, ws handler ×2, cron ×3 · every call chain verified, no cyclic dependencies"
  99. "[snippet] buildDSN → user:pass@tcp(host:port)/db?parseTime=True&charset=utf8mb4 · reads DB_HOST/DB_PORT/DB_USER/DB_PASS from viper config"
  100. "[snippet] handleList → offset defaults to 0, limit caps at 100 with silent truncation · returns {total, items}, pagination contract for the frontend"
  101. "[grep] rabbitmq in docker-compose/dev.yml → 0 matches, dependency absent · queue-based notifications must degrade to synchronous calls"
  102. "[edit] internal/router/route.go +1 -2 → registered notify route under /api/v1 group · swagger annotations updated, takes effect after restart"
  103. "[write] internal/pkg/notify/notify.go 96 lines → new email/SMS notification wrapper with retry · 3s timeout, 2 retries, failures written to dead-letter table"
  104. "[deploy] backend-admin → active (running) · pid 1823 · 48MB RSS · managed by systemd, enabled on boot, listening on port 8081"
  105. "[deploy] frontend-admin → HTTP 200 · dist/assets/index-*.js uploaded · Nginx cache purged, all static assets now serve the new build"
  106. "[check] SSH root@18.220.34.224:10022 → ok (handshake 0.42s, key auth passed) · passwordless channel healthy, follow-up commands can run directly"
  107. "[verify] curl admin.household.ooo.ink → 200 OK (TTFB 86ms, no 5xx) · homepage returns full HTML, every static asset is 200"
  108. "[verify] mysql SHOW TABLES → 19 tables · 312MB total · covers center/tenant/order core groups, no orphaned tables"
  109. "[verify] redis ping → PONG · 1.2GB/4GB in use · 92% hit ratio, all keys carry business prefix, no collisions"
  110. "[skip] migration 20260829002 already applied · dirty=0, nothing to re-run · this release ships no new migration scripts"
  111. "[skip] no changes, up to date · working tree clean, nothing pending · fully in sync with origin/master, no push required"
  112. "[warn] config.yaml placeholder secret left as-is · must be replaced in production · prefer env-var injection over hardcoding"
  113. "[warn] TLS cert expires in 12 days, renew soon · Let's Encrypt auto-renew cron confirmed running"
  114. "[warn] pkg/notify has 3 leftover TODOs: email templates not i18n'ed · JA/CN templates to be added next iteration"
  115. "[err] rate limit exceeded, auto-retry in 1.8s... · 2nd retry succeeded, request replayed"
  116. "ready for connections. socket: '/var/run/mysqld/mysqld.sock' port: 3306 · MySQL 8.4 up, version compatibility check passed"
  117. "schema_migrations: 20260828001 | dirty=0 · last migration 2026-08-28 10:02:34 · 47 entries in history, no conflicts"
  118. "dist/assets/index-*.js 95.77 kB │ gzip: 37.38 kB │ 8 chunks · 12.4% smaller than previous build, tree-shaking effective"
  119. "vite v7.3.6 building client environment for production... 26.39s ✓ · output 4.2MB, zero warnings, dist.zip produced"
  120. "GET /api/v1/orders 200 4.2ms · source: center db, Redis cache hit · no DB touch, no new entries in slow-query log"
  121. "POST /api/v1/orders 201 12.8ms · triggered worker assignment + notification push · status transition normal, transaction committed"
  122. "center_admins: 6 rows returned in 1ms (SELECT id, name, role_ids FROM center_admins) · PRIMARY index used, no full scan"
  123. "db 15/16 used, 1 left · one idle pool reserved · leak detection clean, avg idle 3.2s"
  124. "service worker: 34 active jobs today, avg response 2.1s · spread across Tokyo/Osaka/Fukuoka, no timeouts"
  125. "🤖 analyzing dependency graph... 1284 symbols, 8 clusters, 2 cyclic deps · flagged for decoupling, no impact on this change"
  126. "📊 summarizing key decisions... 4 items: routing/caching/payments/compliance · synced to project ADR for later review"
  127. "✍️ drafting implementation plan... 5 steps, ~40 min · migration/route/service/test/docs, each with acceptance criteria"
  128. "✅ implementation plan approved · no blockers · passed 2 reviewers, all risks low/medium, execution started"
  129. "📐 aligning with existing architecture (ARCHITECTURE.md §3 layering) · new module follows cmd→internal→shared one-way dependency"
  130. "🏗️ scaffolding new module: internal/pkg/notify (service/repository/template) · directory layout matches the skeleton conventions"
  131. "🧩 wiring DI container... injected logger + db + redis deps · constructor signatures updated, compiles clean"
  132. "🧪 writing unit tests for RunMigrations · 3 cases: success/duplicate/failed-rollback · covers edges, 87% coverage"
  133. "⚙️ running gofmt -l + go vet ./... → 0 format issues, 0 vet warnings · no unrelated files reformatted"
  134. "📦 bumping go.mod dependency: gin v1.10.0 → v1.11.0 (CVE-2026-0113 fix) · go.sum synced, compile regression-free"
  135. "📝 updating API docs (docs/api/notify.md) · added 2 endpoint specs · req/resp examples + error codes, linked from README"
  136. "🧠 loading knowledge graph index (codebase-memory)... 12s · covers 3 sub-repos, last indexed 5 min ago"
  137. "🔎 querying codebase graph for CenterAdmin handlers → 6 nodes expanded · located create/update/delete/query ops"
  138. "🚦 checking index coverage: project/backend/admin → 100% covered, no gaps · unindexed historical branches excluded"
  139. "👣 tracing callers of ParseToken → all 6 refs verified · each carries auth context, no privilege-escalation calls"
  140. "🧬 detecting git diff impact (blast radius) → touches router + 2 handlers · 4 related tests run and green"
  141. "🗃️ index refreshed: 1284 symbols, 8 clusters, 312 files, 0 stale · +23 symbols, −5, topology acyclic"
  142. "[test] TestParseToken → PASS (0.112s, 4/4 asserts) · covers expired/tampered/malformed/valid tokens"
  143. "[test] TestBuildDSN → PASS (0.031s, 5/5 asserts, empty-password edge) · includes fallback when host:port has no default"
  144. "[bench] BenchmarkList → 1.2µs/op, 812 allocs/op, 0 B/op leaked · 18% faster than baseline, acceptable"
  145. "[lint] staticcheck ./... → no issues reported · last run 10 min ago · full golangci-lint suite also green"
  146. "[git] status → clean (working tree) · no untracked files · 3 commits staged, awaiting push"
  147. "[git] log --oneline -3 → 3 commits behind origin, need pull · upstream added config refactor + dependency bumps"
  148. "Redis: 127.0.0.1:6379 connected · maxclients 10000 · 15/16 dbs used · AOF persistence on, no blocking commands"
  149. )
  150. # 输出/日志/错误行(可重复,作为“裸输出”随机插入)
  151. OUTPUTS=(
  152. "PASS"
  153. "ok"
  154. "HTTP 200"
  155. "Active: active (running)"
  156. "[GIN-debug] Listening and serving HTTP on :8081"
  157. "warn: chunk size exceeds 4000 kB"
  158. 'ERROR: failed to open source "file://internal/migrations"'
  159. "FATAL: database connection refused, retrying..."
  160. "0 error(s), 5 warning(s)"
  161. "migrations completed successfully"
  162. "no type errors found"
  163. "✓ 307 modules transformed."
  164. "Build succeeded after 3 retries"
  165. )
  166. # 动态随机数字行(消除固定值反复出现)
  167. gen_rand_lines() {
  168. local n=$((1 + RANDOM % 3)) # 1-3 条
  169. local i r
  170. for ((i = 0; i < n; i++)); do
  171. r=$((RANDOM % 4))
  172. case $r in
  173. 0) echo "ok $(awk 'BEGIN{srand(); printf "%.3f", 0.05+rand()*1.5}')s" ;;
  174. 1) echo "dist/assets/index-*.js $(awk 'BEGIN{srand(); printf "%.2f", 8+rand()*300}') kB │ gzip: $(awk 'BEGIN{srand(); printf "%.2f", 3+rand()*80}') kB" ;;
  175. 2) echo "Progress: $((5 + RANDOM % 95))%..." ;;
  176. 3) echo "$((1 + RANDOM % 40)) test(s), $((RANDOM % 4)) failed" ;;
  177. esac
  178. done
  179. }
  180. # 返回 $1 个不重复随机下标(0..$2-1)
  181. pick_unique() {
  182. local count=$1 pool=$2
  183. [ "$pool" -lt "$count" ] && count=$pool
  184. seq 0 $((pool - 1)) | sort -R | head -n "$count"
  185. }
  186. # 随机生成一段“工作日志”:命令(约一半带输出)+独立行+裸输出+动态数字,洗牌打乱
  187. # 块大小不规则、数字随机化,消除固定模式与重复数字
  188. gen_random_content() {
  189. local cmdc=$((1 + RANDOM % 5)) # 1-5 条命令
  190. local stc=$((2 + RANDOM % 6)) # 2-7 条独立行
  191. local ci si oi i
  192. LINES=()
  193. # 命令:约一半紧跟输出(不一定成对,制造不规则块)
  194. for ci in $(pick_unique "$cmdc" "${#COMMANDS[@]}"); do
  195. LINES+=("\$ ${COMMANDS[$ci]}")
  196. if [ $((RANDOM % 2)) -eq 0 ]; then LINES+=("${COMMAND_OUT[$ci]}"); fi
  197. [ $((RANDOM % 4)) -eq 0 ] && LINES+=("") # 随机空行
  198. done
  199. # 独立行(不重复)
  200. for si in $(pick_unique "$stc" "${#STANDALONE[@]}"); do
  201. LINES+=("${STANDALONE[$si]}")
  202. [ $((RANDOM % 3)) -eq 0 ] && LINES+=("") # 随机空行
  203. done
  204. # 裸输出行(可重复)
  205. for ((i = 0; i < $((2 + RANDOM % 4)); i++)); do
  206. LINES+=("${OUTPUTS[$((RANDOM % ${#OUTPUTS[@]}))]}")
  207. done
  208. # 动态随机数字行
  209. while IFS= read -r l; do LINES+=("$l"); done < <(gen_rand_lines)
  210. # 洗牌打乱整体顺序
  211. local idx=($(seq 0 $((${#LINES[@]} - 1)) | sort -R))
  212. local shuffled=()
  213. for i in "${idx[@]}"; do shuffled+=("${LINES[$i]}"); done
  214. LINES=("${shuffled[@]}")
  215. }
  216. # 内容来源:文件参数 > 随机生成
  217. CONTENT="${1-}"
  218. FILE_MODE=0
  219. if [ -n "$CONTENT" ] && [ -f "$CONTENT" ]; then
  220. mapfile -t LINES <"$CONTENT"
  221. FILE_MODE=1
  222. elif [ -n "$CONTENT" ] && [ "$CONTENT" != "-" ]; then
  223. echo "File not found: $CONTENT, falling back to random content." >&2
  224. gen_random_content
  225. else
  226. gen_random_content
  227. fi
  228. [ "${#LINES[@]}" -eq 0 ] && {
  229. echo "Content empty, nothing to slack on." >&2
  230. exit 1
  231. }
  232. # 随机延时
  233. rand_delay() {
  234. if [ "$DELAY_MS" -gt 0 ]; then
  235. sleep "$(awk -v d="$DELAY_MS" 'BEGIN{printf "%.3f", d/1000}')"
  236. else sleep "$(awk -v lo=80 -v hi=400 'BEGIN{srand(); printf "%.3f", (lo+int(rand()*(hi-lo+1)))/1000}')"; fi
  237. }
  238. # 按内容语义配色(优先级:命令 > 致命 > 错误 > 警告 > 成功 > 默认)
  239. # 命令=青 / FATAL=红加粗 / 错误=红 / 警告=黄 / 成功=绿;仅随机生成模式调用
  240. colorize() {
  241. case "$1" in
  242. '$ '*) printf '\033[1;36m%s\033[0m' "$1" ;;
  243. *FATAL*) printf '\033[1;31m%s\033[0m' "$1" ;;
  244. *ERROR* | *\[err\]* | *err:*) printf '\033[31m%s\033[0m' "$1" ;;
  245. *warn* | *WARNING*) printf '\033[33m%s\033[0m' "$1" ;;
  246. *'✓'* | *PASS* | *'ok '* | *'HTTP 200'* | *PONG* | *'no type errors'* | *'completed successfully'* | *'Build succeeded'*) printf '\033[32m%s\033[0m' "$1" ;;
  247. *) printf '%s' "$1" ;;
  248. esac
  249. }
  250. run_once() {
  251. local line
  252. for line in "${LINES[@]}"; do
  253. local out="$line"
  254. # 文件模式或终端不支持颜色 → 原样输出
  255. if [ "$FILE_MODE" = "0" ] && [ "$COLOR" = "1" ]; then out=$(colorize "$line"); fi
  256. if [ "$TYPE_EFFECT" = "1" ]; then
  257. local i
  258. for ((i = 0; i < ${#out}; i++)); do
  259. printf "%s" "${out:i:1}"
  260. rand_delay
  261. done
  262. echo ""
  263. else
  264. printf '%s\n' "$out"
  265. fi
  266. rand_delay
  267. done
  268. echo "" # 每遍之间空行
  269. }
  270. n=0
  271. while [ "$LOOP_TIMES" -eq 0 ] || [ "$n" -lt "$LOOP_TIMES" ]; do
  272. n=$((n + 1))
  273. # 每轮重新生成随机内容(避免内容固定导致反复重复);文件模式则用固定内容
  274. [ "$FILE_MODE" = "0" ] && gen_random_content
  275. run_once
  276. done
  277. exit 0