@ichinese/open-sdk 1.0.0-rc.2 → 1.0.0-rc.4

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # iChinese Open Platform SDK for Node.js
2
2
 
3
- > **Prerelease 1.0.0-rc.2.** 这是预发布版,开放平台还没有正式开放,SDK 的默认地址可能连不上。This is a prerelease: the open platform is not open to the public yet, so the SDK's default address may not answer.
3
+ > **Prerelease 1.0.0-rc.4.** 这是预发布版,开放平台还没有正式开放,SDK 的默认地址可能连不上。This is a prerelease: the open platform is not open to the public yet, so the SDK's default address may not answer.
4
4
 
5
5
  爱中文开放平台的 Node.js 服务端 SDK:开放接口(`/open-apis/v1`)的每个接口都是一个方法,并处理令牌、重试、分页和事件推送。
6
6
 
@@ -42,6 +42,70 @@ try {
42
42
 
43
43
  The `examples` directory has this one in full, a backend for the web login and a server for event pushes.
44
44
 
45
+ ## Posting in a person's name
46
+
47
+ With a user token that has the `community:post` scope, four methods do the uploads and the waiting:
48
+
49
+ - `uploadImage(userToken, data, contentType)`: gets an upload slot, PUTs the image there, returns its `key`.
50
+ - `uploadVideo(userToken, data, { filename, contentType })`: gets a slot, PUTs the video, starts transcoding;
51
+ returns the video (`asset_id`). Pass a large file as `await fs.openAsBlob(path)`.
52
+ - `waitVideoReady(userToken, assetId, { timeoutMs, intervalMs })`: polls until the video is `ready`; throws
53
+ an `OpenApiError` with reason `video_failed`, or code `TIMEOUT`.
54
+ - `createPost(userToken, post)`: posts; adds a `uuid` when you give none, so retries never post twice.
55
+
56
+ ```ts
57
+ import { openAsBlob } from 'node:fs';
58
+ import { readFile } from 'node:fs/promises';
59
+
60
+ try {
61
+ // A post with an image…
62
+ const key = await client.uploadImage(userToken, await readFile('photo.jpg'), 'image/jpeg');
63
+ const post = await client.createPost(userToken, { body: '今天学了新词 #HSK3', images: [{ key }] });
64
+ console.log(post.url, post.status);
65
+
66
+ // …or with a video, once it is transcoded.
67
+ const video = await client.uploadVideo(userToken, await openAsBlob('lesson.mp4'), {
68
+ filename: 'lesson.mp4',
69
+ contentType: 'video/mp4',
70
+ });
71
+ await client.waitVideoReady(userToken, video.asset_id);
72
+ await client.createPost(userToken, { title: '第一课', video: { asset_id: video.asset_id } });
73
+ } catch (e) {
74
+ // The person has not agreed to show their name on posts: only they can, at this address.
75
+ if (isOpenApiError(e) && e.reason === 'consent_required') console.log(e.consentUrl);
76
+ else throw e;
77
+ }
78
+ ```
79
+
80
+ A post has either images (`images: [{ key }]`, up to 9) or one video. Errors keep the server's
81
+ `reason`: `consent_required`, `video_not_ready`, `post_rate_limited`, `app_user_daily_limit`,
82
+ `daily_quota_exceeded`, `minor_not_allowed`, `app_post_stopped`, …. `createPost` and completing a video
83
+ upload are retried after server errors like a GET (the server makes them idempotent).
84
+
85
+ ### Posting as the institution (机构号)
86
+
87
+ With the institution's tenant key the same helpers post as its community account (its name and logo, the
88
+ post says it came from your app): `uploadImageAsOrg`, `uploadVideoAsOrg`, `waitVideoReadyAsOrg`,
89
+ `createPostAsOrg`. The institution must have installed the app and granted `community:post`; before its
90
+ homepage was first published there is no account yet (409 `no_org_account`). Limits and review are the same.
91
+
92
+ ```ts
93
+ const key = await client.uploadImageAsOrg(tenantKey, bytes, 'image/png');
94
+ await client.createPostAsOrg(tenantKey, { body: '秋季班开始报名 #少儿中文', images: [{ key }] });
95
+ ```
96
+
97
+ ### The institution's dashboard
98
+
99
+ `getAllInsights` reads every page of the institution's dashboard the API offers (`GET /insights`, scope
100
+ `insights:readonly`, tenant token): the default page first, then each other tab it lists, with the same
101
+ query. It returns tab → page. Fewer than five people come back as null with `withheld: true`; money is
102
+ per currency, never converted.
103
+
104
+ ```ts
105
+ const pages = await client.getAllInsights(tenantKey, { range: '30d' });
106
+ console.log(pages.enrollment.tiles);
107
+ ```
108
+
45
109
  ## Documentation
