Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
This repo is a modern Telegram bot for CSUST built with Go 1.26+, featuring AI chat, message search (MeiliSearch), image generation (Stable Diffusion), gacha systems, and comprehensive permission controls.
This repo is a modern Telegram bot for CSUST built with Go 1.26+, featuring AI chat, message search (MeiliSearch), gacha systems, and comprehensive permission controls.

## Architecture Overview

Expand All @@ -7,8 +7,8 @@ This repo is a modern Telegram bot for CSUST built with Go 1.26+, featuring AI c
- **Bot Framework**: `gopkg.in/telebot.v3` - All commands registered via `bot.Handle()`
- **Configuration**: `config.yaml` → structs in `config/` → global `config.BotConfig`
- **Data Layer**: `orm/` - Redis-based persistence (NOT a SQL ORM); stores chat state, user lists, caches
- **Queue System**: `store/` - Background task processing (message deletion, SD generation)
- **Feature Packages**: `chat/`, `sd/`, `meili/`, `restrict/`, `base/`, `inline/`
- **Queue System**: `store/` - Background task processing (message deletion)
- **Feature Packages**: `chat/`, `meili/`, `restrict/`, `base/`, `inline/`

### Middleware Pipeline
All requests flow through this ordered chain (see `main.go:116-119`):
Expand All @@ -30,7 +30,6 @@ byeWorldMiddleware → mcMiddleware
- `util.PrivateCommand(handler)` - Only in private chats
- `util.GroupCommand(handler)` - Only in group chats
- `util.GroupCommandCtx(handler)` - Group-only with context tracking
- `whiteMiddleware` - Whitelist enforcement (only for sensitive commands like `/sd`)

### Redis Key Patterns (orm/redis.go)
- `wrapKey(key)` - Adds global prefix
Expand All @@ -41,7 +40,7 @@ byeWorldMiddleware → mcMiddleware
### Async Task Queues (store/)
- `TaskQueue[T]` interface: `Push()`, `Cancel()`, `fetch()`, `process()`
- Example: `ByeWorldQueue` for delayed message deletion
- Background goroutines: `go sd.Process()`, `store.InitQueues(bot)`
- Background goroutines: `store.InitQueues(bot)`

