├── .gitignore ├── README.md ├── back-end ├── .gitignore ├── app.js ├── bin │ └── www ├── config │ ├── mysql-connection.js │ └── mysql-sequelize.js ├── controllers │ ├── Earn.js │ ├── Leaderboard.js │ ├── Referral.js │ ├── Task.js │ ├── Tg.js │ ├── game.js │ └── reward.js ├── middlewares │ └── tg.js ├── models │ ├── Earnings.js │ ├── TGUser.js │ ├── Tasks.js │ └── index.js ├── package-lock.json ├── package.json ├── public │ └── stylesheets │ │ └── style.css ├── routes │ └── api │ │ ├── Earn.js │ │ ├── Leaderboard.js │ │ ├── Main.js │ │ ├── Referral.js │ │ ├── Reward.js │ │ ├── Task.js │ │ ├── Tg.js │ │ └── game.js ├── utils │ └── helperfun.js ├── views │ └── maintenance.ejs └── yarn.lock ├── front-end ├── .eslintrc.cjs ├── index.html ├── manifest.json ├── package-lock.json ├── package.json ├── postcss.config.js ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ ├── font │ │ │ ├── SF-Pro-Display-Bold.otf │ │ │ └── SF-Pro-Display-Medium.otf │ │ ├── img │ │ │ ├── Logo.svg │ │ │ ├── actualizar.png │ │ │ ├── back-arrow.svg │ │ │ ├── background-hero.png │ │ │ ├── bolt-icon.svg │ │ │ ├── boost.png │ │ │ ├── clock.svg │ │ │ ├── coin-background.png │ │ │ ├── coin.png │ │ │ ├── coin_old.png │ │ │ ├── coins.svg │ │ │ ├── defence.svg │ │ │ ├── earn.png │ │ │ ├── energy.png │ │ │ ├── energy.svg │ │ │ ├── friends.svg │ │ │ ├── friends_old.svg │ │ │ ├── gift.svg │ │ │ ├── giftbox.png │ │ │ ├── giftbox1.png │ │ │ ├── giftbox2.png │ │ │ ├── giftbox3.png │ │ │ ├── health.svg │ │ │ ├── ime.jpg │ │ │ ├── ime.png │ │ │ ├── leaderboard.svg │ │ │ ├── leaderboard_old.svg │ │ │ ├── logo-black.png │ │ │ ├── logo.png │ │ │ ├── main.png │ │ │ ├── mine-bg.png │ │ │ ├── not.png │ │ │ ├── people.png │ │ │ ├── play-icon.svg │ │ │ ├── question-icon.svg │ │ │ ├── ref.png │ │ │ ├── robot-1.png │ │ │ ├── robot-2.png │ │ │ ├── robot-3.png │ │ │ ├── robot-4.png │ │ │ ├── stars-robo.svg │ │ │ ├── task.svg │ │ │ ├── task_old.svg │ │ │ ├── token.png │ │ │ ├── upgrade.svg │ │ │ └── wallet-icon.svg │ │ └── sounds │ │ │ ├── ambient-piano-logo-165357.mp3 │ │ │ ├── click-21156.mp3 │ │ │ ├── lofi-guitar-105361.mp3 │ │ │ ├── mixkit-arcade-game-jump-coin-216.wav │ │ │ └── mixkit-arcade-game-opener-222.wav │ ├── components │ │ └── taptap │ │ │ ├── AnimatedCounter.jsx │ │ │ ├── Drawer.jsx │ │ │ ├── FriendsListItem.jsx │ │ │ ├── InviteCard.jsx │ │ │ ├── LoadingScreen.jsx │ │ │ ├── TGAuth.jsx │ │ │ └── Tabs.jsx │ ├── index.css │ ├── main.jsx │ ├── pages │ │ ├── error │ │ │ ├── Error.jsx │ │ │ ├── Error404.jsx │ │ │ └── Error500.jsx │ │ ├── game │ │ │ ├── Earn.jsx │ │ │ ├── Friends.jsx │ │ │ ├── Game.jsx │ │ │ ├── Leaderboard.jsx │ │ │ ├── LoadingScreen.jsx │ │ │ ├── Reward.jsx │ │ │ ├── Tasks.jsx │ │ │ └── Waitlist.jsx │ │ └── layout │ │ │ └── GameLayout.jsx │ ├── routes │ │ ├── GameRouer.jsx │ │ └── MainRouter.jsx │ └── utlis │ │ ├── audioUtils.js │ │ ├── axiosInstance.js │ │ ├── helperfun.js │ │ ├── localstorage.js │ │ ├── tg.js │ │ └── ui.js ├── tailwind.config.js ├── vite.config.js └── yarn.lock ├── handbook.txt └── taptap.sql /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tap and Play Web3 Game 🎮 2 | 3 | ## ⚙️ App in development stage ⚙️ 4 | 5 | Tap and Play is an engaging web3 game where players tap to earn points and rewards. The game is developed using React for the frontend and Express.js for the backend, providing a seamless and interactive gaming experience. 6 | 7 | ## Features 8 | - **Web3 Integration**: Utilize blockchain technology for secure and transparent gameplay. 9 | - **Interactive UI**: Built with React to offer a dynamic and responsive user interface. 10 | - **Robust Backend**: Powered by Express.js for efficient server-side operations. 11 | - **Daily Rewards**: Players can earn daily login rewards, enhancing player retention and engagement. 12 | - **Energy Management**: Strategic energy restoration mechanics ensure a balanced and challenging gameplay. 13 | - **Robo Mine**: Offline miner. 14 | 15 | ## Technologies Used 16 | - **Frontend**: React 17 | - **Backend**: Express.js 18 | 19 | ## Getting Started 🧑‍💻 20 | Follow these steps to set up the project on your local machine. 21 | 22 | ### Prerequisites 23 | - Node.js 24 | - npm 25 | - mysql 26 | 27 | ### Installation 🤖 28 | 1. **Clone the Repository**: 29 | ```bash 30 | git clone https://github.com/your-username/taptap.git 31 | cd taptap 32 | 2. **Front-end**: 33 | ```bash 34 | cd frontend 35 | npm install 36 | npm start 37 | 38 | 3. **Back-end**: 39 | ```bash 40 | cd backend 41 | npm install 42 | npm run dev 43 | 44 | 4. **Data Base**: 45 | ```bash 46 | CREATE DATABASE your_database_name; 47 | mysql -u your_username -p your_database_name < database_dump.sql 48 | -------------------------------------------------------------------------------- /back-end/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | # Dependency directories 11 | node_modules/ 12 | jspm_packages/ 13 | 14 | # Production build directories 15 | dist/ 16 | dist-ssr/ 17 | build/ 18 | 19 | # Environment variables 20 | .env 21 | .env.*.local 22 | 23 | # Local environment files 24 | *.local 25 | 26 | # Editor directories and files 27 | .vscode/ 28 | !.vscode/extensions.json 29 | .idea/ 30 | .DS_Store 31 | *.suo 32 | *.ntvs* 33 | *.njsproj 34 | *.sln 35 | *.sw? 36 | 37 | # OS-specific files 38 | Thumbs.db 39 | ehthumbs.db 40 | Desktop.ini 41 | 42 | # Temporary files 43 | tmp/ 44 | temp/ 45 | 46 | # Coverage directory used by tools like Jest or Istanbul 47 | coverage/ 48 | 49 | # Debug files 50 | debug/ 51 | debug.log* 52 | 53 | # TypeScript cache 54 | *.tsbuildinfo 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Parcel-bundler cache 60 | .cache/ 61 | 62 | # MacOS Finder attributes 63 | .AppleDouble 64 | .LSOverride 65 | 66 | # Windows image file caches 67 | Thumbs.db 68 | ehthumbs.db 69 | 70 | # Windows Installer files 71 | *.cab 72 | *.msi 73 | *.msm 74 | *.msp 75 | 76 | -------------------------------------------------------------------------------- /back-end/app.js: -------------------------------------------------------------------------------- 1 | var createError = require("http-errors"); 2 | var express = require("express"); 3 | var path = require("path"); 4 | var cookieParser = require("cookie-parser"); 5 | var logger = require("morgan"); 6 | var dot_env = require("dotenv"); 7 | var moment = require("moment"); 8 | 9 | dot_env.config(); 10 | var apiRouter = require("./routes/api/Main"); 11 | var app = express(); 12 | 13 | // view engine setup 14 | // app.set("views", path.join(__dirname, "views")); 15 | // app.set("view engine", "ejs"); 16 | 17 | app.use(logger("dev")); 18 | app.use(express.json()); 19 | app.use(express.urlencoded({ extended: false })); 20 | app.use(cookieParser()); 21 | app.use(express.static(path.join(__dirname, "public"))); 22 | app.disable('etag'); // for disable node If-None-Match comparing 23 | 24 | // app.get('*', function(req, res) { 25 | // res.render('maintenance'); 26 | // }); 27 | 28 | app.use("/api", apiRouter); 29 | 30 | //404 handler 31 | app.use(function(req, res, next) { 32 | return res.status(404).json({ 33 | statusCode: 404, 34 | status: "error", 35 | message: "Not found", 36 | }); 37 | }); 38 | 39 | 40 | //500 handler 41 | app.use(function(err, req, res, next) { 42 | console.log( 43 | "err", 44 | err, 45 | "timestamp: ", 46 | moment().utc().format("YYYY-MM-DD HH:mm:ss") 47 | ); 48 | return res.status(err.status || 500).json({ 49 | statusCode: 500, 50 | status: "error", 51 | message: "Internal server error", 52 | }); 53 | }); 54 | 55 | 56 | module.exports = app; -------------------------------------------------------------------------------- /back-end/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require("../app"); 8 | var debug = require("debug")("back-end:server"); 9 | var http = require("http"); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || "3000"); 16 | app.set("port", port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on("error", onError); 30 | server.on("listening", onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== "listen") { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === "string" ? "Pipe " + port : "Port " + port; 62 | 63 | // handle specific listen errors with friendly messages 64 | switch (error.code) { 65 | case "EACCES": 66 | console.error(bind + " requires elevated privileges"); 67 | process.exit(1); 68 | break; 69 | case "EADDRINUSE": 70 | console.error(bind + " is already in use"); 71 | process.exit(1); 72 | break; 73 | default: 74 | throw error; 75 | } 76 | } 77 | 78 | /** 79 | * Event listener for HTTP server "listening" event. 80 | */ 81 | 82 | function onListening() { 83 | var addr = server.address(); 84 | var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port; 85 | debug("Listening on " + bind); 86 | } 87 | -------------------------------------------------------------------------------- /back-end/config/mysql-connection.js: -------------------------------------------------------------------------------- 1 | var mysql = require("mysql"); 2 | var mysql_pool = mysql.createPool({ 3 | connectionLimit: 10, 4 | host: process.env.MYSQL_HOST, 5 | user: process.env.MYSQL_USER, 6 | password: process.env.MYSQL_PASSWORD, 7 | database: process.env.MYSQL_DB, 8 | }); 9 | 10 | module.exports = { 11 | mysql_pool: mysql_pool, 12 | }; 13 | -------------------------------------------------------------------------------- /back-end/config/mysql-sequelize.js: -------------------------------------------------------------------------------- 1 | const { Sequelize, DataTypes } = require("sequelize"); 2 | 3 | const sequelize = new Sequelize( 4 | process.env.MYSQL_DB, 5 | process.env.MYSQL_USER, 6 | process.env.MYSQL_PASSWORD, 7 | { 8 | host: process.env.MYSQL_HOST, 9 | dialect: "mysql", 10 | timezone: "+00:00", 11 | } 12 | ); 13 | 14 | module.exports = { 15 | sequelize, 16 | DataTypes, 17 | Sequelize, 18 | }; 19 | -------------------------------------------------------------------------------- /back-end/controllers/Earn.js: -------------------------------------------------------------------------------- 1 | const { sequelize } = require("../config/mysql-sequelize"); 2 | const { getUTCTime } = require("../utils/helperfun") 3 | const { Op, col } = require("sequelize"); 4 | 5 | const Earnings = require("../models/Earnings"); 6 | const TGUser = require("../models/TGUser"); 7 | 8 | // TODO :Remove the console in pro 9 | async function getscore(req, res, next) { 10 | try { 11 | 12 | const { user: tgUser } = req; 13 | 14 | if (!tgUser || tgUser.id == null) { 15 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 16 | } 17 | 18 | const userDetails = await Earnings.findOne({ where: { userid: tgUser.id } }); 19 | 20 | if (userDetails && userDetails != null) { 21 | const value = { 22 | checkin_score: userDetails.checkin_score, 23 | miner_points: userDetails.miner_points, 24 | referral_score: userDetails.referral_score, 25 | tap_score: userDetails.tap_score, 26 | enery_restore_time: userDetails.enery_restore_time, 27 | energy_remaning: userDetails.energy_remaning, 28 | game_level: userDetails.miner_level, 29 | }; 30 | return res.status(200).json({ message: 'Success', data: value }); 31 | } else { 32 | return res.status(200).json({ message: 'Success', data: [] }); 33 | } 34 | } catch (error) { 35 | console.error("Error retrieving data:", error); 36 | return next('An error occurred on the get score'); 37 | } 38 | } 39 | 40 | async function upscore(req, res, next) { 41 | try { 42 | const { score, energy_remaning, restore_time } = req.body; 43 | 44 | const tgUser = req.user; 45 | 46 | if (!tgUser || !tgUser.id) { 47 | res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 48 | } 49 | 50 | const { id: teleid } = tgUser; 51 | 52 | const userDetails = await Earnings.findOne({ where: { userid: teleid } }); 53 | 54 | //TODO : add score validation 55 | 56 | const updata = { 57 | tap_score: parseInt(score), 58 | energy_remaning: energy_remaning, 59 | enery_restore_time: restore_time, 60 | modified_date: getUTCTime("datetime"), 61 | }; 62 | 63 | if (userDetails) { 64 | const [updated] = await Earnings.update(updata, { where: { userid: teleid } }); 65 | if (updated > 0) { 66 | res.status(200).json({ message: 'Success', data: [] }); 67 | } else { 68 | returnres.status(409).json({ error: 'Conflict', message: 'Score update failed' }); 69 | } 70 | } else { 71 | return res.status(422).json({ error: 'Unprocessable Entity', message: 'Validation failed for the input data' }); 72 | } 73 | } catch (error) { 74 | console.error("Error updating points:", error); 75 | next("An error occurred on update score") 76 | } 77 | } 78 | 79 | 80 | module.exports = { 81 | getscore, 82 | upscore 83 | }; -------------------------------------------------------------------------------- /back-end/controllers/Leaderboard.js: -------------------------------------------------------------------------------- 1 | const { sequelize } = require("../config/mysql-sequelize"); 2 | const { getUTCTime } = require("../utils/helperfun") 3 | const { Op, col } = require("sequelize"); 4 | 5 | const Earnings = require("../models/Earnings"); 6 | const TGUser = require("../models/TGUser"); 7 | 8 | // TODO: need to complete 9 | async function allrank(req, res, next) { 10 | try { 11 | const tgUser = req.user; 12 | 13 | if (!tgUser || !tgUser.id) { 14 | res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 15 | } 16 | 17 | 18 | 19 | 20 | } catch (error) { 21 | 22 | } 23 | 24 | } 25 | 26 | 27 | module.exports = { 28 | allrank 29 | } -------------------------------------------------------------------------------- /back-end/controllers/Referral.js: -------------------------------------------------------------------------------- 1 | const { sequelize } = require("../config/mysql-sequelize"); 2 | const { getUTCTime } = require("../utils/helperfun") 3 | const { Op, col } = require("sequelize"); 4 | 5 | const Earnings = require("../models/Earnings"); 6 | const TGUser = require("../models/TGUser"); 7 | 8 | async function list(req, res, next) { 9 | 10 | try { 11 | 12 | const tgUser = req.user; 13 | 14 | if (tgUser == null && tgUser.id == null) { 15 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 16 | } 17 | 18 | const { id: teleid, referral_code: refCode } = tgUser; 19 | 20 | const UserData = await TGUser.findOne({ 21 | where: { 22 | userid: teleid, 23 | referral_code: refCode, 24 | }, 25 | }); 26 | 27 | if (!UserData && UserData == null) { 28 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 29 | } 30 | 31 | // TODO : need add pagination 32 | const friends = await TGUser.findAll({ 33 | where: { 34 | referral_by: refCode, 35 | }, 36 | order: [ 37 | ["created_date", "DESC"] 38 | ], 39 | limit: 100, 40 | offset: 0, 41 | }); 42 | 43 | // TODO : need to check game level 44 | const friendsData = friends.map((user) => ({ 45 | id: user.id, 46 | first_name: user.first_name, 47 | username: user.username, 48 | claimed: user.ref_claim, 49 | Premium: user.tg_premium_user, 50 | gamelevel: user.game_level || "LVL 1" 51 | })); 52 | 53 | if (friendsData != null) { 54 | return res.status(200).json({ message: 'Success', data: { refCode: refCode, friends: friendsData } }); 55 | } else { 56 | return res.status(200).json({ message: 'Success', data: { refCode: refCode, friends: [] } }); 57 | } 58 | 59 | } catch (error) { 60 | 61 | console.error("Error fetching Friends list:", error); 62 | return next('An error occurred on the get list of referrals'); 63 | 64 | } 65 | 66 | } 67 | async function claim(req, res, next) { 68 | try { 69 | const tgUser = req.user; 70 | const { friendID } = req.body; 71 | 72 | if (!tgUser || !tgUser.id || !tgUser.referral_code) { 73 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 74 | } 75 | 76 | const refCode = tgUser.referral_code 77 | 78 | const earnDetails = await Earnings.findOne({ 79 | where: { 80 | userid: tgUser.id, 81 | }, 82 | }); 83 | 84 | if (!earnDetails && earnDetails == null && !earnDetails.userid) { 85 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 86 | } 87 | const userDetails = await TGUser.findOne({ 88 | where: { 89 | [Op.and]: [ 90 | { id: friendID }, 91 | { ref_claim: "N" }, 92 | { referral_by: refCode }, 93 | ], 94 | }, 95 | }); 96 | 97 | if (!userDetails && userDetails == null) { 98 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 99 | } 100 | 101 | const referral_score = userDetails.tg_premium_user === "Y" ? process.env.PREMIUM : process.env.NON_PREMIUM; 102 | 103 | const earnUpdata = { 104 | referral_score: parseInt(earnDetails.referral_score) + parseInt(referral_score), 105 | tap_score: parseInt(earnDetails.tap_score) + parseInt(referral_score) 106 | }; 107 | const [updated] = await Earnings.update(earnUpdata, { 108 | where: { 109 | userid: tgUser.id, 110 | }, 111 | }); 112 | if (updated > 0) { 113 | const [isClaim] = await TGUser.update({ ref_claim: "Y" }, { 114 | where: { 115 | [Op.and]: [ 116 | { id: friendID }, 117 | { ref_claim: "N" }, 118 | { referral_by: refCode }, 119 | ], 120 | }, 121 | }); 122 | if (isClaim > 0) { 123 | return res.status(200).json({ message: 'Success', data: { friendid: friendID, claimedPoint: referral_score } }); 124 | } else { 125 | return res.status(409).json({ error: 'Conflict', message: 'Referral claim failed' }); 126 | } 127 | } else { 128 | return res.status(409).json({ error: 'Conflict', message: 'Referral claim failed' }); 129 | } 130 | } catch (error) { 131 | console.error("Error calim referral score:", error); 132 | next("An error occurred on calim referral score") 133 | } 134 | } 135 | 136 | async function claimAll(req, res, next) { 137 | try { 138 | const tgUser = req.user; 139 | const refCode = tgUser.referral_code 140 | if (!tgUser || !tgUser.id) { 141 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 142 | } 143 | 144 | const earnDetails = await Earnings.findOne({ where: { "userid": tgUser.id } }); 145 | 146 | if (!earnDetails || !earnDetails.userid) { 147 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 148 | } 149 | 150 | const unclaimedUsers = await TGUser.findAll({ 151 | where: { 152 | ref_claim: "N", 153 | referral_by: refCode 154 | }, 155 | }); 156 | 157 | if (unclaimedUsers.length === 0) { 158 | return res.status(200).json({ message: 'No unclaimed referrals found', data: [] }); 159 | } 160 | 161 | let totalReferralScore = 0; 162 | 163 | unclaimedUsers.forEach(user => { 164 | const referralScore = user.tg_premium_user === "Y" ? parseInt(process.env.PREMIUM) : parseInt(process.env.NON_PREMIUM); 165 | totalReferralScore += referralScore; 166 | }); 167 | 168 | const earnUpdate = { 169 | referral_score: parseInt(earnDetails.referral_score) + totalReferralScore, 170 | tap_score: parseInt(earnDetails.tap_score) + totalReferralScore 171 | }; 172 | 173 | const [updated] = await Earnings.update(earnUpdate, { where: { userid: tgUser.id } }); 174 | 175 | if (updated > 0) { 176 | const userIds = unclaimedUsers.map(user => user.userid); 177 | 178 | const [isClaim] = await TGUser.update({ ref_claim: "Y" }, { 179 | where: { 180 | userid: { 181 | [Op.in]: userIds 182 | }, 183 | referral_by: refCode 184 | } 185 | }); 186 | 187 | if (isClaim === userIds.length) { 188 | return res.status(200).json({ message: 'Success', data: { claimedPoints: totalReferralScore, claimedUsers: userIds } }); 189 | } else { 190 | return res.status(422).json({ error: 'Unprocessable Entity', message: 'Some users could not be updated' }); 191 | } 192 | } else { 193 | return res.status(409).json({ error: 'Conflict', message: 'Score update failed' }); 194 | } 195 | } catch (error) { 196 | console.error("Error claiming all referral scores:", error); 197 | next("Error on claiming all referral scores") 198 | } 199 | } 200 | 201 | module.exports = { 202 | list, 203 | claim, 204 | claimAll 205 | } -------------------------------------------------------------------------------- /back-end/controllers/Task.js: -------------------------------------------------------------------------------- 1 | const { sequelize } = require("../config/mysql-sequelize"); 2 | const { Op, col } = require("sequelize"); 3 | const { getUTCTime } = require("../utils/helperfun") 4 | const Earnings = require("../models/Earnings"); 5 | const TGUser = require("../models/TGUser"); 6 | const Tasks = require("../models/Tasks"); 7 | const moment = require('moment'); 8 | 9 | 10 | function getCheckinDetails(earnDetails) { 11 | 12 | earnDetails.current_streak = parseInt(earnDetails.current_streak); 13 | earnDetails.checkin_points = parseInt(earnDetails.checkin_points); 14 | 15 | const today = moment().utc().startOf('day'); 16 | 17 | let lastLoginDate = null; 18 | if (earnDetails.last_login_at) { 19 | lastLoginDate = moment(earnDetails.last_login_at).utc().startOf('day'); 20 | } 21 | 22 | const daysDifference = lastLoginDate ? today.diff(lastLoginDate, 'days') : null; 23 | 24 | let rewardPoints = 0; 25 | let rewardDay = 0; 26 | let dailycheckin = false; 27 | 28 | if (lastLoginDate === null || daysDifference > 3) { 29 | // First login or login after more than 3 days 30 | rewardPoints = 5000; 31 | rewardDay = 1; 32 | earnDetails.current_streak = 1; 33 | dailycheckin = true; 34 | 35 | } else if (daysDifference === 0) { 36 | // Already logged in today 37 | rewardPoints = 0; 38 | rewardDay = earnDetails.current_streak; 39 | dailycheckin = false; 40 | 41 | } else if (daysDifference === 1) { 42 | // Consecutive login 43 | earnDetails.current_streak += 1; 44 | rewardDay = earnDetails.current_streak; 45 | rewardPoints = earnDetails.current_streak * 5000; 46 | dailycheckin = true; 47 | 48 | } else if (daysDifference > 1 && daysDifference <= 3) { 49 | // Login after a short break (1-3 days) 50 | rewardPoints = 5000; 51 | rewardDay = 1; 52 | earnDetails.current_streak = 1; 53 | dailycheckin = true; 54 | 55 | } 56 | 57 | return { 58 | current_streak: earnDetails.current_streak, 59 | rewardPoints, 60 | rewardDay, 61 | dailycheckin, 62 | today 63 | } 64 | 65 | } 66 | 67 | 68 | async function list(req, res, next) { 69 | 70 | try { 71 | 72 | const tgUser = req.user; 73 | 74 | if (tgUser == null && tgUser.id == null) { 75 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 76 | } 77 | 78 | const { id: teleid } = tgUser; 79 | const earnDetails = await Earnings.findOne({ 80 | where: { 81 | userid: teleid, 82 | }, 83 | }); 84 | if (!earnDetails) { 85 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 86 | } 87 | const checkindetails = getCheckinDetails(earnDetails) 88 | const doneTaskIds = earnDetails.task ? earnDetails.task.split('|').map(id => parseInt(id)) : []; 89 | const whereClause = { 90 | status: "ACTIVE" 91 | }; 92 | 93 | const tasks = await Tasks.findAll({ 94 | where: whereClause, 95 | order: [ 96 | ["created_date", "DESC"] 97 | ], 98 | limit: 10, 99 | offset: 0, 100 | }); 101 | 102 | 103 | 104 | if (tasks == null || tasks.length === 0) { 105 | return res.status(200).json({ message: 'No task found', data: [] }); 106 | } 107 | 108 | const taskList = tasks.map((task) => ({ 109 | id: task.id, 110 | title: task.title, 111 | points: task.claim_score, 112 | url: task.follow_url, 113 | under_by: task.task_under_by, 114 | isClaimed: doneTaskIds.includes(task.id) ? "Y" : "N" 115 | })); 116 | 117 | if (checkindetails != null && taskList != null) { 118 | return res.status(200).json({ message: 'Success', data: { tasklist: taskList, checkin: checkindetails } }); 119 | } else { 120 | return res.status(200).json({ message: 'No task found', data: [] }); 121 | } 122 | 123 | } catch (error) { 124 | console.error("Error fetching task list:", error); 125 | return next('An error occurred while geting task list '); 126 | } 127 | } 128 | 129 | async function claim(req, res, next) { 130 | try { 131 | const tgUser = req.user; 132 | const { taskID } = req.body; 133 | 134 | if (!tgUser || !tgUser.id) { 135 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 136 | } 137 | 138 | const tasksDetails = await Tasks.findOne({ 139 | where: { 140 | [Op.and]: [ 141 | { id: taskID }, 142 | { status: "ACTIVE" } 143 | ], 144 | }, 145 | }); 146 | 147 | if (!tasksDetails && tasksDetails == null) { 148 | return res.status(422).json({ error: 'Unprocessable Entity', message: 'Validation failed for the input data' }); 149 | } 150 | 151 | const taskPoint = tasksDetails.claim_score 152 | const taskId = tasksDetails.id 153 | 154 | const earnDetails = await Earnings.findOne({ 155 | where: { 156 | userid: tgUser.id, 157 | }, 158 | }); 159 | 160 | if (!earnDetails && earnDetails == null) { 161 | return res.status(422).json({ error: 'Unprocessable Entity', message: 'Validation failed for the input data' }); 162 | } 163 | 164 | const doneTaskIds = earnDetails.task ? earnDetails.task.split('|').map(id => id) : []; 165 | 166 | if (doneTaskIds.includes(taskId)) { 167 | return res.status(422).json({ error: 'Unprocessable Entity', message: 'Invaild taks' }); 168 | } 169 | 170 | const earnUpdate = { 171 | task: `${taskId}|`, 172 | task_score: parseInt(earnDetails.task_score) + parseInt(taskPoint), 173 | tap_score: parseInt(earnDetails.tap_score) + parseInt(taskPoint) 174 | } 175 | 176 | const [updated] = await Earnings.update(earnUpdate, { 177 | where: { 178 | userid: tgUser.id, 179 | }, 180 | }); 181 | 182 | if (updated > 0) { 183 | return res.status(200).json({ message: 'Success', data: { taskid: taskId, taskscore: taskPoint } }); 184 | } else { 185 | return res.status(409).json({ error: 'Conflict', message: 'Taks claim failed ', data: { taskid: taskId } }); 186 | } 187 | } catch (error) { 188 | console.error("Error calim Task score:", error); 189 | next("An error occurred on calim Task score") 190 | } 191 | 192 | } 193 | 194 | async function checkin(req, res, next) { 195 | try { 196 | const tgUser = req.user; 197 | 198 | if (!tgUser || !tgUser.id) { 199 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 200 | } 201 | 202 | const earnDetails = await Earnings.findOne({ where: { userid: tgUser.id } }); 203 | 204 | if (!earnDetails || !earnDetails.userid) { 205 | return res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 206 | } 207 | 208 | const checkInData = getCheckinDetails(earnDetails); 209 | 210 | if (checkInData == null) { 211 | throw new Error("Checkin details not provided."); 212 | } 213 | if (checkInData.dailycheckin) { 214 | 215 | const earnUpdata = { 216 | current_streak: parseInt(checkInData.current_streak), 217 | checkin_score: parseInt(checkInData.rewardPoints), 218 | tap_score: parseInt(earnDetails.tap_score) + parseInt(checkInData.rewardPoints), 219 | recent_login: checkInData.today, 220 | } 221 | 222 | const [updated] = await Earnings.update(earnUpdata, { where: { userid: tgUser.id } }); 223 | 224 | if (updated > 0) { 225 | return res.status(200).json({ message: 'Success', data: checkInData }); 226 | } else { 227 | return res.status(422).json({ error: 'Unprocessable Entity', message: 'Checkin not updeted' }); 228 | } 229 | } else { 230 | return res.status(409).json({ error: 'Conflict', message: 'Not vaild checkin ' }); 231 | } 232 | } catch (error) { 233 | 234 | console.error("Error Dail check-in", error); 235 | next("Error on Dail check-in") 236 | 237 | } 238 | 239 | } 240 | 241 | 242 | 243 | module.exports = { 244 | list, 245 | claim, 246 | checkin, 247 | } -------------------------------------------------------------------------------- /back-end/controllers/Tg.js: -------------------------------------------------------------------------------- 1 | var jwt = require("jsonwebtoken"); 2 | const _ = require("lodash"); 3 | const { v4: uuidv4 } = require("uuid"); 4 | 5 | var TGUser = require("../models/TGUser"); 6 | const Earnings = require("../models/Earnings"); 7 | 8 | function isMobileDevice(userAgent) { 9 | return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( 10 | userAgent 11 | ); 12 | } 13 | 14 | async function auth(req, res, next) { 15 | var user_agent = req.headers["user-agent"]; 16 | var is_mobile = isMobileDevice(user_agent); 17 | if (user_agent === undefined || is_mobile !== true) { 18 | return res.status(403).json({ 19 | statusCode: 403, 20 | status: "error", 21 | message: "Only available for mobile devices", 22 | }); 23 | } 24 | 25 | try { 26 | var { 27 | id = null, 28 | username = "", 29 | first_name = "", 30 | last_name = "", 31 | language_code = "", 32 | referral_by = "", 33 | is_premium, 34 | } = req.body; 35 | 36 | if (!_.isNil(id)) { 37 | var tg_user = await TGUser.findOne({ 38 | where: { 39 | userid: id, 40 | }, 41 | }); 42 | 43 | var sync_data = {}; 44 | 45 | if (tg_user === null) { 46 | referral_code = uuidv4().replace(/-/g, ""); 47 | var tg_user_data = { 48 | userid: id, 49 | username: username, 50 | first_name: first_name, 51 | last_name: last_name, 52 | language_code: language_code, 53 | referral_by: referral_by, 54 | referral_code: referral_code, 55 | }; 56 | 57 | if (is_premium === true) { 58 | tg_user_data["tg_premium_user"] = "Y"; 59 | } 60 | var create_tg_user = await TGUser.create(tg_user_data); 61 | if (!create_tg_user) { 62 | throw new Error( 63 | `TGUser insert failed in /api/tg/auth ${JSON.stringify( 64 | tg_user_data 65 | )}` 66 | ); 67 | } else { 68 | const indata = { userid: id }; 69 | const newUser = await Earnings.create(indata); 70 | if (!newUser) { 71 | throw new Error( 72 | `TGUser insert failed in /api/tg/auth ${JSON.stringify( 73 | tg_user_data 74 | )}` 75 | ); 76 | } 77 | } 78 | 79 | sync_data = { 80 | referral_code: referral_code, 81 | miner_level: 0, 82 | last_mine_date: "", 83 | score: 0, 84 | }; 85 | } else { 86 | //TODO: confirm this else block again 87 | var earnings = await Earnings.findOne({ 88 | where: { 89 | userid: tg_user.userid, 90 | }, 91 | }); 92 | sync_data = { 93 | referral_code: tg_user.referral_code, 94 | miner_level: earnings.miner_level === null ? 0 : earnings.miner_level, 95 | last_mine_date: earnings.last_mine_date === null ? "" : earnings.last_mine_date, 96 | score: earnings.tap_points, 97 | }; 98 | } 99 | 100 | var token = jwt.sign({ 101 | id: id, 102 | username: username, 103 | referral_code: sync_data["referral_code"], 104 | }, 105 | process.env.SECRET_KEY 106 | ); 107 | sync_data["auth_token"] = token; 108 | 109 | return res.status(200).json({ 110 | statusCode: 200, 111 | status: "success", 112 | sync_data: sync_data, 113 | message: "Successfully authenticated", 114 | }); 115 | } 116 | 117 | return res.status(400).json({ 118 | statusCode: 400, 119 | status: "error", 120 | message: "Invalid Data", 121 | }); 122 | } catch (err) { 123 | next(err); 124 | } 125 | } 126 | 127 | module.exports = { 128 | auth, 129 | }; -------------------------------------------------------------------------------- /back-end/controllers/reward.js: -------------------------------------------------------------------------------- 1 | const moment = require("moment"); 2 | const Earnings = require("../models/Earnings"); 3 | 4 | function getSecondOfDayUTC(date_time = null) { 5 | const now = date_time !== null ? date_time : new Date(); 6 | const hours = now.getUTCHours(); 7 | const minutes = now.getUTCMinutes(); 8 | const seconds = now.getUTCSeconds(); 9 | 10 | const final_seconds = hours * 3600 + minutes * 60 + seconds; 11 | 12 | return final_seconds; 13 | } 14 | 15 | function findBatch(date_time = null) { 16 | const total_seconds_in_day = 24 * 3600; 17 | const number_of_batches = 8; 18 | const seconds_per_batch = total_seconds_in_day / number_of_batches; 19 | 20 | const seconds_of_day = getSecondOfDayUTC(date_time); 21 | const batch = Math.floor(seconds_of_day / seconds_per_batch) + 1; 22 | 23 | return batch; 24 | } 25 | 26 | function getCurrentDateFormatted() { 27 | var now = new Date(); 28 | 29 | var year = now.getUTCFullYear(); 30 | var month = (now.getUTCMonth() + 1).toString().padStart(2, "0"); // getUTCMonth() returns month index (0-11) 31 | var day = now.getUTCDate().toString().padStart(2, "0"); 32 | 33 | return year + "-" + month + "-" + day; 34 | } 35 | 36 | function getRequiredScore(miner_level) { 37 | if (miner_level === 1) { 38 | return [20000, "20k"]; 39 | } else if (miner_level === 2) { 40 | return [100000, "100k"]; 41 | } else if (miner_level === 3) { 42 | return [200000, "200k"]; 43 | } else if (miner_level === 4) { 44 | return [500000, "500k"]; 45 | } else if (miner_level === 5) { 46 | return [1000000, "1M"]; 47 | } else { 48 | throw new Error( 49 | `miner_level not found for getRequiredScore(${miner_level})` 50 | ); 51 | } 52 | } 53 | 54 | function getClaimScore(miner_level) { 55 | if (miner_level === 1) { 56 | return [10000, "10k"]; 57 | } else if (miner_level === 2) { 58 | return [50000, "50k"]; 59 | } else if (miner_level === 3) { 60 | return [75000, "75k"]; 61 | } else if (miner_level === 4) { 62 | return [100000, "100k"]; 63 | } else if (miner_level === 5) { 64 | return [150000, "150k"]; 65 | } else { 66 | throw new Error(`miner_level not found for getClaimScore(${miner_level})`); 67 | } 68 | } 69 | 70 | async function upgrade(req, res, next) { 71 | var userid = req.user.id; 72 | 73 | var earnings = await Earnings.findOne({ 74 | where: { 75 | userid: userid, 76 | }, 77 | }); 78 | 79 | if (earnings === null) { 80 | return next(`No earnings record found for ${userid}`); 81 | } 82 | 83 | let miner_level = !isNaN(parseInt(earnings.miner_level)) ? 84 | parseInt(earnings.miner_level) : 85 | 0; 86 | let score = !isNaN(parseInt(earnings.tap_points)) ? 87 | parseInt(earnings.tap_points) : 88 | 0; 89 | 90 | if (miner_level < 5) { 91 | let next_miner_level = miner_level + 1; 92 | const [required_score, score_in_text] = getRequiredScore(next_miner_level); 93 | if (score >= required_score) { 94 | score -= required_score; 95 | await earnings.update({ 96 | tap_points: score, 97 | miner_level: next_miner_level, 98 | }); 99 | return res.status(200).json({ 100 | statusCode: 200, 101 | status: "success", 102 | miner_level: next_miner_level, 103 | score: score, 104 | message: "Successfully upgraded", 105 | }); 106 | } else { 107 | return next( 108 | `Insufficient balance for ${userid} to upgrade from ${miner_level} to ${next_miner_level}` 109 | ); 110 | } 111 | } else { 112 | return next(`User Exceed upgrade level ${userid}`); 113 | } 114 | } 115 | 116 | async function claim(req, res, next) { 117 | var userid = req.user.id; 118 | 119 | var earnings = await Earnings.findOne({ 120 | where: { 121 | userid: userid, 122 | }, 123 | }); 124 | 125 | if (earnings !== null) { 126 | let miner_level = !isNaN(parseInt(earnings.miner_level)) ? 127 | parseInt(earnings.miner_level) : 128 | 0; 129 | let last_mine_date = earnings.last_mine_date; 130 | last_mine_date = last_mine_date !== null ? last_mine_date : null; 131 | let last_mine_batch = 132 | last_mine_date !== null ? findBatch(last_mine_date) : 0; 133 | let currrent_batch = findBatch(); 134 | 135 | var claim = false; 136 | if (miner_level > 0) { 137 | if (last_mine_date === null) { 138 | claim = true; 139 | } else if (last_mine_date !== null) { 140 | let last_date = moment.utc(last_mine_date).format("YYYY-MM-DD"); 141 | //TODO: need to change this function to moment 142 | let current_date = getCurrentDateFormatted(); 143 | if (current_date > last_date) { 144 | claim = true; 145 | } else if ( 146 | current_date == last_date && 147 | last_mine_batch < currrent_batch 148 | ) { 149 | claim = true; 150 | } 151 | } 152 | } 153 | 154 | if (claim) { 155 | let score = !isNaN(parseInt(earnings.tap_points)) ? 156 | parseInt(earnings.tap_points) : 157 | 0; 158 | 159 | const [claim_score, clain_score_text] = getClaimScore(miner_level); 160 | score += claim_score; 161 | last_mine_date = new Date(); 162 | await earnings.update({ 163 | tap_points: score, 164 | last_mine_date: last_mine_date, 165 | }); 166 | return res.status(200).json({ 167 | statusCode: 200, 168 | status: "success", 169 | last_mine_date: last_mine_date, 170 | score: score, 171 | message: "Successfully claimed", 172 | }); 173 | } else { 174 | return next(`Ivalid claim request for ${userid}`); 175 | } 176 | } 177 | 178 | return next(`No earnings record found for ${userid}`); 179 | } 180 | 181 | module.exports = { 182 | claim, 183 | upgrade, 184 | }; -------------------------------------------------------------------------------- /back-end/middlewares/tg.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | 3 | function tgauth_required(req, res, next) { 4 | const auth_header = req.headers["authorization"]; 5 | if (!auth_header) { 6 | return res.status(401).json({ 7 | status: "error", 8 | message: "unauthorized access: Invalid or missing token", 9 | }); 10 | } 11 | 12 | const token = auth_header.split(" ")[1]; 13 | if (!token) { 14 | return res.status(401).json({ 15 | status: "error", 16 | message: "unauthorized access: Invalid or missing token", 17 | }); 18 | } 19 | 20 | jwt.verify(token, process.env.SECRET_KEY, (err, user) => { 21 | if (err) { 22 | return res.status(401).json({ 23 | status: "error", 24 | message: "unauthorized access: invalid or missing token", 25 | }); 26 | } 27 | 28 | req.user = user; 29 | 30 | next(); 31 | }); 32 | } 33 | 34 | module.exports = { 35 | tgauth_required, 36 | }; -------------------------------------------------------------------------------- /back-end/models/Earnings.js: -------------------------------------------------------------------------------- 1 | const { 2 | sequelize, 3 | DataTypes, 4 | Sequelize, 5 | } = require("../config/mysql-sequelize"); 6 | 7 | const Earnings = sequelize.define( 8 | "Earnings", { 9 | id: { 10 | type: DataTypes.INTEGER, 11 | autoIncrement: true, 12 | primaryKey: true, 13 | }, 14 | userid: { 15 | type: DataTypes.BIGINT, 16 | allowNull: false, 17 | references: { 18 | model: "tg_users", 19 | key: "userid", 20 | }, 21 | }, 22 | tap_score: { 23 | type: DataTypes.BIGINT, 24 | defaultValue: 0, 25 | }, 26 | referral_score: { 27 | type: DataTypes.BIGINT, 28 | defaultValue: 0, 29 | }, 30 | checkin_score: { 31 | type: DataTypes.BIGINT, 32 | defaultValue: 0, 33 | }, 34 | task: { 35 | type: DataTypes.STRING, 36 | defaultValue: null, 37 | }, 38 | task_score: { 39 | type: DataTypes.BIGINT, 40 | defaultValue: 0, 41 | }, 42 | game_level: { 43 | type: DataTypes.STRING, 44 | defaultValue: null, 45 | }, 46 | current_streak: { 47 | type: DataTypes.INTEGER, 48 | defaultValue: 0, 49 | }, 50 | enery_restore_time: { 51 | type: DataTypes.DATE, 52 | defaultValue: null, 53 | }, 54 | energy_remaning: { 55 | type: DataTypes.INTEGER, 56 | defaultValue: 2000, 57 | }, 58 | last_login_at: { 59 | type: DataTypes.DATE, 60 | defaultValue: null, 61 | }, 62 | miner_level: { 63 | type: DataTypes.INTEGER, 64 | defaultValue: 0, 65 | }, 66 | last_mine_date: { 67 | type: DataTypes.DATE, 68 | defaultValue: null, 69 | }, 70 | created_date: { 71 | type: DataTypes.DATE, 72 | allowNull: false, 73 | defaultValue: Sequelize.NOW, 74 | }, 75 | modified_date: { 76 | type: DataTypes.DATE, 77 | allowNull: false, 78 | defaultValue: Sequelize.NOW, 79 | }, 80 | }, { 81 | tableName: "earnings", 82 | timestamps: false, 83 | indexes: [{ 84 | fields: ["userid"], 85 | }, ], 86 | hooks: { 87 | beforeUpdate: (earnings, options) => { 88 | earnings.modifiydate = new Date(); 89 | }, 90 | }, 91 | } 92 | ); 93 | 94 | module.exports = Earnings; -------------------------------------------------------------------------------- /back-end/models/TGUser.js: -------------------------------------------------------------------------------- 1 | const { 2 | sequelize, 3 | DataTypes, 4 | Sequelize, 5 | } = require("../config/mysql-sequelize"); 6 | 7 | const TGUser = sequelize.define( 8 | "TGUser", 9 | { 10 | id: { 11 | type: DataTypes.INTEGER, 12 | autoIncrement: true, 13 | primaryKey: true, 14 | }, 15 | userid: { 16 | type: DataTypes.INTEGER, 17 | allowNull: false, 18 | }, 19 | username: { 20 | type: DataTypes.STRING(100), 21 | allowNull: true, 22 | }, 23 | first_name: { 24 | type: DataTypes.STRING(100), 25 | allowNull: true, 26 | }, 27 | last_name: { 28 | type: DataTypes.STRING(100), 29 | allowNull: true, 30 | }, 31 | langauage_code: { 32 | type: DataTypes.STRING(30), 33 | allowNull: true, 34 | }, 35 | referral_code: { 36 | type: DataTypes.STRING(100), 37 | allowNull: true, 38 | }, 39 | referral_by: { 40 | type: DataTypes.STRING(100), 41 | allowNull: true, 42 | }, 43 | ref_claim: { 44 | type: DataTypes.ENUM("Y", "N"), 45 | allowNull: false, 46 | defaultValue: "N", 47 | }, 48 | tg_premium_user: { 49 | type: DataTypes.ENUM("Y", "N"), 50 | allowNull: false, 51 | defaultValue: "N", 52 | }, 53 | created_date: { 54 | type: DataTypes.DATE, 55 | allowNull: false, 56 | defaultValue: Sequelize.NOW, 57 | }, 58 | modified_date: { 59 | type: DataTypes.DATE, 60 | allowNull: false, 61 | defaultValue: Sequelize.NOW, 62 | }, 63 | }, 64 | { 65 | tableName: "tg_users", 66 | timestamps: true, 67 | createdAt: "created_date", 68 | updatedAt: "modified_date", 69 | } 70 | ); 71 | 72 | module.exports = TGUser; 73 | -------------------------------------------------------------------------------- /back-end/models/Tasks.js: -------------------------------------------------------------------------------- 1 | const { sequelize, DataTypes, Sequelize } = require("../config/mysql-sequelize");; 2 | 3 | 4 | const Tasks = sequelize.define( 5 | "Tasks", { 6 | id: { 7 | type: DataTypes.BIGINT, 8 | autoIncrement: true, 9 | primaryKey: true, 10 | }, 11 | title: { 12 | type: DataTypes.STRING(200), 13 | allowNull: false, 14 | }, 15 | claim_score: { 16 | type: DataTypes.BIGINT, 17 | allowNull: false, 18 | }, 19 | follow_url: { 20 | type: DataTypes.STRING(255), 21 | allowNull: true, 22 | }, 23 | task_under_by: { 24 | type: DataTypes.STRING(200), 25 | allowNull: true, 26 | }, 27 | task_add_by: { 28 | type: DataTypes.BIGINT, 29 | allowNull: false, 30 | }, 31 | status: { 32 | type: DataTypes.ENUM('ACTIVE', 'INACTIVE', 'HOLD'), 33 | defaultValue: 'ACTIVE', 34 | }, 35 | created_date: { 36 | type: DataTypes.DATE, 37 | allowNull: false, 38 | defaultValue: Sequelize.NOW, 39 | }, 40 | modified_date: { 41 | type: DataTypes.DATE, 42 | allowNull: false, 43 | defaultValue: Sequelize.NOW, 44 | }, 45 | }, { 46 | tableName: "tasks", 47 | timestamps: false, 48 | hooks: { 49 | beforeUpdate: (task, options) => { 50 | task.modified_date = new Date(); 51 | }, 52 | }, 53 | } 54 | ); 55 | 56 | module.exports = Tasks; -------------------------------------------------------------------------------- /back-end/models/index.js: -------------------------------------------------------------------------------- 1 | const TGUser = require('./TGUser'); 2 | const Earnings = require('./Earnings'); 3 | 4 | // Define associations 5 | TGUser.hasOne(Earnings, { foreignKey: 'userid', sourceKey: 'userid' }); 6 | Earnings.belongsTo(TGUser, { foreignKey: 'userid', targetKey: 'userid' }); 7 | 8 | module.exports = { 9 | TGUser, 10 | Earnings, 11 | }; -------------------------------------------------------------------------------- /back-end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "back-end", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "nodemon ./bin/www" 7 | }, 8 | "dependencies": { 9 | "bcrypt": "^5.1.1", 10 | "cookie-parser": "~1.4.4", 11 | "date-fns": "^3.6.0", 12 | "debug": "~2.6.9", 13 | "dotenv": "^16.4.5", 14 | "ejs": "^3.1.10", 15 | "express": "^4.19.2", 16 | "express-session": "^1.18.0", 17 | "http-errors": "~1.6.3", 18 | "jsonwebtoken": "^9.0.2", 19 | "lodash": "^4.17.21", 20 | "moment": "^2.30.1", 21 | "morgan": "~1.9.1", 22 | "mysql": "^2.18.1", 23 | "mysql2": "^3.9.3", 24 | "nodemon": "^3.1.3", 25 | "pug": "^3.0.3", 26 | "sequelize": "^6.37.2", 27 | "uuid": "^9.0.1" 28 | }, 29 | "devDependencies": { 30 | "sequelize-cli": "^6.6.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /back-end/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /back-end/routes/api/Earn.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | const tgMiddleware = require("../../middlewares/tg"); 5 | var earn = require("../../controllers/Earn"); 6 | 7 | router.get("/getscore", tgMiddleware.tgauth_required, earn.getscore); 8 | router.post("/upscore", tgMiddleware.tgauth_required, earn.upscore); 9 | 10 | module.exports = router; -------------------------------------------------------------------------------- /back-end/routes/api/Leaderboard.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | const tgMiddleware = require("../../middlewares/tg"); 5 | var Leaderboard = require("../../controllers/Leaderboard"); 6 | 7 | router.get("/allrank", tgMiddleware.tgauth_required, Leaderboard.allrank); 8 | 9 | 10 | 11 | module.exports = router; -------------------------------------------------------------------------------- /back-end/routes/api/Main.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | var tgauthRouter = require("./Tg"); 5 | var gameRouter = require("./game"); //need to remove 6 | var rewardRouter = require("./Reward"); 7 | var earnRouter = require("./Earn"); 8 | var referralRouter = require("./Referral"); 9 | var taskRouter = require("./Task"); 10 | var Leaderboard = require("./Leaderboard"); 11 | 12 | router.use("/tg", tgauthRouter); 13 | router.use("/game", gameRouter); //need to remove 14 | router.use("/reward", rewardRouter); 15 | router.use("/earn", earnRouter); 16 | router.use("/referral", referralRouter); 17 | router.use("/task", taskRouter) 18 | router.use("/leaderboard", Leaderboard); 19 | 20 | 21 | 22 | module.exports = router; -------------------------------------------------------------------------------- /back-end/routes/api/Referral.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | const tgMiddleware = require("../../middlewares/tg"); 5 | var Referral = require("../../controllers/Referral"); 6 | 7 | router.get("/list", tgMiddleware.tgauth_required, Referral.list); 8 | router.post("/claim", tgMiddleware.tgauth_required, Referral.claim); 9 | router.post("/claimall", tgMiddleware.tgauth_required, Referral.claimAll); 10 | 11 | 12 | module.exports = router; -------------------------------------------------------------------------------- /back-end/routes/api/Reward.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | const tgMiddleware = require("../../middlewares/tg"); 5 | var reward = require("../../controllers/reward"); 6 | 7 | router.get("/claim", tgMiddleware.tgauth_required, reward.claim); 8 | router.get("/upgrade", tgMiddleware.tgauth_required, reward.upgrade); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /back-end/routes/api/Task.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | const tgMiddleware = require("../../middlewares/tg"); 5 | var Task = require("../../controllers/Task"); 6 | 7 | router.get("/list", tgMiddleware.tgauth_required, Task.list); 8 | router.post("/claim", tgMiddleware.tgauth_required, Task.claim); 9 | router.post("/checkin", tgMiddleware.tgauth_required, Task.checkin); 10 | 11 | 12 | module.exports = router; -------------------------------------------------------------------------------- /back-end/routes/api/Tg.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | var tg = require("../../controllers/Tg"); 5 | 6 | router.post("/auth", tg.auth); 7 | 8 | module.exports = router; -------------------------------------------------------------------------------- /back-end/routes/api/game.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | const gameController = require("../../controllers/game"); 5 | const tgMiddleware = require("../../middlewares/tg"); 6 | 7 | // Use POST for routes that accept request body data 8 | router.post("/upscore", tgMiddleware.tgauth_required, gameController.upscore); 9 | router.post("/getscore", tgMiddleware.tgauth_required, gameController.getscore); 10 | router.post("/getref", tgMiddleware.tgauth_required, gameController.getref); 11 | router.post("/refclaim", tgMiddleware.tgauth_required, gameController.refclaim); 12 | router.post("/getcheckin", tgMiddleware.tgauth_required, gameController.getCheckin); 13 | router.post("/upcheckin", tgMiddleware.tgauth_required, gameController.upCheckin); 14 | router.post("/usersrank", tgMiddleware.tgauth_required, gameController.getAllUserRank); 15 | 16 | 17 | 18 | module.exports = router; -------------------------------------------------------------------------------- /back-end/utils/helperfun.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | 3 | function getUTCTime(param = null) { 4 | if (param === 'timestamp') { 5 | return moment().utc().valueOf(); // Returns the UTC timestamp 6 | } else if (param === 'datetime') { 7 | return moment().utc().format('YYYY-MM-DD HH:mm:ss'); 8 | } else if (param === null) { 9 | return moment().utc(); 10 | } else { 11 | return null; 12 | } 13 | } 14 | 15 | function convertDateTime(dateTimeString) { 16 | const formattedDateTime = moment(dateTimeString).utc().format('YYYY-MM-DD HH:mm:ss'); 17 | const timestamp = moment(dateTimeString).utc().valueOf(); 18 | return { 19 | formattedDateTime: formattedDateTime, 20 | timestamp: timestamp 21 | }; 22 | } 23 | 24 | module.exports = { 25 | getUTCTime, 26 | convertDateTime 27 | }; -------------------------------------------------------------------------------- /back-end/views/maintenance.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Tap Tap 7 | 8 | 9 | 10 |

