├── .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 |
--------------------------------------------------------------------------------
/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 |
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 |
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 |
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 |
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 |
4 |
--------------------------------------------------------------------------------
/front-end/src/assets/img/friends.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/front-end/src/assets/img/friends_old.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
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 |
5 |
--------------------------------------------------------------------------------
/front-end/src/assets/img/question-icon.svg:
--------------------------------------------------------------------------------
1 |
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 |
124 |
--------------------------------------------------------------------------------
/front-end/src/assets/img/task.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/front-end/src/assets/img/task_old.svg:
--------------------------------------------------------------------------------
1 |
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 |
4 |
--------------------------------------------------------------------------------
/front-end/src/assets/img/wallet-icon.svg:
--------------------------------------------------------------------------------
1 |
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 |

26 |
27 |
{name}
28 |
29 | {balance}
30 |
31 |
{level}
32 |
33 | {displayType === "leader" && (
34 |
35 | )}
36 | {displayType === "friend" && !buttonDisabled && (
37 |
47 |
63 |
64 | )}
65 | {displayType === "checkin" && (
66 | !buttonDisabled ? (
67 |
77 |
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 |

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 |
582 |
583 | {restoreTime != null && restoreTime !== '' && localEnergy === 0 ? (
584 | !isNaN(parseInt(elapsedSeconds)) ? formatTime(elapsedSeconds) : `1h`
585 | // elapsedSeconds
586 | ) : (
587 | <>
588 | {localEnergy}
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 |
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 |

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 |
325 |
326 |
327 | {/*
328 |

329 |
330 |
331 |
Health
332 | {miner_level}
333 |
334 |
340 |
341 |
342 |
343 |

344 |
345 |
346 |
Defence
347 | {miner_level}
348 |
349 |
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 |

48 |
49 | {tg_user.first_name}
50 |
51 |
52 |
53 | {wallet ? (
54 |
60 | ) : (
61 |
79 | )}
80 |
81 |
82 |
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 |
--------------------------------------------------------------------------------