hushscript 1.0.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.
Files changed (187) hide show
  1. hushscript/__init__.py +97 -0
  2. hushscript/_client.py +687 -0
  3. hushscript/_clock.py +36 -0
  4. hushscript/_config.py +139 -0
  5. hushscript/_errors.py +563 -0
  6. hushscript/_guard.py +61 -0
  7. hushscript/_http.py +624 -0
  8. hushscript/_json.py +55 -0
  9. hushscript/_log.py +155 -0
  10. hushscript/_models.py +144 -0
  11. hushscript/_pacing.py +51 -0
  12. hushscript/_redact.py +140 -0
  13. hushscript/_response.py +67 -0
  14. hushscript/_retry.py +205 -0
  15. hushscript/_shape.py +197 -0
  16. hushscript/_shapes.py +248 -0
  17. hushscript/_version.py +1 -0
  18. hushscript/format.py +224 -0
  19. hushscript/generated/__init__.py +8 -0
  20. hushscript/generated/api/__init__.py +1 -0
  21. hushscript/generated/api/default/__init__.py +1 -0
  22. hushscript/generated/api/default/buy_minutes.py +248 -0
  23. hushscript/generated/api/default/claim_agent_account.py +238 -0
  24. hushscript/generated/api/default/complete_upload.py +238 -0
  25. hushscript/generated/api/default/create_agent_account_signup.py +261 -0
  26. hushscript/generated/api/default/create_setup_session.py +244 -0
  27. hushscript/generated/api/default/create_upload.py +225 -0
  28. hushscript/generated/api/default/get_account.py +169 -0
  29. hushscript/generated/api/default/get_job.py +198 -0
  30. hushscript/generated/api/default/get_org.py +169 -0
  31. hushscript/generated/api/default/get_transcript.py +226 -0
  32. hushscript/generated/api/default/get_transcription_context.py +173 -0
  33. hushscript/generated/api/default/get_upload_part_urls.py +246 -0
  34. hushscript/generated/api/default/heartbeat_upload.py +197 -0
  35. hushscript/generated/api/default/lift_transcript_hold.py +206 -0
  36. hushscript/generated/api/default/list_credit_ledger.py +229 -0
  37. hushscript/generated/api/default/list_deleted_transcripts.py +237 -0
  38. hushscript/generated/api/default/list_holds.py +233 -0
  39. hushscript/generated/api/default/list_jobs.py +249 -0
  40. hushscript/generated/api/default/list_org_audit.py +221 -0
  41. hushscript/generated/api/default/list_org_invoices.py +221 -0
  42. hushscript/generated/api/default/list_org_ledger.py +221 -0
  43. hushscript/generated/api/default/list_org_matters.py +247 -0
  44. hushscript/generated/api/default/list_org_members.py +221 -0
  45. hushscript/generated/api/default/list_org_transcripts.py +233 -0
  46. hushscript/generated/api/default/list_packs.py +169 -0
  47. hushscript/generated/api/default/list_saved_cards.py +169 -0
  48. hushscript/generated/api/default/list_transactions.py +225 -0
  49. hushscript/generated/api/default/list_transcripts.py +225 -0
  50. hushscript/generated/api/default/org_audit_csv.py +163 -0
  51. hushscript/generated/api/default/org_ledger_csv.py +163 -0
  52. hushscript/generated/api/default/org_usage_report.py +247 -0
  53. hushscript/generated/api/default/org_usage_report_csv.py +229 -0
  54. hushscript/generated/api/default/patch_org_member.py +237 -0
  55. hushscript/generated/api/default/place_transcript_hold.py +206 -0
  56. hushscript/generated/api/default/rotate_agent_credentials.py +177 -0
  57. hushscript/generated/api/default/schedule_org_custody.py +215 -0
  58. hushscript/generated/api/default/set_default_card.py +223 -0
  59. hushscript/generated/api/default/upload_part.py +249 -0
  60. hushscript/generated/catalog.py +138 -0
  61. hushscript/generated/client.py +282 -0
  62. hushscript/generated/errors.py +16 -0
  63. hushscript/generated/hints.py +28 -0
  64. hushscript/generated/models/__init__.py +193 -0
  65. hushscript/generated/models/account_response.py +114 -0
  66. hushscript/generated/models/active_hold.py +88 -0
  67. hushscript/generated/models/active_hold_kind.py +18 -0
  68. hushscript/generated/models/agent_claim_request.py +61 -0
  69. hushscript/generated/models/agent_claim_response.py +85 -0
  70. hushscript/generated/models/agent_credentials_rotate_response.py +101 -0
  71. hushscript/generated/models/agent_signup_request.py +118 -0
  72. hushscript/generated/models/agent_signup_request_agent.py +80 -0
  73. hushscript/generated/models/agent_signup_response.py +111 -0
  74. hushscript/generated/models/buy_minutes_body.py +89 -0
  75. hushscript/generated/models/card.py +132 -0
  76. hushscript/generated/models/cards_response.py +90 -0
  77. hushscript/generated/models/complete_upload_body.py +107 -0
  78. hushscript/generated/models/complete_upload_body_parts_type_0_item.py +69 -0
  79. hushscript/generated/models/context_dictionary.py +143 -0
  80. hushscript/generated/models/context_prompt_preset.py +118 -0
  81. hushscript/generated/models/create_setup_session_body.py +62 -0
  82. hushscript/generated/models/create_upload_body.py +254 -0
  83. hushscript/generated/models/create_upload_body_transcription_options_type_0.py +178 -0
  84. hushscript/generated/models/credit_ledger_entry.py +113 -0
  85. hushscript/generated/models/credit_ledger_response.py +90 -0
  86. hushscript/generated/models/deleted_transcript.py +148 -0
  87. hushscript/generated/models/deleted_transcript_cause.py +18 -0
  88. hushscript/generated/models/deleted_transcript_receipt_type_0.py +107 -0
  89. hushscript/generated/models/deleted_transcript_receipt_type_0_items_item.py +69 -0
  90. hushscript/generated/models/deleted_transcripts_response.py +90 -0
  91. hushscript/generated/models/dictionary_entry.py +123 -0
  92. hushscript/generated/models/dictionary_entry_category.py +37 -0
  93. hushscript/generated/models/error.py +155 -0
  94. hushscript/generated/models/error_details.py +94 -0
  95. hushscript/generated/models/get_upload_part_urls_response_200_type_0.py +108 -0
  96. hushscript/generated/models/heartbeat_upload_response_200.py +61 -0
  97. hushscript/generated/models/holds_response.py +90 -0
  98. hushscript/generated/models/job.py +309 -0
  99. hushscript/generated/models/job_failure_kind_type_1.py +16 -0
  100. hushscript/generated/models/job_failure_kind_type_2_type_1.py +16 -0
  101. hushscript/generated/models/job_failure_kind_type_3_type_1.py +16 -0
  102. hushscript/generated/models/job_list.py +90 -0
  103. hushscript/generated/models/lift_transcript_hold_response_200.py +61 -0
  104. hushscript/generated/models/list_jobs_active.py +17 -0
  105. hushscript/generated/models/list_org_matters_status.py +16 -0
  106. hushscript/generated/models/options_summary.py +135 -0
  107. hushscript/generated/models/org_audit_entry.py +105 -0
  108. hushscript/generated/models/org_audit_response.py +90 -0
  109. hushscript/generated/models/org_invoice.py +175 -0
  110. hushscript/generated/models/org_invoices_response.py +90 -0
  111. hushscript/generated/models/org_ledger_entry.py +155 -0
  112. hushscript/generated/models/org_ledger_response.py +90 -0
  113. hushscript/generated/models/org_matter.py +79 -0
  114. hushscript/generated/models/org_matter_status.py +16 -0
  115. hushscript/generated/models/org_matters_response.py +75 -0
  116. hushscript/generated/models/org_member.py +123 -0
  117. hushscript/generated/models/org_members_response.py +90 -0
  118. hushscript/generated/models/org_summary_response.py +482 -0
  119. hushscript/generated/models/org_transcript.py +103 -0
  120. hushscript/generated/models/org_transcripts_response.py +90 -0
  121. hushscript/generated/models/org_usage_report_csv_group_by.py +17 -0
  122. hushscript/generated/models/org_usage_report_group_by.py +17 -0
  123. hushscript/generated/models/org_usage_response.py +112 -0
  124. hushscript/generated/models/org_usage_response_group_by.py +17 -0
  125. hushscript/generated/models/org_usage_row.py +86 -0
  126. hushscript/generated/models/pack.py +107 -0
  127. hushscript/generated/models/packs_response.py +75 -0
  128. hushscript/generated/models/patch_org_member_body.py +102 -0
  129. hushscript/generated/models/patch_org_member_response_200.py +61 -0
  130. hushscript/generated/models/place_transcript_hold_response_200.py +61 -0
  131. hushscript/generated/models/presigned_part_url.py +69 -0
  132. hushscript/generated/models/purchase_response.py +76 -0
  133. hushscript/generated/models/schedule_org_custody_body.py +66 -0
  134. hushscript/generated/models/schedule_org_custody_body_custody.py +17 -0
  135. hushscript/generated/models/schedule_org_custody_response_200.py +69 -0
  136. hushscript/generated/models/set_default_card_body.py +61 -0
  137. hushscript/generated/models/set_default_card_response_200.py +61 -0
  138. hushscript/generated/models/setup_session_response.py +77 -0
  139. hushscript/generated/models/transaction.py +183 -0
  140. hushscript/generated/models/transactions_response.py +98 -0
  141. hushscript/generated/models/transcript_detail.py +194 -0
  142. hushscript/generated/models/transcript_detail_speaker_names.py +47 -0
  143. hushscript/generated/models/transcript_envelope.py +89 -0
  144. hushscript/generated/models/transcript_envelope_translations.py +62 -0
  145. hushscript/generated/models/transcript_language.py +75 -0
  146. hushscript/generated/models/transcript_list.py +90 -0
  147. hushscript/generated/models/transcript_list_row.py +152 -0
  148. hushscript/generated/models/transcript_list_row_insights_state.py +20 -0
  149. hushscript/generated/models/transcription_context_response.py +95 -0
  150. hushscript/generated/models/upload_complete_response.py +100 -0
  151. hushscript/generated/models/upload_create_response.py +148 -0
  152. hushscript/generated/models/upload_create_response_funding.py +16 -0
  153. hushscript/generated/models/upload_create_response_upload_type_0.py +108 -0
  154. hushscript/generated/models/upload_limits.py +86 -0
  155. hushscript/generated/models/upload_part_response_200.py +69 -0
  156. hushscript/generated/models/utterance.py +85 -0
  157. hushscript/generated/scopes.py +42 -0
  158. hushscript/generated/types.py +54 -0
  159. hushscript/money.py +79 -0
  160. hushscript/paginate.py +78 -0
  161. hushscript/py.typed +0 -0
  162. hushscript/resources/__init__.py +1 -0
  163. hushscript/resources/_params.py +33 -0
  164. hushscript/resources/account.py +121 -0
  165. hushscript/resources/agent.py +421 -0
  166. hushscript/resources/billing.py +567 -0
  167. hushscript/resources/jobs.py +105 -0
  168. hushscript/resources/transcripts.py +209 -0
  169. hushscript/transcribe/__init__.py +1 -0
  170. hushscript/transcribe/apipeline.py +234 -0
  171. hushscript/transcribe/aupload.py +197 -0
  172. hushscript/transcribe/heartbeat.py +78 -0
  173. hushscript/transcribe/pipeline.py +312 -0
  174. hushscript/transcribe/preflight.py +272 -0
  175. hushscript/transcribe/probe.py +185 -0
  176. hushscript/transcribe/resume.py +115 -0
  177. hushscript/transcribe/segment.py +23 -0
  178. hushscript/transcribe/session.py +34 -0
  179. hushscript/transcribe/sniff.py +30 -0
  180. hushscript/transcribe/source.py +218 -0
  181. hushscript/transcribe/upload.py +498 -0
  182. hushscript/transcribe/wait.py +124 -0
  183. hushscript/transcript.py +197 -0
  184. hushscript-1.0.0.dist-info/METADATA +502 -0
  185. hushscript-1.0.0.dist-info/RECORD +187 -0
  186. hushscript-1.0.0.dist-info/WHEEL +4 -0
  187. hushscript-1.0.0.dist-info/licenses/LICENSE +21 -0
