Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .sampo/changesets/quiet-beacon-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
pypi/posthog: minor
---

Add `posthog.push.send()`, `AsyncPosthog.push.send()` and the standalone `Push` and `AsyncPush` classes to send transactional push notifications from a PostHog push template to every device of one person. Sends use the client's `secret_key` (a project secret API key with the `transactional_push:write` scope), `app_host`, and email request timeout and retries.
22 changes: 22 additions & 0 deletions posthog/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
)
from .consumer import MAX_MSG_SIZE
from .emails import AsyncEmails, _client_emails, _validate_options
from .push import AsyncPush
from .feature_flag_evaluations import (
FeatureFlagEvaluations,
_EvaluatedFlagRecord,
Expand Down Expand Up @@ -306,6 +307,27 @@ def emails(self) -> AsyncEmails:
)
return self._emails

@property
def push(self) -> AsyncPush:
"""
Send transactional push notifications from PostHog push templates.

Needs ``secret_key``, a project secret API key with the ``transactional_push:write``
scope. Sends go where ``emails`` sends go and use the same timeout and
retries. A disabled or shut-down client sends nothing. See ``posthog.push``
for setup.

Examples:
```python
await client.push.send(
template_id="0190f6d2-7c1e-7b3a-9f5e-1c2d3e4f5a6b",
distinct_id="user_123",
template_data={"order_id": "1042"},
)
```
"""
return AsyncPush._over(self.emails)

def _admit_email_send(self) -> Optional[Callable[[], None]]:
# Shutdown waits for in-flight operations before it closes the email transport.
if not self._accepting or self._closed:
Expand Down
22 changes: 22 additions & 0 deletions posthog/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
tag as _context_tag,
)
from posthog.emails import Emails, _client_emails, _no_op, _validate_options
from posthog.push import Push
from posthog.exception_capture import ExceptionCapture
from posthog._logging import _configure_posthog_logging
from posthog.exception_utils import (
Expand Down Expand Up @@ -1726,6 +1727,27 @@ def emails(self) -> Emails:
)
return self._emails

@property
def push(self) -> Push:
"""
Send transactional push notifications from PostHog push templates.

Needs ``secret_key``, a project secret API key with the ``transactional_push:write``
scope. Sends go where ``emails`` sends go and use the same timeout and
retries. A disabled or shut-down client sends nothing. See ``posthog.push``
for setup.

Examples:
```python
client.push.send(
template_id="0190f6d2-7c1e-7b3a-9f5e-1c2d3e4f5a6b",
distinct_id="user_123",
template_data={"order_id": "1042"},
)
```
"""
return Push._over(self.emails)

@property
def queue(self) -> Queue:
"""The analytics lane's queue (kept for backwards compatibility)."""
Expand Down
21 changes: 20 additions & 1 deletion posthog/emails.py
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,16 @@ def _deliver(self, prepared: _PreparedSend, secret_key: str) -> SendEmailResult:
prepared.max_retries,
)

def _queue(
self, prepared: _PreparedSend, not_done: str, key_needed: str
) -> SendEmailResult:
# posthog.push sends through this, so a push gets the same host, key, timeout and retries.
finish = self._start(not_done)
try:
return self._deliver(prepared, self._secret_key(key_needed))
finally:
finish()

def get(self, message_id: Union[str, UUID]) -> EmailStatus:
"""Read what happened to an email that :meth:`send` queued.

Expand Down Expand Up @@ -1104,6 +1114,15 @@ async def _deliver(
prepared.max_retries,
)

async def _queue(
self, prepared: _PreparedSend, not_done: str, key_needed: str
) -> SendEmailResult:
finish = self._start(not_done)
try:
return await self._deliver(prepared, self._secret_key(key_needed))
finally:
finish()

async def get(self, message_id: Union[str, UUID]) -> EmailStatus:
"""Read what happened to an email that :meth:`send` queued.

Expand Down Expand Up @@ -1234,7 +1253,7 @@ def _client_emails(
if not is_cloud:
# A custom host is often an ingestion proxy, which must not receive the secret key.
emails._host_error = (
"The client's host is not PostHog Cloud, so the client can't tell where to send email. "
"The client's host is not PostHog Cloud, so the client can't tell where to send requests. "
"Set app_host to your PostHog app host on the client."
)
emails._parent_disabled = is_disabled
Expand Down
Loading
Loading