@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 +30 -18
- package/bin/helpers/auth.d.ts +13 -3
- package/bin/helpers/auth.js +268 -186
- package/bin/helpers/lists.js +103 -107
- package/bin/helpers/queries.d.ts +4 -3
- package/bin/helpers/queries.js +11 -7
- package/bin/helpers/types.d.ts +83 -27
- package/bin/index.js +25 -1
- package/package.json +3 -2
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.
|
package/bin/helpers/auth.d.ts
CHANGED
|
@@ -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
|
-
|
|
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 };
|
package/bin/helpers/auth.js
CHANGED
|
@@ -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(`\
|
|
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
|
|
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
|
-
|
|
197
|
-
|
|
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(
|
|
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("\
|
|
227
|
+
console.error("\nFailed to remove the save file during logout:", error.message);
|
|
219
228
|
}
|
|
220
229
|
}
|
|
221
230
|
else {
|
|
222
|
-
console.
|
|
231
|
+
console.warn("\nNo active session found. You may already be logged out.");
|
|
223
232
|
}
|
|
224
233
|
}
|
|
225
234
|
catch (error) {
|
|
226
|
-
console.error(`\
|
|
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
|
|
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
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
347
|
-
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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(
|
|
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(
|
|
375
|
+
console.log("No entries found.");
|
|
398
376
|
}
|
|
399
377
|
}
|
|
400
378
|
else {
|
|
401
|
-
console.log(`\
|
|
379
|
+
console.log(`\nNo anime(s) found in any list.`);
|
|
402
380
|
}
|
|
403
381
|
}
|
|
404
382
|
else {
|
|
405
|
-
console.
|
|
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
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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.
|
|
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(
|
|
449
|
+
console.error("\nNo entries found.");
|
|
476
450
|
}
|
|
477
451
|
}
|
|
478
452
|
else {
|
|
479
|
-
console.error(`\
|
|
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
|
-
|
|
523
|
-
|
|
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
|
-
|
|
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 };
|
package/bin/helpers/lists.js
CHANGED
|
@@ -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
|
|
197
|
+
{ name: "XML (MyAnimeList)", value: 3 },
|
|
127
198
|
],
|
|
128
199
|
pageSize: 10,
|
|
129
200
|
},
|
|
130
201
|
]);
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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
|
}
|
package/bin/helpers/queries.d.ts
CHANGED
|
@@ -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
|
|
23
|
-
declare const userFollowersQuery = "query ($userId: Int
|
|
24
|
-
|
|
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, };
|
package/bin/helpers/queries.js
CHANGED
|
@@ -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
|
-
|
|
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, };
|
package/bin/helpers/types.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
+
"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
|
}
|