namecheap-python 1.2.0__py3-none-any.whl → 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
namecheap/__init__.py CHANGED
@@ -18,12 +18,13 @@ from .models import (
18
18
  DNSRecord,
19
19
  Domain,
20
20
  DomainCheck,
21
+ DomainContacts,
21
22
  DomainInfo,
22
23
  EmailForward,
23
24
  Nameservers,
24
25
  )
25
26
 
26
- __version__ = "1.2.0"
27
+ __version__ = "1.3.0"
27
28
  __all__ = [
28
29
  "AccountBalance",
29
30
  "ConfigurationError",
@@ -31,6 +32,7 @@ __all__ = [
31
32
  "DNSRecord",
32
33
  "Domain",
33
34
  "DomainCheck",
35
+ "DomainContacts",
34
36
  "DomainInfo",
35
37
  "EmailForward",
36
38
  "Namecheap",
namecheap/_api/dns.py CHANGED
@@ -528,3 +528,41 @@ class DnsAPI(BaseAPI):
528
528
  EmailForward(mailbox=f.get("@mailbox", ""), forward_to=f.get("#text", ""))
529
529
  for f in forwards
530
530
  ]
531
+
532
+ def set_email_forwarding(
533
+ self, domain: str, rules: list[EmailForward] | list[dict[str, str]]
534
+ ) -> bool:
535
+ """
536
+ Set email forwarding rules for a domain. Replaces all existing rules.
537
+
538
+ Args:
539
+ domain: Domain name
540
+ rules: List of EmailForward or dicts with 'mailbox' and 'forward_to' keys
541
+
542
+ Returns:
543
+ True if successful
544
+
545
+ Examples:
546
+ >>> nc.dns.set_email_forwarding("example.com", [
547
+ ... EmailForward(mailbox="info", forward_to="me@gmail.com"),
548
+ ... EmailForward(mailbox="support", forward_to="help@gmail.com"),
549
+ ... ])
550
+ """
551
+ assert rules, "At least one forwarding rule is required"
552
+
553
+ params: dict[str, Any] = {"DomainName": domain}
554
+ for i, rule in enumerate(rules, 1):
555
+ if isinstance(rule, EmailForward):
556
+ params[f"MailBox{i}"] = rule.mailbox
557
+ params[f"ForwardTo{i}"] = rule.forward_to
558
+ else:
559
+ params[f"MailBox{i}"] = rule["mailbox"]
560
+ params[f"ForwardTo{i}"] = rule["forward_to"]
561
+
562
+ result: Any = self._request(
563
+ "namecheap.domains.dns.setEmailForwarding",
564
+ params,
565
+ path="DomainDNSSetEmailForwardingResult",
566
+ )
567
+
568
+ return bool(result and result.get("@IsSuccess", "false").lower() == "true")
namecheap/_api/domains.py CHANGED
@@ -9,7 +9,7 @@ from typing import Any
9
9
  import tldextract
10
10
 
11
11
  from namecheap.logging import logger
12
- from namecheap.models import Contact, Domain, DomainCheck, DomainInfo
12
+ from namecheap.models import Contact, Domain, DomainCheck, DomainContacts, DomainInfo
13
13
 
14
14
  from .base import BaseAPI
15
15
 
@@ -156,6 +156,52 @@ class DomainsAPI(BaseAPI):
156
156
 
157
157
  return DomainInfo.model_validate(flat)
158
158
 
159
+ def get_contacts(self, domain: str) -> DomainContacts:
160
+ """
161
+ Get contact information for a domain.
162
+
163
+ Args:
164
+ domain: Domain name
165
+
166
+ Returns:
167
+ DomainContacts with registrant, tech, admin, and aux_billing contacts
168
+
169
+ Examples:
170
+ >>> contacts = nc.domains.get_contacts("example.com")
171
+ >>> print(contacts.registrant.email)
172
+ """
173
+ result: Any = self._request(
174
+ "namecheap.domains.getContacts",
175
+ {"DomainName": domain},
176
+ path="DomainContactsResult",
177
+ )
178
+
179
+ assert result, f"API returned empty result for {domain} getContacts"
180
+
181
+ def parse_contact(data: dict[str, Any]) -> Contact:
182
+ return Contact.model_validate(
183
+ {
184
+ "FirstName": data.get("FirstName", ""),
185
+ "LastName": data.get("LastName", ""),
186
+ "Organization": data.get("Organization"),
187
+ "Address1": data.get("Address1", ""),
188
+ "Address2": data.get("Address2"),
189
+ "City": data.get("City", ""),
190
+ "StateProvince": data.get("StateProvince", ""),
191
+ "PostalCode": data.get("PostalCode", ""),
192
+ "Country": data.get("Country", ""),
193
+ "Phone": data.get("Phone", ""),
194
+ "EmailAddress": data.get("EmailAddress", ""),
195
+ }
196
+ )
197
+
198
+ return DomainContacts(
199
+ registrant=parse_contact(result.get("Registrant", {})),
200
+ tech=parse_contact(result.get("Tech", {})),
201
+ admin=parse_contact(result.get("Admin", {})),
202
+ aux_billing=parse_contact(result.get("AuxBilling", {})),
203
+ )
204
+
159
205
  def register(
160
206
  self,
161
207
  domain: str,
namecheap/models.py CHANGED
@@ -336,6 +336,17 @@ class Contact(BaseModel):
336
336
  model_config = ConfigDict(populate_by_name=True)
337
337
 
338
338
 
339
+ class DomainContacts(BaseModel):
340
+ """Contact information for all roles on a domain."""
341
+
342
+ registrant: Contact
343
+ tech: Contact
344
+ admin: Contact
345
+ aux_billing: Contact
346
+
347
+ model_config = ConfigDict(populate_by_name=True)
348
+
349
+
339
350
  class Config(BaseModel):
340
351
  """Client configuration with validation."""
341
352
 
namecheap_cli/__main__.py CHANGED
@@ -919,6 +919,103 @@ def dns_email_forwarding(config: Config, domain: str) -> None:
919
919
  sys.exit(1)
920
920
 
921
921
 
922
+ @dns_group.command("set-email-forwarding")
923
+ @click.argument("domain")
924
+ @click.argument("rules", nargs=-1, required=True)
925
+ @click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
926
+ @pass_config
927
+ def dns_set_email_forwarding(
928
+ config: Config, domain: str, rules: tuple[str, ...], yes: bool
929
+ ) -> None:
930
+ """Set email forwarding rules. Replaces all existing rules.
931
+
932
+ Rules are in mailbox:forward_to format.
933
+
934
+ Example:
935
+ namecheap-cli dns set-email-forwarding example.com info:me@gmail.com support:help@gmail.com
936
+ """
937
+ nc = config.init_client()
938
+
939
+ parsed = []
940
+ for rule in rules:
941
+ assert ":" in rule, f"Invalid rule format '{rule}', expected mailbox:forward_to"
942
+ mailbox, forward_to = rule.split(":", 1)
943
+ parsed.append({"mailbox": mailbox, "forward_to": forward_to})
944
+
945
+ try:
946
+ if not yes and not config.quiet:
947
+ console.print(f"\n[yellow]Setting email forwarding for {domain}:[/yellow]")
948
+ for p in parsed:
949
+ console.print(f" • {p['mailbox']}@{domain} → {p['forward_to']}")
950
+ console.print()
951
+
952
+ if not Confirm.ask("Continue?", default=True):
953
+ console.print("[yellow]Cancelled[/yellow]")
954
+ return
955
+
956
+ with Progress(
957
+ SpinnerColumn(),
958
+ TextColumn("[progress.description]{task.description}"),
959
+ transient=True,
960
+ ) as progress:
961
+ progress.add_task(f"Setting email forwarding for {domain}...", total=None)
962
+ success = nc.dns.set_email_forwarding(domain, parsed)
963
+
964
+ if success:
965
+ console.print("[green]✅ Email forwarding updated successfully![/green]")
966
+ else:
967
+ console.print("[red]❌ Failed to update email forwarding[/red]")
968
+ sys.exit(1)
969
+
970
+ except NamecheapError as e:
971
+ console.print(f"[red]❌ Error: {e}[/red]")
972
+ sys.exit(1)
973
+
974
+
975
+ @domain_group.command("contacts")
976
+ @click.argument("domain")
977
+ @pass_config
978
+ def domain_contacts(config: Config, domain: str) -> None:
979
+ """Show contact information for a domain."""
980
+ nc = config.init_client()
981
+
982
+ try:
983
+ with Progress(
984
+ SpinnerColumn(),
985
+ TextColumn("[progress.description]{task.description}"),
986
+ transient=True,
987
+ ) as progress:
988
+ progress.add_task(f"Getting contacts for {domain}...", total=None)
989
+ contacts = nc.domains.get_contacts(domain)
990
+
991
+ if config.output_format == "table":
992
+ for role, contact in [
993
+ ("Registrant", contacts.registrant),
994
+ ("Tech", contacts.tech),
995
+ ("Admin", contacts.admin),
996
+ ("Billing", contacts.aux_billing),
997
+ ]:
998
+ console.print(f"\n[bold cyan]{role}[/bold cyan]")
999
+ console.print(f" {contact.first_name} {contact.last_name}")
1000
+ if contact.organization:
1001
+ console.print(f" {contact.organization}")
1002
+ console.print(f" {contact.email}")
1003
+ console.print(f" {contact.phone}")
1004
+ console.print(f" {contact.address1}")
1005
+ if contact.address2:
1006
+ console.print(f" {contact.address2}")
1007
+ console.print(
1008
+ f" {contact.city}, {contact.state_province} {contact.postal_code}"
1009
+ )
1010
+ console.print(f" {contact.country}")
1011
+ else:
1012
+ output_formatter(contacts.model_dump(), config.output_format)
1013
+
1014
+ except NamecheapError as e:
1015
+ console.print(f"[red]❌ Error: {e}[/red]")
1016
+ sys.exit(1)
1017
+
1018
+
922
1019
  @cli.group("account")
923
1020
  def account_group() -> None:
924
1021
  """Account management commands."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: namecheap-python
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: A friendly Python SDK for Namecheap API
5
5
  Project-URL: Homepage, https://github.com/adriangalilea/namecheap-python
6
6
  Project-URL: Repository, https://github.com/adriangalilea/namecheap-python
@@ -380,9 +380,24 @@ print(bal.funds_required_for_auto_renew) # Decimal('20.16')
380
380
  ### Email Forwarding
381
381
 
382
382
  ```python
383
+ # Read
383
384
  rules = nc.dns.get_email_forwarding("example.com")
384
385
  for r in rules:
385
386
  print(f"{r.mailbox} -> {r.forward_to}")
387
+
388
+ # Write (replaces all existing rules)
389
+ nc.dns.set_email_forwarding("example.com", [
390
+ EmailForward(mailbox="info", forward_to="me@gmail.com"),
391
+ EmailForward(mailbox="support", forward_to="help@gmail.com"),
392
+ ])
393
+ ```
394
+
395
+ ### Domain Contacts
396
+
397
+ ```python
398
+ contacts = nc.domains.get_contacts("example.com")
399
+ print(f"{contacts.registrant.first_name} {contacts.registrant.last_name}")
400
+ print(contacts.registrant.email)
386
401
  ```
387
402
 
388
403
  ### Domain Management
@@ -462,11 +477,10 @@ nc.dns.builder().a("www", "192.0.2.1", ttl=1800) # Shows as "30 min"
462
477
 
463
478
  | API | Status | Methods |
464
479
  |-----|--------|---------|
465
- | `namecheap.domains.*` | ✅ Done | `check`, `list`, `getInfo`, `register`, `renew`, `setContacts`, `lock`/`unlock` |
466
- | `namecheap.domains.dns.*` | ✅ Done | `getHosts`, `setHosts` (builder pattern), `add`, `delete`, `export`, `getList`, `setCustom`, `setDefault`, `getEmailForwarding` |
480
+ | `namecheap.domains.*` | ✅ Done | `check`, `list`, `getInfo`, `getContacts`, `register`, `renew`, `setContacts`, `lock`/`unlock` |
481
+ | `namecheap.domains.dns.*` | ✅ Done | `getHosts`, `setHosts` (builder pattern), `add`, `delete`, `export`, `getList`, `setCustom`, `setDefault`, `getEmailForwarding`, `setEmailForwarding` |
467
482
  | `namecheap.users.*` | ⚠️ Partial | `getBalances`, `getPricing` (needs debugging). Planned: `changePassword`, `update`, `create`, `login`, `resetPassword` |
468
- | `namecheap.domains.*` | 🚧 Planned | `getContacts`, `getTldList`, `reactivate` |
469
- | `namecheap.domains.dns.*` | 🚧 Planned | `setEmailForwarding` |
483
+ | `namecheap.domains.*` | 🚧 Planned | `getTldList`, `reactivate` |
470
484
  | `namecheap.users.address.*` | 🚧 Planned | `create`, `delete`, `getInfo`, `getList`, `setDefault`, `update` |
471
485
  | `namecheap.ssl.*` | 🚧 Planned | `create`, `activate`, `renew`, `revoke`, `getList`, `getInfo`, `parseCSR`, `reissue`, and more |
472
486
  | `namecheap.domains.transfer.*` | 🚧 Planned | `create`, `getStatus`, `updateStatus`, `getList` |
@@ -1,16 +1,16 @@
1
- namecheap/__init__.py,sha256=S-84LTSVWDXJeqvDoUoSsq84250m5J0MXYEPJ6xH3kw,837
1
+ namecheap/__init__.py,sha256=sUX2-G_o6k1POjfJ9oV32Hi08zaPNL4WwfgrLr_2Qak,879
2
2
  namecheap/client.py,sha256=KesIaZVa9HpXvlp-hc3nr2x-sqzMPiHEa8NibiPq580,6644
3
3
  namecheap/errors.py,sha256=5bGbV1e4_jkK8YXZXbLF6GJCVUTKw1CtMl9-mz7ogZg,5010
4
4
  namecheap/logging.py,sha256=lMR1fr1dWWz3z2NFEY-vl8b52FmmhH76R2NjyifSdYA,3396
5
- namecheap/models.py,sha256=t4xloDJOgKeoERujhMRtTR0-B--ENzQsAn1WyFHyNbE,14261
5
+ namecheap/models.py,sha256=0Fhni7yKd84bBopPURIvhwmOCYMzw8tlZwHWzRRZphY,14494
6
6
  namecheap/_api/__init__.py,sha256=ymQxKCySphoeoo4s_J0tLziXttLNhOQ8AZbCzFcuAHs,36
7
7
  namecheap/_api/base.py,sha256=FoczO1Q860PaFUFv-S3IoIV2xaGVJAlchkWnmTI6dlw,6121
8
- namecheap/_api/dns.py,sha256=MtOHr0AOUgS_Vv7U8WkLBw7xuU0XWbjAF6ptitteNmk,15827
9
- namecheap/_api/domains.py,sha256=bEPshE2GN6ubd0otTwLdZCpKVT9ErmDRy8fYxS2hkIY,18272
8
+ namecheap/_api/dns.py,sha256=Hny5TsVWmmG-3rF6kb8JwboGFt2wKsd4-Z6T8GannBM,17213
9
+ namecheap/_api/domains.py,sha256=txikTYACM82-3IXg2Gqagh9fRZ8aIZiu05unNpy2Zqs,20039
10
10
  namecheap/_api/users.py,sha256=CCXSZJiPkQiLHYRAlYKTBCDG3-JSPdNkNWWww71JXV0,795
11
11
  namecheap_cli/README.md,sha256=liduIiGr8DHXGTht5swrYnvtAlcdCMQOnSdCD61g4Vw,7337
12
12
  namecheap_cli/__init__.py,sha256=nGRHc_CkO4xKhSQdAVG-koEffP8VS0TvbfbZkg7Jg4k,108
13
- namecheap_cli/__main__.py,sha256=ZOmdVQZWkA3SHw7_nGCp_j5RdJCQff4NO6h_nIjgDnY,36425
13
+ namecheap_cli/__main__.py,sha256=0vHk5aAhEzPKQ-UQOQdYqsolCF13DG3k9_z3pUbZXwk,40020
14
14
  namecheap_cli/completion.py,sha256=JTEMnceQli7TombjZkHh-IcZKW4RFRI8Yk5VynxPsEA,2777
15
15
  namecheap_dns_tui/README.md,sha256=It16ZiZh0haEeaENfF5HX0Ec4dBawdTYiAi-TiG9wi0,1690
16
16
  namecheap_dns_tui/__init__.py,sha256=-yL_1Ha41FlQcmjG-raUrZP9CjTJD3d0w2BW2X-twJg,106
@@ -19,8 +19,8 @@ namecheap_dns_tui/assets/screenshot1.png,sha256=OXO2P80ll5WRzLYgaakcNnzos8svlJoX
19
19
  namecheap_dns_tui/assets/screenshot2.png,sha256=5VN_qDMNhWEyrOqKw7vxl1h-TgmZQ_V9aph3Xmf_AFg,279194
20
20
  namecheap_dns_tui/assets/screenshot3.png,sha256=h39wSKxx1JCkgeAB7Q3_JlBcAtX1vsRFKtWtOwbBVso,220625
21
21
  namecheap_dns_tui/assets/screenshot4.png,sha256=J4nCOW16z3vaRiPbcMiiIRgV7q3XFbi_1N1ivD1Pa4Y,238068
22
- namecheap_python-1.2.0.dist-info/METADATA,sha256=R44Jl31VrMFE8lzs_YxqgGuv7sHvN0rIj9qjgdvKyv0,18433
23
- namecheap_python-1.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
24
- namecheap_python-1.2.0.dist-info/entry_points.txt,sha256=AyhiXroLUpM0Vdo_-RvH0S8o4XDPsDlsEl_65vm6DEk,96
25
- namecheap_python-1.2.0.dist-info/licenses/LICENSE,sha256=pemTblFP6BBje3bBv_yL_sr2iAqB2H0-LdWMvVIR42o,1062
26
- namecheap_python-1.2.0.dist-info/RECORD,,
22
+ namecheap_python-1.3.0.dist-info/METADATA,sha256=phjrSgjok2JzRDjOQsEit1eb9Z7WF7Da5beLiKSEbGA,18802
23
+ namecheap_python-1.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
24
+ namecheap_python-1.3.0.dist-info/entry_points.txt,sha256=AyhiXroLUpM0Vdo_-RvH0S8o4XDPsDlsEl_65vm6DEk,96
25
+ namecheap_python-1.3.0.dist-info/licenses/LICENSE,sha256=pemTblFP6BBje3bBv_yL_sr2iAqB2H0-LdWMvVIR42o,1062
26
+ namecheap_python-1.3.0.dist-info/RECORD,,