diff options
| author | s <[email protected]> | 2025-11-25 09:38:31 -0500 |
|---|---|---|
| committer | s <[email protected]> | 2025-11-25 09:38:31 -0500 |
| commit | 8472267b60b204cea5fbdeaf8fe06443822d1bfb (patch) | |
| tree | eba71104733067072ded109bf96904cd825c2f7f | |
| parent | bc31d9cc8f93a5efef958872f48f3f4370ed5e29 (diff) | |
| download | dborg-8472267b60b204cea5fbdeaf8fe06443822d1bfb.tar.gz dborg-8472267b60b204cea5fbdeaf8fe06443822d1bfb.zip | |
feat: add crypto analysis, email verification, and telegram lookup commands
| -rw-r--r-- | cmd/crypto.go | 461 | ||||
| -rw-r--r-- | cmd/email.go | 45 | ||||
| -rw-r--r-- | cmd/telegram.go | 45 | ||||
| -rw-r--r-- | internal/client/crypto.go | 235 | ||||
| -rw-r--r-- | internal/client/email.go | 28 | ||||
| -rw-r--r-- | internal/client/telegram.go | 28 | ||||
| -rw-r--r-- | internal/formatter/crypto.go | 39 | ||||
| -rw-r--r-- | internal/formatter/email.go | 103 | ||||
| -rw-r--r-- | internal/formatter/telegram.go | 34 | ||||
| -rw-r--r-- | internal/models/admin.go | 19 | ||||
| -rw-r--r-- | internal/models/crypto.go | 12 | ||||
| -rw-r--r-- | internal/models/email.go | 21 | ||||
| -rw-r--r-- | internal/models/github.go | 1 | ||||
| -rw-r--r-- | internal/models/telegram.go | 12 |
14 files changed, 1074 insertions, 9 deletions
diff --git a/cmd/crypto.go b/cmd/crypto.go new file mode 100644 index 0000000..02b39b1 --- /dev/null +++ b/cmd/crypto.go @@ -0,0 +1,461 @@ +package cmd + +import ( + "git.db.org.ai/dborg/internal/client" + "git.db.org.ai/dborg/internal/formatter" + "github.com/spf13/cobra" +) + +var cryptoCmd = &cobra.Command{ + Use: "crypto", + Short: "Crypto analysis commands", + Long: `Analyze cryptocurrency tokens, wallets, and trading activity`, +} + +var bundleDetectionCmd = &cobra.Command{ + Use: "bundle-detection [address]", + Short: "Detect bundled launches", + Long: `Analyzes whether a token contract was bundled during launch`, + Args: cobra.ExactArgs(1), + RunE: runBundleDetection, +} + +var crossTradersCmd = &cobra.Command{ + Use: "cross-traders [addresses]", + Short: "Find cross-token traders", + Long: `Filters common traders active across multiple token contracts (comma-separated addresses)`, + Args: cobra.ExactArgs(1), + RunE: runCrossTraders, +} + +var earlyAdoptersCmd = &cobra.Command{ + Use: "early-adopters [address]", + Short: "Analyze early adopters", + Long: `Analyzes early buyers and their trading activity for a token`, + Args: cobra.ExactArgs(1), + RunE: runEarlyAdopters, +} + +var floorHoldersCmd = &cobra.Command{ + Use: "floor-holders [address]", + Short: "Analyze floor price holders", + Long: `Analyzes top holders who bought at floor prices`, + Args: cobra.ExactArgs(1), + RunE: runFloorHolders, +} + +var freshAccountsCmd = &cobra.Command{ + Use: "fresh-accounts [address]", + Short: "Analyze fresh accounts", + Long: `Analyzes top holder fresh wallets to detect potential sybil attacks`, + Args: cobra.ExactArgs(1), + RunE: runFreshAccounts, +} + +var fundingSourceCmd = &cobra.Command{ + Use: "funding-source [address]", + Short: "Analyze funding source", + Long: `Determines how and when a wallet address was funded`, + Args: cobra.ExactArgs(1), + RunE: runFundingSource, +} + +var graduationStatsCmd = &cobra.Command{ + Use: "graduation-stats", + Short: "Get graduation statistics", + Long: `Retrieves statistics for recently graduated token launches`, + Args: cobra.NoArgs, + RunE: runGraduationStats, +} + +var launchParticipantsCmd = &cobra.Command{ + Use: "launch-participants [address]", + Short: "Get launch participants", + Long: `Retrieves list of early participants in token launch`, + Args: cobra.ExactArgs(1), + RunE: runLaunchParticipants, +} + +var liquidityCmd = &cobra.Command{ + Use: "liquidity [address]", + Short: "Check liquidity status", + Long: `Checks if liquidity has been added or updated on DEX for a token`, + Args: cobra.ExactArgs(1), + RunE: runLiquidity, +} + +var ownerIntelCmd = &cobra.Command{ + Use: "owner-intel [address]", + Short: "Get owner intelligence", + Long: `Retrieves detailed intelligence and analysis on token holders`, + Args: cobra.ExactArgs(1), + RunE: runOwnerIntel, +} + +var ownershipDistributionCmd = &cobra.Command{ + Use: "ownership-distribution [address]", + Short: "Analyze ownership distribution", + Long: `Visualizes token holder ownership distribution and concentration`, + Args: cobra.ExactArgs(1), + RunE: runOwnershipDistribution, +} + +var portfolioCmd = &cobra.Command{ + Use: "portfolio [address]", + Short: "Analyze portfolio", + Long: `Analyzes profit and loss for recently traded tokens in a wallet`, + Args: cobra.ExactArgs(1), + RunE: runPortfolio, +} + +var topOwnersCmd = &cobra.Command{ + Use: "top-owners [address]", + Short: "Analyze top owners", + Long: `Analyzes the top holders and their positions for a token`, + Args: cobra.ExactArgs(1), + RunE: runTopOwners, +} + +var whaleIntelCmd = &cobra.Command{ + Use: "whale-intel [address]", + Short: "Analyze whale holdings", + Long: `Analyzes net worth and positions of top 50 whale holders`, + Args: cobra.ExactArgs(1), + RunE: runWhaleIntel, +} + +func init() { + rootCmd.AddCommand(cryptoCmd) + + cryptoCmd.AddCommand(bundleDetectionCmd) + cryptoCmd.AddCommand(crossTradersCmd) + cryptoCmd.AddCommand(earlyAdoptersCmd) + cryptoCmd.AddCommand(floorHoldersCmd) + cryptoCmd.AddCommand(freshAccountsCmd) + cryptoCmd.AddCommand(fundingSourceCmd) + cryptoCmd.AddCommand(graduationStatsCmd) + cryptoCmd.AddCommand(launchParticipantsCmd) + cryptoCmd.AddCommand(liquidityCmd) + cryptoCmd.AddCommand(ownerIntelCmd) + cryptoCmd.AddCommand(ownershipDistributionCmd) + cryptoCmd.AddCommand(portfolioCmd) + cryptoCmd.AddCommand(topOwnersCmd) + cryptoCmd.AddCommand(whaleIntelCmd) +} + +func runBundleDetection(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetBundleDetection(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runCrossTraders(cmd *cobra.Command, args []string) error { + addresses := args[0] + if err := client.ValidateCryptoAddresses(addresses); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetCrossTraders(addresses) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runEarlyAdopters(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetEarlyAdopters(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runFloorHolders(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetFloorHolders(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runFreshAccounts(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetFreshAccounts(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runFundingSource(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetFundingSource(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runGraduationStats(cmd *cobra.Command, args []string) error { + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetGraduationStats() + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runLaunchParticipants(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetLaunchParticipants(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runLiquidity(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetLiquidity(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runOwnerIntel(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetOwnerIntel(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runOwnershipDistribution(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetOwnershipDistribution(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runPortfolio(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetPortfolio(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runTopOwners(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetTopOwners(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} + +func runWhaleIntel(cmd *cobra.Command, args []string) error { + address := args[0] + if err := client.ValidateCryptoAddress(address); err != nil { + return err + } + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetWhaleIntel(address) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatCryptoResults(response, IsJSONOutput()) +} diff --git a/cmd/email.go b/cmd/email.go new file mode 100644 index 0000000..f4306ec --- /dev/null +++ b/cmd/email.go @@ -0,0 +1,45 @@ +package cmd + +import ( + "git.db.org.ai/dborg/internal/formatter" + "github.com/spf13/cobra" +) + +var emailCmd = &cobra.Command{ + Use: "email", + Short: "Email verification commands", + Long: `Verify email addresses and check deliverability`, +} + +var verifyEmailCmd = &cobra.Command{ + Use: "verify [email]", + Short: "Verify email address", + Long: `Performs comprehensive email verification including format validation, MX records check, SMTP verification, and disposable/webmail detection`, + Args: cobra.ExactArgs(1), + RunE: runVerifyEmail, +} + +func init() { + rootCmd.AddCommand(emailCmd) + emailCmd.AddCommand(verifyEmailCmd) +} + +func runVerifyEmail(cmd *cobra.Command, args []string) error { + email := args[0] + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.VerifyEmail(email) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatEmailResults(response, IsJSONOutput()) +} diff --git a/cmd/telegram.go b/cmd/telegram.go new file mode 100644 index 0000000..1c650b7 --- /dev/null +++ b/cmd/telegram.go @@ -0,0 +1,45 @@ +package cmd + +import ( + "git.db.org.ai/dborg/internal/formatter" + "github.com/spf13/cobra" +) + +var telegramCmd = &cobra.Command{ + Use: "telegram", + Short: "Telegram lookup commands", + Long: `Lookup phone numbers associated with Telegram accounts`, +} + +var phoneCmd = &cobra.Command{ + Use: "phone [identifier]", + Short: "Get phone number for Telegram user", + Long: `Retrieves the phone number associated with a Telegram username (with @ prefix) or user ID`, + Args: cobra.ExactArgs(1), + RunE: runTelegramPhone, +} + +func init() { + rootCmd.AddCommand(telegramCmd) + telegramCmd.AddCommand(phoneCmd) +} + +func runTelegramPhone(cmd *cobra.Command, args []string) error { + identifier := args[0] + + c, err := newClient() + if err != nil { + return err + } + + response, err := c.GetTelegramPhone(identifier) + if err != nil { + return err + } + + if err := checkError(response.Error); err != nil { + return err + } + + return formatter.FormatTelegramResults(response, IsJSONOutput()) +} diff --git a/internal/client/crypto.go b/internal/client/crypto.go new file mode 100644 index 0000000..ee145f9 --- /dev/null +++ b/internal/client/crypto.go @@ -0,0 +1,235 @@ +package client + +import ( + "encoding/json" + "fmt" + "git.db.org.ai/dborg/internal/models" + "strings" +) + +func (c *Client) GetBundleDetection(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/bundle-detection/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetCrossTraders(addresses string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/cross-traders/%s", addresses) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetEarlyAdopters(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/early-adopters/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetFloorHolders(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/floor-holders/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetFreshAccounts(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/fresh-accounts/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetFundingSource(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/funding-source/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetGraduationStats() (*models.CryptoResponse, error) { + data, err := c.Get("/crypto/graduation-stats", nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetLaunchParticipants(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/launch-participants/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetLiquidity(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/liquidity/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetOwnerIntel(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/owner-intel/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetOwnershipDistribution(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/ownership-distribution/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetPortfolio(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/portfolio/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetTopOwners(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/top-owners/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func (c *Client) GetWhaleIntel(address string) (*models.CryptoResponse, error) { + path := fmt.Sprintf("/crypto/whale-intel/%s", address) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.CryptoResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse crypto response: %w", err) + } + + return &response, nil +} + +func ValidateCryptoAddress(address string) error { + if len(address) == 0 { + return fmt.Errorf("address cannot be empty") + } + return nil +} + +func ValidateCryptoAddresses(addresses string) error { + if len(addresses) == 0 { + return fmt.Errorf("addresses cannot be empty") + } + parts := strings.Split(addresses, ",") + if len(parts) < 2 { + return fmt.Errorf("at least 2 addresses required for cross-trader analysis") + } + return nil +} diff --git a/internal/client/email.go b/internal/client/email.go new file mode 100644 index 0000000..a7000fe --- /dev/null +++ b/internal/client/email.go @@ -0,0 +1,28 @@ +package client + +import ( + "encoding/json" + "fmt" + "git.db.org.ai/dborg/internal/models" + "strings" +) + +func (c *Client) VerifyEmail(email string) (*models.EmailVerifyResponse, error) { + email = strings.TrimSpace(email) + if email == "" { + return nil, fmt.Errorf("email address cannot be empty") + } + + path := fmt.Sprintf("/email/verify/%s", email) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.EmailVerifyResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse email verification response: %w", err) + } + + return &response, nil +} diff --git a/internal/client/telegram.go b/internal/client/telegram.go new file mode 100644 index 0000000..33b4ac2 --- /dev/null +++ b/internal/client/telegram.go @@ -0,0 +1,28 @@ +package client + +import ( + "encoding/json" + "fmt" + "git.db.org.ai/dborg/internal/models" + "strings" +) + +func (c *Client) GetTelegramPhone(identifier string) (*models.TelegramPhoneResponse, error) { + identifier = strings.TrimSpace(identifier) + if identifier == "" { + return nil, fmt.Errorf("identifier cannot be empty") + } + + path := fmt.Sprintf("/telegram/phone/%s", identifier) + data, err := c.Get(path, nil) + if err != nil { + return nil, err + } + + var response models.TelegramPhoneResponse + if err := json.Unmarshal(data, &response); err != nil { + return nil, fmt.Errorf("failed to parse telegram response: %w", err) + } + + return &response, nil +} diff --git a/internal/formatter/crypto.go b/internal/formatter/crypto.go new file mode 100644 index 0000000..70ef29b --- /dev/null +++ b/internal/formatter/crypto.go @@ -0,0 +1,39 @@ +package formatter + +import ( + "fmt" + "git.db.org.ai/dborg/internal/models" + "git.db.org.ai/dborg/internal/utils" + "strings" +) + +func FormatCryptoResults(response *models.CryptoResponse, asJSON bool) error { + if asJSON { + return utils.PrintJSON(response) + } + + PrintSection(fmt.Sprintf("📊 Crypto Analysis Results")) + + if response.Query != "" { + fmt.Printf("%s: %s\n\n", Cyan("Query"), response.Query) + } + + if response.Response != "" { + fmt.Println(response.Response) + fmt.Println() + } + + PrintDivider() + fmt.Printf("%s: ", Cyan("Credits Remaining")) + if response.Credits.Unlimited { + fmt.Printf("%s\n", Green("Unlimited")) + } else { + fmt.Printf("%s\n", FormatCredits(int64(response.Credits.Remaining))) + } + + if response.Message != "" && !strings.Contains(strings.ToLower(response.Message), "success") { + fmt.Printf("\n%s: %s\n", Yellow("Message"), response.Message) + } + + return nil +} diff --git a/internal/formatter/email.go b/internal/formatter/email.go new file mode 100644 index 0000000..80f590c --- /dev/null +++ b/internal/formatter/email.go @@ -0,0 +1,103 @@ +package formatter + +import ( + "fmt" + "git.db.org.ai/dborg/internal/models" + "git.db.org.ai/dborg/internal/utils" +) + +func FormatEmailResults(response *models.EmailVerifyResponse, asJSON bool) error { + if asJSON { + return utils.PrintJSON(response) + } + + PrintSection(fmt.Sprintf("📧 Email Verification: %s", Bold(response.Email))) + + statusColor := ColorGreen + switch response.Status { + case "valid": + statusColor = ColorGreen + case "invalid": + statusColor = ColorRed + case "risky", "accept_all": + statusColor = ColorYellow + default: + statusColor = ColorGray + } + + fmt.Printf("%s: %s", Cyan("Status"), Colorize(response.Status, statusColor)) + if response.Score > 0 { + scoreColor := ColorGreen + if response.Score < 50 { + scoreColor = ColorRed + } else if response.Score < 75 { + scoreColor = ColorYellow + } + fmt.Printf(" (Score: %s)", Colorize(fmt.Sprintf("%d/100", response.Score), scoreColor)) + } + fmt.Println() + + fmt.Println() + fmt.Printf("%s\n", Bold("Validation Checks")) + + checks := []struct { + name string + passed bool + }{ + {"Format Valid", response.Regexp}, + {"MX Records Found", response.MXRecords}, + {"SMTP Server Reachable", response.SMTPServer}, + {"Mailbox Verified", response.SMTPCheck}, + } + + for _, check := range checks { + status := StatusError + if check.passed { + status = StatusSuccess + } + fmt.Printf(" %s %s\n", status.String(), check.name) + } + + if response.MXServer != "" { + fmt.Printf("\n%s: %s\n", Cyan("MX Server"), response.MXServer) + } + + fmt.Println() + fmt.Printf("%s\n", Bold("Risk Indicators")) + + risks := []struct { + name string + present bool + }{ + {"Disposable Email", response.Disposable}, + {"Webmail Service", response.Webmail}, + {"Blocked Domain", response.Block}, + {"Gibberish Detected", response.Gibberish}, + } + + hasRisks := false + for _, risk := range risks { + if risk.present { + hasRisks = true + fmt.Printf(" %s %s\n", StatusWarning.String(), risk.name) + } + } + + if !hasRisks { + fmt.Printf(" %s %s\n", StatusSuccess.String(), "No risk indicators found") + } + + if response.ResponseTimeMs > 0 { + fmt.Printf("\n%s: %dms\n", Dim("Response Time"), response.ResponseTimeMs) + } + + if response.ErrorMessage != "" { + fmt.Printf("\n%s: %s\n", Red("Error"), response.ErrorMessage) + } + + if response.VerifiedAt != "" { + fmt.Printf("%s: %s\n", Dim("Verified At"), response.VerifiedAt) + } + + return nil +} diff --git a/internal/formatter/telegram.go b/internal/formatter/telegram.go new file mode 100644 index 0000000..7491c66 --- /dev/null +++ b/internal/formatter/telegram.go @@ -0,0 +1,34 @@ +package formatter + +import ( + "fmt" + "git.db.org.ai/dborg/internal/models" + "git.db.org.ai/dborg/internal/utils" +) + +func FormatTelegramResults(response *models.TelegramPhoneResponse, asJSON bool) error { + if asJSON { + return utils.PrintJSON(response) + } + + PrintSection(fmt.Sprintf("📱 Telegram Phone Lookup")) + + fmt.Printf("%s: %s\n", Cyan("Identifier"), response.Identifier) + + if response.PhoneNumber != "" { + fmt.Printf("%s: %s\n", Cyan("Phone Number"), Green(response.PhoneNumber)) + } else { + fmt.Printf("%s: %s\n", Cyan("Phone Number"), Red("Not found")) + } + + fmt.Println() + PrintDivider() + fmt.Printf("%s: ", Cyan("Credits Remaining")) + if response.Credits.Unlimited { + fmt.Printf("%s\n", Green("Unlimited")) + } else { + fmt.Printf("%s\n", FormatCredits(int64(response.Credits.Remaining))) + } + + return nil +} diff --git a/internal/models/admin.go b/internal/models/admin.go index 852e80f..ee551f3 100644 --- a/internal/models/admin.go +++ b/internal/models/admin.go @@ -1,15 +1,16 @@ package models type Account struct { - ID int `json:"id"` - APIKey string `json:"api_key"` - Name string `json:"name"` - Credits int `json:"credits"` - Unlimited bool `json:"unlimited"` - Disabled bool `json:"disabled"` - IsPremium bool `json:"is_premium"` - IsAdmin bool `json:"is_admin"` - CreatedAt interface{} `json:"created_at,omitempty"` + ID int `json:"id"` + APIKey string `json:"api_key"` + Name string `json:"name"` + Credits int `json:"credits"` + Unlimited bool `json:"unlimited"` + Disabled bool `json:"disabled"` + IsPremium bool `json:"is_premium"` + IsAdmin bool `json:"is_admin"` + PremiumDiscountPct int `json:"premium_discount_pct,omitempty"` + CreatedAt interface{} `json:"created_at,omitempty"` } type AccountCreateRequest struct { diff --git a/internal/models/crypto.go b/internal/models/crypto.go new file mode 100644 index 0000000..1900037 --- /dev/null +++ b/internal/models/crypto.go @@ -0,0 +1,12 @@ +package models + +type CryptoResponse struct { + Query string `json:"query"` + Response string `json:"response"` + Credits struct { + Remaining int `json:"remaining"` + Unlimited bool `json:"unlimited"` + } `json:"credits"` + Message string `json:"message,omitempty"` + Error string `json:"error,omitempty"` +} diff --git a/internal/models/email.go b/internal/models/email.go new file mode 100644 index 0000000..0b95e85 --- /dev/null +++ b/internal/models/email.go @@ -0,0 +1,21 @@ +package models + +type EmailVerifyResponse struct { + Email string `json:"email"` + Status string `json:"status"` + Score int `json:"score"` + Regexp bool `json:"regexp"` + MXRecords bool `json:"mx_records"` + MXServer string `json:"mx_server,omitempty"` + SMTPServer bool `json:"smtp_server"` + SMTPCheck bool `json:"smtp_check"` + Disposable bool `json:"disposable"` + Webmail bool `json:"webmail"` + Block bool `json:"block"` + Gibberish bool `json:"gibberish"` + ErrorMessage string `json:"error_message,omitempty"` + VerifiedAt string `json:"verified_at,omitempty"` + ResponseTimeMs int `json:"response_time_ms"` + Message string `json:"message,omitempty"` + Error string `json:"error,omitempty"` +} diff --git a/internal/models/github.go b/internal/models/github.go index fc1c9c3..3b7fc73 100644 --- a/internal/models/github.go +++ b/internal/models/github.go @@ -9,4 +9,5 @@ type GitHubLead struct { Website string `json:"website,omitempty"` Twitter string `json:"twitter,omitempty"` PFP string `json:"pfp,omitempty"` + Bio string `json:"bio,omitempty"` } diff --git a/internal/models/telegram.go b/internal/models/telegram.go new file mode 100644 index 0000000..05900cb --- /dev/null +++ b/internal/models/telegram.go @@ -0,0 +1,12 @@ +package models + +type TelegramPhoneResponse struct { + Identifier string `json:"identifier"` + PhoneNumber string `json:"phone_number"` + Credits struct { + Remaining int `json:"remaining"` + Unlimited bool `json:"unlimited"` + } `json:"credits"` + Message string `json:"message,omitempty"` + Error string `json:"error,omitempty"` +} |
