@irfanshadikrishad/anilist 1.0.0-forbidden.2 → 1.0.1-forbidden.2
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 +267 -184
- package/bin/helpers/lists.js +103 -107
- package/bin/helpers/queries.d.ts +4 -3
- package/bin/helpers/queries.js +10 -6
- package/bin/helpers/types.d.ts +66 -21
- 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;
|
|
@@ -54,4 +54,14 @@ declare class Auth {
|
|
|
54
54
|
static LikeFollowingActivityV2(perPage: number): Promise<void>;
|
|
55
55
|
static AutoLike(): Promise<void>;
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
declare class Social {
|
|
58
|
+
/**
|
|
59
|
+
* Follow the users that follows you
|
|
60
|
+
*/
|
|
61
|
+
static follow(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Unfollow the users thats not following you
|
|
64
|
+
*/
|
|
65
|
+
static unfollow(): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
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, likeActivityMutation, saveTextActivityMutation, } from "./mutations.js";
|
|
19
|
-
import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, followingActivitiesQuery, globalActivitiesQuery, specificUserActivitiesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, } from "./queries.js";
|
|
20
|
+
import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, followingActivitiesQuery, globalActivitiesQuery, specificUserActivitiesQuery, toggleFollowMutation, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, } from "./queries.js";
|
|
20
21
|
import { activityBy, 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,22 +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, {
|
|
487
|
+
const { data } = yield fetcher(saveTextActivityMutation, {
|
|
515
488
|
status: status,
|
|
516
489
|
});
|
|
517
490
|
if (!data) {
|
|
518
491
|
console.error(`\nSomething went wrong. ${data}.`);
|
|
519
492
|
return;
|
|
520
493
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
console.log(`\n[${savedActivity.id}] status saved successfully!`);
|
|
494
|
+
if (data.SaveTextActivity.id) {
|
|
495
|
+
console.log(`\n[${data.SaveTextActivity.id}] status saved successfully!`);
|
|
524
496
|
}
|
|
525
497
|
}
|
|
526
498
|
catch (error) {
|
|
@@ -880,4 +852,115 @@ Statistics (Manga):
|
|
|
880
852
|
});
|
|
881
853
|
}
|
|
882
854
|
}
|
|
883
|
-
|
|
855
|
+
class Social {
|
|
856
|
+
/**
|
|
857
|
+
* Follow the users that follows you
|
|
858
|
+
*/
|
|
859
|
+
static follow() {
|
|
860
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
861
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
862
|
+
try {
|
|
863
|
+
let pager = 1;
|
|
864
|
+
let hasNextPage = true;
|
|
865
|
+
let allFollowerUsers = [];
|
|
866
|
+
spinner.start("Fetching all the followers...");
|
|
867
|
+
while (hasNextPage) {
|
|
868
|
+
const followerUsers = yield fetcher(userFollowersQuery, {
|
|
869
|
+
userId: yield Auth.MyUserId(),
|
|
870
|
+
page: pager,
|
|
871
|
+
});
|
|
872
|
+
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}...`);
|
|
873
|
+
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)) {
|
|
874
|
+
hasNextPage = false;
|
|
875
|
+
}
|
|
876
|
+
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) || []));
|
|
877
|
+
pager++;
|
|
878
|
+
}
|
|
879
|
+
spinner.stop("Fetched all the followers. Starting follow back.");
|
|
880
|
+
// Filter users that do no follow me
|
|
881
|
+
const notFollowing = allFollowerUsers
|
|
882
|
+
.filter(({ isFollowing }) => !isFollowing)
|
|
883
|
+
.map(({ id, name }) => ({ id: id, name: name }));
|
|
884
|
+
console.log(`\nTotal follower ${allFollowerUsers.length}.\nNot followed back ${notFollowing.length}\n`);
|
|
885
|
+
if (notFollowing.length <= 0) {
|
|
886
|
+
console.log(`Probably followed back all the users.`);
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
// Traverse and follow back
|
|
890
|
+
const maxIdLength = Math.max(...notFollowing.map(({ id }) => String(id).length));
|
|
891
|
+
const maxNameLength = Math.max(...notFollowing.map(({ name }) => name.length));
|
|
892
|
+
for (let nf of notFollowing) {
|
|
893
|
+
try {
|
|
894
|
+
const follow = yield fetcher(toggleFollowMutation, { userId: nf.id });
|
|
895
|
+
console.log(`${String(`[${nf.id}]`).padEnd(maxIdLength)}` +
|
|
896
|
+
`\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)}` +
|
|
897
|
+
`\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) ? "✅" : "🈵"}`);
|
|
898
|
+
}
|
|
899
|
+
catch (error) {
|
|
900
|
+
console.log(`automate_follow_toggle_follow: ${error.message}`);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
console.log(`\n✅ Followed back ${notFollowing.length} users.`);
|
|
904
|
+
}
|
|
905
|
+
catch (error) {
|
|
906
|
+
console.log(`\nautomate_follow ${error.message}`);
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Unfollow the users thats not following you
|
|
912
|
+
*/
|
|
913
|
+
static unfollow() {
|
|
914
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
915
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
916
|
+
try {
|
|
917
|
+
let pager = 1;
|
|
918
|
+
let hasNextPage = true;
|
|
919
|
+
let allFollowingUsers = [];
|
|
920
|
+
spinner.start("Fetching all following users...");
|
|
921
|
+
while (hasNextPage) {
|
|
922
|
+
const followingUsers = yield fetcher(userFollowingQuery, {
|
|
923
|
+
userId: yield Auth.MyUserId(),
|
|
924
|
+
page: pager,
|
|
925
|
+
});
|
|
926
|
+
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} ...`);
|
|
927
|
+
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)) {
|
|
928
|
+
hasNextPage = false;
|
|
929
|
+
}
|
|
930
|
+
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) || []));
|
|
931
|
+
pager++;
|
|
932
|
+
}
|
|
933
|
+
spinner.update(`Fetching complete. Total got ${allFollowingUsers.length} users.`);
|
|
934
|
+
// Filter users that do no follow me
|
|
935
|
+
const notFollowingMe = allFollowingUsers
|
|
936
|
+
.filter((user) => !user.isFollower)
|
|
937
|
+
.map((u3r) => ({ id: u3r.id, name: u3r.name }));
|
|
938
|
+
if (notFollowingMe.length <= 0) {
|
|
939
|
+
console.warn(`\nNot following list is empty!`);
|
|
940
|
+
spinner.stop(`No users to unfollow. Aborting process...`);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
spinner.stop(`Unfollow process activated with ${notFollowingMe.length} users.`);
|
|
944
|
+
let nfmCount = 0;
|
|
945
|
+
console.log(`\n`);
|
|
946
|
+
for (let nfm of notFollowingMe) {
|
|
947
|
+
nfmCount++;
|
|
948
|
+
try {
|
|
949
|
+
const unfollow = yield fetcher(toggleFollowMutation, {
|
|
950
|
+
userId: nfm.id,
|
|
951
|
+
});
|
|
952
|
+
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) ? "✅" : "🈵"}`);
|
|
953
|
+
}
|
|
954
|
+
catch (error) {
|
|
955
|
+
console.log(`unfollow_toggle_follow. ${error.message}`);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
console.log(`\nTotal Unfollowed: ${nfmCount}`);
|
|
959
|
+
}
|
|
960
|
+
catch (error) {
|
|
961
|
+
console.error(`\nautomate_unfollow: ${error.message}`);
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
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
|
@@ -22,6 +22,7 @@ declare const malIdToAnilistMangaId = "query ($malId: Int) {\n Media(idMal: $ma
|
|
|
22
22
|
declare const followingActivitiesQuery = "\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(isFollowing: true, sort: ID_DESC) {\n ... on TextActivity { id type isLiked createdAt user { id name } }\n ... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }\n ... on MessageActivity { id type isLiked message createdAt recipient { id name } }\n }\n }\n}\n";
|
|
23
23
|
declare const globalActivitiesQuery = "\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(sort: ID_DESC) {\n ... on TextActivity { id type isLiked createdAt user { id name } }\n ... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }\n ... on MessageActivity { id type isLiked message createdAt recipient { id name } }\n }\n }\n}\n";
|
|
24
24
|
declare const specificUserActivitiesQuery = "\nquery ($page: Int, $perPage: Int, $userId: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n activities(userId: $userId, sort: ID_DESC) {\n ... on TextActivity { id type isLiked createdAt user { id name } }\n ... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }\n ... on MessageActivity { messenger { name } id type isLiked message createdAt recipient { id name } }\n }\n }\n}\n";
|
|
25
|
-
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 }\n }\n}\n";
|
|
26
|
-
declare const userFollowersQuery = "query ($userId: Int
|
|
27
|
-
|
|
25
|
+
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";
|
|
26
|
+
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";
|
|
27
|
+
declare const toggleFollowMutation = "mutation ($userId: Int!) {\n ToggleFollow(userId: $userId) { id name isFollower isFollowing }\n}\n";
|
|
28
|
+
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, followingActivitiesQuery, globalActivitiesQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, specificUserActivitiesQuery, toggleFollowMutation, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
|
package/bin/helpers/queries.js
CHANGED
|
@@ -159,18 +159,22 @@ query ($page: Int, $perPage: Int, $userId: Int) {
|
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
`;
|
|
162
|
-
const userFollowingQuery = `query ($userId: Int!, $page:Int) {
|
|
162
|
+
const userFollowingQuery = `query ($userId: Int!, $page: Int) {
|
|
163
163
|
Page (page: $page) {
|
|
164
164
|
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
165
|
-
following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }
|
|
165
|
+
following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
`;
|
|
169
|
-
const userFollowersQuery = `query ($userId: Int
|
|
170
|
-
Page {
|
|
169
|
+
const userFollowersQuery = `query ($userId: Int!, $page: Int) {
|
|
170
|
+
Page (page: $page) {
|
|
171
171
|
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
172
|
-
followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage }
|
|
172
|
+
followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
`;
|
|
176
|
-
|
|
176
|
+
const toggleFollowMutation = `mutation ($userId: Int!) {
|
|
177
|
+
ToggleFollow(userId: $userId) { id name isFollower isFollowing }
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
180
|
+
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, followingActivitiesQuery, globalActivitiesQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, specificUserActivitiesQuery, 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: {
|
|
@@ -240,14 +274,7 @@ interface TheActivity {
|
|
|
240
274
|
type UserActivitiesResponse = {
|
|
241
275
|
data?: {
|
|
242
276
|
Page: {
|
|
243
|
-
activities:
|
|
244
|
-
status: string;
|
|
245
|
-
progress: number;
|
|
246
|
-
createdAt: number;
|
|
247
|
-
media: {
|
|
248
|
-
title: MediaTitle;
|
|
249
|
-
};
|
|
250
|
-
}[];
|
|
277
|
+
activities: Activity[];
|
|
251
278
|
};
|
|
252
279
|
};
|
|
253
280
|
errors?: {
|
|
@@ -299,15 +326,7 @@ type UserFollower = {
|
|
|
299
326
|
lastPage: number;
|
|
300
327
|
hasNextPage: boolean;
|
|
301
328
|
};
|
|
302
|
-
followers:
|
|
303
|
-
id: number;
|
|
304
|
-
name: string;
|
|
305
|
-
avatar: {
|
|
306
|
-
large: string;
|
|
307
|
-
medium: string;
|
|
308
|
-
};
|
|
309
|
-
bannerImage: string;
|
|
310
|
-
}[];
|
|
329
|
+
followers: User[];
|
|
311
330
|
};
|
|
312
331
|
};
|
|
313
332
|
errors?: {
|
|
@@ -322,6 +341,8 @@ type User = {
|
|
|
322
341
|
medium: string;
|
|
323
342
|
};
|
|
324
343
|
bannerImage: string;
|
|
344
|
+
isFollower: boolean;
|
|
345
|
+
isFollowing: boolean;
|
|
325
346
|
};
|
|
326
347
|
type UserFollowing = {
|
|
327
348
|
data?: {
|
|
@@ -384,17 +405,30 @@ type SpecificUserActivitiesResponse = {
|
|
|
384
405
|
message: string;
|
|
385
406
|
}[];
|
|
386
407
|
};
|
|
387
|
-
type
|
|
408
|
+
type DeleteActivityResponse = {
|
|
388
409
|
data?: {
|
|
389
|
-
|
|
410
|
+
DeleteMediaListEntry: {
|
|
411
|
+
deleted: boolean;
|
|
412
|
+
};
|
|
413
|
+
};
|
|
414
|
+
errors?: {
|
|
415
|
+
message: string;
|
|
416
|
+
}[];
|
|
417
|
+
};
|
|
418
|
+
type ToggleFollowResponse = {
|
|
419
|
+
data?: {
|
|
420
|
+
ToggleFollow: {
|
|
390
421
|
id: number;
|
|
422
|
+
name: string;
|
|
423
|
+
isFollower: boolean;
|
|
424
|
+
isFollowing: boolean;
|
|
391
425
|
};
|
|
392
426
|
};
|
|
393
427
|
errors?: {
|
|
394
428
|
message: string;
|
|
395
429
|
}[];
|
|
396
430
|
};
|
|
397
|
-
type
|
|
431
|
+
type DeleteMediaListResponse = {
|
|
398
432
|
data?: {
|
|
399
433
|
DeleteMediaListEntry: {
|
|
400
434
|
deleted: boolean;
|
|
@@ -404,4 +438,15 @@ type DeleteActivityResponse = {
|
|
|
404
438
|
message: string;
|
|
405
439
|
}[];
|
|
406
440
|
};
|
|
407
|
-
|
|
441
|
+
type Activity = {
|
|
442
|
+
id: number;
|
|
443
|
+
type: string;
|
|
444
|
+
status: string;
|
|
445
|
+
progress: number | null;
|
|
446
|
+
media: {
|
|
447
|
+
id?: number;
|
|
448
|
+
title: MediaTitle;
|
|
449
|
+
};
|
|
450
|
+
createdAt: number;
|
|
451
|
+
};
|
|
452
|
+
export { Activity, AniListMediaStatus, AnimeDetails, AnimeList, AnimeSearchResponse, DateMonthYear, DeleteActivityResponse, DeleteMangaResponse, DeleteMediaListResponse, LikeActivityResponse, List, MALAnimeStatus, MALAnimeXML, MALMangaStatus, MalIdToAnilistIdResponse, MediaEntry, MediaList, MediaListCollectionResponse, MediaListEntry, MediaTitle, MediaWithProgress, Myself, SaveMediaListEntryResponse, SaveTextActivityResponse, SpecificUserActivitiesResponse, TheActivity, 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();
|
|
@@ -217,4 +217,28 @@ cli
|
|
|
217
217
|
yield Auth.AutoLike();
|
|
218
218
|
}
|
|
219
219
|
}));
|
|
220
|
+
cli
|
|
221
|
+
.command("social")
|
|
222
|
+
.alias("sol")
|
|
223
|
+
.description("Automate your process")
|
|
224
|
+
.option("-f, --follow", "Follow the user whos following you.", false)
|
|
225
|
+
.option("-u, --unfollow", "Unfollow the user whos not following you.", false)
|
|
226
|
+
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ follow, unfollow }) {
|
|
227
|
+
if (!follow && !unfollow) {
|
|
228
|
+
console.error(`\nMust select an option, either --follow or --unfollow`);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
if (yield Auth.isLoggedIn()) {
|
|
232
|
+
if (follow) {
|
|
233
|
+
yield Social.follow();
|
|
234
|
+
}
|
|
235
|
+
else if (unfollow) {
|
|
236
|
+
yield Social.unfollow();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
console.error(`\nPlease login to use this feature.`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}));
|
|
220
244
|
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.0.
|
|
5
|
+
"version": "1.0.1-forbidden.2",
|
|
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
|
}
|