From ef9d324cc80e82a2787d860e7d2d8fa516587da3 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Wed, 30 Jun 2021 21:05:21 -0500 Subject: [PATCH] Process en data --- frontend/package-lock.json | 109 ++ frontend/package.json | 2 + frontend/src/App.js | 341 ++----- frontend/src/setupProxy.js | 23 +- server/app.js | 852 ++++++++++++---- server/app.js.save | 1955 ++++++++++++++++++++++++++++++++++++ 6 files changed, 2841 insertions(+), 441 deletions(-) create mode 100644 server/app.js.save diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ce165a0..e742c25 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1361,6 +1361,11 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.4.tgz", "integrity": "sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg==" }, + "@reach/observe-rect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@reach/observe-rect/-/observe-rect-1.2.0.tgz", + "integrity": "sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==" + }, "@restart/context": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", @@ -3710,6 +3715,11 @@ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, + "components": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/components/-/components-0.1.0.tgz", + "integrity": "sha1-IFfAjpx78mYv7slcSeX/Kpq4XtU=" + }, "compose-function": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", @@ -4273,6 +4283,83 @@ "type": "^1.0.1" } }, + "d3-array": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz", + "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==", + "requires": { + "internmap": "^1.0.0" + } + }, + "d3-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==" + }, + "d3-delaunay": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-5.3.0.tgz", + "integrity": "sha512-amALSrOllWVLaHTnDLHwMIiz0d1bBu9gZXd1FiLfXf8sHcX9jrcj81TVZOqD4UX7MgBZZ07c8GxzEgBpJqc74w==", + "requires": { + "delaunator": "4" + } + }, + "d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" + }, + "d3-interpolate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", + "requires": { + "d3-color": "1 - 2" + } + }, + "d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "d3-scale": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz", + "integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==", + "requires": { + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "1 - 2", + "d3-time-format": "2 - 3" + } + }, + "d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "requires": { + "d3-path": "1" + } + }, + "d3-time": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz", + "integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q==" + }, + "d3-time-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", + "requires": { + "d3-time": "1 - 2" + } + }, + "d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + }, "damerau-levenshtein": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", @@ -4448,6 +4535,11 @@ } } }, + "delaunator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz", + "integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -6849,6 +6941,11 @@ "side-channel": "^1.0.2" } }, + "internmap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.0.tgz", + "integrity": "sha512-SdoDWwNOTE2n4JWUsLn4KXZGuZPjPF9yyOGc8bnfWnBQh7BD/l80rzSznKc/r4Y0aQ7z3RTk9X+tV4tHBpu+dA==" + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -10573,6 +10670,18 @@ "prop-types": "^15.7.2" } }, + "react-charts": { + "version": "2.0.0-beta.7", + "resolved": "https://registry.npmjs.org/react-charts/-/react-charts-2.0.0-beta.7.tgz", + "integrity": "sha512-iUspg9rnx7kD0H/wsK67HNUioOgKgJ8WRXr/Tk3EGP2qcFb9Vo7pjDk4oz1jH12TC+mqL+HFxNYraMkhWd6CUw==", + "requires": { + "@reach/observe-rect": "^1.1.0", + "d3-delaunay": "^5.2.1", + "d3-scale": "^3.2.1", + "d3-shape": "^1.3.7", + "d3-voronoi": "^1.1.2" + } + }, "react-dev-utils": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5eeffc4..32fa192 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,11 +9,13 @@ "@testing-library/user-event": "^7.2.1", "axios": "^0.21.1", "chart.js": "^2.9.4", + "components": "^0.1.0", "http-proxy-middleware": "^1.0.5", "moment": "^2.29.0", "react": "^16.13.1", "react-bootstrap": "^1.3.0", "react-chartjs-2": "^2.11.1", + "react-charts": "^2.0.0-beta.7", "react-dom": "^16.13.1", "react-router-dom": "^5.2.0", "react-router-hash-link": "^2.1.0", diff --git a/frontend/src/App.js b/frontend/src/App.js index 31eb68d..a4622ad 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,3 +1,5 @@ + +import { Chart } from 'react-charts' import React, {useState,useEffect,useRef} from 'react'; import logo from './logo.svg'; import './App.css'; @@ -2224,7 +2226,7 @@ function EventEditor() { const[send,setSend] = useState(false) const[message,setMessage] = useState("") - const EVENTID = 10; + const EVENTID = 17; //console.log(moment().format("YYYY-MM-DDTHH:mm")) @@ -2311,246 +2313,89 @@ function EventEditor() { } -function ChartData() { - const[eventData,setEventData] = useState([]) - const[update,setUpdate] = useState(false) - const [chartData,setChartData] = useState([]) +function CanEditor() { + const [password,setPassword] = useState(""); + const [cans,setCans] = useState(0); + const [games,setGames] = useState(0); + const [update,setUpdate] = useState(false) useEffect(()=>{ + refreshCount() const interval = setInterval(()=>{ - axios.get("http://projectdivar.com/eventdata/t20") - .then((data)=>{ - var values; - data.data.map((obj)=>{if (values[obj.rank]) {values[obj.rank]=[...values[obj.rank],obj]} else {values[obj.rank]=[obj]}}) - setEventData(values); - console.log(data.data) - }) - .catch((err)=>{}) - },30000); + refreshCount() + },5000); return ()=>{clearInterval(interval)} },[update]) useEffect(()=>{ - console.log(eventData) - setChartData( - { - datasets: [{ - label: 'T1', - data: GetChartData(eventData,1), - backgroundColor: [ - 'rgba(255, 99, 132, 0.05)', - 'rgba(54, 162, 235, 0.05)', - 'rgba(255, 206, 86, 0.05)', - 'rgba(75, 192, 192, 0.05)', - 'rgba(153, 102, 255, 0.05)', - 'rgba(255, 159, 64, 0.05)' - ], - borderColor: [ - 'rgba(255, 99, 132, 1)', - 'rgba(54, 162, 235, 1)', - 'rgba(255, 206, 86, 1)', - 'rgba(75, 192, 192, 1)', - 'rgba(153, 102, 255, 1)', - 'rgba(255, 159, 64, 1)' - ] - },{ - label: 'T2', - data: GetChartData(eventData,2), - backgroundColor: [ - 'rgba(54, 162, 235, 0.05)' - ], - borderColor: [ - 'rgba(54, 162, 235, 1)' - ] - },{ - label: 'T3', - data: GetChartData(eventData,3), - backgroundColor: [ - 'rgba(255, 206, 86, 0.05)', - ], - borderColor: [ - 'rgba(255, 206, 86, 1)', - ] - },{ - label: 'T4', - data: GetChartData(eventData,4), - backgroundColor: [ - 'rgba(75, 192, 192, 0.05)', - ], - borderColor: [ - 'rgba(75, 192, 192, 1)', - ] - },{ - label: 'T5', - data: GetChartData(eventData,5), - backgroundColor: [ - 'rgba(153, 102, 255, 0.05)', - ], - borderColor: [ - 'rgba(153, 102, 255, 1)', - ] - },{ - label: 'T6', - data: GetChartData(eventData,6), - backgroundColor: [ - 'rgba(255, 159, 64, 0.05)' - ], - borderColor: [ - 'rgba(255, 159, 64, 1)' - ] - },{ - label: 'T7', - data: GetChartData(eventData,7), - backgroundColor: [ - 'rgba(255, 99, 132, 0.05)' - ], - borderColor: [ - 'rgba(255, 99, 132, 1)' - ] - },{ - label: 'T8', - data: GetChartData(eventData,8), - backgroundColor: [ - 'rgba(54, 162, 235, 0.05)' - ], - borderColor: [ - 'rgba(54, 162, 235, 1)' - ] - },{ - label: 'T9', - data: GetChartData(eventData,9), - backgroundColor: [ - 'rgba(255, 206, 86, 0.05)' - ], - borderColor: [ - 'rgba(255, 206, 86, 1)' - ] - },{ - label: 'T10', - data: GetChartData(eventData,10), - backgroundColor: [ - 'rgba(75, 192, 192, 0.05)' - ], - borderColor: [ - 'rgba(75, 192, 192, 1)' - ] - },{ - label: 'T20', - data: GetChartData(eventData,20), - backgroundColor: [ - 'rgba(0, 0, 0, 0.05)' - ], - borderColor: [ - 'rgba(0, 0, 0, 1)' - ] - },{ - label: 'T50', - data: GetChartData(eventData,50), - backgroundColor: [ - 'rgba(255, 255, 255, 0.5)' - ], - borderColor: [ - 'rgba(255, 255, 255, 1)' - ] - } - ,{ - label: 'T100', - data: GetChartData(eventData,100), - backgroundColor: [ - 'rgba(150, 255, 150, 0.5)' - ], - borderColor: [ - 'rgba(150, 255, 150, 1)' - ] - },{ - label: 'T500', - data: GetChartData(eventData,500), - backgroundColor: [ - 'rgba(160, 0, 0, 0.5)' - ], - borderColor: [ - 'rgba(160, 0, 0, 1)' - ] - },{ - label: 'T1000', - data: GetChartData(eventData,1000), - backgroundColor: [ - 'rgba(255, 150, 150, 0.5)' - ], - borderColor: [ - 'rgba(255, 150, 150, 1)' - ] - },{ - label: 'T5000', - data: GetChartData(eventData,5000), - backgroundColor: [ - 'rgba(0, 140, 0, 0.5)' - ], - borderColor: [ - 'rgba(0, 140, 0, 1)' - ] - },{ - label: 'T10000', - data: GetChartData(eventData,10000), - backgroundColor: [ - 'rgba(30, 30, 255, 0.5)' - ], - borderColor: [ - 'rgba(30, 30, 255, 1)' - ] - }/*,{ - label: 'T100 HAPPY FORTUNE NEW YEAR', - data: [ - {x:moment('2021-01-12 12:00:00+09:00').add(0,'days').add(0,'hours'),y:0}, - {x:moment('2021-01-12 12:00:00+09:00').add(0,'days').add(7,'hours'),y:54036}, - {x:moment('2021-01-12 12:00:00+09:00').add(4,'days').add(23,'hours'),y:451398}, - {x:moment('2021-01-12 12:00:00+09:00').add(5,'days').add(3,'hours'),y:470204}, - {x:moment('2021-01-12 12:00:00+09:00').add(7,'days').add(18,'hours'),y:671150}, - {x:moment('2021-01-12 12:00:00+09:00').add(8,'days').add(15,'hours'),y:915147}, - {x:moment('2021-01-12 12:00:00+09:00').add(8,'days').add(17,'hours'),y:952330}, - {x:moment('2021-01-12 12:00:00+09:00').add(8,'days').add(19,'hours'),y:988548}, - {x:moment('2021-01-12 12:00:00+09:00').add(8,'days').add(21,'hours'),y:1027488}, - ], - backgroundColor: [ - 'rgba(255, 255, 150, 0.5)' - ], - borderColor: [ - 'rgba(255, 255, 150, 1)' - ] - }*/], - options: { - scales: { - yAxes: [{ - ticks: { - beginAtZero: true, - } - }], - xAxes: [{ - type: 'time', - time: { - unit: 'hours', - displayFormats: { - hours: 'MMM D hA' - } - }, - ticks: { - min:EVENTSTART, - max:EVENTEND - }, - distribution: 'linear' - }] - } - } + + },[password]) + + function refreshCount() { + axios.get("http://projectdivar.com/cans") + .then((data)=>{ + if (data.data.cans>cans) { + setCans(data.data.cans) } - ) - },[eventData]) - - return ( - <> -

繋ぎ手たちは導かれ~前編~

- - - ) + if (data.data.cans+data.data.notcan>games) { + setGames(Number(data.data.cans)+Number(data.data.notcan)) + } + }) + } + + function sendRequest(cans) { + axios.post("http://projectdivar.com/cans",{cans:cans}) + .then((data)=>{ + refreshCount() + }) + } + + function addCan(t) { + setCans(Number(cans)+1) + setGames(Number(games)+1) + sendRequest(true) + } + function addGame(t) { + setGames(Number(games)+1) + sendRequest(false) + } + + return <> +
+
+
+

Can %

+

{((cans/games)*100).toFixed(2)+"%"}

+
+
+
+
+
Cans
+

{cans}

+
+
+
Total Games
+

{games}

