Skip to content

Commit 80e94f2

Browse files
committed
server/integrations/plain: add answer snippets
1 parent 0d32988 commit 80e94f2

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

server/polar/integrations/plain/schemas.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class CustomerCardKey(StrEnum):
99
organization = "organization"
1010
customer = "customer"
1111
order = "order"
12+
snippets = "snippets"
1213

1314

1415
class CustomerCardCustomer(BaseModel):

server/polar/integrations/plain/service.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ async def get_cards(
125125
_card_getter_task(self._get_order_card(session, request))
126126
)
127127
)
128+
if CustomerCardKey.snippets in request.cardKeys:
129+
tasks.append(
130+
tg.create_task(
131+
_card_getter_task(self._get_snippets_card(session, request))
132+
)
133+
)
128134

129135
cards = [card for task in tasks if (card := task.result()) is not None]
130136
return CustomerCardsResponse(cards=cards)
@@ -1056,6 +1062,136 @@ def _get_order_container(order: Order) -> ComponentContainerInput:
10561062
],
10571063
)
10581064

1065+
async def _get_snippets_card(
1066+
self, session: AsyncSession, request: CustomerCardsRequest
1067+
) -> CustomerCard | None:
1068+
email = request.customer.email
1069+
1070+
statement = (
1071+
select(Organization)
1072+
.join(Customer, Customer.organization_id == Organization.id)
1073+
.where(func.lower(Customer.email) == email.lower())
1074+
)
1075+
result = await session.execute(statement)
1076+
organizations = result.unique().scalars().all()
1077+
1078+
if len(organizations) == 0:
1079+
return None
1080+
1081+
snippets: list[tuple[str, str]] = [
1082+
(
1083+
"Looping In",
1084+
(
1085+
"Hi 👋🏻\n"
1086+
"I'm looping in the {organization_name} team to the conversation so that they can help you. "
1087+
"Please allow them up to 48 hours to get back to you ([guidelines for merchants on Polar](https://polar.sh/docs/merchant-of-record/account-reviews#operational-guidelines))."
1088+
),
1089+
),
1090+
(
1091+
"Cancellation Portal",
1092+
(
1093+
"Hi 👋🏻\n"
1094+
"You can perform the cancellation on the following URL: https://polar.sh/{organization_slug}/portal\n"
1095+
),
1096+
),
1097+
(
1098+
"Follow-up 48 hours",
1099+
(
1100+
"Hi 👋🏻\n"
1101+
"I'm looping in the {organization_name} team again to the conversation. "
1102+
"Please allow them another 48 hours to get back to you before we [proceed with the documented resolution](https://polar.sh/docs/merchant-of-record/account-reviews#expected-responsiveness)."
1103+
),
1104+
),
1105+
(
1106+
"Follow-up Reply All",
1107+
(
1108+
"Hi 👋🏻\n"
1109+
"I'm looping in the {organization_name} team again to the conversation. "
1110+
'Please use "Reply All" so as to keep everyone involved in the conversation.'
1111+
),
1112+
),
1113+
]
1114+
1115+
def _get_snippet_container(
1116+
organization: Organization,
1117+
) -> ComponentContainerInput:
1118+
snippets_rows: list[ComponentContainerContentInput] = [
1119+
ComponentContainerContentInput(
1120+
component_text=ComponentTextInput(
1121+
text=f"Snippets for {organization.name}",
1122+
)
1123+
),
1124+
ComponentContainerContentInput(
1125+
component_divider=ComponentDividerInput(
1126+
divider_spacing_size=ComponentDividerSpacingSize.M
1127+
)
1128+
),
1129+
]
1130+
for i, (snippet_name, snippet_text) in enumerate(snippets):
1131+
text = snippet_text.format(
1132+
organization_name=organization.name,
1133+
organization_slug=organization.slug,
1134+
)
1135+
snippets_rows.append(
1136+
ComponentContainerContentInput(
1137+
component_row=ComponentRowInput(
1138+
row_main_content=[
1139+
ComponentRowContentInput(
1140+
component_text=ComponentTextInput(
1141+
text_size=ComponentTextSize.S,
1142+
text_color=ComponentTextColor.MUTED,
1143+
text=snippet_name,
1144+
),
1145+
),
1146+
ComponentRowContentInput(
1147+
component_text=ComponentTextInput(text=text)
1148+
),
1149+
],
1150+
row_aside_content=[
1151+
ComponentRowContentInput(
1152+
component_copy_button=ComponentCopyButtonInput(
1153+
copy_button_value=text,
1154+
copy_button_tooltip_label="Copy Snippet",
1155+
)
1156+
)
1157+
],
1158+
)
1159+
)
1160+
)
1161+
if i < len(snippets) - 1:
1162+
snippets_rows.append(
1163+
ComponentContainerContentInput(
1164+
component_spacer=ComponentSpacerInput(
1165+
spacer_size=ComponentSpacerSize.M
1166+
)
1167+
)
1168+
)
1169+
1170+
return ComponentContainerInput(container_content=snippets_rows)
1171+
1172+
components: list[ComponentInput] = []
1173+
for i, organization in enumerate(organizations):
1174+
components.append(
1175+
ComponentInput(component_container=_get_snippet_container(organization))
1176+
)
1177+
if i < len(organizations) - 1:
1178+
components.append(
1179+
ComponentInput(
1180+
component_divider=ComponentDividerInput(
1181+
divider_spacing_size=ComponentDividerSpacingSize.M
1182+
)
1183+
)
1184+
)
1185+
1186+
return CustomerCard(
1187+
key=CustomerCardKey.snippets,
1188+
timeToLiveSeconds=86400,
1189+
components=[
1190+
component.model_dump(by_alias=True, exclude_none=True)
1191+
for component in components
1192+
],
1193+
)
1194+
10591195
@contextlib.asynccontextmanager
10601196
async def _get_plain_client(self) -> AsyncIterator[Plain]:
10611197
async with httpx.AsyncClient(

0 commit comments

Comments
 (0)