feat: add method to change notification preferences and get unseen notifications count

This commit is contained in:
LuanRT
2021-10-13 05:55:02 -03:00
parent 80a9ece314
commit d4dce16be0
4 changed files with 121 additions and 179 deletions

View File

@@ -4,168 +4,89 @@ const Axios = require('axios');
const Utils = require('./Utils');
const Constants = require('./Constants');
async function subscribe(session, video_id, channel_id) {
if (!session.logged_in) throw new Error('You must be logged in to subscribe to a channel');
let data = {
context: session.context,
channelIds: [channel_id]
};
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/subscription/subscribe${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
if (response instanceof Error) {
return {
success: false,
status_code: response.response.status,
message: response.message
};
} else if (response.data.responseContext) {
return {
success: true,
status_code: response.status,
};
async function engage(session, engagement_type, args = {}) {
if (!session.logged_in) throw new Error('You must be signed-in to interact with a video/channel');
let data = {};
switch (engagement_type) {
case 'like/like':
case 'like/dislike':
case 'like/removelike':
data = {
context: session.context,
target: {
videoId: args.video_id
}
};
break;
case 'subscription/subscribe':
case 'subscription/unsubscribe':
data = {
context: session.context,
channelIds: [args.channel_id]
};
break;
case 'comment/create_comment':
data = {
context: session.context,
commentText: args.text,
createCommentParams: Utils.encodeVideoId(args.video_id)
};
break;
default:
}
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/${engagement_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: args.video_id, data })).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
return {
success: true,
status_code: response.status
};
}
async function unsubscribe(session, video_id, channel_id) {
if (!session.logged_in) throw new Error('You must be logged in to unsubscribe from a channel');
let data = {
context: session.context,
channelIds: [channel_id]
};
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/subscription/unsubscribe${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
if (response instanceof Error) {
return {
success: false,
status_code: response.response.status,
message: response.message
};
} else if (response.data.responseContext) {
return {
success: true,
status_code: response.status,
};
}
}
async function likeVideo(session, video_id) {
if (!session.logged_in) throw new Error('You must be logged in to like a video');
let data = {
context: session.context,
target: {
videoId: video_id
}
};
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/like/like${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
if (response instanceof Error) {
return {
success: false,
status_code: response.response.status,
message: response.message
};
} else if (response.data.responseContext) {
return {
success: true,
status_code: response.status,
};
}
}
async function dislikeVideo(session, video_id) {
if (!session.logged_in) throw new Error('You must be logged in to like a video');
let data = {
context: session.context,
target: {
videoId: video_id
}
};
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/like/dislike${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
if (response instanceof Error) {
return {
success: false,
status_code: response.response.status,
message: response.message
};
} else if (response.data.responseContext) {
return {
success: true,
status_code: response.status,
};
}
}
async function removeLike(session, video_id) {
if (!session.logged_in) throw new Error('You must be logged in to remove a like/dislike.');
let data = {
context: session.context,
target: {
videoId: video_id
}
};
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/like/removelike${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
if (response instanceof Error) {
return {
success: false,
status_code: response.response.status,
message: response.message
};
} else if (response.data.responseContext) {
return {
success: true,
status_code: response.status,
};
}
}
async function commentVideo(session, video_id, text) {
if (!text) throw new Error('No text was provided');
if (!session.logged_in) throw new Error('You must be logged in to post a comment.');
let data = {
context: session.context,
commentText: text,
createCommentParams: Utils.encodeId(video_id)
};
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/comment/create_comment${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
if (response instanceof Error) {
return {
success: false,
status_code: response.response.status,
message: response.message
};
} else if (response.data.responseContext) {
return {
success: true,
status_code: response.status,
};
}
}
async function getNotifications(session) {
async function notifications(session, action_type, args = {}) {
if (!session.logged_in) throw new Error('You must be logged in to fetch notifications');
let data = {
context: session.context,
notificationsMenuRequestType: 'NOTIFICATIONS_MENU_REQUEST_TYPE_INBOX'
};
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/notification/get_notification_menu${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, data, desktop: true })).catch((error) => error);
if (response instanceof Error) {
return {
success: false,
status_code: response.response.status,
message: response.message
};
} else {
return {
success: true,
status_code: response.status,
data: response.data
};
let data;
switch (action_type) {
case 'modify_channel_preference':
let pref_types = { ALL: 0, NONE: 1, PERSONALIZED: 2 };
data = {
context: session.context,
params: Utils.encodeChannelId(args.channel_id, pref_types[args.pref.toUpperCase()])
};
break;
case 'get_notification_menu':
data = {
context: session.context,
notificationsMenuRequestType: 'NOTIFICATIONS_MENU_REQUEST_TYPE_INBOX'
};
break;
case 'get_unseen_count':
data = {
context: session.context
};
break;
default:
}
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/notification/${action_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, data, desktop: true })).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
if (action_type === 'modify_channel_preference') return { success: true, status_code: response.status };
return {
success: true,
status_code: response.status,
data: response.data
};
}
module.exports = { subscribe, unsubscribe, likeVideo, dislikeVideo, removeLike, commentVideo, getNotifications };
async function getContinuation(session, continuation_token) {
let data = { context: session.context };
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/next/${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, data, desktop: true })).catch((error) => error);
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
return {
success: true,
status_code: response.status,
data: response.data
};
}
module.exports = { engage, notifications, getContinuation };

View File

@@ -161,15 +161,16 @@ const formatVideoData = (data, context, desktop) => {
video_details.description = data[2].playerResponse.videoDetails.shortDescription;
video_details.thumbnail = data[2].playerResponse.videoDetails.thumbnail.thumbnails.slice(-1)[0];
// actions
video_details.like = like => {};
video_details.dislike = dislike => {};
video_details.removeLike = remove_like => {};
video_details.subscribe = subscribe => {};
video_details.unsubscribe = unsubscribe => {};
video_details.comment = comment => {};
// Actions
video_details.like = () => {};
video_details.dislike = () => {};
video_details.removeLike = () => {};
video_details.subscribe = () => {};
video_details.unsubscribe = () => {};
video_details.comment = () => {};
video_details.setNotificationPref = () => {};
// additional metadata
// Additional metadata
video_details.metadata = metadata;
}
return video_details;

View File

@@ -137,20 +137,22 @@ class Innertube extends EventEmitter {
const data = await this.requestVideoInfo(id, false);
const video_data = Constants.formatVideoData(data, this, false);
video_data.like = like => Actions.likeVideo(this, id);
video_data.dislike = dislike => Actions.dislikeVideo(this, id);
video_data.removeLike = remove_like => Actions.removeLike(this, id);
video_data.subscribe = subscribe => Actions.subscribe(this, id, video_data.metadata.channel_id);
video_data.unsubscribe = unsubscribe => Actions.unsubscribe(this, id, video_data.metadata.channel_id);
video_data.comment = comment => Actions.commentVideo(this, id, comment);
video_data.like = () => Actions.engage(this, 'like/like', { video_id: id });
video_data.dislike = () => Actions.engage(this, 'like/dislike', { video_id: id });
video_data.removeLike = () => Actions.engage(this, 'like/removelike', { video_id: id });
video_data.subscribe = () => Actions.engage(this, 'subscription/subscribe', { video_id: id, channel_id: video_data.metadata.channel_id });
video_data.unsubscribe = () => Actions.engage(this, 'subscription/unsubscribe', { video_id: id, channel_id: video_data.metadata.channel_id });
video_data.comment = text => Actions.engage(this, 'comment/create_comment', { video_id: id, text });
video_data.setNotificationPref = pref => Actions.notifications(this, 'modify_channel_preference', { channel_id: video_data.metadata.channel_id, pref: pref || 'NONE' });
return video_data;
}
async getNotifications() {
const response = await Actions.getNotifications(this);
const contents = response.data.actions[0].openPopupAction.popup.multiPageMenuRenderer.sections[0].multiPageMenuNotificationSectionRenderer.items;
return contents.map((notification) => {
const response = await Actions.notifications(this, 'get_notification_menu');
const contents = response.data.actions[0].openPopupAction.popup.multiPageMenuRenderer.sections[0];
if (!contents.multiPageMenuNotificationSectionRenderer) return { error: 'You don\'t have any notification.' };
return contents.multiPageMenuNotificationSectionRenderer.items.map((notification) => {
if (!notification.notificationRenderer) return;
notification = notification.notificationRenderer;
return {
@@ -159,13 +161,18 @@ class Innertube extends EventEmitter {
channel_name: notification.contextualMenu.menuRenderer.items[1].menuServiceItemRenderer.text.runs[1].text,
channel_thumbnail: notification.thumbnail.thumbnails[0],
video_thumbnail: notification.videoThumbnail.thumbnails[0],
video_url: 'https://youtu.be/' + notification.navigationEndpoint.watchEndpoint.videoId,
video_url: `https://youtu.be/${notification.navigationEndpoint.watchEndpoint.videoId}`,
read: notification.read,
notification_id: notification.notificationId,
};
}).filter((notification_block) => notification_block);
}
async getUnseenNotificationsCount() {
const response = await Actions.notifications(this, 'get_unseen_count');
return response.data.unseenCount;
}
async requestVideoInfo(id, desktop) {
let response;
if (!desktop) {

View File

@@ -39,8 +39,21 @@ function createFunction(input, raw_code) { // I hate this
return new Function(input, raw_code);
}
function encodeId(id) {
return encodeURI(new Buffer.from(` ` + id + `*`).toString('base64').replace('==', '') + 'BQBw==');
function encodeChannelId(id, notification_pref) {
const buff_start = `
`;
const buff_end = [
``, // all
``, // none
``, // personalized
];
let buff = Buffer.from(`${buff_start}${id}${buff_end[notification_pref]}`);
return encodeURIComponent(`${buff.toString('base64')}GAAgBA==`);
}
module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, createFunction, encodeId };
function encodeVideoId(id) {
return encodeURIComponent(`${Buffer.from(` ` + id + `*`).toString('base64').slice(0, -1)}BQBw==`);
}
module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, createFunction, encodeVideoId, encodeChannelId };