+
+
+
+ {/*{password!=="muni_62"&&<> + Password to Edit: + { + setPassword(t.currentTarget.value) + }} + />} + {password=="muni_62"&&<> +
+
+ +
+
+ +
+
+ }*/} + } function Website() { @@ -2570,16 +2415,16 @@ function Website() { return (
- {/*
-

Menu

- -

- Rankings
- Submit Scores
- DivaBot
-
- -
*/} +
+

Menu

+ +

+ Rankings
+ Submit Scores
+ DivaBot
+
+ +
@@ -2621,12 +2466,16 @@ function Website() {

Event Data

- +

Event Editor

+ +

Can or no Can?

+ +

Project DivaR

Welcome! This website is here to store and track all your Project Diva records for yours and others' enjoyment! @@ -2776,7 +2625,7 @@ function App() { return (
- {/*
+
@@ -2784,7 +2633,7 @@ function App() {
-
*/} +
diff --git a/frontend/src/setupProxy.js b/frontend/src/setupProxy.js index 58ce2e2..e4b9eac 100644 --- a/frontend/src/setupProxy.js +++ b/frontend/src/setupProxy.js @@ -1,6 +1,27 @@ const { createProxyMiddleware } = require('http-proxy-middleware'); module.exports = function(app) { + /*app.use( + "/event/query", + createProxyMiddleware({ + target: 'http://server:4501', + changeOrigin: true, + }) + );*/ + app.use( + "/ev", + createProxyMiddleware({ + target: 'http://server:4501', + changeOrigin: true, + }) + ); + app.use( + "/cans", + createProxyMiddleware({ + target: 'http://server:4501', + changeOrigin: true, + }) + ); app.use( "/eventchart", createProxyMiddleware({ @@ -330,4 +351,4 @@ module.exports = function(app) { changeOrigin: true, }) ); -}; \ No newline at end of file +}; diff --git a/server/app.js b/server/app.js index f0df56c..9292ce7 100644 --- a/server/app.js +++ b/server/app.js @@ -1,4 +1,4 @@ -const express = require('express') + const express = require('express') const axios = require('axios') const twitchStreams = require('twitch-get-stream') const app = express() @@ -45,6 +45,29 @@ new Pool({ port: 5432, }) +/*const db2 = +new Pool({ + user: 'read_only_user', + password: 'divar1234', + host: 'postgres', + database: 'divar', + port: 5432, +}) + +app.get('/event/query',(req,res)=>{ + if (req.query.query&&req.query.query.length>0) { + db2.query(req.query.query) + .then((data)=>{ + res.status(200).json(data.rows) + }) + .catch((err)=>{ + res.status(500).json(err.message) + }) + } else { + res.status(400).send("Empty query!") + } +})*/ + app.get('/song/:songname', (req, res) => { db.query('select * from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1', [req.params.songname] , (error, results) => { if (error) { @@ -55,7 +78,7 @@ app.get('/song/:songname', (req, res) => { } }) }) - + app.get('/songs', (req, res) => { db.query('select songs.*,songdata.rating as rating,songdata.difficulty,songdata.notecount from songs left join songdata on songs.id=songdata.songid' , (error, results) => { if (error) { @@ -1166,15 +1189,28 @@ app.post('/streaminfo/:id',function (req,res){ } }) -const lastscores={} +var lastscores={} +const EVENTID=23; +var EVENTSTART=moment('2021-06-28 03:00:00+00'); +var EVENTEND=moment('2021-07-06 11:59:59+00'); + +var lastscores_EN={} +const EVENTID_EN=23; +var EVENTSTART_EN=moment('2021-06-28 03:00:00+00'); +var EVENTEND_EN=moment('2021-07-06 11:59:59+00'); + +app.get("/helpmetestwithoutbreakingshit", (req,res) => { + res.status(200).send("hi") + db.query("select eventid, startdate, enddate from event order by id desc limit 1").then(res.status(200).send) +}) app.post('/eventsubmit',function(req,res) { - const EVENTID=10; + lastscores={} function submit() { lastscores[req.body.rank]=Number(req.body.points) - db.query("insert into eventdata(eventid,rank,date,name,description,points) values($1,$2,$3,$4,$5,$6) returning *;", - [req.body.eventid,req.body.rank,req.body.date?req.body.date:new Date(),req.body.name,req.body.description,req.body.points]) + db.query("insert into "+(req.query.en?"en_":"")+"eventdata(eventid,rank,date,name,description,points) values($1,$2,$3,$4,$5,$6) returning *;", + [req.body.eventid,req.body.rank,req.body.date?req.body.date:req.body.fin?moment(EVENTEND).add(5,'minutes').format("YYYY-MM-DD HH:mm:ssZ"):new Date(),req.body.name,req.body.description,req.body.points]) .then((data)=>{ if (data.rows.length>0) { res.status(200).send("Submitted.") @@ -1189,8 +1225,59 @@ app.post('/eventsubmit',function(req,res) { //add to table. + function FurtherTierIsOkay(tier,scores,points) { + var tiers= [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,50,100,500,1000,2000,5000,10000,20000,30000,50000] + if (tier<=1) { + return true; + } else + { + //Find the previous tier. + var previousTier = -1; + for (var i=0;i=2; + } + + function ScoreIsSanitary(rank,name,description,points) { + if (Number(rank)<=20) { + return true; + } else { + if (EndsWithZeroes(name)||EndsWithZeroes(description)||EndsWithZeroes(points)) { + return false; + } else { + return true; + } + } + } + //Try to update last scores. - db.query('select distinct on (rank) rank,eventid,date,name,description,points from eventdata where eventid='+EVENTID+' order by rank,date desc;') + db.query('select distinct on (rank) rank,eventid,date,name,description,points,difference from (select lead(points) over (partition by rank order by rank,date desc)-points difference,* from '+(req.query.en?"en_":"")+'eventdata where eventid='+EVENTID+' order by rank,date desc)t order by rank,date desc') .then((data) => { if (data.rows.length>0) { @@ -1198,20 +1285,14 @@ app.post('/eventsubmit',function(req,res) { } - if (!lastscores[req.body.rank]||(lastscores[req.body.rank]20||req.body.points100) { - MAXSPEED=Math.floor(chartData['1'][100].points/(moment(chartData['1'][100].date).diff(EVENTSTART,'minutes')/60)) + if (chartData['1']&&chartData['1'].length>400) { + MAXSPEED=Math.floor(chartData['1'][400].points/(moment(chartData['1'][400].date).diff(EVENTSTART,'minutes')/60)) } else if (chartData['1']&&chartData['1'].length>0){ MAXSPEED=Math.floor(chartData['1'][chartData['1'].length-1].points/(moment(chartData['1'][chartData['1'].length-1].date).diff(EVENTSTART,'minutes')/60)) } else { MAXSPEED=0 } + if (en_chartData['1']&&en_chartData['1'].length>400) { + MAXSPEED=Math.floor(en_chartData['1'][400].points/(moment(en_chartData['1'][400].date).diff(EVENTSTART,'minutes')/60)) + } else + if (en_chartData['1']&&en_chartData['1'].length>0){ + MAXSPEED=Math.floor(en_chartData['1'][en_chartData['1'].length-1].points/(moment(en_chartData['1'][en_chartData['1'].length-1].date).diff(EVENTSTART,'minutes')/60)) + } else { + MAXSPEED=0 + } } const RATEDURATION=2 //In hours. How much EP/hr is shown. -function GetRate(rank) { - if (chartData[rank].length>2) { - var lastpoint=chartData[rank][chartData[rank].length-1] - for (var i=chartData[rank].length-1;i>=0;i--) { - var diff = moment().diff(chartData[rank][i].date,'hours') - if (diff>=RATEDURATION) { - break; +function GetRate(rank,en) { + if (en) { + if (en_chartData[rank].length>2) { + var lastpoint=en_chartData[rank][en_chartData[rank].length-1] + for (var i=en_chartData[rank].length-1;i>=0;i--) { + var diff = moment().diff(en_chartData[rank][i].date,'hours') + if (diff>=RATEDURATION) { + break; + } else { + lastpoint=en_chartData[rank][i] + } + } + var timediff = moment(en_chartData[rank][en_chartData[rank].length-1].date).diff(moment(lastpoint.date),'minutes') + if (timediff<120) { + if (lastpoint===en_chartData[rank][en_chartData[rank].length-1]) { + return "???" + } else + return (en_chartData[rank][en_chartData[rank].length-1].points-lastpoint.points)/RATEDURATION } else { - lastpoint=chartData[rank][i] + return Math.ceil((en_chartData[rank][en_chartData[rank].length-1].points-lastpoint.points)/ + (moment(en_chartData[rank][en_chartData[rank].length-1].date).diff(moment(lastpoint.date),'minutes')/60) + ) } - } - var timediff = moment(chartData[rank][chartData[rank].length-1].date).diff(moment(lastpoint.date),'minutes') - if (timediff<120) { - if (lastpoint===chartData[rank][chartData[rank].length-1]) { - return "???" - } else - return (chartData[rank][chartData[rank].length-1].points-lastpoint.points)/RATEDURATION } else { - return Math.ceil((chartData[rank][chartData[rank].length-1].points-lastpoint.points)/ - (moment(chartData[rank][chartData[rank].length-1].date).diff(moment(lastpoint.date),'minutes')/60) - ) + if (en_chartData[rank].length>0) { + var startPoint=en_chartData[rank][en_chartData[rank].length-1] + return Math.ceil(GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60)) + } else { + return 0 + } } } else { - if (chartData[rank].length>0) { - var startPoint=chartData[rank][chartData[rank].length-1] - return Math.ceil(GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60)) + if (chartData[rank].length>2) { + var lastpoint=chartData[rank][chartData[rank].length-1] + for (var i=chartData[rank].length-1;i>=0;i--) { + var diff = moment().diff(chartData[rank][i].date,'hours') + if (diff>=RATEDURATION) { + break; + } else { + lastpoint=chartData[rank][i] + } + } + var timediff = moment(chartData[rank][chartData[rank].length-1].date).diff(moment(lastpoint.date),'minutes') + if (timediff<120) { + if (lastpoint===chartData[rank][chartData[rank].length-1]) { + return "???" + } else + return (chartData[rank][chartData[rank].length-1].points-lastpoint.points)/RATEDURATION + } else { + return Math.ceil((chartData[rank][chartData[rank].length-1].points-lastpoint.points)/ + (moment(chartData[rank][chartData[rank].length-1].date).diff(moment(lastpoint.date),'minutes')/60) + ) + } } else { - return 0 + if (chartData[rank].length>0) { + var startPoint=chartData[rank][chartData[rank].length-1] + return Math.ceil(GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60)) + } else { + return 0 + } } } } -function GetPointCount(rank) { +function GetPointCount(rank,en) { var pointCount=1; - if (!chartData[rank]) { - return pointCount; - } - if (chartData[rank].length>2) { - var lastpoint=chartData[rank][chartData[rank].length-1] - for (var i=chartData[rank].length-1;i>=0;i--) { - var diff = moment().diff(chartData[rank][i].date,'hours') - if (diff>=RATEDURATION) { - break; + if (en) { + if (!en_chartData[rank]) { + return pointCount; + } + if (en_chartData[rank].length>2) { + var lastpoint=en_chartData[rank][en_chartData[rank].length-1] + for (var i=en_chartData[rank].length-1;i>=0;i--) { + var diff = moment().diff(en_chartData[rank][i].date,'hours') + if (diff>=RATEDURATION) { + break; + } else { + lastpoint=en_chartData[rank][i] + pointCount++; + } + } + return pointCount; + } else { + if (en_chartData[rank].length>0) { + return en_chartData[rank].length; } else { - lastpoint=chartData[rank][i] - pointCount++; + return pointCount; } } - return pointCount; - } else { - if (chartData[rank].length>0) { - return chartData[rank].length; - } else { + } else + { + if (!chartData[rank]) { return pointCount; } + if (chartData[rank].length>2) { + var lastpoint=chartData[rank][chartData[rank].length-1] + for (var i=chartData[rank].length-1;i>=0;i--) { + var diff = moment().diff(chartData[rank][i].date,'hours') + if (diff>=RATEDURATION) { + break; + } else { + lastpoint=chartData[rank][i] + pointCount++; + } + } + return pointCount; + } else { + if (chartData[rank].length>0) { + return chartData[rank].length; + } else { + return pointCount; + } + } } } -function CreatePrediction(precision,rank) { - if (!chartData[rank]) { - return [] - } - var startPoint=chartData[rank][chartData[rank].length-1] - if (rank<=20) { - startPoint={points:startPoint.points,date:moment()} - } - var startTime=moment(startPoint.date) - if (PREDICTIONS&&startTime.diff(EVENTSTART,'hours')>36&&moment(startPoint.date).diff(EVENTSTART,'hours')>=36) { - //console.log(MAXSPEED) - //Precision is in hours. 1 is default - var finalChart=[{y:chartData[rank][chartData[rank].length-1].points,x:chartData[rank][chartData[rank].length-1].date}] - //Start from the time of the last reported rank. - var myPoints = startPoint.points - var pointSpeed = Math.ceil(GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60)) - var speedGoal = MAXSPEED*nyoomfactor[rank] - while (startTime11) { - pointSpeed-=pointSpeed*(slowdownFactor[rank]*10/*CONSTANT for adjustment*/) - } else { - pointSpeed=Math.max( - GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60), - Math.min((12-EVENTEND.diff(startTime,'hours'))*(speedGoal/5),speedGoal)) - //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. - //console.log(pointSpeed) - } - finalChart=[...finalChart,{y:Number.isInteger(myPoints)?myPoints:"???",x:moment(startTime)}] +function CreatePrediction(precision,rank,en) { + if (en) { + if (!en_chartData[rank]) { + return [] } - predictionChartData[rank]=finalChart - return finalChart - } else - if (PREDICTIONS&&startTime.diff(EVENTSTART,'hours')>24&&moment(startPoint.date).diff(EVENTSTART,'hours')>=24) { - //console.log(MAXSPEED) - //Precision is in hours. 1 is default - var finalChart=[{y:chartData[rank][chartData[rank].length-1].points,x:chartData[rank][chartData[rank].length-1].date}] - //Start from the time of the last reported rank. - var myPoints = startPoint.points - var pointSpeed = GetRate(rank) - var speedGoal = MAXSPEED*nyoomfactor[rank] - while (startTime11) { - pointSpeed-=pointSpeed*(slowdownFactor[rank]*10/*CONSTANT for adjustment*/) - } else { - pointSpeed=Math.max( - GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60), - Math.min((12-EVENTEND.diff(startTime,'hours'))*(speedGoal/5),speedGoal)) - //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. - //console.log(pointSpeed) + var startPoint=en_chartData[rank][en_chartData[rank].length-1] + if (rank<=20) { + startPoint={points:startPoint.points,date:moment()} + } + var startTime=moment(startPoint.date) + if (en_PREDICTIONS&&startTime.diff(EVENTSTART_en,'hours')>36&&moment(startPoint.date).diff(EVENTSTART_en,'hours')>=36) { + //console.log(MAXSPEED) + //Precision is in hours. 1 is default + var finalChart=[{y:en_chartData[rank][en_chartData[rank].length-1].points,x:en_chartData[rank][en_chartData[rank].length-1].date}] + //Start from the time of the last reported rank. + var myPoints = startPoint.points + var pointSpeed = Math.ceil(GetRank(rank,en)/(moment(startPoint.date).diff(EVENTSTART_en,'minutes')/60)) + var speedGoal = MAXSPEED*nyoomfactor[rank] + while (startTime11) { + pointSpeed-=pointSpeed*(slowdownFactor[rank]*10/*CONSTANT for adjustment*/) + } else { + pointSpeed=Math.max( + GetRank(rank,en)/(moment(startPoint.date).diff(EVENTSTART_en,'minutes')/60), + Math.min((12-EVENTEND_en.diff(startTime,'hours'))*(speedGoal/5),speedGoal)) + //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. + //console.log(pointSpeed) + } + finalChart=[...finalChart,{y:Number.isInteger(myPoints)?myPoints:"???",x:moment(startTime)}] } - finalChart=[...finalChart,{y:Number.isInteger(myPoints)?myPoints:"???",x:moment(startTime)}] + predictionen_chartData[rank]=finalChart + return finalChart + } else + if (PREDICTIONS_en&&startTime.diff(EVENTSTART_en,'hours')>24&&moment(startPoint.date).diff(EVENTSTART_en,'hours')>=24) { + //console.log(MAXSPEED) + //Precision is in hours. 1 is default + var finalChart=[{y:en_chartData[rank][en_chartData[rank].length-1].points,x:en_chartData[rank][en_chartData[rank].length-1].date}] + //Start from the time of the last reported rank. + var myPoints = startPoint.points + var pointSpeed = GetRate(rank,en) + var speedGoal = MAXSPEED*nyoomfactor[rank] + while (startTime11) { + pointSpeed-=pointSpeed*(slowdownFactor[rank]*10/*CONSTANT for adjustment*/) + } else { + pointSpeed=Math.max( + GetRank(rank,en)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60), + Math.min((12-EVENTEND_en.diff(startTime,'hours'))*(speedGoal/5),speedGoal)) + //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. + //console.log(pointSpeed) + } + finalChart=[...finalChart,{y:Number.isInteger(myPoints)?myPoints:"???",x:moment(startTime)}] + } + en_predictionChartData[rank]=finalChart + return finalChart + } else { + return [] } - predictionChartData[rank]=finalChart - return finalChart } else { - return [] + if (!chartData[rank]) { + return [] + } + var startPoint=chartData[rank][chartData[rank].length-1] + if (rank<=20) { + startPoint={points:startPoint.points,date:moment()} + } + var startTime=moment(startPoint.date) + if (PREDICTIONS&&startTime.diff(EVENTSTART,'hours')>36&&moment(startPoint.date).diff(EVENTSTART,'hours')>=36) { + //console.log(MAXSPEED) + //Precision is in hours. 1 is default + var finalChart=[{y:chartData[rank][chartData[rank].length-1].points,x:chartData[rank][chartData[rank].length-1].date}] + //Start from the time of the last reported rank. + var myPoints = startPoint.points + var pointSpeed = Math.ceil(GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60)) + var speedGoal = MAXSPEED*nyoomfactor[rank] + while (startTime11) { + pointSpeed-=pointSpeed*(slowdownFactor[rank]*10/*CONSTANT for adjustment*/) + } else { + pointSpeed=Math.max( + GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60), + Math.min((12-EVENTEND.diff(startTime,'hours'))*(speedGoal/5),speedGoal)) + //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. + //console.log(pointSpeed) + } + finalChart=[...finalChart,{y:Number.isInteger(myPoints)?myPoints:"???",x:moment(startTime)}] + } + predictionChartData[rank]=finalChart + return finalChart + } else + if (PREDICTIONS&&startTime.diff(EVENTSTART,'hours')>24&&moment(startPoint.date).diff(EVENTSTART,'hours')>=24) { + //console.log(MAXSPEED) + //Precision is in hours. 1 is default + var finalChart=[{y:chartData[rank][chartData[rank].length-1].points,x:chartData[rank][chartData[rank].length-1].date}] + //Start from the time of the last reported rank. + var myPoints = startPoint.points + var pointSpeed = GetRate(rank) + var speedGoal = MAXSPEED*nyoomfactor[rank] + while (startTime11) { + pointSpeed-=pointSpeed*(slowdownFactor[rank]*10/*CONSTANT for adjustment*/) + } else { + pointSpeed=Math.max( + GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60), + Math.min((12-EVENTEND.diff(startTime,'hours'))*(speedGoal/5),speedGoal)) + //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. + //console.log(pointSpeed) + } + finalChart=[...finalChart,{y:Number.isInteger(myPoints)?myPoints:"???",x:moment(startTime)}] + } + predictionChartData[rank]=finalChart + return finalChart + } else { + return [] + } } } @@ -1436,74 +1653,136 @@ function numberWithCommas(x) { } } -function ChartData(rank) { - if (!chartData[rank]) { - return [{x:0,y:0}] - } - if (rank<=20) { - return [...chartData[rank].map((data)=>{return {x:data.date,y:data.points}}),{x:moment().isBefore(EVENTEND)?moment():EVENTEND,y:chartData[rank][chartData[rank].length-1].points}] +function ChartData(rank,en) { + if (en) { + if (!en_chartData[rank]) { + return [{x:0,y:0}] + } + if (rank<=20) { + return [...en_chartData[rank].map((data)=>{return {x:data.date,y:data.points}}),{x:moment().isBefore(EVENTEND)?moment():EVENTEND,y:en_chartData[rank][en_chartData[rank].length-1].points}] + } else { + return [{x:EVENTSTART_en,y:0},...en_chartData[rank].map((data)=>{return {x:data.date,y:data.points}})] + } } else { - return [{x:EVENTSTART,y:0},...chartData[rank].map((data)=>{return {x:data.date,y:data.points}})] + if (!chartData[rank]) { + return [{x:0,y:0}] + } + if (rank<=20) { + return [...chartData[rank].map((data)=>{return {x:data.date,y:data.points}}),{x:moment().isBefore(EVENTEND)?moment():EVENTEND,y:chartData[rank][chartData[rank].length-1].points}] + } else { + return [{x:EVENTSTART,y:0},...chartData[rank].map((data)=>{return {x:data.date,y:data.points}})] + } + } } -function GetRank(rank) { - if (chartData[rank]) { - return chartData[rank][chartData[rank].length-1].points +function GetRank(rank,en) { + if (en) { + if (en_chartData[rank]) { + return en_chartData[rank][en_chartData[rank].length-1].points + } else { + return "???" + } } else { - return "???" + if (chartData[rank]) { + return chartData[rank][chartData[rank].length-1].points + } else { + return "???" + } } } -function GetEstimate(rank) { - if (predictionChartData[rank]) { - var currentEstimate = 0 - if (rank>20 && moment().diff(moment(chartData[rank][chartData[rank].length-1].date),'hours')>0.5) { - for (var i=predictionChartData[rank].length-1;i>=0;i--) { - if (moment(predictionChartData[rank][i].x).isAfter(moment())) { - currentEstimate=predictionChartData[rank][i].y +function GetEstimate(rank,en) { + if (en) { + if (en_predictionChartData[rank]) { + var currentEstimate = 0 + if (rank>20 && moment().diff(moment(en_chartData[rank][en_chartData[rank].length-1].date),'hours')>0.5) { + for (var i=en_predictionChartData[rank].length-1;i>=0;i--) { + if (moment(en_predictionChartData[rank][i].x).isAfter(moment())) { + currentEstimate=en_predictionChartData[rank][i].y + } else { + break; + } + } + + return currentEstimate + } else { + if (rank>20) { + return GetRank(rank,en) } else { - break; + return "---" } } - - return currentEstimate } else { - if (rank>20) { - return GetRank(rank) + return "???" + } + } else { + if (predictionChartData[rank]) { + var currentEstimate = 0 + if (rank>20 && moment().diff(moment(chartData[rank][chartData[rank].length-1].date),'hours')>0.5) { + for (var i=predictionChartData[rank].length-1;i>=0;i--) { + if (moment(predictionChartData[rank][i].x).isAfter(moment())) { + currentEstimate=predictionChartData[rank][i].y + } else { + break; + } + } + + return currentEstimate } else { - return "---" + if (rank>20) { + return GetRank(rank) + } else { + return "---" + } } + } else { + return "???" } - } else { - return "???" } } -function GetTime(rank) { - if (chartData[rank]) { - return moment(chartData[rank][chartData[rank].length-1].date).fromNow() +function GetTime(rank,en) { + if (en) { + if (en_chartData[rank]) { + return moment(en_chartData[rank][en_chartData[rank].length-1].date).fromNow() + } else { + return "" + } } else { - return "" + if (chartData[rank]) { + return moment(chartData[rank][chartData[rank].length-1].date).fromNow() + } else { + return "" + } } } -function GetUpdateColor(rank) { - if (chartData[rank]) { - return "rgba(255,"+Math.max(255-moment().diff(moment(chartData[rank][chartData[rank].length-1].date),'hours')*3,0)+","+Math.max(255-moment().diff(moment(chartData[rank][chartData[rank].length-1].date),'hours')*3,0)+",1)" +function GetUpdateColor(rank,en) { + if (en) { + if (en_chartData[rank]) { + return "rgba(255,"+Math.max(255-moment().diff(moment(en_chartData[rank][en_chartData[rank].length-1].date),'hours')*3,0)+","+Math.max(255-moment().diff(moment(en_chartData[rank][en_chartData[rank].length-1].date),'hours')*3,0)+",1)" + } else { + return "" + } } else { - return "" + if (chartData[rank]) { + return "rgba(255,"+Math.max(255-moment().diff(moment(chartData[rank][chartData[rank].length-1].date),'hours')*3,0)+","+Math.max(255-moment().diff(moment(chartData[rank][chartData[rank].length-1].date),'hours')*3,0)+",1)" + } else { + return "" + } } } var tableValues={} +var en_tableValues={} app.get('/eventdata',function(req,res){ var eventinfo = [] - db.query('select * from event order by id desc limit 1') + db.query('select * from '+(req.query.en?"en_":"")+'event order by id desc limit 1') .then((data)=>{ eventinfo = data.rows; if (!req.query.event) { - return db.query('select distinct on (rank) rank,eventid,date,name,description,points from eventdata where eventid=$1 order by rank,date desc',[moment(eventinfo[0].startdate).isBefore(moment())?eventinfo[0].eventid:eventinfo[0].eventid-1]) + return db.query('select distinct on (rank) rank,eventid,date,name,description,points,difference from (select lead(points) over (partition by rank order by rank,date desc)-points difference,* from '+(req.query.en?"en_":"")+'eventdata where eventid=$1 order by rank,date desc)t order by rank,date desc;',[moment(eventinfo[0].startdate).isBefore(moment())?eventinfo[0].eventid:eventinfo[0].eventid-1]) } else { } @@ -1533,10 +1812,87 @@ app.get('/eventdata',function(req,res){ }) }) +var rankings = { +1:"Maho", +2:"Shinobu", +3:"Muni", +4:"Rei", +5:"Yuka", +6:"Kyoko", +7:"Esoran", +8:"Rinku", +9:"Ibuki", +10:"Esora", +11:"Noa", +12:"Saki", +13:"Towa", +14:"Rika", +15:"Aoi", +16:"Kurumi", +17:"Saori", +18:"Hiiro", +19:"Michiru", +20:"Tsubaki" +} + +var lastRankingUpdate=moment() + +var notPlaying=[ +"Dalia","Marika","Nyochio","Nagisa","Miyu","Haruna","Miiko","Airi","Mana","Shano","Touka" +] + +function RunRankingUpdate() { + if (moment().diff(lastRankingUpdate,'minutes')>=1) { + for (var i=0;i<20;i++) { + //Possibility to switch two positions. + var swap=false + if (i==0) { + if (Math.random()<=0.01) { + swap=true; + } + } else + if (Math.random()<=0.05){ + swap=true; + } + if (swap) { + if (i<19) { + var previousName=rankings[i+1] + var newName=rankings[i+2] + rankings[i+1]=newName + rankings[i+2]=previousName + } else { + //Bring in a new name, swap out the old. + var previousName=rankings[i+1] + var newRandomSlot=Math.floor(Math.random()*notPlaying.length) + var newName=notPlaying[newRandomSlot] + rankings[i+1]=newName + notPlaying[newRandomSlot]=previousName + } + } + } + lastRankingUpdate=moment() + } +} + +app.get('/eventdata/t50',function(req,res){ + var eventinfo = [] + db.query('select * from '+(req.query.en?"en_":"")+'event order by id desc limit 1') + .then((data)=>{ + eventinfo = data.rows; + return db.query('select distinct on (rank) rank,eventid,date,name,description,points,difference from (select lead(points) over (partition by rank order by rank,date desc)-points difference,* from '+(req.query.en?"en_":"")+'eventdata where rank>20 and eventid=$1 order by rank,date desc)t order by rank,date desc;',[moment(eventinfo[0].rank_end).isBefore(moment())?eventinfo[0].eventid:eventinfo[0].eventid-1]) + }) + .then((data)=>{ + return res.status(200).json(data.rows) + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) +}) + app.get('/eventdata/t20',function(req,res){ var eventinfo = [] if (req.query.date&&req.query.rank) { - db.query('select * from eventdata where date<=$1 and rank=$2 and eventid=$3 order by date desc limit 1;',[req.query.date,req.query.rank,10]) + db.query('select * from '+(req.query.en?"en_":"")+'eventdata where date<=$1 and rank=$2 and eventid=$3 order by date desc limit 1;',[req.query.date,req.query.rank,req.query.eventid]) .then((data)=>{ res.status(200).json(data.rows) }) @@ -1545,7 +1901,7 @@ app.get('/eventdata/t20',function(req,res){ }) } else if (req.query.luminous) { - db.query("select * from eventdata where (date>='2021-02-17 23:36:16.383+00' and date<'2021-02-19 15:35:16.716+00' and rank=12) or (date>='2021-02-19 15:35:16.716+00' and rank=11) and eventid=10 order by id asc;") + db.query("select * from "+(req.query.en?"en_":"")+"eventdata where (date>='2021-02-17 23:36:16.383+00' and date<'2021-02-19 15:35:16.716+00' and rank=12) or (date>='2021-02-19 15:35:16.716+00' and rank=11) and eventid=10 order by id asc;") .then((data)=>{ res.status(200).json(data.rows) }) @@ -1554,7 +1910,7 @@ app.get('/eventdata/t20',function(req,res){ }) } else if (req.query.all&&req.query.event) { - db.query('select * from eventdata where eventid=$1 order by date asc;',[req.query.event]) + db.query('select * from (select lag(points) over (partition by rank order by date asc)-points difference,rank,eventid,date,name,points from '+(req.query.en?"en_":"")+'eventdata where eventid=$1 order by date asc)t',[req.query.event]) .then((data)=>{ res.status(200).json(data.rows) }) @@ -1563,7 +1919,7 @@ app.get('/eventdata/t20',function(req,res){ }) } else if (req.query.tier&&req.query.event) { - db.query('select * from eventdata where eventid=$1 and rank=$2 order by date desc',[req.query.event,req.query.tier]) + db.query('select * from (select lag(points) over (partition by rank order by date asc)-points difference,rank,eventid,date,name,points from '+(req.query.en?"en_":"")+'eventdata where eventid=$1 and rank=$2 order by date desc)t',[req.query.event,req.query.tier]) .then((data)=>{ res.status(200).json(data.rows) }) @@ -1572,58 +1928,131 @@ app.get('/eventdata/t20',function(req,res){ }) } else if (req.query.chart){ + + function RandomQuestion() { + var value = Math.random() + if (value<=0.7) { + return "???" + } else + if (value<=0.9) { + return "Miyu" + } else + if (value<=0.95) { + return "Muni" + } else { + return "MuniMuni" + } + } if (req.query.event||moment().diff(lastCachedDate,'minutes')>=1||(req.query.force&&moment().diff(lastCachedDate,'seconds')>=10)) { chartData={} predictionChartData={} diffData=[] - db.query('select * from event order by id desc limit 1') + db.query('select * from '+(req.query.en?"en_":"")+'event order by id desc limit 1') .then((data)=>{ eventinfo = data.rows; - return db.query('select * from eventdata where eventid=$1 order by date asc',[(req.query.event)?req.query.event:eventinfo[0].eventid]) + return db.query('select * from (select lag(points) over (partition by rank order by date asc)-points difference,rank,eventid,date,name,points from '+(req.query.en?"en_":"")+'eventdata where eventid=$1 order by date asc)t',[(req.query.event)?req.query.event:moment(eventinfo[0].rank_end).isBefore(moment())?eventinfo[0].eventid:eventinfo[0].eventid-1]) }) .then((data)=>{ if (data&&data.rows&&data.rows.length>0) { - data.rows.map((obj)=>{if (chartData[obj.rank]) {chartData[obj.rank]=[...chartData[obj.rank],obj]} else {chartData[obj.rank]=[obj]}}) + data.rows.map((obj)=>{ + if (req.query.en) { + if (en_chartData[obj.rank]) {en_chartData[obj.rank]=[...en_chartData[obj.rank],obj]} else {en_chartData[obj.rank]=[obj]} + } else { + if (chartData[obj.rank]) {chartData[obj.rank]=[...chartData[obj.rank],obj]} else {chartData[obj.rank]=[obj]} + } + }) SetupPredictionModel() var tiers= [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,50,100,500,1000,2000,5000,10000,20000,30000,50000] - for (t of tiers) { - CreatePrediction(1,t) - var est = GetEstimate(t) - var temprate = 0 - if (chartData[t]) { - temprate=(t<=20)?(chartData[t])?Math.ceil(GetRate(t)):undefined:Math.ceil(GetRank(t)/(moment(chartData[t][chartData[t].length-1].date).diff(EVENTSTART,'minutes')/60)) + if (req.query.en) { + for (t of tiers) { + CreatePrediction(1,t,req.query.en) + var est = GetEstimate(t,req.query.en) + var temprate = 0 + if (en_chartData[t]) { + temprate=(t<=20)?(en_chartData[t])?Math.ceil(GetRate(t,req.query.en)):undefined:Math.ceil(GetRank(t,req.query.en)/(moment(en_chartData[t][en_chartData[t].length-1].date).diff(EVENTSTART_en,'minutes')/60)) + } + var tempname=""; + if (en_chartData[t]&&en_chartData[t][en_chartData[t].length-1]&&en_chartData[t][en_chartData[t].length-1].name) { + tempname=en_chartData[t][en_chartData[t].length-1].name + } + var tempdesc=""; + if (en_chartData[t]&&en_chartData[t][en_chartData[t].length-1]&&en_chartData[t][en_chartData[t].length-1].description) { + tempdesc=en_chartData[t][en_chartData[t].length-1].description + } + en_tableValues[t]={ + points:GetRank(t,req.query.en), + lastUpdate:GetTime(t,req.query.en), + lastUpdateColor:GetUpdateColor(t,req.query.en), + rate:temprate?temprate:0, + count:GetPointCount(t,req.query.en), + name:tempname, + description:tempdesc, + estimate:Number.isInteger(est)?Math.ceil(est):est, + prediction:(en_predictionChartData[t]&&moment().isBefore(EVENTEND_en))?en_predictionChartData[t][en_predictionChartData[t].length-1].y:RandomQuestion() + } } - tableValues[t]={ - points:GetRank(t), - lastUpdate:GetTime(t), - lastUpdateColor:GetUpdateColor(t), - rate:temprate?temprate:0, - count:GetPointCount(t), - estimate:Number.isInteger(est)?Math.ceil(est):est, - prediction:(predictionChartData[t])?predictionChartData[t][predictionChartData[t].length-1].y:"???" + lastCachedDate=moment() + res.status(200).send({predictionData:en_predictionChartData,statistics:tableValues}) + } else { + for (t of tiers) { + CreatePrediction(1,t,req.query.en) + var est = GetEstimate(t,req.query.en) + var temprate = 0 + if (chartData[t]) { + temprate=(t<=20)?(chartData[t])?Math.ceil(GetRate(t)):undefined:Math.ceil(GetRank(t)/(moment(chartData[t][chartData[t].length-1].date).diff(EVENTSTART,'minutes')/60)) + } + var tempname=""; + if (chartData[t]&&chartData[t][chartData[t].length-1]&&chartData[t][chartData[t].length-1].name) { + tempname=chartData[t][chartData[t].length-1].name + } + var tempdesc=""; + if (chartData[t]&&chartData[t][chartData[t].length-1]&&chartData[t][chartData[t].length-1].description) { + tempdesc=chartData[t][chartData[t].length-1].description + } + tableValues[t]={ + points:GetRank(t), + lastUpdate:GetTime(t), + lastUpdateColor:GetUpdateColor(t), + rate:temprate?temprate:0, + count:GetPointCount(t), + name:tempname, + description:tempdesc, + estimate:Number.isInteger(est)?Math.ceil(est):est, + prediction:(predictionChartData[t]&&moment().isBefore(EVENTEND))?predictionChartData[t][predictionChartData[t].length-1].y:RandomQuestion() + } } + lastCachedDate=moment() + res.status(200).send({predictionData:predictionChartData,statistics:tableValues}) + } - lastCachedDate=moment() - res.status(200).send({predictionData:predictionChartData,statistics:tableValues}) } else { - res.status(200).send({predictionData:predictionChartData,statistics:tableValues}) + if (req.query.en) { + res.status(200).send({predictionData:en_predictionChartData,statistics:en_tableValues}) + } else { + res.status(200).send({predictionData:predictionChartData,statistics:tableValues}) + } } }) .catch((err)=>{ res.status(500).send(err.message) }) } else { - res.status(200).send({predictionData:predictionChartData,statistics:tableValues}) + if (req.query.en) { + res.status(200).send({predictionData:en_predictionChartData,statistics:en_tableValues}) + } else { + res.status(200).send({predictionData:predictionChartData,statistics:tableValues}) + } } } else { db.query('select * from event order by id desc limit 1') .then((data)=>{ eventinfo = data.rows; - return db.query('select distinct on (rank) rank,eventid,date,name,description,points from eventdata where rank<=20 and eventid=$1 order by rank,date desc',[moment(eventinfo[0].startdate).isBefore(moment())?eventinfo[0].eventid:eventinfo[0].eventid-1]) + return db.query('select distinct on (rank) rank,eventid,date,name,description,points,difference from (select lead(points) over (partition by rank order by rank,date desc)-points difference,* from '+(req.query.en?"en_":"")+'eventdata where rank<=20 and eventid=$1 order by rank,date desc)t order by rank,date desc;',[moment(eventinfo[0].rank_end).isBefore(moment())?eventinfo[0].eventid:eventinfo[0].eventid-1]) }) .then((data)=>{ var finaldata = data.rows var tiers= [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] + //RunRankingUpdate() for (t of tiers) { //console.log(t) //if (finaldata[String(t)]===undefined) {finaldata[String(t)]={"rank":t,"eventid":eventinfo[0].eventid,"name":"","description":"","date":eventinfo[0].startdate,"points":0}} @@ -1638,6 +2067,10 @@ app.get('/eventdata/t20',function(req,res){ } else { finaldata[i].rate=temprate } + //finaldata[i].name="Muni"; + /*if (rankings[t]) { + finaldata[i].name=rankings[t] + }*/ break; } } @@ -1645,7 +2078,11 @@ app.get('/eventdata/t20',function(req,res){ finaldata=[...finaldata,{"rate":0,"rank":t,"eventid":eventinfo[0].eventid,"name":"","description":"","date":eventinfo[0].startdate,"points":0}] } } - res.status(200).json(finaldata) + if (req.query.maxspeed) { + res.status(200).json([...finaldata,{maxspeed:MAXSPEED}]) + } else { + res.status(200).json(finaldata) + } }) .catch((err)=>{ res.status(500).send(err.message) @@ -1653,6 +2090,33 @@ app.get('/eventdata/t20',function(req,res){ } }) +app.get('/cans',function(req,res){ + db.query('select (select count(*) from cans where can=true) as cans,(select count(*) from cans where can=false) as notcan;') +.then((data)=>{res.status(200).send(data.rows[0])}) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/ev',function(req,res){ + if (req.query.all) { + db.query('select * from '+(req.query.en?"en_":"")+'event order by id desc;') + .then((data)=>{res.status(200).send(data.rows)}) + .catch((err)=>{res.status(500).send(err.message)}) + } else { + db.query('select * from '+(req.query.en?"en_":"")+'event order by id desc limit 1;') + .then((data)=>{res.status(200).send(data.rows[0])}) + .catch((err)=>{res.status(500).send(err.message)}) + } +}) + +app.post('/cans',function(req,res){ + if (req.body&&(req.body.cans!==undefined&&req.body.cans!==null)) { + db.query('insert into cans(date,can) values($1,$2)',[moment(),req.body.cans]) + .then((data)=>{res.status(200).send("Done!")}) + } else { + res.status(400).send("Invalid credentials!") + } +}) + app.post('/streamkill/:id',function (req,res){ if (req.body&&req.body.username&&req.body.authentication_token) { GetUserLoginAllowed(req.body.username.trim(),req.body.authentication_token.trim()) @@ -1900,4 +2364,4 @@ setInterval( setInterval(()=>{ axios.get("http://www.projectdivar.com/eventdata/t20?chart=true&force=true") -},20000) \ No newline at end of file +},20000) diff --git a/server/app.js.save b/server/app.js.save new file mode 100644 index 0000000..48cd014 --- /dev/null +++ b/server/app.js.save @@ -0,0 +1,1955 @@ +const express = require('express') +const axios = require('axios') +const twitchStreams = require('twitch-get-stream') +const app = express() +const port = 4501 +app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`)) +const bodyParser = require('body-parser') +const { json } = require('body-parser') +const Pool = require('pg').Pool +app.use(bodyParser.json()) +app.use( + bodyParser.urlencoded({ + extended: true, + }) +) +const nodemailer = require('nodemailer'); +const fileUpload = require('express-fileupload'); +const unzipper = require('unzipper'); +const fs = require('fs'); +const moment = require('moment'); +const { exec,spawn } = require('child_process'); +app.use( + fileUpload({createParentPath:true, + limits: { fileSize: 15 * 1024 * 1024, files: 1 }, + safeFileNames: true, preserveExtension: true, + abortOnLimit:true, uploadTimeout:0}) +) +const QuickChart = require('quickchart-js'); + + +let allowCrossDomain = function(req, res, next) { + res.header('Access-Control-Allow-Origin', "*"); + res.header('Access-Control-Allow-Headers', "*"); + res.header('Access-Control-Allow-Methods', "*"); + next(); + } + app.use(allowCrossDomain); + +const db = +new Pool({ + user: 'postgres', + password: '', + host: 'postgres', + database: 'divar', + port: 5432, +}) + +app.get('/song/:songname', (req, res) => { + db.query('select * from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1', [req.params.songname] , (error, results) => { + if (error) { + res.status(500).json(error.message) + } else { + //console.log(req.params.songname+":"+JSON.stringify(results.rows)); + res.status(200).json(results.rows) + } + }) +}) + +app.get('/songs', (req, res) => { + db.query('select songs.*,songdata.rating as rating,songdata.difficulty,songdata.notecount from songs left join songdata on songs.id=songdata.songid' , (error, results) => { + if (error) { + res.status(500).json(error.message) + } else { + //console.log(req.params.songname+":"+JSON.stringify(results.rows)); + //res.status(200).json(results.rows) + var data = {} + results.rows.forEach((song)=>{ + if (data[song.id]) { + if (typeof(data[song.id].rating)==="string"){ + var oldRating = data[song.id].rating; + var oldNoteCount = data[song.id].notecount; + data[song.id].rating={} + data[song.id].notecount={} + data[song.id].rating[data[song.id].difficulty]=oldRating; + data[song.id].notecount[data[song.id].difficulty]=oldNoteCount; + } + data[song.id].rating[song.difficulty]=song.rating; + data[song.id].notecount[song.difficulty]=song.notecount; + } else { + data[song.id]=song + if (data[song.id].rating===null) { + data[song.id].rating={} + } + if (data[song.id].notecount===null) { + data[song.id].notecount={}; + } + }}) + res.status(200).json(data) + } + }) +}) + +/*app.post('/register', (req, res) => { + if (req.body && req.body.username && +req.body.username.length>2 && req.body.email) { + var duplicateFound=false; + db.query('select * from users where username=$1 limit 1',[req.body.username]) + .then((data)=>{ + if (data.rows.length>0) { + throw new Error("User "+data.rows[0].username+" already exists!"); + } else { + return db.query('insert into users(username,email) values($1,$2) returning username,email',[req.body.username,req.body.email]) + } + }) + .then((data)=>{res.status(200).json(data.rows)}) + .catch((err)=>{res.status(500).json(err.message)}) + } else { + res.status(400).json("Invalid username!") + } +})*/ + +app.delete('/remove',(req,res)=>{ + if (req.body && + req.body.username!==undefined && req.body.authentication_token!==undefined && req.body.playid!==undefined) { + var userObj={},songObj={},rating=0,isFirstClear=false; + db.query("select id,authentication_token,playcount,fccount,cool,fine,safe,sad,worst,eclear,nclear,hclear,exclear,exexclear from users where username=$1 limit 1",[req.body.username]) + .then((data)=>{if(data && data.rows.length>0){userObj=data.rows[0];if (req.body.authentication_token===userObj.authentication_token){return db.query("delete from plays where id=$1 and userid=$2 returning *",[req.body.playid,userObj.id])}else{throw new Error("Could not authenticate user!")}}else{throw new Error("Cannot find user!")}}) + .then((data)=>{if(data && data.rows.length>0){songObj=data.rows[0];return CalculateRating(req.body.username)}else{throw new Error("Could not find play!")}}) + .then((data)=>{rating=data;return db.query("select * from plays where songid=$1 and userid=$2 and difficulty=$3 and score>0 limit 1",[songObj.songid,userObj.id,songObj.difficulty])}) + .then((data)=>{if(data && data.rows.length===0){isFirstClear=true;}/*console.log([data,userObj.playcount-1,(songObj.safe==0&&songObj.sad==0&&songObj.worst==0)?userObj.fccount-1:userObj.fccount,userObj.cool-songObj.cool,userObj.fine-songObj.fine,userObj.safe-songObj.safe,userObj.sad-songObj.sad,userObj.worst-songObj.worst,(songObj.difficulty=="E")?userObj.ecount-1:userObj.ecount,(songObj.difficulty=="N")?userObj.ncount-1:userObj.ncount,(songObj.difficulty=="H")?userObj.hcount-1:userObj.hcount,(songObj.difficulty=="EX")?userObj.excount-1:userObj.excount,(songObj.difficulty=="EXEX")?userObj.exexcount-1:userObj.exexcount]);*/return db.query("update users set rating=$1,playcount=$2,fccount=$3,cool=$4,fine=$5,safe=$6,sad=$7,worst=$8,eclear=$9,nclear=$10,hclear=$11,exclear=$12,exexclear=$13 where id=$14 returning rating,playcount,fccount,cool,fine,safe,sad,worst,eclear,nclear,hclear,exclear,exexclear",[rating,userObj.playcount-1,(songObj.safe==0&&songObj.sad==0&&songObj.worst==0)?userObj.fccount-1:userObj.fccount,userObj.cool-songObj.cool,userObj.fine-songObj.fine,userObj.safe-songObj.safe,userObj.sad-songObj.sad,userObj.worst-songObj.worst,(songObj.difficulty=="E" && isFirstClear)?userObj.eclear-1:userObj.eclear,(songObj.difficulty=="N" && isFirstClear)?userObj.nclear-1:userObj.nclear,(songObj.difficulty=="H" && isFirstClear)?userObj.hclear-1:userObj.hclear,(songObj.difficulty=="EX" && isFirstClear)?userObj.exclear-1:userObj.exclear,(songObj.difficulty=="EXEX" && isFirstClear)?userObj.exexclear-1:userObj.exexclear,userObj.id])}) + .then((data)=>{if(data && data.rows.length>0){res.status(200).json({user:data.rows[0],song:songObj}) + axios.post("http://projectdivar.com/updates/"+userObj.id,{password:process.env.GMAIL,type:"delete"}) + }else{throw new Error("Could not update user information, but song is deleted!")}}) + .catch((err)=>{res.status(500).json(err.message)}) + } else { + res.status(400).send("Missing required parameters!"); + } +}) + +app.post('/upload', function(req, res) { + if (!req.files || Object.keys(req.files).length === 0 || req.body.username===undefined || req.body.authentication_token===undefined) { + res.status(400).send('No files were uploaded. Invalid parameters.'); + return; + } + + let file = req.files.file; + //console.log(file) + + if (file.size>15*1024*1024) { + res.status(400).send("File is too large! Max is 15MB! Consider splitting your plays into chunks (Recommended 50 files per zip).") + return; + } + + if (file.mimetype!=="application/x-zip-compressed" && + file.mimetype!=="image/jpeg" && file.mimetype!=="image/png" && file.mimetype!=="application/octet-stream") { + return; + } + + var uploads = 0; + var userId = -1; + var fileLoc = ""; + var count=0; + db.query("select uploads,id from users where username=$1 and authentication_token=$2",[req.body.username,req.body.authentication_token]) + .then((data)=>{ + if (data.rows.length>0) { + uploads=data.rows[0].uploads; + userId=data.rows[0].id; + fileLoc = "files/plays/"+userId+"/"+uploads; + return file.mv(fileLoc); + } else { + throw new Error("Could not find / authenticate user with name "+req.body.username+"!") + } + }) + .then((data)=>{ + if (file.mimetype!=="application/x-zip-compressed") { + return db.query("update users set uploads=$1 where username=$2",[Number(uploads)+1,req.body.username]) + } else { + //console.log(uploads) + uploads++; + //console.log(uploads) + return {} + }}) + .then((data)=>{ + if (file.mimetype!=="application/x-zip-compressed") { + if (req.body.playid!==undefined) { + //Add the url to that play. + return db.query("update plays set src=$1 where id=$2 and userid=$3",["http://projectdivar.com/"+fileLoc,Number(req.body.playid),userId]) + } else { + return axios.post("http://projectdivar.com/image",{url:"http://projectdivar.com/"+fileLoc,user:req.body.username,auth:req.body.authentication_token}) + } + } else { + //This is a zip file. + var promises = [] + promises.push(new Promise((resolve)=>{ + var stream = fs.createReadStream(fileLoc) + stream + .pipe(unzipper.Parse()) + .on('entry', function (entry) { + const fileName = entry.path; + const type = entry.type; // 'Directory' or 'File' + const size = entry.vars.uncompressedSize; // There is also compressedSize; + if (type=="File" + && (fileName.includes(".jpg") || fileName.includes(".jpeg") || fileName.includes(".png"))) { + promises.push(new Promise((resolve)=>{var file="files/plays/"+userId+"/"+uploads++;entry.pipe(fs.createWriteStream(file)) + .on('finish',()=>{/*console.log("Promise finished!");*/count++;resolve({file:"http://projectdivar.com/"+file})}) + }) + .then((data)=>{return db.query("insert into uploadedplays values($1,$2,$3)",[data.file,userId,new Date()]);})) + } else { + entry.autodrain(); + } + }) + .on('finish',()=>{/*console.log("Read finished");*/resolve()}) + })) + setTimeout(()=>(console.dir(promises)),5000) + return Promise.all(promises) + }}) + .then((data)=>{ + if (file.mimetype!=="application/x-zip-compressed") { + res.status(200).send("Your play has been submitted! Thank you."); + } else { + //console.log(data) + //console.log(uploads)) + fs.unlink(fileLoc,(err)=>{}) + return db.query("update users set uploads=$1 where username=$2",[Number(uploads)+1,req.body.username]) + } + }) + .then((data)=>{ + if (file.mimetype!=="application/x-zip-compressed") { + } else { + res.status(200).send("Submitted "+count+" plays to the submission system. They will be processed shortly! Thank you.") + } + }) + .catch((err)=>{console.log(err.message);res.status(500).send(err.message)}) +}); + +app.post('/submit', (req, res) => { + function addToQueue(src,userid,songid) { + if (src) { + db.query("select * from uploadedplays where filename=$1",[req.body.src]) + .then((data)=>{ + if (data.rows.length>0) { + if (data.rows[0].tries>-5) { + db.query("update uploadedplays set filename=$1,userid=$2,submissiondate=$3,id=$4,tries=$5 returning *", + [src,userid,moment(),songid,(data.rows[0].tries*-1)+1]) + .then((data)=>{ + if (data.rows.length>0) { + console.log("Added back to queue: "+JSON.stringify(data.rows[0])) + } + }) + } + } + }) + } + } + + if (req.body && + req.body.username!==undefined && req.body.authentication_token!==undefined && req.body.song!==undefined && req.body.difficulty!==undefined && req.body.cool!==undefined && req.body.fine!==undefined && req.body.safe!==undefined && req.body.sad!==undefined && req.body.worst!==undefined && req.body.fail!==undefined && req.body.percent!==undefined) { + + if (req.body.cool==-1||req.body.fine==-1||req.body.safe==-1||req.body.sad==-1||req.body.worst==-1) { + fs.writeFileSync("invalidSongs",JSON.stringify(req.body)+"\n",{flag:"a"}); + res.status(400).json("Invalid note parameters!"); + } + + var fail = true; + if (req.body.fail!==undefined) { + fail = (req.body.fail=='true'); + //console.log("Fail is "+fail+" type:"+typeof(fail)) + } + var submitDate = new Date(); + if (req.body.submitDate!==undefined) { + submitDate=req.body.submitDate; + } + var playstyle="",songsubmitdata={},mod="",combo=-1,gameScore=-1,isFC=false,songRating=-1,userId = -1,songId=-1,playcount=-1,fccount=-1,cool=-1,fine=-1,safe=-1,sad=-1,worst=-1,alreadyPassed=false,eclear=-1,nclear=-1,hclear=-1,exclear=-1,exexclear=-1; + var songdata={},userObj={}; + if (req.body.mod!==undefined) { + mod = req.body.mod; + } + if (req.body.combo!==undefined) { + combo = req.body.combo; + } + if (req.body.gameScore!==undefined) { + gameScore = req.body.gameScore; + } + + db.query("select id,authentication_token,playcount,fccount,cool,fine,safe,sad,worst,eclear,nclear,hclear,exclear,exexclear,playstyle,megamix,futuretone from users where username=$1 limit 1",[req.body.username]) + .then((data)=>{if(data && data.rows.length>0){if (data.rows[0].authentication_token===req.body.authentication_token){ + var obj = data.rows[0]; + userObj = data.rows[0]; + playstyle=data.rows[0].playstyle; + eclear=obj.eclear;nclear=obj.nclear;hclear=obj.hclear;exclear=obj.exclear;exexclear=obj.exexclear; + cool=data.rows[0].cool;fine=data.rows[0].fine;safe=data.rows[0].safe;sad=data.rows[0].sad;worst=data.rows[0].worst; + fccount=data.rows[0].fccount;playcount=data.rows[0].playcount;userId=data.rows[0].id;return db.query("select id,mega39s,futuretone from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1",[req.body.song])}else{throw new Error("Could not authenticate!")}}else{console.log(req.body); throw new Error("Could not find user.")} + }) + .then((data)=>{if(data && data.rows.length>0){songId=data.rows[0].id; + songdata=data.rows[0]; + if (!(req.body.difficulty==="H"||req.body.difficulty==="N"||req.body.difficulty==="E"||req.body.difficulty==="EX"||req.body.difficulty==="EXEX")) + { + throw new Error("Invalid difficulty!") + } + if (req.body.cool==-1||req.body.fine==-1||req.body.safe==-1||req.body.sad==-1||req.body.worst==-1) { + throw new Error("Invalid submission!") + } + return db.query('select rating from songdata where songid=$1 and difficulty=$2 limit 1',[songId,req.body.difficulty])}else{throw new Error("Could not find song.")}}) + .then((data)=>{songRating=data.rows[0].rating;return db.query("select id from plays where userid=$1 and score>0 and difficulty=$2 and songid=$3 limit 1",[userId,req.body.difficulty,songId])}) + .then((data)=>{if(data && data.rows.length>0){alreadyPassed=true;/*console.log(data);*/};var score=CalculateSongScore({rating:songRating,cool:req.body.cool,fine:req.body.fine,safe:req.body.safe,sad:req.body.sad,worst:req.body.worst,percent:req.body.percent,difficulty:req.body.difficulty,fail:fail});return db.query("insert into plays(songId,userId,difficulty,cool,fine,safe,sad,worst,percent,date,score,fail,mod,combo,gamescore,src,playstyle) values($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17) returning *",[songId,userId,req.body.difficulty,req.body.cool,req.body.fine,req.body.safe,req.body.sad,req.body.worst,req.body.percent,submitDate,score,fail,mod,combo,gameScore,(req.body.src)?req.body.src:"",(playstyle)?playstyle:""])}) + .then((data)=>{if(data && data.rows.length>0){ + songsubmitdata = data.rows[0]; + //console.log(alreadyPassed+" / "+typeof(alreadyPassed)) + if(alreadyPassed===false && songsubmitdata.score>0){switch(req.body.difficulty){case"E":{eclear++}break;case"N":{nclear++}break;case"H":{hclear++}break;case"EX":{exclear++}break;case"EXEX":{exexclear++}break;}} + isFC = songsubmitdata.safe===0 && songsubmitdata.sad===0 && songsubmitdata.worst===0; + return CalculateRating(req.body.username)}else{throw new Error("Could not submit song.")}}) + .then((data)=>{return db.query("update users set rating=$1,last_played=$3,playcount=$4,fccount=$5,cool=$6,fine=$7,safe=$8,sad=$9,worst=$10,eclear=$11,nclear=$12,hclear=$13,exclear=$14,exexclear=$15,megamix=$16,futuretone=$17 where username=$2",[data,req.body.username,new Date(),++playcount,fccount+((isFC)?1:0),cool+Number(req.body.cool),fine+Number(req.body.fine),safe+Number(req.body.safe),sad+Number(req.body.sad),worst+Number(req.body.worst),eclear,nclear,hclear,exclear,exexclear,(songdata.mega39s||userObj.megamix),(songdata.futuretone&&!songdata.mega39s)||userObj.futuretone])}) + .then((data)=>{return songsubmitdata;}) + .then((data)=>{ + //userId + //password, type + axios.post("http://projectdivar.com/updates/"+userId,{password:process.env.GMAIL,type:"submit"}) + if (req.body.src) { + db.query("delete from uploadedplays where filename=$1",[req.body.src]) + } + res.status(200).json(data); + }) + .catch((err)=>{ + //console.log(req.body); + console.log(err); + addToQueue(req.body.src,userId,songId) + res.status(500).send(err.message);}) + } else { + console.log(req.body); + res.status(400).send("Missing required parameters!"); + } +}) + +CalculateSongScore=(song)=>{ + if (song.fail==true){return 0;} + var noteCount=Number(song.cool)+Number(song.fine)+Number(song.safe)+Number(song.sad)+Number(song.worst); + var comboBreaks=Number(song.safe)+Number(song.sad)+Number(song.worst); + /*console.log("Combo Breaks: "+comboBreaks) + console.log("Is FC? "+(comboBreaks===0)) + console.log("Is PFC? "+(song.fine===0&&song.safe===0&&song.sad===0&&song.worst===0))*/ + var scoreMult=1; + var percentMult=1; + if (song.percent>110) { + song.percent=100 + } + if (song.fine===0&&song.safe===0&&song.sad===0&&song.worst===0){scoreMult=2.4}else if(comboBreaks===0){scoreMult=1.4}else{scoreMult=1} + switch (song.difficulty){ + case "E":{if(song.percent<30){percentMult=0;}else{percentMult=1+Math.pow(1.0*((song.percent-30)/70.0),3)}}break; + case "N":{if(song.percent<50){percentMult=0}else{percentMult=1+Math.pow(1.0*((song.percent-50)/50.0),3)}}break; + case "H":{if(song.percent<60){percentMult=0}else{percentMult=1+Math.pow(1.0*((song.percent-60)/40.0),3)}}break; + case "EX": + case "EXEX":{if(song.percent<70){percentMult=0}else{percentMult=1+Math.pow(1.0*((song.percent-70)/30.0),3)}}break; + default:{ + if(song.percent<60){percentMult=0}else{percentMult=1+(Math.pow(1.0*((song.percent-60)/40.0),3))} + } + } + /*console.log("Score mult: "+scoreMult) + console.log("Percent mult: "+percentMult)*/ + var score = ((song.cool*100+song.fine*50+song.safe*10+song.sad*5)/1000.0)*percentMult*scoreMult + if (scoreMult>0 && percentMult>0) { + score += Math.pow(song.rating,3)/5 + } + return Number(score); +} + +CalculateAccuracy=(cool,fine,safe,sad,worst)=>{ + var noteCount = cool+fine+safe+sad+worst; + var sum = cool+(fine*0.5)+(safe*0.1)+(sad*0.05); + return sum/noteCount; +} + +CalculateRating=(username)=>{ + var songs = []; + var debugScoreList = ""; + var userId = -1; + /* //Old rating algorithm. + return db.query('select id from users where username=$1',[username]) + .then((data)=>{if(data.rows.length>0){userId=data.rows[0].id;return db.query('select * from plays where userid=$1 order by score desc limit 100',[userId])}else{return 0}}) + .then((data)=>{if(data.rows.length>0){return data.rows.reduce((sum,song,i)=>{ + return sum+Number(CalculateSongScore(song)*(Math.pow(0.8,i)))},0)}else{return 0}}) + .catch((err)=>{throw new Error(err.message)})*/ + return db.query('select id from users where username=$1 limit 1',[username]) + .then((data)=>{if(data.rows.length>0){userId=data.rows[0].id;return db.query('select * from songs order by id asc')}else{return 0}}) + .then((data)=>{if(data.rows.length>0){songs=data.rows;return Promise.all(data.rows.map((song)=>{return db.query('select * from plays where userId=$1 and songId=$2 and score!=$3 order by score desc limit 100',[userId,song.id,"NaN"]).then((data)=>{if (data.rows.length>0){debugScoreList+=song.name+"\n"; songs[song.id-1].score=data.rows.reduce((sum,play,i)=>{debugScoreList+=" "+(play.score)+" -> "+(play.score*Math.pow(0.2,i))+"";if (i===0 && play.fine+play.safe+play.sad+play.worst===0){songs[play.songid-1].pfc=true;debugScoreList+="+"}else if (i===0 && play.safe+play.sad+play.worst===0){songs[play.songid-1].fc=true;debugScoreList+="*"}debugScoreList+="\n";/*console.log("Play score:"+play.score+". Sum:"+sum);*/return sum+play.score*Math.pow(0.2,i);},0);debugScoreList+=" "+songs[song.id-1].score+"\n";}})}))}}) + .then(()=>{return songs.sort((a,b)=>{var scorea=(a.score)?a.score:0;var scoreb=(b.score)?b.score:0;return (scorea>scoreb)?-1:1;}).reduce((sum,song,i)=>{if(song.score && !isNaN(song.score)){debugScoreList+=song.name+": "+song.score+" -> "+(song.score*Math.pow(0.9,i))+((song.pfc)?("+"+2):(song.fc)?("+"+1):0)+"\n";return sum+song.score*Math.pow(0.9,i)+((song.pfc)?2:(song.fc)?+1:0)}else{return sum}},0);}) + .then((data)=>{/*console.log(debugScoreList);*/return data}) +} + +app.get('/songdiffs',(req,res)=>{ + var diffObj={} + db.query("select COUNT(*) from songdata where difficulty='E'") + .then((data)=>{diffObj.E=data.rows[0].count;return db.query("select COUNT(*) from songdata where difficulty='N'")}) + .then((data)=>{diffObj.N=data.rows[0].count;return db.query("select COUNT(*) from songdata where difficulty='H'")}) + .then((data)=>{diffObj.H=data.rows[0].count;return db.query("select COUNT(*) from songdata where difficulty='EX'")}) + .then((data)=>{diffObj.EX=data.rows[0].count;return db.query("select COUNT(*) from songdata where difficulty='EXEX'")}) + .then((data)=>{diffObj.EXEX=data.rows[0].count;res.status(200).json(diffObj)}) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/accuracy/:username',(req,res)=>{ + db.query('select cool,fine,safe,sad,worst from users where username=$1',[req.params.username]) + .then((data)=>{if (data.rows.length>0){return CalculateAccuracy(data.rows[0].cool,data.rows[0].fine,data.rows[0].safe,data.rows[0].sad,data.rows[0].worst)}else{throw new Error("User does not exist!")}}) + .then((data)=>{res.status(200).json({accuracy:data})}) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/updates/:userid',(req,res)=>{ + db.query('select * from userupdate where userid=$1',[req.params.userid]) + .then((data)=>{ + if (data.rows.length>0) { + res.status(200).json(data.rows[0]) + } else { + res.status(400).send("No user update found!") + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) +}) + +app.post('/updates/:userid',(req,res)=>{ + if (req.body&&req.body.password&&req.body.type) { + if (req.body.password===process.env.GMAIL) { + db.query("insert into userupdate(userid,update_type,date) values($1,$2,$3) on conflict(userid) do update set update_type=$2,date=$3 where userupdate.userid=$1 returning *",[req.params.userid,req.body.type,moment()]) + .then((data)=>{ + if (data.rows.length>=0) { + res.status(200).send(data.rows[0]) + } else { + throw new Error("Could not update user.") + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(403).send("Could not authenticate") + } + } else { + res.status(400).send("Invalid credentials!") + } +}) + +app.get('/recalculatescore/:playid',(req,res)=>{ + var userId=-1; + var username=null; + var songRating=-1; + var song; + + db.query('select * from plays where id=$1 limit 1',[req.params.playid]) + .then((data)=>{if (data.rows.length>0){song=data.rows[0];userId=song.userid;/*console.log(song);*/ + return db.query('select rating from songdata where songid=$1 and difficulty=$2 limit 1',[song.songid,song.difficulty]) + }else{throw new Error("This play does not exist!")}}) + .then((data)=>{if (data.rows.length>0){songRating=data.rows[0].rating;var score=CalculateSongScore({rating:songRating,cool:song.cool,fine:song.fine,safe:song.safe,sad:song.sad,worst:song.worst,percent:song.percent,difficulty:song.difficulty,fail:song.fail});return db.query('update plays set score=$1 where id=$2 returning *',[score,req.params.playid]);}else{throw new Error("Failed to retrieve song data!")}}) + .then((data)=>{//console.log(data); + if (data.rows.length>0){ + var scoreData=data.rows[0]; + return db.query('select username from users where id=$1',[userId]) + .then((data)=>{username=data.rows[0].username; return CalculateRating(username)}) + .then((data)=>{db.query("update users set rating=$1 where username=$2",[data,username])}) + .then(()=>{return scoreData;}) + .catch((err)=>{ + throw new Error("Could not update score"); + }) + }else{throw new Error("Failed to update score!")}}) + .then((data)=>{ + res.status(200).json(data) + axios.post("http://projectdivar.com/updates/"+userId,{password:process.env.GMAIL,type:"recalculate"}) + }) + .catch((err)=>{console.log(err);res.status(500).send(err.message);}) +}); + +/* +app.get('/playdata',(req,res)=>{ + db.query('select * from plays') + .then((data)=>{res.status(200).json(data.rows)}) + .catch((err)=>res.status(500).json(err.message)) +})*/ + +app.post('/recalculatePlayerData/:id',(req,res)=>{ + if (req.body&&req.body.password) { + if (req.body.password===process.env.GMAIL) { + db.query(" select COUNT(*) as plays,SUM(cool) as coolsum,SUM(fine) as finesum,SUM(safe) as safesum,SUM(sad) as sadsum,SUM(worst) as worstsum,COUNT(*) filter(where safe=0 and sad=0 and worst=0) as fc from plays where userid=$1",[req.params.id]) + .then((data)=>{ + var d = data.rows[0]; + return db.query("update users set playcount=$1,fccount=$2,cool=$3,fine=$4,safe=$5,sad=$6,worst=$7 where id=$8 returning playcount,fccount,cool,fine,safe,sad,worst,id", + [d.plays,d.fc,(d.coolsum!==null)?d.coolsum:0,(d.finesum!==null)?d.finesum:0,(d.safesum!==null)?d.safesum:0,(d.sadsum!==null)?d.sadsum:0,(d.worstsum!==null)?d.worstsum:0,req.params.id]) + } + ) + .then((data)=>{ + res.status(200).json(data.rows[0]) + }) + } + } +}) + +app.get('/completionreport/:username',(req,res)=>{ + //Number of passes,plays,fcs,pfcs, and the best play. + var userId=-1,songs,promises=[]; + db.query('select id from users where username=$1 limit 1',[req.params.username]) + .then ((data)=>{ + if (data.rows.length>0) { + userId=data.rows[0].id; + return db.query('select * from songs order by id asc') + } else { + throw new Error("Cannot find user!") + } + }) + .then((data)=>{ + songs = data.rows; + songs.forEach((song)=>{ + promises.push(db.query("select * from (select userid,count(*) filter(where difficulty='E' and mod='SD' and score>0) as ESDCount,count(*) filter(where difficulty='N' and mod='SD' and score>0) as NSDCount,count(*) filter(where difficulty='H' and mod='SD' and score>0) as HSDCount,count(*) filter(where difficulty='EX' and mod='SD' and score>0) as EXSDCount,count(*) filter(where difficulty='EXEX' and mod='SD' and score>0) as EXEXSDCount,count(*) filter(where difficulty='E' and mod='HD' and score>0) as EHDCount,count(*) filter(where difficulty='N' and mod='HD' and score>0) as NHDCount,count(*) filter(where difficulty='H' and mod='HD' and score>0) as HHDCount,count(*) filter(where difficulty='EX' and mod='HD' and score>0) as EXHDCount,count(*) filter(where difficulty='EXEX' and mod='HD' and score>0) as EXEXHDCount,count(*) filter(where difficulty='E' and mod='HS' and score>0) as EHSCount,count(*) filter(where difficulty='N' and mod='HS' and score>0) as NHSCount,count(*) filter(where difficulty='H' and mod='HS' and score>0) as HHSCount,count(*) filter(where difficulty='EX' and mod='HS' and score>0) as EXHSCount,count(*) filter(where difficulty='EXEX' and mod='HS' and score>0) as EXEXHSCount,Count(*) filter(where difficulty='E' and score>0) as EClearCount,Count(*) filter(where difficulty='N' and score>0) as NClearCount,Count(*) filter(where difficulty='H' and score>0) as HClearCount,Count(*) filter(where difficulty='EX' and score>0) as EXClearCount,Count(*) filter(where difficulty='EXEX' and score>0) as EXEXClearCount,count(*) filter(where difficulty='E') as ECount,count(*) filter(where difficulty='N') as NCount,count(*) filter(where difficulty='H') as HCount,count(*) filter(where difficulty='EX') as EXCount,count(*) filter(where difficulty='EXEX') as EXEXCount,Count(*) filter(where safe=0 and sad=0 and worst=0 and difficulty='E') as EFCCount,Count(*) filter(where safe=0 and sad=0 and worst=0 and difficulty='N') as NFCCount,Count(*) filter(where safe=0 and sad=0 and worst=0 and difficulty='H') as HFCCount,Count(*) filter(where safe=0 and sad=0 and worst=0 and difficulty='EX') as EXFCCount,Count(*) filter(where safe=0 and sad=0 and worst=0 and difficulty='EXEX') as EXEXFCCount,Count(*) filter(where fine=0 and safe=0 and sad=0 and worst=0 and difficulty='E') as EPFCCount,Count(*) filter(where fine=0 and safe=0 and sad=0 and worst=0 and difficulty='N') as NPFCCount,Count(*) filter(where fine=0 and safe=0 and sad=0 and worst=0 and difficulty='H') as HPFCCount,Count(*) filter(where fine=0 and safe=0 and sad=0 and worst=0 and difficulty='EX') as EXPFCCount,Count(*) filter(where fine=0 and safe=0 and sad=0 and worst=0 and difficulty='EXEX') as EXEXPFCCount from plays where userid=$1 and songid=$2 group by userid)t1 join (select rank,t.score,t.percent from (select row_number()over(order by score desc)rank,* from(select distinct on (userid) * from (select * from plays where songid=$2)t order by userid,score desc)t)t where userid=$1)t2 on t1.userid=t1.userid",[userId,song.id]) + .then((data)=>{ + if (data.rows.length>0) { + song.report=data.rows[0] + } else { + song.report={ecount:0,ncount:0,hcount:0,excount:0,exexcount:0,efccount:0,nfccount:0,hfccount:0,exfccount:0,exexfccount:0,epfccount:0,npfccount:0,hpfccount:0,expfccount:0,exexpfccount:0,rank:0} + } + }) + ) + }) + return Promise.all(promises) + }) + .then((data)=>{ + return res.status(200).json(songs); + }) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/ratings/:songname/:username',(req,res)=>{ + var songId=-1,userId=-1; + db.query('select id from users where username=$1 limit 1', [req.params.username]) + .then((data)=>{ + if (data.rows.length>0) { + userId=data.rows[0].id + return db.query('select id from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1', [req.params.songname]) + } else { + throw new Error("Could not find user!") + } + }) + .then((data)=>{ + if (data.rows.length>0) { + songId=data.rows[0].id + return db.query("select * from (select row_number()over(order by score desc)rank,* from(select distinct on (userid) * from (select * from plays where songid=$1)t order by userid,score desc)t)t where userid=$2",[songId,userId]) + } else { + throw new Error("Could not find song!") + } + }) + .then((data)=>{ + res.status(200).json(data.rows) + }) + .catch((err)=>{res.status(500).send(err.message)}) + +}) + +app.get('/bestplays/:username',(req,res)=>{ + var songId=-1,userId=-1,songPromises=[],plays=[],limit=5,offset=0; + if (req.query.limit) {limit=req.query.limit} + if (req.query.offset) {offset=req.query.offset} + db.query('select id from users where username=$1 limit 1',[req.params.username]) + .then((data)=>{if (data.rows.length>0){userId=data.rows[0].id;return db.query("select * from (select distinct on (songid) * from plays where userid=$1 "+((req.query.fails==="false")?"and score!=0":"")+" order by songid,score desc)t order by score desc limit $2 offset $3",[userId,Number(limit),Number(offset)])}else{throw new Error("Cannot find user!")}}) + .then((data)=>{ + res.status(200).json(data.rows) + }) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/bestplay/:username/:songname/:difficulty',(req,res)=>{ + var songId=-1,userId=-1; + db.query('select id from users where username=$1 limit 1',[req.params.username]) + .then((data)=>{if (data.rows.length>0){userId=data.rows[0].id;if(req.params.songname){return db.query('select id from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1', [req.params.songname])}else{return db.query('select * from plays where userid=$1 order by score desc',[userId])}}else{throw new Error("Cannot find user!")}}) + .then((data)=>{if(req.params.songname &&data.rows.length>0){songId=data.rows[0].id;return db.query('select * from plays where userid=$1 and songid=$2 and difficulty=$3 order by score desc,percent desc limit 1',[userId,songId,req.params.difficulty])}else{res.status(400).send("Could not find song!")}}) + .then((data)=>{if(data && data.rows.length>0){res.status(200).json(data.rows[0])}else{res.status(400).send("No data found!")}}) + .catch((err)=>{res.status(500).send(err.message+JSON.stringify(req.body))}) +}) + +app.get('/userdata/:username',(req,res)=>{ + var songId=-1,userId=-1,finalData={}; + db.query('select id,megamix,futuretone,playstyle,playcount,fccount,rating,last_played,cool,fine,safe,sad,worst,eclear,nclear,hclear,exclear,exexclear from users where username=$1 limit 1',[req.params.username]) + .then((data)=>{if(data && data.rows.length>0){finalData=data.rows[0];return db.query("select t.difficulty,COUNT(t.difficulty) from (select distinct on(songid) songid,*,users.id from plays join users on userid=users.id where users.username=$1 and plays.safe=0 and plays.worst=0 and plays.sad=0)t group by t.difficulty",[req.params.username])}else{throw new Error("Could not retrieve user data!")}}) + .then((data)=>{ + if (data) { + var fcData={} + data.rows.forEach((fc)=>{fcData[fc.difficulty]=fc.count}) + finalData={...{fcdata:fcData},...finalData} + return db.query("select t.difficulty,COUNT(t.difficulty) from (select distinct on(songid) songid,*,users.id from plays join users on userid=users.id where users.username=$1 and plays.fine=0 and plays.safe=0 and plays.worst=0 and plays.sad=0)t group by t.difficulty",[req.params.username]) + }else{throw new Error("Could not retrieve user data!")} + }) + .then((data)=>{ + if (data) { + var fcData={} + data.rows.forEach((fc)=>{fcData[fc.difficulty]=fc.count}) + finalData={...{pfcdata:fcData},...finalData} + res.status(200).json(finalData) + }else{throw new Error("Could not retrieve user data!")} + }) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/plays/:username/:songid',(req,res)=>{ + var limit=5,offset=0; + if (req.query.limit) {limit=req.query.limit} + if (req.query.offset) {offset=req.query.offset} + db.query("select plays.* from plays join users on users.id=plays.userid where users.username=$1 and plays.songid=$2 order by score desc,percent desc,date desc limit $3 offset $4",[req.params.username,req.params.songid,Number(limit),Number(offset)]) + .then((data)=>{ + res.status(200).json(data.rows) + }) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/playcount/:username/:songname/:difficulty',(req,res)=>{ + var songId=-1,userId=-1; + db.query('select id from users where username=$1 limit 1',[req.params.username]) + .then((data)=>{if (data.rows.length>0){userId=data.rows[0].id;return db.query('select id from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1', [req.params.songname])}else{throw new Error("Cannot find user!")}}) + .then((data)=>{if(req.params.songname &&data.rows.length>0){songId=data.rows[0].id;return db.query('select * from plays where userid=$1 and songid=$2 and difficulty=$3 order by score desc',[userId,songId,req.params.difficulty])}else{res.status(400).send("Could not find song!")}}) + .then((data)=>{if(data && data.rows.length>0){res.status(200).json({playcount:data.rows.length})}else{res.status(200).json({playcount:0})}}) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/songpasscount/:username/:songname/:difficulty',(req,res)=>{ + var songId=-1,userId=-1; + db.query('select id from users where username=$1 limit 1',[req.params.username]) + .then((data)=>{if (data.rows.length>0){userId=data.rows[0].id;return db.query('select id from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1', [req.params.songname])}else{throw new Error("Cannot find user!")}}) + .then((data)=>{if(req.params.songname &&data.rows.length>0){songId=data.rows[0].id;return db.query('select * from plays where userid=$1 and songid=$2 and difficulty=$3 and score>0',[userId,songId,req.params.difficulty])}else{res.status(400).send("Could not find song!")}}) + .then((data)=>{if(data && data.rows.length>0){res.status(200).json({passcount:data.rows.length})}else{res.status(200).json({passcount:0})}}) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/songfccount/:username/:songname/:difficulty',(req,res)=>{ + var songId=-1,userId=-1; + db.query('select id from users where username=$1 limit 1',[req.params.username]) + .then((data)=>{if (data.rows.length>0){userId=data.rows[0].id;return db.query('select id from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1', [req.params.songname])}else{throw new Error("Cannot find user!")}}) + .then((data)=>{if(req.params.songname &&data.rows.length>0){songId=data.rows[0].id;return db.query('select * from plays where userid=$1 and songid=$2 and difficulty=$3 and safe=0 and sad=0 and worst=0',[userId,songId,req.params.difficulty])}else{res.status(400).send("Could not find song!")}}) + .then((data)=>{if(data && data.rows.length>0){res.status(200).json({fccount:data.rows.length})}else{res.status(200).json({fccount:0})}}) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/songpfccount/:username/:songname/:difficulty',(req,res)=>{ + var songId=-1,userId=-1; + db.query('select id from users where username=$1 limit 1',[req.params.username]) + .then((data)=>{if (data.rows.length>0){userId=data.rows[0].id;return db.query('select id from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1', [req.params.songname])}else{throw new Error("Cannot find user!")}}) + .then((data)=>{if(req.params.songname &&data.rows.length>0){songId=data.rows[0].id;return db.query('select * from plays where userid=$1 and songid=$2 and difficulty=$3 and fine=0 and safe=0 and sad=0 and worst=0',[userId,songId,req.params.difficulty])}else{res.status(400).send("Could not find song!")}}) + .then((data)=>{if(data && data.rows.length>0){res.status(200).json({fccount:data.rows.length})}else{res.status(200).json({fccount:0})}}) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/songmods/:username/:songname/:difficulty',(req,res)=>{ + var songId=-1,userId=-1,hs=0,sd=0,hd=0; + db.query('select id from users where username=$1 limit 1',[req.params.username]) + .then((data)=>{if (data.rows.length>0){userId=data.rows[0].id;return db.query('select id from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1', [req.params.songname])}else{throw new Error("Cannot find user!")}}) + .then((data)=>{if(req.params.songname &&data.rows.length>0){songId=data.rows[0].id;return db.query('select COUNT(mod) from (select * from plays where userid=$1 and songid=$2 and difficulty=$3 and mod=$4)t',[userId,songId,req.params.difficulty,"HS"])}else{res.status(400).send("Could not find song!")}}) + .then((data)=>{if(data && data.rows.length>0){ + hs=data.rows[0].count; + } + return db.query('select COUNT(mod) from (select * from plays where userid=$1 and songid=$2 and difficulty=$3 and mod=$4)t',[userId,songId,req.params.difficulty,"SD"]) + }) + .then((data)=>{if(data && data.rows.length>0){ + sd=data.rows[0].count; + } + return db.query('select COUNT(mod) from (select * from plays where userid=$1 and songid=$2 and difficulty=$3 and mod=$4)t',[userId,songId,req.params.difficulty,"HD"]) + }) + .then((data)=>{if(data && data.rows.length>0){ + hd=data.rows[0].count; + } + res.status(200).json({hs:hs,sd:sd,hd:hd}) + }) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.get('/rating/:username',(req,res)=>{ + if (req.params.username) { + db.query('select rating from users where username=$1 limit 1',[req.params.username]) + .then((data)=>{if(data.rows.length>0){res.status(200).json(data.rows[0])}else{res.status(200).json({rating:0})}}) + } else { + res.status(400).send("Invalid username!") + } +}) + +app.get('/recentplays/:username',(req,res)=>{ + if (req.params.username) { + db.query('select plays.* from plays join users on users.id=plays.userid where users.username=$1 order by plays.id desc limit 5',[req.params.username]) + .then((data)=>{if(data.rows.length>0){res.status(200).json(data.rows)}else{res.status(200).json([])}}) + } else { + res.status(400).send("Invalid username!") + } +}) + +app.get('/users/:orderby/:sortorder',(req,res)=>{ + if (req.params.orderby && req.params.sortorder && req.query.limit && req.query.offset) { + var valid = ["rating","last_played","playcount","username","fccount"]; + var validsort = ["desc","asc"]; + if (valid.includes(req.params.orderby) && validsort.includes(req.params.sortorder)) { + db.query('select username,rating,last_played,playcount,fccount from users order by '+req.params.orderby+' '+req.params.sortorder+",rating desc limit $1 offset $2",[req.query.limit,req.query.offset]) + .then((data)=>{return res.status(200).json(data.rows)}) + .catch((err)=>{res.status(500).send(err.message)}) + } else { + res.status(400).send("Not a valid sort option!"); + } + } else { + res.status(400).send("Invalid query!") + } +}) + +function ValidateToken(username,token) { + return db.query('select authentication_token from users where username=$1 limit 1',[username]) + .then((data)=>{ + if (data.rows.length>0) { + return token===data.rows[0].authentication_token; + } else { + return false; + } + }) +} + +function GetSongId(songname) { + return db.query("select id from songs where name=$1 or romanized_name=$1 or english_name=$1 limit 1",[songname]) + .then((data)=>{ + if (data.rows.length>0) { + return data.rows[0].id; + } else { + throw new Error("Could not get song ID for song '"+songname+"'") + } + }) +} + +function GetNoteCount(songname,difficulty) { + var songID=-1; + return GetSongId(songname) + .then((id)=>{songID=id;return db.query("select notecount from songdata where songid=$1 and difficulty=$2 limit 1",[songID,difficulty])}) + .then((data)=>{ + if (data.rows.length>0) { + return data.rows[0].notecount; + } else { + throw new Error("Could not get note count for song '"+songname+"' on difficulty '"+difficulty+"'") + } + }) +} + +app.post('/song/:songname/:difficulty',(req,res)=>{ + if (req.body&&req.params.songname&&req.params.difficulty&&req.body.username&&req.body.percent&&req.body.authentication_token) { + var noteCount=0,songID=0,fail=false; + ValidateToken(req.body.username,req.body.authentication_token) + .then((allowed)=>{ + if (allowed) { + return GetSongId(req.params.songname) + }else{throw new Error("Could not authenticate!")} + }) + .then((songId)=>{ + if (songId) { + songID=songId; + return GetNoteCount(req.params.songname,req.params.difficulty) + }else{throw new Error("Could not find song ID!")} + }) + .then((noteCount)=>{ + var percentThreshold=(req.params.difficulty==="E"?100:107)/100.0 + var percent=(req.params.difficulty==="E"?100:107)?Math.min(req.body.percent/100.0,percentThreshold):Math.min(req.body.percent/107.0,percentThreshold); + var cool = 0; + var fine = 0; + var safe = 0; + var sad = 0; + var worst = 0; + for (var i=0;i{ + res.status(200).json(data.data) + }) + .catch((err)=>{res.status(400).send(err.message)}) + } else { + res.status(400).send("Invalid query!") + } +}) + +function CheckUserExists(username,email) { + return db.query("select id,username,email,registered from users where username=$1 or email=$2 limit 1",[username,email]) +} + +function SendLoginEmail(username,emailTo,authCode) { + const transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: 'admin@projectdivar.com', + pass: process.env.GMAIL // naturally, replace both with your real credentials or an application-specific password + } + }); + transporter.sendMail({ + from: 'admin@projectdivar.com', + to: emailTo, + subject: 'Project DivaR Login Code', + html: `${username},

Thank you for using Project DivaR!

Your authentication code is ${authCode}!` + }, (err, info) => { + if (err) { + console.log(err.message) + } else { + console.log(info.envelope); + console.log(info.messageId); + } + }); +} + +function SendRegistrationEmail(username,emailTo,authCode) { + const transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: 'admin@projectdivar.com', + pass: process.env.GMAIL // naturally, replace both with your real credentials or an application-specific password + } + }); + transporter.sendMail({ + from: 'admin@projectdivar.com', + to: emailTo, + subject: 'Project DivaR Registration Code', + html: `${username},

Thank you for signing up for Project DivaR!

Your authentication code is ${authCode}!` + }, (err, info) => { + if (err) { + console.log(err.message) + } else { + console.log(info.envelope); + console.log(info.messageId); + } + }); +} + +function GetUserInfo(username) { + return db.query("select id,username,email,code_time from users where username=$1 limit 1",[username]) +} +function GetUserLoginAllowed(username,authCode) { + return db.query("select id,username,email,code_time,playstyle,twitter_name,twitch_name from users where username=$1 and code=$2 limit 1",[username,authCode]) +} +app.post('/authenticate/authToken',(req,res)=>{ + if (req.body&&req.body.username&&req.body.authCode) { + GetUserLoginAllowed(req.body.username.trim(),req.body.authCode.trim()) + .then((data)=>{ + if (data.rows.length>0) { + return db.query("select authentication_token from users where id=$1",[data.rows[0].id]) + } else { + return new Error("Failed login!") + } + }) + .then((data)=>{ + if (data.rows.length>0) { + res.status(200).json(data.rows[0]) + } else { + return new Error("Failed to get authentication token!") + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Invalid Credentials!") + } +}) + +app.post('/authenticate/login',(req,res)=>{ + if (req.body&&req.body.username&&req.body.authCode) { + GetUserLoginAllowed(req.body.username.trim(),req.body.authCode.trim()) + .then((data)=>{ + if (data.rows.length>0) { + res.status(200).json(data.rows[0]) + } else { + return new Error("Failed login!") + } + }) + } else { + res.status(400).send("Invalid Credentials!") + } +}) + +app.post('/sendemail/login',function(req,res) { + if (req.body&&req.body.username) { + GetUserInfo(req.body.username.trim()) + .then((data)=>{ + if (data.rows.length>0) { + res.status(200).send("Email sent.") + //console.log(data.rows[0].code_time) + if (data.rows[0].code_time) { + if (moment(data.rows[0].code_time,"YYYY-MM-DD HH:mm:ss.SSSZ").diff(moment(),'minutes')<=-15) { + var authCode=Math.floor(Math.random()*90000)+10000 + SendLoginEmail(req.body.username,data.rows[0].email,authCode) + db.query("update users set code=$1,code_time=$3 where id=$2",[authCode,data.rows[0].id,moment()]) + } + //console.log(moment(data.rows[0].code_time,"YYYY-MM-DD HH:mm:ss.SSSZ").diff(moment(),'minutes')) + } else { + var authCode=Math.floor(Math.random()*90000)+10000 + SendLoginEmail(req.body.username,data.rows[0].email,authCode) + db.query("update users set code=$1,code_time=$3 where id=$2",[authCode,data.rows[0].id,moment()]) + } + } else { + return new Error("User does not exist!") + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +app.patch('/updateRegisteredState',function(req,res) { + if (req.body&&req.body.username&&req.body.authCode) { + GetUserLoginAllowed(req.body.username,req.body.authCode) + .then((data)=>{ + if (data.rows.length>0) { + return db.query("update users set registered=$1 where id=$2",[true,data.rows[0].id]) + } else { + throw new Error("Could not login!") + } + }) + .then((data)=>{ + res.status(200).send("Registered!") + }) + .catch((err)=>{ + res.status(500).send("Could not finish registration!") + }) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +app.post('/sendemail/register',function(req,res) { + if (req.body&&req.body.username&&req.body.email) { + //Generate a token for the user to login with. + CheckUserExists(req.body.username.trim(),req.body.email.trim()) + .then((data)=>{ + var authCode=Math.floor(Math.random()*90000)+10000 + var authenticationToken=String(Math.floor(Math.random()*90000)+10000)+"-"+String(Math.floor(Math.random()*90000)+10000)+"-"+String(Math.floor(Math.random()*90000)+10000); + if (data.rows.length>0) { + //db.query("update users set code=$1 where id=$2",[authCode,data.rows[0].id]) + if (data.rows[0].registered) { + throw new Error("User already exists!") + } else { + return db.query("update users set code=$1 where id=$2 returning code",[authCode,data.rows[0].id]) + } + } else { + return db.query("insert into users(username,email,authentication_token,code) values($1,$2,$3,$4) returning code", + [req.body.username,req.body.email,authenticationToken,authCode]) + } + }) + .then((data)=>{ + if (data.rows.length>0) { + res.status(200).send("Email sent.") + SendRegistrationEmail(req.body.username,req.body.email,data.rows[0].code) + } else { + throw new Error("Something bad happened!") + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +function AuthenticateUser(username,auth) { + return db.query("select id,username,email from users where username=$1 and authentication_token=$2 limit 1",[username,auth]) +} + +app.post('/authenticateuser',function(req,res) { + if (req.body&&req.body.username&&req.body.authenticationToken) { + AuthenticateUser(req.body.username.trim(),req.body.authenticationToken.trim()) + .then((data)=>{ + if (data.rows.length>0) { + res.status(200).send("Authentication Success!") + } else { + throw new Error("Authentication Failed!") + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +app.post('/updateuser', function(req, res) { + var userId=-1; + if (req.body&&req.body.playStyle&&req.body.username&&req.body.authCode) { + GetUserLoginAllowed(req.body.username ,req.body.authCode) + .then((data)=>{ + if (data.rows.length>0) { + userId=data.rows[0].id + if (req.body.twitchName) { + db.query("update users set twitch_name=$1 where id=$2",[req.body.twitchName,userId]) + } + + + if (req.body.twitterName) { + return axios.get('https://api.twitter.com/1.1/users/show.json?screen_name='+req.body.twitterName, { + headers: { + /*BEARER*/ Authorization: 'Bearer '+process.env.TWITTER_BEARER //the token is a variable which holds the token + } + }) + } else + { + return {data:{}} + } + } else { + throw new Error("Could not login!") + } + }) + .then((data)=>{ + if (data.data.id) { + return db.query("update users set playstyle=$1,twitter=$2,twitter_name=$3 where id=$4",[req.body.playStyle,data.data.id,req.body.twitterName,userId]) + } else { + return db.query("update users set playstyle=$1 where id=$2",[req.body.playStyle,userId]) + } + }) + .then((data)=>{ + res.status(200).send("Successfully updated user settings!") + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +app.post('/getUserAuthData', function(req, res) { + if (req.body&&req.body.password&&req.body.userId) { + if (req.body.password===process.env.GUARDIANPASSWORD) { + db.query("select uploads,username,authentication_token from users where id=$1",[req.body.userId]) + .then((data)=>{ + if (data.rows.length>0) { + res.status(400).json(data.rows[0]) + db.query("update users set uploads=$1 where id=$2",[data.rows[0].uploads+1,req.body.userId]) + } else { + res.status(400).send("No user found!") + } + } + ) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Authentication failed!") + } + } else { + res.status(400).send("Invalid credentials!"); + } +}) + +app.post('/passImageData',function(req,res) { + if (req.body&&req.body.user&&req.body.auth&&req.body.url) { + axios.post("http://projectdivar.com/image",{user:req.body.user,auth:req.body.auth,url:req.body.url}) + .then((data)=>{ + res.status(200).json(data.data) + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +app.post('/streamstart/:id',function (req,res){ + if (req.body&&req.body.username&&req.body.authentication_token) { + GetUserLoginAllowed(req.body.username.trim(),req.body.authentication_token.trim()) + .then((data)=>{ + if (data.rows.length>0) { + spawn("../divabotguardian/stream_monitor.sh",[Number(req.params.id)]) + res.status(200).send("Monitor started") + } else { + res.status(400).send("Failed to authenticate") + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +app.post('/streamtop/:id',function (req,res){ + exec("ps 43",(err,out,stderr)=>{ + if (err) { + res.status(500).send(`${err.message}`); + return; + } + if (stderr) { + res.status(500).send(`${stderr}`); + return; + } + res.status(200).send(`${out}`); + }) +}) + +app.post('/streaminfo/:id',function (req,res){ + if (req.body&&req.body.username&&req.body.authentication_token) { + GetUserLoginAllowed(req.body.username.trim(),req.body.authentication_token.trim()) + .then((data)=>{ + if (data.rows.length>0) { + exec("ps $(cat ../divabotguardian/processes/"+Number(req.params.id)+".ffmpeg)|wc -l",(err,out,stderr)=>{ + if (err) { + res.status(500).send(`${err.message}`); + return; + } + if (stderr) { + res.status(500).send(`${stderr}`); + return; + } + res.status(200).send(`${out}`); + }) + } else { + res.status(400).send("Failed to authenticate") + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +const lastscores={} + +app.post('/eventsubmit',function(req,res) { + const EVENTID=12; + + function submit() { + lastscores[req.body.rank]=Number(req.body.points) + db.query("insert into eventdata(eventid,rank,date,name,description,points) values($1,$2,$3,$4,$5,$6) returning *;", + [req.body.eventid,req.body.rank,req.body.date?req.body.date:new Date(),req.body.name,req.body.description,req.body.points]) + .then((data)=>{ + if (data.rows.length>0) { + res.status(200).send("Submitted.") + } else { + res.status(500).send("Failed to submit.") + } + }) + .catch((err)=>{ + res.status(500).send(`${err.message}`); + }) + } + + //add to table. + + function FurtherTierIsOkay(tier,scores,points) { + var tiers= [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,50,100,500,1000,2000,5000,10000,20000,30000,50000] + if (tier<=1) { + return true; + } else + { + //Find the previous tier. + var previousTier = -1; + for (var i=0;i + { + if (data.rows.length>0) { + data.rows.map((row)=>{lastscores[row.rank]=row.points}) + + } + + if (!lastscores[req.body.rank]||(lastscores[req.body.rank]!=req.body.points&&lastscores[req.body.rank]400) { + MAXSPEED=Math.floor(chartData['1'][400].points/(moment(chartData['1'][400].date).diff(EVENTSTART,'minutes')/60)) + } else + if (chartData['1']&&chartData['1'].length>0){ + MAXSPEED=Math.floor(chartData['1'][chartData['1'].length-1].points/(moment(chartData['1'][chartData['1'].length-1].date).diff(EVENTSTART,'minutes')/60)) + } else { + MAXSPEED=0 + } +} +const RATEDURATION=2 //In hours. How much EP/hr is shown. + +function GetRate(rank) { + if (chartData[rank].length>2) { + var lastpoint=chartData[rank][chartData[rank].length-1] + for (var i=chartData[rank].length-1;i>=0;i--) { + var diff = moment().diff(chartData[rank][i].date,'hours') + if (diff>=RATEDURATION) { + break; + } else { + lastpoint=chartData[rank][i] + } + } + var timediff = moment(chartData[rank][chartData[rank].length-1].date).diff(moment(lastpoint.date),'minutes') + if (timediff<120) { + if (lastpoint===chartData[rank][chartData[rank].length-1]) { + return "???" + } else + return (chartData[rank][chartData[rank].length-1].points-lastpoint.points)/RATEDURATION + } else { + return Math.ceil((chartData[rank][chartData[rank].length-1].points-lastpoint.points)/ + (moment(chartData[rank][chartData[rank].length-1].date).diff(moment(lastpoint.date),'minutes')/60) + ) + } + } else { + if (chartData[rank].length>0) { + var startPoint=chartData[rank][chartData[rank].length-1] + return Math.ceil(GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60)) + } else { + return 0 + } + } +} + +function GetPointCount(rank) { + var pointCount=1; + if (!chartData[rank]) { + return pointCount; + } + if (chartData[rank].length>2) { + var lastpoint=chartData[rank][chartData[rank].length-1] + for (var i=chartData[rank].length-1;i>=0;i--) { + var diff = moment().diff(chartData[rank][i].date,'hours') + if (diff>=RATEDURATION) { + break; + } else { + lastpoint=chartData[rank][i] + pointCount++; + } + } + return pointCount; + } else { + if (chartData[rank].length>0) { + return chartData[rank].length; + } else { + return pointCount; + } + } +} + +function CreatePrediction(precision,rank) { + if (!chartData[rank]) { + return [] + } + var startPoint=chartData[rank][chartData[rank].length-1] + if (rank<=20) { + startPoint={points:startPoint.points,date:moment()} + } + var startTime=moment(startPoint.date) + if (PREDICTIONS&&startTime.diff(EVENTSTART,'hours')>36&&moment(startPoint.date).diff(EVENTSTART,'hours')>=36) { + //console.log(MAXSPEED) + //Precision is in hours. 1 is default + var finalChart=[{y:chartData[rank][chartData[rank].length-1].points,x:chartData[rank][chartData[rank].length-1].date}] + //Start from the time of the last reported rank. + var myPoints = startPoint.points + var pointSpeed = Math.ceil(GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60)) + var speedGoal = MAXSPEED*nyoomfactor[rank] + while (startTime11) { + pointSpeed-=pointSpeed*(slowdownFactor[rank]*10/*CONSTANT for adjustment*/) + } else { + pointSpeed=Math.max( + GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60), + Math.min((12-EVENTEND.diff(startTime,'hours'))*(speedGoal/5),speedGoal)) + //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. + //console.log(pointSpeed) + } + finalChart=[...finalChart,{y:Number.isInteger(myPoints)?myPoints:"???",x:moment(startTime)}] + } + predictionChartData[rank]=finalChart + return finalChart + } else + if (PREDICTIONS&&startTime.diff(EVENTSTART,'hours')>24&&moment(startPoint.date).diff(EVENTSTART,'hours')>=24) { + //console.log(MAXSPEED) + //Precision is in hours. 1 is default + var finalChart=[{y:chartData[rank][chartData[rank].length-1].points,x:chartData[rank][chartData[rank].length-1].date}] + //Start from the time of the last reported rank. + var myPoints = startPoint.points + var pointSpeed = GetRate(rank) + var speedGoal = MAXSPEED*nyoomfactor[rank] + while (startTime11) { + pointSpeed-=pointSpeed*(slowdownFactor[rank]*10/*CONSTANT for adjustment*/) + } else { + pointSpeed=Math.max( + GetRank(rank)/(moment(startPoint.date).diff(EVENTSTART,'minutes')/60), + Math.min((12-EVENTEND.diff(startTime,'hours'))*(speedGoal/5),speedGoal)) + //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. + //console.log(pointSpeed) + } + finalChart=[...finalChart,{y:Number.isInteger(myPoints)?myPoints:"???",x:moment(startTime)}] + } + predictionChartData[rank]=finalChart + return finalChart + } else { + return [] + } +} + +function numberWithCommas(x) { + if (Number.isInteger(x)) { + var num_parts = x.toString().split("."); + num_parts[0] = num_parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); + return num_parts.join("."); + } else { + return x + } +} + +function ChartData(rank) { + if (!chartData[rank]) { + return [{x:0,y:0}] + } + if (rank<=20) { + return [...chartData[rank].map((data)=>{return {x:data.date,y:data.points}}),{x:moment().isBefore(EVENTEND)?moment():EVENTEND,y:chartData[rank][chartData[rank].length-1].points}] + } else { + return [{x:EVENTSTART,y:0},...chartData[rank].map((data)=>{return {x:data.date,y:data.points}})] + } +} + +function GetRank(rank) { + if (chartData[rank]) { + return chartData[rank][chartData[rank].length-1].points + } else { + return "???" + } +} + +function GetEstimate(rank) { + if (predictionChartData[rank]) { + var currentEstimate = 0 + if (rank>20 && moment().diff(moment(chartData[rank][chartData[rank].length-1].date),'hours')>0.5) { + for (var i=predictionChartData[rank].length-1;i>=0;i--) { + if (moment(predictionChartData[rank][i].x).isAfter(moment())) { + currentEstimate=predictionChartData[rank][i].y + } else { + break; + } + } + + return currentEstimate + } else { + if (rank>20) { + return GetRank(rank) + } else { + return "---" + } + } + } else { + return "???" + } +} + +function GetTime(rank) { + if (chartData[rank]) { + return moment(chartData[rank][chartData[rank].length-1].date).fromNow() + } else { + return "" + } +} +function GetUpdateColor(rank) { + if (chartData[rank]) { + return "rgba(255,"+Math.max(255-moment().diff(moment(chartData[rank][chartData[rank].length-1].date),'hours')*3,0)+","+Math.max(255-moment().diff(moment(chartData[rank][chartData[rank].length-1].date),'hours')*3,0)+",1)" + } else { + return "" + } +} + +var tableValues={} + +app.get('/eventdata',function(req,res){ + var eventinfo = [] + db.query('select * from event order by id desc limit 1') + .then((data)=>{ + eventinfo = data.rows; + if (!req.query.event) { + return db.query('select distinct on (rank) rank,eventid,date,name,description,points from eventdata where eventid=$1 order by rank,date desc',[moment(eventinfo[0].startdate).isBefore(moment())?eventinfo[0].eventid:eventinfo[0].eventid-1]) + } else { + + } + }) + .then((data)=>{ + var finaldata = data.rows + var tiers= [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,50,100,500,1000,2000,5000,10000,20000,30000,50000] + for (t of tiers) { + //console.log(t) + //if (finaldata[String(t)]===undefined) {finaldata[String(t)]={"rank":t,"eventid":eventinfo[0].eventid,"name":"","description":"","date":eventinfo[0].startdate,"points":0}} + var found=false + for (i in finaldata) { + //console.log(finaldata[i].rank) + if (finaldata[i].rank===t) { + found=true + break; + } + } + if (!found) { + finaldata=[...finaldata,{"rank":t,"eventid":eventinfo[0].eventid,"name":"","description":"","date":eventinfo[0].startdate,"points":0}] + } + } + res.status(200).json(finaldata) + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) +}) + +app.get('/eventdata/t20',function(req,res){ + var eventinfo = [] + if (req.query.date&&req.query.rank) { + db.query('select * from eventdata where date<=$1 and rank=$2 and eventid=$3 order by date desc limit 1;',[req.query.date,req.query.rank,10]) + .then((data)=>{ + res.status(200).json(data.rows) + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else + if (req.query.luminous) { + db.query("select * from eventdata where (date>='2021-02-17 23:36:16.383+00' and date<'2021-02-19 15:35:16.716+00' and rank=12) or (date>='2021-02-19 15:35:16.716+00' and rank=11) and eventid=10 order by id asc;") + .then((data)=>{ + res.status(200).json(data.rows) + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else + if (req.query.all&&req.query.event) { + db.query('select * from eventdata where eventid=$1 order by date asc;',[req.query.event]) + .then((data)=>{ + res.status(200).json(data.rows) + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else + if (req.query.tier&&req.query.event) { + db.query('select * from eventdata where eventid=$1 and rank=$2 order by date desc',[req.query.event,req.query.tier]) + .then((data)=>{ + res.status(200).json(data.rows) + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else + if (req.query.chart){ + + function RandomQuestion() { + var value = Math.random() + if (value<=0.7) { + return "???" + } else + if (value<=0.9) { + return "Miyu" + } else + if (value<=0.95) { + return "Muni" + } else { + return "MuniMuni" + } + } + if (req.query.event||moment().diff(lastCachedDate,'minutes')>=1||(req.query.force&&moment().diff(lastCachedDate,'seconds')>=10)) { + chartData={} + predictionChartData={} + diffData=[] + db.query('select * from event order by id desc limit 1') + .then((data)=>{ + eventinfo = data.rows; + return db.query('select * from eventdata where eventid=$1 order by date asc',[(req.query.event)?req.query.event:eventinfo[0].eventid]) + }) + .then((data)=>{ + if (data&&data.rows&&data.rows.length>0) { + data.rows.map((obj)=>{if (chartData[obj.rank]) {chartData[obj.rank]=[...chartData[obj.rank],obj]} else {chartData[obj.rank]=[obj]}}) + SetupPredictionModel() + var tiers= [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,50,100,500,1000,2000,5000,10000,20000,30000,50000] + for (t of tiers) { + CreatePrediction(1,t) + var est = GetEstimate(t) + var temprate = 0 + if (chartData[t]) { + temprate=(t<=20)?(chartData[t])?Math.ceil(GetRate(t)):undefined:Math.ceil(GetRank(t)/(moment(chartData[t][chartData[t].length-1].date).diff(EVENTSTART,'minutes')/60)) + } + tableValues[t]={ + points:GetRank(t), + lastUpdate:GetTime(t), + lastUpdateColor:GetUpdateColor(t), + rate:temprate?temprate:0, + count:GetPointCount(t), + estimate:Number.isInteger(est)?Math.ceil(est):est, + prediction:(predictionChartData[t]&&moment().isBefore(EVENTEND))?predictionChartData[t][predictionChartData[t].length-1].y:RandomQuestion() + } + } + lastCachedDate=moment() + res.status(200).send({predictionData:predictionChartData,statistics:tableValues}) + } else { + res.status(200).send({predictionData:predictionChartData,statistics:tableValues}) + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(200).send({predictionData:predictionChartData,statistics:tableValues}) + } + } else { + db.query('select * from event order by id desc limit 1') + .then((data)=>{ + eventinfo = data.rows; + return db.query('select distinct on (rank) rank,eventid,date,name,description,points from eventdata where rank<=20 and eventid=$1 order by rank,date desc',[moment(eventinfo[0].rank_end).isBefore(moment())?eventinfo[0].eventid:eventinfo[0].eventid-1]) + }) + .then((data)=>{ + var finaldata = data.rows + var tiers= [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] + for (t of tiers) { + //console.log(t) + //if (finaldata[String(t)]===undefined) {finaldata[String(t)]={"rank":t,"eventid":eventinfo[0].eventid,"name":"","description":"","date":eventinfo[0].startdate,"points":0}} + var found=false + for (i in finaldata) { + //console.log(finaldata[i].rank) + if (finaldata[i].rank===t) { + found=true + var temprate = (chartData[t])?Math.ceil(GetRate(t)):undefined + if (!temprate) { + finaldata[i].rate="???" + } else { + finaldata[i].rate=temprate + } +//finaldata[i].name="Muni"; +var names=["Rinku","Muni","Esoran","Maho", +"Rei","Kyoko","Esora","Yuka"," + break; + } + } + if (!found) { + finaldata=[...finaldata,{"rate":0,"rank":t,"eventid":eventinfo[0].eventid,"name":"","description":"","date":eventinfo[0].startdate,"points":0}] + } + } +finaldata[1].name="Muni" + res.status(200).json(finaldata) + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } +}) + +app.get('/cans',function(req,res){ + db.query('select (select count(*) from cans where can=true) as cans,(select count(*) from cans where can=false) as notcan;') +.then((data)=>{res.status(200).send(data.rows[0])}) + .catch((err)=>{res.status(500).send(err.message)}) +}) + +app.post('/cans',function(req,res){ + if (req.body&&(req.body.cans!==undefined&&req.body.cans!==null)) { + db.query('insert into cans(date,can) values($1,$2)',[moment(),req.body.cans]) + .then((data)=>{res.status(200).send("Done!")}) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +app.post('/streamkill/:id',function (req,res){ + if (req.body&&req.body.username&&req.body.authentication_token) { + GetUserLoginAllowed(req.body.username.trim(),req.body.authentication_token.trim()) + .then((data)=>{ + if (data.rows.length>0) { + exec("kill $(cat ../divabotguardian/processes/"+Number(req.params.id)+".ffmpeg)|wc -l",(err,out,stderr)=>{ + if (err) { + res.status(500).send(`${err.message}`); + return; + } + if (stderr) { + res.status(500).send(`${stderr}`); + return; + } + res.status(200).send(`${out}`); + }) + } else { + res.status(400).send("Failed to authenticate") + } + }) + .catch((err)=>{ + res.status(500).send(err.message) + }) + } else { + res.status(400).send("Invalid credentials!") + } +}) + +app.get('/streamdata/:id',function (req,res){ + db.query("select twitch_name from users where id=$1",[req.params.id]) + .then((data)=>{ + return twitchStreams.get(data.rows[0].twitch_name) + }) + .then(function(streams) { + if (streams.length>0) { + var streamchoice = undefined + for (var i=0;i{ + res.status(500).send(err.message) + }) +}) + +app.get('/eventchart',function (req,res){ + const myChart = new QuickChart(); + myChart + .setConfig({ + type: 'bar', + data: { labels: ['Hello world', 'Foo bar'], datasets: [{ label: 'Foo', data: [1, 2] }] }, + }) + .setWidth(300) + .setHeight(150); + + // You can send the URL to someone... + const chartImageUrl = myChart.getUrl(); + res.status(200).send('') +}) + + + +var process_images = [] +var processPromises = [] +var largestId = 0 +var filterId = 0 +var MAX_INDEX = 12 //To prevent being rate-limited. + +function Process(data,ind){ + for (var i in data.data.statuses) { + var tweet = data.data.statuses[i] + if (tweet.source && tweet.source.includes("Nintendo Switch Share")||tweet.source.includes("PlayStation®Network")) { + if (tweet.extended_entities) { + //console.log(tweet.extended_entities.media) + for (var j=0;j{ + //console.log("Going to next: "+(ind+1)) + return Process(data,ind+1)}) + } + return "Done!"; +} + +app.use('/files',express.static('files',{ + maxAge: 86400000 * 30 +})) + +/* +axios.get('https://api.twitter.com/1.1/search/tweets.json?q=@divarbot', { + headers: { + Authorization: 'Bearer '+process.env.TWITTER_BEARER //the token is a variable which holds the token + } +}) +.then((data)=>{ + //console.log(data.data.statuses) + //console.log(data.data) + return Process(data); +}) +.then((data)=>{process_images.forEach((image)=>{console.log(image)})})*/ +/*setInterval( +()=>{ + twitchStreams.get('smallant') + .then(function(streams) { + //console.log(streams) + if (streams.length>0) { + db.query("update streams set stream=$1 where id=1",[streams[0].url]); + } + }) + .catch((err)=>{ + console.log(err.message) + }) +},5000)*/ + +/* +setInterval( +()=>{ + function addToQueue(uploadData) { + if (uploadData.tries===undefined||uploadData.tries===null) { + uploadData.tries=1; + } else { + uploadData.tries+=1; + } + if (uploadData.tries<5) { + console.log("Failed to upload. Added back to queue. Tries: "+uploadData.tries+" / "+JSON.stringify(uploadData)) + db.query("insert into uploadedplays(filename,userid,submissiondate,id,playid,tries) values($1,$2,$3,$4,$5,$6);", + [uploadData.filename,uploadData.userid,uploadData.submissiondate,uploadData.id,uploadData.playid,uploadData.tries]) + } + } + var uploadData=undefined,user=undefined,auth=undefined,playData; + db.query("select * from uploadedplays where tries is null or tries>=0 order by submissiondate asc limit 1") + .then((data)=>{ + if (data.rows.length>0) { + uploadData=data.rows[0]; + //console.log(uploadData) + return db.query("select username,authentication_token from users where id=$1",[uploadData.userid]) + } + }) + .then((data)=>{ + if (uploadData && data.rows.length>0) { + user=data.rows[0].username + auth=data.rows[0].authentication_token + if (uploadData.tries!==undefined&&uploadData.tries!==null) { + return db.query("update uploadedplays set tries=$2 where id=$1",[uploadData.id,(uploadData.tries*-1)]) + } else { + return db.query("update uploadedplays set tries=-1 where id=$1",[uploadData.id]) + } + } + }) + .then((data)=>{ + if (uploadData) { + //console.log(data.data) + return axios.post("http://projectdivar.com/image", + {url:uploadData.filename,user:user,auth:auth}) + } + }) + .then((data)=>{ + if (uploadData) { + if (data.data==="Invalid parameters!") { + throw new Error("Invalid parameters while trying to submit play!") + } + } + }) + .catch((err)=>{ + if (uploadData) { + addToQueue(uploadData) + } + }) +} +,1000) +*/ + +//setInterval(()=>{db.query("select * from twitter_bot limit 1") +//.then((data)=>{ +// largestId=filterId=data.rows[0].lastpost; +// //console.log("Filter Id: "+filterId); +// /*return axios.get('https://api.twitter.com/1.1/search/tweets.json?q=%23mega39s', { +// headers: { +// Authorization: 'Bearer '+process.env.TWITTER_BEARER //the token is a variable which holds the token +// } +// })*/ +// return axios.get('https://api.twitter.com/1.1/search/tweets.json?q=%23divarbot', { +// headers: { +// Authorization: 'Bearer '+process.env.TWITTER_BEARER //the token is a variable which holds the token +// } +// }) +//}) +//.then((data)=>{ +// //console.log(data.data.statuses) +// //console.log(data.data) +// //data.data.s +// //console.log("Reading Twitter Data...") +// return Process(data,0); +//}) +//.then((data)=>{ +// //console.log(process_images) +// var promisesDone=0; +// process_images.forEach((obj)=>{ +// if (filterId{ +// console.log("Process Twitter Post: "+obj.id); +// return db.query("select id from users where twitter=$1",[obj.user]) +// .then((data)=>{ +// if (data.rows.length>0) { +// console.log("Process new play for User id "+data.rows[0].id+"...") +// return db.query("insert into uploadedplays values($1,$2,$3)",[obj.image,data.rows[0].id,new Date()]) +// .then(()=>{resolve("Done!")}) +// } else { +// reject("Not associated with an Id!") +// } +// }) +// .catch((err)=>{console.log(err.message);reject("Failed!")})})) +// } +//}) +////setTimeout(()=>{console.dir(processPromises, {'maxArrayLength': null})},2000) +//return Promise.allSettled(processPromises) +//}) +//.then((data)=>{ +// //console.log(largestId) +// return db.query("update twitter_bot set lastpost=$1 returning *",[largestId]) +//}) +//.catch((err)=>{console.log(err.message)}) +//},60000) + +setInterval(()=>{ + axios.get("http://www.projectdivar.com/eventdata/t20?chart=true&force=true") +},20000)