Finish Pacosako + add GameStat table to know how many live games are played
[vchess.git] / server / models / Game.js
index 080cab3..4a8c517 100644 (file)
@@ -18,6 +18,8 @@ const UserModel = require("./User");
  *   randomness: integer
  *   deletedByWhite: boolean
  *   deletedByBlack: boolean
+ *   chatReadWhite: datetime
+ *   chatReadBlack: datetime
  *
  * Structure table Moves:
  *   gid: ref game id
@@ -32,8 +34,8 @@ const UserModel = require("./User");
  *   added: datetime
  */
 
-const GameModel =
-{
+const GameModel = {
+
   checkGameInfo: function(g) {
     return (
       g.vid.toString().match(/^[0-9]+$/) &&
@@ -45,6 +47,16 @@ const GameModel =
     );
   },
 
+  incrementCounter: function(vid, cb) {
+    db.serialize(function() {
+      let query =
+        "UPDATE GameStat " +
+        "SET total = total + 1 " +
+        "WHERE vid = " + vid;
+      db.run(query, cb);
+    });
+  },
+
   create: function(vid, fen, randomness, cadence, players, cb) {
     db.serialize(function() {
       let query =
@@ -74,6 +86,7 @@ const GameModel =
         "SELECT " +
           "g.id, g.fen, g.fenStart, g.cadence, g.created, " +
           "g.white, g.black, g.score, g.scoreMsg, " +
+          "g.chatReadWhite, g.chatReadBlack, " +
           "g.drawOffer, g.rematchOffer, v.name AS vname " +
         "FROM Games g " +
         "JOIN Variants v " +
@@ -125,12 +138,13 @@ const GameModel =
     db.serialize(function() {
       let query =
         "SELECT id, vid, cadence, created, score, white, black " +
-        "FROM Games ";
-      if (uid > 0) query +=
-        "WHERE " +
-        "  created < " + cursor + " AND " +
-        "  white <> " + uid + " AND " +
-        "  black <> " + uid + " ";
+        "FROM Games " +
+        "WHERE created < " + cursor + " ";
+      if (uid > 0) {
+        query +=
+          "  AND white <> " + uid + " " +
+          "  AND black <> " + uid + " ";
+      }
       query +=
         "ORDER BY created DESC " +
         "LIMIT 20"; //TODO: 20 hard-coded...
@@ -232,11 +246,18 @@ const GameModel =
         "JOIN Variants v " +
         "  ON g.vid = v.id " +
         "WHERE " +
-        "  score <> '*' AND " +
-        "  created < " + cursor + " AND " +
+        "  score <> '*' AND" +
+        "  created < " + cursor + " AND" +
         "  (" +
-        "    (" + uid + " = white AND NOT deletedByWhite) OR " +
-        "    (" + uid + " = black AND NOT deletedByBlack)" +
+        "    (" +
+        "      white = " + uid + " AND" +
+        "      (deletedByWhite IS NULL OR NOT deletedByWhite)" +
+        "    )" +
+        "    OR " +
+        "    (" +
+        "      black = " + uid + " AND" +
+        "      (deletedByBlack IS NULL OR NOT deletedByBlack)" +
+        "    )" +
         "  ) ";
       query +=
         "ORDER BY created DESC " +
@@ -293,18 +314,18 @@ const GameModel =
     // Check all that is possible (required) in obj:
     return (
       (
-        !obj.move || (
-          !!(obj.move.played.toString().match(/^[0-9]+$/)) &&
-          !!(obj.move.idx.toString().match(/^[0-9]+$/))
-        )
+        !obj.move || !!(obj.move.idx.toString().match(/^[0-9]+$/))
       ) && (
         !obj.drawOffer || !!(obj.drawOffer.match(/^[wbtn]$/))
       ) && (
         !obj.rematchOffer || !!(obj.rematchOffer.match(/^[wbn]$/))
       ) && (
-        !obj.fen || !!(obj.fen.match(/^[a-zA-Z0-9, /-]*$/))
+        // TODO: check if commas are still used (probably not)
+        !obj.fen || !!(obj.fen.match(/^[a-zA-Z0-9,. /-]*$/))
       ) && (
         !obj.score || !!(obj.score.match(/^[012?*\/-]+$/))
+      ) && (
+        !obj.chatRead || ['w','b'].includes(obj.chatRead)
       ) && (
         !obj.scoreMsg || !!(obj.scoreMsg.match(/^[a-zA-Z ]+$/))
       ) && (
@@ -339,6 +360,10 @@ const GameModel =
         const myColor = obj.deletedBy == 'w' ? "White" : "Black";
         modifs += "deletedBy" + myColor + " = true,";
       }
+      if (!!obj.chatRead) {
+        const myColor = obj.chatRead == 'w' ? "White" : "Black";
+        modifs += "chatRead" + myColor + " = " + Date.now() + ",";
+      }
       if (!!obj.score) {
         modifs += "score = '" + obj.score + "'," +
                   "scoreMsg = '" + obj.scoreMsg + "',";
@@ -366,8 +391,10 @@ const GameModel =
                 "(" + id + ",?," + Date.now() + "," + obj.move.idx + ")";
               db.run(query, JSON.stringify(obj.move.squares));
               finishAndSendQuery();
-            } else cb({ errmsg: "Wrong move index" });
-          } else {
+            }
+            else cb({ errmsg: "Wrong move index" });
+          }
+          else {
             if (ret.maxIdx < 2) cb({ errmsg: "Time not over" });
             else {
               // We also need the game cadence
@@ -376,7 +403,7 @@ const GameModel =
                 "FROM Games " +
                 "WHERE id = " + id;
               db.get(query, (err2, ret2) => {
-                const daysTc = parseInt(ret2.cadence.match(/\(^[0-9]+\)/)[0]);
+                const daysTc = parseInt(ret2.cadence.match(/^[0-9]+/)[0]);
                 if (Date.now() - ret.lastPlayed > daysTc * 24 * 3600 * 1000)
                   finishAndSendQuery();
                 else cb({ errmsg: "Time not over" });
@@ -384,14 +411,16 @@ const GameModel =
             }
           }
         });
-      } else finishAndSendQuery();
+      }
+      else finishAndSendQuery();
       // NOTE: chat and delchat are mutually exclusive
       if (!!obj.chat) {
         const query =
           "INSERT INTO Chats (gid, msg, name, added) VALUES ("
             + id + ",?,'" + obj.chat.name + "'," + Date.now() + ")";
         db.run(query, obj.chat.msg);
-      } else if (obj.delchat) {
+      }
+      else if (obj.delchat) {
         const query =
           "DELETE " +
           "FROM Chats " +
@@ -450,7 +479,7 @@ const GameModel =
           "SELECT gid, count(*) AS nbMoves, MAX(played) AS lastMaj " +
           "FROM Moves " +
           "GROUP BY gid";
-        db.get(query, (err2, mstats) => {
+        db.all(query, (err2, mstats) => {
           // Reorganize moves data to avoid too many array lookups:
           let movesGroups = {};
           mstats.forEach(ms => {
@@ -460,18 +489,19 @@ const GameModel =
             };
           });
           // Remove games still not really started,
-          // with no action in the last 3 months:
+          // with no action in the last 2 weeks:
           let toRemove = [];
           games.forEach(g => {
             if (
               (
                 !movesGroups[g.id] &&
-                tsNow - g.created > 91*day
+                tsNow - g.created > 14*day
               )
               ||
               (
+                !!movesGroups[g.id] &&
                 movesGroups[g.id].nbMoves == 1 &&
-                tsNow - movesGroups[g.id].lastMaj > 91*day
+                tsNow - movesGroups[g.id].lastMaj > 14*day
               )
             ) {
               toRemove.push(g.id);
@@ -482,6 +512,7 @@ const GameModel =
       });
     });
   }
-}
+
+};
 
 module.exports = GameModel;