Skip to content

Commit fd3d177

Browse files
committed
feat: new API getComment
1 parent 6da05f2 commit fd3d177

File tree

6 files changed

+99
-21
lines changed

6 files changed

+99
-21
lines changed

CHANGELOG.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
## Unreleased
44

5-
- BREAKING: normalize `replacementItem` in `ReplaceChatItemAction`
5+
- New: `getComment` for fetching video comment by id
6+
- BREAKING: normalize `ReplaceChatItemAction.replacementItem` interfaces
67
- New params for `AddBannerAction`: `viewerIsCreator`, `targetId`
7-
- BREAKING: incompatible name changes
8-
- `AddBannerAction.id` is renamed to `AddBannerAction.actionId` as actionId
9-
- `AddBannerAction.id` is now become liveChatId
8+
- BREAKING: incompatible property name changes
9+
- `AddBannerAction.id` which is actually action id has been renamed to `AddBannerAction.actionId`
10+
- `AddBannerAction.id` now refer to chat id
1011
- New API `getComments` for fetching video comments
1112
- Move cli tools (`tools/`) to [`masterchat-cli`](https://github.com/holodata/masterchat-cli)
1213

MANUAL.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ async function main() {
5050
// "private" => No permission (private video)
5151
// "unavailable" => Deleted OR wrong video id
5252
// "unarchived" => Live stream recording is not available
53-
// "denied" => Access denied
53+
// "denied" => Access denied (429)
5454
// "invalid" => Invalid request
5555
});
5656

@@ -59,6 +59,7 @@ async function main() {
5959
console.log("Live stream has ended");
6060
}
6161

62+
// start polling live chat API
6263
mc.listen();
6364
}
6465

@@ -79,8 +80,8 @@ async function main() {
7980
);
8081

8182
mc.on("chats", async (chats) => {
82-
const jsonl = chats.map((chat) => JSON.stringify(chat)).join("\n");
83-
await appendFile("./chats.jsonl", jsonl + "\n");
83+
const jsonl = chats.map((chat) => JSON.stringify(chat)).join("\n") + "\n";
84+
await appendFile("./chats.jsonl", jsonl);
8485

8586
// save checkpoint
8687
await writeFile("./checkpoint", continuation.token);
@@ -92,7 +93,7 @@ async function main() {
9293
main();
9394
```
9495
95-
### Tailor-made moderation bot
96+
### Chat moderation bot
9697
9798
```js
9899
import { Masterchat, stringify } from "masterchat";
@@ -117,7 +118,12 @@ async function main() {
117118
emojiHandler: (emoji) => "",
118119
});
119120

120-
if (isSpam(message)) await mc.remove(action.id);
121+
if (isSpam(message) || /UGLY/.test(message)) {
122+
// delete chat
123+
// if flagged as spam by Spamreaper
124+
// or contains "UGLY"
125+
await mc.remove(action.id);
126+
}
121127
}
122128
});
123129

@@ -127,6 +133,29 @@ async function main() {
127133
main();
128134
```
129135
136+
### Get video comments (≠ live chats)
137+
138+
```js
139+
import { getComments, getComment } from "masterchat";
140+
141+
async function main() {
142+
// Iterate over all comments
143+
let res = getComments("<videoId>", { top: true });
144+
while (true) {
145+
console.log(res.comments);
146+
147+
if (!res.next) break;
148+
res = await res.next();
149+
}
150+
151+
// Get comment by id
152+
const comment = await getComment("<videoId>", "<commentId>");
153+
console.log(comment);
154+
}
155+
156+
main();
157+
```
158+
130159
## Advanced usage
131160
132161
### Faster instantiation

src/comments/index.test.ts

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,53 @@
1-
import { getComments } from ".";
2-
import { csc } from "../protobuf/assembler";
1+
import assert from "assert";
2+
import { getComment, getComments } from ".";
3+
import { YTCommentThreadRenderer } from "..";
34
import { stringify } from "../utils";
45

56
it("can fetch comments", async () => {
67
const videoId = "q5ctC_sWU4g";
7-
const commentId = "UgzNuL5flAW9vygeE9V4AaABAg";
88

9-
const res = await getComments(videoId, {
10-
highlightedCommentId: commentId,
11-
});
9+
const res = await getComments(videoId, { top: true });
1210
const first = res.comments[0];
11+
prettyPrint(first);
12+
});
13+
14+
it("can fetch comment by id", async () => {
15+
const videoId = "q5ctC_sWU4g";
16+
const commentId = "UgzNuL5flAW9vygeE9V4AaABAg";
17+
18+
const comment = await getComment(videoId, commentId);
19+
assert(comment);
20+
21+
prettyPrint(comment);
22+
const fetchedId = comment.comment.commentRenderer.commentId;
23+
24+
expect(fetchedId).toEqual(commentId);
25+
});
26+
27+
it("return undefined if wrong id specified", async () => {
28+
const videoId = "q5ctC_sWU4g";
29+
const commentId = "UgzNuL5flAW9vygeE9V4AaABAgwrong";
30+
31+
const comment = await getComment(videoId, commentId);
32+
expect(comment).toBeUndefined();
33+
});
34+
35+
function prettyPrint(comment: YTCommentThreadRenderer) {
36+
const id = comment.comment.commentRenderer.commentId;
1337
const membership =
14-
first.comment.commentRenderer.sponsorCommentBadge
38+
comment.comment.commentRenderer.sponsorCommentBadge
1539
?.sponsorCommentBadgeRenderer.tooltip;
16-
const authorName = first.comment.commentRenderer.authorText.simpleText;
17-
console.log("Membership status for comment id:", commentId, "is", membership);
18-
});
40+
const authorName = comment.comment.commentRenderer.authorText.simpleText;
41+
const message = stringify(comment.comment.commentRenderer.contentText);
42+
43+
console.log(
44+
"id:",
45+
id,
46+
"membership:",
47+
membership,
48+
"author:",
49+
authorName,
50+
"message:",
51+
message
52+
);
53+
}

src/comments/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { EP_NXT } from "../constants";
22
import {
3+
RenderingPriority,
34
YTCommentThreadRenderer,
45
YTContinuationItem,
56
} from "../interfaces/yt/comments";
@@ -8,6 +9,17 @@ import { withContext, ytFetch } from "../utils";
89

910
// Comment
1011

12+
export async function getComment(videoId: string, commentId: string) {
13+
const comments = await getComments(videoId, {
14+
highlightedCommentId: commentId,
15+
});
16+
const first = comments.comments?.[0];
17+
if (first.renderingPriority !== RenderingPriority.LinkedComment)
18+
return undefined;
19+
20+
return first;
21+
}
22+
1123
export async function getComments(
1224
videoId: string,
1325
continuation: string | CscOptions = {}

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from "./comments";
12
export * from "./errors";
23
export * from "./interfaces";
34
export * from "./masterchat";
@@ -18,4 +19,3 @@ export {
1819
tsToDate,
1920
tsToNumber,
2021
} from "./utils";
21-
export * from "./comments";

src/interfaces/yt/comments.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ export enum VoteStatus {
195195
}
196196

197197
export enum RenderingPriority {
198-
RenderingPriorityUnknown = "RENDERING_PRIORITY_UNKNOWN",
198+
Unknown = "RENDERING_PRIORITY_UNKNOWN",
199+
LinkedComment = "RENDERING_PRIORITY_LINKED_COMMENT",
199200
}
200201

201202
export interface Replies {

0 commit comments

Comments
 (0)