5732bf3384
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
138 lines
3.8 KiB
Go
138 lines
3.8 KiB
Go
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) }
|