@mathieuc/tradingview 3.4.0 → 3.4.1

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/.eslintrc.js CHANGED
@@ -20,5 +20,7 @@ module.exports = {
20
20
  devDependencies: ['./test.js', './tests/**'],
21
21
  },
22
22
  ],
23
+ 'no-restricted-syntax': 'off',
24
+ 'no-await-in-loop': 'off',
23
25
  },
24
26
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mathieuc/tradingview",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
5
5
  "main": "main.js",
6
6
  "scripts": {
package/src/client.js CHANGED
@@ -214,6 +214,7 @@ module.exports = class Client {
214
214
  /**
215
215
  * @typedef {Object} ClientOptions
216
216
  * @prop {string} [token] User auth token (in 'sessionid' cookie)
217
+ * @prop {string} [signature] User auth token signature (in 'sessionid_sign' cookie)
217
218
  * @prop {boolean} [DEBUG] Enable debug mode
218
219
  * @prop {'data' | 'prodata' | 'widgetdata'} [server] Server type
219
220
  */
@@ -230,7 +231,10 @@ module.exports = class Client {
230
231
  });
231
232
 
232
233
  if (clientOptions.token) {
233
- misc.getUser(clientOptions.token).then((user) => {
234
+ misc.getUser(
235
+ clientOptions.token,
236
+ clientOptions.signature ? clientOptions.signature : '',
237
+ ).then((user) => {
234
238
  this.#sendQueue.unshift(protocol.formatWSPacket({
235
239
  m: 'set_auth_token',
236
240
  p: [user.authToken],
@@ -375,8 +375,11 @@ module.exports = {
375
375
 
376
376
  if (data.error) throw new Error(data.error);
377
377
 
378
- const cookie = cookies.find((c) => c.includes('sessionid='));
379
- const session = (cookie.match(/sessionid=(.*?);/) ?? [])[1];
378
+ const sessionCookie = cookies.find((c) => c.includes('sessionid='));
379
+ const session = (sessionCookie.match(/sessionid=(.*?);/) ?? [])[1];
380
+
381
+ const signCookie = cookies.find((c) => c.includes('sessionid_sign='));
382
+ const signature = (signCookie.match(/sessionid_sign=(.*?);/) ?? [])[1];
380
383
 
381
384
  return {
382
385
  id: data.user.id,
@@ -388,6 +391,7 @@ module.exports = {
388
391
  followers: data.user.followers,
389
392
  notifications: data.user.notification_count,
390
393
  session,
394
+ signature,
391
395
  sessionHash: data.user.session_hash,
392
396
  privateChannel: data.user.private_channel,
393
397
  authToken: data.user.auth_token,
@@ -399,19 +403,20 @@ module.exports = {
399
403
  * Get user from 'sessionid' cookie
400
404
  * @function getUser
401
405
  * @param {string} session User 'sessionid' cookie
406
+ * @param {string} [signature] User 'sessionid_sign' cookie
402
407
  * @param {string} [location] Auth page location (For france: https://fr.tradingview.com/)
403
408
  * @returns {Promise<User>} Token
404
409
  */
405
- async getUser(session, location = 'https://www.tradingview.com/') {
410
+ async getUser(session, signature = '', location = 'https://www.tradingview.com/') {
406
411
  return new Promise((cb, err) => {
407
412
  https.get(location, {
408
- headers: { cookie: `sessionid=${session}` },
413
+ headers: { cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` },
409
414
  }, (res) => {
410
415
  let rs = '';
411
416
  res.on('data', (d) => { rs += d; });
412
417
  res.on('end', async () => {
413
418
  if (res.headers.location && location !== res.headers.location) {
414
- cb(await module.exports.getUser(session, res.headers.location));
419
+ cb(await module.exports.getUser(session, signature, res.headers.location));
415
420
  return;
416
421
  }
417
422
  if (rs.includes('auth_token')) {
@@ -428,12 +433,13 @@ module.exports = {
428
433
  user: parseFloat(/"notification_count":\{"following":[0-9]*,"user":([0-9]*)/.exec(rs)[1] || 0),
429
434
  },
430
435
  session,
436
+ signature,
431
437
  sessionHash: /"session_hash":"(.*?)"/.exec(rs)[1],
432
438
  privateChannel: /"private_channel":"(.*?)"/.exec(rs)[1],
433
439
  authToken: /"auth_token":"(.*?)"/.exec(rs)[1],
434
440
  joinDate: new Date(/"date_joined":"(.*?)"/.exec(rs)[1] || 0),
435
441
  });
436
- } else err(new Error('Wrong or expired sessionid'));
442
+ } else err(new Error('Wrong or expired sessionid/signature'));
437
443
  });
438
444
 
439
445
  res.on('error', err);
@@ -445,12 +451,13 @@ module.exports = {
445
451
  * Get user's private indicators from a 'sessionid' cookie
446
452
  * @function getPrivateIndicators
447
453
  * @param {string} session User 'sessionid' cookie
454
+ * @param {string} [signature] User 'sessionid_sign' cookie
448
455
  * @returns {Promise<SearchIndicatorResult[]>} Search results
449
456
  */
450
- async getPrivateIndicators(session) {
457
+ async getPrivateIndicators(session, signature = '') {
451
458
  return new Promise((cb, err) => {
452
459
  https.get('https://pine-facade.tradingview.com/pine-facade/list?filter=saved', {
453
- headers: { cookie: `sessionid=${session}` },
460
+ headers: { cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` },
454
461
  }, (res) => {
455
462
  let rs = '';
456
463
  res.on('data', (d) => { rs += d; });
@@ -499,18 +506,19 @@ module.exports = {
499
506
  * Get a chart token from a layout ID and the user credentials if the layout is not public
500
507
  * @function getChartToken
501
508
  * @param {string} layout The layout ID found in the layout URL (Like: 'XXXXXXXX')
502
- * @param {UserCredentials} [credentials] User credentials (id + session)
509
+ * @param {UserCredentials} [credentials] User credentials (id + session + [signature])
503
510
  * @returns {Promise<string>} Token
504
511
  */
505
512
  async getChartToken(layout, credentials = {}) {
506
513
  const creds = credentials.id && credentials.session;
507
514
  const userID = creds ? credentials.id : -1;
508
515
  const session = creds ? credentials.session : null;
516
+ const signature = creds ? credentials.signature : null;
509
517
 
510
518
  const { data } = await request({
511
519
  host: 'www.tradingview.com',
512
520
  path: `/chart-token/?image_url=${layout}&user_id=${userID}`,
513
- headers: { cookie: session ? `sessionid=${session}` : '' },
521
+ headers: { cookie: session ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` : '' },
514
522
  });
515
523
 
516
524
  if (!data.token) throw new Error('Wrong layout or credentials');
@@ -545,7 +553,7 @@ module.exports = {
545
553
  * @function getDrawings
546
554
  * @param {string} layout The layout ID found in the layout URL (Like: 'XXXXXXXX')
547
555
  * @param {string | ''} [symbol] Market filter (Like: 'BINANCE:BTCEUR')
548
- * @param {UserCredentials} [credentials] User credentials (id + session)
556
+ * @param {UserCredentials} [credentials] User credentials (id + session + [signature])
549
557
  * @param {number} [chartID] Chart ID
550
558
  * @returns {Promise<Drawing[]>} Drawings
551
559
  */
@@ -553,12 +561,13 @@ module.exports = {
553
561
  const chartToken = await module.exports.getChartToken(layout, credentials);
554
562
  const creds = credentials.id && credentials.session;
555
563
  const session = creds ? credentials.session : null;
564
+ const signature = creds ? credentials.signature : null;
556
565
 
557
566
  const { data } = await request({
558
567
  host: 'charts-storage.tradingview.com',
559
568
  path: `/charts-storage/layout/${layout}/sources?chart_id=${chartID
560
569
  }&jwt=${chartToken}${symbol ? `&symbol=${symbol}` : ''}`,
561
- headers: { cookie: session ? `sessionid=${session}` : '' },
570
+ headers: { cookie: session ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` : '' },
562
571
  });
563
572
 
564
573
  if (!data.payload) throw new Error('Wrong layout, user credentials, or chart id.');
@@ -135,10 +135,48 @@ module.exports = async (log, success, warn, err, cb) => {
135
135
  next();
136
136
  });
137
137
  },
138
+
139
+ async (next) => { /* Testing "Wrong or expired sessionid/signature" using getUser method */
140
+ log('Testing "Wrong or expired sessionid/signature" error using getUser method:');
141
+
142
+ if (!process.env.SESSION) {
143
+ warn('=> Skipping test because no sessionid/signature was provided');
144
+ next();
145
+ return;
146
+ }
147
+
148
+ try {
149
+ await TradingView.getUser(process.env.SESSION);
150
+ err('=> User found !');
151
+ } catch (error) {
152
+ success('=> User not found:', error);
153
+ next();
154
+ }
155
+ },
156
+
157
+ async (next) => { /* Testing "Wrong or expired sessionid/signature" using client */
158
+ log('Testing "Wrong or expired sessionid/signature" error using client:');
159
+
160
+ if (!process.env.SESSION) {
161
+ warn('=> Skipping test because no sessionid/signature was provided');
162
+ next();
163
+ return;
164
+ }
165
+
166
+ log('Creating a new client...');
167
+ const client2 = new TradingView.Client({
168
+ token: process.env.SESSION,
169
+ });
170
+
171
+ client2.onError((...error) => {
172
+ success('=> Client error:', error);
173
+ client2.end();
174
+ next();
175
+ });
176
+ },
138
177
  ];
139
178
 
140
179
  (async () => {
141
- // eslint-disable-next-line no-restricted-syntax, no-await-in-loop
142
180
  for (const t of tests) await new Promise(t);
143
181
  success(`Crashtests ${tests.length}/${tests.length} done !`);
144
182
  cb();
@@ -1,13 +1,17 @@
1
- /* eslint-disable no-restricted-syntax */
2
- /* eslint-disable no-await-in-loop */
3
1
  const TradingView = require('../main');
4
2
 
5
- const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3
+ const wait = (ms) => new Promise((cb) => { setTimeout(cb, ms); });
6
4
 
7
5
  module.exports = async (log, success, warn, err, cb) => {
6
+ if (!process.env.SESSION || !process.env.SIGNATURE) {
7
+ warn('No sessionid/signature was provided');
8
+ cb();
9
+ return;
10
+ }
11
+
8
12
  log('Getting user info');
9
13
 
10
- const userInfos = await TradingView.getUser(process.env.SESSION);
14
+ const userInfos = await TradingView.getUser(process.env.SESSION, process.env.SIGNATURE);
11
15
  if (userInfos && userInfos.id) {
12
16
  success('User info:', {
13
17
  id: userInfos.id,
@@ -24,8 +28,8 @@ module.exports = async (log, success, warn, err, cb) => {
24
28
  await wait(1000);
25
29
 
26
30
  log('Getting user indicators');
27
-
28
31
  const userIndicators = await TradingView.getPrivateIndicators(process.env.SESSION);
32
+
29
33
  if (userIndicators) {
30
34
  if (userIndicators.length === 0) warn('No private indicator found');
31
35
  else success('User indicators:', userIndicators.map((i) => i.name));
@@ -36,7 +40,9 @@ module.exports = async (log, success, warn, err, cb) => {
36
40
  log('Creating logged client');
37
41
  const client = new TradingView.Client({
38
42
  token: process.env.SESSION,
43
+ signature: process.env.SIGNATURE,
39
44
  });
45
+
40
46
  client.onError((...error) => {
41
47
  err('Client error', error);
42
48
  });
@@ -76,7 +82,6 @@ module.exports = async (log, success, warn, err, cb) => {
76
82
  await wait(1000);
77
83
 
78
84
  log('Loading indicators...');
79
-
80
85
  for (const indic of userIndicators) {
81
86
  const privateIndic = await indic.get();
82
87
  log(`[${indic.name}] Loading indicator...`);
@@ -92,4 +97,5 @@ module.exports = async (log, success, warn, err, cb) => {
92
97
  await check(indic.id);
93
98
  });
94
99
  }
100
+ log('Indicators loaded !');
95
101
  };