chore: 初始开源发布

iotlcm-bot:独立飞书应用机器人(读取 Worker 终态 Run JSON,渲染并发送全量同步结果卡片)。

- Go module:git.5iot.com/iotlcm/iotlcm-bot → git.5iot.com/iotlcm-public/iotlcm-bot
- 飞书凭据占位:appId/appSecret/chatId → ${FEISHU_APP_ID}/${FEISHU_APP_SECRET}/${FEISHU_CHAT_ID}
- 移除内部资产:CI 触发器
- Dockerfile 改用公共基础镜像(golang:1.25-alpine → scratch)
- README 重写(命令/配置/挂载契约/常见问题),去掉内部环境路径
- 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes
This commit is contained in:
MiFaZhan
2026-10-22 16:14:24 +08:00
commit 5732bf3384
33 changed files with 2676 additions and 0 deletions
+137
View File
@@ -0,0 +1,137 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"git.5iot.com/iotlcm-public/iotlcm-bot/internal/archive"
"git.5iot.com/iotlcm-public/iotlcm-bot/internal/config"
"git.5iot.com/iotlcm-public/iotlcm-bot/internal/feishu"
"git.5iot.com/iotlcm-public/iotlcm-bot/internal/notify"
"git.5iot.com/iotlcm-public/iotlcm-bot/internal/render"
"git.5iot.com/iotlcm-public/iotlcm-bot/internal/state"
)
func main() {
if len(os.Args) < 2 {
usage()
}
switch os.Args[1] {
case "render":
renderCommand(os.Args[2:])
case "send":
sendCommand(os.Args[2:])
case "send-text":
sendTextCommand(os.Args[2:])
case "watch":
watchCommand(os.Args[2:])
default:
usage()
}
}
func renderCommand(args []string) {
fs := flag.NewFlagSet("render", flag.ExitOnError)
file := fs.String("file", "", "path to a Worker Run JSON")
environment := fs.String("environment", "preview", "environment label")
jsonOutput := fs.Bool("json", false, "print Feishu interactive-card JSON")
fs.Parse(args)
if *file == "" {
fmt.Fprintln(os.Stderr, "render requires --file")
os.Exit(2)
}
a, err := archive.Load(*file)
if err != nil {
fail(err)
}
if *jsonOutput {
body, err := render.Card(a, *environment)
if err != nil {
fail(err)
}
fmt.Println(string(body))
return
}
fmt.Println(render.Text(a, *environment))
}
func sendCommand(args []string) {
fs := flag.NewFlagSet("send", flag.ExitOnError)
file := fs.String("file", "", "path to a Worker Run JSON")
configPath := fs.String("config", "", "path to YAML config")
force := fs.Bool("force", false, "send even when the Run was already marked sent")
fs.Parse(args)
if *file == "" || *configPath == "" {
fmt.Fprintln(os.Stderr, "send requires --file and --config")
os.Exit(2)
}
cfg, err := config.Load(*configPath)
if err != nil {
fail(err)
}
if err := cfg.ValidateSend(); err != nil {
fail(err)
}
client := feishu.NewClient(cfg.Feishu.AppID, cfg.Feishu.AppSecret, cfg.Feishu.ChatID)
svc := notify.New(cfg.ArchiveDir, cfg.Environment, cfg.PollDuration, state.New(cfg.StateDir), client, notify.DefaultLogger())
if err := svc.SendFile(context.Background(), *file, *force); err != nil {
fail(err)
}
}
func sendTextCommand(args []string) {
fs := flag.NewFlagSet("send-text", flag.ExitOnError)
configPath := fs.String("config", "", "path to YAML config")
text := fs.String("text", "", "text to send")
fs.Parse(args)
if *configPath == "" || *text == "" {
fmt.Fprintln(os.Stderr, "send-text requires --config and --text")
os.Exit(2)
}
cfg, err := config.Load(*configPath)
if err != nil {
fail(err)
}
if err := cfg.ValidateSend(); err != nil {
fail(err)
}
client := feishu.NewClient(cfg.Feishu.AppID, cfg.Feishu.AppSecret, cfg.Feishu.ChatID)
if err := client.SendText(context.Background(), *text); err != nil {
fail(err)
}
fmt.Println("Feishu text message sent")
}
func watchCommand(args []string) {
fs := flag.NewFlagSet("watch", flag.ExitOnError)
configPath := fs.String("config", "", "path to YAML config")
fs.Parse(args)
if *configPath == "" {
fmt.Fprintln(os.Stderr, "watch requires --config")
os.Exit(2)
}
cfg, err := config.Load(*configPath)
if err != nil {
fail(err)
}
if err := cfg.ValidateSend(); err != nil {
fail(err)
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
client := feishu.NewClient(cfg.Feishu.AppID, cfg.Feishu.AppSecret, cfg.Feishu.ChatID)
svc := notify.New(cfg.ArchiveDir, cfg.Environment, cfg.PollDuration, state.New(cfg.StateDir), client, notify.DefaultLogger())
if err := svc.Watch(ctx); err != nil && err != context.Canceled {
fail(err)
}
}
func usage() {
fmt.Fprintln(os.Stderr, "usage: iotlcm-bot <render|send|send-text|watch> [options]")
os.Exit(2)
}
func fail(err error) { fmt.Fprintln(os.Stderr, "iotlcm-bot:", err); os.Exit(1) }