### Chat Config System (config/chat.go)
- Multi-model AI support with templates (Go `text/template`)
Expand Down Expand Up @@ -174,12 +173,6 @@ The chat module is the core AI conversation system with MCP (Model Context Proto
2. Background: Queue processor pushes to MeiliSearch
3. Search: `/search [-id chatID] [-p page] keyword` with pagination

### Stable Diffusion (sd/)
1. Queue: `ch <- context` (buffered, size 10)
2. Worker: `go sd.Process()` consumes queue
3. HTTP/3: Custom `mixRoundTripper` tries QUIC first, falls back to TCP
4. Rate limiting: `busyUser` map tracks per-user concurrency

## Pull Request Guidelines
1. **Base branch**: Always create PRs against `dev` (not `master`)
2. **Pre-commit**: Run `make build && make fmt && make test`
Expand Down
12 changes: 0 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ A modern Telegram bot for CSUST, developed in Go.

- 🤖 AI Chat Conversations (supports multiple models)
- 🔍 Message Search (powered by MeiliSearch)
- 🎨 Stable Diffusion Image Generation
- 🎲 Gacha System
- 🎭 Entertainment Features
- 🔧 Flexible Configuration System
Expand Down Expand Up @@ -153,16 +152,6 @@ gacha - Draw cards according to your configuration
getvoice - character=<character> gender=<sex> theme=<topic> type=<type> <text>
```

### Stable Diffusion

``` text
sd - <prompt> Generate images
sdcfg - Configure SD server
sdcfg - set <key> <value> Set configuration
sdcfg - get <key> Get configuration
sdlast - Get last used prompt
```

### Utility Functions

``` text
Expand All @@ -185,7 +174,6 @@ setiwant - f=<format> vf=<format> sf=<format> Set sticker format
- **Database**: Redis
- **Search**: MeiliSearch
- **AI**: OpenAI API Compatible Interface
- **Image Generation**: Stable Diffusion WebUI
- **Containerization**: Docker & Docker Compose

## Development
Expand Down
12 changes: 0 additions & 12 deletions README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

- 🤖 AI 聊天对话(支持多种模型)
- 🔍 消息搜索(基于 MeiliSearch)
- 🎨 Stable Diffusion 图像生成
- 🎲 抽卡系统
- 🎭 各种娱乐功能
- 🔧 灵活的配置系统
Expand Down Expand Up @@ -153,16 +152,6 @@ gacha - 抽卡,按照你的配置
getvoice - 角色=<character> 性别=<sex> 主题=<topic> 类型=<type> <text>
```

### Stable Diffusion

``` text
sd - <prompt> 生成图片
sdcfg - 配置SD服务器
sdcfg - set <key> <value> 设置配置
sdcfg - get <key> 获取配置
sdlast - 获取上次使用的prompt
```

### 工具功能

``` text
Expand All @@ -185,7 +174,6 @@ setiwant - f=<format> vf=<format> sf=<format> 设置我要Sticker
- **数据库**: Redis
- **搜索**: MeiliSearch
- **AI**: OpenAI API 兼容接口
- **图像生成**: Stable Diffusion WebUI
- **容器化**: Docker & Docker Compose

## 开发
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ require (
github.com/mark3labs/mcp-go v0.49.0
github.com/meilisearch/meilisearch-go v0.36.2
github.com/puzpuzpuz/xsync/v4 v4.5.0
github.com/quic-go/quic-go v0.59.0
github.com/redis/go-redis/v9 v9.17.3
github.com/sashabaranov/go-openai v1.41.2
github.com/spf13/viper v1.21.0
Expand Down Expand Up @@ -49,6 +48,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nikolalohinski/gonja v1.5.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f // indirect
github.com/swaggest/jsonschema-go v0.3.78 // indirect
Expand All @@ -57,10 +57,11 @@ require (
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/yargevad/filepathx v1.0.0 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.uber.org/mock v0.5.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect
golang.org/x/net v0.47.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

Expand All @@ -73,7 +74,6 @@ require (
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/samber/lo v1.53.0
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
Expand Down
7 changes: 1 addition & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
Expand Down Expand Up @@ -450,10 +451,6 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/puzpuzpuz/xsync/v4 v4.5.0 h1:vOSWu6b57/emh+L/Cw0BeQfvxa/cogFywXHeGUxQxAg=
github.com/puzpuzpuz/xsync/v4 v4.5.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/redis/go-redis/v9 v9.17.3 h1:fN29NdNrE17KttK5Ndf20buqfDZwGNgoUr9qjl1DQx4=
github.com/redis/go-redis/v9 v9.17.3/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
Expand Down Expand Up @@ -684,8 +681,6 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down
26 changes: 0 additions & 26 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"csust-got/chatv2"
"csust-got/inline"
"csust-got/meili"
"csust-got/sd"
"csust-got/store"
"csust-got/util/gacha"
"encoding/json"
Expand Down Expand Up @@ -67,17 +66,12 @@ func main() {
registerRestrictHandler(bot)
registerEventHandler(bot)
registerChatConfigHandler(bot)
bot.Handle("/sd", sd.Handler, whiteMiddleware)
bot.Handle("/sdcfg", sd.ConfigHandler)
bot.Handle("/sdlast", sd.LastPromptHandler)

// inline mode
inline.RegisterInlineHandler(bot, config.BotConfig)

meili.InitMeili()

go sd.Process()

base.Init()

store.InitQueues(bot)
Expand Down Expand Up @@ -406,26 +400,6 @@ func rateMiddleware(next HandlerFunc) HandlerFunc {
}
}

func whiteMiddleware(next HandlerFunc) HandlerFunc {
return func(ctx Context) error {
if !config.BotConfig.WhiteListConfig.Enabled {
return next(ctx)
}

m := ctx.Message()
// continue with inline query
if m == nil && ctx.Query() != nil {
return next(ctx)
}

if ctx.Chat() != nil && !config.BotConfig.WhiteListConfig.Check(ctx.Chat().ID) {
log.Info("chat ignore by white list", zap.String("chat", ctx.Chat().Title))
return nil
}
return next(ctx)
}
}

func noStickerMiddleware(next HandlerFunc) HandlerFunc {
return func(ctx Context) error {
m := ctx.Message()
Expand Down
54 changes: 0 additions & 54 deletions orm/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,60 +534,6 @@ func GetTargetState(target string) bool {
return r == "1"
}

// SetSDConfig set stable diffusion config.
func SetSDConfig(userID int64, cfg string) error {
err := rc.Set(context.TODO(), wrapKeyWithUser("stable_diffusion_config", userID), cfg, 0).Err()
if err != nil {
log.Error("set stable diffusion config to redis failed", zap.Int64("user", userID), zap.String("config", cfg), zap.Error(err))
return err
}
return nil
}

// GetSDConfig get stable diffusion config.
func GetSDConfig(userID int64) (string, error) {
cfg, err := rc.Get(context.TODO(), wrapKeyWithUser("stable_diffusion_config", userID)).Result()
if err != nil {
if !errors.Is(err, redis.Nil) {
log.Error("get stable diffusion config from redis failed", zap.Int64("user", userID), zap.Error(err))
}
return "", err
}
return cfg, nil
}

// SetSDLastPrompt save user's last stable diffusion prompt.
func SetSDLastPrompt(userID int64, lastPrompt string) error {
err := rc.Set(context.TODO(), wrapKeyWithUser("stable_diffusion_last_prompt", userID), lastPrompt, 0).Err()
if err != nil {
log.Error("set stable diffusion last prompt to redis failed", zap.Int64("user", userID), zap.String("lastPrompt", lastPrompt), zap.Error(err))
return err
}
return nil
}

// GetSDLastPrompt get user's last stable diffusion prompt.
func GetSDLastPrompt(userID int64) (string, error) {
lastPrompt, err := rc.Get(context.TODO(), wrapKeyWithUser("stable_diffusion_last_prompt", userID)).Result()
if err != nil {
if !errors.Is(err, redis.Nil) {
log.Error("get stable diffusion last prompt from redis failed", zap.Int64("user", userID), zap.Error(err))
return "", err
}
return "", err
}
return lastPrompt, nil
}

// GetSDDefaultServer get stable diffusion default server from redis.
func GetSDDefaultServer() string {
defaultServer, err := rc.Get(context.TODO(), wrapKey("stable_diffusion::default_server")).Result()
if err != nil {
return ""
}
return defaultServer
}

// SetChatContext save user's chat context with GPT to redis.
func SetChatContext(chatID int64, msgID int, chatContext []openai.ChatCompletionMessage) error {
if len(chatContext) == 0 {
Expand Down
Loading
Loading