46
110
 
47
111
  Guides and the API reference: <https://open.ichinese.cn>
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Operations } from './gen/operations.ts';
2
- import type { CallSpec, ClientOptions, RequestOptions, TokenType, UserAccessToken } from './types.ts';
1
+ import { type BodyOf, Operations, type QueryOf, type ResultOf } from './gen/operations.ts';
2
+ import type { CallSpec, ClientOptions, RequestOptions, TokenType, UserAccessToken, WaitVideoOptions } from './types.ts';
3
3
  export declare class Client extends Operations {
4
4
  readonly appId: string;
5
5
  readonly baseUrl: string;
@@ -34,6 +34,73 @@ export declare class Client extends Operations {
34
34
  revokeToken(token: string): Promise<void>;
35
35
  /** Forgets the cached app and tenant tokens (they are fetched again when next needed). */
36
36
  clearTokenCache(): void;
37
+ /**
38
+ * Uploads an image for a post: asks for an upload slot, PUTs the bytes there and returns the `key` to
39
+ * put in `images[].key`. Up to 10 MB; jpeg, png, webp or gif.
40
+ */
41
+ uploadImage(userAccessToken: string, data: Uint8Array | Blob, contentType: BodyOf<'createCommunityImageUpload'>['content_type'], options?: {
42
+ signal?: AbortSignal;
43
+ }): Promise<string>;
44
+ /** `uploadImage` for a post of the institution (its tenant token, `community:post` granted by it). */
45
+ uploadImageAsOrg(tenantKey: string, data: Uint8Array | Blob, contentType: BodyOf<'createCommunityImageUpload'>['content_type'], options?: {
46
+ signal?: AbortSignal;
47
+ }): Promise<string>;
48
+ /**
49
+ * Uploads a video for a post (up to 4 GB, 1 hour): asks for an upload slot, PUTs the file there and
50
+ * starts transcoding. A large file goes as a Blob that reads from disk (`await fs.openAsBlob(path)`).
51
+ * Returns the video's status (`asset_id`); wait for `ready` with `waitVideoReady` before posting.
52
+ */
53
+ uploadVideo(userAccessToken: string, data: Uint8Array | Blob, file: {
54
+ filename: string;
55
+ contentType: string;
56
+ }, options?: {
57
+ signal?: AbortSignal;
58
+ }): Promise<ResultOf<'completeCommunityVideoUpload'>>;
59
+ /** `uploadVideo` for a post of the institution. */
60
+ uploadVideoAsOrg(tenantKey: string, data: Uint8Array | Blob, file: {
61
+ filename: string;
62
+ contentType: string;
63
+ }, options?: {
64
+ signal?: AbortSignal;
65
+ }): Promise<ResultOf<'completeCommunityVideoUpload'>>;
66
+ /**
67
+ * Waits until a video has been transcoded and returns it (`status: ready`). A failed transcode throws
68
+ * an OpenApiError with reason `video_failed` (upload another file); running out of time throws one
69
+ * with code TIMEOUT (reason `video_not_ready`). You also get the `community.video_ready` event.
70
+ */
71
+ waitVideoReady(userAccessToken: string, assetId: string, options?: WaitVideoOptions): Promise<ResultOf<'getCommunityVideo'>>;
72
+ /** `waitVideoReady` for a video uploaded for the institution. */
73
+ waitVideoReadyAsOrg(tenantKey: string, assetId: string, options?: WaitVideoOptions): Promise<ResultOf<'getCommunityVideo'>>;
74
+ /**
75
+ * Posts in the person's name. Without a `uuid` the SDK makes one, so its own retries never post twice;
76
+ * give your own `uuid` to make your retries safe too (one post per `uuid` within an hour). Errors keep the server's reason: `consent_required`
77
+ * (send the person to `error.consentUrl`), `video_not_ready`, `video_failed`, `post_rate_limited`,
78
+ * `app_user_daily_limit`, `daily_quota_exceeded`, `minor_not_allowed`, `app_post_stopped`, …
79
+ */
80
+ createPost(userAccessToken: string, post: BodyOf<'createCommunityPost'>, options?: {
81
+ signal?: AbortSignal;
82
+ }): Promise<ResultOf<'createCommunityPost'>>;
83
+ /**
84
+ * Posts as the institution, with its community account (its name and logo; the post says it came from
85
+ * this app). The institution must have installed the app and granted `community:post`; before its
86
+ * homepage was first published there is no account yet (409 `no_org_account`). The same limits,
87
+ * review and `uuid` handling as `createPost`.
88
+ */
89
+ createPostAsOrg(tenantKey: string, post: BodyOf<'createCommunityPost'>, options?: {
90
+ signal?: AbortSignal;
91
+ }): Promise<ResultOf<'createCommunityPost'>>;
92
+ /**
93
+ * Every page of the institution's dashboard the API offers (`GET /insights`, `insights:readonly`): the
94
+ * default page first, then each other tab it lists, with the same query (range, dates, filters). Returns
95
+ * tab → page. Fewer than five people come back as `null` with `withheld: true`; money is per currency.
96
+ */
97
+ getAllInsights(tenantKey: string, query?: Omit<NonNullable<QueryOf<'getInsights'>>, 'tab'>, options?: {
98
+ signal?: AbortSignal;
99
+ }): Promise<Record<string, ResultOf<'getInsights'>>>;
100
+ private uploadImageWith;
101
+ private uploadVideoWith;
102
+ private waitVideoWith;
103
+ private createPostWith;
37
104
  /** Any open API call by method and path (`/tenant/members`), for what the generated methods do not cover. */
38
105
  request<T = unknown>(method: CallSpec['method'], path: string, init?: {
39
106
  query?: Record<string, unknown>;
@@ -43,6 +110,12 @@ export declare class Client extends Operations {
43
110
  protected call<T>(spec: CallSpec, options?: RequestOptions): Promise<T>;
44
111
  protected iterate<T>(spec: CallSpec, options?: RequestOptions): AsyncIterable<T>;
45
112
  private credentials;
113
+ private postingAuth;
114
+ /**
115
+ * PUTs an upload to the slot the platform gave (object storage, not the open API: no token, its own
116
+ * headers). Not limited by `timeoutMs`, a large video takes as long as it takes; cancel with the signal.
117
+ */
118
+ private putUpload;
46
119
  private cached;
47
120
  /** The token for a call, and the cache key it came from (dropped and fetched again after a 401). */
48
121
  private tokenFor;
package/dist/errors.d.ts CHANGED
@@ -27,6 +27,11 @@ export declare class OpenApiError extends Error {
27
27
  get scope(): string | null;
28
28
  /** The user chose「不再询问」for the refused scope: do not ask again. */
29
29
  get neverAsk(): boolean;
30
+ /**
31
+ * Where the person agrees to show their name on posts (409 `consent_required`): send them there, an
32
+ * app cannot agree for them.
33
+ */
34
+ get consentUrl(): string | null;
30
35
  /** A refused, not yet asked or undeclared scope (403): degrade gracefully. */
31
36
  get isScopeError(): boolean;
32
37
  }
@@ -1,16 +1,16 @@
1
1
  /** The SDK's version (it follows the open API's). */
2
- export declare const VERSION = "1.0.0-rc.2";
2
+ export declare const VERSION = "1.0.0-rc.4";
3
3
  /** The open API's address from the release configuration (sdks/release.json); `baseUrl` overrides it. */
4
4
  export declare const DEFAULT_BASE_URL = "https://open.ichinese.cn";
5
5
  export declare const API_PREFIX = "/open-apis/v1";
6
6
  /** Every scope (permission code) of the open API. */
7
- export declare const SCOPES: readonly ["openid", "profile", "org:member:readonly", "org:member", "class:readonly", "class:member", "schedule:readonly", "schedule", "attendance:readonly", "coursework", "grade:readonly", "course", "learning:progress:readonly", "im:message:send_as_bot", "im:chat:readonly", "notify:send", "live:recording:readonly", "event", "event:registration:readonly", "job", "job:candidate:readonly", "trial", "trade:order:readonly", "review", "profile:interest:readonly", "profile:interest", "profile:learning:readonly", "report:create"];
7
+ export declare const SCOPES: readonly ["openid", "profile", "org:member:readonly", "org:member", "class:readonly", "class:member", "schedule:readonly", "schedule", "attendance:readonly", "coursework", "grade:readonly", "course", "learning:progress:readonly", "im:message:send_as_bot", "im:chat:readonly", "notify:send", "live:recording:readonly", "event", "event:registration:readonly", "job", "job:candidate:readonly", "trial", "trade:order:readonly", "trade:booking:readonly", "venue:readonly", "venue", "opening:readonly", "review", "profile:interest:readonly", "profile:interest", "profile:learning:readonly", "report:create", "community:post", "me:schedule:readonly", "me:learning:readonly", "me:words:readonly", "me:words", "insights:readonly"];
8
8
  export type Scope = (typeof SCOPES)[number];
9
9
  /** The events an app can subscribe to. */
10
- export declare const EVENT_TYPES: readonly ["app.installed", "app.uninstalled", "app.scopes_changed", "coursework.submitted", "grade.published", "schedule.session_changed", "event.registered", "job.candidate_applied", "trade.order_paid", "review.created", "trial.booked", "im.message_received"];
10
+ export declare const EVENT_TYPES: readonly ["app.installed", "app.uninstalled", "app.scopes_changed", "coursework.submitted", "grade.published", "schedule.session_changed", "event.registered", "job.candidate_applied", "trade.order_paid", "review.created", "trial.booked", "im.message_received", "class.member_changed", "course.completed", "exam.results_released", "event.checked_in", "live.recording_ready", "trade.order_refunded", "trial.confirmed", "trial.cancelled", "community.post_reviewed", "community.video_ready", "booking.changed"];
11
11
  export type EventType = (typeof EVENT_TYPES)[number];
12
12
  /** `error.details.reason` values. */
13
- export declare const ERROR_REASONS: readonly ["invalid_credentials", "invalid_token", "token_expired", "token_type", "app_disabled", "not_installed", "not_available", "scope_denied", "scope_pending", "scope_not_declared", "gold_only", "rate_limited", "daily_quota_exceeded", "code_expired", "code_used", "code_invalid", "redirect_uri_mismatch", "origin_not_trusted", "ip_not_allowed"];
13
+ export declare const ERROR_REASONS: readonly ["invalid_credentials", "invalid_token", "token_expired", "token_type", "app_disabled", "not_installed", "not_available", "scope_denied", "scope_pending", "scope_not_declared", "gold_only", "rate_limited", "daily_quota_exceeded", "code_expired", "code_used", "code_invalid", "redirect_uri_mismatch", "origin_not_trusted", "ip_not_allowed", "org_not_installed", "minor_not_allowed", "birth_year_required", "feature_disabled", "app_post_stopped", "consent_required", "video_not_ready", "video_failed", "post_rate_limited", "app_user_daily_limit", "post_in_progress", "no_org_account", "teacher_busy", "online_only", "venue_unavailable", "venue_not_found", "slot_overlap", "slot_taken", "not_draft", "not_editable", "org_not_verified", "opening_ended", "opening_incomplete"];
14
14
  export type ErrorReason = (typeof ERROR_REASONS)[number];
15
15
  /** Token types each operation accepts (the client picks the token by them). */
16
16
  export declare const OPERATION_TOKENS: Record<string, readonly ('app' | 'tenant' | 'user')[]>;
@@ -37,8 +37,18 @@ export declare const SCOPE_JOB = "job";
37
37
  export declare const SCOPE_JOB_CANDIDATE_READONLY = "job:candidate:readonly";
38
38
  export declare const SCOPE_TRIAL = "trial";
39
39
  export declare const SCOPE_TRADE_ORDER_READONLY = "trade:order:readonly";
40
+ export declare const SCOPE_TRADE_BOOKING_READONLY = "trade:booking:readonly";
41
+ export declare const SCOPE_VENUE_READONLY = "venue:readonly";
42
+ export declare const SCOPE_VENUE = "venue";
43
+ export declare const SCOPE_OPENING_READONLY = "opening:readonly";
40
44
  export declare const SCOPE_REVIEW = "review";
41
45
  export declare const SCOPE_PROFILE_INTEREST_READONLY = "profile:interest:readonly";
42
46
  export declare const SCOPE_PROFILE_INTEREST = "profile:interest";
43
47
  export declare const SCOPE_PROFILE_LEARNING_READONLY = "profile:learning:readonly";
44
48
  export declare const SCOPE_REPORT_CREATE = "report:create";
49
+ export declare const SCOPE_COMMUNITY_POST = "community:post";
50
+ export declare const SCOPE_ME_SCHEDULE_READONLY = "me:schedule:readonly";
51
+ export declare const SCOPE_ME_LEARNING_READONLY = "me:learning:readonly";
52
+ export declare const SCOPE_ME_WORDS_READONLY = "me:words:readonly";
53
+ export declare const SCOPE_ME_WORDS = "me:words";
54
+ export declare const SCOPE_INSIGHTS_READONLY = "insights:readonly";
@@ -65,6 +65,10 @@ export declare abstract class Operations {
65
65
  listClassSessions(classId: string, query: QueryOf<'listClassSessions'>, options?: RequestOptions): Promise<ResultOf<'listClassSessions'>>;
66
66
  /** Every item of `listClassSessions`, page after page. */
67
67
  listClassSessionsAll(classId: string, query?: Omit<QueryOf<'listClassSessions'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listClassSessions'>>;
68
+ /** Schedule a class's sessions (once, or repeating weekly), online or at one of the institution's teaching sites (`POST /classes/{class_id}/sessions`; tenant / user token; scopes: schedule) */
69
+ createClassSessions(classId: string, body: BodyOf<'createClassSessions'>, options?: RequestOptions): Promise<ResultOf<'createClassSessions'>>;
70
+ /** Change where one session (or this one and the later ones of its series) is held (`PUT /classes/{class_id}/sessions/{session_id}/place`; tenant / user token; scopes: schedule) */
71
+ setClassSessionPlace(classId: string, sessionId: string, body: BodyOf<'setClassSessionPlace'>, options?: RequestOptions): Promise<ResultOf<'setClassSessionPlace'>>;
68
72
  /** Cancel a class session (members are notified, the same as cancelling in the timetable) (`POST /classes/{class_id}/sessions/{session_id}/cancel`; tenant / user token; scopes: schedule) */
69
73
  cancelClassSession(classId: string, sessionId: string, options?: RequestOptions): Promise<ResultOf<'cancelClassSession'>>;
70
74
  /** Reschedule a class session (`POST /classes/{class_id}/sessions/{session_id}/reschedule`; tenant / user token; scopes: schedule) */
@@ -137,24 +141,48 @@ export declare abstract class Operations {
137
141
  listTrialVenues(query?: QueryOf<'listTrialVenues'>, options?: RequestOptions): Promise<ResultOf<'listTrialVenues'>>;
138
142
  /** Every item of `listTrialVenues`, page after page. */
139
143
  listTrialVenuesAll(query?: Omit<QueryOf<'listTrialVenues'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listTrialVenues'>>;
140
- /** Trial lesson slots (by default only those not over yet, by time) (`GET /trial/slots`; tenant token; scopes: trial) */
144
+ /** Trial times (a trial lesson's own times at teaching sites; by default only those not over yet, by time) (`GET /trial/slots`; tenant token; scopes: trial) */
141
145
  listTrialSlots(query?: QueryOf<'listTrialSlots'>, options?: RequestOptions): Promise<ResultOf<'listTrialSlots'>>;
142
146
  /** Every item of `listTrialSlots`, page after page. */
143
147
  listTrialSlotsAll(query?: Omit<QueryOf<'listTrialSlots'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listTrialSlots'>>;
144
- /** Schedule trial lesson slots (they can repeat weekly); the teaching site must be published (`POST /trial/slots`; tenant token; scopes: trial) */
148
+ /** Open trial times (they can repeat weekly): add them to an existing trial lesson, or open a new trial draft (`POST /trial/slots`; tenant token; scopes: trial) */
145
149
  createTrialSlots(body: BodyOf<'createTrialSlots'>, options?: RequestOptions): Promise<ResultOf<'createTrialSlots'>>;
146
- /** Cancel a trial lesson slot (whoever booked it is notified as usual; the booking is cancelled with it) (`POST /trial/slots/{slot_id}/cancel`; tenant token; scopes: trial) */
150
+ /** Close a trial time (no more bookings; bookings already made stay as they are, and nobody is notified) (`POST /trial/slots/{slot_id}/cancel`; tenant token; scopes: trial) */
147
151
  cancelTrialSlot(slotId: string, body?: BodyOf<'cancelTrialSlot'>, options?: RequestOptions): Promise<ResultOf<'cancelTrialSlot'>>;
152
+ /** Fill in (or change) the teacher of a trial draft (`PUT /trial/openings/{opening_id}/teacher`; tenant token; scopes: trial) */
153
+ setTrialTeacher(openingId: string, body: BodyOf<'setTrialTeacher'>, options?: RequestOptions): Promise<ResultOf<'setTrialTeacher'>>;
148
154
  /** Trial lesson bookings at the institution's teaching sites (`GET /trial/bookings`; tenant token; scopes: trial) */
149
155
  listTrialBookings(query?: QueryOf<'listTrialBookings'>, options?: RequestOptions): Promise<ResultOf<'listTrialBookings'>>;
150
156
  /** Every item of `listTrialBookings`, page after page. */
151
157
  listTrialBookingsAll(query?: Omit<QueryOf<'listTrialBookings'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listTrialBookings'>>;
152
158
  /** Confirm a trial lesson booking (`POST /trial/bookings/{booking_id}/confirm`; tenant token; scopes: trial) */
153
159
  confirmTrialBooking(bookingId: string, options?: RequestOptions): Promise<ResultOf<'confirmTrialBooking'>>;
160
+ /** The institution's teaching sites (campuses), newest first (`GET /venues`; tenant token; scopes: venue:readonly, venue, trial) */
161
+ listVenues(query?: QueryOf<'listVenues'>, options?: RequestOptions): Promise<ResultOf<'listVenues'>>;
162
+ /** Every item of `listVenues`, page after page. */
163
+ listVenuesAll(query?: Omit<QueryOf<'listVenues'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listVenues'>>;
164
+ /** Create a draft teaching site (campus); it is published in the platform and reviewed as usual (`POST /venues`; tenant token; scopes: venue) */
165
+ createVenue(body: BodyOf<'createVenue'>, options?: RequestOptions): Promise<ResultOf<'createVenue'>>;
166
+ /** One teaching site (of this institution only) (`GET /venues/{venue_id}`; tenant token; scopes: venue:readonly, venue, trial) */
167
+ getVenue(venueId: string, options?: RequestOptions): Promise<ResultOf<'getVenue'>>;
168
+ /** Openings: every class and course opened (group classes, one-to-ones, open classes, trial lessons, video courses), newest first (`GET /openings`; tenant / user token; scopes: opening:readonly) */
169
+ listOpenings(query?: QueryOf<'listOpenings'>, options?: RequestOptions): Promise<ResultOf<'listOpenings'>>;
170
+ /** Every item of `listOpenings`, page after page. */
171
+ listOpeningsAll(query?: Omit<QueryOf<'listOpenings'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listOpenings'>>;
172
+ /** One opening (`GET /openings/{opening_id}`; tenant / user token; scopes: opening:readonly) */
173
+ getOpening(openingId: string, options?: RequestOptions): Promise<ResultOf<'getOpening'>>;
174
+ /** One page of the institution's dashboard (read only): enrolment, teaching, teaching sites, income, community and events (`GET /insights`; tenant token; scopes: insights:readonly) */
175
+ getInsights(query?: QueryOf<'getInsights'>, options?: RequestOptions): Promise<ResultOf<'getInsights'>>;
154
176
  /** The institution's orders (read-only, for reconciliation). Write operations involving money are not open (`GET /trade/orders`; tenant token; scopes: trade:order:readonly) */
155
177
  listTradeOrders(query?: QueryOf<'listTradeOrders'>, options?: RequestOptions): Promise<ResultOf<'listTradeOrders'>>;
156
178
  /** Every item of `listTradeOrders`, page after page. */
157
179
  listTradeOrdersAll(query?: Omit<QueryOf<'listTradeOrders'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listTradeOrders'>>;
180
+ /** The institution's bookings (one-to-ones and trial lessons, read only for reconciliation), latest lesson first (`GET /bookings`; tenant token; scopes: trade:booking:readonly) */
181
+ listBookings(query?: QueryOf<'listBookings'>, options?: RequestOptions): Promise<ResultOf<'listBookings'>>;
182
+ /** Every item of `listBookings`, page after page. */
183
+ listBookingsAll(query?: Omit<QueryOf<'listBookings'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listBookings'>>;
184
+ /** One booked lesson (`GET /bookings/{booking_id}`; tenant token; scopes: trade:booking:readonly) */
185
+ getBooking(bookingId: string, options?: RequestOptions): Promise<ResultOf<'getBooking'>>;
158
186
  /** Reviews of this institution (and its courses and teaching sites) (`GET /reviews`; tenant token; scopes: review) */
159
187
  listTenantReviews(query?: QueryOf<'listTenantReviews'>, options?: RequestOptions): Promise<ResultOf<'listTenantReviews'>>;
160
188
  /** Every item of `listTenantReviews`, page after page. */
@@ -173,5 +201,47 @@ export declare abstract class Operations {
173
201
  getUserLearningProfile(options?: RequestOptions): Promise<ResultOf<'getUserLearningProfile'>>;
174
202
  /** Report a piece of public content on behalf of a user (it enters the platform's moderation queue; the user can see it in their own report history) (`POST /reports`; user token; scopes: report:create) */
175
203
  reportOnBehalf(body: BodyOf<'reportOnBehalf'>, options?: RequestOptions): Promise<ResultOf<'reportOnBehalf'>>;
204
+ /** Get an upload URL for an image (it is re-encoded and its EXIF removed when the post is made) (`POST /community/images`; user / tenant token; scopes: community:post) */
205
+ createCommunityImageUpload(body: BodyOf<'createCommunityImageUpload'>, options?: RequestOptions): Promise<ResultOf<'createCommunityImageUpload'>>;
206
+ /** Get an upload URL for a video (up to 4 GB, 1 hour) (`POST /community/videos`; user / tenant token; scopes: community:post) */
207
+ createCommunityVideoUpload(body: BodyOf<'createCommunityVideoUpload'>, options?: RequestOptions): Promise<ResultOf<'createCommunityVideoUpload'>>;
208
+ /** The upload is done; start transcoding (calling it again has no side effects) (`POST /community/videos/{asset_id}/complete`; user / tenant token; scopes: community:post) */
209
+ completeCommunityVideoUpload(assetId: string, options?: RequestOptions): Promise<ResultOf<'completeCommunityVideoUpload'>>;
210
+ /** Transcoding status (only for videos this app uploaded for this person) (`GET /community/videos/{asset_id}`; user / tenant token; scopes: community:post) */
211
+ getCommunityVideo(assetId: string, options?: RequestOptions): Promise<ResultOf<'getCommunityVideo'>>;
212
+ /** Posts this app made in this person's name (the person's own posts and other apps' posts are not visible) (`GET /community/posts`; user / tenant token; scopes: community:post) */
213
+ listCommunityPosts(query?: QueryOf<'listCommunityPosts'>, options?: RequestOptions): Promise<ResultOf<'listCommunityPosts'>>;
214
+ /** Every item of `listCommunityPosts`, page after page. */
215
+ listCommunityPostsAll(query?: Omit<QueryOf<'listCommunityPosts'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listCommunityPosts'>>;
216
+ /** Publish a public post in this person's name (text, up to 9 images or 1 video, up to 10 topics) (`POST /community/posts`; user / tenant token; scopes: community:post) */
217
+ createCommunityPost(body: BodyOf<'createCommunityPost'>, options?: RequestOptions): Promise<ResultOf<'createCommunityPost'>>;
218
+ /** The status of one post (only posts this app made; anything else is 404) (`GET /community/posts/{post_id}`; user / tenant token; scopes: community:post) */
219
+ getCommunityPost(postId: string, options?: RequestOptions): Promise<ResultOf<'getCommunityPost'>>;
220
+ /** Delete a post (only posts this app made; anything else is 404) (`DELETE /community/posts/{post_id}`; user / tenant token; scopes: community:post) */
221
+ deleteCommunityPost(postId: string, options?: RequestOptions): Promise<ResultOf<'deleteCommunityPost'>>;
222
+ /** Categories a post can be filed under (those enabled in this region) (`GET /community/categories`; user / tenant token; scopes: community:post) */
223
+ listCommunityCategories(options?: RequestOptions): Promise<ResultOf<'listCommunityCategories'>>;
224
+ /** This person's timetable: their sessions, the events they signed up for, confirmed trial lessons and exams (for calendar apps; read-only) (`GET /me/schedule`; user token; scopes: me:schedule:readonly) */
225
+ getMySchedule(query: QueryOf<'getMySchedule'>, options?: RequestOptions): Promise<ResultOf<'getMySchedule'>>;
226
+ /** Every item of `getMySchedule`, page after page. */
227
+ getMyScheduleAll(query?: Omit<QueryOf<'getMySchedule'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'getMySchedule'>>;
228
+ /** The courses this person is studying and their progress (lessons completed, last studied; level and goals belong to the gold-only profile and are not here) (`GET /me/courses`; user token; scopes: me:learning:readonly) */
229
+ listMyCourses(query?: QueryOf<'listMyCourses'>, options?: RequestOptions): Promise<ResultOf<'listMyCourses'>>;
230
+ /** Every item of `listMyCourses`, page after page. */
231
+ listMyCoursesAll(query?: Omit<QueryOf<'listMyCourses'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listMyCourses'>>;
232
+ /** This person's word bank: words, pinyin, review state (no meanings, and review records cannot be changed) (`GET /me/words`; user token; scopes: me:words:readonly, me:words) */
233
+ listMyWords(query?: QueryOf<'listMyWords'>, options?: RequestOptions): Promise<ResultOf<'listMyWords'>>;
234
+ /** Every item of `listMyWords`, page after page. */
235
+ listMyWordsAll(query?: Omit<QueryOf<'listMyWords'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listMyWords'>>;
236
+ /** Add a word to this person's word bank (unchanged if it is already there; its source is recorded as this app) (`POST /me/words`; user token; scopes: me:words) */
237
+ addMyWord(body: BodyOf<'addMyWord'>, options?: RequestOptions): Promise<ResultOf<'addMyWord'>>;
238
+ /** A class's exams (tenant tokens only, own institution) (`GET /classes/{class_id}/exams`; tenant token; scopes: grade:readonly) */
239
+ listClassExams(classId: string, query?: QueryOf<'listClassExams'>, options?: RequestOptions): Promise<ResultOf<'listClassExams'>>;
240
+ /** Every item of `listClassExams`, page after page. */
241
+ listClassExamsAll(classId: string, query?: Omit<QueryOf<'listClassExams'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listClassExams'>>;
242
+ /** Each learner's result in one exam (scores only once released; tenant tokens only, own institution) (`GET /classes/{class_id}/exams/{exam_id}/results`; tenant token; scopes: grade:readonly) */
243
+ listClassExamResults(classId: string, examId: string, query?: QueryOf<'listClassExamResults'>, options?: RequestOptions): Promise<ResultOf<'listClassExamResults'>>;
244
+ /** Every item of `listClassExamResults`, page after page. */
245
+ listClassExamResultsAll(classId: string, examId: string, query?: Omit<QueryOf<'listClassExamResults'>, 'page_token'>, options?: RequestOptions): AsyncIterable<ItemOf<'listClassExamResults'>>;
176
246
  }
177
247
  export {};