hushscript/format.py ADDED
@@ -0,0 +1,224 @@
1
+ """The SRT, VTT and text exporters, byte for byte what the Hushscript app produces (spec 6.2).
2
+
3
+ The conformance goldens pin the output. JavaScript's rules are kept where they differ from
4
+ Python's: its whitespace set, UTF-16 line lengths, `Math.round` rounding.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import math
10
+ import re
11
+ from dataclasses import dataclass
12
+ from typing import TYPE_CHECKING, Final
13
+
14
+ if TYPE_CHECKING:
15
+ from collections.abc import Sequence
16
+
17
+ from .transcript import Utterance
18
+
19
+ MAX_CAPTION_CHARS: Final = 42
20
+ MAX_CAPTION_LINES: Final = 2
21
+ MAX_CUE_MS: Final = 7000
22
+ _ASTRAL: Final = 0x10000
23
+ _MS_PER_HOUR: Final = 3_600_000
24
+ _MS_PER_MINUTE: Final = 60_000
25
+ _SECONDS_PER_HOUR: Final = 3600
26
+ _SECONDS_PER_MINUTE: Final = 60
27
+
28
+ # JavaScript's whitespace (the set `\s` and `String.prototype.trim` use): it has U+FEFF and lacks
29
+ # U+0085 and the C0 separators that Python's `str.split()` treats as space.
30
+ JS_SPACE: Final = "".join(
31
+ chr(code)
32
+ for code in (
33
+ *(9, 10, 11, 12, 13, 32, 0xA0, 0x1680),
34
+ *range(0x2000, 0x200B),
35
+ *(0x2028, 0x2029, 0x202F, 0x205F, 0x3000, 0xFEFF),
36
+ )
37
+ )
38
+ _WORD: Final = re.compile(f"[^{re.escape(JS_SPACE)}]+")
39
+ # An SRT reader takes a line holding "-->" for a timing line, so a run of dashes before ">" is cut
40
+ # to "->" (a plain "-->" swap would turn "--->" back into "-->").
41
+ _DASH_ARROW: Final = re.compile(r"-{2,}>")
42
+
43
+
44
+ @dataclass(frozen=True, slots=True)
45
+ class Cue:
46
+ start_ms: int
47
+ end_ms: int
48
+ speaker_name: str
49
+ """Empty when the cue carries no speaker prefix."""
50
+ text: str
51
+
52
+
53
+ def js_strip(text: str) -> str:
54
+ return text.strip(JS_SPACE)
55
+
56
+
57
+ def utf16_length(text: str) -> int:
58
+ """The length JavaScript reports for `text`; a lone surrogate counts as one unit."""
59
+ return len(text) + sum(1 for char in text if ord(char) >= _ASTRAL)
60
+
61
+
62
+ def round_half_up(value: float) -> int:
63
+ """`Math.round` for a non-negative value (Python's `round` rounds halves to even)."""
64
+ floor = math.floor(value)
65
+ return floor + 1 if value - floor >= 0.5 else floor # noqa: PLR2004
66
+
67
+
68
+ def _wrap_state(words: list[str], max_chars: int, max_lines: int) -> list[str]:
69
+ cues: list[str] = []
70
+ lines: list[str] = []
71
+ line = ""
72
+ line_length = 0
73
+
74
+ def flush_line() -> None:
75
+ nonlocal line, line_length
76
+ if line:
77
+ lines.append(line)
78
+ line, line_length = "", 0
79
+
80
+ def flush_cue() -> None:
81
+ flush_line()
82
+ if lines:
83
+ cues.append("\n".join(lines))
84
+ lines.clear()
85
+
86
+ for word in words:
87
+ word_length = utf16_length(word)
88
+ candidate = f"{line} {word}" if line else word
89
+ candidate_length = line_length + 1 + word_length if line else word_length
90
+ if candidate_length <= max_chars:
91
+ line, line_length = candidate, candidate_length
92
+ continue
93
+ flush_line()
94
+ if len(lines) >= max_lines:
95
+ flush_cue()
96
+ line, line_length = word, word_length
97
+ flush_cue()
98
+ return cues
99
+
100
+
101
+ def wrap_into_cues(text: str, max_chars: int, max_lines: int) -> list[str]:
102
+ """Word-wraps `text` into cues of at most `max_lines` lines of `max_chars`; an overlong word
103
+ keeps a line to itself. Text without a word gives one empty cue."""
104
+ words = _WORD.findall(text)
105
+ if not words:
106
+ return [""]
107
+ return _wrap_state(words, max_chars, max_lines) or [""]
108
+
109
+
110
+ def utterance_cues(utterances: Sequence[Utterance]) -> list[Cue]:
111
+ return [Cue(u.start_ms, u.end_ms, u.speaker_name, u.text) for u in utterances]
112
+
113
+
114
+ def segment_captions(utterances: Sequence[Utterance]) -> list[Cue]:
115
+ """Re-cuts utterances into readable cues: lines of at most 42 characters, two lines to a cue,
116
+ the utterance span split by character share, and every cue, the last of an utterance included,
117
+ capped at 7000 ms. Positions come from the cumulative character share, so the cap never shifts
118
+ a later cue."""
119
+ out: list[Cue] = []
120
+ for utterance in utterances:
121
+ chunks = wrap_into_cues(utterance.text, MAX_CAPTION_CHARS, MAX_CAPTION_LINES)
122
+ span = max(0, utterance.end_ms - utterance.start_ms)
123
+ total = sum(utf16_length(chunk) for chunk in chunks) or 1
124
+ accumulated = 0
125
+ for index, text in enumerate(chunks):
126
+ start_ms = utterance.start_ms + round_half_up(span * accumulated / total)
127
+ accumulated += max(1, utf16_length(text))
128
+ end_ms = utterance.end_ms
129
+ if index != len(chunks) - 1:
130
+ end_ms = utterance.start_ms + round_half_up(span * accumulated / total)
131
+ end_ms = min(end_ms, start_ms + MAX_CUE_MS)
132
+ out.append(Cue(start_ms, max(end_ms, start_ms), utterance.speaker_name, text))
133
+ return out
134
+
135
+
136
+ def timestamp(milliseconds: int, separator: str) -> str:
137
+ """`HH:MM:SS,mmm`, floored to whole milliseconds; VTT swaps the comma for a dot."""
138
+ total = max(0, milliseconds)
139
+ hours, rest = divmod(total, _MS_PER_HOUR)
140
+ minutes, rest = divmod(rest, _MS_PER_MINUTE)
141
+ seconds, millis = divmod(rest, 1000)
142
+ return f"{hours:02d}:{minutes:02d}:{seconds:02d}{separator}{millis:03d}"
143
+
144
+
145
+ def cue_text(text: str) -> str:
146
+ """Keeps a cue's line breaks but drops its blank lines: SRT and VTT end a cue at the first."""
147
+ lines = text.replace("\r\n", "\n").replace("\r", "\n").split("\n")
148
+ return "\n".join(line for line in lines if js_strip(line))
149
+
150
+
151
+ def _with_speaker(cue: Cue) -> str:
152
+ return f"{cue.speaker_name}: {cue.text}" if cue.speaker_name else cue.text
153
+
154
+
155
+ def _cues(utterances: Sequence[Utterance], *, captions: bool) -> list[Cue]:
156
+ return segment_captions(utterances) if captions else utterance_cues(utterances)
157
+
158
+
159
+ def render_srt(utterances: Sequence[Utterance], *, captions: bool = True) -> str:
160
+ blocks = [
161
+ f"{number}\n{timestamp(cue.start_ms, ',')} --> {timestamp(cue.end_ms, ',')}\n"
162
+ + _DASH_ARROW.sub("->", cue_text(_with_speaker(cue)))
163
+ for number, cue in enumerate(_cues(utterances, captions=captions), start=1)
164
+ ]
165
+ return "\n\n".join(blocks)
166
+
167
+
168
+ def _vtt_text(text: str) -> str:
169
+ """Escapes what WebVTT reads as markup (`&` and `<`) and as the end of a cue (`-->`)."""
170
+ return cue_text(text).replace("&", "&amp;").replace("<", "&lt;").replace("-->", "--&gt;")
171
+
172
+
173
+ def render_vtt(utterances: Sequence[Utterance], *, captions: bool = True) -> str:
174
+ blocks = [
175
+ f"{number}\n{timestamp(cue.start_ms, '.')} --> {timestamp(cue.end_ms, '.')}\n"
176
+ + _vtt_text(_with_speaker(cue))
177
+ for number, cue in enumerate(_cues(utterances, captions=captions), start=1)
178
+ ]
179
+ return "WEBVTT\n\n" + "\n\n".join(blocks) + "\n"
180
+
181
+
182
+ @dataclass(slots=True)
183
+ class _Block:
184
+ speaker: str
185
+ speaker_name: str
186
+ start_ms: int
187
+ text: str
188
+
189
+
190
+ def _blocks(utterances: Sequence[Utterance], *, merge: bool) -> list[_Block]:
191
+ blocks: list[_Block] = []
192
+ for utterance in utterances:
193
+ if merge and blocks and blocks[-1].speaker == utterance.speaker:
194
+ blocks[-1].text += " " + utterance.text
195
+ continue
196
+ blocks.append(
197
+ _Block(utterance.speaker, utterance.speaker_name, utterance.start_ms, utterance.text)
198
+ )
199
+ return blocks
200
+
201
+
202
+ def _clock(milliseconds: int) -> str:
203
+ """`M:SS` or `H:MM:SS`, floored to whole seconds."""
204
+ seconds = max(0, milliseconds) // 1000
205
+ hours, rest = divmod(seconds, _SECONDS_PER_HOUR)
206
+ minutes, secs = divmod(rest, _SECONDS_PER_MINUTE)
207
+ return f"{hours}:{minutes:02d}:{secs:02d}" if hours > 0 else f"{minutes}:{secs:02d}"
208
+
209
+
210
+ def render_text(
211
+ utterances: Sequence[Utterance],
212
+ *,
213
+ speakers: bool = True,
214
+ timestamps: bool = False,
215
+ merge: bool = True,
216
+ ) -> str:
217
+ """ "Name: text" blocks joined by a blank line. `\\r` and whitespace-only lines are kept as they
218
+ come, as in the app."""
219
+ out: list[str] = []
220
+ for block in _blocks(utterances, merge=merge):
221
+ prefix = f"[{_clock(block.start_ms)}] " if timestamps else ""
222
+ who = block.speaker_name if speakers else ""
223
+ out.append(f"{prefix}{who}: {block.text}" if who else f"{prefix}{block.text}")
224
+ return "\n\n".join(out)
@@ -0,0 +1,8 @@
1
+ """A client library for accessing Hushscript Public API"""
2
+
3
+ from .client import AuthenticatedClient, Client
4
+
5
+ __all__ = (
6
+ "AuthenticatedClient",
7
+ "Client",
8
+ )
@@ -0,0 +1 @@
1
+ """Contains methods for accessing the API"""
@@ -0,0 +1 @@
1
+ """Contains endpoint functions for accessing the API"""
@@ -0,0 +1,248 @@
1
+ from http import HTTPStatus
2
+ from typing import Any
3
+
4
+ import httpx
5
+
6
+ from ... import errors
7
+ from ...client import AuthenticatedClient, Client
8
+ from ...models.buy_minutes_body import BuyMinutesBody
9
+ from ...models.error import Error
10
+ from ...models.purchase_response import PurchaseResponse
11
+ from ...types import Response
12
+
13
+
14
+ def _get_kwargs(
15
+ *,
16
+ body: BuyMinutesBody,
17
+ ) -> dict[str, Any]:
18
+ headers: dict[str, Any] = {}
19
+
20
+ _kwargs: dict[str, Any] = {
21
+ "method": "post",
22
+ "url": "/v1/billing/purchase",
23
+ }
24
+
25
+ _kwargs["json"] = body.to_dict()
26
+
27
+ headers["Content-Type"] = "application/json"
28
+
29
+ _kwargs["headers"] = headers
30
+ return _kwargs
31
+
32
+
33
+ def _parse_response(
34
+ *, client: AuthenticatedClient | Client, response: httpx.Response
35
+ ) -> Error | PurchaseResponse | None:
36
+ if response.status_code == 200:
37
+ response_200 = PurchaseResponse.from_dict(response.json())
38
+
39
+ return response_200
40
+
41
+ if response.status_code == 400:
42
+ response_400 = Error.from_dict(response.json())
43
+
44
+ return response_400
45
+
46
+ if response.status_code == 401:
47
+ response_401 = Error.from_dict(response.json())
48
+
49
+ return response_401
50
+
51
+ if response.status_code == 403:
52
+ response_403 = Error.from_dict(response.json())
53
+
54
+ return response_403
55
+
56
+ if response.status_code == 422:
57
+ response_422 = Error.from_dict(response.json())
58
+
59
+ return response_422
60
+
61
+ if response.status_code == 429:
62
+ response_429 = Error.from_dict(response.json())
63
+
64
+ return response_429
65
+
66
+ if response.status_code == 500:
67
+ response_500 = Error.from_dict(response.json())
68
+
69
+ return response_500
70
+
71
+ if response.status_code == 502:
72
+ response_502 = Error.from_dict(response.json())
73
+
74
+ return response_502
75
+
76
+ if client.raise_on_unexpected_status:
77
+ raise errors.UnexpectedStatus(response.status_code, response.content)
78
+ else:
79
+ return None
80
+
81
+
82
+ def _build_response(
83
+ *, client: AuthenticatedClient | Client, response: httpx.Response
84
+ ) -> Response[Error | PurchaseResponse]:
85
+ return Response(
86
+ status_code=HTTPStatus(response.status_code),
87
+ content=response.content,
88
+ headers=response.headers,
89
+ parsed=_parse_response(client=client, response=response),
90
+ )
91
+
92
+
93
+ def sync_detailed(
94
+ *,
95
+ client: AuthenticatedClient,
96
+ body: BuyMinutesBody,
97
+ ) -> Response[Error | PurchaseResponse]:
98
+ """Charge the account's saved default card for a minute pack
99
+
100
+ Charges a saved, fast_checkout_eligible payment method for one pack and credits the account's
101
+ balance on success. quick_payment_method_id must be the account's current default, except when the
102
+ account has no default at all, in which case naming an eligible card here makes it the new default.
103
+ quick_payment_method_id is required: this API can only complete a purchase against a card already on
104
+ file and has no way to open an interactive Checkout session, so an agent cannot use it to attach a
105
+ new card. list_saved_cards can only list a card already on file, not create one; add a card first,
106
+ either through the Hushscript app or by opening the hosted Checkout session create_setup_session
107
+ returns. A request with no quick_payment_method_id, or a Quick Buy charge that needed
108
+ authentication, a different payment method, or was declined, is refused outright rather than opened
109
+ as a Checkout session nobody in this flow could complete. Needs the billing scope AND machine
110
+ billing switched on for the account in the Hushscript app; either one off is a pat_scope_missing
111
+ (code 2023) refusal, whichever it was.
112
+
113
+ Args:
114
+ body (BuyMinutesBody):
115
+
116
+ Raises:
117
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
118
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
119
+
120
+ Returns:
121
+ Response[Error | PurchaseResponse]
122
+ """
123
+
124
+ kwargs = _get_kwargs(
125
+ body=body,
126
+ )
127
+
128
+ response = client.get_httpx_client().request(
129
+ **kwargs,
130
+ )
131
+
132
+ return _build_response(client=client, response=response)
133
+
134
+
135
+ def sync(
136
+ *,
137
+ client: AuthenticatedClient,
138
+ body: BuyMinutesBody,
139
+ ) -> Error | PurchaseResponse | None:
140
+ """Charge the account's saved default card for a minute pack
141
+
142
+ Charges a saved, fast_checkout_eligible payment method for one pack and credits the account's
143
+ balance on success. quick_payment_method_id must be the account's current default, except when the
144
+ account has no default at all, in which case naming an eligible card here makes it the new default.
145
+ quick_payment_method_id is required: this API can only complete a purchase against a card already on
146
+ file and has no way to open an interactive Checkout session, so an agent cannot use it to attach a
147
+ new card. list_saved_cards can only list a card already on file, not create one; add a card first,
148
+ either through the Hushscript app or by opening the hosted Checkout session create_setup_session
149
+ returns. A request with no quick_payment_method_id, or a Quick Buy charge that needed
150
+ authentication, a different payment method, or was declined, is refused outright rather than opened
151
+ as a Checkout session nobody in this flow could complete. Needs the billing scope AND machine
152
+ billing switched on for the account in the Hushscript app; either one off is a pat_scope_missing
153
+ (code 2023) refusal, whichever it was.
154
+
155
+ Args:
156
+ body (BuyMinutesBody):
157
+
158
+ Raises:
159
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
160
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
161
+
162
+ Returns:
163
+ Error | PurchaseResponse
164
+ """
165
+
166
+ return sync_detailed(
167
+ client=client,
168
+ body=body,
169
+ ).parsed
170
+
171
+
172
+ async def asyncio_detailed(
173
+ *,
174
+ client: AuthenticatedClient,
175
+ body: BuyMinutesBody,
176
+ ) -> Response[Error | PurchaseResponse]:
177
+ """Charge the account's saved default card for a minute pack
178
+
179
+ Charges a saved, fast_checkout_eligible payment method for one pack and credits the account's
180
+ balance on success. quick_payment_method_id must be the account's current default, except when the
181
+ account has no default at all, in which case naming an eligible card here makes it the new default.
182
+ quick_payment_method_id is required: this API can only complete a purchase against a card already on
183
+ file and has no way to open an interactive Checkout session, so an agent cannot use it to attach a
184
+ new card. list_saved_cards can only list a card already on file, not create one; add a card first,
185
+ either through the Hushscript app or by opening the hosted Checkout session create_setup_session
186
+ returns. A request with no quick_payment_method_id, or a Quick Buy charge that needed
187
+ authentication, a different payment method, or was declined, is refused outright rather than opened
188
+ as a Checkout session nobody in this flow could complete. Needs the billing scope AND machine
189
+ billing switched on for the account in the Hushscript app; either one off is a pat_scope_missing
190
+ (code 2023) refusal, whichever it was.
191
+
192
+ Args:
193
+ body (BuyMinutesBody):
194
+
195
+ Raises:
196
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
197
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
198
+
199
+ Returns:
200
+ Response[Error | PurchaseResponse]
201
+ """
202
+
203
+ kwargs = _get_kwargs(
204
+ body=body,
205
+ )
206
+
207
+ response = await client.get_async_httpx_client().request(**kwargs)
208
+
209
+ return _build_response(client=client, response=response)
210
+
211
+
212
+ async def asyncio(
213
+ *,
214
+ client: AuthenticatedClient,
215
+ body: BuyMinutesBody,
216
+ ) -> Error | PurchaseResponse | None:
217
+ """Charge the account's saved default card for a minute pack
218
+
219
+ Charges a saved, fast_checkout_eligible payment method for one pack and credits the account's
220
+ balance on success. quick_payment_method_id must be the account's current default, except when the
221
+ account has no default at all, in which case naming an eligible card here makes it the new default.
222
+ quick_payment_method_id is required: this API can only complete a purchase against a card already on
223
+ file and has no way to open an interactive Checkout session, so an agent cannot use it to attach a
224
+ new card. list_saved_cards can only list a card already on file, not create one; add a card first,
225
+ either through the Hushscript app or by opening the hosted Checkout session create_setup_session
226
+ returns. A request with no quick_payment_method_id, or a Quick Buy charge that needed
227
+ authentication, a different payment method, or was declined, is refused outright rather than opened
228
+ as a Checkout session nobody in this flow could complete. Needs the billing scope AND machine
229
+ billing switched on for the account in the Hushscript app; either one off is a pat_scope_missing
230
+ (code 2023) refusal, whichever it was.
231
+
232
+ Args:
233
+ body (BuyMinutesBody):
234
+
235
+ Raises:
236
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
237
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
238
+
239
+ Returns:
240
+ Error | PurchaseResponse
241
+ """
242
+
243
+ return (
244
+ await asyncio_detailed(
245
+ client=client,
246
+ body=body,
247
+ )
248
+ ).parsed