diff --git a/CPBLClass.js b/CPBLClass.js new file mode 100644 index 0000000..9d4828d --- /dev/null +++ b/CPBLClass.js @@ -0,0 +1,255 @@ +const dateFormat = require('dateformat'); +const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; +const { decode } = require('querystring'); + +/** CPBL */ +class CPBLClass { + // constructor(bot, JianMiaubot, Tools_MYSQLDB) { + // this.bot = bot; + // this.JianMiaubot = JianMiaubot; + // this.Tools_MYSQLDB = Tools_MYSQLDB; + // } + + async GetCPBLTV(data) { + /** 統一獅 */ + // let id = "OTT_LIVE_0000001975"; + + /** 兄弟 */ + // let id = "OTT_LIVE_0000001976"; + + /** 味全 */ + // let id = "OTT_LIVE_0000001977"; + + /** 富邦 */ + // let id = "OTT_LIVE_0000001978"; + + /** 樂天 */ + // let id = "OTT_LIVE_0000001979"; + + + let id = data.id; + if (!id) { + return ""; + } + let freeProduct = "0"; + let timestamp = (new Date()).getTime(); + let axios = require('axios'); + + let config = { + method: 'post', + url: `https://hamivideo.hinet.net/api/play.do?id=${id}&freeProduct=${freeProduct}&_=${timestamp}`, + headers: { + 'Host': 'hamivideo.hinet.net', + 'Cookie': '_gcl_au=1.1.38427805.1650705872; _ga=GA1.3.1684527816.1650705872; __BWfp=c1650705872117x754ae4340; _fbp=fb.2.1650705872306.262830585; _fbp=fb.1.1650705872306.262830585;fullCover_182=1; _ga=GA1.1.1684527816.1650705872; video_muted=false; video_volume=1; ohu=c7058664da3a32d55f3c499712af03271b3e5058103c4019f4d3ee3932d15bcc356a34f3a2382291afc3b056a10b4b06ad7f1919efd7b70f949b59738a888196d3e6e4da638f6ee428cdb432fe2654162ffa8df07977b385441e10f8d7e30a10f87003ca0e8b5da36e036df76bbc1a74845dcd2767247104b5c962ec6da91dcd7140846b4f55e8e2eb0580e1a1788d4bc76b944539bcd7f4c4ae9894cdf3cdc517bc7af9c34b6da4abaf1d25fc8ddba7f5a0cdf4e07d976aa310c3227414c604c1deae379b73d602574137660d4f08881e9b4c30ba774359097306cd928133eef98b5c4c74d79341edbe5c9199fcb9179e6fe87ae87032441ee181db0a4ce99d4afac3a29af3b6cfb938112e0fbc3dd33c757e11c34a36f948f867d405fb3bc5271bce33d66ca73b786d211fa05826c2beded1b7a4283c22ff77baceb9cfe80104302cc45d0d0e1ce120428b7ce85fb8010674dd8ed13a77549d1711a59141f4a0d07997e07ec44222a82a5e28e7b12f8cb86ebcef7cb0f91491f3655821eba9; JSESSIONID=858A4E482F2186429A775A2A3D27AC0C; keepMenuId=; BIGipServerrBtu5cKbUKuOQaGS4KMTNg=!TJ/BKSGmc9Mp6SPp6Pe0Pw5wVAvTriWNfFUcDJWbPXCckbutXV1WZWfFhstuR3ksm6QJYeqyQjQUkHU=; seconds=0; _ga_NCTT6HZ347=GS1.1.1655083524.71.1.1655083691.59; csrftoken=9af4d005-d07f-4a10-a1ca-bb5a4baf6810' + } + }; + + let response = await axios(config) + return response.data.url; + } + + async GetCPBL(data) { + let url = `https://www.cpbl.com.tw/box/getlive`; + let topostdata = { + GameSno: data.GameSno, + KindCode: data.KindCode, + Year: data.Year + }; + let Response = await this.Postgetlive(url, topostdata); + return new Promise((resolve, reject) => { + // 傳入 resolve 與 reject,表示資料成功與失敗 + resolve(this.ParseCPBL(Response, data.today)); + }); + } + + async GetCPBLList(data) { + let url = `https://www.cpbl.com.tw/schedule/getgamedatas`; + let topostdata = { + kindCode: "A", + calendar: "2021/01/01", + location: "", + }; + let Response = await this.PostData(url, topostdata); + return new Promise((resolve, reject) => { + // 傳入 resolve 與 reject,表示資料成功與失敗 + resolve(this.ParseCPBLList(Response, data.today)); + }); + } + + ParseCPBL(dataStr, today) { + let todaygame = {}; + let data = JSON.parse(dataStr); + let gamedata = []; + if (data["Success"]) { + let LiveLogJson = JSON.parse(data.LiveLogJson); + if (LiveLogJson.length <= 1) { + todaygame = "比賽尚未開始"; + return JSON.stringify(todaygame); + } + for (let i = 0; i < LiveLogJson.length; i++) { + let index = null; + if (LiveLogJson[i].Content === "比賽結束") { + let LiveLog = LiveLogJson[i - 1]; + let InningSeq = LiveLog.InningSeq + ""; + let VisitingHomeType = LiveLog.VisitingHomeType + ""; + todaygame[InningSeq] = todaygame[InningSeq] ? todaygame[InningSeq] : {}; + todaygame[InningSeq][VisitingHomeType] = todaygame[InningSeq][VisitingHomeType] ? todaygame[InningSeq][VisitingHomeType] : []; + let game = { + HitterName: LiveLog.HitterName, + Content: LiveLog.Content, + }; + todaygame[InningSeq][VisitingHomeType].push(game); + + LiveLog = LiveLogJson[i]; + InningSeq = LiveLog.InningSeq + ""; + VisitingHomeType = LiveLog.VisitingHomeType + ""; + todaygame[InningSeq] = todaygame[InningSeq] ? todaygame[InningSeq] : {}; + todaygame[InningSeq][VisitingHomeType] = todaygame[InningSeq][VisitingHomeType] ? todaygame[InningSeq][VisitingHomeType] : []; + game = { + HitterName: "", + Content: LiveLog.Content, + }; + todaygame[InningSeq][VisitingHomeType].push(game); + } else if (i !== LiveLogJson.length - 1 && LiveLogJson[i].HitterUniformNo !== LiveLogJson[i + 1].HitterUniformNo) { + let LiveLog = LiveLogJson[i]; + let InningSeq = LiveLog.InningSeq + ""; + let VisitingHomeType = LiveLog.VisitingHomeType + ""; + todaygame[InningSeq] = todaygame[InningSeq] ? todaygame[InningSeq] : {}; + todaygame[InningSeq][VisitingHomeType] = todaygame[InningSeq][VisitingHomeType] ? todaygame[InningSeq][VisitingHomeType] : []; + let game = { + HitterName: LiveLog.HitterName, + Content: LiveLog.Content, + }; + todaygame[InningSeq][VisitingHomeType].push(game); + } + } + for (let i = 1; i <= 9; i++) { + for (let j = 1; j <= 2; j++) { + if (todaygame[i] && todaygame[i][j]) { + let Msg = `\n\n-----${i}${j === 1 ? "上" : "下"}-----` + gamedata.push(Msg); + let gamethis = todaygame[i][j]; + for (let k = 0; k < gamethis.length; k++) { + let game = gamethis[k]; + let Msg = `\n\n${game.HitterName} ${game.Content}` + if (Msg.indexOf("比賽結束") !== -1) { + Msg = "比賽結束"; + } + gamedata.push(Msg); + } + } + } + } + if (gamedata.length < 1) { + gamedata = "比賽尚未開始"; + return JSON.stringify(gamedata); + } + } + return JSON.stringify(gamedata); + } + + ParseCPBLList(dataStr, today) { + let todaygame = []; + let data = JSON.parse(dataStr); + let allgame = null; + if (data["Success"]) { + allgame = JSON.parse(data["GameDatas"]); + for (let i = 0; i < allgame.length; i++) { + let game = allgame[i]; + let gamedate = game.PreExeDate.split("T")[0]; + let KindCode = game.KindCode; + if (gamedate === today && KindCode === "A") { + todaygame.push(game); + } + } + } + return JSON.stringify(todaygame); + } + + /** + * 取得表 + * @param Url Url + * @param arrange 是否需要整理 + */ + GetData(Url) { + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 400) { + var response = xhr.responseText; + resolve(response); + } + } + }; + xhr.open("GET", Url, true); + xhr.send(); + }); + } + + /** + * 取得表 + * @param Url Url + * @param data data + */ + Postgetlive(Url, data) { + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.withCredentials = true; + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 400) { + var response = xhr.responseText; + resolve(response); + } + } + }; + xhr.open("Post", Url); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + let encodedData = this.encodeFormData(data); + xhr.send(encodedData); + }); + } + + /** + * 取得表 + * @param Url Url + * @param data data + */ + PostData(Url, data) { + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.withCredentials = true; + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 400) { + var response = xhr.responseText; + resolve(response); + } + } + }; + xhr.open("Post", Url); + // xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8'); + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhr.setRequestHeader("requestverificationtoken", "aA2VWAXiewD1fyMrVhoSmdZAOhNa5YYPdnBxyLU-MlbWdnLHNKtMOkF1xtcZN3KCLW3RrFYLRCPm0DtgZW8sBIQXzxA1:wSKMTfFjGh65R1J0CDU_e91xfxHoqFwPomyXUVEz6Lm3itbHmDMjj11vCCoU4FD-QZjd690_4GD79luvLDTrb6aEFeU1"); + let encodedData = this.encodeFormData(data); + xhr.send(encodedData); + }); + } + + encodeFormData(data) { + if (!data) return ""; // Always return a string + var pairs = []; // To hold name=value pairs + for (var name in data) { // For each name + if (!data.hasOwnProperty(name)) continue; // Skip inherited + if (typeof data[name] === "function") continue; // Skip methods + var value = data[name].toString(); // Value as string + name = encodeURIComponent(name.replace(" ", "+")); // Encode name + value = encodeURIComponent(value.replace(" ", "+")); // Encode value + pairs.push(name + "=" + value); // Remember name=value pair + } + return pairs.join('&'); // Return joined pairs separated with & + } +} + +module.exports = CPBLClass \ No newline at end of file diff --git a/LINENotifyClass.js b/LINENotifyClass.js new file mode 100644 index 0000000..90891e8 --- /dev/null +++ b/LINENotifyClass.js @@ -0,0 +1,177 @@ +const dateFormat = require('dateformat'); +const Tools_MYSQLDBClass = require('../line-cost-js/Tools_MYSQLDBClass'); +const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; + +/** LINENotify */ +class LINENotifyClass { + constructor() { + this.Tools_MYSQLDB = new Tools_MYSQLDBClass(); + } + + async LINENotify_Receive(data) { + let postdata = { + 'grant_type': 'authorization_code', + 'code': data.code, + 'redirect_uri': process.env.LINENotify_redirect_uri, + 'client_id': process.env.LINENotify_client_id, + 'client_secret': process.env.LINENotify_client_secret + }; + let Response = await this.Get_token(postdata); + let Responsedata = JSON.parse(Response); + if (Responsedata.status === 200) { + let access_token = Responsedata.access_token; + + let datetime = dateFormat(new Date(), "yyyy-mm-dd HH:MM:ss"); + let state = data.state.split("$"); + let userId = state[0]; + let displayName = state[1]; + let Query = `INSERT INTO \`UserData\` (UserData.datetime, UserData.userId, UserData.displayName, UserData.LINENotify) VALUES ('${datetime}', '${userId}', '${displayName}', '${access_token}') ON DUPLICATE KEY UPDATE UserData.LINENotify = '${access_token}';`; + await this.Tools_MYSQLDB.Query(Query); + + let message = "\\n阿巴阿巴"; + this.Send(access_token, message) + return new Promise((resolve, reject) => { + // 傳入 resolve 與 reject,表示資料成功與失敗 + // resolve("連接已經完成"); + let html = ` + + + + + + +

連接已經完成

+ + + + `; + resolve(html); + }); + } else { + return new Promise((resolve, reject) => { + // 傳入 resolve 與 reject,表示資料成功與失敗 + resolve("連接失敗"); + }); + } + } + + /** + * 取得表 + * @param Url Url + * @param arrange 是否需要整理 + */ + GetData(Url) { + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 400) { + var response = xhr.responseText; + resolve(response); + } + } + }; + xhr.open("GET", Url, true); + xhr.send(); + }); + } + + /** + * 取得表 + * @param Url Url + * @param data data + */ + PostData(Url, data) { + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.withCredentials = true; + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 400) { + var response = xhr.responseText; + resolve(response); + } + } + }; + xhr.open("Post", Url); + // xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8'); + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhr.setRequestHeader("requestverificationtoken", "aA2VWAXiewD1fyMrVhoSmdZAOhNa5YYPdnBxyLU-MlbWdnLHNKtMOkF1xtcZN3KCLW3RrFYLRCPm0DtgZW8sBIQXzxA1:wSKMTfFjGh65R1J0CDU_e91xfxHoqFwPomyXUVEz6Lm3itbHmDMjj11vCCoU4FD-QZjd690_4GD79luvLDTrb6aEFeU1"); + let encodedData = this.encodeFormData(data); + xhr.send(encodedData); + }); + } + + /** + * Get_token + * @param data data + */ + Get_token(data) { + return new Promise((resolve, reject) => { + let url = 'https://notify-bot.line.me/oauth/token'; + var xhr = new XMLHttpRequest(); + xhr.withCredentials = true; + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 400) { + var response = xhr.responseText; + resolve(response); + } + } + }; + xhr.open("Post", url); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + let encodedData = this.encodeFormData(data); + xhr.send(encodedData); + }); + } + + /** + * Send + * @param data data + */ + Send(access_token, data) { + return new Promise((resolve, reject) => { + let url = 'https://notify-api.line.me/api/notify'; + var xhr = new XMLHttpRequest(); + xhr.withCredentials = true; + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 400) { + var response = xhr.responseText; + resolve(response); + } + } + }; + // let encodedData = this.encodeFormData(data); + let newline = encodeURI("\\n"); + let encodedData = "%0D%0A" + encodeURI(data).replace(newline, "%0D%0A"); + xhr.open("POST", `${url}?message=${encodedData}`); + access_token = access_token ? access_token : "S32BS5DulNLWFjlp1if24yn2SXMaEGyzmSRl75kfCXv"; + xhr.setRequestHeader("Authorization", `Bearer ${access_token}`); + xhr.send(); + }); + } + + encodeFormData(data) { + if (!data) return ""; // Always return a string + var pairs = []; // To hold name=value pairs + for (var name in data) { // For each name + if (!data.hasOwnProperty(name)) continue; // Skip inherited + if (typeof data[name] === "function") continue; // Skip methods + var value = data[name].toString(); // Value as string + name = encodeURIComponent(name.replace(" ", "+")); // Encode name + value = encodeURIComponent(value.replace(" ", "+")); // Encode value + pairs.push(name + "=" + value); // Remember name=value pair + } + return pairs.join('&'); // Return joined pairs separated with & + } +} + +module.exports = LINENotifyClass \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e84f94c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# api \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..e40cd05 --- /dev/null +++ b/app.js @@ -0,0 +1,145 @@ +// 背景執行 forever start -w -a -l api.log app.js +// 重新背景執行 forever restart -a -l api.log app.js +// 監聽檔案變化 nodemon app.js +// npm start +// npm run dev +// Debug nodemon --inspect=192.168.168.15:9229 app.js + +const dateFormat = require('dateformat'); +require('dotenv').config() +const http = require('http'); +const https = require('https'); +const fs = require('fs'); +const express = require('express'); +const app = express(); + +//讀取憑證及金鑰 +const prikey = fs.readFileSync('../certificate/RSA-privkey.pem', 'utf8'); +const cert = fs.readFileSync('../certificate/RSA-cert.pem', 'utf8'); +const cafile = fs.readFileSync('../certificate/RSA-chain.pem', 'utf-8'); + +//建立憑證及金鑰 +const credentials = { + key: prikey, + cert: cert, + ca: cafile +}; + +const CPBLClass = require('./CPBLClass'); +const LINENotifyClass = require('./LINENotifyClass'); +const { decode } = require('querystring'); +const CPBL = new CPBLClass(); +const LINENotify = new LINENotifyClass(); + +const port = process.env.PORT || 3000; + +const server = https.createServer(credentials, async function (req, res) { + // const server = http.createServer(async function (req, res) { + // console.log(`rawBody: ${req.rawBody}`); + // res.writeHead(200); + if (req.method === 'GET') { + let Request = req.url.replace("/", ""); + let Response = ""; + let data = ''; + req.on('data', chunk => { + data += chunk; + }); + req.on('end', async () => { + switch (req.headers["content-type"]) { + case "application/x-www-form-urlencoded": { + data = decode(data); + break; + } + + case "application/json": { + data = JSON.parse(data); + break; + } + + default: + break; + } + switch (Request) { + case "CPBLTV": + Response = await CPBL.GetCPBLTV(data); + break; + + default: + break; + } + res.writeHead(200); + res.write(Response); + return res.end(); + }); + } else if (req.method === 'POST') { + // console.log(`body: ${JSON.stringify(req.body)}`); + let Request = req.url.replace("/", ""); + let Response = ""; + let data = ''; + req.on('data', chunk => { + data += chunk; + }); + req.on('end', async () => { + switch (req.headers["content-type"]) { + case "application/x-www-form-urlencoded": { + data = decode(data); + // data = [] + // let strs = str.split("&"); + // for (let i = 0; i < strs.length; i++) { + // data[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]); + // } + break; + } + + case "application/json": { + data = JSON.parse(data); + break; + } + + default: + break; + } + switch (Request) { + case "CPBLTV": + Response = await CPBL.GetCPBLTV(data); + break; + + case "CPBL": + Response = await CPBL.GetCPBL(data); + break; + + case "CPBLList": + Response = await CPBL.GetCPBLList(data); + break; + + case "LINENotify": + Response = await LINENotify.LINENotify_Receive(data); + break; + + case "SendLINENotify": + Response = await LINENotify.Send(data["access_token"], data["message"] ? data["message"] : ""); + break; + + default: + break; + } + res.writeHead(200); + res.write(Response); + return res.end(); + }); + // } else if (req.method === 'GET' && req.url === path) { + // let Response = await CPBL.GetCPBL(); + // res.writeHead(200); + // res.write(Response); + // return res.end(); + } else { + res.statusCode = 404; + res.setHeader('Content-Type', 'text/html; charset=utf-8'); + return res.end('Not found'); + } +}); +server.listen(port, function () { + let datetime = dateFormat(new Date(), "yyyy-mm-dd HH:MM:ss"); + console.log(`${datetime} listening on ${port}`); + console.log(`${datetime} [api已準備就緒]`); +}); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..7b4c489 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "cpbl", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "start": "forever start -a -l api.log app.js", + "dev": "nodemon --inspect=127.0.0.1:9229 app.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.27.2", + "cheerio": "^1.0.0-rc.6", + "dateformat": "^4.5.1", + "dotenv": "^8.2.0", + "express": "^4.17.1", + "http": "0.0.1-security", + "https": "^1.0.0", + "node-html-parser": "^3.1.5", + "nodemon": "^2.0.7", + "request": "^2.88.2", + "xmlhttprequest": "^1.8.0" + } +}