@irfanshadikrishad/anilist 1.2.5 → 1.2.6

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
@@ -44,24 +44,25 @@ anilist login -i <client-id> -s <client-secret>
44
44
 
45
45
  #### CLI Commands Overview
46
46
 
47
- | **Command** | **Options** | **Description** |
48
- | ------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
49
- | **`login`** | `-i, --id` `-s, --secret` | Log in with your AniList credentials |
50
- | **`logout`** | _None_ | Log out from your AniList account |
51
- | **`whoami`** | _None_ | Display information about the logged-in user |
52
- | **`-V, --version`** | _None_ | Display the current version of the CLI |
53
- | **`-h, --help`** | _None_ | Display available commands and options |
54
- | **`trending`** <br> _(alias: `tr`)_ | `-c (default: 10)` | Fetch trending anime (default count is 10) |
55
- | **`popular`** <br> _(alias: `plr`)_ | `-c (default: 10)` | Fetch popular anime (default count is 10) |
56
- | **`user`** | `<username>` | Get information about a specific AniList user |
57
- | **`lists`** <br> _(alias: `ls`)_ | `-a, --anime` <br> `-m, --manga` | Fetch anime or manga lists of the logged-in user |
58
- | **`delete`** <br> _(alias: `del`)_ | `-a, --anime` <br> `-m, --manga` <br> `-s, --activity` | Delete collections of anime, manga or activities |
59
- | **`upcoming`** <br> _(alias:`up`)_ | `-c (default: 10)` | Fetch upcoming anime (default count is 10) |
60
- | **`anime`** | `<anime-id>` | Get anime details by Anime Id |
61
- | **`search`** <br> _(alias:`srch`/`find`)_ | `<query>` <br> `-a, --anime` <br> `-m, --manga` <br> `-c (default: 10)` | Get anime/manga search results |
62
- | **`status`** <br> _(alias: `write`/`post`)_ | `<status>` | Write a status... (text/markdown/html) |
63
- | **`export`** <br> _(alias: `exp`)_ | `-a, --anime` <br> `-m, --manga` | Export anime or manga list in JSON, CSV or XML (MyAnimeList/AniDB) |
64
- | **`import`** <br> _(alias: `imp`)_ | `-a, --anime` <br> `-m, --manga` | Import anime or manga list from exported JSON, MyAnimeList (XML) or AniDB (json-large) |
47
+ | **Command** | **Options** | **Description** |
48
+ | ------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
49
+ | **`login`** | `-i, --id` `-s, --secret` | Log in with your AniList credentials |
50
+ | **`logout`** | _None_ | Log out from your AniList account |
51
+ | **`whoami`** | _None_ | Display information about the logged-in user |
52
+ | **`-V, --version`** | _None_ | Display the current version of the CLI |
53
+ | **`-h, --help`** | _None_ | Display available commands and options |
54
+ | **`trending`** <br> _(alias: `tr`)_ | `-c (default: 10)` | Fetch trending anime (default count is 10) |
55
+ | **`popular`** <br> _(alias: `plr`)_ | `-c (default: 10)` | Fetch popular anime (default count is 10) |
56
+ | **`user`** | `<username>` | Get information about a specific AniList user |
57
+ | **`lists`** <br> _(alias: `ls`)_ | `-a, --anime` <br> `-m, --manga` | Fetch anime or manga lists of the logged-in user |
58
+ | **`delete`** <br> _(alias: `del`)_ | `-a, --anime` <br> `-m, --manga` <br> `-s, --activity` | Delete collections of anime, manga or activities |
59
+ | **`upcoming`** <br> _(alias:`up`)_ | `-c (default: 10)` | Fetch upcoming anime (default count is 10) |
60
+ | **`anime`** | `<anime-id>` | Get anime details by Anime Id |
61
+ | **`search`** <br> _(alias:`srch`/`find`)_ | `<query>` <br> `-a, --anime` <br> `-m, --manga` <br> `-c (default: 10)` | Get anime/manga search results |
62
+ | **`status`** <br> _(alias: `write`/`post`)_ | `<status>` | Write a status... (text/markdown/html) |
63
+ | **`export`** <br> _(alias: `exp`)_ | `-a, --anime` <br> `-m, --manga` | Export anime or manga list in JSON, CSV or XML (MyAnimeList/AniDB) |
64
+ | **`import`** <br> _(alias: `imp`)_ | `-a, --anime` <br> `-m, --manga` | Import anime or manga list from exported JSON, MyAnimeList (XML) or AniDB (json-large) |
65
+ | \*\*`social` <br> _(alias: `sol`)_ | `-f, --follow` <br> `-u, --unfollow` | Follow users who follows you or Unfollow who doesn't follow you back with a simple command |
65
66
 
66
67
  #### Command Breakdown:
67
68
 
@@ -232,6 +233,17 @@ anilist import -m
232
233
  > [!IMPORTANT]
233
234
  > If you are importing from a file, place the file in the system specific download folder, And the exported file will also be exported there as well.
234
235
 
236
+ #### `social` _(alias: `sol`)_:
237
+
238
+ ```bash
239
+ anilist sol -f
240
+ ```
241
+
242
+ - **Options**:
243
+ - `-f, --follow`: To follow users who follows you automatically.
244
+ - `-u, --unfollow`: To unfollow users who doesn't follow you back.
245
+ - **Description**: It follows users who follows you or unfollow users who doesn't follow you back at ease.
246
+
235
247
  #### Security
236
248
 
237
249
  Since you are creating your own API client for login no else else can get your credentials and the generated access token will be stored in your own system. So, As long as you don't share your device (in case you do, just logout) you are safe.
@@ -3,9 +3,9 @@ declare class Auth {
3
3
  /**
4
4
  * Get access-token from user
5
5
  */
6
- static GetAccessToken(): Promise<string>;
6
+ static GetAccessToken(): Promise<string | null>;
7
7
  static StoreAccessToken(token: string): Promise<void>;
8
- static RetriveAccessToken(): Promise<string>;
8
+ static RetriveAccessToken(): Promise<string | null>;
9
9
  static Login(clientId: number, clientSecret: string): Promise<void>;
10
10
  static Myself(): Promise<{
11
11
  id: number;
@@ -49,4 +49,14 @@ declare class Auth {
49
49
  static callAnimeImporter(): Promise<void>;
50
50
  static callMangaImporter(): Promise<void>;
51
51
  }
52
- export { Auth };
52
+ declare class Social {
53
+ /**
54
+ * Follow the users that follows you
55
+ */
56
+ static follow(): Promise<void>;
57
+ /**
58
+ * Unfollow the users thats not following you
59
+ */
60
+ static unfollow(): Promise<void>;
61
+ }
62
+ export { Auth, Social };
@@ -13,13 +13,15 @@ import fetch from "node-fetch";
13
13
  import open from "open";
14
14
  import os from "os";
15
15
  import path from "path";
16
+ import Spinner from "tiny-spinner";
16
17
  import { fetcher } from "./fetcher.js";
17
18
  import { AniDB, AniList, MyAnimeList } from "./lists.js";
18
19
  import { deleteActivityMutation, saveTextActivityMutation, } from "./mutations.js";
19
- import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, userActivityQuery, userFollowersQuery, userFollowingQuery, } from "./queries.js";
20
+ import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, toggleFollowMutation, userActivityQuery, userFollowersQuery, userFollowingQuery, } from "./queries.js";
20
21
  import { aniListEndpoint, getTitle, redirectUri, timestampToTimeAgo, } from "./workers.js";