Under Maintenance Break 🛠️

11 | 12 | -------------------------------------------------------------------------------- /front-end/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /front-end/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tap Tap 8 | 9 | 10 | 11 | 12 | 13 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /front-end/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "https://taptap.bot", 3 | "name": "TapTap", 4 | "iconUrl": "https://i.ibb.co/fdwcwtw/Green-Symbol-Black-BG-No-Glow-Square.png" 5 | } -------------------------------------------------------------------------------- /front-end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "front-end", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.7.2", 14 | "framer-motion": "^11.2.10", 15 | "moment": "^2.30.1", 16 | "react": "^18.2.0", 17 | "react-device-detect": "^2.2.3", 18 | "react-dom": "^18.2.0", 19 | "react-router-dom": "^6.22.3", 20 | "react-toastify": "^10.0.5", 21 | "@tonconnect/ui-react": "^2.0.5", 22 | "react-use-measure": "^2.1.1" 23 | }, 24 | "devDependencies": { 25 | "@types/react": "^18.2.66", 26 | "@types/react-dom": "^18.2.22", 27 | "@vitejs/plugin-react": "^4.2.1", 28 | "autoprefixer": "^10.4.19", 29 | "eslint": "^8.57.0", 30 | "eslint-plugin-react": "^7.34.1", 31 | "eslint-plugin-react-hooks": "^4.6.0", 32 | "eslint-plugin-react-refresh": "^0.4.6", 33 | "postcss": "^8.4.38", 34 | "tailwindcss": "^3.4.3", 35 | "vite": "^5.2.0" 36 | } 37 | } -------------------------------------------------------------------------------- /front-end/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /front-end/src/App.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --radial-gradient-background: 250, 250, 250; 3 | --solid-color-background: 15, 15, 15; 4 | --overlay-color: 255, 255, 255; 5 | } 6 | 7 | body { 8 | @apply bg-neutral-950; 9 | } 10 | 11 | .radial-gradient { 12 | background: radial-gradient( circle at 50% 0%, rgba(var(--radial-gradient-background), 0.05) 0%, transparent 60%) rgba(var(--solid-color-background), 1); 13 | } 14 | 15 | .linear-mask { 16 | mask-image: linear-gradient( -75deg, white calc(var(--x) + 20%), transparent calc(var(--x) + 30%), white calc(var(--x) + 100%)); 17 | -webkit-mask-image: linear-gradient( -75deg, white calc(var(--x) + 20%), transparent calc(var(--x) + 30%), white calc(var(--x) + 100%)); 18 | } 19 | 20 | .linear-overlay { 21 | background-image: linear-gradient( -75deg, rgba(var(--overlay-color), 0.1) calc(var(--x) + 20%), rgba(var(--overlay-color), 0.5) calc(var(--x) + 25%), rgba(var(--overlay-color), 0.1) calc(var(--x) + 100%)); 22 | mask: linear-gradient(black, black) content-box, linear-gradient(black, black); 23 | -webkit-mask: linear-gradient(black, black) content-box, linear-gradient(black, black); 24 | mask-composite: exclude; 25 | -webkit-mask-composite: xor; 26 | } -------------------------------------------------------------------------------- /front-end/src/App.jsx: -------------------------------------------------------------------------------- 1 | import MainRouter from "./routes/MainRouter"; 2 | 3 | function App() { 4 | return ; 5 | } 6 | 7 | export default App; 8 | -------------------------------------------------------------------------------- /front-end/src/assets/font/SF-Pro-Display-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/font/SF-Pro-Display-Bold.otf -------------------------------------------------------------------------------- /front-end/src/assets/font/SF-Pro-Display-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/font/SF-Pro-Display-Medium.otf -------------------------------------------------------------------------------- /front-end/src/assets/img/Logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Artboard 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /front-end/src/assets/img/actualizar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/actualizar.png -------------------------------------------------------------------------------- /front-end/src/assets/img/back-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /front-end/src/assets/img/background-hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/background-hero.png -------------------------------------------------------------------------------- /front-end/src/assets/img/bolt-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /front-end/src/assets/img/boost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/boost.png -------------------------------------------------------------------------------- /front-end/src/assets/img/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /front-end/src/assets/img/coin-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/coin-background.png -------------------------------------------------------------------------------- /front-end/src/assets/img/coin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/coin.png -------------------------------------------------------------------------------- /front-end/src/assets/img/coin_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/coin_old.png -------------------------------------------------------------------------------- /front-end/src/assets/img/coins.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front-end/src/assets/img/defence.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /front-end/src/assets/img/earn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/earn.png -------------------------------------------------------------------------------- /front-end/src/assets/img/energy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/energy.png -------------------------------------------------------------------------------- /front-end/src/assets/img/energy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /front-end/src/assets/img/friends.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front-end/src/assets/img/friends_old.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /front-end/src/assets/img/gift.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front-end/src/assets/img/giftbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/giftbox.png -------------------------------------------------------------------------------- /front-end/src/assets/img/giftbox1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/giftbox1.png -------------------------------------------------------------------------------- /front-end/src/assets/img/giftbox2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/giftbox2.png -------------------------------------------------------------------------------- /front-end/src/assets/img/giftbox3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/giftbox3.png -------------------------------------------------------------------------------- /front-end/src/assets/img/health.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /front-end/src/assets/img/ime.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/ime.jpg -------------------------------------------------------------------------------- /front-end/src/assets/img/ime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/ime.png -------------------------------------------------------------------------------- /front-end/src/assets/img/leaderboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front-end/src/assets/img/leaderboard_old.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /front-end/src/assets/img/logo-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/logo-black.png -------------------------------------------------------------------------------- /front-end/src/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/logo.png -------------------------------------------------------------------------------- /front-end/src/assets/img/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/main.png -------------------------------------------------------------------------------- /front-end/src/assets/img/mine-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/mine-bg.png -------------------------------------------------------------------------------- /front-end/src/assets/img/not.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/not.png -------------------------------------------------------------------------------- /front-end/src/assets/img/people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/people.png -------------------------------------------------------------------------------- /front-end/src/assets/img/play-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /front-end/src/assets/img/question-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /front-end/src/assets/img/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/ref.png -------------------------------------------------------------------------------- /front-end/src/assets/img/robot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/robot-1.png -------------------------------------------------------------------------------- /front-end/src/assets/img/robot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/robot-2.png -------------------------------------------------------------------------------- /front-end/src/assets/img/robot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/robot-3.png -------------------------------------------------------------------------------- /front-end/src/assets/img/robot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/robot-4.png -------------------------------------------------------------------------------- /front-end/src/assets/img/stars-robo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /front-end/src/assets/img/task.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front-end/src/assets/img/task_old.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /front-end/src/assets/img/token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/img/token.png -------------------------------------------------------------------------------- /front-end/src/assets/img/upgrade.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /front-end/src/assets/img/wallet-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /front-end/src/assets/sounds/ambient-piano-logo-165357.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/sounds/ambient-piano-logo-165357.mp3 -------------------------------------------------------------------------------- /front-end/src/assets/sounds/click-21156.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/sounds/click-21156.mp3 -------------------------------------------------------------------------------- /front-end/src/assets/sounds/lofi-guitar-105361.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/sounds/lofi-guitar-105361.mp3 -------------------------------------------------------------------------------- /front-end/src/assets/sounds/mixkit-arcade-game-jump-coin-216.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/sounds/mixkit-arcade-game-jump-coin-216.wav -------------------------------------------------------------------------------- /front-end/src/assets/sounds/mixkit-arcade-game-opener-222.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebeat/taptap/5cbafcdb2b589a4dd2dd7dd0e4094772d1fadd7d/front-end/src/assets/sounds/mixkit-arcade-game-opener-222.wav -------------------------------------------------------------------------------- /front-end/src/components/taptap/AnimatedCounter.jsx: -------------------------------------------------------------------------------- 1 | 2 | import { animate, useInView, useIsomorphicLayoutEffect } from "framer-motion"; 3 | import { useRef } from "react"; 4 | 5 | const AnimatedCounter = ({ from, to, animationOptions }) => { 6 | const ref = useRef(null); 7 | const inView = useInView(ref, { once: true }); 8 | const formatNumber = (value) => { 9 | return new Intl.NumberFormat("en-US").format(value); 10 | }; 11 | useIsomorphicLayoutEffect(() => { 12 | const element = ref.current; 13 | 14 | if (!element) return; 15 | if (!inView) return; 16 | 17 | // Set initial value 18 | element.textContent = String(from); 19 | 20 | // If reduced motion is enabled in system's preferences 21 | if (window.matchMedia("(prefers-reduced-motion)").matches) { 22 | element.textContent = String(to); 23 | return; 24 | } 25 | 26 | const controls = animate(from, to, { 27 | duration: 0.5, 28 | ease: "easeOut", 29 | ...animationOptions, 30 | onUpdate(value) { 31 | element.textContent = formatNumber(value.toFixed(0)); 32 | }, 33 | }); 34 | 35 | // Cancel on unmount 36 | return () => { 37 | controls.stop(); 38 | }; 39 | }, [ref, inView, from, to]); 40 | 41 | return ; 42 | }; 43 | 44 | export default AnimatedCounter; 45 | -------------------------------------------------------------------------------- /front-end/src/components/taptap/Drawer.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import useMeasure from "react-use-measure"; 3 | import { 4 | useDragControls, 5 | useMotionValue, 6 | useAnimate, 7 | motion, 8 | } from "framer-motion"; 9 | 10 | function Drawer({ open, setOpen, children }) { 11 | const [scope, animate] = useAnimate(); 12 | const [drawerRef, { height }] = useMeasure(); 13 | 14 | const y = useMotionValue(0); 15 | const controls = useDragControls(); 16 | 17 | const handleClose = async () => { 18 | animate(scope.current, { 19 | opacity: [1, 0], 20 | }); 21 | 22 | const yStart = typeof y.get() === "number" ? y.get() : 0; 23 | 24 | await animate("#drawer", { 25 | y: [yStart, height], 26 | }); 27 | 28 | setOpen(false); 29 | }; 30 | 31 | return ( 32 | <> 33 | {open && ( 34 | 41 | e.stopPropagation()} 45 | initial={{ y: "100%" }} 46 | animate={{ y: "0%" }} 47 | transition={{ 48 | ease: "easeInOut", 49 | }} 50 | className="absolute bottom-0 h-1/3 w-full overflow-hidden rounded-t-3xl bg-[#0b0b0b] border-t-2" 51 | style={{ y }} 52 | drag="y" 53 | dragControls={controls} 54 | onDragEnd={() => { 55 | if (y.get() >= 100) { 56 | handleClose(); 57 | } 58 | }} 59 | dragListener={false} 60 | dragConstraints={{ 61 | top: 0, 62 | bottom: 0, 63 | }} 64 | dragElastic={{ 65 | top: 0, 66 | bottom: 0.5, 67 | }} 68 | > 69 |
70 | 76 |
77 |
{children}
78 |
79 |
80 | )} 81 | 82 | ); 83 | } 84 | 85 | export default Drawer; 86 | -------------------------------------------------------------------------------- /front-end/src/components/taptap/FriendsListItem.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { motion } from "framer-motion"; 3 | 4 | const FriendsListItem = ({ 5 | name = "Unknown", 6 | level = "N/A", 7 | icon = "https://via.placeholder.com/150", 8 | displayType = "", 9 | rank = "", 10 | balance = "", 11 | profile = "https://via.placeholder.com/150", 12 | onButtonClick = () => {}, 13 | buttonDisabled = false, 14 | }) => { 15 | return ( 16 |
20 |

{rank}

21 | Profile 26 |
27 |

{name}

28 |

29 | {balance} Leader Icon 30 |

31 |

{level}

32 |
33 | {displayType === "leader" && ( 34 |
35 | )} 36 | {displayType === "friend" && !buttonDisabled && ( 37 | 47 | 59 | 60 | 61 | 62 | 63 | 64 | )} 65 | {displayType === "checkin" && ( 66 | !buttonDisabled ? ( 67 | 77 | 89 | 90 | 91 | 92 | 93 | 94 | ) : ( 95 | claimed 96 | ) 97 | )} 98 |
99 | ); 100 | }; 101 | 102 | export default FriendsListItem; 103 | -------------------------------------------------------------------------------- /front-end/src/components/taptap/InviteCard.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const InviteCard = ({ title, points, description, logo, background }) => { 4 | return ( 5 |
9 |

{title}

10 |
11 | Logo 12 |

{points}

13 |
14 |

{description}

15 |
16 | ); 17 | }; 18 | 19 | export default InviteCard; 20 | -------------------------------------------------------------------------------- /front-end/src/components/taptap/LoadingScreen.jsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect, useState } from "react"; 2 | import { AnimatePresence, motion } from "framer-motion"; 3 | import { useNavigate } from "react-router-dom"; 4 | 5 | function LoadingScreen({ isloaded, reURL='' }) { 6 | const words = ["Our", "Robo", "is", "waking", "up...."]; 7 | const [currentWord, setCurrentWord] = useState(words[0]); 8 | const [isAnimating, setIsAnimating] = useState(false); 9 | 10 | const navigate = useNavigate(); 11 | 12 | const duration = 100; 13 | 14 | const startAnimation = useCallback(() => { 15 | const word = words[(words.indexOf(currentWord) + 1) % words.length]; 16 | setCurrentWord(word); 17 | setIsAnimating(true); 18 | }, [currentWord, words]); 19 | 20 | useEffect(() => { 21 | if (!isAnimating) 22 | setTimeout(() => { 23 | startAnimation(); 24 | }, duration); 25 | }, [isAnimating, duration, startAnimation]); 26 | 27 | useEffect(() => { 28 | setTimeout(() => { 29 | 30 | 31 | if(reURL!='') navigate(reURL); 32 | }, 2000); 33 | }, [navigate, reURL]); 34 | 35 | if (isloaded) { 36 | return ( 37 |
38 | { 40 | setIsAnimating(false); 41 | }} 42 | > 43 | 73 | {currentWord.split("").map((letter, index) => ( 74 | 84 | {letter} 85 | 86 | ))} 87 | 88 | 89 |
90 | ); 91 | } else { 92 | return null; 93 | } 94 | } 95 | 96 | export default LoadingScreen; 97 | -------------------------------------------------------------------------------- /front-end/src/components/taptap/TGAuth.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Navigate } from "react-router-dom"; 3 | 4 | import { isAuth } from "../../utlis/localstorage"; 5 | 6 | function TGAuth({ children }) { 7 | return isAuth() === true ? ( 8 | children 9 | ) : ( 10 | 15 | ); 16 | } 17 | 18 | export default TGAuth; 19 | -------------------------------------------------------------------------------- /front-end/src/components/taptap/Tabs.jsx: -------------------------------------------------------------------------------- 1 | import { Link, useLocation } from "react-router-dom"; 2 | 3 | import TokenImg from "../../assets/img/coins.svg"; 4 | import FriendImg from "../../assets/img/friends.svg"; 5 | import GiftImg from "../../assets/img/gift.svg"; 6 | import TaskImg from "../../assets/img/task.svg"; 7 | import LeaderBoardImg from "../../assets/img/leaderboard.svg"; 8 | 9 | import { useNavigate } from "react-router-dom"; 10 | 11 | let tabs = [ 12 | { id: "Earn", label: "Earn", img: TokenImg, path: "/game/earn" }, 13 | { id: "Friends", label: "Friends", img: FriendImg, path: "/game/friends" }, 14 | { id: "Reward", label: "Mine", img: GiftImg, path: "/game/reward" }, 15 | { id: "Tasks", label: "Tasks", img: TaskImg, path: "/game/tasks" }, 16 | // { 17 | // id: "Leaderboard", 18 | // label: "Leaderboard", 19 | // img: LeaderBoardImg, 20 | // path: "/game/leaderboard", 21 | // }, 22 | ]; 23 | 24 | export default function Tabs() { 25 | const location = useLocation(); 26 | return ( 27 |
28 |
29 | {tabs.map((tab) => ( 30 | 37 | 43 | 48 | {tab.label} 49 | 50 | 51 | ))} 52 |
53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /front-end/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | @layer base { 5 | * { 6 | user-select: none; 7 | } 8 | @font-face { 9 | font-family: SFregular; 10 | src: url("./assets/font/SF-Pro-Display-Medium.otf"); 11 | } 12 | @font-face { 13 | font-family: SFsemi; 14 | src: url("./assets/font/SF-Pro-Display-Bold.otf"); 15 | } 16 | body { 17 | overflow: hidden; 18 | user-select: none; 19 | } 20 | :root { 21 | --radial-gradient-background: 250, 250, 250; 22 | --solid-color-background: 15, 15, 15; 23 | --overlay-color: 255, 255, 255; 24 | } 25 | body { 26 | @apply bg-neutral-950; 27 | } 28 | .radial-gradient { 29 | background: radial-gradient( circle at 50% 0%, rgba(var(--radial-gradient-background), 0.05) 0%, transparent 60%) rgba(var(--solid-color-background), 1); 30 | } 31 | .linear-mask { 32 | mask-image: linear-gradient( -75deg, white calc(var(--x) + 20%), transparent calc(var(--x) + 30%), white calc(var(--x) + 100%)); 33 | -webkit-mask-image: linear-gradient( -75deg, white calc(var(--x) + 20%), transparent calc(var(--x) + 30%), white calc(var(--x) + 100%)); 34 | } 35 | .linear-overlay { 36 | background-image: linear-gradient( -75deg, rgba(var(--overlay-color), 0.1) calc(var(--x) + 20%), rgba(var(--overlay-color), 0.5) calc(var(--x) + 25%), rgba(var(--overlay-color), 0.1) calc(var(--x) + 100%)); 37 | mask: linear-gradient(black, black) content-box, linear-gradient(black, black); 38 | -webkit-mask: linear-gradient(black, black) content-box, linear-gradient(black, black); 39 | mask-composite: exclude; 40 | -webkit-mask-composite: xor; 41 | } 42 | } -------------------------------------------------------------------------------- /front-end/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import "./index.css"; 5 | 6 | ReactDOM.createRoot(document.getElementById("root")).render( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /front-end/src/pages/error/Error.jsx: -------------------------------------------------------------------------------- 1 | function Error() { 2 | return ( 3 |
4 |

5 | Something went wrong try again! 6 |

7 |
8 | ); 9 | } 10 | 11 | export default Error; 12 | -------------------------------------------------------------------------------- /front-end/src/pages/error/Error404.jsx: -------------------------------------------------------------------------------- 1 | function Error404() { 2 | return ( 3 |
4 |

404

5 |

Page Not Found

6 |
7 | ); 8 | } 9 | 10 | export default Error404; 11 | -------------------------------------------------------------------------------- /front-end/src/pages/error/Error500.jsx: -------------------------------------------------------------------------------- 1 | function Error500() { 2 | function handleReload() { 3 | window.location.reload(); 4 | } 5 | return ( 6 |
7 |

500

8 |

Internal Server error

9 | 15 |
16 | ); 17 | } 18 | 19 | export default Error500; 20 | -------------------------------------------------------------------------------- /front-end/src/pages/game/Earn.jsx: -------------------------------------------------------------------------------- 1 | import { easeInOut, motion } from "framer-motion"; 2 | import React, { useState, useEffect } from "react"; 3 | import { Link, useNavigate } from "react-router-dom"; 4 | import axios from "axios"; 5 | import moment from "moment"; 6 | import GameLayout from "../layout/GameLayout"; 7 | import { 8 | getAuth, 9 | setLocalStorage, 10 | getLocalStorage, 11 | } from "../../utlis/localstorage"; 12 | import { getTGUser } from "../../utlis/tg"; 13 | import { 14 | initializeAudio, 15 | playAudio, 16 | stopAudio, 17 | } from "../../utlis/audioUtils"; 18 | import AnimatedCounter from "../../components/taptap/AnimatedCounter"; 19 | import PlayIcon from "../../assets/img/play-icon.svg"; 20 | import coinBackgroundImg from "../../assets/img/coin-background.png"; 21 | import heroBackgroundImg from "../../assets/img/background-hero.png"; 22 | import LogoImg from "../../assets/img/logo.png"; 23 | import RobotImg from "../../assets/img/robot-1.png"; 24 | import RobotImg4 from "../../assets/img/robot-4.png"; 25 | import CoinImg from "../../assets/img/coin.png"; 26 | import BoltIcon from "../../assets/img/bolt-icon.svg"; 27 | import tapaudio from "../../assets/sounds/mixkit-arcade-game-jump-coin-216.wav"; 28 | 29 | import robot_1 from "../../assets/img/robot-1.png"; 30 | import robot_2 from "../../assets/img/robot-2.png"; 31 | import robot_3 from "../../assets/img/robot-3.png"; 32 | import robot_4 from "../../assets/img/robot-4.png"; 33 | 34 | import LoadingScreen from "../../components/taptap/LoadingScreen"; 35 | 36 | 37 | function Earn() { 38 | const navigate = useNavigate(); 39 | 40 | const [clicks, setClicks] = useState([]); 41 | const [scale, setScale] = useState(1); 42 | const [meteorStyles, setMeteorStyles] = useState([]); 43 | 44 | const [deductCount, setDeductCount] = useState(1); 45 | const [prePoint, setprePoint] = useState(1); 46 | 47 | const [localEnergy, setLocalEnergy] = useState(2000); 48 | const [localPoints, setLocalPoints] = useState(0); 49 | const [restoreTime, setRestoreTime] = useState(null); 50 | const [newsCount, setNewsCount] = useState(0); 51 | const [elapsedSeconds, setElapsedSeconds] = useState(null); 52 | const [isActive, setIsActive] = useState(true); 53 | const [user, setUser] = useState(); 54 | const [remingtime,setRemingtime] = useState(); 55 | const [gamelevel,setGamelevel] =useState(); 56 | const [isLoading,setIsLoading] = useState(true); 57 | 58 | const defaultEnergyLevel = 2000; 59 | 60 | const robot = { 61 | 0: robot_1, 62 | 1: robot_1, 63 | 2: robot_2, 64 | 3: robot_3, 65 | 4: robot_4, 66 | 5: robot_4, 67 | }; 68 | 69 | const robotlevel = { 70 | 0: "LVL 1", 71 | 1: "LVL 1", 72 | 2: "LVL 2", 73 | 3: "LVL 3", 74 | 4: "LVL 4", 75 | 5: "LVL 5" 76 | } 77 | 78 | const checkTime = (time) => { 79 | const localTime = moment(time, 'YYYY-MM-DD HH:mm:ss'); 80 | const utcTime = localTime.utc(); 81 | const currentUtcTime = moment.utc(); 82 | return utcTime.isBefore(currentUtcTime) ? utcTime.format('YYYY-MM-DD HH:mm:ss') :''; 83 | }; 84 | 85 | //TODO 86 | useEffect(() => { 87 | console.log("effect test", isActive, localEnergy, localPoints, user, restoreTime) 88 | }, []); 89 | 90 | useEffect(() => { 91 | const initAudio = async () => { 92 | try { 93 | await initializeAudio(tapaudio); 94 | } catch (error) { 95 | console.error("Error initializing audio:", error); 96 | } 97 | }; 98 | initAudio(); 99 | return () => { 100 | stopAudio(); 101 | }; 102 | }, []); 103 | 104 | useEffect(() => { 105 | const fetchUser = async () => { 106 | const token = getAuth(); 107 | const tgUser = getTGUser(); 108 | 109 | const response = await axios.get(`/api/earn/getscore`,{ 110 | headers: { Authorization: `Bearer ${token}` }, 111 | }); 112 | 113 | console.log("response==>",response) 114 | 115 | const res = response.data; 116 | const resdata = res.data; 117 | 118 | if (!resdata) { 119 | // navigate("/game"); 120 | return; 121 | }else{ 122 | 123 | 124 | 125 | setIsLoading(false) 126 | } 127 | 128 | const userData = resdata; 129 | setUser(userData); 130 | 131 | setGamelevel(userData.game_level); 132 | 133 | // console.log("ds",userData) 134 | // if(userData.points == 0){ 135 | 136 | // localStorage.setItem("energy",2000); 137 | // localStorage.setItem("score",0); 138 | // localStorage.setItem("lastSyncTime",null); 139 | // localStorage.setItem("restoreTime",null); 140 | 141 | // } 142 | 143 | 144 | 145 | 146 | const storedEnergy = localStorage.getItem("energy"); 147 | var storedPoints = localStorage.getItem("score"); 148 | const lastSyncTime = localStorage.getItem("lastSyncTime"); 149 | 150 | console.log("storedPoints==>",storedPoints) 151 | 152 | 153 | if(storedPoints==0){ 154 | localStorage.setItem("score",userData.points) 155 | storedPoints = userData.points 156 | 157 | } 158 | 159 | 160 | 161 | 162 | if (storedEnergy && storedPoints) { 163 | const now = Date.now(); 164 | if (!lastSyncTime || (lastSyncTime && now - lastSyncTime > 2000)) { 165 | let tempLocalEn = localStorage.getItem("energy"); 166 | let tempLocalPO = localStorage.getItem("score"); 167 | await syncWithServer(tempLocalEn, tempLocalPO, userData.restore_time) 168 | .then(() => { 169 | setLocalEnergy(tempLocalEn); 170 | setLocalPoints(tempLocalPO); 171 | }); 172 | } else { 173 | const energy = storedEnergy; 174 | const points = storedPoints; 175 | if (energy !== null && points !== null) { 176 | setLocalEnergy(energy <= 0 ? defaultEnergyLevel : energy); 177 | setLocalPoints(points); 178 | } else { 179 | setLocalEnergy(defaultEnergyLevel); 180 | setLocalPoints(0); 181 | } 182 | } 183 | } else { 184 | const energy = userData.energy; 185 | const points = userData.points; 186 | if (energy !== null && points !== null) { 187 | setLocalEnergy(energy <= 0 ? defaultEnergyLevel : energy); 188 | setLocalPoints(points); 189 | } else { 190 | setLocalEnergy(defaultEnergyLevel); 191 | setLocalPoints(0); 192 | } 193 | } 194 | 195 | const storedRestoreTime = localStorage.getItem("restoreTime"); 196 | 197 | 198 | if (storedRestoreTime && storedRestoreTime !== null) { 199 | const result = checkTime(storedRestoreTime); 200 | console.log("1") 201 | if (result !== null && result !== '') { 202 | console.log("2") 203 | setRestoreTime(result); 204 | const currentTime = moment.utc().format("YYYY-MM-DD HH:mm:ss"); 205 | const duration = moment.duration(moment( localStorage.getItem("restoreTime")).diff(currentTime)); 206 | setElapsedSeconds(duration.asSeconds()); 207 | if(storedEnergy>0 ){ 208 | setLocalEnergy(storedEnergy); 209 | }else{ 210 | setLocalEnergy(0); 211 | } 212 | 213 | // localStorage.getItem("restoreTime"); 214 | console.log("result", localStorage.getItem("restoreTime")) 215 | setRestoreTime( localStorage.getItem("restoreTime")); 216 | } else { 217 | console.log("3") 218 | setRestoreTime(''); 219 | setLocalEnergy(storedEnergy !== null ? storedEnergy : defaultEnergyLevel); 220 | } 221 | } else if (userData.restore_time && userData.restore_time !== null) { 222 | console.log("4") 223 | const result = checkTime(userData.restore_time); 224 | if (result !== null && result !== '') { 225 | setRestoreTime(result); 226 | console.log("5") 227 | if(userData.energy==0){ 228 | console.log("from db 5") 229 | const currentTime = moment.utc().format("YYYY-MM-DD HH:mm:ss"); 230 | 231 | console.log("userData.restore_time",userData.restore_time) 232 | 233 | const temp = moment(userData.restore_time).format("YYYY-MM-DD HH:mm:ss"); 234 | console.log("temp",temp) 235 | 236 | const duration = moment.duration(moment(currentTime).diff(moment(temp))).asSeconds(); 237 | setElapsedSeconds(duration); 238 | setLocalEnergy(userData.energy); 239 | setRestoreTime(userData.restore_time); 240 | 241 | }else{ 242 | 243 | console.log("from db 5 relse") 244 | setLocalEnergy(userData.energy); 245 | setElapsedSeconds('') 246 | setRestoreTime(userData.restore_time); 247 | 248 | } 249 | 250 | } else { 251 | console.log("6") 252 | setRestoreTime(''); 253 | setLocalEnergy(storedEnergy !== null ? storedEnergy : userData.energy || defaultEnergyLevel); 254 | } 255 | } else { 256 | console.log("7") 257 | setRestoreTime(''); 258 | setElapsedSeconds(null); 259 | setLocalEnergy(storedEnergy !== null ? storedEnergy : defaultEnergyLevel); 260 | } 261 | }; 262 | fetchUser(); 263 | }, [!user, navigate]); 264 | 265 | const syncWithServer = async (energy, points, restore_time) => { 266 | const token = getAuth(); 267 | if (user ) { 268 | 269 | if( points>0){ 270 | await axios.post( 271 | `/api/game/upscore`, 272 | { 273 | score: 10000, 274 | energy_remaning: 100, 275 | restore_time:moment.utc().format("YYYY-MM-DD HH:mm:ss"), 276 | }, 277 | { 278 | headers: { Authorization: `Bearer ${token}` }, 279 | } 280 | ); 281 | 282 | localStorage.setItem("lastSyncTime", Date.now()); 283 | 284 | console.log("reset") 285 | 286 | }else{ 287 | if(user.points>0){ 288 | localStorage.setItem("score",user.points); 289 | }else{ 290 | localStorage.setItem("score",localPoints); 291 | } 292 | 293 | } 294 | 295 | 296 | } 297 | }; 298 | useEffect(() => { 299 | const interval = setInterval(() => { 300 | setElapsedSeconds((prevSeconds) => { 301 | if (prevSeconds > 0) { 302 | return prevSeconds - 1; 303 | } else { 304 | setLocalEnergy(defaultEnergyLevel) 305 | clearInterval(interval); 306 | return 0; 307 | } 308 | }); 309 | }, 1000); 310 | 311 | return () => clearInterval(interval); // Clean up the interval on component unmount 312 | }, []); 313 | 314 | const handleTap = (tapcount = 1) => { 315 | console.log("tab test", isActive, localEnergy, localPoints, user, restoreTime) 316 | 317 | if (localEnergy > 0) { 318 | const newEnergy = parseInt(localEnergy) - parseInt(tapcount); 319 | const newPoints = parseInt(localPoints) + parseInt(tapcount); 320 | 321 | setLocalEnergy(newEnergy); 322 | setLocalPoints((prevLocalPoints) => { 323 | setprePoint(prevLocalPoints); 324 | return newPoints; 325 | }); 326 | setNewsCount(newPoints); 327 | localStorage.setItem("energy", newEnergy); 328 | localStorage.setItem("score", newPoints); 329 | 330 | console.log("tap 1") 331 | 332 | 333 | console.log("tap 2") 334 | const currentUtcTime = moment.utc(); 335 | const futureUtcTime = currentUtcTime.add(1, 'hours'); 336 | const restoreTimess = futureUtcTime.format("YYYY-MM-DD HH:mm:ss"); 337 | 338 | localStorage.setItem("restoreTime", restoreTimess); 339 | if(newPoints == 0){ 340 | 341 | 342 | setRestoreTime(restoreTimess); 343 | setElapsedSeconds(3600); 344 | } 345 | 346 | 347 | 348 | setIsActive(true); 349 | playAudio(); 350 | } else { 351 | console.log("tap 3") 352 | stopAudio(); 353 | } 354 | console.log("effect test done", isActive, localEnergy, localPoints, user, restoreTime) 355 | }; 356 | 357 | const formatTime = (seconds) => { 358 | const hrs = Math.floor(seconds / 3600); 359 | const mins = Math.floor((seconds % 3600) / 60); 360 | const secs = seconds % 60; 361 | 362 | if (hrs > 0) { 363 | return `${hrs}:${mins.toString().padStart(2, '0')} hr`; 364 | } else if (mins > 0) { 365 | return `${mins}:${secs.toString().padStart(2, '0')} mins`; 366 | } else { 367 | return `${secs} secs`; 368 | } 369 | }; 370 | 371 | useEffect(() => { 372 | if (localEnergy === 0 && restoreTime && elapsedSeconds == null) { 373 | const interval = setInterval(() => { 374 | const now = moment.utc().format("YYYY-MM-DD HH:mm:ss"); 375 | console.log("restoreTime",restoreTime) 376 | console.log("now",now) 377 | // const lcaorestime = moment(restoreTime).utc().format("YYYY-MM-DD HH:mm:ss"); 378 | // console.log("lcaorestime",lcaorestime) 379 | const remainingTime = moment.duration(moment(restoreTime).diff(moment(now))).asSeconds(); 380 | console.log("remainingTime",remainingTime) 381 | if (remainingTime <= 0) { 382 | setLocalEnergy(defaultEnergyLevel); 383 | setRestoreTime(null); 384 | // localStorage.removeItem("restoreTime"); 385 | clearInterval(interval); // Clear interval once the restore time has been processed 386 | } else { 387 | setElapsedSeconds(remainingTime); 388 | } 389 | }, 1000); 390 | 391 | // Clean up interval on component unmount or when conditions change 392 | return () => clearInterval(interval); 393 | } 394 | }, [localEnergy, restoreTime,elapsedSeconds]); 395 | 396 | useEffect(() => { 397 | let syncInterval; 398 | let inactivityTimer; 399 | 400 | const startSync = () => { 401 | clearInterval(syncInterval); 402 | syncInterval = setInterval(() => { 403 | syncWithServer(localEnergy, localPoints, restoreTime); 404 | }, 1500); // Sync every 4 seconds 405 | }; 406 | 407 | const stopSync = () => { 408 | clearInterval(syncInterval); 409 | }; 410 | 411 | const resetInactivityTimer = () => { 412 | clearTimeout(inactivityTimer); 413 | inactivityTimer = setTimeout(() => { 414 | setIsActive(false); 415 | }, 1000); // 5 seconds of inactivity 416 | }; 417 | 418 | if (isActive) { 419 | startSync(); 420 | resetInactivityTimer(); 421 | } else { 422 | stopSync(); 423 | } 424 | 425 | return () => { 426 | clearInterval(syncInterval); 427 | clearTimeout(inactivityTimer); 428 | }; 429 | }, [isActive, localEnergy, localPoints, user, restoreTime]); 430 | 431 | useEffect(() => { 432 | const handleBeforeUnload = async () => { 433 | await syncWithServer(localEnergy, localPoints, restoreTime); 434 | }; 435 | handleBeforeUnload() 436 | // window.addEventListener("beforeunload", handleBeforeUnload); 437 | 438 | return () => { 439 | window.removeEventListener("beforeunload", handleBeforeUnload); 440 | }; 441 | }, [localEnergy, localPoints, user, restoreTime]); 442 | 443 | const handleTouchStart = (e) => { 444 | const touchCount = e.touches.length; 445 | for (let i = 0; i < touchCount; i++) { 446 | handleTap(touchCount); 447 | handleCoinAnimaton(); 448 | 449 | const tg = window.Telegram.WebApp; 450 | tg.HapticFeedback.impactOccurred("medium"); 451 | const touch = e.touches[i]; 452 | const x = touch.clientX; 453 | const y = touch.clientY; 454 | 455 | const newClick = { 456 | id: Math.random(), 457 | x, 458 | y, 459 | }; 460 | 461 | setClicks((prevClicks) => [...prevClicks, newClick]); 462 | 463 | setTimeout(() => { 464 | setClicks((prevClicks) => 465 | prevClicks.filter((click) => click.id !== newClick.id) 466 | ); 467 | }, 1000); 468 | } 469 | }; 470 | 471 | const handleCoinAnimaton = () => { 472 | setScale(0.9); 473 | setTimeout(() => setScale(1), 50); // Reset to original scale after 100ms 474 | }; 475 | 476 | const number = 20; 477 | useEffect(() => { 478 | const styles = [...new Array(number)].map(() => ({ 479 | top: 0, 480 | left: Math.floor(Math.random() * window.innerWidth) + "px", 481 | animationDelay: Math.random() * 1 + 0.2 + "s", 482 | animationDuration: Math.floor(Math.random() * 8 + 2) + "s", 483 | })); 484 | setMeteorStyles(styles); 485 | }, [number]); 486 | 487 | return ( 488 | 489 | 490 | 491 | 492 | {!isLoading && ( 493 | <> 494 | 495 |
499 | 500 | {" "} 505 | PLAY 506 | 507 |
508 |
515 |
516 | 520 | 522 |

{robotlevel[gamelevel]}

523 | 524 |
525 |

YOU'VE EARNED

526 |

527 | 528 | 529 |

530 |
531 |
532 | 533 |
534 | {[...meteorStyles].map((style, idx) => ( 535 | 542 |
543 | 544 | ))} 545 |
546 |
547 | 555 | {localEnergy 556 | ? clicks.map((click) => ( 557 | 565 | +{deductCount || "5"} 566 | 567 | )) 568 | : ""} 569 |
570 |
571 |
572 |
573 |
574 |
575 |
581 |
582 |

583 | {restoreTime != null && restoreTime !== '' && localEnergy === 0 ? ( 584 | !isNaN(parseInt(elapsedSeconds)) ? formatTime(elapsedSeconds) : `1h` 585 | // elapsedSeconds 586 | ) : ( 587 | <> 588 | {localEnergy} Bolt Icon 589 | 590 | )} 591 |

592 |
593 |
594 | 595 | 596 | )} 597 | 598 | 599 | ); 600 | } 601 | 602 | export default Earn; 603 | -------------------------------------------------------------------------------- /front-end/src/pages/game/Friends.jsx: -------------------------------------------------------------------------------- 1 | import GameLayout from "../layout/GameLayout"; 2 | import { useNavigate } from "react-router-dom"; 3 | import { motion } from "framer-motion"; 4 | import React, { useState, useEffect, useRef, useCallback } from "react"; 5 | import { ToastContainer, toast } from 'react-toastify'; 6 | import 'react-toastify/dist/ReactToastify.css'; 7 | import axios from "axios"; 8 | 9 | import InviteCard from "../../components/taptap/InviteCard"; 10 | import FriendsListItem from "../../components/taptap/FriendsListItem"; 11 | 12 | import LogoImg from "../../assets/img/logo.png"; 13 | import ActualizarImg from "../../assets/img/actualizar.png"; 14 | import PeopleImg from "../../assets/img/people.png"; 15 | import coinIcon from "../../assets/img/coin.png"; 16 | import { getTGUser } from "../../utlis/tg"; 17 | import { getAuth } from "../../utlis/localstorage"; 18 | import giftbox1 from "../../assets/img/giftbox1.png"; 19 | import giftbox2 from "../../assets/img/giftbox2.png"; 20 | import giftbox3 from "../../assets/img/giftbox3.png"; 21 | import Drawer from "../../components/taptap/Drawer"; 22 | 23 | const Friends = () => { 24 | const [friends, setFriends] = useState([]); 25 | const [refLink, setRefLink] = useState(''); 26 | const [refcode,setRefcode] = useState(''); 27 | // THIS IS FOR DRAWER 28 | const [open, setOpen] = useState(false); 29 | // ------------------ 30 | const navigate = useNavigate(); 31 | const effectRan = useRef(false); 32 | 33 | 34 | const postAjaxCall = async (endpoint, data) => { 35 | const token = getAuth(); 36 | try { 37 | const response = await axios.post(endpoint, data,{ 38 | headers: { Authorization: `Bearer ${token}` }, 39 | }); 40 | return response.data; 41 | } catch (error) { 42 | console.error('Error in endpoint:', error); 43 | throw new Error("Error in endpoint", error); 44 | } 45 | }; 46 | 47 | const [cusText,setCusText] = useState('Invite Copied, Share it with your friends and family.') 48 | 49 | const getUserData = async (tgData) => { 50 | if (!tgData) { 51 | // navigate("/game"); 52 | return; 53 | } 54 | 55 | const GAME_TG_URL = "https://t.me/taptapcore_bot/Earn"; 56 | const { id: tid } = tgData; 57 | 58 | try { 59 | const res = await postAjaxCall('/api/referral/claim', { }); 60 | console.log(res.data) 61 | const userDetails = res?.data || null; 62 | 63 | if (userDetails ) { 64 | setFriends(userDetails.friends); 65 | // console.log(userDetails.friends); 66 | 67 | const refCode = userDetails?.refCode || ''; 68 | if (refCode) { 69 | setRefcode(refCode); 70 | setRefLink(`${GAME_TG_URL}?startapp=${refCode}`); 71 | } 72 | } else { 73 | // navigate("/game"); 74 | } 75 | } catch (error) { 76 | console.error('Error:', error); 77 | } 78 | }; 79 | 80 | useEffect(() => { 81 | if (!effectRan.current) { 82 | const tgData = getTGUser(); 83 | getUserData(tgData); 84 | effectRan.current = true; 85 | } 86 | }, [navigate]); 87 | 88 | const loadFriends = () => { 89 | const tgData = getTGUser(); 90 | getUserData(tgData); 91 | navigate('/game/friends'); 92 | }; 93 | 94 | const triggerCopy = (e) => { 95 | e.preventDefault(); 96 | 97 | navigator.clipboard 98 | .writeText(refLink) 99 | .then(() => { 100 | setOpen(true); 101 | setCusText('Invite Copied, Share it with your friends and family.') 102 | 103 | }) 104 | .catch(() => { 105 | setOpen(false); 106 | setCusText('Fail to copy.') 107 | }) 108 | .finally(() => { 109 | setTimeout(() => setOpen(false), 5000); 110 | }); 111 | }; 112 | const Claim = async (friendId) => { 113 | try { 114 | const tgData = getTGUser(); 115 | // console.log({ friendID:friendId,"refCode":refcode}) 116 | const res = await postAjaxCall('/api/game/refclaim', { friendID:friendId,"refCode":refcode}); 117 | 118 | // console.log(res) 119 | if(res && res.icalimed){ 120 | 121 | setFriends((prevFriends) => 122 | prevFriends.map((friend) => 123 | friend.id === friendId ? { ...friend, isClaimed: "Y" } : friend 124 | ) 125 | ); 126 | 127 | 128 | setCusText(`Claimed your ${res.refpoint} coins, well done keep going.`) 129 | setOpen(true); 130 | } else { 131 | 132 | setOpen(false); 133 | navigate("/game/friends"); 134 | } 135 | } catch (error) { 136 | console.error("Error claiming reward:", error); 137 | } 138 | }; 139 | 140 | return ( 141 | 142 |
143 | 144 | 145 |
146 |

147 | {cusText} 148 |

149 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 |
176 |
177 |

Invite Friends

178 | 179 | 186 | 193 | 194 |
195 |

Friends List

196 | 201 | Refresh 206 | 207 |
208 | 209 | {friends.length > 0 && 210 | friends.map((frd) => ( 211 | Claim(frd.id)} 220 | /> 221 | ))} 222 |
223 | 224 | 252 |
253 | ); 254 | }; 255 | 256 | export default Friends; 257 | -------------------------------------------------------------------------------- /front-end/src/pages/game/Game.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { useLocation, useNavigate } from "react-router-dom"; 3 | import axios from "axios"; 4 | 5 | import Error500 from "../error/Error500"; 6 | 7 | import { getTGUser } from "../../utlis/tg"; 8 | import { setSession } from "../../utlis/localstorage"; 9 | 10 | import LoadingScreen from "../../components/taptap/LoadingScreen" 11 | 12 | function Game() { 13 | const navigate = useNavigate(); 14 | const location = useLocation(); 15 | const query_params = new URLSearchParams(location.search); 16 | const referral_by = query_params.get("tgWebAppStartParam"); 17 | 18 | const [error, setError] = useState(false); 19 | const [isTg, setIsTg] = useState(false); 20 | const [isLoading, setIsLoading] = useState(true); 21 | useEffect(function () { 22 | let unmounted = false; 23 | let tg_user = getTGUser(); 24 | setIsTg(tg_user !== false); 25 | 26 | if (tg_user !== false) { 27 | tg_user["referral_by"] = referral_by; 28 | axios 29 | .post("/api/tg/auth/", tg_user) 30 | .then((res) => { 31 | var data = res.data; 32 | //TODO: check this sync_data validation 33 | if (data.sync_data) { 34 | setSession(data.sync_data); 35 | setIsLoading(false); 36 | navigate("/game/earn"); 37 | return; 38 | } else { 39 | throw new Error("Sync data is not found"); 40 | } 41 | }) 42 | .catch((err) => { 43 | if (!unmounted) { 44 | if (err.response.status === 403) { 45 | setIsTg(false); 46 | } else { 47 | setError(true); 48 | } 49 | setIsLoading(false); 50 | } 51 | }); 52 | } else { 53 | setIsLoading(false); 54 | } 55 | 56 | return () => { 57 | unmounted = true; 58 | }; 59 | }, []); 60 | 61 | return ( 62 | <> 63 | {isLoading === true && } 64 | {isLoading === false && error === true && } 65 | {isLoading === false && error === false && isTg === false && ( 66 |

67 | Please open in TG 68 |

69 | )} 70 | 71 | ); 72 | } 73 | 74 | export default Game; 75 | -------------------------------------------------------------------------------- /front-end/src/pages/game/Leaderboard.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { motion } from "framer-motion"; 3 | import React, { useState, useEffect, useRef, useCallback } from "react"; 4 | import { ToastContainer, toast } from "react-toastify"; 5 | import { useNavigate } from "react-router-dom"; 6 | 7 | import { getTGUser } from "../../utlis/tg"; 8 | import { getAuth } from "../../utlis/localstorage"; 9 | 10 | import minerbg from "../../assets/img/mine-bg.png"; 11 | import coin from "../../assets/img/token.png"; 12 | import logo from "../../assets/img/logo.png"; 13 | import back from "../../assets/img/back-arrow.svg"; 14 | import coinIcon from "../../assets/img/coin.png"; 15 | 16 | import FriendsListItem from "../../components/taptap/FriendsListItem"; 17 | import LoadingScreen from "../../components/taptap/LoadingScreen"; 18 | 19 | function Leaderboard() { 20 | const navigate = useNavigate(); 21 | const effectRan = useRef(false); 22 | 23 | const [topPlayers, setTopPlayers] = useState([]); 24 | const [userPosition, setUserPosition] = useState(""); 25 | const [userEarndetails, setUserEarndetails] = useState([]); 26 | const [isLoading,setIsLoading] = useState(true); 27 | 28 | const postAjaxCall = async (endpoint, data) => { 29 | const token = getAuth(); 30 | try { 31 | const response = await axios.post(endpoint, data, { 32 | headers: { Authorization: `Bearer ${token}` }, 33 | }); 34 | return response.data; 35 | } catch (error) { 36 | console.error("Error in endpoint:", error); 37 | throw new Error("Error in endpoint", error); 38 | } 39 | }; 40 | 41 | const getUserData = async (tgData) => { 42 | if (!tgData) { 43 | navigate("/game"); 44 | return; 45 | } 46 | 47 | const { id: tid } = tgData; 48 | 49 | try { 50 | const res = await postAjaxCall("/api/game/usersrank", { tid }); 51 | 52 | // console.log("res", res); 53 | const userDetails = res?.value || null; 54 | 55 | // console.log("userDetails", userDetails); 56 | 57 | if (userDetails && res.isthere == true) { 58 | setTopPlayers(userDetails.topplayers); 59 | setUserPosition(userDetails.userPosition); 60 | setUserEarndetails(userDetails.specificUserDetails); 61 | setIsLoading(false) 62 | } else { 63 | navigate("/game/earn"); 64 | } 65 | } catch (error) { 66 | console.error("Error:", error); 67 | } 68 | }; 69 | 70 | useEffect(() => { 71 | if (!effectRan.current) { 72 | const tgData = getTGUser(); 73 | getUserData(tgData); 74 | effectRan.current = true; 75 | } 76 | }, [navigate]); 77 | 78 | useEffect(() => { 79 | const tg = window.Telegram.WebApp; 80 | tg.expand(); 81 | 82 | tg.BackButton.show(); 83 | tg.BackButton.onClick(() => { 84 | navigate(-1); 85 | tg.BackButton.hide(); 86 | }); 87 | }); 88 | 89 | const formatNumber = (value) => { 90 | if (value >= 1e9) { 91 | return (value / 1e9).toFixed(1).replace(/\.0$/, "") + "b"; 92 | } else if (value >= 1e6) { 93 | return (value / 1e6).toFixed(1).replace(/\.0$/, "") + "m"; 94 | } else if (value >= 1e3) { 95 | return (value / 1e3).toFixed(1).replace(/\.0$/, "") + "k"; 96 | } else { 97 | return value; 98 | } 99 | }; 100 | 101 | return ( 102 | <> 103 | 104 | 105 | {!isLoading && ( 106 |
110 |

Leaderboard

111 |
112 | {topPlayers && 113 | topPlayers.map((player, index) => ( 114 | 124 | ))} 125 |
126 | {userEarndetails && ( 127 |
128 |
129 |
130 |

{userPosition}

131 | Profile 136 |
137 |

138 | {userEarndetails.firstname || userEarndetails.username} 139 |

140 |

141 | {formatNumber(userEarndetails.overallPoints)}{" "} 142 | 143 |

144 |

145 | {userEarndetails.game_level} 146 |

147 |
148 |
149 |
150 |
151 |
152 | )} 153 |
154 | )} 155 | 156 | ); 157 | 158 | 159 | 160 | } 161 | 162 | export default Leaderboard; 163 | -------------------------------------------------------------------------------- /front-end/src/pages/game/LoadingScreen.jsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect, useState } from "react"; 2 | import { AnimatePresence, motion, LayoutGroup } from "framer-motion"; 3 | import { Navigate, useNavigate } from "react-router-dom"; 4 | function LoadingScreen() { 5 | const words = [ 6 | "Our", 7 | "Robo", 8 | "is", 9 | "waking", 10 | "up....", 11 | 12 | ]; 13 | const [currentWord, setCurrentWord] = useState(words[0]); 14 | const [isAnimating, setIsAnimating] = useState(false); 15 | 16 | const navigate = useNavigate() 17 | 18 | const duration = 100; 19 | // thanks for the fix Julian - https://github.com/Julian-AT 20 | const startAnimation = useCallback(() => { 21 | const word = words[words.indexOf(currentWord) + 1] || words[0]; 22 | setCurrentWord(word); 23 | setIsAnimating(true); 24 | 25 | }, [currentWord, words]); 26 | 27 | useEffect(() => { 28 | if (!isAnimating) 29 | setTimeout(() => { 30 | startAnimation(); 31 | }, duration); 32 | 33 | }, [isAnimating, duration, startAnimation]); 34 | 35 | useEffect(()=>{ 36 | setTimeout(()=>{ 37 | navigate('/game') 38 | },2000) 39 | },[]) 40 | 41 | 42 | 43 | return ( 44 |
45 | { 47 | setIsAnimating(false); 48 | }} 49 | > 50 | 79 | {currentWord.split("").map((letter, index) => ( 80 | 90 | {letter} 91 | 92 | ))} 93 | 94 | 95 |
96 | ); 97 | } 98 | 99 | export default LoadingScreen; 100 | -------------------------------------------------------------------------------- /front-end/src/pages/game/Reward.jsx: -------------------------------------------------------------------------------- 1 | import {useNavigate} from "react-router-dom"; 2 | import {useEffect, useState} from "react"; 3 | import {motion} from "framer-motion"; 4 | import axios from "axios"; 5 | import moment from "moment"; 6 | 7 | import TGAuth from "../../components/taptap/TGAuth"; 8 | import Drawer from "../../components/taptap/Drawer"; 9 | 10 | import {getAuth} from "../../utlis/localstorage"; 11 | 12 | import minerbg from "../../assets/img/mine-bg.png"; 13 | import stars from "../../assets/img/stars-robo.svg"; 14 | import energy from "../../assets/img/energy.svg"; 15 | import clock from "../../assets/img/clock.svg"; 16 | import upgrade from "../../assets/img/upgrade.svg"; 17 | import robot_1 from "../../assets/img/robot-1.png"; 18 | import robot_2 from "../../assets/img/robot-2.png"; 19 | import robot_3 from "../../assets/img/robot-3.png"; 20 | import robot_4 from "../../assets/img/robot-4.png"; 21 | 22 | function RoboMine() { 23 | const navigate = useNavigate(); 24 | const [is_claim, setIsClaim] = useState(false); 25 | const [contdown, setCountDown] = useState(0); 26 | 27 | //TODO: rework the drawer 28 | const [open, setOpen] = useState(false); 29 | const [content, setContent] = useState(""); 30 | 31 | const robot = { 32 | 0: robot_1, 33 | 1: robot_1, 34 | 2: robot_2, 35 | 3: robot_3, 36 | 4: robot_4, 37 | 5: robot_4, 38 | }; 39 | 40 | let miner_level = localStorage.getItem("miner_level"); 41 | miner_level = !isNaN(parseInt(miner_level)) ? parseInt(miner_level) : 0; 42 | let last_mine_date = localStorage.getItem("last_mine_date"); 43 | last_mine_date = 44 | last_mine_date !== null && last_mine_date !== "" ? last_mine_date : null; 45 | let last_mine_batch = last_mine_date !== null ? findBatch(last_mine_date) : 0; 46 | let currrent_batch = findBatch(); 47 | 48 | function getCurrentDateFormatted() { 49 | var now = new Date(); 50 | var year = now.getUTCFullYear(); 51 | var month = (now.getUTCMonth() + 1).toString().padStart(2, "0"); // getUTCMonth() returns month index (0-11) 52 | var day = now.getUTCDate().toString().padStart(2, "0"); 53 | 54 | return year + "-" + month + "-" + day; 55 | } 56 | 57 | if (is_claim === false && miner_level > 0) { 58 | if (last_mine_date === null) { 59 | setIsClaim(true); 60 | } else if (last_mine_date !== null) { 61 | let last_date = moment.utc(last_mine_date).format("YYYY-MM-DD"); 62 | let current_date = getCurrentDateFormatted(); 63 | if (current_date > last_date) { 64 | console.log("dwsded"); 65 | setIsClaim(true); 66 | } else if ( 67 | current_date == last_date && 68 | last_mine_batch < currrent_batch 69 | ) { 70 | console.log("dsdsd"); 71 | setIsClaim(true); 72 | } 73 | } 74 | } 75 | 76 | function getSecondOfDayUTC(date_time = null) { 77 | const now = date_time !== null ? new Date(date_time) : new Date(); 78 | const hours = now.getUTCHours(); 79 | const minutes = now.getUTCMinutes(); 80 | const seconds = now.getUTCSeconds(); 81 | 82 | const final_seconds = hours * 3600 + minutes * 60 + seconds; 83 | 84 | return final_seconds; 85 | } 86 | 87 | function findBatch(date_time = null) { 88 | const total_seconds_in_day = 24 * 3600; 89 | const number_of_batches = 8; 90 | const seconds_per_batch = total_seconds_in_day / number_of_batches; 91 | 92 | const seconds_of_day = getSecondOfDayUTC(date_time); 93 | const batch = Math.floor(seconds_of_day / seconds_per_batch) + 1; 94 | 95 | return batch; 96 | } 97 | 98 | function convertSecondsToTime(seconds) { 99 | const hours = Math.floor(seconds / 3600); 100 | const minutes = Math.floor((seconds % 3600) / 60); 101 | const remaining_seconds = seconds % 60; 102 | 103 | const formatted_time = `${String(hours).padStart(2, "0")}:${String( 104 | minutes 105 | ).padStart(2, "0")}:${String(remaining_seconds).padStart(2, "0")}`; 106 | 107 | return formatted_time; 108 | } 109 | 110 | function getRemainingSeconds() { 111 | const total_seconds_in_day = 24 * 3600; 112 | const number_of_batches = 8; 113 | const seconds_per_batch = total_seconds_in_day / number_of_batches; 114 | 115 | const seconds_of_day = getSecondOfDayUTC(); 116 | const batch = findBatch(); 117 | 118 | const end_of_batch_second = batch * seconds_per_batch; 119 | const remaining_seconds = end_of_batch_second - seconds_of_day; 120 | 121 | return remaining_seconds; 122 | } 123 | 124 | function getRequiredScore(miner_level) { 125 | if (miner_level === 1) { 126 | return [20000, "20k"]; 127 | } else if (miner_level === 2) { 128 | return [100000, "100k"]; 129 | } else if (miner_level === 3) { 130 | return [200000, "200k"]; 131 | } else if (miner_level === 4) { 132 | return [500000, "500k"]; 133 | } else if (miner_level === 5) { 134 | return [1000000, "1M"]; 135 | } else { 136 | throw new Error( 137 | `miner_level not found for getRequiredScore(${miner_level})` 138 | ); 139 | } 140 | } 141 | 142 | function getClaimScore(miner_level) { 143 | if (miner_level === 1) { 144 | return [10000, "10k"]; 145 | } else if (miner_level === 2) { 146 | return [50000, "50k"]; 147 | } else if (miner_level === 3) { 148 | return [75000, "75k"]; 149 | } else if (miner_level === 4) { 150 | return [100000, "100k"]; 151 | } else if (miner_level === 5) { 152 | return [150000, "150k"]; 153 | } else { 154 | throw new Error( 155 | `miner_level not found for getClaimScore(${miner_level})` 156 | ); 157 | } 158 | } 159 | 160 | useEffect(function () { 161 | let score = localStorage.getItem("score"); 162 | let miner_level = localStorage.getItem("miner_level"); 163 | let last_mine_date = localStorage.getItem("last_mine_date"); 164 | 165 | if (score === null || miner_level === null || last_mine_date === null) { 166 | return navigate("/game"); 167 | } 168 | let remaining_seconds = getRemainingSeconds(); 169 | setCountDown(remaining_seconds); 170 | let interval_id = setInterval(function () { 171 | setCountDown((prev_count_down) => { 172 | if (prev_count_down <= 0) { 173 | setIsClaim(false); 174 | let remaining_seconds = getRemainingSeconds(); 175 | return remaining_seconds; 176 | } 177 | return prev_count_down - 1; 178 | }); 179 | }, 1000); 180 | return () => { 181 | clearInterval(interval_id); 182 | }; 183 | }, []); 184 | 185 | function handleClaim() { 186 | let miner_level = localStorage.getItem("miner_level"); 187 | miner_level = !isNaN(parseInt(miner_level)) ? parseInt(miner_level) : 0; 188 | const token = getAuth(); 189 | axios 190 | .get("/api/reward/claim", { 191 | headers: { 192 | Authorization: `Bearer ${token}`, 193 | }, 194 | }) 195 | .then((res) => { 196 | //TODO need to handle res properly 197 | var data = res.data; 198 | localStorage.setItem("last_mine_date", data.last_mine_date); 199 | localStorage.setItem("score", data.score); 200 | setIsClaim(false); 201 | setContent(`${getClaimScore(miner_level)[1]} claimed`); 202 | setOpen(true); 203 | }) 204 | .catch((err) => { 205 | //TODO need to handle err properly 206 | alert("Something went wrong!"); 207 | if (err.response.status === 401) { 208 | return navigate("/game"); 209 | } 210 | }); 211 | } 212 | 213 | function handleUpgrade() { 214 | let score = localStorage.getItem("score"); 215 | score = !isNaN(parseInt(score)) ? parseInt(score) : 0; 216 | let miner_level = localStorage.getItem("miner_level"); 217 | miner_level = !isNaN(parseInt(miner_level)) ? parseInt(miner_level) : 0; 218 | let next_miner_level = miner_level + 1; 219 | 220 | if (next_miner_level > 5) { 221 | setContent("You Exceed max upgrade"); 222 | setOpen(true); 223 | return; 224 | } 225 | const [required_score, score_in_text] = getRequiredScore(next_miner_level); 226 | 227 | if (score < required_score) { 228 | setContent(`Insufficient coins (${score_in_text} coins required)`); 229 | setOpen(true); 230 | return; 231 | } 232 | 233 | const token = getAuth(); 234 | axios 235 | .get("/api/reward/upgrade", { 236 | headers: { 237 | Authorization: `Bearer ${token}`, 238 | }, 239 | }) 240 | .then((res) => { 241 | //TODO need to handle res properly 242 | var data = res.data; 243 | localStorage.setItem("miner_level", data.miner_level); 244 | localStorage.setItem("score", data.score); 245 | setContent(`Upgraded to level ${next_miner_level}`); 246 | setOpen(true); 247 | }) 248 | .catch((err) => { 249 | //TODO need to handle err properly 250 | alert("Something went wrong!"); 251 | if (err.response.status === 401) { 252 | return navigate("/game"); 253 | } 254 | }); 255 | } 256 | 257 | useEffect(() => { 258 | const tg = window.Telegram.WebApp; 259 | tg.expand(); 260 | 261 | tg.BackButton.show(); 262 | tg.BackButton.onClick(() => { 263 | navigate(-1); 264 | tg.BackButton.hide(); 265 | }); 266 | }); 267 | 268 | return ( 269 | 270 | 271 |
272 |

{content}

273 |
274 |
275 |
279 | {/* */} 285 |

ROBO MINER

286 |

Upgrade your robot

287 |
288 | 293 | 301 | 309 |
310 |
311 |
312 |
313 | 314 |
315 |
316 |

Energy

317 |

{miner_level}

318 |
319 |
320 |
324 |
325 |
326 |
327 | {/*
328 | 329 |
330 |
331 |

Health

332 |

{miner_level}

333 |
334 |
335 |
339 |
340 |
341 |
342 |
343 | 344 |
345 |
346 |

Defence

347 |

{miner_level}

348 |
349 |
350 |
354 |
355 |
356 |
*/} 357 |
358 | {miner_level == 0 && ( 359 | 365 | )} 366 | {miner_level > 0 && is_claim && ( 367 | <> 368 | 375 | 381 | 382 | )} 383 | {miner_level > 0 && is_claim === false && ( 384 | <> 385 | 392 | 396 | 397 | )} 398 |
399 |
400 |
401 |
402 | ); 403 | } 404 | 405 | export default RoboMine; 406 | -------------------------------------------------------------------------------- /front-end/src/pages/game/Tasks.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { useNavigate } from "react-router-dom"; 3 | import React, { useState, useEffect, useRef } from "react"; 4 | import GameLayout from "../layout/GameLayout"; 5 | import FriendsListItem from "../../components/taptap/FriendsListItem"; 6 | import Drawer from "../../components/taptap/Drawer"; 7 | import minerbg from "../../assets/img/mine-bg.png"; 8 | import coin from "../../assets/img/token.png"; 9 | import logo from "../../assets/img/logo.png"; 10 | import Xlogo from "../../assets/img/logo-black.png"; 11 | import telelogo from "../../assets/img/Logo.svg"; 12 | import ime from "../../assets/img/ime.jpg"; 13 | import { getTGUser } from "../../utlis/tg"; 14 | import { getAuth } from "../../utlis/localstorage"; 15 | import LoadingScreen from "../../components/taptap/LoadingScreen"; 16 | 17 | function Tasks() { 18 | const [isCheckin, setIsCheckin] = useState(false); 19 | const [checkinDetails, setCheckinDetails] = useState({}); 20 | const [open, setOpen] = useState(false); 21 | const [isLoading,setIsLoading] = useState(true); 22 | const [taskList, setTaskList] = useState({ 23 | dailycheckin: false, 24 | X: true, 25 | T: false, 26 | ime: false, 27 | telecom:false 28 | }); 29 | const [cusText, setCusText] = useState('Claimed Successfully'); 30 | 31 | const navigate = useNavigate(); 32 | const effectRan = useRef(false); 33 | 34 | const postAjaxCall = async (endpoint, data) => { 35 | const token = getAuth(); 36 | try { 37 | const response = await axios.get(endpoint, { 38 | headers: { Authorization: `Bearer ${token}` }, 39 | }); 40 | return response.data; 41 | } catch (error) { 42 | console.error("Error in endpoint:", error); 43 | throw new Error("Error in endpoint", error); 44 | } 45 | }; 46 | 47 | const getUserData = async (tgData) => { 48 | if (!tgData) { 49 | // navigate("/game"); 50 | return; 51 | } 52 | const { id: tid } = tgData; 53 | 54 | try { 55 | const res = await postAjaxCall("/api/task/list", { tid }); 56 | console.log("res=>",res) 57 | const checkinDetails = res?.value || {}; 58 | const taskDoneList = res?.taskDoneList || {}; 59 | 60 | if (checkinDetails && res.isCheckin === true) { 61 | setIsCheckin(true); 62 | setCheckinDetails(checkinDetails); 63 | setTaskList(taskDoneList); 64 | setOpen(false); 65 | 66 | } 67 | setIsLoading(false) 68 | } catch (error) { 69 | console.error("Error:", error); 70 | } 71 | }; 72 | 73 | useEffect(() => { 74 | if (!effectRan.current) { 75 | const tgData = getTGUser(); 76 | getUserData(tgData); 77 | effectRan.current = true; 78 | } 79 | }, [navigate]); 80 | 81 | const Claim = async (tasktype = null) => { 82 | try { 83 | const tgData = getTGUser(); 84 | const res = await postAjaxCall("/api/game/upCheckin", { 85 | teleid: tgData.id, 86 | tasktype, 87 | }); 88 | 89 | if (res && res.isCheckin) { 90 | let newTaskList = { ...taskList }; 91 | 92 | if (tasktype === "daily") { 93 | setIsCheckin(false); 94 | newTaskList.dailycheckin = true; 95 | const pointsInLocalStorage = localStorage.getItem("score"); 96 | localStorage.setItem( 97 | "score", 98 | parseInt(pointsInLocalStorage) + parseInt(checkinDetails.points) 99 | ); 100 | } else if (tasktype === "x") { 101 | window.Telegram.WebApp.openLink("https://x.com/TapTap_bot"); 102 | newTaskList.X = true; 103 | const pointsInLocalStorage = localStorage.getItem("score"); 104 | localStorage.setItem("score", parseInt(pointsInLocalStorage) + 5000); 105 | } else if (tasktype === "telegram") { 106 | window.Telegram.WebApp.openLink("https://t.me/taptap_official"); 107 | newTaskList.T = true; 108 | const pointsInLocalStorage = localStorage.getItem("score"); 109 | localStorage.setItem("score", parseInt(pointsInLocalStorage) + 5000); 110 | } else if (tasktype === "ime") { 111 | window.Telegram.WebApp.openLink("https://t.me/ime_en"); 112 | newTaskList.ime = true; 113 | const pointsInLocalStorage = localStorage.getItem("score"); 114 | localStorage.setItem("score", parseInt(pointsInLocalStorage) + 5000); 115 | }else if (tasktype === "telecommunity") { 116 | window.Telegram.WebApp.openLink("https://t.me/taptapbotchat"); 117 | newTaskList.telecom = true; 118 | const pointsInLocalStorage = localStorage.getItem("score"); 119 | localStorage.setItem("score", parseInt(pointsInLocalStorage) + 5000); 120 | } 121 | 122 | setTaskList(newTaskList); 123 | setOpen(false); 124 | 125 | setTimeout(() => { 126 | setOpen(false); 127 | }, 3000); 128 | } else { 129 | setIsCheckin(false); 130 | setOpen(false); 131 | navigate("/game/earn"); 132 | } 133 | } catch (error) { 134 | console.error("Error claiming reward:", error); 135 | } 136 | }; 137 | 138 | const formatNumber = (value) => { 139 | if (value >= 1e9) { 140 | return (value / 1e9).toFixed(1).replace(/\.0$/, "") + "B"; 141 | } else if (value >= 1e6) { 142 | return (value / 1e6).toFixed(1).replace(/\.0$/, "") + "M"; 143 | } else if (value >= 1e3) { 144 | return (value / 1e3).toFixed(1).replace(/\.0$/, "") + "K"; 145 | } else { 146 | return value; 147 | } 148 | }; 149 | 150 | return ( 151 | 152 | 153 | {isLoading ? ( 154 | 155 | ) : ( 156 | <> 157 | 158 |

159 | {cusText} 160 |

161 |
162 | <> 163 | {/* !isCheckin ? Claim("daily") : null} 172 | /> */} 173 | !taskList.X ? Claim("x") : null} 182 | /> 183 | !taskList.T ? Claim("telegram") : null} 192 | /> 193 | 194 | !taskList.ime ? Claim("ime") : null} 203 | /> 204 | !taskList.telecom ? Claim("telecommunity") : null} 213 | /> 214 | 215 | 216 | )} 217 | 218 |
219 | ); 220 | } 221 | 222 | export default Tasks; 223 | -------------------------------------------------------------------------------- /front-end/src/pages/game/Waitlist.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { motion } from "framer-motion"; 3 | function Waitlist() { 4 | return ( 5 |
6 | 12 | Waitlist. 13 | 14 | 20 | TapTap is currently accepting invites only. Apply for waitlist or ask a 21 | friend for invite. 22 | 23 | 29 | 50 | 51 | Join Waitlist 52 | 53 | 54 | 55 | 56 |
57 | ); 58 | } 59 | 60 | export default Waitlist; 61 | -------------------------------------------------------------------------------- /front-end/src/pages/layout/GameLayout.jsx: -------------------------------------------------------------------------------- 1 | import Tabs from "../../components/taptap/Tabs"; 2 | import TGAuth from "../../components/taptap/TGAuth"; 3 | 4 | import { getTGUser } from "../../utlis/tg"; 5 | 6 | import walletIcon from "../../assets/img/wallet-icon.svg"; 7 | import leaderboard from "../../assets/img/leaderboard.svg"; 8 | import coinIcon from "../../assets/img/coin.png"; 9 | import { Link } from "react-router-dom"; 10 | import { useEffect } from "react"; 11 | import { 12 | TonConnectButton, 13 | TonConnectUI, 14 | TonConnectUIProvider, 15 | useTonAddress, 16 | useTonConnectModal, 17 | useTonWallet, 18 | } from "@tonconnect/ui-react"; 19 | 20 | function GameLayout({ children }) { 21 | let tg_user = getTGUser(); 22 | const tg = window.Telegram.WebApp; 23 | useEffect(() => { 24 | tg.expand(); 25 | }, []); 26 | const { state, open, close } = useTonConnectModal(); 27 | const wallet = useTonWallet(); 28 | const userFriendlyAddress = useTonAddress(); 29 | const formatWalletAddress = (address) => { 30 | if (!address || address.length < 10) return address; 31 | const start = address.slice(0, 2); 32 | const end = address.slice(-4); 33 | return `${start}...${end}`; 34 | }; 35 | return ( 36 | 37 | {tg_user !== false && ( 38 |
39 |
40 |
41 |
42 | Profile 48 | 49 | {tg_user.first_name} 50 | 51 |
52 |
53 | {wallet ? ( 54 | 60 | ) : ( 61 | 79 | )} 80 | 81 | 82 | 94 | 95 | 96 | 97 | 98 | 99 |
100 |
101 |
102 | {children} 103 | 104 |
105 | )} 106 | {tg_user === false && ( 107 |

108 | 109 |

110 | )} 111 |
112 | ); 113 | } 114 | 115 | export default GameLayout; 116 | -------------------------------------------------------------------------------- /front-end/src/routes/GameRouer.jsx: -------------------------------------------------------------------------------- 1 | import { Route } from "react-router-dom"; 2 | 3 | import Game from "../pages/game/Game"; 4 | import Earn from "../pages/game/Earn"; 5 | import Friends from "../pages/game/Friends"; 6 | import Reward from "../pages/game/Reward"; 7 | import Tasks from "../pages/game/Tasks"; 8 | import Leaderboard from "../pages/game/Leaderboard"; 9 | 10 | import Waitlist from "../pages/game/Waitlist"; 11 | import LoadingScreen from "../pages/game/LoadingScreen"; 12 | 13 | 14 | const GameRouter = [ 15 | 16 | } />, 17 | } />, 18 | } />, 19 | } />, 20 | } />, 21 | } />, 22 | } />, 23 | 24 | ]; 25 | export default GameRouter; 26 | -------------------------------------------------------------------------------- /front-end/src/routes/MainRouter.jsx: -------------------------------------------------------------------------------- 1 | import { BrowserRouter, Routes, Route } from "react-router-dom"; 2 | 3 | import GameRouter from "./GameRouer"; 4 | import Error404 from "../pages/error/Error404"; 5 | import { TonConnectUIProvider } from "@tonconnect/ui-react"; 6 | function MainRouter() { 7 | return ( 8 | 14 | 15 | 16 | {GameRouter} 17 | } /> 18 | 19 | 20 | 21 | ); 22 | } 23 | 24 | export default MainRouter; 25 | -------------------------------------------------------------------------------- /front-end/src/utlis/audioUtils.js: -------------------------------------------------------------------------------- 1 | let audioContext = null; 2 | let gainNode = null; 3 | let audioBuffer = null; 4 | let source = null; 5 | 6 | export const initializeAudio = (audioUrl) => { 7 | return new Promise(async(resolve, reject) => { 8 | if (audioBuffer) { 9 | resolve(); 10 | return; 11 | } 12 | try { 13 | audioContext = new(window.AudioContext || window.webkitAudioContext)(); 14 | gainNode = audioContext.createGain(); 15 | 16 | const data = await fetchAudioData(audioUrl); 17 | audioBuffer = await audioContext.decodeAudioData(data); 18 | resolve(); 19 | } catch (error) { 20 | reject(error); 21 | } 22 | }); 23 | }; 24 | 25 | const fetchAudioData = (audioUrl) => { 26 | return new Promise((resolve, reject) => { 27 | const request = new XMLHttpRequest(); 28 | request.open('GET', audioUrl, true); 29 | request.responseType = 'arraybuffer'; 30 | request.onload = () => { 31 | if (request.status === 200) { 32 | resolve(request.response); 33 | } else { 34 | reject(new Error('Failed to load audio data')); 35 | } 36 | }; 37 | request.onerror = () => reject(new Error('Network error')); 38 | request.send(); 39 | }); 40 | }; 41 | 42 | export const playAudio = (volume = 1) => { 43 | if (!audioBuffer || !audioContext || !gainNode) return; 44 | source = audioContext.createBufferSource(); 45 | source.buffer = audioBuffer; 46 | source.connect(gainNode); 47 | gainNode.connect(audioContext.destination); 48 | source.start(0); 49 | gainNode.gain.setValueAtTime(volume, audioContext.currentTime); 50 | }; 51 | 52 | export const stopAudio = () => { 53 | if (source) { 54 | source.stop(); 55 | source = null; 56 | } 57 | }; 58 | 59 | export const Vibration = () => { 60 | if ('vibrate' in navigator) { 61 | 62 | navigator.vibrate([500, 200, 500]); 63 | } else { 64 | console.log('Vibration not supported on this device.'); 65 | } 66 | }; -------------------------------------------------------------------------------- /front-end/src/utlis/axiosInstance.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const axiosInstance = axios.create({}); 4 | 5 | axiosInstance.interceptors.request.use( 6 | (config) => { 7 | const auth_token = localStorage.getItem("auth_token"); 8 | if (auth_token) { 9 | config.headers.Authorization = `Bearer ${auth_token}`; 10 | } 11 | return config; 12 | }, 13 | (error) => { 14 | return Promise.reject(error); 15 | } 16 | ); 17 | 18 | axiosInstance.interceptors.response.use( 19 | (response) => { 20 | return response; 21 | }, 22 | (error) => { 23 | const { status } = error.response; 24 | if (status === 401) { 25 | window.location.href = "/game"; 26 | } else { 27 | return Promise.reject(error); 28 | } 29 | } 30 | ); 31 | 32 | export default axiosInstance; 33 | -------------------------------------------------------------------------------- /front-end/src/utlis/helperfun.js: -------------------------------------------------------------------------------- 1 | export function debounce(func, delay) { 2 | let timer; 3 | return (...args) => { 4 | clearTimeout(timer); 5 | timer = setTimeout(() => { 6 | func.apply(this, args); 7 | }, delay); 8 | }; 9 | }; 10 | 11 | export function dateTimeToTimestamp(dateTimeStr) { 12 | const date = new Date(dateTimeStr); 13 | if (isNaN(date.getTime())) { 14 | throw new Error('Invalid date-time string'); 15 | } 16 | return date.getTime(); 17 | } 18 | 19 | export function isDiff(timestamp1, timestamp2) { 20 | const differenceInMilliseconds = Math.abs(timestamp1 - timestamp2); 21 | const differenceInSeconds = differenceInMilliseconds / 1000; 22 | return differenceInSeconds < 5; 23 | } -------------------------------------------------------------------------------- /front-end/src/utlis/localstorage.js: -------------------------------------------------------------------------------- 1 | //***************session function***************// 2 | export function setItem(key, value) { 3 | localStorage.setItem(key, value); 4 | return localStorage.getItem(key) !== null; 5 | } 6 | 7 | export function setSession(data) { 8 | for (const [key, value] of Object.entries(data)) { 9 | if (setItem(key, value) === false) { 10 | throw new Error("Failed to set session"); 11 | } 12 | } 13 | return true; 14 | } 15 | //***************session function***************// 16 | 17 | //***************auth function***************// 18 | export function isAuth() { 19 | if (localStorage.getItem("auth_token")) { 20 | return true; 21 | } 22 | return false; 23 | } 24 | 25 | export function setAuth(token) { 26 | localStorage.setItem("auth_token", token); 27 | return localStorage.getItem("auth_token") !== null; 28 | } 29 | 30 | export function getAuth() { 31 | return localStorage.getItem("auth_token"); 32 | } 33 | 34 | export function clearAuth() { 35 | localStorage.removeItem("auth_token"); 36 | return localStorage.getItem("auth_token") !== null; 37 | } 38 | //***************auth function***************// 39 | 40 | //***************score function***************// 41 | export function getScore() { 42 | return localStorage.getItem("score"); 43 | } 44 | 45 | export function setScore(score) { 46 | localStorage.setItem("score", score); 47 | return localStorage.getItem("score") !== null; 48 | } 49 | //***************score function***************// 50 | 51 | export function clearStorage() { 52 | localStorage.clear(); 53 | } 54 | 55 | export function setLocalStorage(key, value) { 56 | if (typeof window !== "undefined") { 57 | localStorage.setItem(key, value); 58 | } 59 | } 60 | 61 | export function getLocalStorage(key) { 62 | if (typeof window !== "undefined") { 63 | const storedValue = localStorage.getItem(key); 64 | return storedValue; 65 | } 66 | return null; 67 | } 68 | -------------------------------------------------------------------------------- /front-end/src/utlis/tg.js: -------------------------------------------------------------------------------- 1 | export function TGReady() { 2 | if (typeof window !== "undefined" && window.Telegram) { 3 | const tg = window.Telegram.WebApp; 4 | if (tg) { 5 | tg.expand(); 6 | } 7 | tg.ready(); 8 | tg.setHeaderColor('#000000'); 9 | 10 | } 11 | } 12 | 13 | export function getTGUser() { 14 | if ( 15 | window.Telegram !== undefined && 16 | window.Telegram.WebApp.initDataUnsafe.user !== undefined 17 | ) { 18 | 19 | return window.Telegram.WebApp.initDataUnsafe.user; 20 | } 21 | return false; 22 | } -------------------------------------------------------------------------------- /front-end/src/utlis/ui.js: -------------------------------------------------------------------------------- 1 | export function setBodyClass(class_name) { 2 | try { 3 | document.getElementById("body").classList.add(class_name); 4 | } catch (err) { 5 | /* empty */ 6 | } 7 | } 8 | 9 | export function removeBodyClass(class_name) { 10 | try { 11 | document.getElementById("body").classList.remove(class_name); 12 | } catch (err) { 13 | /* empty */ 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /front-end/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: { 6 | animation: { 7 | meteor: "meteor 5s linear infinite", 8 | }, 9 | keyframes: { 10 | meteor: { 11 | "0%": { transform: "rotate(215deg) translateX(0)", opacity: 1 }, 12 | "70%": { opacity: 1 }, 13 | "100%": { 14 | transform: "rotate(215deg) translateX(-500px)", 15 | opacity: 0, 16 | }, 17 | }, 18 | }, 19 | fontFamily: { 20 | 'sfRegular': ["SFregular"], 21 | 'sfSemi': ["SFsemi"] 22 | }, 23 | screens: { 24 | 'small': { 'raw': '(max-height: 600px)' }, 25 | // => @media (min-height: 800px) { ... } 26 | } 27 | }, 28 | }, 29 | plugins: [], 30 | }; -------------------------------------------------------------------------------- /front-end/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | server: { 7 | proxy: { 8 | "/api": "http://localhost:3000", 9 | }, 10 | }, 11 | plugins: [react()], 12 | }); -------------------------------------------------------------------------------- /handbook.txt: -------------------------------------------------------------------------------- 1 | // Success Responses 2 | res.status(200).json({ message: 'Success', data: responseData }); 3 | res.status(201).json({ message: 'Success', data: [] }); 4 | res.status(204).send(); //send a No Content response 5 | 6 | //Client Error Responses 7 | res.status(400).json({ error: 'Bad Request', message: 'Invalid input data' }); 8 | res.status(401).json({ error: 'Unauthorized', message: 'Authentication required' }); 9 | res.status(402).json({ error: 'Payment Required', message: 'Payment Required contact team' }); 10 | res.status(403).json({ error: 'Forbidden', message: 'You do not have access to this resource' }); 11 | res.status(404).json({ error: 'Not Found', message: 'Resource not found' }); 12 | res.status(405).json({ error: 'Invalid Method', message: 'Method Not Allowed' }); 13 | res.status(409).json({ error: 'Conflict', message: 'Insert or update operation failed' }); 14 | res.status(422).json({ error: 'Unprocessable Entity', message: 'Validation failed for the input data' }); 15 | 16 | // Server Error Responses 17 | res.status(500).json({ error: 'Internal Server Error', message: 'An error occurred on the server' }); 18 | res.status(502).json({ error: 'Bad Gateway', message: 'Invalid response from upstream server' }); 19 | res.status(503).json({ error: 'Service Unavailable', message: 'The server is temporarily unable to service your request' }); 20 | 21 | //Redirect 22 | res.status(301).redirect('/[URL]'); 23 | 24 | -------------------------------------------------------------------------------- /taptap.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 8.0.37, for Linux (x86_64) 2 | -- 3 | -- Host: localhost Database: taptap 4 | -- ------------------------------------------------------ 5 | -- Server version 8.0.37-0ubuntu0.24.04.1 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!50503 SET NAMES utf8mb4 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `earnings` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `earnings`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!50503 SET character_set_client = utf8mb4 */; 25 | CREATE TABLE `earnings` ( 26 | `id` int NOT NULL AUTO_INCREMENT, 27 | `userid` bigint NOT NULL, 28 | `tap_score` bigint DEFAULT '0', 29 | `referral_score` bigint DEFAULT '0', 30 | `checkin_score` bigint DEFAULT '0', 31 | `task_score` bigint DEFAULT '0', 32 | `created_date` datetime NOT NULL, 33 | `modified_date` datetime NOT NULL, 34 | `game_level` varchar(255) DEFAULT NULL, 35 | `last_login_at` datetime DEFAULT NULL, 36 | `current_streak` int NOT NULL DEFAULT '0', 37 | `enery_restore_time` timestamp NULL DEFAULT NULL, 38 | `energy_remaning` int DEFAULT NULL, 39 | `task` varchar(200) DEFAULT NULL, 40 | `miner_level` int DEFAULT '0', 41 | `last_mine_date` datetime DEFAULT NULL, 42 | PRIMARY KEY (`id`), 43 | KEY `teleid` (`userid`), 44 | KEY `earnings_teleid` (`userid`), 45 | CONSTRAINT `earnings_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `tg_users` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE 46 | ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 47 | /*!40101 SET character_set_client = @saved_cs_client */; 48 | 49 | -- 50 | -- Table structure for table `tasks` 51 | -- 52 | 53 | DROP TABLE IF EXISTS `tasks`; 54 | /*!40101 SET @saved_cs_client = @@character_set_client */; 55 | /*!50503 SET character_set_client = utf8mb4 */; 56 | CREATE TABLE `tasks` ( 57 | `id` int NOT NULL AUTO_INCREMENT, 58 | `title` varchar(200) NOT NULL, 59 | `claim_score` bigint NOT NULL, 60 | `follow_url` varchar(255) DEFAULT NULL, 61 | `task_under_by` varchar(200) DEFAULT NULL, 62 | `task_add_by` bigint NOT NULL, 63 | `status` enum('ACTIVE','INACTIVE','HOLD') DEFAULT 'ACTIVE', 64 | `created_date` datetime DEFAULT CURRENT_TIMESTAMP, 65 | `modified_date` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 66 | PRIMARY KEY (`id`) 67 | ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 68 | /*!40101 SET character_set_client = @saved_cs_client */; 69 | 70 | -- 71 | -- Table structure for table `tg_users` 72 | -- 73 | 74 | DROP TABLE IF EXISTS `tg_users`; 75 | /*!40101 SET @saved_cs_client = @@character_set_client */; 76 | /*!50503 SET character_set_client = utf8mb4 */; 77 | CREATE TABLE `tg_users` ( 78 | `id` int NOT NULL AUTO_INCREMENT, 79 | `userid` bigint NOT NULL, 80 | `username` varchar(100) DEFAULT NULL, 81 | `first_name` varchar(100) DEFAULT NULL, 82 | `last_name` varchar(100) DEFAULT NULL, 83 | `langauage_code` varchar(30) DEFAULT NULL, 84 | `referral_by` varchar(100) DEFAULT NULL, 85 | `created_date` datetime NOT NULL, 86 | `modified_date` datetime NOT NULL, 87 | `referral_code` varchar(100) DEFAULT NULL, 88 | `ref_claim` enum('Y','N') NOT NULL DEFAULT 'N', 89 | `tg_premium_user` enum('Y','N') NOT NULL DEFAULT 'N', 90 | `wallet_address` varchar(255) DEFAULT NULL, 91 | PRIMARY KEY (`id`), 92 | UNIQUE KEY `userid_UNIQUE` (`userid`) 93 | ) ENGINE=InnoDB AUTO_INCREMENT=87 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 94 | /*!40101 SET character_set_client = @saved_cs_client */; 95 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 96 | 97 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 98 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 99 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 100 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 101 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 102 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 103 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 104 | 105 | -- Dump completed on 2024-06-27 3:03:44 106 | --------------------------------------------------------------------------------