summaryrefslogtreecommitdiff
path: root/namecheap_api.py
blob: c85da0202ca5ada3569df9833c086d08f976aab6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import requests
import xml.etree.ElementTree as ET
from typing import Dict, Any, Optional
from config import Config

class NamecheapAPI:
    def __init__(self, config: Config):
        self.config = config
        self.base_url = "https://api.namecheap.com/xml.response"
    
    def _make_request(self, command: str, params: Dict[str, Any]) -> ET.Element:
        """Make a request to Namecheap API"""
        default_params = {
            'ApiUser': self.config.namecheap_api_user,
            'ApiKey': self.config.namecheap_api_key,
            'UserName': self.config.namecheap_username,
            'ClientIp': self.config.namecheap_client_ip,
            'Command': command
        }
        
        all_params = {**default_params, **params}
        
        try:
            response = requests.get(self.base_url, params=all_params, timeout=60)
            response.raise_for_status()
        except requests.exceptions.Timeout:
            raise Exception(f"API request timed out for command: {command}")
        except requests.exceptions.RequestException as e:
            raise Exception(f"API request failed for command {command}: {str(e)}")
        
        root = ET.fromstring(response.text)
        
        # Check for API errors
        if root.get('Status') == 'ERROR':
            ns = {'ns': 'http://api.namecheap.com/xml.response'}
            errors = root.find('.//ns:Errors', ns)
            if errors is not None:
                error_elem = errors.find('.//ns:Error', ns)
                if error_elem is not None:
                    error_msg = error_elem.text
                    raise Exception(f"Namecheap API Error: {error_msg}")
            raise Exception("Namecheap API returned an error")
        
        return root
    
    def check_domain_availability(self, domain: str) -> bool:
        """Check if domain is available for registration"""
        params = {
            'DomainList': domain
        }
        
        root = self._make_request('namecheap.domains.check', params)
        
        # Parse the response
        ns = {'ns': 'http://api.namecheap.com/xml.response'}
        domain_check = root.find('.//ns:DomainCheckResult', ns)
        
        if domain_check is None:
            raise Exception(f"Could not find domain check result for {domain}")
        
        return domain_check.get('Available') == 'true'
    
    def get_domain_pricing(self, domain: str) -> Dict[str, float]:
        """Get pricing information for a domain"""
        tld = domain.split('.')[-1].upper()
        
        params = {
            'ProductType': 'DOMAIN',
            'ProductCategory': 'DOMAINS',
            'ActionName': 'REGISTER',
            'ProductName': tld
        }
        
        root = self._make_request('namecheap.users.getPricing', params)
        
        # Find pricing for the TLD
        ns = {'ns': 'http://api.namecheap.com/xml.response'}
        
        # Look for the specific TLD in the response
        for product in root.findall('.//ns:Product', ns):
            product_name = product.get('Name')
            if product_name and product_name.lower() == tld.lower():
                for price in product.findall('.//ns:Price', ns):
                    duration = price.get('Duration')
                    if duration == '1':
                        price_val = float(price.get('Price', 0))
                        
                        # Get renewal price (might be in a separate call or same structure)
                        renew_val = price_val  # Use same price for renewal if not specified
                        
                        return {
                            'register': price_val,
                            'renew': renew_val
                        }
        
        raise Exception(f"No pricing information found for .{tld} domains")
    
    def get_account_balance(self) -> float:
        """Get current account balance"""
        root = self._make_request('namecheap.users.getBalances', {})
        
        ns = {'ns': 'http://api.namecheap.com/xml.response'}
        balance_result = root.find('.//ns:UserGetBalancesResult', ns)
        
        if balance_result is not None:
            available_balance = balance_result.get('AvailableBalance')
            if available_balance:
                return float(available_balance)
        
        raise Exception("Could not retrieve account balance")
    
    def register_domain(self, domain: str, years: int = 1, 
                       registrant_info: Optional[Dict[str, str]] = None) -> bool:
        """Register a domain"""
        if not registrant_info:
            registrant_info = {
                'FirstName': 'John',
                'LastName': 'Doe',
                'Address1': '123 Main St',
                'City': 'Anytown',
                'StateProvince': 'NY',
                'PostalCode': '12345',
                'Country': 'US',
                'Phone': '+1.5551234567',
                'EmailAddress': 'john.doe@example.com'
            }
        
        params = {
            'DomainName': domain,
            'Years': str(years),
            **{f'Registrant{key}': value for key, value in registrant_info.items()},
            **{f'Tech{key}': value for key, value in registrant_info.items()},
            **{f'Admin{key}': value for key, value in registrant_info.items()},
            **{f'AuxBilling{key}': value for key, value in registrant_info.items()}
        }
        
        root = self._make_request('namecheap.domains.create', params)
        
        # Check if registration was successful
        ns = {'ns': 'http://api.namecheap.com/xml.response'}
        domain_create = root.find('.//ns:DomainCreateResult', ns)
        if domain_create is None:
            raise Exception(f"Could not find domain creation result for {domain}")
        
        return domain_create.get('Registered') == 'true'
    
    def set_dns_servers(self, domain: str, nameservers: list) -> bool:
        """Set DNS servers for a domain"""
        sld, tld = domain.split('.', 1)
        
        params = {
            'SLD': sld,
            'TLD': tld,
            'Nameservers': ','.join(nameservers)
        }
        
        root = self._make_request('namecheap.domains.dns.setCustom', params)
        
        # Check if DNS update was successful
        ns = {'ns': 'http://api.namecheap.com/xml.response'}
        dns_result = root.find('.//ns:DomainDNSSetCustomResult', ns)
        if dns_result is None:
            raise Exception(f"Could not find DNS update result for {domain}")
        
        return dns_result.get('Updated') == 'true'