skool-cli 1.0.0

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 (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +179 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +33 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/create-folder.d.ts +3 -0
  8. package/dist/commands/create-folder.d.ts.map +1 -0
  9. package/dist/commands/create-folder.js +24 -0
  10. package/dist/commands/create-folder.js.map +1 -0
  11. package/dist/commands/create-lesson.d.ts +3 -0
  12. package/dist/commands/create-lesson.d.ts.map +1 -0
  13. package/dist/commands/create-lesson.js +35 -0
  14. package/dist/commands/create-lesson.js.map +1 -0
  15. package/dist/commands/create-post.d.ts +3 -0
  16. package/dist/commands/create-post.d.ts.map +1 -0
  17. package/dist/commands/create-post.js +36 -0
  18. package/dist/commands/create-post.js.map +1 -0
  19. package/dist/commands/debug-api.d.ts +3 -0
  20. package/dist/commands/debug-api.d.ts.map +1 -0
  21. package/dist/commands/debug-api.js +112 -0
  22. package/dist/commands/debug-api.js.map +1 -0
  23. package/dist/commands/debug-manual.d.ts +3 -0
  24. package/dist/commands/debug-manual.d.ts.map +1 -0
  25. package/dist/commands/debug-manual.js +66 -0
  26. package/dist/commands/debug-manual.js.map +1 -0
  27. package/dist/commands/debug.d.ts +3 -0
  28. package/dist/commands/debug.d.ts.map +1 -0
  29. package/dist/commands/debug.js +114 -0
  30. package/dist/commands/debug.js.map +1 -0
  31. package/dist/commands/delete-lesson.d.ts +3 -0
  32. package/dist/commands/delete-lesson.d.ts.map +1 -0
  33. package/dist/commands/delete-lesson.js +17 -0
  34. package/dist/commands/delete-lesson.js.map +1 -0
  35. package/dist/commands/get-categories.d.ts +3 -0
  36. package/dist/commands/get-categories.d.ts.map +1 -0
  37. package/dist/commands/get-categories.js +34 -0
  38. package/dist/commands/get-categories.js.map +1 -0
  39. package/dist/commands/get-members.d.ts +3 -0
  40. package/dist/commands/get-members.d.ts.map +1 -0
  41. package/dist/commands/get-members.js +35 -0
  42. package/dist/commands/get-members.js.map +1 -0
  43. package/dist/commands/get-posts.d.ts +3 -0
  44. package/dist/commands/get-posts.d.ts.map +1 -0
  45. package/dist/commands/get-posts.js +41 -0
  46. package/dist/commands/get-posts.js.map +1 -0
  47. package/dist/commands/list-lessons.d.ts +3 -0
  48. package/dist/commands/list-lessons.d.ts.map +1 -0
  49. package/dist/commands/list-lessons.js +45 -0
  50. package/dist/commands/list-lessons.js.map +1 -0
  51. package/dist/commands/login.d.ts +3 -0
  52. package/dist/commands/login.d.ts.map +1 -0
  53. package/dist/commands/login.js +30 -0
  54. package/dist/commands/login.js.map +1 -0
  55. package/dist/commands/test-api.d.ts +3 -0
  56. package/dist/commands/test-api.d.ts.map +1 -0
  57. package/dist/commands/test-api.js +143 -0
  58. package/dist/commands/test-api.js.map +1 -0
  59. package/dist/commands/whoami.d.ts +3 -0
  60. package/dist/commands/whoami.d.ts.map +1 -0
  61. package/dist/commands/whoami.js +17 -0
  62. package/dist/commands/whoami.js.map +1 -0
  63. package/dist/core/browser-manager.d.ts +25 -0
  64. package/dist/core/browser-manager.d.ts.map +1 -0
  65. package/dist/core/browser-manager.js +104 -0
  66. package/dist/core/browser-manager.js.map +1 -0
  67. package/dist/core/config.d.ts +15 -0
  68. package/dist/core/config.d.ts.map +1 -0
  69. package/dist/core/config.js +17 -0
  70. package/dist/core/config.js.map +1 -0
  71. package/dist/core/html-generator.d.ts +15 -0
  72. package/dist/core/html-generator.d.ts.map +1 -0
  73. package/dist/core/html-generator.js +254 -0
  74. package/dist/core/html-generator.js.map +1 -0
  75. package/dist/core/page-ops.d.ts +69 -0
  76. package/dist/core/page-ops.d.ts.map +1 -0
  77. package/dist/core/page-ops.js +511 -0
  78. package/dist/core/page-ops.js.map +1 -0
  79. package/dist/core/skool-api.d.ts +74 -0
  80. package/dist/core/skool-api.d.ts.map +1 -0
  81. package/dist/core/skool-api.js +389 -0
  82. package/dist/core/skool-api.js.map +1 -0
  83. package/dist/core/skool-client.d.ts +63 -0
  84. package/dist/core/skool-client.d.ts.map +1 -0
  85. package/dist/core/skool-client.js +501 -0
  86. package/dist/core/skool-client.js.map +1 -0
  87. package/dist/core/types.d.ts +58 -0
  88. package/dist/core/types.d.ts.map +1 -0
  89. package/dist/core/types.js +2 -0
  90. package/dist/core/types.js.map +1 -0
  91. package/package.json +51 -0
@@ -0,0 +1,511 @@
1
+ import { SKOOL_BASE_URL, SPA_NAV_DELAY, MAX_TITLE_LENGTH, DEFAULT_TIMEOUT, } from "./config.js";
2
+ /**
3
+ * Low-level page operations for Skool automation.
4
+ *
5
+ * Encapsulates all 6 production workarounds:
6
+ * 1. SAVE disabled after HTML injection -> space+backspace dirty trigger
7
+ * 2. Dropdown overlay blocks clicks -> pointerEvents:none fix
8
+ * 3. Volatile refs -> N/A with Playwright native locators
9
+ * 4. SPA timing -> wait after navigation
10
+ * 5. HTML conversion -> handled by html-generator
11
+ * 6. 50-char title limit -> auto-truncate
12
+ */
13
+ export class PageOps {
14
+ browser;
15
+ constructor(browser) {
16
+ this.browser = browser;
17
+ }
18
+ /** Get the current page */
19
+ async page() {
20
+ return this.browser.getPage();
21
+ }
22
+ // ----------------------------------------------------------
23
+ // Navigation
24
+ // ----------------------------------------------------------
25
+ /** Navigate to a Skool URL with SPA delay (workaround #4) */
26
+ async goto(path) {
27
+ const page = await this.page();
28
+ const url = path.startsWith("http") ? path : `${SKOOL_BASE_URL}/${path}`;
29
+ await page.goto(url, { waitUntil: "domcontentloaded" });
30
+ await page.waitForTimeout(SPA_NAV_DELAY);
31
+ }
32
+ /** Navigate to a group's community page */
33
+ async gotoCommunity(groupSlug) {
34
+ await this.goto(groupSlug);
35
+ }
36
+ /** Navigate to a group's classroom page */
37
+ async gotoClassroom(groupSlug) {
38
+ await this.goto(`${groupSlug}/classroom`);
39
+ }
40
+ /** Navigate to a group's members page */
41
+ async gotoMembers(groupSlug) {
42
+ await this.goto(`${groupSlug}/members`);
43
+ }
44
+ // ----------------------------------------------------------
45
+ // Auth detection
46
+ // ----------------------------------------------------------
47
+ /** Check if user is authenticated on the current page */
48
+ async isAuthenticated() {
49
+ const page = await this.page();
50
+ const content = await page.content();
51
+ // If we see login/signup prompts, we're not authenticated
52
+ const unauthIndicators = [
53
+ 'href="/login"',
54
+ "Log in",
55
+ "Sign in",
56
+ "Sign up",
57
+ "Create your free account",
58
+ ];
59
+ for (const indicator of unauthIndicators) {
60
+ if (content.includes(indicator)) {
61
+ // Could be a false positive on a public page -- check more carefully
62
+ const loginButton = page.locator('a[href="/login"], button:has-text("Log in"), button:has-text("Sign in")');
63
+ if ((await loginButton.count()) > 0) {
64
+ return false;
65
+ }
66
+ }
67
+ }
68
+ return true;
69
+ }
70
+ // ----------------------------------------------------------
71
+ // Login
72
+ // ----------------------------------------------------------
73
+ /** Perform login with email and password */
74
+ async login(email, password) {
75
+ const page = await this.page();
76
+ await this.goto("login");
77
+ // Fill email
78
+ const emailInput = page.locator('input[name="email"], input[type="email"]');
79
+ await emailInput.first().fill(email);
80
+ // Fill password
81
+ const passwordInput = page.locator('input[name="password"], input[type="password"]');
82
+ await passwordInput.first().fill(password);
83
+ // Submit
84
+ const submitBtn = page.locator('button[type="submit"]');
85
+ await submitBtn.first().click();
86
+ // Wait for navigation away from login page
87
+ try {
88
+ await page.waitForURL((url) => !url.pathname.includes("/login"), {
89
+ timeout: DEFAULT_TIMEOUT,
90
+ });
91
+ await page.waitForTimeout(3000);
92
+ // Save auth state to disk so it persists across CLI invocations
93
+ await this.browser.saveAuthState();
94
+ return true;
95
+ }
96
+ catch {
97
+ return false;
98
+ }
99
+ }
100
+ // ----------------------------------------------------------
101
+ // Click helpers (workaround #2: dropdown overlay)
102
+ // ----------------------------------------------------------
103
+ /** Remove all known overlays that block pointer events */
104
+ async clearOverlays() {
105
+ const page = await this.page();
106
+ await page.evaluate(() => {
107
+ const selectors = [
108
+ '[class*="DropdownBackground"]',
109
+ '[class*="InputBackdrop"]',
110
+ '[class*="Backdrop"]',
111
+ '[class*="Overlay"]',
112
+ ];
113
+ selectors.forEach((sel) => {
114
+ document.querySelectorAll(sel).forEach((el) => {
115
+ el.style.pointerEvents = "none";
116
+ });
117
+ });
118
+ });
119
+ }
120
+ /** Click a locator, fixing overlays if needed */
121
+ async safeClick(locator) {
122
+ const page = await this.page();
123
+ try {
124
+ await locator.click({ timeout: 5000 });
125
+ }
126
+ catch {
127
+ // Workaround: remove all known overlays blocking clicks
128
+ await page.evaluate(() => {
129
+ const selectors = [
130
+ '[class*="DropdownBackground"]',
131
+ '[class*="dropdown-background"]',
132
+ '[class*="InputBackdrop"]',
133
+ '[class*="Backdrop"]',
134
+ ];
135
+ selectors.forEach((sel) => {
136
+ document.querySelectorAll(sel).forEach((el) => {
137
+ el.style.pointerEvents = "none";
138
+ });
139
+ });
140
+ });
141
+ await locator.click({ timeout: 10000 });
142
+ }
143
+ }
144
+ // ----------------------------------------------------------
145
+ // Title filling (workaround #6: 50-char limit)
146
+ // ----------------------------------------------------------
147
+ /** Fill a title field, truncating to MAX_TITLE_LENGTH */
148
+ async fillTitle(locator, title) {
149
+ const truncated = title.length > MAX_TITLE_LENGTH
150
+ ? title.slice(0, MAX_TITLE_LENGTH - 3) + "..."
151
+ : title;
152
+ await locator.fill(truncated);
153
+ return truncated;
154
+ }
155
+ // ----------------------------------------------------------
156
+ // TipTap HTML injection (workaround #1: dirty trigger)
157
+ // ----------------------------------------------------------
158
+ /**
159
+ * Inject HTML content into the TipTap editor.
160
+ * Includes the space+backspace dirty trigger so SAVE becomes enabled.
161
+ */
162
+ async injectTipTapContent(html) {
163
+ const page = await this.page();
164
+ // Click into the body area first to ensure it's focused
165
+ // The body editor is the TipTap ProseMirror element (not the title)
166
+ const bodyEditor = page.locator('div.tiptap.ProseMirror, div[class*="skool-editor"], div.ProseMirror').first();
167
+ if ((await bodyEditor.count()) > 0) {
168
+ await bodyEditor.click();
169
+ await page.waitForTimeout(300);
170
+ }
171
+ const result = await page.evaluate((htmlContent) => {
172
+ // Try multiple selectors for the TipTap editor (body, not title)
173
+ const selectors = [
174
+ 'div.tiptap.ProseMirror',
175
+ 'div[class*="skool-editor"]',
176
+ 'div.ProseMirror[contenteditable]',
177
+ '[contenteditable="true"]',
178
+ ];
179
+ let ce = null;
180
+ for (const sel of selectors) {
181
+ const candidates = document.querySelectorAll(sel);
182
+ // If multiple, pick the last one (body is usually after title)
183
+ if (candidates.length > 0) {
184
+ ce = candidates[candidates.length - 1];
185
+ break;
186
+ }
187
+ }
188
+ if (!ce)
189
+ return { ok: false, error: "No contenteditable element found" };
190
+ // Access TipTap editor instance
191
+ const editor = ce.editor;
192
+ if (!editor)
193
+ return { ok: false, error: "No TipTap editor instance found on element: " + ce.className };
194
+ try {
195
+ editor.commands.focus();
196
+ editor.commands.setContent(htmlContent, true);
197
+ return { ok: true, length: editor.getHTML().length };
198
+ }
199
+ catch (e) {
200
+ return {
201
+ ok: false,
202
+ error: `setContent failed: ${e.message}`,
203
+ };
204
+ }
205
+ }, html);
206
+ if (!result.ok) {
207
+ throw new Error(`TipTap injection failed: ${result.error}`);
208
+ }
209
+ // Workaround #1: trigger dirty state so SAVE button becomes enabled
210
+ const editor = page.locator("[contenteditable=true]").first();
211
+ await editor.click();
212
+ await page.keyboard.press("End");
213
+ await page.keyboard.type(" ");
214
+ await page.keyboard.press("Backspace");
215
+ await page.waitForTimeout(1000);
216
+ return true;
217
+ }
218
+ // ----------------------------------------------------------
219
+ // Save and verify
220
+ // ----------------------------------------------------------
221
+ /** Click SAVE and verify it completes (button becomes disabled) */
222
+ async clickSaveAndVerify() {
223
+ const page = await this.page();
224
+ const saveBtn = page.getByRole("button", { name: "SAVE" });
225
+ // Ensure SAVE is enabled before clicking
226
+ const isDisabled = await saveBtn.getAttribute("disabled");
227
+ if (isDisabled !== null) {
228
+ // Already saved / nothing to save
229
+ return true;
230
+ }
231
+ await this.safeClick(saveBtn);
232
+ // Wait for SAVE to become disabled (= saved successfully)
233
+ try {
234
+ await saveBtn.waitFor({ state: "visible", timeout: 10000 });
235
+ // Poll for disabled state
236
+ for (let i = 0; i < 20; i++) {
237
+ const disabled = await saveBtn.getAttribute("disabled");
238
+ if (disabled !== null)
239
+ return true;
240
+ await page.waitForTimeout(500);
241
+ }
242
+ return false;
243
+ }
244
+ catch {
245
+ return false;
246
+ }
247
+ }
248
+ // ----------------------------------------------------------
249
+ // Community post operations
250
+ // ----------------------------------------------------------
251
+ /** Create a community post */
252
+ async createCommunityPost(groupSlug, title, body, category) {
253
+ const page = await this.page();
254
+ await this.gotoCommunity(groupSlug);
255
+ // Click "Write something" area to open post form
256
+ const postTrigger = page.getByText("Write something");
257
+ await this.safeClick(postTrigger.first());
258
+ await page.waitForTimeout(1500);
259
+ // Fill title
260
+ const titleInput = page.locator('input[placeholder="Title"]');
261
+ await titleInput.fill(title);
262
+ // Fill body via TipTap editor (div.tiptap.ProseMirror.skool-editor)
263
+ const bodyEditor = page.locator("div.tiptap.ProseMirror.skool-editor");
264
+ await bodyEditor.click();
265
+ await bodyEditor.fill(body);
266
+ // Select category if provided
267
+ if (category) {
268
+ await this.clearOverlays();
269
+ const categoryBtn = page.getByText("Select a category");
270
+ await categoryBtn.click({ timeout: 5000 });
271
+ await page.waitForTimeout(500);
272
+ // Clear overlays again after dropdown opens
273
+ await this.clearOverlays();
274
+ // Click the category option via JS to bypass any remaining overlays
275
+ const clicked = await page.evaluate((cat) => {
276
+ let found = false;
277
+ // Find elements with the category text inside a dropdown
278
+ document.querySelectorAll('[class*="Dropdown"] *').forEach((el) => {
279
+ if (!found && el.textContent?.trim() === cat && el.children.length === 0) {
280
+ el.click();
281
+ found = true;
282
+ }
283
+ });
284
+ if (found)
285
+ return true;
286
+ // Fallback: find by GroupFeedLinkLabel class (the dropdown option)
287
+ document.querySelectorAll('[class*="GroupFeedLinkLabel"]').forEach((el) => {
288
+ if (!found && el.textContent?.trim() === cat) {
289
+ el.click();
290
+ found = true;
291
+ }
292
+ });
293
+ return found;
294
+ }, category);
295
+ if (!clicked) {
296
+ throw new Error(`Category "${category}" not found in dropdown`);
297
+ }
298
+ await page.waitForTimeout(500);
299
+ }
300
+ // Click Post button (the submit button with SubmitButtonWrapper class)
301
+ const postBtn = page.locator('button[class*="SubmitButtonWrapper"]');
302
+ await this.safeClick(postBtn.first());
303
+ await page.waitForTimeout(3000);
304
+ return true;
305
+ }
306
+ // ----------------------------------------------------------
307
+ // Classroom lesson operations
308
+ // ----------------------------------------------------------
309
+ /**
310
+ * Create a new lesson in a classroom module.
311
+ *
312
+ * Flow: enter course → "..." menu → "Add page" → click pencil → fill title → inject HTML → save
313
+ */
314
+ async createLesson(groupSlug, moduleName, title, html, courseName) {
315
+ const page = await this.page();
316
+ // Step 1: Navigate to classroom and enter course
317
+ await this.gotoClassroom(groupSlug);
318
+ if (courseName) {
319
+ await this.safeClick(page.getByText(courseName, { exact: false }).first());
320
+ }
321
+ else {
322
+ const courseLink = page.locator('a[href*="/classroom/"]').first();
323
+ if ((await courseLink.count()) > 0)
324
+ await courseLink.click();
325
+ }
326
+ await page.waitForTimeout(SPA_NAV_DELAY);
327
+ // Step 2: Open "..." menu and click "Add page"
328
+ const courseTopArea = page.locator('div[class*="CourseMenuTop"]').first();
329
+ await courseTopArea.hover();
330
+ await page.waitForTimeout(500);
331
+ // Click "..." via JS (needed because it's an SVG icon)
332
+ await page.evaluate(() => {
333
+ const topArea = document.querySelector('div[class*="CourseMenuTop"]');
334
+ if (!topArea)
335
+ return;
336
+ topArea.querySelectorAll('div, button, span, svg').forEach((el) => {
337
+ const rect = el.getBoundingClientRect();
338
+ const text = el.textContent?.trim() || "";
339
+ if (rect.width > 10 && rect.width < 50 && rect.height > 10 && rect.height < 50 && text.length < 4) {
340
+ el.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
341
+ }
342
+ });
343
+ });
344
+ await page.waitForTimeout(800);
345
+ // Click "Add page" from dropdown
346
+ try {
347
+ await page.getByText("Add page", { exact: true }).click({ timeout: 5000 });
348
+ }
349
+ catch {
350
+ await this.clearOverlays();
351
+ await page.getByText("Add page", { exact: true }).click({ force: true, timeout: 5000 });
352
+ }
353
+ await page.waitForTimeout(3000);
354
+ // Step 3: Click the pencil (edit) icon using REAL Playwright click
355
+ // The pencil is the last SVG inside ModuleTitleRow in the content panel
356
+ const titleRow = page.locator('div[class*="ModuleTitleRow"]').first();
357
+ const pencilSvg = titleRow.locator("svg").last();
358
+ try {
359
+ await pencilSvg.click({ timeout: 5000 });
360
+ }
361
+ catch {
362
+ // Fallback: click by coordinates near the right edge of the title row
363
+ const box = await titleRow.boundingBox();
364
+ if (box) {
365
+ await page.mouse.click(box.x + box.width - 25, box.y + box.height / 2);
366
+ }
367
+ }
368
+ await page.waitForTimeout(2000);
369
+ // Verify edit mode (SAVE button visible)
370
+ const inEditMode = (await page.locator('button:has-text("SAVE")').count()) > 0;
371
+ if (!inEditMode) {
372
+ throw new Error("Could not enter edit mode. Try running with SKOOL_CLI_HEADLESS=false to debug.");
373
+ }
374
+ // Step 4: Fill title + inject content
375
+ // In edit mode, the entire page is one TipTap editor.
376
+ // The title "New page" is the first text, body is below it.
377
+ // Strategy: select "New page" text, type new title, press Enter, then inject HTML for body.
378
+ const truncatedTitle = title.length > MAX_TITLE_LENGTH
379
+ ? title.slice(0, MAX_TITLE_LENGTH - 3) + "..."
380
+ : title;
381
+ // Click the "New page" title text in the content area
382
+ const contentTitle = page.locator('div[class*="CourseModuleWrapper"] div[class*="ModuleTitle"]').first();
383
+ if ((await contentTitle.count()) > 0) {
384
+ await contentTitle.click();
385
+ }
386
+ // Select all title text and replace
387
+ await page.keyboard.press("Meta+a");
388
+ await page.keyboard.type(truncatedTitle);
389
+ await page.waitForTimeout(300);
390
+ // Move to body: press Enter to create a new line below the title
391
+ await page.keyboard.press("Enter");
392
+ await page.waitForTimeout(300);
393
+ // Now inject HTML into the TipTap editor for the body content
394
+ // The editor already has focus, so we inject into the current editor instance
395
+ await this.injectTipTapContent(html);
396
+ // Step 6: Click SAVE
397
+ const saved = await this.clickSaveAndVerify();
398
+ if (!saved) {
399
+ throw new Error(`Lesson "${truncatedTitle}" created but SAVE not confirmed.`);
400
+ }
401
+ return true;
402
+ }
403
+ // ----------------------------------------------------------
404
+ // Read operations
405
+ // ----------------------------------------------------------
406
+ /** Extract posts from the community page */
407
+ async extractPosts(groupSlug) {
408
+ const page = await this.page();
409
+ await this.gotoCommunity(groupSlug);
410
+ // Posts are rendered as links with href pattern /{group}/{post-slug}
411
+ // Each post block has: author link, category link, title link, preview text
412
+ // We find post title links by their href pattern (not category or author links)
413
+ return page.evaluate((slug) => {
414
+ const posts = [];
415
+ // Post title links match /{group}/{post-slug} but NOT /{group}?c= (categories)
416
+ // and NOT /@username (authors) and NOT /{group}/classroom etc.
417
+ const reservedPaths = ["classroom", "calendar", "about", "-", "?", "signup", "legal"];
418
+ const allLinks = document.querySelectorAll(`a[href^="/${slug}/"]`);
419
+ allLinks.forEach((link) => {
420
+ const href = link.getAttribute("href") || "";
421
+ const path = href.replace(`/${slug}/`, "");
422
+ // Skip non-post links
423
+ if (!path || path.includes("?") || reservedPaths.some((r) => path.startsWith(r)))
424
+ return;
425
+ // This is a post title link - find surrounding context
426
+ const text = link.textContent?.trim() || "";
427
+ if (!text || text.length < 5)
428
+ return;
429
+ // Look for author and category in nearby sibling/parent elements
430
+ const container = link.closest('[class*="Post"], [class*="card"], div') || link.parentElement;
431
+ let author = "";
432
+ let category = "";
433
+ let preview = "";
434
+ if (container) {
435
+ // Author links have href starting with /@ — pick the one with a real name (not "1" avatar)
436
+ const authorLinks = container.querySelectorAll('a[href^="/@"]');
437
+ authorLinks.forEach((al) => {
438
+ if (!author) {
439
+ const t = al.textContent?.trim() || "";
440
+ if (t.length > 1)
441
+ author = t;
442
+ }
443
+ });
444
+ // Category links have href with ?c=
445
+ const catLink = container.querySelector('a[href*="?c="]');
446
+ if (catLink)
447
+ category = catLink.textContent?.trim() || "";
448
+ // Preview is the first <p> or text block after the title
449
+ const pEl = container.querySelector("p");
450
+ if (pEl)
451
+ preview = pEl.textContent?.trim().slice(0, 200) || "";
452
+ }
453
+ posts.push({ title: text, author, category, preview, url: href });
454
+ });
455
+ return posts;
456
+ }, groupSlug);
457
+ }
458
+ /** Extract categories from the community page */
459
+ async extractCategories(groupSlug) {
460
+ const page = await this.page();
461
+ await this.gotoCommunity(groupSlug);
462
+ // Categories are rendered as button chips with class ChipWrapper
463
+ return page.evaluate(() => {
464
+ const categories = [];
465
+ // Skool uses buttons with ChipWrapper class for category filters
466
+ const chips = document.querySelectorAll('button[class*="ChipWrapper"]');
467
+ chips.forEach((btn) => {
468
+ const text = btn.textContent?.trim();
469
+ if (text && text !== "All" && text !== "More..." && !categories.includes(text)) {
470
+ categories.push(text);
471
+ }
472
+ });
473
+ return categories;
474
+ });
475
+ }
476
+ /** Extract members from the members page */
477
+ async extractMembers(groupSlug, search) {
478
+ const page = await this.page();
479
+ await this.gotoMembers(groupSlug);
480
+ // If search query provided, fill search box
481
+ if (search) {
482
+ const searchInput = page.locator('input[placeholder="Search"]');
483
+ if ((await searchInput.count()) > 0) {
484
+ await searchInput.first().fill(search);
485
+ await page.waitForTimeout(1500);
486
+ }
487
+ }
488
+ // Members are listed as links with href /@username?g=group
489
+ return page.evaluate((slug) => {
490
+ const members = [];
491
+ const seen = new Set();
492
+ const memberLinks = document.querySelectorAll(`a[href*="?g=${slug}"]`);
493
+ memberLinks.forEach((link) => {
494
+ const href = link.getAttribute("href") || "";
495
+ if (!href.startsWith("/@"))
496
+ return;
497
+ const name = link.textContent?.trim() || "";
498
+ if (!name || name.length < 2 || seen.has(name))
499
+ return;
500
+ seen.add(name);
501
+ members.push({
502
+ name,
503
+ level: 0,
504
+ contributions: 0,
505
+ });
506
+ });
507
+ return members;
508
+ }, groupSlug);
509
+ }
510
+ }
511
+ //# sourceMappingURL=page-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-ops.js","sourceRoot":"","sources":["../../src/core/page-ops.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Direct HTTP API client for Skool's internal API (api2.skool.com).
3
+ * Discovered via network interception.
4
+ *
5
+ * Uses auth cookies from the browser session for authentication.
6
+ */
7
+ import { BrowserManager } from "./browser-manager.js";
8
+ export declare class SkoolApi {
9
+ private browser;
10
+ constructor(browser: BrowserManager);
11
+ /** Get auth cookies from the browser session */
12
+ private getCookies;
13
+ /** Make an authenticated API request */
14
+ private request;
15
+ /**
16
+ * Create a new page (lesson) in a course.
17
+ *
18
+ * @param groupId - Group ID (from the classroom URL or course data)
19
+ * @param userId - User ID (the logged-in user)
20
+ * @param parentId - Parent folder/module ID
21
+ * @param rootId - Root course ID
22
+ * @param title - Page title
23
+ * @param content - HTML content for the page body
24
+ */
25
+ createPage(options: {
26
+ groupId: string;
27
+ userId: string;
28
+ parentId: string;
29
+ rootId: string;
30
+ title: string;
31
+ content?: string;
32
+ }): Promise<{
33
+ success: boolean;
34
+ pageId: string;
35
+ message: string;
36
+ }>;
37
+ /**
38
+ * List courses and their modules for a group.
39
+ * This gives us the real root_id and parent_id (folder IDs) needed for API calls.
40
+ */
41
+ listCourses(groupId: string): Promise<Record<string, unknown>[]>;
42
+ /**
43
+ * Create a folder (module) in a course.
44
+ */
45
+ createFolder(options: {
46
+ groupId: string;
47
+ parentId: string;
48
+ rootId: string;
49
+ title: string;
50
+ }): Promise<{
51
+ success: boolean;
52
+ folderId: string;
53
+ message: string;
54
+ }>;
55
+ /**
56
+ * List all items (folders + pages) in a course.
57
+ */
58
+ listCourseItems(groupId: string, rootId: string): Promise<Record<string, unknown>[]>;
59
+ /**
60
+ * Delete a page by ID.
61
+ */
62
+ deletePage(pageId: string): Promise<boolean>;
63
+ /**
64
+ * Extract group_id, user_id, root_id from the classroom page.
65
+ * These IDs are needed for API calls.
66
+ */
67
+ getClassroomIds(groupSlug: string, courseName?: string): Promise<{
68
+ groupId: string;
69
+ userId: string;
70
+ rootId: string;
71
+ courseUrl: string;
72
+ } | null>;
73
+ }
74
+ //# sourceMappingURL=skool-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skool-api.d.ts","sourceRoot":"","sources":["../../src/core/skool-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAyJtD,qBAAa,QAAQ;IACP,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,cAAc;IAE3C,gDAAgD;YAClC,UAAU;IAOxB,wCAAwC;YAC1B,OAAO;IAgCrB;;;;;;;;;OASG;IACG,UAAU,CAAC,OAAO,EAAE;QACxB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAsDlE;;;OAGG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAatE;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE;QAC1B,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAyBpE;;OAEG;IACG,eAAe,CACnB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAuBrC;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlD;;;OAGG;IACG,eAAe,CACnB,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;CAoEV"}