diff --git a/cmd/agent/main.go b/cmd/agent/main.go index d885905b..72d89aaa 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -9,6 +9,7 @@ import ( "time" "github.com/memohai/memoh/internal/chat" + "github.com/memohai/memoh/internal/channel" "github.com/memohai/memoh/internal/config" "github.com/memohai/memoh/internal/logger" ctr "github.com/memohai/memoh/internal/containerd" @@ -181,6 +182,12 @@ func main() { settingsHandler := handlers.NewSettingsHandler(logger.L, settingsService) historyService := history.NewService(logger.L, queries) historyHandler := handlers.NewHistoryHandler(logger.L, historyService) + channelService := channel.NewService(queries) + channelManager := channel.NewManager(channelService, chatResolver) + channelManager.RegisterAdapter(channel.NewTelegramAdapter()) + channelManager.RegisterAdapter(channel.NewFeishuAdapter()) + channelManager.Start(ctx) + channelHandler := handlers.NewChannelHandler(channelService, channelManager) scheduleService := schedule.NewService(logger.L, queries, chatResolver, cfg.Auth.JWTSecret) if err := scheduleService.Bootstrap(ctx); err != nil { logger.Error("schedule bootstrap", slog.Any("error", err)) @@ -189,7 +196,7 @@ func main() { scheduleHandler := handlers.NewScheduleHandler(logger.L, scheduleService) subagentService := subagent.NewService(logger.L, queries) subagentHandler := handlers.NewSubagentHandler(logger.L, subagentService) - srv := server.NewServer(logger.L, addr, cfg.Auth.JWTSecret, pingHandler, authHandler, memoryHandler, embeddingsHandler, chatHandler, swaggerHandler, providersHandler, modelsHandler, settingsHandler, historyHandler, scheduleHandler, subagentHandler, containerdHandler) + srv := server.NewServer(logger.L, addr, cfg.Auth.JWTSecret, pingHandler, authHandler, memoryHandler, embeddingsHandler, chatHandler, swaggerHandler, providersHandler, modelsHandler, settingsHandler, historyHandler, scheduleHandler, subagentHandler, containerdHandler, channelHandler) if err := srv.Start(); err != nil { logger.Error("server failed", slog.Any("error", err)) diff --git a/go.mod b/go.mod index 833eebd1..79e71034 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/go-openapi/swag/stringutils v0.25.4 // indirect github.com/go-openapi/swag/typeutils v0.25.4 // indirect github.com/go-openapi/swag/yamlutils v0.25.4 // indirect + github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/go-cmp v0.7.0 // indirect @@ -60,6 +61,7 @@ require ( github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/klauspost/compress v1.18.3 // indirect github.com/labstack/gommon v0.4.2 // indirect + github.com/larksuite/oapi-sdk-go/v3 v3.5.3 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/moby/locker v1.0.1 // indirect diff --git a/go.sum b/go.sum index dbe9cdc2..e8a5606d 100644 --- a/go.sum +++ b/go.sum @@ -85,6 +85,8 @@ github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxE github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= +github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= @@ -118,6 +120,7 @@ github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -140,6 +143,8 @@ github.com/labstack/echo/v4 v4.15.0 h1:hoRTKWcnR5STXZFe9BmYun9AMTNeSbjHi2vtDuADJ github.com/labstack/echo/v4 v4.15.0/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/larksuite/oapi-sdk-go/v3 v3.5.3 h1:xvf8Dv29kBXC5/DNDCLhHkAFW8l/0LlQJimO5Zn+JUk= +github.com/larksuite/oapi-sdk-go/v3 v3.5.3/go.mod h1:ZEplY+kwuIrj/nqw5uSCINNATcH3KdxSN7y+UxYY5fI= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= diff --git a/internal/db/sqlc/models.go b/internal/db/sqlc/models.go index 5271b577..677c19a4 100644 --- a/internal/db/sqlc/models.go +++ b/internal/db/sqlc/models.go @@ -8,6 +8,25 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) +type ChannelConfig struct { + ID pgtype.UUID `json:"id"` + ChannelType string `json:"channel_type"` + Config []byte `json:"config"` + UserID pgtype.UUID `json:"user_id"` + IsGlobal bool `json:"is_global"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +type ChannelUserConfig struct { + ID pgtype.UUID `json:"id"` + ChannelType string `json:"channel_type"` + UserID pgtype.UUID `json:"user_id"` + Config []byte `json:"config"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + type Container struct { ID pgtype.UUID `json:"id"` UserID pgtype.UUID `json:"user_id"` diff --git a/internal/memory/llm_client.go b/internal/memory/llm_client.go index 3f0004c8..f1f80fe6 100644 --- a/internal/memory/llm_client.go +++ b/internal/memory/llm_client.go @@ -21,6 +21,9 @@ type LLMClient struct { } func NewLLMClient(log *slog.Logger, baseURL, apiKey, model string, timeout time.Duration) *LLMClient { + if log == nil { + log = slog.Default() + } if baseURL == "" { baseURL = "https://api.openai.com/v1" } diff --git a/internal/memory/llm_client_test.go b/internal/memory/llm_client_test.go index 7aa20b1b..e7770a23 100644 --- a/internal/memory/llm_client_test.go +++ b/internal/memory/llm_client_test.go @@ -20,7 +20,7 @@ func TestLLMClientExtract(t *testing.T) { })) defer server.Close() - client := NewLLMClient(server.URL, "test-key", "gpt-4.1-nano-2025-04-14", 0) + client := NewLLMClient(nil, server.URL, "test-key", "gpt-4.1-nano-2025-04-14", 0) resp, err := client.Extract(context.Background(), ExtractRequest{ Messages: []Message{{Role: "user", Content: "hi"}}, }) diff --git a/internal/server/server.go b/internal/server/server.go index 1eb183e5..8f35d866 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -17,7 +17,7 @@ type Server struct { logger *slog.Logger } -func NewServer(log *slog.Logger, addr string, jwtSecret string, pingHandler *handlers.PingHandler, authHandler *handlers.AuthHandler, memoryHandler *handlers.MemoryHandler, embeddingsHandler *handlers.EmbeddingsHandler, chatHandler *handlers.ChatHandler, swaggerHandler *handlers.SwaggerHandler, providersHandler *handlers.ProvidersHandler, modelsHandler *handlers.ModelsHandler, settingsHandler *handlers.SettingsHandler, historyHandler *handlers.HistoryHandler, scheduleHandler *handlers.ScheduleHandler, subagentHandler *handlers.SubagentHandler, containerdHandler *handlers.ContainerdHandler) *Server { +func NewServer(log *slog.Logger, addr string, jwtSecret string, pingHandler *handlers.PingHandler, authHandler *handlers.AuthHandler, memoryHandler *handlers.MemoryHandler, embeddingsHandler *handlers.EmbeddingsHandler, chatHandler *handlers.ChatHandler, swaggerHandler *handlers.SwaggerHandler, providersHandler *handlers.ProvidersHandler, modelsHandler *handlers.ModelsHandler, settingsHandler *handlers.SettingsHandler, historyHandler *handlers.HistoryHandler, scheduleHandler *handlers.ScheduleHandler, subagentHandler *handlers.SubagentHandler, containerdHandler *handlers.ContainerdHandler, channelHandler *handlers.ChannelHandler) *Server { if addr == "" { addr = ":8080" } @@ -90,6 +90,9 @@ func NewServer(log *slog.Logger, addr string, jwtSecret string, pingHandler *han if containerdHandler != nil { containerdHandler.Register(e) } + if channelHandler != nil { + channelHandler.Register(e) + } return &Server{ echo: e, diff --git a/sqlc.yaml b/sqlc.yaml index 79a1b571..b3c2a82e 100644 --- a/sqlc.yaml +++ b/sqlc.yaml @@ -1,7 +1,9 @@ version: "2" sql: - engine: "postgresql" - schema: "db/migrations/0001_init.up.sql" + schema: + - "db/migrations/0001_init.up.sql" + - "db/migrations/0002_channel.up.sql" queries: "db/queries" gen: go: