srtrain
Version:
Node.js SRT train unofficial SDK
188 lines (187 loc) • 7.62 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SRT = void 0;
const axios_1 = __importDefault(require("axios"));
const moment_1 = __importDefault(require("moment"));
const axios_cookiejar_support_1 = require("axios-cookiejar-support");
const lodash_1 = __importDefault(require("lodash"));
const random_useragent_1 = require("random-useragent");
const tough_cookie_1 = require("tough-cookie");
const errorCode_1 = require("./constants/errorCode");
const loginMethod_1 = require("./constants/loginMethod");
const station_1 = require("./constants/station");
const error_1 = require("./error");
const reservation_1 = require("./reservation");
const station_2 = require("./station");
const train_1 = require("./train");
class SRT {
isLoggined = false;
axios;
userId;
password;
baseURL = 'https://app.srail.or.kr';
userAgent = this.generateUserAgent();
cookieJar = new tough_cookie_1.CookieJar();
constructor(options) {
if (options) {
const { baseURL, userAgent } = options;
if (baseURL)
this.baseURL = baseURL;
if (userAgent)
this.userAgent = userAgent;
}
this.axios = this.initAxios();
}
initAxios() {
this.axios = (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({
jar: this.cookieJar,
baseURL: this.baseURL,
headers: {
'User-Agent': this.userAgent,
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
},
}));
this.axios.interceptors.response.use(async (res) => {
if (lodash_1.default.get(res.data, 'resultMap[0].msgCd') !== 'S111')
return res;
const { userId, password } = this;
if (!userId || !password || res.retry) {
throw new error_1.SRTError(errorCode_1.SRTErrorCode.LOGIN_REQUIRED, '로그인 후 사용하십시요.');
}
res.retry = true;
this.isLoggined = false;
await this.login({ userId, password });
return this.axios(res.config);
});
return this.axios;
}
async login(options) {
const { userId, password, accessToken } = options;
this.userId = userId;
this.password = password;
if (accessToken) {
this.isLoggined = true;
const json = JSON.parse(accessToken);
this.cookieJar = await tough_cookie_1.CookieJar.deserialize(json);
this.initAxios();
return;
}
const method = this.detectUserMethod(userId);
const referer = options.referer || 'https://app.srail.or.kr/main/main.do';
const { data } = await this.axios.post('/apb/selectListApb01080_n.do', {
'auto': 'Y',
'check': 'Y',
'page': 'menu',
'deviceKey': '-',
'customerYn': '',
'login_referer': referer,
'srchDvCd': method,
'srchDvNm': userId.replace(/-/g, ''),
'hmpgPwdCphd': password,
});
const message = data.MSG || '';
if (message.includes('존재하지않는 회원입니다')) {
throw new error_1.SRTError(errorCode_1.SRTErrorCode.USER_NOT_FOUND, message);
}
if (message.includes('비밀번호 오류')) {
throw new error_1.SRTError(errorCode_1.SRTErrorCode.PASSWORD_INCORRECT, message);
}
this.isLoggined = true;
}
getStation(name) {
const code = station_1.SRTStationCode[name];
if (!code) {
throw new error_1.SRTError(errorCode_1.SRTErrorCode.STATION_NOT_FOUND, '해당 역을 찾을 수 없습니다.');
}
return new station_2.SRTStation(code);
}
async find(options) {
const { departureStation, arrivalStation, date, all } = options;
const actualDate = (date || (0, moment_1.default)()).format('YYYYMMDD');
const actualTime = date?.format('HHmmss') || '000000';
const { data } = await this.axios.post('/ara/selectListAra10007_n.do', {
'chtnDvCd': '1',
'arriveTime': 'N',
'seatAttCd': '015',
'psgNum': 1,
'trnGpCd': 109,
'stlbTrnClsfCd': '05',
'dptDt': actualDate,
'dptTm': actualTime,
'arvRsStnCd': arrivalStation.code,
'dptRsStnCd': departureStation.code,
});
if (!all)
this.parseResponse(data);
const message = data.resultMap[0]?.msgTxt;
if (message && message === '조회 결과가 없습니다.')
return [];
const output = data.outDataSets.dsOutput1;
const trains = output.map((train) => new train_1.SRTTrain(this, train));
if (!all)
return trains;
const lastDepartureDate = trains[trains.length - 1].departureDate;
const nextDepartureDate = lastDepartureDate.clone().add(1, 'seconds');
const nextTrains = await this.find({
departureStation,
arrivalStation,
date: nextDepartureDate,
all: true,
});
trains.push(...nextTrains);
return trains;
}
async getReservationById(reservationId) {
const reservations = await this.getReservations();
return reservations.find((r) => r.reservationId === reservationId);
}
async getReservations() {
if (!this.isLoggined) {
throw new error_1.SRTError(errorCode_1.SRTErrorCode.LOGIN_REQUIRED, '로그인이 필요한 서비스입니다.');
}
const { data } = await this.axios.post('/atc/selectListAtc14016_n.do', {
pageNo: '0',
});
this.parseResponse(data);
const reservations = lodash_1.default.zip(data['trainListMap'], data['payListMap']).map(([trainData, payData]) => new reservation_1.SRTReservation(this, { trainData, payData }));
return reservations;
}
parseResponse(data) {
if (data.resultMap?.length !== 1) {
throw new error_1.SRTError(errorCode_1.SRTErrorCode.UNKNOWN, '오류가 발생하였습니다.');
}
const [firstOfResult] = data.resultMap;
const resultCode = firstOfResult.strResult;
const errorCode = firstOfResult.msgCd;
const message = firstOfResult.msgTxt;
if (resultCode !== 'SUCC') {
let code = errorCode_1.SRTErrorCode.UNKNOWN;
if (errorCode === 'S111')
code = errorCode_1.SRTErrorCode.LOGIN_REQUIRED;
if (errorCode === 'ERR800052')
code = errorCode_1.SRTErrorCode.ALREADY_CANCELLED;
throw new error_1.SRTError(code, message);
}
return data;
}
detectUserMethod(userId) {
if (userId.match(/[^@]+@[^@]+\.[^@]+/g))
return loginMethod_1.SRTLoginMethod.EMAIL;
if (userId.match(/^\d{3}-\d{3,4}-\d{4}$/g)) {
return loginMethod_1.SRTLoginMethod.PHONE_NUMBER;
}
return loginMethod_1.SRTLoginMethod.MEMBERSHIP_ID;
}
generateUserAgent() {
const ua = (0, random_useragent_1.getRandom)((ua) => ua.osName === 'Android');
return `${ua} SRT-APP-Android V.1.0.6`;
}
async getAccessToken() {
return this.cookieJar.serialize().then((r) => JSON.stringify(r));
}
}
exports.SRT = SRT;