· 5 years ago · Feb 05, 2021, 07:42 PM
1require('./utils/Logger/BetterLogger')
2const mongoose = require('mongoose');
3const Config = require('./config');
4const fetch = require('node-fetch');
5const fse = require('fs-extra');
6const moment = require('moment');
7const WebSocket = require('ws');
8
9let Refresh_Token = require('./oauth_tokens/current.json').refresh_token;
10let Auth = '';
11
12/* I is for rate limiting getNewToken() */
13let i = 0;
14setInterval(() => i = 0, 1000 * 5);
15
16let Socket = null;
17
18
19const ApiRequest = async function (Api, Paths, Query) {
20 if (!Api) return {
21 error: 'Api path cannot be empty'
22 }
23 if (!Paths) Paths = '';
24 if (!Query) Query = '';
25 const BaseURL = 'https://api.tdameritrade.com/v1/';
26 const obj = {
27 method: 'GET',
28 headers: {
29 'Accept': 'application/json',
30 'Content-Type': 'application/json',
31 'Authorization': `Bearer ${Auth}`,
32 'apikey': Auth
33 }
34 }
35 const RequestURL = BaseURL + Api + Paths + Query;
36 const res = await fetch(RequestURL, obj);
37 const _json = await res.json();
38 if (_json.error) {
39 if (IsFatalError(_json.error)) return { error: _json.error }
40 await GetNewToken();
41 return ApiRequest(Api, Paths, Query);
42 }
43 return _json;
44}
45
46const FatalErrors = ['The endpoint doesnt exist.'];
47const IsFatalError = function (ErrorMessage) {
48 return FatalErrors.includes(ErrorMessage);
49}
50
51let Grabbing = false;
52//Grabs a new Acess token using refresh_token https://api.tdameritrade.com/v1/oauth2/token
53const GetNewToken = async function GetNewToken() {
54 if (Grabbing) return;
55 Grabbing = true;
56 i++;
57 if (i > 5) {
58 console.log('GetNewToken called too many times... Exiting...')
59 return process.exit('0')
60 }
61 return new Promise(async resolve => {
62 console.log(`Grabbing new Acess Token`)
63 const res = await fetch("https://api.tdameritrade.com/v1/oauth2/token", {
64 body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(Refresh_Token)}&access_type=offline&code=&client_id=${encodeURIComponent(Config.TDA.ClientID)}&redirect_uri=${encodeURIComponent(Config.TDA.RedirectURI)}`,
65 headers: {
66 'Accept': 'application/json',
67 "Content-Type": "application/x-www-form-urlencoded"
68 },
69 method: "POST"
70 })
71
72 console.log(`Grabbed new Access Token... Chaning to json`);
73 const _json = await res.json();
74 console.log(`Changed access token to json`);
75
76 //Sets Access token to new one
77 Auth = _json.access_token;
78 Refresh_Token = _json.refresh_token;
79 console.log(`Set Auth to [ REDACTED ]`)
80
81 //Saves old refrehs token to a new file & sets new refresh token in /oauth_tokens/current.json
82 fse.renameSync(`${__dirname}/oauth_tokens/current.json`, `${__dirname}/oauth_tokens/current-${new Date().getTime()}.json`)
83 fse.outputFileSync(`${__dirname}/oauth_tokens/current.json`, JSON.stringify(_json));
84 Grabbing = false;
85 resolve()
86 })
87
88};
89
90const SortCredentials = function (userPrincipalsResponse) {
91 const tokenTimeStampAsDateObj = new Date(userPrincipalsResponse.streamerInfo.tokenTimestamp);
92 const tokenTimeStampAsMs = tokenTimeStampAsDateObj.getTime();
93 return {
94 "userid": userPrincipalsResponse.accounts[0].accountId,
95 "token": userPrincipalsResponse.streamerInfo.token,
96 "company": userPrincipalsResponse.accounts[0].company,
97 "segment": userPrincipalsResponse.accounts[0].segment,
98 "cddomain": userPrincipalsResponse.accounts[0].accountCdDomainId,
99 "usergroup": userPrincipalsResponse.streamerInfo.userGroup,
100 "accesslevel": userPrincipalsResponse.streamerInfo.accessLevel,
101 "authorized": "Y",
102 "timestamp": tokenTimeStampAsMs,
103 "appid": userPrincipalsResponse.streamerInfo.appId,
104 "acl": userPrincipalsResponse.streamerInfo.acl
105 }
106};
107const jsonToQuery = function (json) {
108 return Object.keys(json).map(function (key) {
109 return encodeURIComponent(key) + '=' +
110 encodeURIComponent(json[key]);
111 }).join('&');
112};
113//Code entry point
114(async () => {
115
116 await mongoose.connect(Config.mongoose.url, Config.mongoose.options)
117 console.log('Connected to MongoDB');
118 await GetNewToken();
119 await connect();
120 console.log(`END OF MAIN`)
121}).call()
122
123const SubscribeToQuotes = function (userPrincipalsResponse, SYMBOL) {
124 if (!userPrincipalsResponse) return;
125 if (!SYMBOL || !SYMBOL.length || SYMBOL.length > 4) return;
126 const SubscriptionRequest = {
127 "requests": [
128 {
129 "service": "QUOTE",
130 "requestid": "2",
131 "command": "SUBS",
132 "account": userPrincipalsResponse.accounts[0].accountId,
133 "source": userPrincipalsResponse.streamerInfo.appId,
134 "parameters": {
135 "keys": SYMBOL,
136 "fields": "0,1,2,3,4,5,6,7,8"
137 }
138 }
139 ]
140 }
141 Socket.send(JSON.stringify(SubscriptionRequest))
142 console.log('Subscribed to GME');
143}
144
145const Quotes = new Map();
146
147const UpdateQuotes = function (Update) {
148 const Quote = Quotes.get(Update.key)
149 if (!Quote) return Quotes.set(Update.key, Update);
150 Object.keys(Update).forEach(key => {
151 if (key === 'key') return;
152 Quote[key] = Update[key];
153 })
154 Quotes.set(Update.key, Quote);
155 //TradeV2();
156}
157
158
159const connect = async function () {
160 if (Socket) throw new Error('Socket is already defined');
161
162 const ApiResponse = await ApiRequest('userprincipals', null, '?fields=streamerSubscriptionKeys,streamerConnectionInfo');
163 if (ApiResponse.error) return console.log(ApiResponse);
164 //console.log(ApiResponse);
165 const Credentials = SortCredentials(ApiResponse);
166 const WebSocketURL = `wss://${ApiResponse.streamerInfo.streamerSocketUrl}/ws`;
167 console.log(WebSocketURL)
168
169 var LoginRequest = {
170 "requests": [
171 {
172 "service": "ADMIN",
173 "command": "LOGIN",
174 "requestid": 0,
175 "account": ApiResponse.accounts[0].accountId,
176 "source": ApiResponse.streamerInfo.appId,
177 "parameters": {
178 "credential": jsonToQuery(Credentials),
179 "token": ApiResponse.streamerInfo.token,
180 "version": "1.0"
181 }
182 }
183 ]
184 }
185
186
187
188 Socket = new WebSocket(WebSocketURL);
189 Socket.on('open', e => {
190 Socket.send(JSON.stringify(LoginRequest))
191 })
192 Socket.on('close', () => {
193 console.log('Socket closed')
194 })
195 Socket.on('message', evt => {
196 const Response = JSON.parse(evt);
197 if (Response.response && Response.response[0].command === 'LOGIN') {
198 //Logged in
199 return SubscribeToQuotes(ApiResponse, 'TSLA');
200 }
201 if (Response.data) {
202 UpdateQuotes(Response.data[0].content[0])
203 }
204 })
205}
206
207