diff --git a/cmd/handlers.go b/cmd/handlers.go index a56ab0f..3b3f1d1 100644 --- a/cmd/handlers.go +++ b/cmd/handlers.go @@ -3,7 +3,10 @@ package main import ( "context" "database/sql" + "encoding/json" "fmt" + "net/http" + "net/url" "os" "strconv" "strings" @@ -60,6 +63,54 @@ func getWeekStart(t time.Time) string { return t.AddDate(0, 0, -offset).Format("2006-01-02") } +// sendPollNonAnonymous sends a non-anonymous poll by manually constructing the request +// Workaround for echotron bug where IsAnonymous=false is ignored (bool false is zero value) +func sendPollNonAnonymous(chatID int64, question string, options []echotron.InputPollOption, opts *echotron.PollOptions) (*echotron.APIResponseMessage, error) { + token := os.Getenv("TELEGRAM__TOKEN") + if token == "" { + return nil, fmt.Errorf("TELEGRAM__TOKEN not set") + } + + baseURL := "https://api.telegram.org/bot" + token + "/sendPoll" + + vals := make(url.Values) + vals.Set("chat_id", strconv.FormatInt(chatID, 10)) + vals.Set("question", question) + + optionsJSON, err := json.Marshal(options) + if err != nil { + return nil, err + } + vals.Set("options", string(optionsJSON)) + + // IMPORTANT: Explicitly set is_anonymous=false to get poll_answer updates + // echotron ignores IsAnonymous=false because bool false is zero value in scan() + vals.Set("is_anonymous", "false") + + if opts != nil && opts.AllowsMultipleAnswers { + vals.Set("allows_multiple_answers", "true") + } + + // Make the HTTP request + resp, err := http.PostForm(baseURL, vals) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Parse response + var result echotron.APIResponseMessage + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } + + if !result.Ok { + return nil, fmt.Errorf("telegram API error: %s", result.Description) + } + + return &result, nil +} + func parseCommaSeparatedIDs(envKey, fieldName string) []int64 { value := os.Getenv(envKey) if value == "" { @@ -221,12 +272,14 @@ func SendQuiz(ctx context.Context, db *sql.DB, api echotron.API, groupID int64) {Text: "Да!"}, {Text: "Нет"}, } + + // Workaround for echotron bug: IsAnonymous=false is ignored because bool false is zero value + // We need to explicitly set is_anonymous=false in the request opts := &echotron.PollOptions{ - IsAnonymous: false, AllowsMultipleAnswers: false, } - result, err := api.SendPoll(groupID, question, options, opts) + result, err := sendPollNonAnonymous(groupID, question, options, opts) if err != nil { log.Error().Err(err).Int64("group_id", groupID).Msg("SendPoll failed") return diff --git a/cmd/main.go b/cmd/main.go index c214525..40cb280 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -117,11 +117,11 @@ func main() { dsp := echotron.NewDispatcher(botToken, newBot) updateOpts := echotron.UpdateOptions{ - AllowedUpdates: []echotron.UpdateType{ - echotron.MessageUpdate, - echotron.CallbackQueryUpdate, - echotron.PollAnswerUpdate, - }, + // AllowedUpdates: []echotron.UpdateType{ + // echotron.MessageUpdate, + // echotron.CallbackQueryUpdate, + // echotron.PollAnswerUpdate, + // }, } errChan := make(chan error, 1) diff --git a/database/database.go b/database/database.go index 37912f0..b0ed69c 100644 --- a/database/database.go +++ b/database/database.go @@ -58,10 +58,17 @@ func GetAllParticipants(ctx context.Context, db *sql.DB, groupID int64) ([]Parti participants := make([]Participant, 0) for rows.Next() { var p Participant - var idStr string - if err := rows.Scan(&idStr, &p.GroupID, &p.UserID, &p.Username, &p.FullName, &p.CreatedAt); err != nil { + var idStr, createdAtStr string + if err := rows.Scan(&idStr, &p.GroupID, &p.UserID, &p.Username, &p.FullName, &createdAtStr); err != nil { return nil, err } + + // Parse time string - SQLite returns timestamps as strings + p.CreatedAt, _ = time.Parse(time.RFC3339, createdAtStr) + if p.CreatedAt.IsZero() { + p.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAtStr) + } + p.ID, _ = uuid.Parse(idStr) participants = append(participants, p) } @@ -131,13 +138,25 @@ func GetAvailablePairs(ctx context.Context, db *sql.DB, groupID int64) ([][2]Par for rows.Next() { var p1, p2 Participant var p1IDStr, p2IDStr string + var p1CreatedAtStr, p2CreatedAtStr string p1.GroupID = groupID p2.GroupID = groupID - if err := rows.Scan(&p1IDStr, &p1.UserID, &p1.Username, &p1.FullName, &p1.CreatedAt, - &p2IDStr, &p2.UserID, &p2.Username, &p2.FullName, &p2.CreatedAt); err != nil { + if err := rows.Scan(&p1IDStr, &p1.UserID, &p1.Username, &p1.FullName, &p1CreatedAtStr, + &p2IDStr, &p2.UserID, &p2.Username, &p2.FullName, &p2CreatedAtStr); err != nil { return nil, err } + + // Parse time strings - SQLite returns timestamps as strings + p1.CreatedAt, _ = time.Parse(time.RFC3339, p1CreatedAtStr) + if p1.CreatedAt.IsZero() { + p1.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", p1CreatedAtStr) + } + p2.CreatedAt, _ = time.Parse(time.RFC3339, p2CreatedAtStr) + if p2.CreatedAt.IsZero() { + p2.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", p2CreatedAtStr) + } + p1.ID, _ = uuid.Parse(p1IDStr) p2.ID, _ = uuid.Parse(p2IDStr) pairs = append(pairs, [2]Participant{p1, p2})