summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/crypto.go461
-rw-r--r--cmd/email.go45
-rw-r--r--cmd/telegram.go45
-rw-r--r--internal/client/crypto.go235
-rw-r--r--internal/client/email.go28
-rw-r--r--internal/client/telegram.go28
-rw-r--r--internal/formatter/crypto.go39
-rw-r--r--internal/formatter/email.go103
-rw-r--r--internal/formatter/telegram.go34
-rw-r--r--internal/models/admin.go19
-rw-r--r--internal/models/crypto.go12
-rw-r--r--internal/models/email.go21
-rw-r--r--internal/models/github.go1
-rw-r--r--internal/models/telegram.go12
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"`
+}