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
61 changes: 61 additions & 0 deletions cmd/bot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import (
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"

"github.com/sunba91-su/Roket.Chat-GeekBot/internal/commands"
"github.com/sunba91-su/Roket.Chat-GeekBot/internal/config"
"github.com/sunba91-su/Roket.Chat-GeekBot/internal/convstate"
"github.com/sunba91-su/Roket.Chat-GeekBot/internal/rocket"
"github.com/sunba91-su/Roket.Chat-GeekBot/internal/store"
)
Expand Down Expand Up @@ -59,8 +62,13 @@ func main() {

cmdReg := commands.New()
commands.RegisterTeamCommands(cmdReg)
commands.RegisterStandupCommands(cmdReg)

convMgr := convstate.NewManager()

client.OnMessage(func(msg rocket.IncomingMessage) {
isDM, _ := client.IsDMRoom(msg.RoomID)

ctx := &commands.Context{
UserID: msg.UserID,
Username: msg.Username,
Expand All @@ -70,6 +78,8 @@ func main() {
Messenger: client,
UserProvider: &userProviderAdapter{client: client},
Config: cfg,
ConvState: convMgr,
IsDM: isDM,
}

handled, err := cmdReg.Dispatch(ctx)
Expand All @@ -78,6 +88,15 @@ func main() {
}
if handled {
log.Printf("[%s] %s: %s", msg.RoomID, msg.Username, msg.Text)
return
}

if !isDM {
return
}

if err := handleStandupReply(ctx); err != nil {
log.Printf("Standup reply error: %v", err)
}
})

Expand All @@ -99,3 +118,45 @@ func main() {
fmt.Println()
log.Println("Shutting down...")
}

func handleStandupReply(ctx *commands.Context) error {
conv, ok := ctx.ConvState.GetConversation(ctx.UserID)
if !ok {
return nil
}
if conv.RoomID != ctx.RoomID {
return nil
}

finished, nextQ, err := ctx.ConvState.RecordAnswer(ctx.UserID, ctx.RawText)
if err != nil {
return ctx.SendMessage(ctx.RoomID, "Error recording your answer.")
}

if finished {
conv, ok := ctx.ConvState.GetConversation(ctx.UserID)
if !ok {
return ctx.SendMessage(ctx.RoomID, "Error: conversation lost.")
}
answersJoined := strings.Join(conv.Answers, "|")
resp := &store.StandupResponse{
ID: fmt.Sprintf("resp-%d", time.Now().UnixMilli()),
SessionID: conv.SessionID,
UserID: conv.UserID,
Username: conv.Username,
Answers: answersJoined,
SubmittedAt: time.Now(),
}
if err := ctx.Store.SubmitResponse(resp); err != nil {
return ctx.SendMessage(ctx.RoomID,
fmt.Sprintf("Error saving standup: %v", err))
}

ctx.ConvState.EndConversation(ctx.UserID)
return ctx.SendMessage(ctx.RoomID,
"✅ *Standup submitted!* Thank you. Have a great day!")
}

return ctx.SendMessage(ctx.RoomID,
fmt.Sprintf("**Q%d:** %s\n\nReply with your answer.", conv.CurrentQ+1, nextQ))
}
8 changes: 5 additions & 3 deletions internal/commands/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"

"github.com/sunba91-su/Roket.Chat-GeekBot/internal/config"
"github.com/sunba91-su/Roket.Chat-GeekBot/internal/convstate"
"github.com/sunba91-su/Roket.Chat-GeekBot/internal/store"
)

Expand Down Expand Up @@ -40,9 +41,10 @@ type Context struct {
Store *store.Store
Messenger
UserProvider
Config *config.Config
CmdName string
IsDM bool
Config *config.Config
ConvState *convstate.Manager
CmdName string
IsDM bool
}

type Handler func(ctx *Context) error
Expand Down
115 changes: 115 additions & 0 deletions internal/commands/standup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package commands

import (
"fmt"
"strings"
"time"

"github.com/sunba91-su/Roket.Chat-GeekBot/internal/store"
)

func RegisterStandupCommands(r *Registry) {
r.Register("submit", handleSubmit, PermissionMember)
r.Register("status", handleStatus, PermissionMember)
}

func handleSubmit(ctx *Context) error {
teams, err := ctx.Store.GetTeamsForUser(ctx.UserID)
if err != nil {
return send(ctx.Messenger, ctx.RoomID,
"Error looking up your teams.")
}
if len(teams) == 0 {
return send(ctx.Messenger, ctx.RoomID,
"You are not a member of any team.")
}

dmRoomID, err := ensureDMRoom(ctx)
if err != nil {
return send(ctx.Messenger, ctx.RoomID,
fmt.Sprintf("Could not start DM: %v", err))
}

team := teams[0]
date := time.Now().Format("2006-01-02")

sessionID := fmt.Sprintf("sess-%d-%s", time.Now().UnixMilli(), team.ID)

session := &store.StandupSession{
ID: sessionID,
TeamID: team.ID,
Date: date,
Status: "open",
}
if err := ctx.Store.CreateSession(session); err != nil {
return send(ctx.Messenger, ctx.RoomID,
fmt.Sprintf("Failed to create session: %v", err))
}

questions := strings.Split(team.Questions, "|")

ctx.ConvState.StartConversation(
ctx.UserID, ctx.Username, team.ID,
dmRoomID, questions, sessionID,
)

firstQ := questions[0]
_ = send(ctx.Messenger, ctx.RoomID,
fmt.Sprintf("Starting your standup for **%s**. Check your DMs!", team.Name))

return send(ctx.Messenger, dmRoomID,
fmt.Sprintf("📋 *Daily Standup — %s*\n\n**Q1:** %s\n\nReply with your answer.", team.Name, firstQ))
}

func handleStatus(ctx *Context) error {
teams, err := ctx.Store.GetTeamsForUser(ctx.UserID)
if err != nil || len(teams) == 0 {
return send(ctx.Messenger, ctx.RoomID, "You are not a member of any team.")
}

date := time.Now().Format("2006-01-02")
team := teams[0]

submitted, total, err := ctx.Store.GetSessionStatus(team.ID, date)
if err != nil {
session, err2 := ctx.Store.GetActiveSession(team.ID, date)
if err2 != nil {
return send(ctx.Messenger, ctx.RoomID,
"No active standup for today.")
}
submitted, total, _ = ctx.Store.GetSessionStatus(team.ID, session.Date)
}

hasSubmitted, _ := ctx.Store.HasSubmitted(sessionID(ctx, team.ID), ctx.UserID)
statusLine := ""
if hasSubmitted {
statusLine = "✅ You have submitted."
} else {
statusLine = "⏳ You have not submitted yet."
}

return send(ctx.Messenger, ctx.RoomID,
fmt.Sprintf("*Standup Status — %s*\n%s\nTeam progress: %d/%d submitted.",
team.Name, statusLine, submitted, total))
}

func sessionID(ctx *Context, teamID string) string {
date := time.Now().Format("2006-01-02")
session, err := ctx.Store.GetActiveSession(teamID, date)
if err != nil {
return ""
}
return session.ID
}

func ensureDMRoom(ctx *Context) (string, error) {
if ctx.IsDM && ctx.RoomID != "" {
return ctx.RoomID, nil
}

dmProvider, ok := ctx.Messenger.(interface{ CreateDM(string) (string, error) })
if !ok {
return "", fmt.Errorf("DM creation not available")
}
return dmProvider.CreateDM(ctx.Username)
}
Loading
Loading