package bot import ( "context" "fmt" "strings" "time" "github.com/celestix/gotgproto" "github.com/celestix/gotgproto/sessionMaker" "github.com/glebarez/sqlite" "go.uber.org/zap" ) // BotInfo contains information about a validated bot type BotInfo struct { ID int64 `json:"id"` Username string `json:"username"` FirstName string `json:"first_name"` IsBot bool `json:"is_bot"` } // BotValidator handles bot token validation type BotValidator struct { log *zap.Logger } // NewBotValidator creates a new bot validator func NewBotValidator(log *zap.Logger) *BotValidator { return &BotValidator{ log: log.Named("BotValidator"), } } // ValidateBotToken validates a bot token and returns bot information func (bv *BotValidator) ValidateBotToken(token string) (*BotInfo, error) { // Basic token format validation if !bv.isValidTokenFormat(token) { return nil, fmt.Errorf("invalid token format") } // Create a temporary client to test the token _, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // Use a temporary session file tempSessionPath := fmt.Sprintf("/tmp/validate_%d.session", time.Now().UnixNano()) client, err := gotgproto.NewClient( 0, // We don't need API ID/Hash for bot validation "", gotgproto.ClientTypeBot(token), &gotgproto.ClientOpts{ Session: sessionMaker.SqlSession( sqlite.Open(tempSessionPath), ), DisableCopyright: true, }, ) if err != nil { bv.log.Error("Failed to create validation client", zap.Error(err)) return nil, fmt.Errorf("invalid bot token: %w", err) } // Clean up the temporary session defer func() { if client != nil { client.Stop() } }() // Get bot information self := client.Self if self == nil { return nil, fmt.Errorf("failed to get bot information") } if !self.Bot { return nil, fmt.Errorf("token belongs to a user account, not a bot") } botInfo := &BotInfo{ ID: self.ID, Username: self.Username, FirstName: self.FirstName, IsBot: self.Bot, } bv.log.Info("Successfully validated bot token", zap.Int64("botID", botInfo.ID), zap.String("username", botInfo.Username)) return botInfo, nil } // isValidTokenFormat checks if the token has the correct format func (bv *BotValidator) isValidTokenFormat(token string) bool { // Bot tokens have format: {bot_id}:{auth_token} // Example: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz if len(token) < 35 || len(token) > 50 { return false } parts := strings.Split(token, ":") if len(parts) != 2 { return false } // First part should be numeric (bot ID) botID := parts[0] if len(botID) < 8 || len(botID) > 12 { return false } for _, char := range botID { if char < '0' || char > '9' { return false } } // Second part should be the auth token (alphanumeric + some special chars) authToken := parts[1] if len(authToken) < 25 || len(authToken) > 40 { return false } return true } // TestBotPermissions tests if the bot has necessary permissions func (bv *BotValidator) TestBotPermissions(token string) error { // This could be extended to test specific permissions // For now, just validate that we can create a client _, err := bv.ValidateBotToken(token) return err }