Spaces:
Paused
Paused
| 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 | |
| } |