|
1 | 1 | from __future__ import annotations |
| 2 | + |
| 3 | +from typing import Any |
| 4 | + |
2 | 5 | import requests |
| 6 | + |
| 7 | + |
| 8 | +class YournotifyApiError(Exception): |
| 9 | + def __init__(self, message: str, status_code: int, body: Any) -> None: |
| 10 | + super().__init__(message) |
| 11 | + self.status_code = status_code |
| 12 | + self.body = body |
| 13 | + |
| 14 | + |
| 15 | +def _normalize_list(value: Any, key: str) -> list[dict[str, Any]]: |
| 16 | + if value is None: |
| 17 | + return [] |
| 18 | + items = value if isinstance(value, list) else [value] |
| 19 | + return [{key: item} if isinstance(item, str) else item for item in items] |
| 20 | + |
| 21 | + |
3 | 22 | class Yournotify: |
4 | 23 | def __init__(self, api_key: str, api_url: str = "https://api.yournotify.com/") -> None: |
| 24 | + if not api_key or not api_key.strip(): |
| 25 | + raise ValueError("Yournotify API key is required.") |
5 | 26 | self.api_key = api_key |
6 | 27 | self.api_url = api_url.rstrip("/") + "/" |
7 | | - def set_api_url(self, api_url: str): self.api_url = api_url.rstrip("/") + "/"; return self |
8 | | - def request(self, endpoint: str, method: str = "GET", data: dict | None = None) -> dict: |
| 28 | + |
| 29 | + def set_api_url(self, api_url: str): |
| 30 | + self.api_url = api_url.rstrip("/") + "/" |
| 31 | + return self |
| 32 | + |
| 33 | + def request(self, endpoint: str, method: str = "GET", data: dict[str, Any] | None = None) -> dict[str, Any]: |
9 | 34 | url = self.api_url + endpoint.lstrip("/") |
10 | | - headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"} |
11 | | - kwargs = {"headers": headers, "timeout": 30} |
| 35 | + headers = {"Authorization": f"Bearer {self.api_key}", "Accept": "application/json"} |
| 36 | + kwargs: dict[str, Any] = {"headers": headers, "timeout": 30} |
12 | 37 | method = method.upper() |
13 | | - if method == "GET": kwargs["params"] = data or {} |
14 | | - elif data is not None: kwargs["json"] = data |
15 | | - return requests.request(method, url, **kwargs).json() |
16 | | - def send_email(self, name, subject, html, text="", status="draft", from_email="", to=None): |
17 | | - lists = to if isinstance(to, list) else ([{"email": to}] if to else []) |
18 | | - return self.request("campaigns/email", "POST", {"name": name, "subject": subject, "html": html, "body": html, "text": text, "from": from_email, "from_email": from_email, "status": status, "channel": "email", "lists": lists}) |
19 | | - def send_sms(self, name, from_sender, text, status="draft", to=None): |
20 | | - lists = to if isinstance(to, list) else ([{"telephone": to}] if to else []) |
21 | | - return self.request("campaigns/sms", "POST", {"name": name, "from": from_sender, "sender": from_sender, "text": text, "body": text, "status": status, "channel": "sms", "lists": lists}) |
22 | | - def add_contact(self, email=None, telephone=None, list_id=None, name="", attribs=None): |
23 | | - lists = [] if list_id is None else (list_id if isinstance(list_id, list) else [list_id]) |
24 | | - return self.request("contacts", "POST", {"email": email, "telephone": telephone, "lists": lists, "name": name, "attribs": attribs or {}}) |
| 38 | + if method == "GET": |
| 39 | + kwargs["params"] = data or {} |
| 40 | + elif data is not None: |
| 41 | + headers["Content-Type"] = "application/json" |
| 42 | + kwargs["json"] = data |
| 43 | + response = requests.request(method, url, **kwargs) |
| 44 | + body = response.json() if response.content else {} |
| 45 | + if not response.ok: |
| 46 | + raise YournotifyApiError(body.get("message") or f"Yournotify API request failed with status {response.status_code}.", response.status_code, body) |
| 47 | + return body |
| 48 | + |
| 49 | + def validate_auth(self): return self.request("auth/me") |
| 50 | + def get_profile(self): return self.validate_auth() |
| 51 | + def create_campaign(self, data=None): return self.request("campaigns", "POST", data or {}) |
| 52 | + def send_email(self, name, subject, html, text="", status="draft", from_email="", to=None): return self.create_campaign({"name": name, "subject": subject, "html": html, "body": html, "text": text, "from": from_email, "from_email": from_email, "status": status, "channel": "email", "lists": _normalize_list(to, "email")}) |
| 53 | + def send_sms(self, name, from_sender, text, status="draft", to=None): return self.create_campaign({"name": name, "from": from_sender, "sender": from_sender, "text": text, "body": text, "status": status, "channel": "sms", "lists": _normalize_list(to, "telephone")}) |
| 54 | + def send_whatsapp(self, data=None): return self.create_campaign({**(data or {}), "channel": "whatsapp"}) |
| 55 | + def send_push(self, data=None): return self.create_campaign({**(data or {}), "channel": "push"}) |
| 56 | + def send_inapp(self, data=None): return self.create_campaign({**(data or {}), "channel": "inapp"}) |
| 57 | + def test_campaign(self, data=None): return self.request("campaigns/test", "POST", data or {}) |
| 58 | + def get_campaign(self, campaign_id): return self.request(f"campaigns/{campaign_id}") |
| 59 | + def get_campaigns(self, channel=None, params=None): return self.request("campaigns", "GET", ({"channel": channel} if channel else {}) | (params or {})) |
| 60 | + def update_campaign(self, campaign_id, data=None): return self.request(f"campaigns/{campaign_id}", "PUT", data or {}) |
| 61 | + def delete_campaign(self, campaign_id): return self.request(f"campaigns/{campaign_id}", "DELETE") |
| 62 | + def get_campaign_stats(self, campaign_id, channel="email"): return self.request(f"campaigns/{campaign_id}/analytics/stats", "GET", {"channel": channel}) |
| 63 | + def get_campaign_reports(self, campaign_id, channel="email"): return self.request(f"campaigns/{campaign_id}/analytics/reports", "GET", {"channel": channel}) |
| 64 | + def add_contact(self, email=None, telephone=None, list_id=None, name="", attribs=None): return self.request("contacts", "POST", {"email": email, "telephone": telephone, "lists": [] if list_id is None else (list_id if isinstance(list_id, list) else [list_id]), "name": name, "attribs": attribs or {}}) |
25 | 65 | def get_contact(self, contact_id): return self.request(f"contacts/{contact_id}") |
26 | 66 | def get_contacts(self, params=None): return self.request("contacts", "GET", params or {}) |
27 | 67 | def update_contact(self, contact_id, email=None, telephone=None, lists=None, name="", attribs=None): return self.request(f"contacts/{contact_id}", "PUT", {"email": email, "telephone": telephone, "lists": lists or [], "name": name, "attribs": attribs or {}}) |
28 | 68 | def delete_contact(self, contact_id): return self.request(f"contacts/{contact_id}", "DELETE") |
| 69 | + def validate_contacts(self, data=None): return self.request("contacts/validate", "POST", data or {}) |
29 | 70 | def add_list(self, title, type="public", optin="single"): return self.request("lists", "POST", {"name": title, "title": title, "type": type, "optin": optin}) |
30 | 71 | def get_list(self, list_id): return self.request(f"lists/{list_id}") |
31 | 72 | def get_lists(self, params=None): return self.request("lists", "GET", params or {}) |
32 | 73 | def update_list(self, list_id, title): return self.request(f"lists/{list_id}", "PUT", {"name": title, "title": title}) |
33 | 74 | def delete_list(self, list_id): return self.request(f"lists/{list_id}", "DELETE") |
34 | | - def get_campaign(self, campaign_id): return self.request(f"campaigns/{campaign_id}") |
35 | | - def get_campaigns(self, channel, params=None): return self.request("campaigns", "GET", {"channel": channel, **(params or {})}) |
36 | | - def update_campaign(self, campaign_id, data=None): return self.request(f"campaigns/{campaign_id}", "PUT", data or {}) |
37 | | - def delete_campaign(self, campaign_id): return self.request(f"campaigns/{campaign_id}", "DELETE") |
38 | | - def get_campaign_stats(self, ids, channel="email"): |
39 | | - id_param = ",".join(str(v) for v in ids) if isinstance(ids, list) else ids |
40 | | - return self.request("campaigns/analytics/stats", "GET", {"id": id_param, "channel": channel}) |
41 | | - def get_campaign_reports(self, ids, channel="email"): |
42 | | - id_param = ",".join(str(v) for v in ids) if isinstance(ids, list) else ids |
43 | | - return self.request("campaigns/analytics/reports", "GET", {"id": id_param, "channel": channel}) |
44 | | - def get_profile(self): return self.request("account/profile") |
| 75 | + def get_rewards(self, params=None): return self.request("rewards", "GET", params or {}) |
| 76 | + def create_reward(self, data=None): return self.request("rewards", "POST", data or {}) |
| 77 | + def send_reward(self, data=None): return self.request("rewards/send", "POST", data or {}) |
| 78 | + def get_reward_products(self, params=None): return self.request("rewards/products", "GET", params or {}) |
| 79 | + def get_reward_analytics(self, reward_id): return self.request(f"rewards/{reward_id}/analytics") |
| 80 | + def get_reward_submissions(self, reward_id, params=None): return self.request(f"rewards/{reward_id}/submissions", "GET", params or {}) |
| 81 | + def get_loyalty_programs(self, params=None): return self.request("loyalty/programs", "GET", params or {}) |
| 82 | + def get_loyalty_program(self, program_id): return self.request(f"loyalty/programs/{program_id}") |
| 83 | + def create_loyalty_program(self, data=None): return self.request("loyalty/programs", "POST", data or {}) |
| 84 | + def update_loyalty_program(self, program_id, data=None): return self.request(f"loyalty/programs/{program_id}", "PUT", data or {}) |
| 85 | + def get_loyalty_members(self, program_id, params=None): return self.request(f"loyalty/programs/{program_id}/members", "GET", params or {}) |
| 86 | + def adjust_loyalty_points(self, program_id, data=None): return self.request(f"loyalty/programs/{program_id}/points", "POST", data or {}) |
| 87 | + def track_loyalty_event(self, program_id, data=None): return self.request(f"loyalty/programs/{program_id}/events", "POST", data or {}) |
| 88 | + def redeem_loyalty_reward(self, program_id, data=None): return self.request(f"loyalty/programs/{program_id}/redeem", "POST", data or {}) |
| 89 | + def get_referral_programs(self, params=None): return self.request("referrals/programs", "GET", params or {}) |
| 90 | + def get_referral_program(self, program_id): return self.request(f"referrals/programs/{program_id}") |
| 91 | + def create_referral_program(self, data=None): return self.request("referrals/programs", "POST", data or {}) |
| 92 | + def update_referral_program(self, program_id, data=None): return self.request(f"referrals/programs/{program_id}", "PUT", data or {}) |
| 93 | + def delete_referral_program(self, program_id): return self.request(f"referrals/programs/{program_id}", "DELETE") |
| 94 | + def get_referral_advocates(self, program_id, params=None): return self.request(f"referrals/programs/{program_id}/advocates", "GET", params or {}) |
| 95 | + def add_referral_advocate(self, program_id, data=None): return self.request(f"referrals/programs/{program_id}/advocates", "POST", data or {}) |
| 96 | + def track_referral_event(self, program_id, data=None): return self.request(f"referrals/programs/{program_id}/events", "POST", data or {}) |
| 97 | + def get_referral_analytics(self, program_id, params=None): return self.request(f"referrals/programs/{program_id}/analytics", "GET", params or {}) |
| 98 | + def identify(self, data=None): return self.request("automations/identify", "POST", data or {}) |
| 99 | + def track(self, data=None): return self.request("automations/events", "POST", data or {}) |
0 commit comments