Skip to content

Commit

Permalink
updated core and firstrade to getcontact info
Browse files Browse the repository at this point in the history
  • Loading branch information
LLLLinda committed Feb 28, 2021
1 parent c5b77dd commit 33df5b9
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 60 deletions.
3 changes: 2 additions & 1 deletion bin/firstrade
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ command:
getTradeHistory
getPosition
getSessionTimeLeft
getContact
argument:
--username=username (required)
Expand All @@ -18,7 +19,7 @@ argument:
--cookies=cookies (optional)
`;
const re = /(?:--(\w+)="?(\S+)"?)/;
const command = process.argv.find(x => ["login", "getBalance", "getTradeHistory", "getPosition", "getSessionTimeLeft"].includes(x))
const command = process.argv.find(x => ["login", "getBalance", "getTradeHistory", "getPosition", "getSessionTimeLeft", "getContact"].includes(x))
let argument = readCredential(re);
if (command == null || process.argv.includes('-h') || process.argv.includes('--help')) {
console.log(msg)
Expand Down
61 changes: 51 additions & 10 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
declare class Firstrade {
login(credential: Firstrade.Credential): Promise<Firstrade.Cookie | Firstrade.LoginReason>
getBalance(credential: Firstrade.Credential): Promise<Firstrade.Balance>
getTradeHistory(credential: Firstrade.Credential): Promise<Firstrade.TradeRecord[]>
getPosition(credential: Firstrade.Credential): Promise<Firstrade.Position[]>
getSessionTimeLeft(credential: Firstrade.Credential): Promise<number>
login(credential: Firstrade.LoginContext): Promise<Firstrade.Cookie | Firstrade.LoginReason>
getBalance(credential: Firstrade.LoginContext): Promise<Firstrade.Balance>
getTradeHistory(credential: Firstrade.LoginContext): Promise<Firstrade.TradeRecord[]>
getPosition(credential: Firstrade.LoginContext): Promise<Firstrade.Position[]>
getSessionTimeLeft(credential: Firstrade.LoginContext): Promise<number>
getContact(credential: Firstrade.LoginContext): Promise<Firstrade.Contact>
}
declare namespace Firstrade {
export const firstrade: Firstrade

export type Contact = {
phoneNumbers: PhoneNumber[];
mailing: Mailing;
residential: Mailing;
isForeign: boolean;
bResidentAddress: string;
dob: string;
name: string;
countries: Country[];
isPrimary: boolean;
accountType: string;
}

export type Cookie = {
key: string
value: string
Expand All @@ -17,11 +31,9 @@ declare namespace Firstrade {
quantity: number
price?: number
}
export type Credential = {
username: string;
password: string;
pin: string;
} | { cookies: Cookie[] };

export type LoginContext = Credential | { cookies: Cookie[] };

export type Balance = {
totalValue: number;
buyingpower: number;
Expand Down Expand Up @@ -62,6 +74,35 @@ declare namespace Firstrade {
"Trader data not complete, please try later",
"Invalid session. Please log in again."
}

type Credential = {
username: string;
password: string;
pin: string;
};

type PhoneNumber = {
phoneNumber: string;
phoneNumberType: string;
extension?: any;
};

type Mailing = {
streetAddress: string[];
city: string;
state?: any;
postalCode?: any;
country: string;
alpha2: string;
country_name: string;
};

type Country = {
alpha3: string;
label: string;
name: string;
};

}

export = Firstrade;
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";

const firstrade = require("./lib/firstrade.js")
const firstrade = require("./lib/firstrade")

module.exports = { firstrade };
// Allow use of default import syntax in TypeScript
Expand Down
73 changes: 31 additions & 42 deletions lib/__tests__/firstrade.test.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,54 @@
const firstrade = require("../firstrade.js");

const { firstrade } = require("../../index");
const CUSTOM_TIMEOUT = 30000;
const toTest = [
"getBalance",
"getPosition",
"getTradeHistory",
"getSessionTimeLeft",
"getContact",
];
let credential

beforeAll(() => {
require("dotenv").config();
credential = {
username: process.env.FIRSTRADE_USERNAME,
password: process.env.FIRSTRADE_PASSWORD,
pin: process.env.FIRSTRADE_PIN
}
jest.setTimeout(CUSTOM_TIMEOUT);
});

describe("with previous session", () => {
let session;
let session = [];

beforeAll(async () => {
session = await firstrade.login(credential);
});

test("Should login and return session cookies", async () => {
console.log(session);
});

test("Should return balance using previous session", async () => {
const res = await firstrade.getBalance(session);
console.log(res);
session = await loginTry(session);
});

test("Should return position using previous session", async () => {
const res = await firstrade.getPosition(session);
console.log(res);
test.each(toTest)('Should invoke %s using previous session', async (fn) => {
const res = await firstrade[fn](session);
expect(res).toStrictEqual(expect.anything());
});

test("Should return history using previous session", async () => {
const res = await firstrade.getTradeHistory(session);
console.log(
res);
}, 30000);
test("Should return session time left using previous session", async () => {
const res = await firstrade.getSessionTimeLeft(session);
console.log(res);
});
});

describe("without previous session", () => {
test("Should return balance after login", async () => {
const res = await firstrade.getBalance(credential);
console.log(res);
}, 30000);

test("Should return position after login", async () => {
const res = await firstrade.getPosition(credential);
console.log(res);
}, 30000);

test("Should return history after login", async () => {
const res = await firstrade.getTradeHistory(credential);
console.log(res);
}, 30000);
test.each(toTest)('Should invoke %s after login', async (fn) => {
const res = await firstrade[fn](credential);
expect(res).toStrictEqual(expect.anything());
});

test("Should return session time left after login", async () => {
const res = await firstrade.getSessionTimeLeft(credential);
console.log(res);
}, 30000);
});

async function loginTry(session, limit = 5) {
let sid;
do {
session = await firstrade.login(credential);
sid = (session.find(x => x.key == "SID") || {}).value;
} while (!sid && limit-- > 0);
if (!sid)
throw new Error("Login Failure");
return session;
}
8 changes: 7 additions & 1 deletion lib/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const URL = {
loginApi: "https://invest.firstrade.com/cgi-bin/login",
enterPinApi: "https://invest.firstrade.com/cgi-bin/enter_pin",
getTimeLeftApi: 'https://invest.firstrade.com/scripts/util/get_tm_left.php',
getTotalValueApi: 'https://invest.firstrade.com/scripts/charts/tvs.php'
getTotalValueApi: 'https://invest.firstrade.com/scripts/charts/tvs.php',
getContactApi: 'https://invest.firstrade.com/scripts/profile/contact.php'
};

const DATA = {
Expand All @@ -35,6 +36,7 @@ const DATA = {
}),
/** @param {number} accountID @param {"1w"|"1m"|"3m"|"6m"|"1y"|"2y"} duration */
getTotalValueApi: (accountID, duration) => ({ d: duration, a: accountID }),
getContactApi: () => "req=get_contact"
};

const stringifyCookies = cookies => cookies.map(cookie => `${cookie.key}=${cookie.value};`).join(" ")
Expand All @@ -49,6 +51,10 @@ const HEADERS = {
enterPinApi: () => ({
Referer: URL.enterPinApi,
}),
getContactApi: cookies => ({
'Referer': URL.home,
Cookie: stringifyCookies(cookies)
}),
getTotalValueApi: cookies => ({
'Referer': URL.home,
Cookie: stringifyCookies(cookies)
Expand Down
14 changes: 13 additions & 1 deletion lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ async function getTimeLeft(cookies) {
return axios(config);
}

/** @param {Object[]} cookies @returns {Promise<axios.AxiosInstance<{rc:"OK"|"FAILED", msg, data}>>} **/
async function getContact(cookies) {
let config = {
method: 'post',
url: URL.getContactApi,
withCredentials: true,
data: DATA.getContactApi(),
headers: HEADERS.getContactApi(cookies)
};
return axios(config);
}

/** @param {Object[]} cookies @returns {Promise<axios.AxiosInstance<[number,number][]>>} **/
async function getTotalValue(cookies, accountID, duration) {
const config = {
Expand All @@ -109,4 +121,4 @@ async function getTotalValue(cookies, accountID, duration) {
return axios(config);
}

module.exports = { exchangeCredential, getCurrentXml, renewCookies, getTimeLeft, getTotalValue };
module.exports = { exchangeCredential, getCurrentXml, renewCookies, getTimeLeft, getTotalValue, getContact };
12 changes: 10 additions & 2 deletions lib/firstrade.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';
const { parseMoney } = require('./utils.js');
const { renewCookies, getCurrentXml, getTimeLeft } = require('./core');
const { parseMoney } = require('./utils');
const { renewCookies, getCurrentXml, getTimeLeft, getContact } = require('./core');

class Firstrade {
async login(credential) {
Expand Down Expand Up @@ -62,6 +62,14 @@ class Firstrade {
const res = await getTimeLeft(cookies)
return res.data || 0
}

async getContact(credential) {
let cookies = await renewCookies(credential);
const res = await getContact(cookies)
if (res.data.rc == "FAILED")
throw new Error(res.data.msg)
return res.data.data || {}
}
}

const firstrade = new Firstrade();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firstrade-cli",
"version": "0.6.7",
"version": "0.6.8",
"description": "An unofficial CLI tool for Firstrade. The CLI manages your stocks on Firstrade.",
"homepage": "https://github.com/LLLLinda/firstrade-cli#readme",
"author": {
Expand Down
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ An unofficial CLI tool for Firstrade. The CLI manages your stocks on Firstrade.

[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage percentage][coveralls-image]][coveralls-url]


> DISCLAIMER: We disclaim any and all responsibility for losses incurred through the use of this information. By using this information, you are deemed to have accepted these conditions of use, and you agree NOT to sue us.
>
> CLARIFICATION: The above disclaimer states as plainly as possible that if you decide to make use of any of the information contained within this document that you do so at your own risk.
Expand All @@ -33,6 +32,7 @@ command:
getTradeHistory
getPosition
getSessionTimeLeft
getContact
argument:
Expand All @@ -50,6 +50,7 @@ argument:
* firstrade getTradeHistory
* firstrade getPosition
* firstrade getSessionTimeLeft
* firstrade getContact

## Debugging

Expand Down

0 comments on commit 33df5b9

Please sign in to comment.