@irfanshadikrishad/anilist 1.7.0 → 1.8.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.
package/CITATION.cff CHANGED
@@ -1,8 +1,8 @@
1
1
  cff-version: 1.2.0
2
- message: "If you use this software, please cite it as below."
3
- title: "@irfanshadikrishad/anilist"
2
+ message: 'If you use this software, please cite it as below.'
3
+ title: '@irfanshadikrishad/anilist'
4
4
  authors:
5
- - name: "Irfan Shadik Rishad"
6
- orcid: "0009-0001-6745-5291"
7
- date-released: "2024-10-12"
8
- repository-code: "https://github.com/irfanshadikrishad/anilist"
5
+ - name: 'Irfan Shadik Rishad'
6
+ orcid: '0009-0001-6745-5291'
7
+ date-released: '2024-10-12'
8
+ repository-code: 'https://github.com/irfanshadikrishad/anilist'
@@ -1,4 +1,4 @@
1
- import { MediaTitle, User } from "./types.js";
1
+ import { MediaTitle, User } from './types.js';
2
2
  declare class Auth {
3
3
  /**
4
4
  * Get access-token from user
@@ -7,24 +7,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { Cipher } from "@irfanshadikrishad/cipher";
11
- import fs from "fs";
12
- import inquirer from "inquirer";
13
- import fetch from "node-fetch";
14
- import open from "open";
15
- import os from "os";
16
- import path from "path";
17
- import Spinner from "tiny-spinner";
18
- import { fetcher } from "./fetcher.js";
19
- import { AniDB, AniList, MyAnimeList } from "./lists.js";
20
- import { deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, saveTextActivityMutation, toggleFollowMutation, } from "./mutations.js";
21
- import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, } from "./queries.js";
22
- import { responsiveOutput } from "./truncate.js";
23
- import { aniListEndpoint, getTitle, redirectUri, timestampToTimeAgo, } from "./workers.js";
10
+ import { Cipher } from '@irfanshadikrishad/cipher';
11
+ import fs from 'fs';
12
+ import inquirer from 'inquirer';
13
+ import fetch from 'node-fetch';
14
+ import open from 'open';
15
+ import os from 'os';
16
+ import path from 'path';
17
+ import Spinner from 'tiny-spinner';
18
+ import { colorize } from '../lib/colorize.js';
19
+ import { fetcher } from './fetcher.js';
20
+ import { AniDB, AniList, MyAnimeList } from './lists.js';
21
+ import { deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, saveTextActivityMutation, toggleFollowMutation, } from './mutations.js';
22
+ import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, } from './queries.js';
23
+ import { responsiveOutput } from './truncate.js';
24
+ import { aniListEndpoint, getTitle, redirectUri, timestampToTimeAgo, } from './workers.js';
24
25
  const home_dir = os.homedir();
25
- const save_path = path.join(home_dir, ".anilist_tok3n");
26
+ const save_path = path.join(home_dir, '.anilist_tok3n');
26
27
  const spinner = new Spinner();
27
- const vigenere = new Cipher.Vigenere("anilist");
28
+ const vigenere = new Cipher.Vigenere('anilist');
28
29
  class Auth {
29
30
  /**
30
31
  * Get access-token from user
@@ -34,13 +35,13 @@ class Auth {
34
35
  try {
35
36
  const { token } = yield inquirer.prompt([
36
37
  {
37
- type: "password",
38
- name: "token",
39
- message: "Please enter your AniList access token:",
38
+ type: 'password',
39
+ name: 'token',
40
+ message: 'Please enter your AniList access token:',
40
41
  },
41
42
  ]);
42
43
  if (!token) {
43
- console.warn("\nNo token entered. Please try again.");
44
+ console.warn('\nNo token entered. Please try again.');
44
45
  return null;
45
46
  }
46
47
  return token;
@@ -55,10 +56,10 @@ class Auth {
55
56
  return __awaiter(this, void 0, void 0, function* () {
56
57
  try {
57
58
  if (!token) {
58
- console.warn("\nNo token provided. Nothing to store.");
59
+ console.warn('\nNo token provided. Nothing to store.');
59
60
  return;
60
61
  }
61
- fs.writeFileSync(save_path, vigenere.encrypt(token), { encoding: "utf8" });
62
+ fs.writeFileSync(save_path, vigenere.encrypt(token), { encoding: 'utf8' });
62
63
  }
63
64
  catch (error) {
64
65
  console.error(`\nError storing access token: ${error.message}`);
@@ -69,7 +70,7 @@ class Auth {
69
70
  return __awaiter(this, void 0, void 0, function* () {
70
71
  try {
71
72
  if (fs.existsSync(save_path)) {
72
- return vigenere.decrypt(fs.readFileSync(save_path, { encoding: "utf8" }));
73
+ return vigenere.decrypt(fs.readFileSync(save_path, { encoding: 'utf8' }));
73
74
  }
74
75
  else {
75
76
  return null;
@@ -84,18 +85,18 @@ class Auth {
84
85
  static Login(clientId, clientSecret) {
85
86
  return __awaiter(this, void 0, void 0, function* () {
86
87
  try {
87
- console.log("Starting AniList login...");
88
+ console.log('Starting AniList login...');
88
89
  const authUrl = `https://anilist.co/api/v2/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code`;
89
- console.log("Opening browser for AniList login...");
90
+ console.log('Opening browser for AniList login...');
90
91
  open(authUrl);
91
92
  const authCode = yield Auth.GetAccessToken();
92
- const tokenResponse = yield fetch("https://anilist.co/api/v2/oauth/token", {
93
- method: "POST",
93
+ const tokenResponse = yield fetch('https://anilist.co/api/v2/oauth/token', {
94
+ method: 'POST',
94
95
  headers: {
95
- "Content-Type": "application/json",
96
+ 'Content-Type': 'application/json',
96
97
  },
97
98
  body: JSON.stringify({
98
- grant_type: "authorization_code",
99
+ grant_type: 'authorization_code',
99
100
  client_id: String(clientId),
100
101
  client_secret: clientSecret,
101
102
  redirect_uri: redirectUri,
@@ -114,7 +115,7 @@ class Auth {
114
115
  }
115
116
  }
116
117
  else {
117
- console.error("\nFailed to get access token:", token_Data);
118
+ console.error('\nFailed to get access token:', token_Data);
118
119
  }
119
120
  }
120
121
  catch (error) {
@@ -128,11 +129,11 @@ class Auth {
128
129
  try {
129
130
  if (yield Auth.isLoggedIn()) {
130
131
  const headers = {
131
- "Content-Type": "application/json",
132
- "Authorization": `Bearer ${yield Auth.RetriveAccessToken()}`,
132
+ 'Content-Type': 'application/json',
133
+ 'Authorization': `Bearer ${yield Auth.RetriveAccessToken()}`,
133
134
  };
134
135
  const request = yield fetch(aniListEndpoint, {
135
- method: "POST",
136
+ method: 'POST',
136
137
  headers: headers,
137
138
  body: JSON.stringify({ query: currentUserQuery }),
138
139
  });
@@ -185,7 +186,7 @@ Statistics (Manga):
185
186
  console.log(`\nRecent Activities:`);
186
187
  if (activities.length > 0) {
187
188
  activities.map(({ status, progress, media, createdAt }) => {
188
- responsiveOutput(`${timestampToTimeAgo(createdAt)}\t${status} ${progress ? `${progress} of ` : ""}${getTitle(media === null || media === void 0 ? void 0 : media.title)}`);
189
+ responsiveOutput(`${timestampToTimeAgo(createdAt)}\t${status} ${progress ? `${progress} of ` : ''}${getTitle(media === null || media === void 0 ? void 0 : media.title)}`);
189
190
  });
190
191
  }
191
192
  return user;
@@ -227,11 +228,11 @@ Statistics (Manga):
227
228
  console.log(`\nLogout successful. See you soon, ${username}.`);
228
229
  }
229
230
  catch (error) {
230
- console.error("\nFailed to remove the save file during logout:", error.message);
231
+ console.error('\nFailed to remove the save file during logout:', error.message);
231
232
  }
232
233
  }
233
234
  else {
234
- console.warn("\nNo active session found. You may already be logged out.");
235
+ console.warn('\nNo active session found. You may already be logged out.');
235
236
  }
236
237
  }
237
238
  catch (error) {
@@ -271,16 +272,16 @@ Statistics (Manga):
271
272
  }
272
273
  const { activityType } = yield inquirer.prompt([
273
274
  {
274
- type: "list",
275
- name: "activityType",
276
- message: "What type of activity you want to delete?",
275
+ type: 'list',
276
+ name: 'activityType',
277
+ message: 'What type of activity you want to delete?',
277
278
  choices: [
278
- { name: "All Activity", value: 0 },
279
- { name: "Text Activity", value: 1 },
280
- { name: "Media List Activity", value: 2 },
281
- { name: "Anime List Activity", value: 3 },
282
- { name: "Manga List Activity", value: 4 },
283
- { name: "Message Activity", value: 5 },
279
+ { name: 'All Activity', value: 0 },
280
+ { name: 'Text Activity', value: 1 },
281
+ { name: 'Media List Activity', value: 2 },
282
+ { name: 'Anime List Activity', value: 3 },
283
+ { name: 'Manga List Activity', value: 4 },
284
+ { name: 'Message Activity', value: 5 },
284
285
  ],
285
286
  },
286
287
  ]);
@@ -317,7 +318,7 @@ Statistics (Manga):
317
318
  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;
318
319
  count++;
319
320
  totalCount++;
320
- console.log(`[${count}/${activities.length}/${totalCount}]\t${act === null || act === void 0 ? void 0 : act.id} ${isDeleted ? "✅" : "❌"}`);
321
+ console.log(`[${count}/${activities.length}/${totalCount}]\t${act === null || act === void 0 ? void 0 : act.id} ${isDeleted ? '✔' : '✘'}`);
321
322
  // Avoiding rate-limit
322
323
  yield new Promise((resolve) => setTimeout(resolve, 1100));
323
324
  }
@@ -353,9 +354,9 @@ Statistics (Manga):
353
354
  if (lists.length > 0) {
354
355
  const { selectedList } = yield inquirer.prompt([
355
356
  {
356
- type: "list",
357
- name: "selectedList",
358
- message: "Select an anime list:",
357
+ type: 'list',
358
+ name: 'selectedList',
359
+ message: 'Select an anime list:',
359
360
  choices: lists.map((list) => list.name),
360
361
  pageSize: 10,
361
362
  },
@@ -375,7 +376,7 @@ Statistics (Manga):
375
376
  }
376
377
  }
377
378
  else {
378
- console.log("No entries found.");
379
+ console.log('No entries found.');
379
380
  }
380
381
  }
381
382
  else {
@@ -394,7 +395,7 @@ Statistics (Manga):
394
395
  const response = yield fetcher(deleteMediaEntryMutation, { id: id });
395
396
  if (response === null || response === void 0 ? void 0 : response.data) {
396
397
  const deleted = (_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.DeleteMediaListEntry) === null || _b === void 0 ? void 0 : _b.deleted;
397
- console.log(`del ${title ? getTitle(title) : ""} ${deleted ? "✅" : "❌"}`);
398
+ console.log(`del ${title ? getTitle(title) : ''} ${deleted ? '✔' : '✘'}`);
398
399
  }
399
400
  else {
400
401
  console.log(`\nError deleting anime. ${(_c = response === null || response === void 0 ? void 0 : response.errors[0]) === null || _c === void 0 ? void 0 : _c.message}`);
@@ -427,9 +428,9 @@ Statistics (Manga):
427
428
  if (lists.length > 0) {
428
429
  const { selectedList } = yield inquirer.prompt([
429
430
  {
430
- type: "list",
431
- name: "selectedList",
432
- message: "Select a manga list:",
431
+ type: 'list',
432
+ name: 'selectedList',
433
+ message: 'Select a manga list:',
433
434
  choices: lists.map((list) => list.name),
434
435
  pageSize: 10,
435
436
  },
@@ -449,7 +450,7 @@ Statistics (Manga):
449
450
  }
450
451
  }
451
452
  else {
452
- console.error("\nNo entries found.");
453
+ console.error('\nNo entries found.');
453
454
  }
454
455
  }
455
456
  else {
@@ -466,10 +467,10 @@ Statistics (Manga):
466
467
  var _a, _b, _c, _d;
467
468
  try {
468
469
  const response = yield fetcher(deleteMangaEntryMutation, { id });
469
- const statusMessage = title ? getTitle(title) : "";
470
+ const statusMessage = title ? getTitle(title) : '';
470
471
  if (response === null || response === void 0 ? void 0 : response.data) {
471
472
  const deleted = (_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.DeleteMediaListEntry) === null || _b === void 0 ? void 0 : _b.deleted;
472
- console.log(`del ${statusMessage} ${deleted ? "✅" : "❌"}`);
473
+ console.log(`del ${statusMessage} ${deleted ? '✔' : '✘'}`);
473
474
  }
474
475
  else {
475
476
  console.error(`Error deleting manga. ${(_d = (_c = response === null || response === void 0 ? void 0 : response.errors) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.message}`);
@@ -508,13 +509,13 @@ Statistics (Manga):
508
509
  try {
509
510
  const { source } = yield inquirer.prompt([
510
511
  {
511
- type: "list",
512
- name: "source",
513
- message: "Select a source:",
512
+ type: 'list',
513
+ name: 'source',
514
+ message: 'Select a source:',
514
515
  choices: [
515
- { name: "Exported JSON file.", value: 1 },
516
- { name: "MyAnimeList (XML)", value: 2 },
517
- { name: "AniDB (json-large)", value: 3 },
516
+ { name: 'Exported JSON file.', value: 1 },
517
+ { name: 'MyAnimeList (XML)', value: 2 },
518
+ { name: 'AniDB (json-large)', value: 3 },
518
519
  ],
519
520
  pageSize: 10,
520
521
  },
@@ -544,12 +545,12 @@ Statistics (Manga):
544
545
  try {
545
546
  const { source } = yield inquirer.prompt([
546
547
  {
547
- type: "list",
548
- name: "source",
549
- message: "Select a source:",
548
+ type: 'list',
549
+ name: 'source',
550
+ message: 'Select a source:',
550
551
  choices: [
551
- { name: "Exported JSON file.", value: 1 },
552
- { name: "MyAnimeList (XML)", value: 2 },
552
+ { name: 'Exported JSON file.', value: 1 },
553
+ { name: 'MyAnimeList (XML)', value: 2 },
553
554
  ],
554
555
  pageSize: 10,
555
556
  },
@@ -582,9 +583,9 @@ class Social {
582
583
  try {
583
584
  let pager = 1;
584
585
  let hasNextPage = true;
585
- let allFollowerUsers = [];
586
+ const allFollowerUsers = [];
586
587
  let followedBack = 0;
587
- spinner.start("Fetching all the followers...");
588
+ spinner.start('Fetching all the followers...');
588
589
  while (hasNextPage) {
589
590
  const followerUsers = yield fetcher(userFollowersQuery, {
590
591
  userId: yield Auth.MyUserId(),
@@ -597,7 +598,7 @@ class Social {
597
598
  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) || []));
598
599
  pager++;
599
600
  }
600
- spinner.stop("Fetched all the followers. Starting follow back.");
601
+ spinner.stop('Fetched all the followers. Starting follow back.');
601
602
  // Filter users that do no follow me
602
603
  const notFollowing = allFollowerUsers
603
604
  .filter(({ isFollowing }) => !isFollowing)
@@ -610,12 +611,12 @@ class Social {
610
611
  // Traverse and follow back
611
612
  const maxIdLength = Math.max(...notFollowing.map(({ id }) => String(id).length));
612
613
  const maxNameLength = Math.max(...notFollowing.map(({ name }) => name.length));
613
- for (let nf of notFollowing) {
614
+ for (const nf of notFollowing) {
614
615
  try {
615
616
  const follow = yield fetcher(toggleFollowMutation, { userId: nf.id });
616
617
  console.log(`${String(`[${nf.id}]`).padEnd(maxIdLength)}` +
617
618
  `\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)}` +
618
- `\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) ? "✅" : "🈵"}`);
619
+ `\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) ? colorize.Green('✔') : colorize.Red('✘')}`);
619
620
  // Count the followed back users
620
621
  if ((_p = (_o = follow === null || follow === void 0 ? void 0 : follow.data) === null || _o === void 0 ? void 0 : _o.ToggleFollow) === null || _p === void 0 ? void 0 : _p.id) {
621
622
  followedBack++;
@@ -625,7 +626,7 @@ class Social {
625
626
  console.log(`automate_follow_toggle_follow: ${error.message}`);
626
627
  }
627
628
  }
628
- console.log(`\n Followed back ${followedBack} users.`);
629
+ console.log(`\n${colorize.Green('✔')} Followed back ${followedBack} users.`);
629
630
  }
630
631
  catch (error) {
631
632
  console.log(`\nautomate_follow ${error.message}`);
@@ -641,9 +642,9 @@ class Social {
641
642
  try {
642
643
  let pager = 1;
643
644
  let hasNextPage = true;
644
- let allFollowingUsers = [];
645
+ const allFollowingUsers = [];
645
646
  let unfollowedUsers = 0;
646
- spinner.start("Fetching all following users...");
647
+ spinner.start('Fetching all following users...');
647
648
  while (hasNextPage) {
648
649
  const followingUsers = yield fetcher(userFollowingQuery, {
649
650
  userId: yield Auth.MyUserId(),
@@ -656,7 +657,7 @@ class Social {
656
657
  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) || []));
657
658
  pager++;
658
659
  }
659
- spinner.update(`Fetching complete. Total got ${allFollowingUsers.length} users.`);
660
+ spinner.update(`${colorize.Green('✔')} Fetching complete. Total got ${allFollowingUsers.length} users.`);
660
661
  // Filter users that do no follow me
661
662
  const notFollowingMe = allFollowingUsers
662
663
  .filter((user) => !user.isFollower)
@@ -668,13 +669,13 @@ class Social {
668
669
  spinner.stop(`Unfollow process activated with ${notFollowingMe.length} users.`);
669
670
  let nfmCount = 0;
670
671
  console.log(`\n`);
671
- for (let nfm of notFollowingMe) {
672
+ for (const nfm of notFollowingMe) {
672
673
  nfmCount++;
673
674
  try {
674
675
  const unfollow = yield fetcher(toggleFollowMutation, {
675
676
  userId: nfm.id,
676
677
  });
677
- 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) ? "✅" : "🈵"}`);
678
+ 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) ? colorize.Green('✔') : colorize.Red('✘')}`);
678
679
  // Count the unfollowed users
679
680
  if ((_p = (_o = unfollow === null || unfollow === void 0 ? void 0 : unfollow.data) === null || _o === void 0 ? void 0 : _o.ToggleFollow) === null || _p === void 0 ? void 0 : _p.id) {
680
681
  unfollowedUsers++;
@@ -7,9 +7,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import fetch from "node-fetch";
11
- import { Auth } from "./auth.js";
12
- import { aniListEndpoint, handleRateLimitRetry } from "./workers.js";
10
+ import fetch from 'node-fetch';
11
+ import { Auth } from './auth.js';
12
+ import { aniListEndpoint, handleRateLimitRetry } from './workers.js';
13
13
  /**
14
14
  * Sends a GraphQL request to the AniList API.
15
15
  *
@@ -26,15 +26,15 @@ function fetcher(query, variables) {
26
26
  var _a, _b;
27
27
  try {
28
28
  const headers = {
29
- "content-type": "application/json",
29
+ 'content-type': 'application/json',
30
30
  };
31
31
  const token = (yield Auth.isLoggedIn())
32
32
  ? yield Auth.RetriveAccessToken()
33
33
  : null;
34
34
  if (token)
35
- headers["Authorization"] = `Bearer ${token}`;
35
+ headers['Authorization'] = `Bearer ${token}`;
36
36
  const request = yield fetch(aniListEndpoint, {
37
- method: "POST",
37
+ method: 'POST',
38
38
  headers: headers,
39
39
  body: JSON.stringify({ query, variables }),
40
40
  });
@@ -47,7 +47,7 @@ function fetcher(query, variables) {
47
47
  return yield fetcher(query, variables);
48
48
  }
49
49
  else {
50
- console.error(`\n${request.status} ${((_b = (_a = response === null || response === void 0 ? void 0 : response.errors) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.message) || "Unknown error"}.`);
50
+ console.error(`\n${request.status} ${((_b = (_a = response === null || response === void 0 ? void 0 : response.errors) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.message) || 'Unknown error'}.`);
51
51
  return null;
52
52
  }
53
53
  }