Untitled
unknown
plain_text
3 years ago
44 kB
5
Indexable
//Import modules
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const webPush = require('web-push');
const socket = require('socket.io');
const admin = require('firebase-admin');
const helmet = require('helmet');
const Authenticate = require('./routes/authentication/auth.js');
const User = require('./models/user.js');
const router = require('./routes/index.js');
const Token = require('./models/jwt.js');
const Challenge = require('./models/challenge.js');
const changeBallance = require('./routes/admin/changeballance.js');
const History = require('./models/history.js');
const Setting = require('./models/setting.js');
const Log = require('./models/log');
const BlockedIps = require('./models/blockedIp');
const GameResult = require('./models/gameResult.js');
const validateUserBalance = require('./helper/validateUserBalance');
const { RateLimiterMemory } = require('rate-limiter-flexible');
const getRoomCode = require('./helper/getRoomCode.js');
const { leftGame, cancelGame } = require('./routes/admin/settleChallenge.js');
require('dotenv').config();
const {
logBadBallance,
logToChannel,
logFrequentLost,
logCORSDefaulter,
logRateLimit,
logToChannelIdisUndefined,
} = require('./helper/tgBot');
const {
PUBLIC_VAPID_KEY,
PRIVATE_VAPID_KEY,
DB_URI_LOCAL,
DB_URI_ATLAS,
ROOM_SRV_URL,
IS_DEV,
} = require('./config.js');
const createHistory = require('./helper/createHistory.js');
const { verifyFromPGWebHook } = require('./routes/user/buyChipsPG.js');
const logThisToDb = require('./helper/logThisToDb.js');
const { getSiteStatus } = require('./routes/siteStatesCache.js');
const DEVLOPMENT = IS_DEV;
const ENABLE_REQUEST_LOG = true;
//setting vapid details
//test comment
//test 2
webPush.setVapidDetails(
'mailto:test@test.com',
PUBLIC_VAPID_KEY,
PRIVATE_VAPID_KEY
);
//constants
const PORT = 5000;
//Initializing express app
const app = express();
app.use(helmet());
app.use(
express.urlencoded({
limit: '50mb',
extended: true,
parameterLimit: 50000,
})
);
// parse application/json
app.use(express.json({ limit: '50mb' }));
app.options('*', cors());
app.post(
'/pg-webhook',
cors({
origin: '*',
}),
verifyFromPGWebHook()
);
app.post(
'/whfbweghfebrhebfehwgrwhfbehjfehfvrh',
cors({
origin: '*',
}),
leftGame()
);
app.get(
'/logout-user/:id',
cors({
origin: '*',
}),
async (req, res) => {
io.to(req.params.id).emit('logout');
return res.send('OK');
}
);
var whitelist = [
'https://ludoplayers.com',
'https://admin.ludoplayers.com',
'https://test.ludoplayers.com',
'https://www.ludoplayers.com',
'https://uberki.com',
'https://admin.uberki.com',
'https://test.uberki.com',
'https://www.uberki.com',
];
var corsOptions = DEVLOPMENT
? {
origin: '*',
}
: {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(`Not allowed by CORS: ${origin}`);
}
},
};
let BLOCKED_IPS = [];
let connectedUsers = [];
app.get('/get-connected-users', (req, res) => {
res.json({
'No of connectedUsers': connectedUsers.length,
'No of connections': io.engine.clientsCount,
connectedUsers,
connections: io.sockets.sockets.keys(),
});
});
async function getBlockedIps() {
const res = await BlockedIps.findOne({});
BLOCKED_IPS = res.ip;
return BLOCKED_IPS;
}
getBlockedIps();
app.get('/refresh-blocked-ip', async (req, res) => {
BLOCKED_IPS = await getBlockedIps();
res.json(BLOCKED_IPS);
});
if (ENABLE_REQUEST_LOG) {
app.use(
require('morgan')(function (tokens, req, res) {
const ip =
req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const id = req.headers['id'];
const userPhone = req.headers['user-phone'];
return [
tokens.method(req, res),
tokens.url(req, res),
tokens.status(req, res),
'-',
tokens['response-time'](req, res),
'ms',
'-',
userPhone,
'-',
ip,
].join(' ');
})
);
}
app.use((req, res, next) => {
if (DEVLOPMENT) {
return next();
}
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const url = req?.baseUrl + req?.path;
if (BLOCKED_IPS.includes(ip)) {
console.log('[BLOCKED_IPS]', ip + ' ' + url);
res.status(403).send('Forbidden');
return;
}
var origin = req.get('origin');
if (whitelist.indexOf(origin) == -1) {
const shouldAllow =
url.includes('redirect') ||
url.includes('logo') ||
url.includes('favicon') ||
url.includes('robots');
if (!shouldAllow) {
logCORSDefaulter(req.headers['id'], {
url,
origin,
ip,
});
return res.status(403).send('Forbidden');
}
}
next();
});
app.get('/redirect/:ref', cors(), (req, res) => {
res.redirect(`https://ludoplayers.com/#/register/${req.params.ref}`);
});
app.get('/redirect/', cors(), (req, res) => {
res.redirect(`https://ludoplayers.com/#/register/`);
});
app.use(cors(corsOptions));
const dev = true;
if (dev) {
mongoose
.connect(DB_URI_LOCAL, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
})
.catch((err) => {
console.error('107@index.js', err.message);
});
} else {
mongoose
.connect(DB_URI_ATLAS, {
useNewUrlParser: true,
useUnifiedTopology: true,
user: 'ludoplayers',
pass: '3aiMsQ61WtlmFtCR',
authMechanism: 'SCRAM-SHA-1',
})
.catch((err) => {
console.error('123@index.js', err.message);
});
}
let db = mongoose.connection;
db.on('error', (err) => console.error('Line 96:', err));
//Using coustom middlewares
app.use(Authenticate(User));
//Connected to database
db.once('open', async (err) => {
if (err) {
console.error(err);
}
});
//not responding to all the users that are blocked
app.use((req, res, next) => {
if (req.user) {
if (req.user.isBlocked) {
console.log(`User is blocked ${req.user._id} ${req.user.username}`);
res.json({ err: 'something went wrong' });
return;
}
}
next();
});
//Setting express router
app.use('/api', router);
const server = app.listen(PORT, (err) => {
if (!err) {
console.info(`server started at port ${PORT}`);
return;
}
console.error('error starting server on port:' + PORT);
});
//passing server to socket.io
var io = socket(server, {
cors: {
origin: DEVLOPMENT
? '*'
: ['https://ludoplayers.com', 'https://test.ludoplayers.com'],
methods: ['GET', 'POST'],
},
});
const rateLimiter = new RateLimiterMemory({
points: 120,
duration: 60,
});
io.use(async (socket, next) => {
if (DEVLOPMENT) {
return next();
}
const { id, key, now, userPhone } = socket.handshake.query;
const ip =
socket?.request?.headers['x-forwarded-for'] ||
socket?.request?.connection.remoteAddress;
if (BLOCKED_IPS.includes(ip)) {
return socket.disconnect(true);
}
if (ENABLE_REQUEST_LOG) {
console.log('socket init by phone:', userPhone);
}
if (!now) {
console.log('connecting socket outside website', ip);
logToChannel(
`connecting to socket outside website! blocking this IP ${ip}`,
true
);
const newBlockedIps = await BlockedIps.findOneAndUpdate(
{},
{
$push: {
ip: ip,
},
},
{
new: true,
}
);
BLOCKED_IPS = newBlockedIps.ip;
return socket.disconnect(true);
}
let userToken = false;
if (!id) {
console.log('no id', ip);
logToChannel(`no id ${ip}`, true);
return socket.disconnect(true);
} else if (id === 'undefined' || id === 'null') {
console.log('id is undefined', ip);
logToChannelIdisUndefined(ip);
return socket.disconnect(true);
} else {
try {
userToken = await Token.findById(id).populate('userId');
} catch (err) {
console.error('io connection error:', err);
}
}
if (!userToken) {
console.error('token not found in db', ip);
// logToChannel(`token not found in db ${ip}`, true);
socket.emit('logout');
return socket.disconnect(true);
}
try {
await rateLimiter.consume(ip);
} catch (err) {
console.error(
`Rate limit exceeded for ip: ${ip} ${userToken?.userId?.username}`
);
// if (userToken?.userId?.isBlocked) {
// const newBlockedIps = await BlockedIps.findOneAndUpdate(
// {},
// {
// $push: {
// ip: ip,
// },
// },
// {
// new: true,
// }
// );
// BLOCKED_IPS = newBlockedIps.ip;
// }
}
const alreadyConnected = connectedUsers.find(
(user) => user?.id == userToken?.userId?._id.toString()
);
if (alreadyConnected) {
try {
if (io.sockets.sockets.has(alreadyConnected.id)) {
console.log('disconnecting socket', alreadyConnected.username);
io.sockets.sockets[alreadyConnected.socketId].disconnect();
}
connectedUsers = connectedUsers.filter(
(user) => user.id !== userToken?.userId?._id.toString()
);
} catch (err) {
console.error('error disconnecting socket:', err);
}
}
connectedUsers.push({
id: userToken?.userId?._id.toString(),
username: userToken?.userId?.username,
socketId: socket.id,
});
next();
});
const scammers = ['62dd01ee8be85abd773ba8b0'];
io.on('connection', async (socket) => {
let user;
//initialization event
let inited = false;
socket.on('terminate', async () => {
socket.disconnect(true);
});
socket.on('init', async (data) => {
inited = true;
setTimeout(() => {
inited = false;
}, 50 * 1000);
//authentication
const token = await Token.findById(data.id);
if (!token) {
return;
}
user = await User.findById(token.userId);
//delete challenges
await DeleteChallenges();
//join this user to a room that is named after his id so we can uniqely send messages to him
socket.join(token.userId.toString());
//sending the initialization your challenges
sendAllNonPlayingChallenges(socket);
//all running challenge
sendPlayableChallanges(socket);
//running challenges
sendRunningChallenges(socket);
});
//create challenge event
socket.on('create', async ({ amount, test }) => {
if (!user) return;
if (
Number(amount) % 50 != 0 ||
Number(amount) < 49 ||
Number(amount) > 10001
) {
return;
}
const isPlayEnabled = await getSiteStatus();
if (
!test &&
(isPlayEnabled?.playPage == false ||
isPlayEnabled?.playPage == 'false')
) {
socket.emit('error', {
message:
'Playing Challenge is Currently Disabled as Website is Under Maintenance. Please try again later',
});
return;
}
//check if user has enough ballance
let fuckingUser = await User.findById(user._id);
if (scammers.includes(fuckingUser.id)) {
logToChannel(
`[Scammer] ${fuckingUser.username} tried creating a game!`
);
return;
}
if (fuckingUser.isBlocked) {
socket.emit('error', { message: 'You are banned' });
return;
}
if (fuckingUser.wallet - amount < 0) {
socket.emit('error', {
message: "You don't have Enough Chips ",
});
return;
}
const userOtherchallneges = await Challenge.find({
creator: user._id,
state: { $in: ['open', 'requested'] },
}).countDocuments();
if (userOtherchallneges === 3) {
socket.emit('error', {
message: 'You can Set Maximum 3 Challenge at a time',
});
return;
}
//check if same amount challenge exists
let challenge = await Challenge.findOne({
creator: user._id,
amount: amount,
state: { $in: ['open', 'requested'] },
});
if (challenge) {
//emmit an error event
socket.emit('error', {
message: 'Same Amount Challenge Already exists',
});
return;
}
//check if user hasn't updated results in a challenge
challenge = await Challenge.findOne({
$or: [
{ creator: fuckingUser._id, 'results.creator': null },
{ player: fuckingUser._id, 'results.player': null },
],
state: { $in: ['playing', 'hold'] },
});
if (challenge) {
socket.emit('error', {
message: 'Update Your Result In Previous Match First',
});
return;
}
//create a challenge
challenge = new Challenge();
challenge.creator = user._id;
challenge.creatorUsername = user.username;
challenge.amount = amount;
await challenge.save();
//send non playing challenges in other words challenges that are in state open or requested
//I dont fucking know why I named this function sendAllNonPlayingChallenges :|
sendAllNonPlayingChallenges(socket);
//send challenges of state open or requested but with a different category :|
sendPlayableChallanges(io);
//send notification to everyone
try {
if (!test) {
await admin.messaging().send({
topic: 'ludoplayers_user',
webpush: {
notification: {
title: `Ludo Players`,
body: `New Challenge Of ₹${amount}`,
icon: 'https://ludoplayers.com/favicon.ico',
//image: data.image,
},
fcmOptions: {
link: 'http://www.ludoplayers.com',
},
},
});
}
} catch (err) {
console.error('326@index.js' + err);
}
});
//delete event
socket.on('delete', async (id) => {
try {
if (!user) {
return;
}
if (scammers.includes(user.id)) {
logToChannel(
`[Scammer] ${user.username} tried deleting a game!`
);
return;
}
let challenge = await Challenge.findById(id);
//check if the user is actually the creator
if (String(challenge?.creator) !== String(user._id)) {
socket.emit('error', {
message: 'Error',
});
return;
}
const { state } = challenge;
console.log(`deleteing challenge ${id} with state ${state}`);
if (['playing', 'hold', 'cancelled', 'resolved'].includes(state)) {
logToChannel(
`user ${user.username} tried deleting a game in state ${state}`
);
return;
}
//delete the challenge
//comment added
await Challenge.deleteOne({ _id: id });
sendAllNonPlayingChallenges(socket);
sendPlayableChallanges(io);
} catch (err) {
console.error('socket delete event' + err.message);
}
});
socket.on('request', async (id) => {
try {
if (!user) {
return;
}
if (scammers.includes(user.id)) {
logToChannel(
`[Scammer] ${user.username} tried requesting a game!`
);
return;
}
let challenge = await Challenge.findOne({
$or: [
{ creator: user._id, 'results.creator': null },
{ player: user._id, 'results.player': null },
],
state: { $in: ['playing', 'hold'] },
});
if (challenge) {
socket.emit('error', {
message: 'Update Your Result in Previous Match First',
});
return;
}
//check if theres any other challenge on requested state of that player
challenge = await Challenge.findOne({
$or: [{ creator: user._id }, { player: user._id }],
state: 'requested',
});
if (challenge && challenge._id.toString() === id) {
//do nothing
return;
}
if (challenge) {
//now the max request error needs to be given in a different way
socket.emit('max-request', id);
return;
}
//validate the id
challenge = await Challenge.findById(id);
if (!challenge) {
console.error('Invalid Id has been passed:', id);
return;
}
//check if its already challenged
if (challenge.player) {
//socket.emit("error", { message: "Someone already challanged" });
return;
}
//check if user has enough ballance
let latestUser = await User.findById(user._id);
if (latestUser.wallet - challenge.amount < 0) {
socket.emit('low-balance', id);
return;
}
// check if user is the creator of the challenge
if (String(challenge.creator) === String(user._id)) {
socket.emit('error', {
message: 'Error: You cannot challenge yourself',
});
return;
}
//update the challenge
challenge.player = user._id;
challenge.playerUsername = user.username;
challenge.state = 'requested';
await challenge.save();
//refresh the challengers your-challenges
sendAllNonPlayingChallenges(io);
//refresg everyone's playable-challenges
sendPlayableChallanges(io);
//play audio of the creator
io.to(challenge.creator.toString()).emit('play-audio');
} catch (err) {
console.error('error in request event:', err.message);
}
});
socket.on('cancel', async (id) => {
try {
if (!user) {
console.error('cancel event: user is not defined');
return;
}
if (scammers.includes(user.id)) {
logToChannel(
`[Scammer] ${user.username} tried cancelling a game!`
);
return;
}
//check if user is either creator or player
let challenge = await Challenge.findOneAndUpdate(
{
_id: id,
state: 'requested',
$or: [{ creator: user._id }, { player: user._id }],
},
{ player: undefined, playerUsername: undefined, state: 'open' }
);
if (!challenge) {
console.error('cancel event: challenge not found');
return;
}
//refresh everyones cancelers playable-challenges
sendPlayableChallanges(io);
//refresh the creators your-challenges
sendAllNonPlayingChallenges(io);
//emmit the rejected and or canceled event
if (challenge.creator.toString() === user._id.toString()) {
io.to(challenge.player.toString()).emit(
'rejected',
challenge._id
);
} else {
//io.to(challenge.creator.toString()).emit("cancelled", challenge._id);
io.to(challenge.player.toString()).emit(
'cancelled',
challenge._id
);
}
} catch (err) {
console.error('error in cancel event:', err);
}
});
socket.on('play', async (id) => {
//check if the challenge is valid and playable
if (!user) {
return;
}
if (scammers.includes(user.id)) {
logToChannel(`[Scammer] ${user.username} tried playing a game!`);
return;
}
let challenge = await Challenge.findOne({
$or: [
{ creator: user._id, 'results.creator': null },
{ player: user._id, 'results.player': null },
],
state: { $in: ['playing', 'hold'] },
});
if (challenge) {
socket.emit('error', {
message: 'Update Your Result in Previous Match First',
});
return;
}
challenge = await Challenge.findOneAndUpdate(
{ _id: id, creator: user._id, state: 'requested' },
{ state: 'playing' }
);
if (!challenge || !user) {
return;
}
//check for both users ballance
let creator = await User.findById(challenge.creator);
let player = await User.findById(challenge.player);
if (
creator.wallet - challenge.amount < 0 ||
player.wallet - challenge.amount < 0
) {
await Challenge.updateOne({ _id: id }, { state: 'requested' });
return;
}
//check if opoponent is already in a game
let opponentsChallenge = await Challenge.findOne({
_id: { $ne: id },
$or: [
{ creator: challenge.player, 'results.creator': null },
{ player: challenge.player, 'results.player': null },
],
state: { $in: ['playing', 'hold'] },
});
if (opponentsChallenge) {
await Challenge.updateOne({ _id: id }, { state: 'requested' });
return;
}
//update the challenge
await Challenge.updateOne({ _id: id }, { state: 'playing' });
await logThisToDb({
scope: '[Wallet balance change]',
idx1: challenge.amount,
challengeId: challenge._id,
user: user._id,
msg: `on play event - dec`,
info: `${challenge.creator} - ${challenge.player}`,
});
//decrease creators ballance
await User.updateMany(
{
$or: [{ _id: challenge.creator }, { _id: challenge.player }],
},
{ $inc: { wallet: -challenge.amount } }
);
// REMOVED FOR NOW
// const isCreaterValid = await validateUserBalance(challenge.creator);
// const isPlayerValid = await validateUserBalance(challenge.player);
// if (!isCreaterValid[0] || !isPlayerValid[0]) {
// if (!isCreaterValid[0]) {
// logBadBallance(
// challenge.creator,
// 'requesting challenge',
// isCreaterValid[1]
// );
// }
// if (!isPlayerValid[0]) {
// console.log(`${challenge.player} is not valid`);
// logBadBallance(
// challenge.player,
// 'requesting challenge',
// isPlayerValid[1]
// );
// }
// }
io.to(challenge.player.toString()).emit('play-audio');
io.to(challenge.player.toString()).emit('start-button', id);
//send a update event to everyone for category running-challenges
sendRunningChallenges(io);
sendAllNonPlayingChallenges(socket);
sendPlayableChallanges(io.to(challenge.player));
io.to(challenge.creator.toString()).emit('redirect', {
user: 'everyone',
to: `/game/${id}`,
});
//get the room code
try {
let roomCode = await getRoomCode();
if (!roomCode) {
logToChannel('room code did not came', true);
roomCode = await getRoomCode();
}
if (!roomCode) {
logToChannel('room code did not came in second try');
}
if (roomCode.length != 8) {
logToChannel(
'room code is not 8 chars long, cancelling this game',
true
);
await cancelGame(id, 'no room code');
return;
}
try {
const gameResult = new GameResult({
_id: roomCode,
gameStartedAt: new Date(),
state: 'created',
});
await gameResult.save();
} catch (error) {
// if duplicate key error
if (error.code === 11000) {
console.log('duplicate key error');
const copyGame = await GameResult.findById(roomCode).lean();
await GameResult.findByIdAndUpdate(roomCode, {
gameStartedAt: new Date(),
state: 'created',
duplicate: true,
$push: {
prevGame: copyGame,
},
playerOne: {
name: '',
result: 0,
tokensOut: -1,
endResult: '',
},
playerTwo: {
name: '',
result: 0,
tokensOut: -1,
endResult: '',
},
gameUpdatedAt: new Date(),
gameFinishedAt: new Date(),
});
} else {
console.error(`[GameResult-created] error`);
console.error(error);
logToChannel(
`[GameResult-created] error: ${error.message}`,
true
);
}
}
// logToChannel(
// `room code is ${roomCode} for ${creator.username} vs ${player.username}`
// );
await Challenge.updateOne(
{
_id: id,
$or: [
{ creator: user._id, 'results.creator': null },
{ player: user._id, 'results.player': null },
],
},
{ code: roomCode }
);
io.to(id).emit('code', roomCode);
io.to(String(creator._id)).emit(
'update_wallet',
creator.wallet - challenge.amount
);
return;
} catch (err) {
console.error('570@index.js' + err);
}
});
socket.on('view', async (id) => {
let challenge = await Challenge.findById(id);
const isCreator = challenge.creator.toString() === user?._id.toString();
const isPlayer = challenge.player.toString() === user?._id.toString();
if (
(isCreator && challenge.results.creator) ||
(isPlayer && challenge.results.player)
) {
socket.emit('redirect', {
user: user._id,
to: `/view/${id}`,
viewInfo: {
code: challenge.code,
result: isCreator
? challenge.results.creator
: challenge.results.player,
resultScreenshoot: isCreator
? challenge.winnerScreenshoot.creator
: challenge.winnerScreenshoot.player,
challengeText: `${challenge.creatorUsername} vs ${challenge.playerUsername} for ₹${challenge.amount}`,
},
});
return;
}
if (user) {
socket.emit('redirect', { user: user._id, to: `/game/${id}` });
}
});
socket.on('refresh', (category) => {
if (category === 'your-challenges') {
sendAllNonPlayingChallenges(socket);
}
if (category === 'playable-challenges') {
sendPlayableChallanges(io);
}
});
//send specefic group of challenges
async function sendAllNonPlayingChallenges(localSocket) {
try {
//requested challenges
const challenges = await Challenge.find({
state: {
$in: ['requested', 'open'],
},
})
.select(
'-winnerScreenshoot -code -s3WinnerScreenshoot -deleted -isAdminClear -isSus'
)
.sort({ createdAt: -1 });
//emit the things and stuffs
localSocket.emit('update', {
category: 'your-challenges',
challenges,
});
} catch (err) {
console.error('sending all non playing challenges' + err);
}
}
async function sendPlayableChallanges(localSocket) {
try {
//all playable challenges
let challenges = await Challenge.find({
state: { $in: ['open', 'requested'] },
})
.select(
'-winnerScreenshoot -code -deleted -isAdminClear -isSus -s3WinnerScreenshoot'
)
.sort({ createdAt: -1 });
localSocket.emit('update', {
category: 'playable-challenges',
challenges: challenges,
});
} catch (err) {
console.error('sending playable challenges: ' + err);
}
}
async function sendRunningChallenges(localSocket) {
try {
let challenges = await Challenge.find({
state: { $in: ['playing', 'hold'] },
}).select(
'-winnerScreenshoot -code -s3WinnerScreenshoot -deleted -isAdminClear -isSus'
);
localSocket.emit('update', {
category: 'running-challenges',
challenges: challenges,
});
} catch (err) {
console.error('sending running challenges: ' + err);
}
}
//all the room related shits
socket.on('joinRoom', async (id) => {
if (!user) {
socket.emit('try-again');
return;
}
let challenge = await Challenge.findById(id);
//make sure this user is either the creator or the player
if (
challenge?.creator?.toString() !== String(user._id) &&
challenge?.player?.toString() !== user._id.toString()
) {
return;
}
//emmit redirect_error if the user has updated his result
let userState = 'creator';
if (challenge.player.toString() === user._id.toString()) {
userState = 'player';
}
if (challenge.state === 'resolved') {
socket.emit('redirect', {
to: '/',
});
}
if (challenge.results[userState] && challenge.state === 'hold') {
socket.emit('redirect_error', {
to: '/',
message:
'आपका रिज़ल्ट अपलोड हो चुका है ॥ गेम clear होने का 2-5 मिनट इंतज़ार करे ।\n\nYour Result is submitted. Please wait until the Game is Cleared within 2-5 Minutes.',
});
return;
}
//join the socket to a room
socket.join(id);
//emit some info
socket.emit('creator', challenge.creator);
socket.emit(
'challenge_text',
`${challenge.creatorUsername} vs ${challenge.playerUsername} for ₹${challenge.amount}`
);
//check if theres code if there is then emit code otherwise the codeChangable
if (challenge.code) {
socket.emit('code', challenge.code);
}
try {
// const logThisToDb = new Log({
// scope: 'joinRoom',
// msg: `${challenge.creatorUsername} vs ${challenge.playerUsername} for ₹${challenge.amount} room code ${challenge.code}`,
// cleared: true,
// });
// await logThisToDb.save();
} catch (err) {
console.error('joinRoom: ' + err);
}
});
async function resolveChallange({
id,
challenge,
userRole,
oponentRole,
result,
}) {
const checkWon = await Challenge.findById(id);
if (
checkWon.winnerScreenshoot.player &&
checkWon.winnerScreenshoot.creator
) {
return;
}
if (checkWon.adminHold) {
console.log('admin hold');
return;
}
//get the latest challenge
const challenegExists = await Challenge.findOneAndUpdate(
{
_id: id,
state: 'hold',
$or: [
{ 'results.creator': undefined },
{ 'results.player': undefined },
],
},
{ state: 'resolved' }
);
//run change ballace funtion for the player and his opoponent as this player is winner
//change this player's ballance (winner)
if (challenegExists) {
const userResult = challenge.results[userRole]
? ` (Your Result: ${challenge.results[userRole]})`
: null;
const oponentResult = challenge.results[oponentRole]
? ` (Your Result: ${challenge.results[oponentRole]})`
: null;
await changeBallance(
challenge[userRole],
{
amount: challenge.amount,
reason: 'win',
naration: `Won against ${
challenge[oponentRole + 'Username']
}${userResult ?? ''}`,
uniqueId: `${challenge.id}-${
challenge.code || 'no-code'
}-win`,
},
`${result}@index.js`,
id
);
//change opponent's ballance (looser)
await changeBallance(
challenge[oponentRole],
{
amount: challenge.amount,
reason: 'lose',
winner: await User.findById(challenge[userRole]),
naration: `lost against ${
challenge[userRole + 'Username']
}${oponentResult ?? ''}`,
uniqueId: `${challenge.id}-${
challenge.code || 'no-code'
}-lose`,
},
`${result}@index.js`,
id
);
}
let tempUser = await User.findById(challenge[userRole]);
//update users ballance
io.to(String(challenge[userRole])).emit(
'update_wallet',
tempUser.wallet
);
//referesh running challanges of all players
sendRunningChallenges(io);
return;
}
async function cancelChallange({
id,
challenge,
userRole,
oponentRole,
result,
}) {
let challenge_on_resolve_time = await Challenge.findById(id);
if (
challenge_on_resolve_time.results[oponentRole] ||
challenge_on_resolve_time.state === 'resolved'
) {
return;
}
//resolve the challange
await Challenge.findByIdAndUpdate(id, { state: 'resolved' });
//giving the amount of the challenge back to creator and player because it was dedducted when match started
// await User.updateMany(
// {
// $or: [{ _id: challenge.creator }, { _id: challenge.player }],
// },
// { $inc: { wallet: challenge.amount } }
// );
const creatorUniqueId = `${challenge.id}-${
challenge.code || 'no-code'
}-cancel-creator`;
//cancled history for player and creator
const creatorHistory = await createHistory(
{
user: challenge.creator,
amount: challenge.amount,
historyType: 'cancel',
naration: `Cancelled Against ${challenge.playerUsername} (Your Result: ${challenge.results.creator})`,
uniqueId: creatorUniqueId,
},
'index.js@cancelChallangeCreator',
id
);
if (creatorHistory) {
await logThisToDb({
scope: '[Wallet balance change]',
idx1: challenge.amount,
challengeId: challenge._id,
user: challenge.creator,
msg: `on cancel - inc`,
});
await User.findByIdAndUpdate(challenge.creator, {
$inc: { wallet: challenge.amount },
});
} else {
logToChannel(
`skipping changing wallet balance for player ${challenge.creator} of ${challenge.amount} because of double hisory creation. uniqueId: ${creatorUniqueId}`,
true
);
}
const playerUniqueId = `${challenge.id}-${
challenge.code || 'no-code'
}-cancel-player`;
const playerHistory = await createHistory(
{
user: challenge.player,
amount: challenge.amount,
historyType: 'cancel',
naration: `Canceled against ${challenge.creatorUsername} (Your Result: ${challenge.results.player})`,
uniqueId: playerUniqueId,
},
'index.js@cancelChallangePlayer',
id
);
if (playerHistory) {
await logThisToDb({
scope: '[Wallet balance change]',
idx1: challenge.amount,
challengeId: challenge._id,
user: challenge.player,
msg: `on cancel - inc`,
});
await User.findByIdAndUpdate(challenge.player, {
$inc: { wallet: challenge.amount },
});
} else {
logToChannel(
`skipping changing wallet balance for player ${challenge.creator} of ${challenge.amount} because of double hisory creation. uniqueId: ${playerUniqueId}`,
true
);
}
//referesh running challanges of all players
sendRunningChallenges(io);
}
socket.on('update_result', async ({ id, result, cancellationReason }) => {
try {
await logThisToDb(
{
scope: '[update_result] event',
challengeId: id,
msg: result,
user: user ? user._id : 'null',
},
'index.js@update_result'
);
if (!user) {
await logThisToDb(
{
scope: '[update_result] user not found',
challengeResult: result,
challengeId: id,
},
'index.js@update_result'
);
return;
}
// const isPlayerValid = await validateUserBalance(user._id);
// if (!isPlayerValid[0]) {
// logBadBallance(user._id, 'update result', isPlayerValid[1]);
// }
let ogChallenge = await Challenge.findById(id);
if (!ogChallenge) {
await logThisToDb(
{
scope: '[update_result] ogChallenge not found',
user: user._id,
challengeId: id,
challengeResult: result,
},
'index.js@update_result'
);
return;
}
await logThisToDb(
{
scope: '[update_result] ogChallenge found',
user: user._id,
challengeId: id,
challengeResult: result,
info: `INIT STATE: ${JSON.stringify(
ogChallenge.results
)} ${JSON.stringify(ogChallenge.state)}}`,
},
'index.js@update_result'
);
//make sure this user is either creator orplayer
if (
ogChallenge.player.toString() !== String(user._id) &&
ogChallenge.creator.toString() !== user._id.toString()
) {
await logThisToDb(
{
scope: '[update_result] user is not creator or player',
user: user._id,
challengeId: id,
challengeResult: result,
challengeCode: ogChallenge.code,
},
'index.js@update_result'
);
if (ogChallenge.state === 'resolved') {
await logThisToDb(
{
scope: '[update_result] user is not creator or player and challenge is already resolved',
user: user._id,
challengeId: id,
challengeResult: result,
challengeCode: ogChallenge.code,
},
'index.js@update_result'
);
} else {
ogChallenge.state = 'hold';
}
await ogChallenge.save();
return;
}
//get the user role
let userRole = 'creator';
let oponentRole = 'player';
if (ogChallenge.player.toString() == user._id.toString()) {
userRole = 'player';
oponentRole = 'creator';
}
//check if user already updated
if (ogChallenge.results[userRole]) {
await logThisToDb(
{
scope: '[update_result] user already updated result',
challengeId: id,
user: user._id,
challengeResult: result,
challengeCode: ogChallenge.code,
},
'index.js@update_result'
);
return;
}
let challenge;
let ogSus = false;
if (result === 'lost') {
let updateQuery = {};
if (
ogChallenge.createdAt >
new Date().getTime() - 1000 * 60 * 7
) {
console.log('challenge is less than 7 minutes old');
updateQuery = { state: 'hold', isSus: true };
ogSus = true;
} else {
updateQuery = { state: 'resolved' };
}
if (userRole == 'creator') {
updateQuery.creatorResultTime = new Date();
updateQuery.isSus = ogSus || ogChallenge.isSus;
} else {
updateQuery.playerResultTime = new Date();
updateQuery.isSus = ogSus || ogChallenge.isSus;
}
challenge = await Challenge.findOneAndUpdate(
{
_id: id,
$or: [{ state: 'playing' }, { state: 'hold' }],
},
updateQuery
);
await logThisToDb(
{
scope: '[update_result] lost premautre update',
challengeId: id,
user: user._id,
challengeResult: result,
challengeCode: ogChallenge.code,
info: JSON.stringify(updateQuery),
},
'index.js@update_result'
);
} else {
challenge = await Challenge.findOne({
_id: id,
$or: [{ state: 'playing' }, { state: 'hold' }],
});
}
if (!challenge || challenge.state === 'resolved') {
const scope = !challenge
? 'Redirecting because challenge not found'
: 'Redirecting because challenge is resolved';
await logThisToDb(
{
scope: `[update_result] ${scope}`,
challengeId: id,
user: user._id,
challengeResult: result,
challengeCode: ogChallenge.code,
},
'index.js@update_result'
);
socket.emit('redirect', { user: user._id, to: '/' });
return;
}
let isSus = false || ogSus;
let updationQuery = { results: challenge.results };
updationQuery.results[userRole] = result;
if (result === 'won' && challenge.state !== 'resolved') {
updationQuery.state = 'hold';
} else if (
result === 'cancel' &&
challenge.state !== 'resolved' &&
challenge.results[oponentRole] !== 'cancel'
) {
updationQuery.state = 'hold';
} else if (result === 'lost') {
if (
ogChallenge.createdAt >
new Date().getTime() - 1000 * 60 * 7 ||
ogChallenge.results[oponentRole] === 'cancel'
) {
console.log('challenge is less than 7 minutes old');
updationQuery.state = 'hold';
isSus = true;
} else {
updationQuery.state = 'resolved';
}
}
if (
updationQuery.results.creator === 'cancel' &&
updationQuery.results.player === 'cancel'
) {
updationQuery.state = 'resolved';
}
if (result === 'cancel') {
updationQuery.cancellationReasons =
challenge.cancellationReasons || {};
updationQuery.cancellationReasons[userRole] =
cancellationReason;
}
if (userRole == 'creator') {
updationQuery.creatorResultTime = new Date();
updationQuery.isSus = isSus || challenge.isSus;
} else {
updationQuery.playerResultTime = new Date();
updationQuery.isSus = isSus || challenge.isSus;
}
await Challenge.updateOne({ _id: id }, updationQuery);
await logThisToDb(
{
scope: '[update_result] updationQuery',
challengeId: id,
user: user._id,
challengeResult: result,
challengeCode: challenge.code,
msg: `BEFORE UPDATE: ${JSON.stringify(
challenge.results
)} ${JSON.stringify(challenge.state)}}`,
info: JSON.stringify(updationQuery),
},
'index.js@update_result'
);
//determine who fuck what
if (result === 'lost') {
//change ballace for this palyer
// const getLastLost = await History.find({
// user: user._id,
// historyType: 'lose',
// })
// .sort({ createdAt: -1 })
// .limit(1);
// const lastLost = getLastLost[0];
// if (lastLost) {
// const lastLostDate = new Date(lastLost?.createdAt);
// const currentDate = new Date();
// const diff = currentDate.getTime() - lastLostDate.getTime();
// if (lastLost && diff < 4 * 60 * 1000) {
// console.log(user._id, ' last lost less than 4 mins');
// logFrequentLost(
// user._id,
// `last lost was ${(diff / 60000).toFixed(
// 2
// )} minutes ago`
// );
// }
// }
if (!isSus) {
const userResult = challenge.results[userRole]
? ` (Your Result: ${challenge.results[userRole]})`
: null;
const oponentResult = challenge.results[oponentRole]
? ` (Your Result: ${challenge.results[oponentRole]})`
: null;
await changeBallance(
user._id,
{
amount: challenge.amount,
reason: 'lose',
naration: `Lost against ${
challenge[oponentRole + 'Username']
}${userResult ?? ''}`,
winner: await User.findById(challenge[oponentRole]),
uniqueId: `${challenge.id}-${
challenge.code || 'no-code'
}-lose`,
},
`${result}@index.js`,
id
);
//change the ballace of the oponentRole (which automatically adds a history)
await changeBallance(
challenge[oponentRole],
{
amount: challenge.amount,
reason: 'win',
naration: `Won against ${
challenge[userRole + 'Username']
}${oponentResult ?? ''}`,
uniqueId: `${challenge.id}-${
challenge.code || 'no-code'
}-win`,
},
`${result}@index.js`,
id
);
}
//redirect oponent to homepage
io.to(String(challenge[oponentRole])).emit('redirect', {
id: String(challenge[oponentRole]),
to: '/',
});
let tempUser = await User.findById(challenge[oponentRole]);
//update users ballance
io.to(String(challenge[oponentRole])).emit(
'update_wallet',
tempUser.wallet
);
}
//give them their ballace back if both cancels
// TODO
if (
updationQuery.results.creator === 'cancel' &&
updationQuery.results.player === 'cancel'
) {
// await User.updateMany(
// {
// $or: [
// { _id: challenge.creator },
// { _id: challenge.player },
// ],
// },
// { $inc: { wallet: challenge.amount } }
// );
//add two history's
const creatorUniqueId = `${challenge.id}-${
challenge.code || 'no-code'
}-cancel-creator`;
const creatorHistory = await createHistory(
{
user: challenge.creator,
amount: challenge.amount,
historyType: 'cancel',
naration: `Cancelled Against ${challenge.playerUsername} (Your Result: ${challenge.results.creator})`,
uniqueId: creatorUniqueId,
},
'index.js@cancel-creator',
id
);
if (creatorHistory) {
await logThisToDb({
scope: '[Wallet balance change]',
idx1: challenge.amount,
challengeId: challenge._id,
user: challenge.creator,
msg: `on cancel - inc`,
});
await User.findByIdAndUpdate(challenge.creator, {
$inc: { wallet: challenge.amount },
});
} else {
logToChannel(
`skipping changing wallet balance for player ${challenge.creator} of ${challenge.amount} because of double hisory creation. uniqueId: ${creatorUniqueId}`,
true
);
}
const playerUniqueId = `${challenge.id}-${
challenge.code || 'no-code'
}-cancel-player`;
const playerHistory = await createHistory(
{
user: challenge.player,
amount: challenge.amount,
historyType: 'cancel',
naration: `Cancelled against ${challenge.creatorUsername} (Your Result: ${challenge.results.player})`,
uniqueId: playerUniqueId,
},
'index.js@cancel-player',
id
);
if (playerHistory) {
await logThisToDb({
scope: '[Wallet balance change]',
idx1: challenge.amount,
challengeId: challenge._id,
user: challenge.player,
msg: `on cancel - inc`,
});
await User.findByIdAndUpdate(challenge.player, {
$inc: { wallet: challenge.amount },
});
} else {
logToChannel(
`skipping changing wallet balance for player ${challenge.creator} of ${challenge.amount} because of double hisory creation. uniqueId: ${playerUniqueId}`,
true
);
}
}
socket.emit('redirect', { id: user._id, to: '/' });
sendRunningChallenges(io);
//some other shits
// if (result === 'won') {
// setTimeout(() => {
// resolveChallange({
// id,
// challenge,
// userRole,
// oponentRole,
// result,
// });
// }, 15 * 60 * 1000);
// }
//this function is meant to be called when this player updated cancel and the opponent havent updated result for x mins
// if (result === 'cancel') {
// setTimeout(() => {
// cancelChallange({
// id,
// challenge,
// userRole,
// oponentRole,
// result,
// });
// }, 15 * 60 * 1000);
// }
} catch (err) {
console.error('976@index.js' + err);
}
});
async function DeleteChallenges() {
if (!user) {
return;
}
//delete all open or requested challenges of this user
let deletionQuery = {
creator: user._id,
state: { $in: ['open', ''] },
};
let response = await Challenge.deleteMany(deletionQuery);
//update all the challenges that this user is a player on
let secondResponse = await Challenge.updateMany(
{ state: 'requested', player: user._id },
{ state: 'open', player: undefined, playerUsername: undefined }
);
//emmit refresh events
if (response.deletedCount > 0 || secondResponse.nModified > 0) {
sendPlayableChallanges(io);
sendAllNonPlayingChallenges(io);
}
}
socket.on('page_change', async () => {
await DeleteChallenges();
});
socket.on('disconnect', async () => {
if (inited) {
return;
}
connectedUsers = connectedUsers.filter(
(_user) => _user.id !== user?._id.toString()
);
await DeleteChallenges();
});
});Editor is loading...