Skip to content

Commit

Permalink
Merge pull request #197 from gasparesganga/patch-sessionid-signature
Browse files Browse the repository at this point in the history
Optionally use 'sessionid_sign' cookie
  • Loading branch information
Mathieu2301 authored May 6, 2023
2 parents f5336dd + 4597a22 commit 7aecf88
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 20 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ module.exports = {
devDependencies: ['./test.js', './tests/**'],
},
],
'no-restricted-syntax': 'off',
'no-await-in-loop': 'off',
},
};
6 changes: 5 additions & 1 deletion src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ module.exports = class Client {
/**
* @typedef {Object} ClientOptions
* @prop {string} [token] User auth token (in 'sessionid' cookie)
* @prop {string} [signature] User auth token signature (in 'sessionid_sign' cookie)
* @prop {boolean} [DEBUG] Enable debug mode
* @prop {'data' | 'prodata' | 'widgetdata'} [server] Server type
*/
Expand All @@ -230,7 +231,10 @@ module.exports = class Client {
});

if (clientOptions.token) {
misc.getUser(clientOptions.token).then((user) => {
misc.getUser(
clientOptions.token,
clientOptions.signature ? clientOptions.signature : '',
).then((user) => {
this.#sendQueue.unshift(protocol.formatWSPacket({
m: 'set_auth_token',
p: [user.authToken],
Expand Down
33 changes: 21 additions & 12 deletions src/miscRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,11 @@ module.exports = {

if (data.error) throw new Error(data.error);

const cookie = cookies.find((c) => c.includes('sessionid='));
const session = (cookie.match(/sessionid=(.*?);/) ?? [])[1];
const sessionCookie = cookies.find((c) => c.includes('sessionid='));
const session = (sessionCookie.match(/sessionid=(.*?);/) ?? [])[1];

const signCookie = cookies.find((c) => c.includes('sessionid_sign='));
const signature = (signCookie.match(/sessionid_sign=(.*?);/) ?? [])[1];

return {
id: data.user.id,
Expand All @@ -388,6 +391,7 @@ module.exports = {
followers: data.user.followers,
notifications: data.user.notification_count,
session,
signature,
sessionHash: data.user.session_hash,
privateChannel: data.user.private_channel,
authToken: data.user.auth_token,
Expand All @@ -399,19 +403,20 @@ module.exports = {
* Get user from 'sessionid' cookie
* @function getUser
* @param {string} session User 'sessionid' cookie
* @param {string} [signature] User 'sessionid_sign' cookie
* @param {string} [location] Auth page location (For france: https://fr.tradingview.com/)
* @returns {Promise<User>} Token
*/
async getUser(session, location = 'https://www.tradingview.com/') {
async getUser(session, signature = '', location = 'https://www.tradingview.com/') {
return new Promise((cb, err) => {
https.get(location, {
headers: { cookie: `sessionid=${session}` },
headers: { cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` },
}, (res) => {
let rs = '';
res.on('data', (d) => { rs += d; });
res.on('end', async () => {
if (res.headers.location && location !== res.headers.location) {
cb(await module.exports.getUser(session, res.headers.location));
cb(await module.exports.getUser(session, signature, res.headers.location));
return;
}
if (rs.includes('auth_token')) {
Expand All @@ -428,12 +433,13 @@ module.exports = {
user: parseFloat(/"notification_count":\{"following":[0-9]*,"user":([0-9]*)/.exec(rs)[1] || 0),
},
session,
signature,
sessionHash: /"session_hash":"(.*?)"/.exec(rs)[1],
privateChannel: /"private_channel":"(.*?)"/.exec(rs)[1],
authToken: /"auth_token":"(.*?)"/.exec(rs)[1],
joinDate: new Date(/"date_joined":"(.*?)"/.exec(rs)[1] || 0),
});
} else err(new Error('Wrong or expired sessionid'));
} else err(new Error('Wrong or expired sessionid/signature'));
});

res.on('error', err);
Expand All @@ -445,12 +451,13 @@ module.exports = {
* Get user's private indicators from a 'sessionid' cookie
* @function getPrivateIndicators
* @param {string} session User 'sessionid' cookie
* @param {string} [signature] User 'sessionid_sign' cookie
* @returns {Promise<SearchIndicatorResult[]>} Search results
*/
async getPrivateIndicators(session) {
async getPrivateIndicators(session, signature = '') {
return new Promise((cb, err) => {
https.get('https://pine-facade.tradingview.com/pine-facade/list?filter=saved', {
headers: { cookie: `sessionid=${session}` },
headers: { cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` },
}, (res) => {
let rs = '';
res.on('data', (d) => { rs += d; });
Expand Down Expand Up @@ -499,18 +506,19 @@ module.exports = {
* Get a chart token from a layout ID and the user credentials if the layout is not public
* @function getChartToken
* @param {string} layout The layout ID found in the layout URL (Like: 'XXXXXXXX')
* @param {UserCredentials} [credentials] User credentials (id + session)
* @param {UserCredentials} [credentials] User credentials (id + session + [signature])
* @returns {Promise<string>} Token
*/
async getChartToken(layout, credentials = {}) {
const creds = credentials.id && credentials.session;
const userID = creds ? credentials.id : -1;
const session = creds ? credentials.session : null;
const signature = creds ? credentials.signature : null;

const { data } = await request({
host: 'www.tradingview.com',
path: `/chart-token/?image_url=${layout}&user_id=${userID}`,
headers: { cookie: session ? `sessionid=${session}` : '' },
headers: { cookie: session ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` : '' },
});

if (!data.token) throw new Error('Wrong layout or credentials');
Expand Down Expand Up @@ -545,20 +553,21 @@ module.exports = {
* @function getDrawings
* @param {string} layout The layout ID found in the layout URL (Like: 'XXXXXXXX')
* @param {string | ''} [symbol] Market filter (Like: 'BINANCE:BTCEUR')
* @param {UserCredentials} [credentials] User credentials (id + session)
* @param {UserCredentials} [credentials] User credentials (id + session + [signature])
* @param {number} [chartID] Chart ID
* @returns {Promise<Drawing[]>} Drawings
*/
async getDrawings(layout, symbol = '', credentials = {}, chartID = 1) {
const chartToken = await module.exports.getChartToken(layout, credentials);
const creds = credentials.id && credentials.session;
const session = creds ? credentials.session : null;
const signature = creds ? credentials.signature : null;

const { data } = await request({
host: 'charts-storage.tradingview.com',
path: `/charts-storage/layout/${layout}/sources?chart_id=${chartID
}&jwt=${chartToken}${symbol ? `&symbol=${symbol}` : ''}`,
headers: { cookie: session ? `sessionid=${session}` : '' },
headers: { cookie: session ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` : '' },
});

if (!data.payload) throw new Error('Wrong layout, user credentials, or chart id.');
Expand Down
40 changes: 39 additions & 1 deletion tests/09. AllErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,48 @@ module.exports = async (log, success, warn, err, cb) => {
next();
});
},

async (next) => { /* Testing "Wrong or expired sessionid/signature" using getUser method */
log('Testing "Wrong or expired sessionid/signature" error using getUser method:');

if (!process.env.SESSION) {
warn('=> Skipping test because no sessionid/signature was provided');
next();
return;
}

try {
await TradingView.getUser(process.env.SESSION);
err('=> User found !');
} catch (error) {
success('=> User not found:', error);
next();
}
},

async (next) => { /* Testing "Wrong or expired sessionid/signature" using client */
log('Testing "Wrong or expired sessionid/signature" error using client:');

if (!process.env.SESSION) {
warn('=> Skipping test because no sessionid/signature was provided');
next();
return;
}

log('Creating a new client...');
const client2 = new TradingView.Client({
token: process.env.SESSION,
});

client2.onError((...error) => {
success('=> Client error:', error);
client2.end();
next();
});
},
];

(async () => {
// eslint-disable-next-line no-restricted-syntax, no-await-in-loop
for (const t of tests) await new Promise(t);
success(`Crashtests ${tests.length}/${tests.length} done !`);
cb();
Expand Down
18 changes: 12 additions & 6 deletions tests/10. Authenticated.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-await-in-loop */
const TradingView = require('../main');

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const wait = (ms) => new Promise((cb) => { setTimeout(cb, ms); });

module.exports = async (log, success, warn, err, cb) => {
if (!process.env.SESSION || !process.env.SIGNATURE) {
warn('No sessionid/signature was provided');
cb();
return;
}

log('Getting user info');

const userInfos = await TradingView.getUser(process.env.SESSION);
const userInfos = await TradingView.getUser(process.env.SESSION, process.env.SIGNATURE);
if (userInfos && userInfos.id) {
success('User info:', {
id: userInfos.id,
Expand All @@ -24,8 +28,8 @@ module.exports = async (log, success, warn, err, cb) => {
await wait(1000);

log('Getting user indicators');

const userIndicators = await TradingView.getPrivateIndicators(process.env.SESSION);

if (userIndicators) {
if (userIndicators.length === 0) warn('No private indicator found');
else success('User indicators:', userIndicators.map((i) => i.name));
Expand All @@ -36,7 +40,9 @@ module.exports = async (log, success, warn, err, cb) => {
log('Creating logged client');
const client = new TradingView.Client({
token: process.env.SESSION,
signature: process.env.SIGNATURE,
});

client.onError((...error) => {
err('Client error', error);
});
Expand Down Expand Up @@ -76,7 +82,6 @@ module.exports = async (log, success, warn, err, cb) => {
await wait(1000);

log('Loading indicators...');

for (const indic of userIndicators) {
const privateIndic = await indic.get();
log(`[${indic.name}] Loading indicator...`);
Expand All @@ -92,4 +97,5 @@ module.exports = async (log, success, warn, err, cb) => {
await check(indic.id);
});
}
log('Indicators loaded !');
};

0 comments on commit 7aecf88

Please sign in to comment.