21
22
  const home_dir = os.homedir();
22
23
  const save_path = path.join(home_dir, ".anilist_token");
24
+ const spinner = new Spinner();
23
25
  class Auth {
24
26
  /**
25
27
  * Get access-token from user
@@ -34,20 +36,29 @@ class Auth {
34
36
  message: "Please enter your AniList access token:",
35
37
  },
36
38
  ]);
39
+ if (!token) {
40
+ console.warn("\nNo token entered. Please try again.");
41
+ return null;
42
+ }
37
43
  return token;
38
44
  }
39
45
  catch (error) {
40
- console.error(`\nSomething went wrong. ${error.message}`);
46
+ console.error(`\nAn error occurred while getting the access token: ${error.message}`);
47
+ return null;
41
48
  }
42
49
  });
43
50
  }
44
51
  static StoreAccessToken(token) {
45
52
  return __awaiter(this, void 0, void 0, function* () {
46
53
  try {
54
+ if (!token) {
55
+ console.warn("\nNo token provided. Nothing to store.");
56
+ return;
57
+ }
47
58
  fs.writeFileSync(save_path, token, { encoding: "utf8" });
48
59
  }
49
60
  catch (error) {
50
- console.error(`\nError storing acess-token. ${error.message}`);
61
+ console.error(`\nError storing access token: ${error.message}`);
51
62
  }
52
63
  });
53
64
  }
@@ -63,6 +74,7 @@ class Auth {
63
74
  }
64
75
  catch (error) {
65
76
  console.error(`\nError retriving acess-token. ${error.message}`);
77
+ return null;
66
78
  }
67
79
  });
68
80
  }
@@ -193,15 +205,12 @@ Statistics (Manga):
193
205
  static isLoggedIn() {
194
206
  return __awaiter(this, void 0, void 0, function* () {
195
207
  try {
196
- if ((yield Auth.RetriveAccessToken()) !== null) {
197
- return true;
198
- }
199
- else {
200
- return false;
201
- }
208
+ const token = yield Auth.RetriveAccessToken();
209
+ return token !== null;
202
210
  }
203
211
  catch (error) {
204
- console.error(`\nError getting isLoggedIn. ${error.message}`);
212
+ console.error(`Error checking login status: ${error.message}`);
213
+ return false;
205
214
  }
206
215
  });
207
216
  }
@@ -215,15 +224,15 @@ Statistics (Manga):
215
224
  console.log(`\nLogout successful. See you soon, ${username}.`);
216
225
  }
217
226
  catch (error) {
218
- console.error("\nError logging out:", error);
227
+ console.error("\nFailed to remove the save file during logout:", error.message);
219
228
  }
220
229
  }
221
230
  else {
222
- console.error("\nYou may already be logged out.");
231
+ console.warn("\nNo active session found. You may already be logged out.");
223
232
  }
224
233
  }
225
234
  catch (error) {
226
- console.error(`\nError logging out. ${error.message}`);
235
+ console.error(`\nAn error occurred during logout: ${error.message}`);
227
236
  }
228
237
  });
229
238
  }
@@ -234,20 +243,7 @@ Statistics (Manga):
234
243
  console.warn(`\nUser not logged in.`);
235
244
  return null;
236
245
  }
237
- const token = yield Auth.RetriveAccessToken();
238
- const request = yield fetch(aniListEndpoint, {
239
- method: "POST",
240
- headers: {
241
- "Content-Type": "application/json",
242
- "Authorization": `Bearer ${token}`,
243
- },
244
- body: JSON.stringify({ query: currentUserQuery }),
245
- });
246
- if (!(request.status === 200)) {
247
- console.error(`Failed to fetch user data. Status: ${request.status}`);
248
- return null;
249
- }
250
- const { data } = yield request.json();
246
+ const { data } = yield fetcher(currentUserQuery, {});
251
247
  return (_b = (_a = data === null || data === void 0 ? void 0 : data.Viewer) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null;
252
248
  });
253
249
  }
@@ -258,20 +254,7 @@ Statistics (Manga):
258
254
  console.log(`\nUser not logged in.`);
259
255
  return null;
260
256
  }
261
- const token = yield Auth.RetriveAccessToken();
262
- const request = yield fetch(aniListEndpoint, {
263
- method: "POST",
264
- headers: {
265
- "Content-Type": "application/json",
266
- "Authorization": `Bearer ${token}`,
267
- },
268
- body: JSON.stringify({ query: currentUserQuery }),
269
- });
270
- if (!request.ok) {
271
- console.error(`Failed to fetch user data. Status: ${request.status}`);
272
- return null;
273
- }
274
- const { data } = yield request.json();
257
+ const { data } = yield fetcher(currentUserQuery, {});
275
258
  return (_b = (_a = data === null || data === void 0 ? void 0 : data.Viewer) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : null;
276
259
  });
277
260
  }
@@ -279,72 +262,70 @@ Statistics (Manga):
279
262
  return __awaiter(this, void 0, void 0, function* () {
280
263
  var _a, _b, _c, _d, _e, _f;
281
264
  try {
282
- if (yield Auth.isLoggedIn()) {
283
- const { activityType } = yield inquirer.prompt([
284
- {
285
- type: "list",
286
- name: "activityType",
287
- message: "What type of activity you want to delete?",
288
- choices: [
289
- { name: "All Activity", value: 0 },
290
- { name: "Text Activity", value: 1 },
291
- { name: "Media List Activity", value: 2 },
292
- { name: "Anime List Activity", value: 3 },
293
- { name: "Manga List Activity", value: 4 },
294
- { name: "Message Activity", value: 5 },
295
- ],
296
- },
297
- ]);
298
- const queryMap = {
299
- 0: activityAllQuery,
300
- 1: activityTextQuery,
301
- 2: activityMediaList,
302
- 3: activityAnimeListQuery,
303
- 4: activityMangaListQuery,
304
- 5: activityMessageQuery,
305
- };
306
- const query = queryMap[activityType];
307
- let hasMoreActivities = true;
308
- let totalCount = 0;
309
- while (hasMoreActivities) {
310
- const response = yield fetcher(query, {
311
- page: 1,
312
- perPage: 50,
313
- userId: yield Auth.MyUserId(),
314
- });
315
- if ((_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.Page) === null || _b === void 0 ? void 0 : _b.activities) {
316
- let count = 0;
317
- const activities = (_d = (_c = response === null || response === void 0 ? void 0 : response.data) === null || _c === void 0 ? void 0 : _c.Page) === null || _d === void 0 ? void 0 : _d.activities;
318
- if (!activities || activities.length === 0) {
319
- console.log(`\nNo more activities available.`);
320
- hasMoreActivities = false;
321
- }
322
- else {
323
- for (const act of activities) {
324
- // Ensure ID is present to avoid unintended errors
325
- if (act === null || act === void 0 ? void 0 : act.id) {
326
- const deleteResponse = yield fetcher(deleteActivityMutation, {
327
- id: act === null || act === void 0 ? void 0 : act.id,
328
- });
329
- const isDeleted = (_f = (_e = deleteResponse === null || deleteResponse === void 0 ? void 0 : deleteResponse.data) === null || _e === void 0 ? void 0 : _e.DeleteActivity) === null || _f === void 0 ? void 0 : _f.deleted;
330
- count++;
331
- totalCount++;
332
- console.log(`[${count}/${activities.length}/${totalCount}]\t${act === null || act === void 0 ? void 0 : act.id} ${isDeleted ? "✅" : "❌"}`);
333
- // Avoiding rate-limit
334
- yield new Promise((resolve) => setTimeout(resolve, 1100));
335
- }
336
- }
337
- }
265
+ if (!(yield Auth.isLoggedIn())) {
266
+ console.error(`\nPlease log in to delete your activities.`);
267
+ return;
268
+ }
269
+ const { activityType } = yield inquirer.prompt([
270
+ {
271
+ type: "list",
272
+ name: "activityType",
273
+ message: "What type of activity you want to delete?",
274
+ choices: [
275
+ { name: "All Activity", value: 0 },
276
+ { name: "Text Activity", value: 1 },
277
+ { name: "Media List Activity", value: 2 },
278
+ { name: "Anime List Activity", value: 3 },
279
+ { name: "Manga List Activity", value: 4 },
280
+ { name: "Message Activity", value: 5 },
281
+ ],
282
+ },
283
+ ]);
284
+ const queryMap = {
285
+ 0: activityAllQuery,
286
+ 1: activityTextQuery,
287
+ 2: activityMediaList,
288
+ 3: activityAnimeListQuery,
289
+ 4: activityMangaListQuery,
290
+ 5: activityMessageQuery,
291
+ };
292
+ const query = queryMap[activityType];
293
+ let hasMoreActivities = true;
294
+ let totalCount = 0;
295
+ while (hasMoreActivities) {
296
+ const response = yield fetcher(query, {
297
+ page: 1,
298
+ perPage: 50,
299
+ userId: yield Auth.MyUserId(),
300
+ });
301
+ if ((_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.Page) === null || _b === void 0 ? void 0 : _b.activities) {
302
+ let count = 0;
303
+ const activities = (_d = (_c = response === null || response === void 0 ? void 0 : response.data) === null || _c === void 0 ? void 0 : _c.Page) === null || _d === void 0 ? void 0 : _d.activities;
304
+ if (!activities || activities.length === 0) {
305
+ console.log(`\nNo more activities available.`);
306
+ hasMoreActivities = false;
338
307
  }
339
308
  else {
340
- // In case of an unexpected null response, exit the loop
341
- console.log(`\nProbably deleted all the activities of this type.`);
342
- hasMoreActivities = false;
309
+ for (const act of activities) {
310
+ if (act === null || act === void 0 ? void 0 : act.id) {
311
+ const deleteResponse = yield fetcher(deleteActivityMutation, {
312
+ id: act === null || act === void 0 ? void 0 : act.id,
313
+ });
314
+ const isDeleted = (_f = (_e = deleteResponse === null || deleteResponse === void 0 ? void 0 : deleteResponse.data) === null || _e === void 0 ? void 0 : _e.DeleteActivity) === null || _f === void 0 ? void 0 : _f.deleted;
315
+ count++;
316
+ totalCount++;
317
+ console.log(`[${count}/${activities.length}/${totalCount}]\t${act === null || act === void 0 ? void 0 : act.id} ${isDeleted ? "✅" : "❌"}`);
318
+ // Avoiding rate-limit
319
+ yield new Promise((resolve) => setTimeout(resolve, 1100));
320
+ }
321
+ }
343
322
  }
344
323
  }
345
- }
346
- else {
347
- console.error(`\nPlease log in to delete your activities.`);
324
+ else {
325
+ // In case of an unexpected null response, exit the loop
326
+ console.log(`\nProbably deleted all the activities of this type.`);
327
+ hasMoreActivities = false;
328
+ }
348
329
  }
349
330
  }
350
331
  catch (error) {
@@ -355,54 +336,51 @@ Statistics (Manga):
355
336
  static DeleteMyAnimeList() {
356
337
  return __awaiter(this, void 0, void 0, function* () {
357
338
  var _a, _b, _c, _d;
358
- if (yield Auth.isLoggedIn()) {
359
- const userID = yield Auth.MyUserId();
360
- if (userID) {
361
- const response = yield fetcher(currentUserAnimeList, { id: userID });
362
- if (response !== null) {
363
- const lists = (_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.MediaListCollection) === null || _b === void 0 ? void 0 : _b.lists;
364
- if (lists.length > 0) {
365
- const { selectedList } = yield inquirer.prompt([
366
- {
367
- type: "list",
368
- name: "selectedList",
369
- message: "Select an anime list:",
370
- choices: lists.map((list) => list.name),
371
- pageSize: 10,
372
- },
373
- ]);
374
- const selectedEntries = lists.find((list) => list.name === selectedList);
375
- if (selectedEntries) {
376
- console.log(`\nDeleting entries of '${selectedEntries.name}':`);
377
- for (const [, entry] of selectedEntries.entries.entries()) {
378
- if (entry === null || entry === void 0 ? void 0 : entry.id) {
379
- yield Auth.DeleteAnimeById(entry === null || entry === void 0 ? void 0 : entry.id, (_c = entry === null || entry === void 0 ? void 0 : entry.media) === null || _c === void 0 ? void 0 : _c.title);
380
- yield new Promise((resolve) => setTimeout(resolve, 1100));
381
- }
382
- else {
383
- console.log(`No id in entry.`);
384
- console.log(entry);
385
- }
386
- }
339
+ if (!(yield Auth.isLoggedIn())) {
340
+ console.error(`\nPlease log in first to delete your lists.`);
341
+ return;
342
+ }
343
+ if (!(yield Auth.MyUserId())) {
344
+ console.log(`\nFailed getting current user Id.`);
345
+ return;
346
+ }
347
+ const response = yield fetcher(currentUserAnimeList, { id: yield Auth.MyUserId() });
348
+ if (response !== null) {
349
+ const lists = (_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.MediaListCollection) === null || _b === void 0 ? void 0 : _b.lists;
350
+ if (lists.length > 0) {
351
+ const { selectedList } = yield inquirer.prompt([
352
+ {
353
+ type: "list",
354
+ name: "selectedList",
355
+ message: "Select an anime list:",
356
+ choices: lists.map((list) => list.name),
357
+ pageSize: 10,
358
+ },
359
+ ]);
360
+ const selectedEntries = lists.find((list) => list.name === selectedList);
361
+ if (selectedEntries) {
362
+ console.log(`\nDeleting entries of '${selectedEntries.name}':`);
363
+ for (const [, entry] of selectedEntries.entries.entries()) {
364
+ if (entry === null || entry === void 0 ? void 0 : entry.id) {
365
+ yield Auth.DeleteAnimeById(entry === null || entry === void 0 ? void 0 : entry.id, (_c = entry === null || entry === void 0 ? void 0 : entry.media) === null || _c === void 0 ? void 0 : _c.title);
366
+ yield new Promise((resolve) => setTimeout(resolve, 1100));
387
367
  }
388
368
  else {
389
- console.log("No entries found.");
369
+ console.log(`No id in entry.`);
370
+ console.log(entry);
390
371
  }
391
372
  }
392
- else {
393
- console.log(`\nNo anime(s) found in any list.`);
394
- }
395
373
  }
396
374
  else {
397
- console.log(`\nSomething went wrong. ${(_d = response === null || response === void 0 ? void 0 : response.errors[0]) === null || _d === void 0 ? void 0 : _d.message}`);
375
+ console.log("No entries found.");
398
376
  }
399
377
  }
400
378
  else {
401
- console.log(`\nFailed getting current user Id.`);
379
+ console.log(`\nNo anime(s) found in any list.`);
402
380
  }
403
381
  }
404
382
  else {
405
- console.error(`\nPlease log in first to delete your lists.`);
383
+ console.log(`\nSomething went wrong. ${(_d = response === null || response === void 0 ? void 0 : response.errors[0]) === null || _d === void 0 ? void 0 : _d.message}`);
406
384
  }
407
385
  });
408
386
  }
@@ -429,54 +407,50 @@ Statistics (Manga):
429
407
  return __awaiter(this, void 0, void 0, function* () {
430
408
  var _a, _b, _c, _d;
431
409
  try {
432
- if (yield Auth.isLoggedIn()) {
433
- const userID = yield Auth.MyUserId();
434
- if (userID) {
435
- const response = yield fetcher(currentUserMangaList, { id: userID });
436
- if (response === null || response === void 0 ? void 0 : response.data) {
437
- const lists = (_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.MediaListCollection) === null || _b === void 0 ? void 0 : _b.lists;
438
- if (lists.length > 0) {
439
- const { selectedList } = yield inquirer.prompt([
440
- {
441
- type: "list",
442
- name: "selectedList",
443
- message: "Select a manga list:",
444
- choices: lists.map((list) => list.name),
445
- pageSize: 10,
446
- },
447
- ]);
448
- const selectedEntries = lists.find((list) => list.name === selectedList);
449
- if (selectedEntries) {
450
- console.log(`\nDeleting entries of '${selectedEntries.name}':`);
451
- for (const [, entry] of selectedEntries.entries.entries()) {
452
- if (entry === null || entry === void 0 ? void 0 : entry.id) {
453
- yield Auth.DeleteMangaById(entry === null || entry === void 0 ? void 0 : entry.id, (_c = entry === null || entry === void 0 ? void 0 : entry.media) === null || _c === void 0 ? void 0 : _c.title);
454
- yield new Promise((resolve) => setTimeout(resolve, 1100));
455
- }
456
- else {
457
- console.log(`No id in entry.`);
458
- console.log(entry);
459
- }
460
- }
461
- }
462
- else {
463
- console.error("\nNo entries found.");
464
- }
410
+ if (!(yield Auth.isLoggedIn())) {
411
+ console.error(`\nPlease log in first to delete your lists.`);
412
+ return;
413
+ }
414
+ if (!(yield Auth.MyUserId())) {
415
+ console.error(`\nFailed getting current user Id.`);
416
+ return;
417
+ }
418
+ const response = yield fetcher(currentUserMangaList, { id: yield Auth.MyUserId() });
419
+ if (!(response === null || response === void 0 ? void 0 : response.data)) {
420
+ console.error(`\nSomething went wrong. ${(_a = response === null || response === void 0 ? void 0 : response.errors[0]) === null || _a === void 0 ? void 0 : _a.message}`);
421
+ return;
422
+ }
423
+ const lists = (_c = (_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.MediaListCollection) === null || _c === void 0 ? void 0 : _c.lists;
424
+ if (lists.length > 0) {
425
+ const { selectedList } = yield inquirer.prompt([
426
+ {
427
+ type: "list",
428
+ name: "selectedList",
429
+ message: "Select a manga list:",
430
+ choices: lists.map((list) => list.name),
431
+ pageSize: 10,
432
+ },
433
+ ]);
434
+ const selectedEntries = lists.find((list) => list.name === selectedList);
435
+ if (selectedEntries) {
436
+ console.log(`\nDeleting entries of '${selectedEntries.name}':`);
437
+ for (const [, entry] of selectedEntries.entries.entries()) {
438
+ if (entry === null || entry === void 0 ? void 0 : entry.id) {
439
+ yield Auth.DeleteMangaById(entry === null || entry === void 0 ? void 0 : entry.id, (_d = entry === null || entry === void 0 ? void 0 : entry.media) === null || _d === void 0 ? void 0 : _d.title);
440
+ yield new Promise((resolve) => setTimeout(resolve, 1100));
465
441
  }
466
442
  else {
467
- console.error(`\nNo manga(s) found in any list.`);
443
+ console.log(`No id in entry.`);
444
+ console.log(entry);
468
445
  }
469
446
  }
470
- else {
471
- console.error(`\nSomething went wrong. ${(_d = response === null || response === void 0 ? void 0 : response.errors[0]) === null || _d === void 0 ? void 0 : _d.message}`);
472
- }
473
447
  }
474
448
  else {
475
- console.error(`\nFailed getting current user Id.`);
449
+ console.error("\nNo entries found.");
476
450
  }
477
451
  }
478
452
  else {
479
- console.error(`\nPlease log in first to delete your lists.`);
453
+ console.error(`\nNo manga(s) found in any list.`);
480
454
  }
481
455
  }
482
456
  catch (error) {
@@ -505,23 +479,20 @@ Statistics (Manga):
505
479
  }
506
480
  static Write(status) {
507
481
  return __awaiter(this, void 0, void 0, function* () {
508
- var _a;
509
482
  try {
510
483
  if (!(yield Auth.isLoggedIn())) {
511
484
  console.error(`\nPlease login to use this feature.`);
512
485
  return;
513
486
  }
514
- const data = yield fetcher(saveTextActivityMutation, {
515
- status: status +
516
- `<br><br><br><br>*Written using [@irfanshadikrishad/anilist](https://www.npmjs.com/package/@irfanshadikrishad/anilist).*`,
487
+ const { data } = yield fetcher(saveTextActivityMutation, {
488
+ status: status,
517
489
  });
518
490
  if (!data) {
519
491
  console.error(`\nSomething went wrong. ${data}.`);
520
492
  return;
521
493
  }
522
- const savedActivity = (_a = data.data) === null || _a === void 0 ? void 0 : _a.SaveTextActivity;
523
- if (savedActivity === null || savedActivity === void 0 ? void 0 : savedActivity.id) {
524
- console.log(`\n[${savedActivity.id}] status saved successfully!`);
494
+ if (data.SaveTextActivity.id) {
495
+ console.log(`\n[${data.SaveTextActivity.id}] status saved successfully!`);
525
496
  }
526
497
  }
527
498
  catch (error) {
@@ -598,4 +569,115 @@ Statistics (Manga):
598
569
  });
599
570
  }
600
571
  }
601
- export { Auth };
572
+ class Social {
573
+ /**
574
+ * Follow the users that follows you
575
+ */
576
+ static follow() {
577
+ return __awaiter(this, void 0, void 0, function* () {
578
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
579
+ try {
580
+ let pager = 1;
581
+ let hasNextPage = true;
582
+ let allFollowerUsers = [];
583
+ spinner.start("Fetching all the followers...");
584
+ while (hasNextPage) {
585
+ const followerUsers = yield fetcher(userFollowersQuery, {
586
+ userId: yield Auth.MyUserId(),
587
+ page: pager,
588
+ });
589
+ spinner.update(`Fetched page ${pager} of ${(_c = (_b = (_a = followerUsers === null || followerUsers === void 0 ? void 0 : followerUsers.data) === null || _a === void 0 ? void 0 : _a.Page) === null || _b === void 0 ? void 0 : _b.pageInfo) === null || _c === void 0 ? void 0 : _c.lastPage}...`);
590
+ if (!((_f = (_e = (_d = followerUsers === null || followerUsers === void 0 ? void 0 : followerUsers.data) === null || _d === void 0 ? void 0 : _d.Page) === null || _e === void 0 ? void 0 : _e.pageInfo) === null || _f === void 0 ? void 0 : _f.hasNextPage)) {
591
+ hasNextPage = false;
592
+ }
593
+ allFollowerUsers.push(...(((_h = (_g = followerUsers === null || followerUsers === void 0 ? void 0 : followerUsers.data) === null || _g === void 0 ? void 0 : _g.Page) === null || _h === void 0 ? void 0 : _h.followers) || []));
594
+ pager++;
595
+ }
596
+ spinner.stop("Fetched all the followers. Starting follow back.");
597
+ // Filter users that do no follow me
598
+ const notFollowing = allFollowerUsers
599
+ .filter(({ isFollowing }) => !isFollowing)
600
+ .map(({ id, name }) => ({ id: id, name: name }));
601
+ console.log(`\nTotal follower ${allFollowerUsers.length}.\nNot followed back ${notFollowing.length}\n`);
602
+ if (notFollowing.length <= 0) {
603
+ console.log(`Probably followed back all the users.`);
604
+ return;
605
+ }
606
+ // Traverse and follow back
607
+ const maxIdLength = Math.max(...notFollowing.map(({ id }) => String(id).length));
608
+ const maxNameLength = Math.max(...notFollowing.map(({ name }) => name.length));
609
+ for (let nf of notFollowing) {
610
+ try {
611
+ const follow = yield fetcher(toggleFollowMutation, { userId: nf.id });
612
+ console.log(`${String(`[${nf.id}]`).padEnd(maxIdLength)}` +
613
+ `\t${String(`[${(_k = (_j = follow === null || follow === void 0 ? void 0 : follow.data) === null || _j === void 0 ? void 0 : _j.ToggleFollow) === null || _k === void 0 ? void 0 : _k.name}]`).padEnd(maxNameLength)}` +
614
+ `\t${((_m = (_l = follow === null || follow === void 0 ? void 0 : follow.data) === null || _l === void 0 ? void 0 : _l.ToggleFollow) === null || _m === void 0 ? void 0 : _m.id) ? "✅" : "🈵"}`);
615
+ }
616
+ catch (error) {
617
+ console.log(`automate_follow_toggle_follow: ${error.message}`);
618
+ }
619
+ }
620
+ console.log(`\n✅ Followed back ${notFollowing.length} users.`);
621
+ }
622
+ catch (error) {
623
+ console.log(`\nautomate_follow ${error.message}`);
624
+ }
625
+ });
626
+ }
627
+ /**
628
+ * Unfollow the users thats not following you
629
+ */
630
+ static unfollow() {
631
+ return __awaiter(this, void 0, void 0, function* () {
632
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
633
+ try {
634
+ let pager = 1;
635
+ let hasNextPage = true;
636
+ let allFollowingUsers = [];
637
+ spinner.start("Fetching all following users...");
638
+ while (hasNextPage) {
639
+ const followingUsers = yield fetcher(userFollowingQuery, {
640
+ userId: yield Auth.MyUserId(),
641
+ page: pager,
642
+ });
643
+ spinner.update(`Fetched page ${pager} of ${(_c = (_b = (_a = followingUsers === null || followingUsers === void 0 ? void 0 : followingUsers.data) === null || _a === void 0 ? void 0 : _a.Page) === null || _b === void 0 ? void 0 : _b.pageInfo) === null || _c === void 0 ? void 0 : _c.lastPage} ...`);
644
+ if (!((_f = (_e = (_d = followingUsers === null || followingUsers === void 0 ? void 0 : followingUsers.data) === null || _d === void 0 ? void 0 : _d.Page) === null || _e === void 0 ? void 0 : _e.pageInfo) === null || _f === void 0 ? void 0 : _f.hasNextPage)) {
645
+ hasNextPage = false;
646
+ }
647
+ allFollowingUsers.push(...(((_h = (_g = followingUsers === null || followingUsers === void 0 ? void 0 : followingUsers.data) === null || _g === void 0 ? void 0 : _g.Page) === null || _h === void 0 ? void 0 : _h.following) || []));
648
+ pager++;
649
+ }
650
+ spinner.update(`Fetching complete. Total got ${allFollowingUsers.length} users.`);
651
+ // Filter users that do no follow me
652
+ const notFollowingMe = allFollowingUsers
653
+ .filter((user) => !user.isFollower)
654
+ .map((u3r) => ({ id: u3r.id, name: u3r.name }));
655
+ if (notFollowingMe.length <= 0) {
656
+ console.warn(`\nNot following list is empty!`);
657
+ spinner.stop(`No users to unfollow. Aborting process...`);
658
+ return;
659
+ }
660
+ spinner.stop(`Unfollow process activated with ${notFollowingMe.length} users.`);
661
+ let nfmCount = 0;
662
+ console.log(`\n`);
663
+ for (let nfm of notFollowingMe) {
664
+ nfmCount++;
665
+ try {
666
+ const unfollow = yield fetcher(toggleFollowMutation, {
667
+ userId: nfm.id,
668
+ });
669
+ console.log(`[${nfm.id}]\t[${(_k = (_j = unfollow === null || unfollow === void 0 ? void 0 : unfollow.data) === null || _j === void 0 ? void 0 : _j.ToggleFollow) === null || _k === void 0 ? void 0 : _k.name}]\t${((_m = (_l = unfollow === null || unfollow === void 0 ? void 0 : unfollow.data) === null || _l === void 0 ? void 0 : _l.ToggleFollow) === null || _m === void 0 ? void 0 : _m.id) ? "✅" : "🈵"}`);
670
+ }
671
+ catch (error) {
672
+ console.log(`unfollow_toggle_follow. ${error.message}`);
673
+ }
674
+ }
675
+ console.log(`\nTotal Unfollowed: ${nfmCount}`);
676
+ }
677
+ catch (error) {
678
+ console.error(`\nautomate_unfollow: ${error.message}`);
679
+ }
680
+ });
681
+ }
682
+ }
683
+ export { Auth, Social };
@@ -114,7 +114,78 @@ class AniList {
114
114
  static exportAnime() {
115
115
  return __awaiter(this, void 0, void 0, function* () {
116
116
  var _a, _b, _c;
117
- if (yield Auth.isLoggedIn()) {
117
+ if (!(yield Auth.isLoggedIn())) {
118
+ console.error(`\nMust login to use this feature.`);
119
+ return;
120
+ }
121
+ const { exportType } = yield inquirer.prompt([
122
+ {
123
+ type: "list",
124
+ name: "exportType",
125
+ message: "Choose export type:",
126
+ choices: [
127
+ { name: "CSV", value: 1 },
128
+ { name: "JSON", value: 2 },
129
+ { name: "XML (MyAnimeList/AniDB)", value: 3 },
130
+ ],
131
+ pageSize: 10,
132
+ },
133
+ ]);
134
+ const animeList = yield fetcher(currentUserAnimeList, {
135
+ id: yield Auth.MyUserId(),
136
+ });
137
+ if (animeList) {
138
+ const lists = (_c = (_b = (_a = animeList === null || animeList === void 0 ? void 0 : animeList.data) === null || _a === void 0 ? void 0 : _a.MediaListCollection) === null || _b === void 0 ? void 0 : _b.lists) !== null && _c !== void 0 ? _c : [];
139
+ const mediaWithProgress = lists.flatMap((list) => list.entries.map((entry) => {
140
+ var _a, _b, _c, _d, _e;
141
+ return ({
142
+ id: (_a = entry === null || entry === void 0 ? void 0 : entry.media) === null || _a === void 0 ? void 0 : _a.id,
143
+ title: exportType === 1
144
+ ? getTitle((_b = entry === null || entry === void 0 ? void 0 : entry.media) === null || _b === void 0 ? void 0 : _b.title)
145
+ : (_c = entry === null || entry === void 0 ? void 0 : entry.media) === null || _c === void 0 ? void 0 : _c.title,
146
+ episodes: (_d = entry === null || entry === void 0 ? void 0 : entry.media) === null || _d === void 0 ? void 0 : _d.episodes,
147
+ siteUrl: (_e = entry === null || entry === void 0 ? void 0 : entry.media) === null || _e === void 0 ? void 0 : _e.siteUrl,
148
+ progress: entry.progress,
149
+ status: entry === null || entry === void 0 ? void 0 : entry.status,
150
+ hiddenFromStatusLists: entry.hiddenFromStatusLists,
151
+ });
152
+ }));
153
+ switch (exportType) {
154
+ case 1:
155
+ yield saveJSONasCSV(mediaWithProgress, "anime");
156
+ break;
157
+ case 2:
158
+ yield saveJSONasJSON(mediaWithProgress, "anime");
159
+ break;
160
+ case 3:
161
+ yield MyAnimeList.exportAnime();
162
+ break;
163
+ default:
164
+ console.log(`\nInvalid export type. ${exportType}`);
165
+ break;
166
+ }
167
+ }
168
+ else {
169
+ console.error(`\nNo anime(s) found in your lists.`);
170
+ }
171
+ });
172
+ }
173
+ static exportManga() {
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ var _a, _b;
176
+ if (!(yield Auth.isLoggedIn())) {
177
+ console.error(`\nPlease login to use this feature.`);
178
+ return;
179
+ }
180
+ const mangaLists = yield fetcher(currentUserMangaList, {
181
+ id: yield Auth.MyUserId(),
182
+ });
183
+ if (!(mangaLists === null || mangaLists === void 0 ? void 0 : mangaLists.data)) {
184
+ console.error(`\nCould not get manga list.`);
185
+ return;
186
+ }
187
+ const lists = ((_b = (_a = mangaLists === null || mangaLists === void 0 ? void 0 : mangaLists.data) === null || _a === void 0 ? void 0 : _a.MediaListCollection) === null || _b === void 0 ? void 0 : _b.lists) || [];
188
+ if (lists.length > 0) {
118
189
  const { exportType } = yield inquirer.prompt([
119
190
  {
120
191
  type: "list",
@@ -123,116 +194,42 @@ class AniList {
123
194
  choices: [
124
195
  { name: "CSV", value: 1 },
125
196
  { name: "JSON", value: 2 },
126
- { name: "XML (MyAnimeList/AniDB)", value: 3 },
197
+ { name: "XML (MyAnimeList)", value: 3 },
127
198
  ],
128
199
  pageSize: 10,
129
200
  },
130
201
  ]);
131
- const animeList = yield fetcher(currentUserAnimeList, {
132
- id: yield Auth.MyUserId(),
133
- });
134
- if (animeList) {
135
- const lists = (_c = (_b = (_a = animeList === null || animeList === void 0 ? void 0 : animeList.data) === null || _a === void 0 ? void 0 : _a.MediaListCollection) === null || _b === void 0 ? void 0 : _b.lists) !== null && _c !== void 0 ? _c : [];
136
- const mediaWithProgress = lists.flatMap((list) => list.entries.map((entry) => {
137
- var _a, _b, _c, _d, _e;
138
- return ({
139
- id: (_a = entry === null || entry === void 0 ? void 0 : entry.media) === null || _a === void 0 ? void 0 : _a.id,
140
- title: exportType === 1
141
- ? getTitle((_b = entry === null || entry === void 0 ? void 0 : entry.media) === null || _b === void 0 ? void 0 : _b.title)
142
- : (_c = entry === null || entry === void 0 ? void 0 : entry.media) === null || _c === void 0 ? void 0 : _c.title,
143
- episodes: (_d = entry === null || entry === void 0 ? void 0 : entry.media) === null || _d === void 0 ? void 0 : _d.episodes,
144
- siteUrl: (_e = entry === null || entry === void 0 ? void 0 : entry.media) === null || _e === void 0 ? void 0 : _e.siteUrl,
145
- progress: entry.progress,
146
- status: entry === null || entry === void 0 ? void 0 : entry.status,
147
- hiddenFromStatusLists: entry.hiddenFromStatusLists,
148
- });
149
- }));
150
- switch (exportType) {
151
- case 1:
152
- yield saveJSONasCSV(mediaWithProgress, "anime");
153
- break;
154
- case 2:
155
- yield saveJSONasJSON(mediaWithProgress, "anime");
156
- break;
157
- case 3:
158
- yield MyAnimeList.exportAnime();
159
- break;
160
- default:
161
- console.log(`\nInvalid export type. ${exportType}`);
162
- break;
163
- }
164
- }
165
- else {
166
- console.error(`\nNo anime(s) found in your lists.`);
167
- }
168
- }
169
- else {
170
- console.error(`\nMust login to use this feature.`);
171
- }
172
- });
173
- }
174
- static exportManga() {
175
- return __awaiter(this, void 0, void 0, function* () {
176
- var _a, _b;
177
- if (yield Auth.isLoggedIn()) {
178
- const mangaLists = yield fetcher(currentUserMangaList, {
179
- id: yield Auth.MyUserId(),
180
- });
181
- if (mangaLists) {
182
- const lists = ((_b = (_a = mangaLists === null || mangaLists === void 0 ? void 0 : mangaLists.data) === null || _a === void 0 ? void 0 : _a.MediaListCollection) === null || _b === void 0 ? void 0 : _b.lists) || [];
183
- if (lists.length > 0) {
184
- const { exportType } = yield inquirer.prompt([
185
- {
186
- type: "list",
187
- name: "exportType",
188
- message: "Choose export type:",
189
- choices: [
190
- { name: "CSV", value: 1 },
191
- { name: "JSON", value: 2 },
192
- { name: "XML (MyAnimeList)", value: 3 },
193
- ],
194
- pageSize: 10,
195
- },
196
- ]);
197
- const mediaWithProgress = lists.flatMap((list) => list.entries.map((entry) => {
198
- var _a, _b, _c;
199
- return ({
200
- id: (_a = entry === null || entry === void 0 ? void 0 : entry.media) === null || _a === void 0 ? void 0 : _a.id,
201
- title: exportType === 1
202
- ? getTitle((_b = entry === null || entry === void 0 ? void 0 : entry.media) === null || _b === void 0 ? void 0 : _b.title)
203
- : (_c = entry === null || entry === void 0 ? void 0 : entry.media) === null || _c === void 0 ? void 0 : _c.title,
204
- private: entry.private,
205
- chapters: entry.media.chapters,
206
- progress: entry.progress,
207
- status: entry === null || entry === void 0 ? void 0 : entry.status,
208
- hiddenFromStatusLists: entry.hiddenFromStatusLists,
209
- });
210
- }));
211
- switch (exportType) {
212
- case 1:
213
- yield saveJSONasCSV(mediaWithProgress, "manga");
214
- break;
215
- case 2:
216
- yield saveJSONasJSON(mediaWithProgress, "manga");
217
- break;
218
- case 3:
219
- yield MyAnimeList.exportManga();
220
- break;
221
- default:
222
- console.log(`\nInvalid export type. ${exportType}`);
223
- break;
224
- }
225
- }
226
- else {
227
- console.log(`\nList seems to be empty.`);
228
- }
229
- }
230
- else {
231
- console.error(`\nCould not get manga list.`);
202
+ const mediaWithProgress = lists.flatMap((list) => list.entries.map((entry) => {
203
+ var _a, _b, _c;
204
+ return ({
205
+ id: (_a = entry === null || entry === void 0 ? void 0 : entry.media) === null || _a === void 0 ? void 0 : _a.id,
206
+ title: exportType === 1
207
+ ? getTitle((_b = entry === null || entry === void 0 ? void 0 : entry.media) === null || _b === void 0 ? void 0 : _b.title)
208
+ : (_c = entry === null || entry === void 0 ? void 0 : entry.media) === null || _c === void 0 ? void 0 : _c.title,
209
+ private: entry.private,
210
+ chapters: entry.media.chapters,
211
+ progress: entry.progress,
212
+ status: entry === null || entry === void 0 ? void 0 : entry.status,
213
+ hiddenFromStatusLists: entry.hiddenFromStatusLists,
214
+ });
215
+ }));
216
+ switch (exportType) {
217
+ case 1:
218
+ yield saveJSONasCSV(mediaWithProgress, "manga");
219
+ break;
220
+ case 2:
221
+ yield saveJSONasJSON(mediaWithProgress, "manga");
222
+ break;
223
+ case 3:
224
+ yield MyAnimeList.exportManga();
225
+ break;
226
+ default:
227
+ console.log(`\nInvalid export type. ${exportType}`);
228
+ break;
232
229
  }
233
230
  }
234
231
  else {
235
- console.error(`\nPlease login to use this feature.`);
232
+ console.log(`\nList seems to be empty.`);
236
233
  }
237
234
  });
238
235
  }
@@ -243,11 +240,10 @@ class AniList {
243
240
  if (!(yield Auth.isLoggedIn())) {
244
241
  return console.error(`\nPlease log in first to access your lists.`);
245
242
  }
246
- const userId = yield Auth.MyUserId();
247
- if (!userId) {
243
+ if (!(yield Auth.MyUserId())) {
248
244
  return console.log(`\nFailed getting current user Id.`);
249
245
  }
250
- const data = yield fetcher(currentUserAnimeList, { id: userId });
246
+ const data = yield fetcher(currentUserAnimeList, { id: yield Auth.MyUserId() });
251
247
  if (data === null || data === void 0 ? void 0 : data.errors) {
252
248
  return console.log(`\nSomething went wrong. ${(_b = (_a = data === null || data === void 0 ? void 0 : data.errors) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.message}`);
253
249
  }
@@ -19,6 +19,7 @@ declare const activityAllQuery = "query ($userId: Int, $page: Int, $perPage: Int
19
19
  declare const activityMediaList = "query ($userId: Int, $page: Int, $perPage: Int, $type: ActivityType) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total currentPage lastPage hasNextPage perPage }\n activities(userId: $userId, type: $type, sort: ID_DESC) {\n ... on ListActivity { id type status progress media { id title { romaji english native } format } createdAt }\n }\n }\n}";
20
20
  declare const malIdToAnilistAnimeId = "query ($malId: Int) {\n Media(idMal: $malId, type: ANIME) {\n id title { romaji english } } \n}\n";
21
21
  declare const malIdToAnilistMangaId = "query ($malId: Int) {\n Media(idMal: $malId, type: MANGA) {\n id title { romaji english } } \n}\n";
22
- declare const userFollowingQuery = "query ($userId: Int!) {\n Page {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }\n }\n}\n";
23
- declare const userFollowersQuery = "query ($userId: Int!) {\n Page {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }\n }\n}\n";
24
- export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
22
+ declare const userFollowingQuery = "query ($userId: Int!, $page: Int) {\n Page (page: $page) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }\n }\n}\n";
23
+ declare const userFollowersQuery = "query ($userId: Int!, $page: Int) {\n Page (page: $page) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }\n }\n}\n";
24
+ declare const toggleFollowMutation = "mutation ($userId: Int!) {\n ToggleFollow(userId: $userId) { id name isFollower isFollowing }\n}\n";
25
+ export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, toggleFollowMutation, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
@@ -129,18 +129,22 @@ const malIdToAnilistMangaId = `query ($malId: Int) {
129
129
  id title { romaji english } }
130
130
  }
131
131
  `;
132
- const userFollowingQuery = `query ($userId: Int!) {
133
- Page {
132
+ const userFollowingQuery = `query ($userId: Int!, $page: Int) {
133
+ Page (page: $page) {
134
134
  pageInfo { total perPage currentPage lastPage hasNextPage }
135
- following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }
135
+ following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }
136
136
  }
137
137
  }
138
138
  `;
139
- const userFollowersQuery = `query ($userId: Int!) {
140
- Page {
139
+ const userFollowersQuery = `query ($userId: Int!, $page: Int) {
140
+ Page (page: $page) {
141
141
  pageInfo { total perPage currentPage lastPage hasNextPage }
142
- followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }
142
+ followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }
143
143
  }
144
144
  }
145
145
  `;
146
- export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
146
+ const toggleFollowMutation = `mutation ($userId: Int!) {
147
+ ToggleFollow(userId: $userId) { id name isFollower isFollowing }
148
+ }
149
+ `;
150
+ export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, toggleFollowMutation, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
@@ -121,6 +121,29 @@ interface MediaEntry {
121
121
  status: string;
122
122
  hiddenFromStatusLists: boolean;
123
123
  }
124
+ interface SaveTextActivityResponse {
125
+ data?: {
126
+ SaveTextActivity: {
127
+ id: number;
128
+ userId: number;
129
+ text: string;
130
+ createdAt: number;
131
+ };
132
+ };
133
+ errors?: {
134
+ message: string;
135
+ }[];
136
+ }
137
+ interface MediaListCollectionResponse {
138
+ data?: {
139
+ MediaListCollection: {
140
+ lists: MediaList[];
141
+ };
142
+ };
143
+ errors?: {
144
+ message: string;
145
+ }[];
146
+ }
124
147
  interface List {
125
148
  name: string;
126
149
  entries: MediaEntry[];
@@ -197,6 +220,17 @@ interface AnimeDetails {
197
220
  message: string;
198
221
  }[];
199
222
  }
223
+ interface SaveMediaListEntryResponse {
224
+ data?: {
225
+ SaveMediaListEntry: {
226
+ id: number;
227
+ status: string;
228
+ };
229
+ };
230
+ errors?: {
231
+ message: string;
232
+ }[];
233
+ }
200
234
  interface MediaListEntry {
201
235
  id?: number;
202
236
  media: {
@@ -215,14 +249,7 @@ interface MediaListEntry {
215
249
  type UserActivitiesResponse = {
216
250
  data?: {
217
251
  Page: {
218
- activities: {
219
- status: string;
220
- progress: number;
221
- createdAt: number;
222
- media: {
223
- title: MediaTitle;
224
- };
225
- }[];
252
+ activities: Activity[];
226
253
  };
227
254
  };
228
255
  errors?: {
@@ -264,6 +291,17 @@ type UserResponse = {
264
291
  message: string;
265
292
  }[];
266
293
  };
294
+ type User = {
295
+ id: number;
296
+ name: string;
297
+ avatar: {
298
+ large: string;
299
+ medium: string;
300
+ };
301
+ bannerImage: string;
302
+ isFollower: boolean;
303
+ isFollowing: boolean;
304
+ };
267
305
  type UserFollower = {
268
306
  data?: {
269
307
  Page: {
@@ -274,15 +312,7 @@ type UserFollower = {
274
312
  lastPage: number;
275
313
  hasNextPage: boolean;
276
314
  };
277
- followers: {
278
- id: number;
279
- name: string;
280
- avatar: {
281
- large: string;
282
- medium: string;
283
- };
284
- bannerImage: string;
285
- }[];
315
+ followers: User[];
286
316
  };
287
317
  };
288
318
  errors?: {
@@ -299,15 +329,7 @@ type UserFollowing = {
299
329
  lastPage: number;
300
330
  hasNextPage: boolean;
301
331
  };
302
- following: {
303
- id: number;
304
- name: string;
305
- avatar: {
306
- large: string;
307
- medium: string;
308
- };
309
- bannerImage: string;
310
- }[];
332
+ following: User[];
311
333
  };
312
334
  };
313
335
  errors?: {
@@ -331,4 +353,38 @@ type AnimeSearchResponse = {
331
353
  message: string;
332
354
  }[];
333
355
  };
334
- export { AniListMediaStatus, AnimeDetails, AnimeList, AnimeSearchResponse, DateMonthYear, DeleteMangaResponse, List, MALAnimeStatus, MALAnimeXML, MALMangaStatus, MalIdToAnilistIdResponse, MediaEntry, MediaList, MediaListEntry, MediaTitle, MediaWithProgress, Myself, UserActivitiesResponse, UserFollower, UserFollowing, UserResponse, saveAnimeWithProgressResponse, };
356
+ type ToggleFollowResponse = {
357
+ data?: {
358
+ ToggleFollow: {
359
+ id: number;
360
+ name: string;
361
+ isFollower: boolean;
362
+ isFollowing: boolean;
363
+ };
364
+ };
365
+ errors?: {
366
+ message: string;
367
+ }[];
368
+ };
369
+ type DeleteMediaListResponse = {
370
+ data?: {
371
+ DeleteMediaListEntry: {
372
+ deleted: boolean;
373
+ };
374
+ };
375
+ errors?: {
376
+ message: string;
377
+ }[];
378
+ };
379
+ type Activity = {
380
+ id: number;
381
+ type: string;
382
+ status: string;
383
+ progress: number | null;
384
+ media: {
385
+ id?: number;
386
+ title: MediaTitle;
387
+ };
388
+ createdAt: number;
389
+ };
390
+ export { Activity, AniListMediaStatus, AnimeDetails, AnimeList, AnimeSearchResponse, DateMonthYear, DeleteMangaResponse, DeleteMediaListResponse, List, MALAnimeStatus, MALAnimeXML, MALMangaStatus, MalIdToAnilistIdResponse, MediaEntry, MediaList, MediaListCollectionResponse, MediaListEntry, MediaTitle, MediaWithProgress, Myself, SaveMediaListEntryResponse, SaveTextActivityResponse, ToggleFollowResponse, User, UserActivitiesResponse, UserFollower, UserFollowing, UserResponse, saveAnimeWithProgressResponse, };
package/bin/index.js CHANGED
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  import { Command } from "commander";
12
12
  import process from "process";
13
- import { Auth } from "./helpers/auth.js";
13
+ import { Auth, Social } from "./helpers/auth.js";
14
14
  import { AniList } from "./helpers/lists.js";
15
15
  import { getCurrentPackageVersion } from "./helpers/workers.js";
16
16
  const cli = new Command();
@@ -203,4 +203,28 @@ cli
203
203
  }
204
204
  }
205
205
  }));
206
+ cli
207
+ .command("social")
208
+ .alias("sol")
209
+ .description("Automate your process")
210
+ .option("-f, --follow", "Follow the user whos following you.", false)
211
+ .option("-u, --unfollow", "Unfollow the user whos not following you.", false)
212
+ .action((_a) => __awaiter(void 0, [_a], void 0, function* ({ follow, unfollow }) {
213
+ if (!follow && !unfollow) {
214
+ console.error(`\nMust select an option, either --follow or --unfollow`);
215
+ }
216
+ else {
217
+ if (yield Auth.isLoggedIn()) {
218
+ if (follow) {
219
+ yield Social.follow();
220
+ }
221
+ else if (unfollow) {
222
+ yield Social.unfollow();
223
+ }
224
+ }
225
+ else {
226
+ console.error(`\nPlease login to use this feature.`);
227
+ }
228
+ }
229
+ }));
206
230
  cli.parse(process.argv);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@irfanshadikrishad/anilist",
3
3
  "description": "Minimalist unofficial AniList CLI for Anime and Manga Enthusiasts",
4
4
  "author": "Irfan Shadik Rishad",
5
- "version": "1.2.5",
5
+ "version": "1.2.6",
6
6
  "main": "./bin/index.js",
7
7
  "type": "module",
8
8
  "types": "./bin/index.d.ts",
@@ -76,6 +76,7 @@
76
76
  "json2csv": "^6.0.0-alpha.2",
77
77
  "jsonrepair": "^3.11.2",
78
78
  "node-fetch": "^3.3.2",
79
- "open": "^10.1.0"
79
+ "open": "^10.1.0",
80
+ "tiny-spinner": "^2.0.4"
80
81
  }
81
82
  }