From 71c4b16654dff6fe08903d9fbcb9bdc87ed5749c Mon Sep 17 00:00:00 2001 From: LuanRT Date: Mon, 30 May 2022 17:03:38 -0300 Subject: [PATCH] chore: add `notification/record_interactions` endpoint --- lib/core/Actions.js | 4 +- lib/parser/youtube/others/NotificationItem.js | 5 +- lib/proto/index.js | 6 +- lib/proto/messages.js | 425 ++++++++++++++---- lib/proto/youtube.proto | 17 +- 5 files changed, 342 insertions(+), 115 deletions(-) diff --git a/lib/core/Actions.js b/lib/core/Actions.js index 84bcd265..445939f6 100644 --- a/lib/core/Actions.js +++ b/lib/core/Actions.js @@ -303,8 +303,10 @@ class Actions { data.notificationsMenuRequestType = 'NOTIFICATIONS_MENU_REQUEST_TYPE_INBOX'; args.ctoken && (data.ctoken = args.ctoken); break; + case 'record_interactions': + data.serializedRecordNotificationInteractionsRequest = args.params; + break; case 'get_unseen_count': - // doesn't require any parameter break; default: throw new Utils.InnertubeError('Action not implemented', action); diff --git a/lib/parser/youtube/others/NotificationItem.js b/lib/parser/youtube/others/NotificationItem.js index b99ffda1..c88d865e 100644 --- a/lib/parser/youtube/others/NotificationItem.js +++ b/lib/parser/youtube/others/NotificationItem.js @@ -8,15 +8,16 @@ class NotificationItem { static parseItem(item) { if (item.notificationRenderer) { const notification = item.notificationRenderer; + return { title: notification?.shortMessage?.simpleText, sent_time: notification?.sentTimeText?.simpleText, + timestamp: notification.notificationId, channel_name: notification?.contextualMenu?.menuRenderer?.items[1]?.menuServiceItemRenderer?.text?.runs[1]?.text || 'N/A', channel_thumbnail: notification?.thumbnail?.thumbnails[0], video_thumbnail: notification?.videoThumbnail?.thumbnails[0], video_url: notification.navigationEndpoint.watchEndpoint && `https://youtu.be/${notification.navigationEndpoint.watchEndpoint.videoId}` || 'N/A', - read: notification.read, - notification_id: notification.notificationId, + read: notification.read }; } } diff --git a/lib/proto/index.js b/lib/proto/index.js index 0d4ce18f..d51a67a9 100644 --- a/lib/proto/index.js +++ b/lib/proto/index.js @@ -8,7 +8,7 @@ class Proto { * * @param {string} id * @param {number} timestamp - * + * * @returns {string} */ static encodeVisitorData(id, timestamp) { @@ -129,7 +129,7 @@ class Proto { * @returns {string} */ static encodeCommentsSectionParams(video_id, options = {}) { - const sort_menu = { TOP_COMMENTS: 0, NEWEST_FIRST: 1 }; + const sort_options = { TOP_COMMENTS: 0, NEWEST_FIRST: 1 }; const buf = messages.GetCommentsSectionParams.encode({ ctx: { video_id }, @@ -137,7 +137,7 @@ class Proto { params: { opts: { video_id, - sort_by: sort_menu[options.sort_by || 'TOP_COMMENTS'], + sort_by: sort_options[options.sort_by || 'TOP_COMMENTS'], type: options.type || 2 }, target: 'comments-section' diff --git a/lib/proto/messages.js b/lib/proto/messages.js index 3d348f88..01bcd7ff 100644 --- a/lib/proto/messages.js +++ b/lib/proto/messages.js @@ -38,6 +38,13 @@ var SoundInfoParams = exports.SoundInfoParams = { decode: null } +var NotificationInteraction = exports.NotificationInteraction = { + buffer: true, + encodingLength: null, + encode: null, + decode: null +} + var NotificationPreferences = exports.NotificationPreferences = { buffer: true, encodingLength: null, @@ -91,6 +98,7 @@ defineVisitorData() defineChannelAnalytics() defineInnertubePayload() defineSoundInfoParams() +defineNotificationInteraction() defineNotificationPreferences() defineLiveMessageParams() defineGetCommentsSectionParams() @@ -302,15 +310,7 @@ function defineInnertubePayload () { decode: null } - var SoundSearchParams = InnertubePayload.SoundSearchParams = { - buffer: true, - encodingLength: null, - encode: null, - decode: null - } - defineContext() - defineSoundSearchParams() function defineContext () { var Client = Context.Client = { @@ -467,75 +467,6 @@ function defineInnertubePayload () { } } - function defineSoundSearchParams () { - SoundSearchParams.encodingLength = encodingLength - SoundSearchParams.encode = encode - SoundSearchParams.decode = decode - - function encodingLength (obj) { - var length = 0 - if (defined(obj.target_id)) { - var len = encodings.string.encodingLength(obj.target_id) - length += 1 + len - } - if (defined(obj.query)) { - var len = encodings.string.encodingLength(obj.query) - length += 1 + len - } - return length - } - - function encode (obj, buf, offset) { - if (!offset) offset = 0 - if (!buf) buf = Buffer.allocUnsafe(encodingLength(obj)) - var oldOffset = offset - if (defined(obj.target_id)) { - buf[offset++] = 18 - encodings.string.encode(obj.target_id, buf, offset) - offset += encodings.string.encode.bytes - } - if (defined(obj.query)) { - buf[offset++] = 26 - encodings.string.encode(obj.query, buf, offset) - offset += encodings.string.encode.bytes - } - encode.bytes = offset - oldOffset - return buf - } - - function decode (buf, offset, end) { - if (!offset) offset = 0 - if (!end) end = buf.length - if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") - var oldOffset = offset - var obj = { - target_id: "", - query: "" - } - while (true) { - if (end <= offset) { - decode.bytes = offset - oldOffset - return obj - } - var prefix = varint.decode(buf, offset) - offset += varint.decode.bytes - var tag = prefix >> 3 - switch (tag) { - case 2: - obj.target_id = encodings.string.decode(buf, offset) - offset += encodings.string.decode.bytes - break - case 3: - obj.query = encodings.string.decode(buf, offset) - offset += encodings.string.decode.bytes - break - default: - offset = skip(prefix & 7, buf, offset) - } - } - } - } - InnertubePayload.encodingLength = encodingLength InnertubePayload.encode = encode InnertubePayload.decode = decode @@ -551,11 +482,6 @@ function defineInnertubePayload () { var len = encodings.string.encodingLength(obj.target) length += 1 + len } - if (defined(obj.sound_search_params)) { - var len = SoundSearchParams.encodingLength(obj.sound_search_params) - length += varint.encodingLength(len) - length += 2 + len - } return length } @@ -575,14 +501,6 @@ function defineInnertubePayload () { encodings.string.encode(obj.target, buf, offset) offset += encodings.string.encode.bytes } - if (defined(obj.sound_search_params)) { - buf[offset++] = 130 - buf[offset++] = 1 - varint.encode(SoundSearchParams.encodingLength(obj.sound_search_params), buf, offset) - offset += varint.encode.bytes - SoundSearchParams.encode(obj.sound_search_params, buf, offset) - offset += SoundSearchParams.encode.bytes - } encode.bytes = offset - oldOffset return buf } @@ -594,8 +512,7 @@ function defineInnertubePayload () { var oldOffset = offset var obj = { context: null, - target: "", - sound_search_params: null + target: "" } while (true) { if (end <= offset) { @@ -616,12 +533,6 @@ function defineInnertubePayload () { obj.target = encodings.string.decode(buf, offset) offset += encodings.string.decode.bytes break - case 16: - var len = varint.decode(buf, offset) - offset += varint.decode.bytes - obj.sound_search_params = SoundSearchParams.decode(buf, offset, offset + len) - offset += SoundSearchParams.decode.bytes - break default: offset = skip(prefix & 7, buf, offset) } @@ -920,6 +831,324 @@ function defineSoundInfoParams () { } } +function defineNotificationInteraction () { + var Body = NotificationInteraction.Body = { + buffer: true, + encodingLength: null, + encode: null, + decode: null + } + + defineBody() + + function defineBody () { + var Params = Body.Params = { + buffer: true, + encodingLength: null, + encode: null, + decode: null + } + + defineParams() + + function defineParams () { + var Notification = Params.Notification = { + buffer: true, + encodingLength: null, + encode: null, + decode: null + } + + defineNotification() + + function defineNotification () { + Notification.encodingLength = encodingLength + Notification.encode = encode + Notification.decode = decode + + function encodingLength (obj) { + var length = 0 + if (defined(obj.id)) { + var len = encodings.int32.encodingLength(obj.id) + length += 1 + len + } + if (defined(obj.type)) { + var len = encodings.int32.encodingLength(obj.type) + length += 1 + len + } + return length + } + + function encode (obj, buf, offset) { + if (!offset) offset = 0 + if (!buf) buf = Buffer.allocUnsafe(encodingLength(obj)) + var oldOffset = offset + if (defined(obj.id)) { + buf[offset++] = 8 + encodings.int32.encode(obj.id, buf, offset) + offset += encodings.int32.encode.bytes + } + if (defined(obj.type)) { + buf[offset++] = 16 + encodings.int32.encode(obj.type, buf, offset) + offset += encodings.int32.encode.bytes + } + encode.bytes = offset - oldOffset + return buf + } + + function decode (buf, offset, end) { + if (!offset) offset = 0 + if (!end) end = buf.length + if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") + var oldOffset = offset + var obj = { + id: 0, + type: 0 + } + while (true) { + if (end <= offset) { + decode.bytes = offset - oldOffset + return obj + } + var prefix = varint.decode(buf, offset) + offset += varint.decode.bytes + var tag = prefix >> 3 + switch (tag) { + case 1: + obj.id = encodings.int32.decode(buf, offset) + offset += encodings.int32.decode.bytes + break + case 2: + obj.type = encodings.int32.decode(buf, offset) + offset += encodings.int32.decode.bytes + break + default: + offset = skip(prefix & 7, buf, offset) + } + } + } + } + + Params.encodingLength = encodingLength + Params.encode = encode + Params.decode = decode + + function encodingLength (obj) { + var length = 0 + if (defined(obj.notification)) { + var len = Notification.encodingLength(obj.notification) + length += varint.encodingLength(len) + length += 1 + len + } + if (defined(obj.cpn)) { + var len = encodings.string.encodingLength(obj.cpn) + length += 1 + len + } + return length + } + + function encode (obj, buf, offset) { + if (!offset) offset = 0 + if (!buf) buf = Buffer.allocUnsafe(encodingLength(obj)) + var oldOffset = offset + if (defined(obj.notification)) { + buf[offset++] = 10 + varint.encode(Notification.encodingLength(obj.notification), buf, offset) + offset += varint.encode.bytes + Notification.encode(obj.notification, buf, offset) + offset += Notification.encode.bytes + } + if (defined(obj.cpn)) { + buf[offset++] = 18 + encodings.string.encode(obj.cpn, buf, offset) + offset += encodings.string.encode.bytes + } + encode.bytes = offset - oldOffset + return buf + } + + function decode (buf, offset, end) { + if (!offset) offset = 0 + if (!end) end = buf.length + if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") + var oldOffset = offset + var obj = { + notification: null, + cpn: "" + } + while (true) { + if (end <= offset) { + decode.bytes = offset - oldOffset + return obj + } + var prefix = varint.decode(buf, offset) + offset += varint.decode.bytes + var tag = prefix >> 3 + switch (tag) { + case 1: + var len = varint.decode(buf, offset) + offset += varint.decode.bytes + obj.notification = Notification.decode(buf, offset, offset + len) + offset += Notification.decode.bytes + break + case 2: + obj.cpn = encodings.string.decode(buf, offset) + offset += encodings.string.decode.bytes + break + default: + offset = skip(prefix & 7, buf, offset) + } + } + } + } + + Body.encodingLength = encodingLength + Body.encode = encode + Body.decode = decode + + function encodingLength (obj) { + var length = 0 + if (defined(obj.unkparam)) { + var len = encodings.int32.encodingLength(obj.unkparam) + length += 1 + len + } + if (defined(obj.params)) { + var len = Params.encodingLength(obj.params) + length += varint.encodingLength(len) + length += 1 + len + } + return length + } + + function encode (obj, buf, offset) { + if (!offset) offset = 0 + if (!buf) buf = Buffer.allocUnsafe(encodingLength(obj)) + var oldOffset = offset + if (defined(obj.unkparam)) { + buf[offset++] = 8 + encodings.int32.encode(obj.unkparam, buf, offset) + offset += encodings.int32.encode.bytes + } + if (defined(obj.params)) { + buf[offset++] = 114 + varint.encode(Params.encodingLength(obj.params), buf, offset) + offset += varint.encode.bytes + Params.encode(obj.params, buf, offset) + offset += Params.encode.bytes + } + encode.bytes = offset - oldOffset + return buf + } + + function decode (buf, offset, end) { + if (!offset) offset = 0 + if (!end) end = buf.length + if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") + var oldOffset = offset + var obj = { + unkparam: 0, + params: null + } + while (true) { + if (end <= offset) { + decode.bytes = offset - oldOffset + return obj + } + var prefix = varint.decode(buf, offset) + offset += varint.decode.bytes + var tag = prefix >> 3 + switch (tag) { + case 1: + obj.unkparam = encodings.int32.decode(buf, offset) + offset += encodings.int32.decode.bytes + break + case 14: + var len = varint.decode(buf, offset) + offset += varint.decode.bytes + obj.params = Params.decode(buf, offset, offset + len) + offset += Params.decode.bytes + break + default: + offset = skip(prefix & 7, buf, offset) + } + } + } + } + + NotificationInteraction.encodingLength = encodingLength + NotificationInteraction.encode = encode + NotificationInteraction.decode = decode + + function encodingLength (obj) { + var length = 0 + if (defined(obj.body)) { + var len = Body.encodingLength(obj.body) + length += varint.encodingLength(len) + length += 1 + len + } + if (defined(obj.unkparam)) { + var len = encodings.int32.encodingLength(obj.unkparam) + length += 1 + len + } + return length + } + + function encode (obj, buf, offset) { + if (!offset) offset = 0 + if (!buf) buf = Buffer.allocUnsafe(encodingLength(obj)) + var oldOffset = offset + if (defined(obj.body)) { + buf[offset++] = 18 + varint.encode(Body.encodingLength(obj.body), buf, offset) + offset += varint.encode.bytes + Body.encode(obj.body, buf, offset) + offset += Body.encode.bytes + } + if (defined(obj.unkparam)) { + buf[offset++] = 40 + encodings.int32.encode(obj.unkparam, buf, offset) + offset += encodings.int32.encode.bytes + } + encode.bytes = offset - oldOffset + return buf + } + + function decode (buf, offset, end) { + if (!offset) offset = 0 + if (!end) end = buf.length + if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") + var oldOffset = offset + var obj = { + body: null, + unkparam: 0 + } + while (true) { + if (end <= offset) { + decode.bytes = offset - oldOffset + return obj + } + var prefix = varint.decode(buf, offset) + offset += varint.decode.bytes + var tag = prefix >> 3 + switch (tag) { + case 2: + var len = varint.decode(buf, offset) + offset += varint.decode.bytes + obj.body = Body.decode(buf, offset, offset + len) + offset += Body.decode.bytes + break + case 5: + obj.unkparam = encodings.int32.decode(buf, offset) + offset += encodings.int32.decode.bytes + break + default: + offset = skip(prefix & 7, buf, offset) + } + } + } +} + function defineNotificationPreferences () { var Preference = NotificationPreferences.Preference = { buffer: true, diff --git a/lib/proto/youtube.proto b/lib/proto/youtube.proto index d11d7a7a..8b466e63 100644 --- a/lib/proto/youtube.proto +++ b/lib/proto/youtube.proto @@ -7,11 +7,11 @@ message VisitorData { } message ChannelAnalytics { - message Params { - string channel_id = 1001; - } + message Params { + string channel_id = 1001; + } - Params params = 32; + Params params = 32; } message InnertubePayload { @@ -27,13 +27,6 @@ message InnertubePayload { Context context = 1; optional string target = 2; - - message SoundSearchParams { - string target_id = 2; - string query = 3; - } - - optional SoundSearchParams sound_search_params = 16; } message SoundInfoParams { @@ -58,6 +51,7 @@ message NotificationPreferences { message Preference { int32 index = 1; } + Preference pref_id = 2; optional int32 number_0 = 3; @@ -72,6 +66,7 @@ message LiveMessageParams { } Ids ids = 5; } + Params params = 1; optional int32 number_0 = 2;