├── bot ├── logs │ └── .gitignore └── tmp │ └── .gitignore ├── public ├── img │ ├── logo.png │ ├── t-side.png │ ├── ct-side.png │ ├── emots │ │ ├── gg.png │ │ ├── hs.png │ │ ├── chan.png │ │ ├── dead.png │ │ ├── duck.png │ │ ├── fail.png │ │ ├── kappa.png │ │ ├── logo.png │ │ ├── lol.png │ │ ├── lucky.png │ │ ├── rekt.png │ │ ├── rip.png │ │ ├── salt.png │ │ ├── dollars.png │ │ ├── facepalm.png │ │ └── illuminati.png │ ├── coins-green.png │ ├── coins-white.png │ ├── free-flip.png │ ├── loading-2.gif │ └── signin-steam.png ├── favicon-16x16.png ├── favicon-32x32.png ├── audio │ └── opening-sound.mp3 ├── fonts │ └── fontawesome-webfont.woff2 ├── js │ ├── detail.js │ ├── verify.js │ ├── profile.js │ ├── cmodal.js │ ├── md5.js │ ├── app.js │ ├── jquery.countdown360.js │ └── chat.js └── css │ ├── chat.css │ ├── ring.css │ └── coinflip.css ├── routes ├── faq.js ├── terms.js ├── giveaway.js ├── getStarted.js ├── coinflip-faq.js ├── edit-profile.js ├── auth.js ├── api.js ├── leaderboard.js ├── coinflip.js └── jackpot.js ├── views ├── error.ejs ├── giveaway.ejs ├── templates │ ├── top-menu-profile.ejs │ ├── side-menu-profile.ejs │ ├── games-scripts.ejs │ ├── jackpot-handlebars.ejs │ └── coinflip-handlebars.ejs ├── chat.ejs ├── edit-profile.ejs ├── get-started.ejs ├── leaderboard.ejs ├── coinflip-leaderboard.ejs ├── layouts │ ├── error.ejs │ └── main.ejs ├── coinflip-history.ejs ├── coinflip.ejs ├── faq.ejs ├── jackpot-history.ejs ├── jackpot-details.ejs ├── coinflip-details.ejs ├── terms.ejs ├── jackpot.ejs └── coinflip-faq.ejs ├── models ├── index.js ├── db │ ├── coinflipRound.js │ ├── jackpotRound.js │ ├── offer.js │ ├── coinflipOffer.js │ ├── user.js │ └── index.js ├── log.js ├── passport.js ├── api.js └── helper.js ├── servers ├── status.js └── chat.js ├── README.md ├── bin └── www ├── package.json ├── prices.js ├── express.js └── config └── index.example.js /bot/logs/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /bot/tmp/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/logo.png -------------------------------------------------------------------------------- /public/img/t-side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/t-side.png -------------------------------------------------------------------------------- /public/img/ct-side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/ct-side.png -------------------------------------------------------------------------------- /public/img/emots/gg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/gg.png -------------------------------------------------------------------------------- /public/img/emots/hs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/hs.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/coins-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/coins-green.png -------------------------------------------------------------------------------- /public/img/coins-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/coins-white.png -------------------------------------------------------------------------------- /public/img/emots/chan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/chan.png -------------------------------------------------------------------------------- /public/img/emots/dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/dead.png -------------------------------------------------------------------------------- /public/img/emots/duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/duck.png -------------------------------------------------------------------------------- /public/img/emots/fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/fail.png -------------------------------------------------------------------------------- /public/img/emots/kappa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/kappa.png -------------------------------------------------------------------------------- /public/img/emots/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/logo.png -------------------------------------------------------------------------------- /public/img/emots/lol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/lol.png -------------------------------------------------------------------------------- /public/img/emots/lucky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/lucky.png -------------------------------------------------------------------------------- /public/img/emots/rekt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/rekt.png -------------------------------------------------------------------------------- /public/img/emots/rip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/rip.png -------------------------------------------------------------------------------- /public/img/emots/salt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/salt.png -------------------------------------------------------------------------------- /public/img/free-flip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/free-flip.png -------------------------------------------------------------------------------- /public/img/loading-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/loading-2.gif -------------------------------------------------------------------------------- /public/img/emots/dollars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/dollars.png -------------------------------------------------------------------------------- /public/img/signin-steam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/signin-steam.png -------------------------------------------------------------------------------- /public/audio/opening-sound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/audio/opening-sound.mp3 -------------------------------------------------------------------------------- /public/img/emots/facepalm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/facepalm.png -------------------------------------------------------------------------------- /public/img/emots/illuminati.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/img/emots/illuminati.png -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baterka/CSGOHunt.com-Copy/HEAD/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /routes/faq.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('faq', {}); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

<%- (error.code === 404 ? " " : "") + error.title %>

4 |

<%= error.message %>

5 |
6 |
-------------------------------------------------------------------------------- /routes/terms.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('terms', {}); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /routes/giveaway.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('giveaway', {}); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /routes/getStarted.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('get-started', {}); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /routes/coinflip-faq.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('coinflip-faq', {}); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /models/index.js: -------------------------------------------------------------------------------- 1 | const Log = require('./log'), 2 | Passport = require('./passport'), 3 | Db = require('./db'), 4 | Api = require('./api'), 5 | Helper = require('./helper'); 6 | 7 | module.exports = { 8 | Log, 9 | Db, 10 | Passport, 11 | Api, 12 | Helper 13 | }; -------------------------------------------------------------------------------- /routes/edit-profile.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | models = require('../models'); 3 | const router = express.Router(); 4 | 5 | /* GET home page. */ 6 | router.get('/', models.Passport.isAuthenticated, function(req, res) { 7 | res.render('edit-profile', {}); 8 | }); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /views/giveaway.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 8 |
9 |
-------------------------------------------------------------------------------- /routes/auth.js: -------------------------------------------------------------------------------- 1 | const app = require('express'), 2 | router = app.Router(); 3 | 4 | module.exports = function(passport) { 5 | router.get('/', passport.authenticate('steam')); 6 | 7 | router.get('/return', 8 | passport.authenticate('steam', { failureRedirect: '/404' }), (req, res) => { 9 | res.redirect('/'); 10 | }); 11 | 12 | router.get('/logout', function(req, res){ 13 | req.logout(); 14 | req.session.destroy(); 15 | res.redirect('/'); 16 | }); 17 | 18 | return router; 19 | }; -------------------------------------------------------------------------------- /views/templates/top-menu-profile.ejs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/js/detail.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | for (var i = 0; i < bets.length; i++){ 3 | var bet = bets[i]; 4 | bet.items.sort(function (a, b) { 5 | return a.price < b.price; 6 | }); 7 | var poContent = ""; 8 | for (var k in bet.items) { 9 | var itm = bet.items[k]; 10 | var wrp = '
'; 11 | wrp += ''; 12 | wrp += '
' + itm.price + '
'; 13 | wrp += '
'; 14 | poContent += wrp; 15 | } 16 | $("div[data-offer=" + bet.offerID + "]").popover({content: poContent, html: true, placement: "left", trigger: "hover"}); 17 | } 18 | }); -------------------------------------------------------------------------------- /public/js/verify.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $("#verify-btn").on("click", function () { 3 | var data = {hash: $("#hash").val(), secret: $("#secret").val(), percent: $("#percent").val(), nbrTickets: $("#nbrTickets").val()}; 4 | if (CryptoJS.MD5(data.percent + ":" + data.secret) == data.hash) { 5 | var wt = Math.floor((data.nbrTickets - 0.0000000001) * (data.percent / 100)); 6 | alert("The winning ticket can be found at position " + wt); 7 | } 8 | else alert("Hash doesn't match."); 9 | }); 10 | if (window.location.hash) { 11 | var data = window.location.hash.substring(1).split(';'); 12 | $("#hash").val(data[0]), $("#secret").val(data[1]), $("#percent").val(data[2]), $("#nbrTickets").val(data[3]) 13 | } 14 | }); -------------------------------------------------------------------------------- /routes/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | models = require('../models'); 3 | require('express-async-errors'); 4 | 5 | const router = express.Router(); 6 | 7 | /* API Routes */ 8 | router.post('/update-profile', models.Passport.isAuthenticated, async function (req, res, next) { 9 | res.json(await models.Api.updateProfile(req, res)); 10 | }); 11 | 12 | router.get('/get-inventory', models.Passport.isAuthenticated, async function (req, res, next) { 13 | res.json(await models.Api.getInventory(req.query.sid)); 14 | }); 15 | 16 | router.get('/duel-check', models.Passport.isAuthenticated, async function (req, res, next) { 17 | const provably = await models.Api.coinflipProvably(req.query); 18 | if (provably.error) { 19 | res.send(400, {error: provably.error}); 20 | } else 21 | res.json(provably) 22 | }); 23 | 24 | module.exports = router; 25 | -------------------------------------------------------------------------------- /models/db/coinflipRound.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | return sequelize.define('CoinflipRound', { 3 | roundID: { 4 | type: DataTypes.CHAR(24), 5 | allowNull: false, 6 | unique: true 7 | }, 8 | hashes: { 9 | type: DataTypes.JSON, 10 | allowNull: false 11 | }, 12 | status: { 13 | type: DataTypes.INTEGER(1), 14 | defaultValue: 1, 15 | allowNull: false, 16 | }, 17 | creator: { 18 | type: DataTypes.JSON, 19 | defaultValue: [], 20 | allowNull: false 21 | }, 22 | joiner: { 23 | type: DataTypes.JSON, 24 | defaultValue: [], 25 | allowNull: false 26 | }, 27 | winnerID: { 28 | type: DataTypes.CHAR(17), 29 | allowNull: true, 30 | } 31 | }); 32 | }; -------------------------------------------------------------------------------- /public/js/profile.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $("#save-profile-btn").on("click", function () { 3 | var req = $.ajax({method: "POST", url: "/api/update-profile", dataType: "json", data: {tradeurl: $("#trade-url").val(), _csrf: $("#csrf").val()}}); 4 | req.done(function (resp) { 5 | $(".profile-form .alert").remove(); 6 | if (resp.result) { 7 | $(".profile-form").prepend('
Profile saved.
'); 8 | } 9 | else { 10 | $(".profile-form").prepend('
' + resp.message + '
'); 11 | } 12 | setTimeout(function () { 13 | $(".profile-form .alert").fadeOut(function () { 14 | this.remove(); 15 | }); 16 | }, 10000); 17 | }); 18 | req.fail(function (jqXHR, textStatus) { 19 | console.log(jqXHR) 20 | }); 21 | }); 22 | }); -------------------------------------------------------------------------------- /views/chat.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |
8 |
9 |
10 |
11 | 12 |
13 |
14 |
15 |
16 |
17 | 18 | 19 | 22 |
23 |
24 |
25 |
-------------------------------------------------------------------------------- /models/log.js: -------------------------------------------------------------------------------- 1 | process.stdout.isTTY = true; //Fix for WebStorm to show colors in terminal 2 | const config = require('../config'), 3 | log4js = require('log4js'); 4 | 5 | log4js.configure({ 6 | appenders: { 7 | out: { 8 | type: 'stdout', 9 | layout: { 10 | type: 'pattern', 11 | pattern: '[%d{dd.MM.yyyy hh:mm:ss}]%[[%p] %m%]', 12 | } 13 | }, 14 | nodebug: { 15 | type: 'logLevelFilter', 16 | appender: 'out', 17 | level: 'info' 18 | }, 19 | app: { 20 | type: 'file', 21 | filename: config.logging.file, 22 | layout: { 23 | type: 'pattern', 24 | pattern: '[%d{dd.MM.yyyy hh:mm:ss:SSS}][%p] %m', 25 | } 26 | } 27 | }, 28 | categories: { 29 | default: { 30 | appenders: [(config.logging.debug ? 'out' : 'nodebug'), 'app'], 31 | level: 'debug' 32 | } 33 | } 34 | }); 35 | const logger = log4js.getLogger(); 36 | 37 | module.exports = logger; -------------------------------------------------------------------------------- /servers/status.js: -------------------------------------------------------------------------------- 1 | const socketio = require('socket.io'), 2 | axios = require('axios'), 3 | config = require('../config'), 4 | Helper = require('../models').Helper, 5 | models = require('../models'); 6 | 7 | const Log = models.Log; 8 | 9 | const io = socketio(); 10 | 11 | const originURL = "http://localhost:"; 12 | 13 | broadcast(); 14 | 15 | async function broadcast() { 16 | let jackpot = await emit(config.botServers.jackpot, "totalValue"); 17 | let coinflip = await emit(config.botServers.coinflip, "totalValue"); 18 | 19 | io.sockets.emit('currentlyPlayed', { 20 | "jackpot_total": jackpot, 21 | "cf_total": coinflip 22 | }); 23 | 24 | await Helper.delay(2000); 25 | broadcast(); 26 | } 27 | 28 | async function emit(port, action) { 29 | try { 30 | let res = await axios.request({ 31 | method: "post", 32 | url: originURL + port + "/" + action, 33 | timeout: 500 34 | }); 35 | return res.data; 36 | } catch (err) { 37 | //Log.error("Port: " + port + " " + err.toString()); 38 | return 0; 39 | } 40 | } 41 | 42 | module.exports = io; 43 | -------------------------------------------------------------------------------- /routes/leaderboard.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | const models = require('../models'); 5 | 6 | const Db = models.Db, 7 | Helper = models.Helper; 8 | 9 | /* GET home page. */ 10 | router.get('/', async (req, res, next) => { 11 | let players = await Db.User.findAll({ 12 | order: [['totalWon', 'DESC']], 13 | limit: 10 14 | }); 15 | 16 | if (!players) 17 | players = []; 18 | 19 | for (let i in players) { 20 | players[i].avatar = Helper.User.buildAvatar(players[i].avatar) 21 | } 22 | 23 | res.locals.players = players; 24 | 25 | res.render('leaderboard', {}); 26 | }); 27 | 28 | router.get('/coinflip', async (req, res, next) => { 29 | let players = await Db.User.findAll({ 30 | order: [['coinflipTotalWon', 'DESC']], 31 | limit: 10 32 | }); 33 | 34 | if (!players) 35 | players = []; 36 | 37 | for (let i in players) { 38 | players[i].avatar = Helper.User.buildAvatar(players[i].avatar) 39 | } 40 | 41 | res.locals.players = players; 42 | 43 | res.render('coinflip-leaderboard', {}); 44 | }); 45 | 46 | module.exports = router; 47 | -------------------------------------------------------------------------------- /views/templates/side-menu-profile.ejs: -------------------------------------------------------------------------------- 1 |
2 |
Level: <%= user.level %>
3 |
profil_avatar
4 | 26 |
-------------------------------------------------------------------------------- /views/edit-profile.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 10 | 11 | 12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 |
20 |
21 | <%- contentFor('scripts') %> 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSGOHunt.com - Copy 2 | 3 | ## This project was private and paid. I made it public after client lost interest in it. It means it will never be updated and there is no guarantee that it works and does not have security flaws. Enjoy! 4 | 5 | Almost identical copy of [CSGOHunt.com](https://www.csgohunt.com/) from 2018. Full NodeJS project. 6 | 7 | ### Powered by 8 | 9 | * [Node.js](http://nodejs.org) 10 | * [MariaDB](https://mariadb.org/) 11 | 12 | ### Installation 13 | 14 | Requires [Node.js](https://nodejs.org/) 8+ and [MariaDB](https://mariadb.org/) 10.2+ database to run. 15 | 16 | Install the dependencies: 17 | 18 | ```sh 19 | $ npm install 20 | ``` 21 | 22 | Rename [config.index.js](https://github.com/Baterka/CSGOHunt.com-Copy/blob/master/config/index.example.js) file to index.js and fill all required data. 23 | 24 | Start website: 25 | 26 | ```sh 27 | $ node bin/www 28 | ``` 29 | 30 | Start servers: 31 | 32 | ```sh 33 | $ node servers/[jackpot|coinflip|chat].js 34 | ``` 35 | 36 | Start bots: 37 | 38 | ```sh 39 | $ node bot/[jackpot|coinflip].js 40 | ``` 41 | 42 | I recommend using [pm2](https://github.com/Unitech/pm2) for managing bot. 43 | 44 | ### What you can do for project 45 | - Code revision 46 | - Testing 47 | - Rewrite to Non-relational database (MongoDB) 48 | -------------------------------------------------------------------------------- /models/db/jackpotRound.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | return sequelize.define('JackpotRound', { 3 | roundID: { 4 | type: DataTypes.CHAR(24), 5 | allowNull: false, 6 | unique: true 7 | }, 8 | hashes: { 9 | type: DataTypes.JSON, 10 | allowNull: false 11 | }, 12 | status: { 13 | type: DataTypes.INTEGER(1), 14 | defaultValue: 0, 15 | allowNull: false, 16 | }, 17 | players: { 18 | type: DataTypes.JSON, 19 | defaultValue: [], 20 | allowNull: false 21 | }, 22 | bets: { 23 | type: DataTypes.JSON, 24 | defaultValue: [], 25 | allowNull: false 26 | }, 27 | winnerSteamID: { 28 | type: DataTypes.CHAR(17), 29 | allowNull: true 30 | }, 31 | winnerFee: { 32 | type: DataTypes.INTEGER, 33 | allowNull: true 34 | }, 35 | feeItems: { 36 | type: DataTypes.JSON, 37 | defaultValue: [], 38 | allowNull: false 39 | }, 40 | endedAt: { 41 | type: DataTypes.DATE, 42 | allowNull: true, 43 | } 44 | }); 45 | }; -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const app = require('../express'), 3 | log = require('../models/log'), 4 | http = require('http'), 5 | config = require('../config'), 6 | Db = require('../models/db'); 7 | 8 | const port = config.servers.ports.website; 9 | app.set('port', port); 10 | 11 | const server = http.createServer(app); 12 | 13 | const io = app.io; 14 | io.attach(server); 15 | 16 | Db.sequelize.authenticate().then(() => { 17 | log.info("Successfully connected to database"); 18 | server.listen(port); 19 | 20 | server.on('error', onError); 21 | server.on('listening', onListening); 22 | }).catch(err => { 23 | log.error("An error occurred while connecting to database: " + err); 24 | process.exit(1); 25 | }); 26 | 27 | function onError(error) { 28 | switch (error.code) { 29 | case 'EACCES': 30 | log.error('Port ' + port + ' requires elevated privileges'); 31 | process.exit(1); 32 | break; 33 | case 'EADDRINUSE': 34 | log.error('Port ' + port + ' is already in use'); 35 | process.exit(1); 36 | break; 37 | default: 38 | log.error('An error occurred while starting Express server: ' + error.code); 39 | process.exit(1); 40 | } 41 | } 42 | 43 | function onListening() { 44 | log.info('HTTP & Socket.IO servers listening on port ' + port); 45 | } -------------------------------------------------------------------------------- /views/get-started.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

How it works ?

6 |

Players deposit skins in a round. Once the number of required skins is reached, a winner will be picked based on the number of tickets his deposit represents and 7 | will be awarded all the skins in the pot !

8 |

For every 0.01 bet value a player will get 1 ticket, so a higher number of tickets means a better % of winning !

9 |

How to play ?

10 |
    11 |
  • Login with your steam account
  • 12 |
  • Set your trade link by editing your profile url 13 | here.This is a necessary step to receive the item you wins. 14 |
  • 15 |
  • Click on the "Deposit" button to open a trade and chose the items you want to enter the round with.
  • 16 |
  • Verify the total value of your bet and confirm to enter the current round.
  • 17 |
  • Once the item threshold of 50 items is reached, the winner will be announced.
  • 18 |
  • If you won, you will receive all the items in the pot as a steam trade offer.
  • 19 |
20 |
21 |
22 |
23 |
-------------------------------------------------------------------------------- /models/db/offer.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | return sequelize.define('Offer', { 3 | offerID: { 4 | type: DataTypes.CHAR(11), 5 | allowNull: true, 6 | unique: true 7 | }, 8 | type: { 9 | type: DataTypes.CHAR(8), 10 | allowNull: false 11 | }, 12 | botID: { 13 | type: DataTypes.INTEGER, 14 | allowNull: false 15 | }, 16 | currentState: { 17 | type: DataTypes.INTEGER(2), 18 | defaultValue: -1, 19 | allowNull: false 20 | }, 21 | partner: { 22 | type: DataTypes.CHAR(17), 23 | allowNull: false 24 | }, 25 | message: { 26 | type: DataTypes.STRING(100), 27 | allowNull: true 28 | }, 29 | items: { 30 | type: DataTypes.JSON, 31 | allowNull: true 32 | }, 33 | totalValue:{ 34 | type: DataTypes.INTEGER, 35 | defaultValue: -1, 36 | allowNull: false 37 | }, 38 | status: { 39 | type: DataTypes.INTEGER(1), 40 | defaultValue: 0, 41 | allowNull: false 42 | }, 43 | data: { 44 | type: DataTypes.JSON, 45 | allowNull: true 46 | }, 47 | attempts: { 48 | type: DataTypes.INTEGER(3), 49 | defaultValue: 0, 50 | allowNull: false 51 | } 52 | }); 53 | }; -------------------------------------------------------------------------------- /models/db/coinflipOffer.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | return sequelize.define('CoinflipOffer', { 3 | offerID: { 4 | type: DataTypes.CHAR(11), 5 | allowNull: true, 6 | unique: true 7 | }, 8 | type: { 9 | type: DataTypes.CHAR(8), 10 | allowNull: false 11 | }, 12 | botID: { 13 | type: DataTypes.INTEGER, 14 | allowNull: false 15 | }, 16 | currentState: { 17 | type: DataTypes.INTEGER(2), 18 | defaultValue: -1, 19 | allowNull: false 20 | }, 21 | partner: { 22 | type: DataTypes.CHAR(17), 23 | allowNull: false 24 | }, 25 | message: { 26 | type: DataTypes.STRING(100), 27 | allowNull: true 28 | }, 29 | items: { 30 | type: DataTypes.JSON, 31 | allowNull: true 32 | }, 33 | totalValue:{ 34 | type: DataTypes.INTEGER, 35 | defaultValue: -1, 36 | allowNull: false 37 | }, 38 | status: { 39 | type: DataTypes.INTEGER(1), 40 | defaultValue: 0, 41 | allowNull: false 42 | }, 43 | data: { 44 | type: DataTypes.JSON, 45 | allowNull: true 46 | }, 47 | attempts: { 48 | type: DataTypes.INTEGER(3), 49 | defaultValue: 0, 50 | allowNull: false 51 | } 52 | }); 53 | }; -------------------------------------------------------------------------------- /models/db/user.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | return sequelize.define('User', { 3 | steamid: { 4 | type: DataTypes.CHAR(17), 5 | allowNull: false, 6 | unique: true 7 | }, 8 | name: { 9 | type: DataTypes.STRING(32), 10 | allowNull: false, 11 | unique: false 12 | }, 13 | rank:{ 14 | type: DataTypes.STRING(2), 15 | allowNull: false, 16 | defaultValue: 0 17 | }, 18 | avatar: { 19 | type: DataTypes.STRING(50), 20 | allowNull: false 21 | }, 22 | tradeToken: { 23 | type: DataTypes.STRING(10), 24 | allowNull: true 25 | }, 26 | xp: { 27 | type: DataTypes.INTEGER, 28 | allowNull: false, 29 | defaultValue: 0 30 | }, 31 | totalBet: { 32 | type: DataTypes.FLOAT, 33 | allowNull: false, 34 | defaultValue: 0 35 | }, 36 | totalWon: { 37 | type: DataTypes.FLOAT, 38 | allowNull: false, 39 | defaultValue: 0 40 | }, 41 | coinflipTotalBet: { 42 | type: DataTypes.FLOAT, 43 | allowNull: false, 44 | defaultValue: 0 45 | }, 46 | coinflipTotalWon: { 47 | type: DataTypes.FLOAT, 48 | allowNull: false, 49 | defaultValue: 0 50 | } 51 | }, { 52 | timestamps: false 53 | }); 54 | }; -------------------------------------------------------------------------------- /views/leaderboard.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Top 10 jackpot players

6 |
7 | Jackpot 8 | Coinflip 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% 20 | let i = 1; 21 | for(let player of players){ 22 | %> 23 | 24 | 25 | 28 | 29 | 30 | <% 31 | i++; 32 | } 33 | %> 34 | 35 |
#NameTotal won
<%= i %> 26 | <%= player.name %> 27 | <%= player.totalWon %>
36 |
37 |
38 |
39 |
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "csgohunt", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "nodemon ./bin/www" 7 | }, 8 | "dependencies": { 9 | "async": "latest", 10 | "axios": "^0.18.0", 11 | "body-parser": "~1.18.2", 12 | "colors": "^1.2.1", 13 | "connect-session-sequelize": "^5.2.1", 14 | "cookie-parser": "~1.4.3", 15 | "csurf": "^1.9.0", 16 | "debug": "~2.6.9", 17 | "ejs": "^2.5.8", 18 | "express": "~4.15.5", 19 | "express-async-errors": "^2.1.2", 20 | "express-ejs-layouts": "^2.4.0", 21 | "express-mysql-session": "^1.2.3", 22 | "express-sequelize-session": "^0.4.0", 23 | "express-session": "^1.15.6", 24 | "express-socket.io-session": "^1.3.3", 25 | "fifo": "^2.3.0", 26 | "log4js": "^2.5.3", 27 | "memwatch-next": "^0.3.0", 28 | "moment": "latest", 29 | "morgan": "~1.9.0", 30 | "mysql": "^2.15.0", 31 | "mysql2": "^1.5.3", 32 | "passport": "^0.4.0", 33 | "passport-steam": "latest", 34 | "passport.socketio": "^3.7.0", 35 | "sequelize": "^5.3.0", 36 | "serve-favicon": "~2.4.5", 37 | "socket.io": "^2.1.0", 38 | "socket.io-bundle": "^0.1.2", 39 | "socket.io-passport": "^0.1.1", 40 | "speakeasy": "^2.0.0", 41 | "steam-totp": "^2.1.0", 42 | "steam-tradeoffer-manager": "^2.9.3", 43 | "steam-user": "^3.27.0", 44 | "steamcommunity": "latest", 45 | "steamid": "^1.1.0", 46 | "string-argv": "0.0.2", 47 | "usage": "^0.7.1", 48 | "yargs": "latest" 49 | }, 50 | "devDependencies": { 51 | "nodemon": "^1.17.3" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /views/coinflip-leaderboard.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Top 10 coinflip players

6 |
7 | Jackpot 8 | Coinflip 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% 20 | let i = 1; 21 | for(let player of players){ 22 | %> 23 | 24 | 25 | 28 | 29 | 30 | <% 31 | i++; 32 | } 33 | %> 34 | 35 |
#NameTotal won
<%= i %> 26 | <%= player.name %> 27 | <%= player.coinflipTotalWon %>
36 |
37 |
38 |
39 |
-------------------------------------------------------------------------------- /views/layouts/error.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CSGOHunt.com - <%= error.title %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <%- defineContent('styles') %> 19 | 20 | 21 | 36 | <%- body %> 37 | 38 | 39 | -------------------------------------------------------------------------------- /models/passport.js: -------------------------------------------------------------------------------- 1 | const passport = require('passport'), 2 | SteamStrategy = require('passport-steam').Strategy, 3 | config = require('../config'), 4 | Log = require('./log'), 5 | Db = require('./db'), 6 | Helper = require('./helper'); 7 | 8 | passport.use(new SteamStrategy({ 9 | returnURL: config.website.url + '/auth/return', 10 | realm: config.website.url, 11 | apiKey: config.steam.apiKey 12 | }, 13 | function (identifier, profile, done) { 14 | return done(null, profile); 15 | } 16 | )); 17 | 18 | passport.serializeUser(async (user, cb) => { 19 | let avatarUrl = user.photos[0].value.split("/"); 20 | let data = { 21 | steamid: user.id, 22 | name: user.displayName, 23 | avatar: avatarUrl[avatarUrl.length - 1].slice(0, -4) 24 | }; 25 | try { 26 | await Db.action.User.update(data, {steamid: user.id}); 27 | cb(null, user.id); 28 | } catch (err) { 29 | Log.error("An error occurred while executing database action: " + err); 30 | cb(null, null); 31 | } 32 | }); 33 | 34 | passport.deserializeUser(async (steamid, cb) => { 35 | try { 36 | const user = await Db.action.User.get({steamid: steamid}); 37 | user.level = Math.floor(user.xp / config.global.xpPerLevel); 38 | user.avatar = config.steam.avatarStore + user.avatar.slice(0, 2) + "/" + user.avatar; 39 | user.tradelink = (user.tradeToken !== null ? "https://steamcommunity.com/tradeoffer/new/?partner=" + Helper.getSteamID3(steamid) + "&token=" + user.tradeToken : ""); 40 | cb(null, user); 41 | } catch (err) { 42 | Log.error("An error occurred while executing database action: " + err); 43 | cb(null, null); 44 | } 45 | }); 46 | 47 | passport.isAuthenticated = function (req, res, next) { 48 | if (req.isAuthenticated()) { 49 | return next(); 50 | } 51 | res.redirect('/auth'); 52 | }; 53 | 54 | module.exports = passport; -------------------------------------------------------------------------------- /prices.js: -------------------------------------------------------------------------------- 1 | let axios = require('axios'); 2 | let fs = require('fs'); 3 | let speakeasy = require('speakeasy'); 4 | 5 | let secret = "TM72QL2MPOQZLZEL"; 6 | let api_key = "f937d313-e693-491f-ae29-c4258878fc5c"; 7 | 8 | let token = speakeasy.totp({ 9 | secret: secret, 10 | encoding: 'base32' 11 | }); 12 | 13 | let url = "https://bitskins.com/api/v1/get_all_item_prices/?api_key=" + api_key + "&code=" + token; //Full URL to source 14 | let path = "./"; //Full path to prices.json file 15 | 16 | function loadPrices() { 17 | console.log('Downloading prices from: \'' + url + '\''); 18 | axios.get(url) 19 | .then(function (response) { 20 | if (response.data.status === "success") { 21 | fs.writeFileSync(path + 'prices_raw.json', JSON.stringify(response.data.prices)); 22 | let prices = response.data.prices; 23 | let processedPrices = {}; 24 | let l = Object.keys(prices).length; 25 | for (let i = 0; i < l; i++) { 26 | processedPrices[prices[i].market_hash_name] = (prices[i].price * 100).toFixed(0); 27 | } 28 | let json = JSON.stringify(processedPrices); 29 | fs.writeFileSync(path + 'prices.json', json); 30 | console.log('Prices saved to \'' + path + '\''); 31 | } else 32 | console.log('Error while downloading...'); 33 | }) 34 | .catch(function (error) { 35 | console.log(error); 36 | }); 37 | } 38 | 39 | loadPrices(); 40 | 41 | //parsePrices(); 42 | function parsePrices() { 43 | let raw = fs.readFileSync(path + '/prices_raw.txt'); 44 | let prices = JSON.parse(raw); 45 | let i; 46 | let l = Object.keys(prices.prices).length; 47 | let prices = {}; 48 | for (i = 0; i < l; i++) { 49 | prices[prices.prices[i].market_hash_name] = prices.prices[i].price; 50 | } 51 | let json = JSON.stringify(prices); 52 | fs.writeFileSync(path + '/prices.txt', json); 53 | console.log('Prices saved to \'' + path + '\''); 54 | } -------------------------------------------------------------------------------- /views/templates/games-scripts.ejs: -------------------------------------------------------------------------------- 1 | 2 | 10 | 32 | 41 | 42 | -------------------------------------------------------------------------------- /public/js/cmodal.js: -------------------------------------------------------------------------------- 1 | function CModal() { 2 | this.tpl = Handlebars.compile($("#tpl-cmodal").html()); 3 | this.elem = null; 4 | this.options = { 5 | isConfirm: true, 6 | altTheme: false, 7 | large: false, 8 | confirmCallback: null, 9 | closeCallback: null, 10 | closeOnConfirm: true, 11 | confirmLabel: "Confirm", 12 | closeLabel: "Cancel", 13 | bs_options: {} 14 | }; 15 | } 16 | 17 | CModal.prototype.init = function (options) { 18 | var self = this; 19 | var html = this.tpl({title: options.title, message: options.content}); 20 | jQuery.extend(self.options, options); 21 | self.elem = $(html); 22 | if (options.altTheme) 23 | self.elem.addClass("modal-alt"); 24 | if (options.large) 25 | self.elem.find(".modal-dialog").addClass("modal-lg"); 26 | self.elem.find(".btn-cancel").html(options.closeLabel); 27 | if (self.options.isConfirm || self.options.confirmCallback) { 28 | self.options.isConfirm = true; 29 | var cBtn = self.elem.find(".modal-footer .btn-confirm"); 30 | cBtn.html(self.options.confirmLabel); 31 | cBtn.on("click", function (e) { 32 | if (self.options.closeOnConfirm) 33 | self.elem.modal('hide'); 34 | options.confirmCallback && options.confirmCallback(); 35 | }); 36 | } 37 | else { 38 | self.elem.find(".btn-cancel").html("Close"); 39 | self.elem.find(".btn-confirm").remove(); 40 | } 41 | self.elem.on("shown.bs.modal", function (e) { 42 | if (options.shownCallback) 43 | options.shownCallback(e); 44 | }); 45 | self.elem.find(".modal-footer .btn-cancel").on("click", function (e) { 46 | self.elem.modal('hide'); 47 | options.closeCallback && options.closeCallback(); 48 | }); 49 | self.elem.on("hidden.bs.modal", function (e) { 50 | self.destroy(); 51 | }); 52 | return this; 53 | }; 54 | CModal.prototype.setContent = function (html) { 55 | this.elem.find(".modal-body").html(html); 56 | }; 57 | CModal.prototype.show = function () { 58 | this.elem.modal(this.options.bs_options); 59 | this.elem.modal('show'); 60 | }; 61 | CModal.prototype.hide = function () { 62 | this.elem.modal('hide'); 63 | }; 64 | CModal.prototype.destroy = function () { 65 | this.elem.remove(); 66 | }; -------------------------------------------------------------------------------- /views/coinflip-history.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 8 |
9 |
10 |

Total bet: <%= user.coinflipTotalBet %>

11 |

Total won: <%= user.coinflipTotalWon %>

12 |

Profit: <%= Number(user.coinflipTotalWon - user.coinflipTotalBet.toFixed(2)) %>

13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | <% rounds.forEach(round => { %> 29 | 30 | 33 | 36 | 37 | 40 | 41 | 42 | 45 | <% }); %> 46 | 47 |
Duel #End dateTotal valueWin/LoseTrade offer CodeDetails
31 | <%= round.roundID; %> 32 | 34 | <%= round.updatedAt; %> 35 | <%= round.totalValue; %> 38 | <%- (round.won ? "W" : "L") %> 39 | <%= round.pot; %><%= round.pin; %> 43 | Details 44 |
48 |
49 |
50 |
51 |
-------------------------------------------------------------------------------- /public/css/chat.css: -------------------------------------------------------------------------------- 1 | #chatWindow { 2 | color: #fff; 3 | height: 80%; 4 | position: relative; 5 | } 6 | 7 | .coinflip-banner { 8 | position: absolute; 9 | z-index: 500; 10 | } 11 | 12 | .coinflip-banner a { 13 | display: block; 14 | width: 100%; 15 | height: 192px; 16 | text-align: center; 17 | } 18 | 19 | .coinflip-banner img { 20 | height: 192px; 21 | } 22 | 23 | .coinflip-banner-b { 24 | position: absolute; 25 | z-index: 500; 26 | } 27 | 28 | .coinflip-banner-b a { 29 | display: block; 30 | width: 100%; 31 | height: 75px; 32 | text-align: center; 33 | } 34 | 35 | .coinflip-banner-b img { 36 | height: 192px; 37 | } 38 | 39 | .jackpot-banner-b img { 40 | height: 75px; 41 | } 42 | 43 | #chatStatus { 44 | padding: 10px 15px; 45 | color: #fff; 46 | } 47 | 48 | #chatStatus:after { 49 | clear: both; 50 | display: block; 51 | content: ""; 52 | } 53 | 54 | #chatStatus .close { 55 | opacity: 0.8; 56 | color: #fff; 57 | } 58 | 59 | #chatStatus .close:hover { 60 | opacity: 1.0; 61 | color: #fff; 62 | } 63 | 64 | #chatStatus.online { 65 | border-bottom: 1px solid #2196f3; 66 | } 67 | 68 | #chatStatus.offline { 69 | border-bottom: 1px solid #f44336; 70 | } 71 | 72 | #chatMessages { 73 | position: absolute; 74 | top: 50px; 75 | left: 0; 76 | right: 0; 77 | bottom: 50px; 78 | font-size: 0.9em; 79 | padding: 10px 15px 0 15px; 80 | overflow: auto; 81 | } 82 | 83 | #chatMessages::-webkit-scrollbar-track { 84 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 85 | background-color: #111; 86 | } 87 | 88 | #chatMessages::-webkit-scrollbar { 89 | width: 6px; 90 | height: 6px; 91 | background-color: #111; 92 | } 93 | 94 | #chatMessages::-webkit-scrollbar-thumb { 95 | background-color: #555555; 96 | } 97 | 98 | .chat-message { 99 | padding: 5px 0; 100 | } 101 | 102 | .chat-message .chat-user { 103 | font-size: 13px; 104 | color: #eee; 105 | margin-bottom: 5px; 106 | } 107 | 108 | .chat-message .chat-user img { 109 | margin-right: 5px; 110 | width: 20px; 111 | height: 20px; 112 | } 113 | 114 | .chat-message .chat-content { 115 | padding: 3px; 116 | display: inline; 117 | color: #aaa; 118 | } 119 | 120 | .emot { 121 | margin-right: 2px; 122 | cursor: pointer; 123 | } 124 | 125 | #chatActions { 126 | position: absolute; 127 | left: 0; 128 | right: 0; 129 | bottom: 0px; 130 | padding: 10px; 131 | } 132 | 133 | #chatInput { 134 | color: #aaa; 135 | width: 55%; 136 | float: left; 137 | background-color: #252525; 138 | border-color: #444; 139 | } 140 | 141 | #chatInput:focus { 142 | box-shadow: none; 143 | } 144 | 145 | #chatEmotsBtn { 146 | width: 15%; 147 | float: right; 148 | } 149 | 150 | #chatSendBtn { 151 | width: 30%; 152 | float: right; 153 | } -------------------------------------------------------------------------------- /views/templates/jackpot-handlebars.ejs: -------------------------------------------------------------------------------- 1 | 4 | 7 | 19 | 32 | 37 | 45 | -------------------------------------------------------------------------------- /routes/coinflip.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | config = require('../config'), 3 | models = require('../models'); 4 | 5 | const Db = models.Db, 6 | Log = models.Log, 7 | Helper = models.Helper, 8 | Passport = models.Passport; 9 | 10 | const router = express.Router(); 11 | 12 | /* GET home page. */ 13 | router.get('/', function (req, res, next) { 14 | res.locals.page.title = "Coinflip"; 15 | res.render('coinflip', {}); 16 | }); 17 | 18 | router.get('/history', Passport.isAuthenticated, async (req, res, next) => { 19 | //Rounds 20 | let rounds = await Db.CoinflipRound.findAll({ 21 | where: { 22 | [Db.Op.or]: [ 23 | Db.sequelize.fn('JSON_CONTAINS', Db.sequelize.col('creator'), '{"steam_id" : "' + res.locals.user.steamid + '"}', '$.user'), 24 | Db.sequelize.fn('JSON_CONTAINS', Db.sequelize.col('joiner'), '{"steam_id" : "' + res.locals.user.steamid + '"}', '$.user') 25 | ], 26 | status: 3 27 | }, 28 | attributes: [ 29 | "roundID", 30 | "status", 31 | "winnerID", 32 | "updatedAt", 33 | "creator", 34 | "joiner", 35 | [Db.sequelize.fn('JSON_VALUE', Db.sequelize.col('hashes'), '$.pin'), 'pin'], 36 | [Db.sequelize.fn('JSON_VALUE', Db.sequelize.col('creator'), '$.value'), 'cValue'] 37 | ], 38 | order: [['updatedAt', 'DESC']] 39 | }); 40 | res.locals.rounds = []; 41 | 42 | for (let round of rounds) { 43 | round.joiner = JSON.parse(round.joiner); 44 | res.locals.rounds.push({ 45 | roundID: round.roundID, 46 | status: Helper.coinflipRoundStatus(round.status), 47 | pin: (round.winnerID === res.locals.user.steamid ? round.dataValues.pin : "-"), 48 | won: (round.winnerID === res.locals.user.steamid), 49 | updatedAt: round.updatedAt, 50 | totalValue: Helper.toUSD(parseInt(round.dataValues.cValue) + (round.joiner.value ? parseInt(round.joiner.value) : 0)) 51 | }); 52 | } 53 | res.locals.page.title = "Coinflip History"; 54 | res.render('coinflip-history'); 55 | }); 56 | 57 | router.get('/details/:rid([a-f0-9]{24})', Passport.isAuthenticated, async (req, res, next) => { 58 | //Rounds 59 | let round = await await Db.CoinflipRound.findOne({ 60 | where: { 61 | roundID: req.params.rid, 62 | status: 3 63 | } 64 | }); 65 | 66 | if (!round) 67 | next(); 68 | 69 | // Parsed from database 70 | round.hashes = JSON.parse(round.hashes); 71 | round.creator = JSON.parse(round.creator); 72 | round.joiner = JSON.parse(round.joiner); 73 | 74 | round.totalValue = Helper.toUSD(parseInt(round.creator.value) + parseInt(round.joiner.value)); 75 | 76 | round.winner = (round.creator.user.steam_id === round.winnerID ? round.creator.user.name : round.joiner.user.name) 77 | 78 | res.locals.round = round; 79 | res.locals.page.title = "Coinflip Round Detail"; 80 | res.render('coinflip-details'); 81 | }); 82 | 83 | module.exports = router; 84 | -------------------------------------------------------------------------------- /views/coinflip.ejs: -------------------------------------------------------------------------------- 1 | <%- contentFor('styles') %> 2 | 3 |
4 |
5 | 6 | Chat 7 |
8 |
Connecting please wait.
9 | 66 |
67 | <% include chat %> 68 | <%- contentFor('scripts') %> 69 | <% include templates/coinflip-handlebars %> 70 | 71 | <% include templates/games-scripts %> 72 | -------------------------------------------------------------------------------- /views/faq.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

FAQ

6 |

What items can I bet ?

7 |

You can only bet CS:GO items. If there is any other items in the trade, such as trading cards, gifts etc... it will be automaticaly declined.

8 |

How is the winner selected ?

9 |

A ticket is equal to 0.01 in bet value (1 ticket = 0.01 of the skin you submitted EX: skin valued at 5.00 = 500 tickets). The more tickets a player has, the more 10 | chances of winning he gets. 11 | As soon as a round is over, the winning ticket number will be calculated based on the random percentage and the total number of tickets with the following 12 | formula:

13 | Math.floor((TotalNumberOfTickets - 0.0000000001) * (winnerPercentage / 100)) 14 |

Keep in mind that the order of the tickets are the same as the order of the deposit. For instance, if the first player places 50.00 in 15 | bet 16 | value into the pot, he will own tickets 1-5000, 17 | then if the second player places 20.00 in bet value, he will own tickets from 5001 to 7001. This will keep going until the pot is full at which point the round 18 | is 19 | set to end. Also note that the bots may accept a few late bets if there is any.

20 |

What is the round hash ?

21 |

The hash is a md5 encryption of the round secret and winning percentage in the following format 22 | percentage:secret 23 |

24 |

When the round is over, both the secret and the winning percentage are revealed. You can verify that the hash matches with them with the form below.

25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 | 36 | 37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 | 51 | <%- contentFor('scripts') %> 52 | 53 | -------------------------------------------------------------------------------- /views/jackpot-history.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 8 |
9 |

Total bet: <%= user.totalBet %>

10 |

Total won: <%= user.totalWon %>

11 |

Profit: <%= Number(user.totalWon - user.totalBet.toFixed(2)) %>

12 |
13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | <% rounds.forEach(round => { %> 27 | 28 | 35 | 36 | 37 | 44 | 45 | 46 | <% }); %> 47 | 48 |
Round #StatusPinWin/LosePot
29 | <% if(round.status === "Over") { %> 30 | <%= round.roundID; %> 31 | <% } else { %> 32 | <%= round.roundID; %> 33 | <% } %> 34 | <%= round.status; %><%= round.pin; %> 38 | <% if(round.status === "Over") { %> 39 | <%= (round.won ? "Win" : "Lose") %> 40 | <% } else { %> 41 | ? 42 | <% } %> 43 | <%= round.pot; %>
49 |
50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | <% offers.forEach(offer => { %> 62 | 63 | 70 | 71 | 72 | 73 | 74 | <% }); %> 75 | 76 |
TradeIDStatusMessageValue
64 | <% if(offer.status === "Active") { %> 65 | <%= offer.offerID; %> 66 | <% } else { %> 67 | <%= offer.offerID; %> 68 | <% } %> 69 | <%= offer.status; %><%= offer.message; %><%= offer.value; %>
77 |
78 |
79 |
80 |
-------------------------------------------------------------------------------- /express.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | expressLayouts = require('express-ejs-layouts'), 3 | path = require('path'), 4 | favicon = require('serve-favicon'), 5 | logger = require('morgan'), 6 | cookieParser = require('cookie-parser'), 7 | bodyParser = require('body-parser'), 8 | session = require('express-session'), 9 | csrf = require('csurf'), 10 | SessionStore = require('express-sequelize-session')(session.Store), 11 | app = express(), 12 | passport = require('./models/passport'), 13 | config = require('./config'), 14 | models = require('./models'); 15 | 16 | const Db = models.Db, 17 | Log = models.Log; 18 | 19 | Db.sequelize.sync(); 20 | 21 | const sessionStore = new SessionStore(Db.sequelize); 22 | 23 | const auth = require('./routes/auth')(passport), 24 | jackpot = require('./routes/jackpot'), 25 | coinflip = require('./routes/coinflip'), 26 | giveaway = require('./routes/giveaway'), 27 | leaderboard = require('./routes/leaderboard'), 28 | getStarted = require('./routes/getStarted'), 29 | faq = require('./routes/faq'), 30 | coinflipFaq = require('./routes/coinflip-faq'), 31 | terms = require('./routes/terms'), 32 | editProfile = require('./routes/edit-profile'), 33 | api = require('./routes/api'); 34 | 35 | // APP - VIEW ENGINE SETUP 36 | app.set('views', path.join(__dirname, 'views')); 37 | app.set('view engine', 'ejs'); 38 | app.use(expressLayouts); 39 | app.set('layout', 'layouts/main'); 40 | 41 | // APP - SETUP 42 | // uncomment after placing your favicon in /public 43 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 44 | app.use(logger('dev')); 45 | app.use(bodyParser.json()); 46 | app.use(bodyParser.urlencoded({extended: false})); 47 | app.use(cookieParser()); 48 | app.use(express.static(path.join(__dirname, 'public'))); 49 | 50 | //APP - SESSION 51 | app.use(session({ 52 | name: 'sid', 53 | secret: config.servers.cookie.secret, 54 | cookie: { 55 | domain: config.servers.cookie.domain 56 | }, 57 | store: sessionStore, 58 | resave: false, 59 | saveUninitialized: true 60 | })); 61 | 62 | //APP - CSRF 63 | app.use(csrf()); 64 | app.use(function (req, res, next) { 65 | res.locals._csrf = req.csrfToken(); 66 | next(); 67 | }); 68 | 69 | //APP - PASSPORT 70 | app.use(passport.initialize()); 71 | app.use(passport.session()); 72 | 73 | //APP - DEFAULT TEMPLATE VARIABLES 74 | app.use(function (req, res, next) { 75 | res.locals.user = req.user; 76 | res.locals.config = config; 77 | res.locals.language = req.headers["accept-language"]; 78 | res.locals.page = { 79 | title: config.website.defaultTitle 80 | }; 81 | next(); 82 | }); 83 | 84 | //APP - ROUTES 85 | app.use('/', jackpot); 86 | app.use('/auth', auth); 87 | app.use('/coinflip', coinflip); 88 | app.use('/giveaway', giveaway); 89 | app.use('/leaderboard', leaderboard); 90 | app.use('/get-started', getStarted); 91 | app.use('/faq', faq); 92 | app.use('/coinflip-faq', coinflipFaq); 93 | app.use('/terms', terms); 94 | 95 | app.use('/edit-profile', editProfile); 96 | 97 | app.use('/api', api); 98 | 99 | 100 | app.io = require('./servers/status'); 101 | 102 | //APP - CATCH 404 103 | app.use(function (req, res, next) { 104 | res.locals.error = { 105 | title: "Page not found", 106 | code: 404, 107 | message: "The page you are trying to reach does not exists." 108 | }; 109 | res.status(404); 110 | res.render('error', { 111 | layout: 'layouts/error' 112 | }); 113 | }); 114 | 115 | //APP - CATCH OTHER ERRORS 116 | app.use(function (err, req, res, next) { 117 | res.locals.error = { 118 | title: "Error " + (err.status || 500), 119 | code: err.status, 120 | message: req.app.get('env') === 'development' ? err : "" 121 | }; 122 | Log.error(err); 123 | res.locals.config = config; 124 | res.status(err.status || 500); 125 | res.render('error', { 126 | layout: 'layouts/error' 127 | }); 128 | }); 129 | 130 | module.exports = app; 131 | -------------------------------------------------------------------------------- /views/jackpot-details.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Round data

5 |
    6 |
  • 7 | Round ID: <%= round.roundID %>
  • 8 |
  • 9 | Start date: <%- new Date(round.createdAt).toISOString() %>
  • 10 |
  • 11 | End date: <%- new Date(round.endedAt).toISOString() %>
  • 12 |
13 |

Players

14 | 15 | 16 | <% for(let k in round.players) { %> 17 | 18 | 22 | 23 | 24 | <% } %> 25 | 26 |
19 | 20 | <%= round.players[k].name %> 21 | <%= round.players[k].probability.percent %>% / $<%= round.players[k].probability.value %>
27 |
28 |
29 |
30 |
31 |
32 | won $<%= round.winnerValue %> with <%= round.players[round.winnerSteamID].probability.percent %>% chance 37 | 38 |
39 |
Round secret: <%= round.hashes.secret %> - percent: <%= round.hashes.percentage %> 40 | what is this? 41 |
42 |
43 | <% for(let bet of round.bets){ %> 44 |
45 | 48 | placed <%= bet.nbrItems %> items valued at $<%= bet.valuedAt %> 49 |
50 | <% } %> 51 |
New round started with hash <%= round.hashes.hash %>
52 |
53 |
54 |
55 |
56 |
57 |

Items you placed

58 | <% for(let item of round.itemsPlaced){ %> 59 |
60 |
61 | 62 |
<%= item.name %>
63 |
<%= item.price %>
64 |
65 |
66 | <% }; %> 67 |
68 |
69 |
70 |
71 | <%- contentFor('scripts') %> 72 | 75 | -------------------------------------------------------------------------------- /servers/chat.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | cookieParser = require('cookie-parser'), 3 | session = require('express-session'), 4 | socketio = require('socket.io'), 5 | passportSocketio = require('passport.socketio'), 6 | SessionStore = require('express-sequelize-session')(session.Store), 7 | app = express(), 8 | passport = require('../models/passport'), 9 | config = require('../config'), 10 | models = require('../models'); 11 | 12 | const Db = models.Db, 13 | Log = models.Log, 14 | Helper = models.Helper; 15 | 16 | const sessionStore = new SessionStore(Db.sequelize); 17 | 18 | app.use(session({ 19 | name: 'sid', 20 | secret: config.servers.cookie.secret, 21 | cookie: { 22 | domain: config.servers.cookie.domain 23 | }, 24 | store: sessionStore, 25 | resave: false, 26 | saveUninitialized: true 27 | })); 28 | 29 | app.use(passport.initialize()); 30 | app.use(passport.session()); 31 | 32 | const server = require('http').createServer(app), 33 | io = socketio(server); 34 | 35 | io.use(passportSocketio.authorize({ 36 | key: 'sid', 37 | secret: config.servers.cookie.secret, 38 | store: sessionStore, 39 | passport: passport, 40 | cookieParser: cookieParser, 41 | success: (data, accept) => { 42 | accept() 43 | }, 44 | fail: (data, message, error, accept) => { 45 | accept() 46 | } 47 | })); 48 | 49 | const maxArchivedMessages = 25; 50 | 51 | let users = {}, 52 | messages = [], 53 | online = 0; 54 | 55 | let Chat = {}; 56 | 57 | Chat.PushMessageHistory = (message) => { 58 | messages.push(message); 59 | let len = messages.length; 60 | if (len > maxArchivedMessages) { 61 | messages = messages.slice(len - maxArchivedMessages, len); 62 | } 63 | }; 64 | 65 | Chat.OnMessage = (user, msg, socket) => { 66 | if (!user) { 67 | socket.emit("chatError", { 68 | "message": "You need to be logged in to send message." 69 | }); 70 | return; 71 | } 72 | let message = { 73 | "from": { 74 | "steamID": user.steamid, 75 | "displayName": user.name, 76 | "avatar": user.avatar + ".jpg", 77 | "profileUrl": "http://steamcommunity.com/profiles/" + user.steamid 78 | }, 79 | "message": msg.message, 80 | "msg_date": new Date().toISOString(), 81 | "privilege": null, 82 | }; 83 | Chat.PushMessageHistory(message); 84 | io.sockets.emit("chatMsg", message) 85 | }; 86 | 87 | Chat.UpdateOnline = () => { 88 | online = Object.keys(io.sockets.connected).length; 89 | }; 90 | Chat.EmitHistory = (socket) => { 91 | socket.emit('connected', messages); 92 | }; 93 | 94 | Chat.BroadcastOnline = () => { 95 | io.sockets.emit('userCount', online); 96 | }; 97 | 98 | io.on("connection", function (socket) { 99 | Log.debug('Client connected'); 100 | Chat.UpdateOnline(); 101 | Chat.EmitHistory(socket); 102 | 103 | let req = socket.request.user; 104 | let user = false; 105 | 106 | if (req.logged_in) { 107 | user = socket.request.user.dataValues; 108 | if (!users.hasOwnProperty(user.steamid)) 109 | users[user.steamid] = { 110 | sockets: [socket.id], 111 | name: user.name, 112 | rank: user.rank 113 | }; 114 | else 115 | users[user.steamid].sockets.push(socket.id); 116 | } 117 | 118 | socket.on('sendMessage', msg => { 119 | Chat.OnMessage(user, msg, socket) 120 | }); 121 | 122 | socket.on('disconnect', function () { 123 | Chat.UpdateOnline(); 124 | 125 | if (user && users[user.steamid].sockets.length > 1) 126 | Helper.remove(users[user.steamid].sockets, socket.id); 127 | else 128 | delete users[user.steamid]; 129 | 130 | Log.debug('Client disconnected'); 131 | }); 132 | }); 133 | 134 | setInterval(() => { 135 | Chat.BroadcastOnline(); 136 | }, 10000); 137 | 138 | 139 | server.listen(config.servers.ports.chat, () => { 140 | Log.info("HTTP & Socket.IO servers listening on port " + config.servers.ports.chat); 141 | }); -------------------------------------------------------------------------------- /config/index.example.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | servers: { 3 | ports: { 4 | website: 3000, 5 | chat: 3001, 6 | jackpot: 3002, 7 | coinflip: 3003 8 | }, 9 | cookie: { 10 | domain: ".domain.com", 11 | secret: "GeezyoSucks" 12 | } 13 | }, 14 | botServers: { 15 | jackpot: 3004, 16 | coinflip: 3005 17 | }, 18 | database: { 19 | database: "", 20 | username: "", 21 | password: "", 22 | options: { 23 | host: 'localhost', 24 | dialect: 'mysql', 25 | pool: { 26 | max: 5, 27 | min: 0, 28 | idle: 10000 29 | }, 30 | logging: false 31 | } 32 | }, 33 | logging: { 34 | debug: true, 35 | file: "logs/main.log" 36 | }, 37 | website: { 38 | name: "CSGOHunt.com", 39 | defaultTitle: "CS:GO Jackpot / Coinflip", 40 | url: "https://hunt.domain.com", 41 | support: "http://support.csgohunt.com", 42 | facebook: "https://www.facebook.com/CZBaterka", 43 | twitter: "https://twitter.com/CZBaterka", 44 | meta: { 45 | description: "CSGO Jackpot and Coinflip : Build your dream inventory by winning hundreds of items on CSGOHunt.com and enjoy the thrill of playing Counter-Strike Global Offensive skins with other players all around the world!", 46 | keywords: "cs:go jackpot, cs:go skins jackpot, cs:go best jackpot, csgojackpot, cs:go skin, win cs:go skins, cs:go skins, cs:go coinflip, cs:go coin flip" 47 | } 48 | }, 49 | steam: { 50 | apiKey: "", 51 | avatarStore: "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/", 52 | botsAdmins: [ 53 | ], 54 | bots: { 55 | 0: { 56 | serverID: "jackpot", 57 | cancelOfferAfter: 10, 58 | tradelink: "", 59 | account: { 60 | username: "", 61 | steamID: "", 62 | name: "", 63 | password: "", 64 | sharedSecret: "", 65 | identitySecret: "" 66 | } 67 | }, 68 | 1: { 69 | serverID: "coinflip", 70 | cancelOfferAfter: 2, 71 | tradelink: "", 72 | account: { 73 | username: "", 74 | steamID: "", 75 | name: "", 76 | password: "", 77 | sharedSecret: "", 78 | identitySecret: "" 79 | } 80 | } 81 | }, 82 | inventoryFetchLimiter: 1, //Seconds 83 | }, 84 | global: { 85 | siteAppID: 730, 86 | xpPerLevel: 100, 87 | jackpot: { 88 | earlyTimer: 10, 89 | earlyStartBets: 1, 90 | finalTimer: 3, 91 | potItemsLimit: 10, 92 | fee: 25, //% 93 | alertBar: [ 94 | { 95 | type: "giveaway", 96 | html: "🎁 test.com - Butterfly Knife Fade Giveaway 🎁" 97 | + "Don't click here" 98 | } 99 | ] 100 | }, 101 | coinflip: { 102 | fee: 10, //% 103 | } 104 | }, 105 | client: { 106 | sockets: { 107 | status: "//domain.com", 108 | chat: "//chat.domain.com", 109 | jackpot: "//jackpot.domain.com", 110 | coinflip: "//coinflip.domain.com" 111 | } 112 | }, 113 | prices: { 114 | file: "./prices.json" 115 | }, 116 | offers: { 117 | jackpot: { 118 | maxItems: 5, 119 | minItemPrice: 1, //¢ (cents) 120 | minTotalPrice: 1, //¢ (cents) 121 | maxResendAttempts: 10, 122 | forbiddenItems: [] 123 | }, 124 | coinflip: { 125 | maxItems: 5, 126 | minItemPrice: 1, //¢ (cents) 127 | minTotalPrice: 1, //¢ (cents) 128 | maxResendAttempts: 10, 129 | forbiddenItems: [] 130 | } 131 | } 132 | }; -------------------------------------------------------------------------------- /views/coinflip-details.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Details for coinflip #5ab6ce20eb643700141595f1

6 |
    7 |
  • ID 8 | <%= round.roundID %> 9 |
  • 10 |
  • Ended on 11 | <%= round.updatedAt %> 12 |
  • 13 |
  • Server seed 14 | <%= round.hashes.seed %> 15 |
  • 16 |
  • Server secret 17 | <%= round.hashes.secret %> 18 |
  • 19 |
  • Server hash 20 | <%= round.hashes.hash %> 21 |
  • 22 |
  • Player 1 seed 23 | <%= round.hashes.c_seed %> 24 |
  • 25 |
  • Player 2 seed 26 | <%= round.hashes.j_seed %> 27 |
  • 28 |
  • Roll 29 | <%= round.hashes.percent %> 30 |
  • 31 |
  • Winner 32 | <%= round.winner %> 33 |
  • 34 |
  • Total value 35 | <%= round.totalValue %> 36 |
  • 37 |
38 |
39 |
40 |
41 |
42 | 43 |

44 | <%= round.creator.user.name %> 46 |

47 |
    48 | <% for(let item of round.creator.items){ %> 49 |
  • 50 |
    51 |
    52 | 53 |
    <%= item.name %>
    54 |
    <%= parseFloat(item.price / 100).toFixed(2) %>
    55 |
    56 |
    57 |
  • 58 | <% } %> 59 |
60 |
61 |
62 | -side.png" width="64"> 63 |

64 | <%= round.joiner.user.name %> 65 |

66 |
    67 | <% for(let item of round.joiner.items){ %> 68 |
  • 69 |
    70 |
    71 | 72 |
    <%= item.name %>
    73 |
    <%= parseFloat(item.price / 100).toFixed(2) %>
    74 |
    75 |
    76 |
  • 77 | <% } %> 78 |
79 |
80 |
81 | 88 |
89 |
90 | <%- contentFor('scripts') %> 91 | 94 | -------------------------------------------------------------------------------- /models/db/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'), 2 | path = require('path'), 3 | Sequelize = require('sequelize'), 4 | Op = Sequelize.Op, 5 | basename = path.basename(module.filename), 6 | config = require('../../config').database; 7 | 8 | const db = {}; 9 | 10 | const sequelize = new Sequelize( 11 | config.database, 12 | config.username, 13 | config.password, 14 | Object.assign(config.options, { 15 | operatorsAliases: Op 16 | }) 17 | ); 18 | fs 19 | .readdirSync(__dirname) 20 | .filter((file) => 21 | (file.indexOf('.') !== 0) && 22 | (file !== basename) && 23 | (file.slice(-3) === '.js')) 24 | .forEach((file) => { 25 | const model = sequelize.import(path.join(__dirname, file)); 26 | db[model.name] = model; 27 | }); 28 | 29 | Object.keys(db).forEach((modelName) => { 30 | if (db[modelName].associate) { 31 | db[modelName].associate(db); 32 | } 33 | }); 34 | 35 | db.sequelize = sequelize; 36 | db.Sequelize = Sequelize; 37 | db.Op = Op; 38 | 39 | db.action = { 40 | User: { 41 | update: async (data, where, createIfNotExists = true) => { 42 | try { 43 | const row = await db.User.findOne({where: where}); 44 | if (row) { 45 | await row.update(data); 46 | } else if (createIfNotExists) { 47 | await db.User.create(data); 48 | } else 49 | throw "User does not exist"; 50 | } catch (err) { 51 | throw err; 52 | } 53 | }, 54 | get: async (where) => { 55 | try { 56 | const data = await db.User.findOne({where: where}); 57 | return data; 58 | } catch (err) { 59 | throw err; 60 | } 61 | }, 62 | isAdmin: async (where) => { 63 | try { 64 | const data = await db.User.findOne({where: where, attributes: ['rank']}); 65 | return (parseInt(data.rank) === 10) 66 | } catch (err) { 67 | throw err; 68 | } 69 | } 70 | }, 71 | Round: { 72 | insert: async (data) => { 73 | try { 74 | await db.Round.create(data); 75 | } catch (err) { 76 | throw err; 77 | } 78 | }, 79 | get: async (where) => { 80 | try { 81 | const data = await db.Round.findOne({where: where}); 82 | return data; 83 | } catch (err) { 84 | throw err; 85 | } 86 | } 87 | }, 88 | Offer: { 89 | insert: async (data) => { 90 | try { 91 | await db.Offer.create(data); 92 | } catch (err) { 93 | throw err; 94 | } 95 | }, 96 | duplicate: async (id) => { 97 | try { 98 | let data = await db.Offer.findOne({ 99 | where: {offerID: id}, 100 | attributes: ['status'] 101 | }); 102 | if (!data) 103 | return false; 104 | return data.dataValues.status !== 0; 105 | } catch (err) { 106 | return true; 107 | } 108 | }, 109 | get: async (where) => { 110 | try { 111 | return await db.Offer.findOne({where: where}); 112 | } catch (err) { 113 | throw err; 114 | } 115 | }, 116 | update: async (data, where) => { 117 | try { 118 | await db.Offer.update(data, {where: where}); 119 | } catch (err) { 120 | throw err; 121 | } 122 | }, 123 | delete: async (where) => { 124 | try { 125 | await db.Offer.destroy({where: where}); 126 | } catch (err) { 127 | throw err; 128 | } 129 | }, 130 | pop: async (botID) => { 131 | try { 132 | const data = await db.Offer.findOne({ 133 | where: { 134 | desiredState: { 135 | [Op.ne]: 0, 136 | }, 137 | botID: botID 138 | } 139 | }); 140 | if (data) 141 | await data.update({desiredState: 0}); 142 | return data; 143 | } catch (err) { 144 | throw err; 145 | } 146 | } 147 | } 148 | }; 149 | 150 | module.exports = db; -------------------------------------------------------------------------------- /public/css/ring.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes uil-ring-anim { 2 | 0% { 3 | -ms-transform: rotate(0deg); 4 | -moz-transform: rotate(0deg); 5 | -webkit-transform: rotate(0deg); 6 | -o-transform: rotate(0deg); 7 | transform: rotate(0deg); 8 | } 9 | 100% { 10 | -ms-transform: rotate(360deg); 11 | -moz-transform: rotate(360deg); 12 | -webkit-transform: rotate(360deg); 13 | -o-transform: rotate(360deg); 14 | transform: rotate(360deg); 15 | } 16 | } 17 | 18 | @-webkit-keyframes uil-ring-anim { 19 | 0% { 20 | -ms-transform: rotate(0deg); 21 | -moz-transform: rotate(0deg); 22 | -webkit-transform: rotate(0deg); 23 | -o-transform: rotate(0deg); 24 | transform: rotate(0deg); 25 | } 26 | 100% { 27 | -ms-transform: rotate(360deg); 28 | -moz-transform: rotate(360deg); 29 | -webkit-transform: rotate(360deg); 30 | -o-transform: rotate(360deg); 31 | transform: rotate(360deg); 32 | } 33 | } 34 | 35 | @-moz-keyframes uil-ring-anim { 36 | 0% { 37 | -ms-transform: rotate(0deg); 38 | -moz-transform: rotate(0deg); 39 | -webkit-transform: rotate(0deg); 40 | -o-transform: rotate(0deg); 41 | transform: rotate(0deg); 42 | } 43 | 100% { 44 | -ms-transform: rotate(360deg); 45 | -moz-transform: rotate(360deg); 46 | -webkit-transform: rotate(360deg); 47 | -o-transform: rotate(360deg); 48 | transform: rotate(360deg); 49 | } 50 | } 51 | 52 | @-ms-keyframes uil-ring-anim { 53 | 0% { 54 | -ms-transform: rotate(0deg); 55 | -moz-transform: rotate(0deg); 56 | -webkit-transform: rotate(0deg); 57 | -o-transform: rotate(0deg); 58 | transform: rotate(0deg); 59 | } 60 | 100% { 61 | -ms-transform: rotate(360deg); 62 | -moz-transform: rotate(360deg); 63 | -webkit-transform: rotate(360deg); 64 | -o-transform: rotate(360deg); 65 | transform: rotate(360deg); 66 | } 67 | } 68 | 69 | @-moz-keyframes uil-ring-anim { 70 | 0% { 71 | -ms-transform: rotate(0deg); 72 | -moz-transform: rotate(0deg); 73 | -webkit-transform: rotate(0deg); 74 | -o-transform: rotate(0deg); 75 | transform: rotate(0deg); 76 | } 77 | 100% { 78 | -ms-transform: rotate(360deg); 79 | -moz-transform: rotate(360deg); 80 | -webkit-transform: rotate(360deg); 81 | -o-transform: rotate(360deg); 82 | transform: rotate(360deg); 83 | } 84 | } 85 | 86 | @-webkit-keyframes uil-ring-anim { 87 | 0% { 88 | -ms-transform: rotate(0deg); 89 | -moz-transform: rotate(0deg); 90 | -webkit-transform: rotate(0deg); 91 | -o-transform: rotate(0deg); 92 | transform: rotate(0deg); 93 | } 94 | 100% { 95 | -ms-transform: rotate(360deg); 96 | -moz-transform: rotate(360deg); 97 | -webkit-transform: rotate(360deg); 98 | -o-transform: rotate(360deg); 99 | transform: rotate(360deg); 100 | } 101 | } 102 | 103 | @-o-keyframes uil-ring-anim { 104 | 0% { 105 | -ms-transform: rotate(0deg); 106 | -moz-transform: rotate(0deg); 107 | -webkit-transform: rotate(0deg); 108 | -o-transform: rotate(0deg); 109 | transform: rotate(0deg); 110 | } 111 | 100% { 112 | -ms-transform: rotate(360deg); 113 | -moz-transform: rotate(360deg); 114 | -webkit-transform: rotate(360deg); 115 | -o-transform: rotate(360deg); 116 | transform: rotate(360deg); 117 | } 118 | } 119 | 120 | @keyframes uil-ring-anim { 121 | 0% { 122 | -ms-transform: rotate(0deg); 123 | -moz-transform: rotate(0deg); 124 | -webkit-transform: rotate(0deg); 125 | -o-transform: rotate(0deg); 126 | transform: rotate(0deg); 127 | } 128 | 100% { 129 | -ms-transform: rotate(360deg); 130 | -moz-transform: rotate(360deg); 131 | -webkit-transform: rotate(360deg); 132 | -o-transform: rotate(360deg); 133 | transform: rotate(360deg); 134 | } 135 | } 136 | 137 | .uil-ring-css { 138 | background: none; 139 | position: relative; 140 | width: 200px; 141 | height: 200px; 142 | } 143 | 144 | .uil-ring-css > div { 145 | position: absolute; 146 | display: block; 147 | width: 160px; 148 | height: 160px; 149 | top: 20px; 150 | left: 20px; 151 | border-radius: 80px; 152 | box-shadow: 0 6px 0 0 #31708f; 153 | -ms-animation: uil-ring-anim 1s linear infinite; 154 | -moz-animation: uil-ring-anim 1s linear infinite; 155 | -webkit-animation: uil-ring-anim 1s linear infinite; 156 | -o-animation: uil-ring-anim 1s linear infinite; 157 | animation: uil-ring-anim 1s linear infinite; 158 | } -------------------------------------------------------------------------------- /views/terms.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Tems and conditions of use

6 |

Legal Notice

7 |

The terms and conditions set out below (the “Terms and Conditions”) apply to and govern any services used by you (“you”, the “user”) and marketed by us under the 8 | brand name ‘CSGOHunt including any services provided through any website with a domain name ending ‘CSGOHunt.com’ (the “Website”), and to any email and other 9 | correspondence between us relating to such a service.

10 |

CSGOHunt is for entertainment purposes only, no real money is exchanged on our platform. 11 | Therefore players participate in bets with virtual items of artificial value in a closed gaming system. CSGOHunt does not offer an opportunity to win real 12 | money.

13 |

You must be at least 18 years old to play on csgohunt.com.

14 |

Usage of our site, CSGOHunt.com and its services constitute your acceptance of this agreement.

15 |

If you do not agree to any of these stipulations please leave the site immediately.

16 |

By participating in CSGOHunt.com you agree that you are not a resident of the United Kingdom.

17 |

Limitations of Liability

18 |

Use of our services is at your own risk. CSGOHunt.com, its owners and affiliates should / will never be held liable for any individual’s profits / loss gained on 19 | CSGOHunt.com. CSGOHunt.com and it’s affiliates assume no responsibility for any missed bets or loss of virtual currency / items.

20 |

Virtual Currency

21 |

We give you the opportunity to exchange intangible items for CSGOHunt's virtual currency, coins. This virtual currency is not real money, nor does it hold any 22 | monetary value and may never be redeemed for “real world” money, or other items of monetary value / inherent value. You acknowledge that CSGOHunt's virtual 23 | currency is not redeemable for any sum of “real-world” money from us at any time. We make no guarantee and take no responsibility as to the nature, value, or 24 | quality of the features of the Service or any third-party goods or services that will be accessible through the use of CSGOHunt's virtual currency.

25 |

Bet participation

26 |

CSGOHunt.com is intended for an adult audience and you must be 18 or older, of sound mind and capable of taking responsibility for your own actions. 27 | CSGOHunt.com comes with no guarantees, expressed or implied, in connection with the service which is provided to you ‘as is’ and we provide you with no warranty 28 | whatsoever regarding its quality, completeness or accuracy. As such, CSGOHunt.com cannot be held responsible in any event, direct, indirect or consequential 29 | with the use of the website. 30 | CSGOHunt.com reserves the right to suspend and/or cancel any bet/wager at any time. When a platform is suspended, any bets entered will be on hold. CSGOHunt.com 31 | also reserves the right to cease betting at any time without notice.

32 |

Deposit, withdraw or lost items

33 |

If any loss occur during a bet caused by a software or network issue, you have 7 days to make a claim by opening a ticket at support.csgohunt.com, after which 34 | these items will be considered surrendered. We strongly encourage you to withdraw your winning as soon as possible to avoid any issues. 35 | Keep in mind that the items you get after winning a round may not be the same as the one you deposit, meaning that stickers and nametag may be lost. However the 36 | total value won will stay the same. For every round played on CSGOHunt.com, a commission of approximatly 5% will be taken. It is calculated on the total pot 37 | value and is taken from the items pool.

38 |

Content

39 |

The content of the pages of this website is for your general information and use only. It is subject to change without notice. This website contains material 40 | which is owned by us. This material includes, but is not limited to, the design, layout, look, appearance and graphics. Reproduction is prohibited other than in 41 | accordance with the copyright notice, which forms part of these terms and conditions.

42 |

Affiliation

43 |

CSGOHunt.com is in NO way affiliated with Valve Corporation, Counter-Strike Global Offensive or any other trademarks of the Valve Corporation.

44 |
45 |
46 |
47 |
-------------------------------------------------------------------------------- /routes/jackpot.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | config = require('../config'), 3 | models = require('../models'); 4 | 5 | const Db = models.Db, 6 | Log = models.Log, 7 | Helper = models.Helper, 8 | Passport = models.Passport; 9 | 10 | const router = express.Router(); 11 | 12 | router.get('/', function (req, res) { 13 | res.locals.page.title = "CS:GO Jackpot site"; 14 | res.render('jackpot'); 15 | }); 16 | 17 | router.get('/jackpot/deposit', Passport.isAuthenticated, function (req, res) { 18 | let bots = config.steam.bots; 19 | let random = Helper.Generate.randomNum(0, Object.keys(bots).length - 1); 20 | res.redirect(bots[random].tradelink); 21 | }); 22 | 23 | router.get('/jackpot/history', Passport.isAuthenticated, async (req, res) => { 24 | //Offers 25 | let offers = await await Db.Offer.findAll({ 26 | where: { 27 | partner: res.locals.user.steamid, 28 | currentState: { 29 | [Db.Op.ne]: -1 30 | } 31 | }, 32 | attributes: ["offerID", "currentState", "message", "totalValue"], 33 | order: [['updatedAt', 'DESC']] 34 | }); 35 | res.locals.offers = []; 36 | offers.forEach(offer => { 37 | res.locals.offers.push({ 38 | offerID: offer.offerID, 39 | status: Helper.Steam.ETradeOfferState(offer.currentState), 40 | message: offer.message, 41 | value: (offer.totalValue !== -1 ? Helper.toUSD(offer.totalValue) : "-") 42 | }); 43 | }); 44 | 45 | //Rounds 46 | let rounds = await await Db.JackpotRound.findAll({ 47 | where: Db.sequelize.fn('JSON_CONTAINS_PATH', Db.sequelize.col('players'), 'all', '$.' + res.locals.user.steamid), 48 | attributes: [ 49 | "roundID", 50 | "status", 51 | "winnerSteamID", 52 | [Db.sequelize.fn('JSON_EXTRACT', Db.sequelize.col('bets'), '$[*].items[*].price'), 'prices'], 53 | [Db.sequelize.fn('JSON_VALUE', Db.sequelize.col('hashes'), '$.pin'), 'pin'] 54 | ], 55 | order: [['updatedAt', 'DESC']] 56 | }); 57 | res.locals.rounds = []; 58 | for (let round of rounds) { 59 | round.dataValues.prices = JSON.parse(round.dataValues.prices); 60 | res.locals.rounds.push({ 61 | roundID: round.roundID, 62 | status: Helper.roundStatus(round.status), 63 | pin: (round.winnerSteamID === res.locals.user.steamid ? round.dataValues.pin : "-"), 64 | won: (round.winnerSteamID === res.locals.user.steamid), 65 | pot: Helper.toUSD(Helper.sumArray(round.dataValues.prices)), 66 | }); 67 | } 68 | 69 | 70 | res.locals.page.title = "Jackpot History"; 71 | res.render('jackpot-history'); 72 | } 73 | ); 74 | 75 | router.get('/jackpot/details/:rid([a-f0-9]{24})', Passport.isAuthenticated, async (req, res, next) => { 76 | //Rounds 77 | let round = await await Db.JackpotRound.findOne({ 78 | where: { 79 | roundID: req.params.rid, 80 | status: 3 81 | } 82 | }); 83 | 84 | if (!round) 85 | next(); 86 | 87 | // Parsed from database 88 | round.hashes = JSON.parse(round.hashes); 89 | round.players = JSON.parse(round.players); 90 | round.bets = JSON.parse(round.bets); 91 | 92 | //Will be computed 93 | round.items = []; 94 | round.itemsPlaced = []; 95 | round.totalValue = 0; 96 | 97 | let betValues = {}; 98 | for (let bet of round.bets) { 99 | 100 | for (let item of bet.items) { 101 | round.totalValue += item.price; 102 | 103 | if (betValues[item.from_steamID]) 104 | betValues[item.from_steamID] += item.price; 105 | else 106 | betValues[item.from_steamID] = item.price; 107 | 108 | if (item.from_steamID === res.locals.user.steamid) { 109 | item.avatar = Helper.Item.buildImageURL(item.classid); 110 | item.price = Helper.toUSD(item.price); 111 | round.itemsPlaced.push(item); 112 | } 113 | } 114 | } 115 | 116 | let pKeys = Object.keys(round.players); 117 | for (let i = 0, len = pKeys.length; i < len; i++) { 118 | let percent = ((betValues[pKeys[i]] / round.totalValue) * 100).toFixed(2); 119 | round.players[pKeys[i]].probability = { 120 | percent, 121 | value: Helper.toUSD(betValues[pKeys[i]]) 122 | } 123 | } 124 | 125 | round.winnerValue = Helper.toUSD(round.totalValue - round.winnerFee); 126 | round.totalValue = Helper.toUSD(round.totalValue); 127 | 128 | res.locals.round = round; 129 | res.locals.page.title = "Round Detail"; 130 | res.render('jackpot-details'); 131 | }); 132 | 133 | module.exports = router; 134 | -------------------------------------------------------------------------------- /models/api.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'), 2 | config = require('../config'), 3 | Db = require('./db'), 4 | Log = require('./log'), 5 | Helper = require('./helper'); 6 | 7 | const inventoryFetches = {}; 8 | 9 | const prices = require('../prices.json'); 10 | 11 | Api = {}; 12 | 13 | Api.updateProfile = async (req, res) => { 14 | let data = req.body, 15 | steamid = res.locals.user.steamid, 16 | tradeToken = res.locals.user.tradeToken; 17 | 18 | let ret = { 19 | result: true 20 | }; 21 | 22 | let tradelinkRegex = new RegExp("https:\\/\\/steamcommunity\\.com\\/tradeoffer\\/new\\/\\?partner=[0-9]{1,}&token=[a-zA-Z0-9_-]{1,10}$"); 23 | 24 | if (!data.tradeurl) { 25 | ret.result = false; 26 | ret.message = "You need to enter something first."; 27 | return ret; 28 | } 29 | 30 | if (!tradelinkRegex.test(data.tradeurl)) { 31 | ret.result = false; 32 | ret.message = "Invalid trade link."; 33 | return ret; 34 | } 35 | 36 | let steamid3 = Helper.getSteamID3(steamid); 37 | let params = Helper.parseGetParameters(data.tradeurl); 38 | 39 | if (parseInt(params['partner']) !== steamid3) { 40 | ret.result = false; 41 | ret.message = "This trade link not belongs to this account."; 42 | return ret; 43 | } 44 | 45 | if (params['token'] === tradeToken) { 46 | return ret; 47 | } 48 | 49 | try { 50 | await Db.action.User.update({tradeToken: params['token']}, {steamid: steamid}, false); 51 | return ret; 52 | } 53 | catch (err) { 54 | Log.error("An error occurred while saving tradelink: " + err); 55 | ret.result = false; 56 | ret.message = "Trade link not saved."; 57 | return ret; 58 | } 59 | }; 60 | 61 | Api.getInventory = async steamid => { 62 | let ret = {}; 63 | 64 | if (!Helper.isSteamID64(steamid)) 65 | return ret; 66 | 67 | if (inventoryFetches[steamid] && inventoryFetches[steamid] > Date.now()) { 68 | ret.error = "You can only refresh once every " + config.steam.inventoryFetchLimiter + " seconds."; 69 | return ret; 70 | } 71 | 72 | try { 73 | const res = await axios.get("http://steamcommunity.com/inventory/" + steamid + "/" + config.global.siteAppID + "/2?l=english&count=1000"); 74 | 75 | inventoryFetches[steamid] = Date.now() + (config.steam.inventoryFetchLimiter * 1000); 76 | 77 | const data = res.data; 78 | let inventory = []; 79 | const descriptions = {}; 80 | 81 | for (let i in data.descriptions) { 82 | let description = data.descriptions[i]; 83 | descriptions[description.classid] = description; 84 | } 85 | 86 | for (let i in data.assets) { 87 | let asset = data.assets[i]; 88 | let item = descriptions[asset.classid]; 89 | let price = prices[item.market_hash_name]; 90 | if (price) { 91 | inventory.push({ 92 | "name": item.market_name, 93 | "color": item.name_color, 94 | "id": asset.assetid, 95 | "img": "https://steamcommunity-a.akamaihd.net/economy/image/class/" + config.global.siteAppID + "/" + asset.classid + "/120fx100f", 96 | "price": price 97 | }); 98 | } 99 | } 100 | 101 | inventory = Helper.sortBy(inventory); 102 | 103 | return { 104 | data: inventory 105 | }; 106 | } catch (err) { 107 | Log.error("An error occurred while fetching steam inventory: " + err); 108 | ret.error = "Steam error."; 109 | return ret; 110 | } 111 | }; 112 | 113 | Api.coinflipProvably = async data => { 114 | try { 115 | const seed = data.server_seed, 116 | c_seed = data.c_seed, 117 | j_seed = data.j_seed; 118 | 119 | if (!seed || !c_seed || !j_seed || seed === "" || c_seed === "" || j_seed === "") 120 | return { 121 | error: "You need to set all the seed to check the roll" 122 | }; 123 | 124 | if (!/^[a-zA-Z0-9]*$/ig.test(seed) || !/^[a-zA-Z0-9]*$/ig.test(c_seed) || !/^[a-zA-Z0-9]*$/ig.test(j_seed)) 125 | return { 126 | error: "All the seed must be in valid format" 127 | }; 128 | 129 | let hashes = '{"seed" : "' + seed + '","c_seed" : "' + c_seed + '","j_seed" : "' + j_seed + '"}'; 130 | 131 | let round = await Db.CoinflipRound.findOne({ 132 | where: { 133 | [Db.Op.and]: [ 134 | Db.sequelize.fn('JSON_CONTAINS', Db.sequelize.col('hashes'), hashes, '$'), 135 | {'status': 3} 136 | ] 137 | }, 138 | attributes: [ 139 | [Db.sequelize.fn('JSON_VALUE', Db.sequelize.col('hashes'), '$.percent'), 'percent'] 140 | ] 141 | }); 142 | console.log(round); 143 | if (round) 144 | return { 145 | roll: round.dataValues.percent 146 | }; 147 | else 148 | return { 149 | roll: Helper.Generate.coinflipPercent(seed + c_seed + j_seed) 150 | }; 151 | } catch(err){ 152 | console.log(err); 153 | } 154 | }; 155 | 156 | module.exports = Api; 157 | 158 | /*let a = { 159 | color: "D2D2D2" 160 | id: "14282850077" 161 | img: "https://steamcommunity-a.akamaihd.net/economy/image/class/730/575563074/120fx100f" 162 | name: "Glock-18 | Reactor (Field-Tested)" 163 | price: 152 164 | };*/ -------------------------------------------------------------------------------- /views/jackpot.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | Chat 5 |
6 |
7 | 8 |
9 |
10 | <% if(user){ %> 11 | 12 | Deposit 13 | 14 | <% } else { %> 15 | 16 | Sign in to Deposit 17 | 18 | <% } %> 19 |
20 |
21 |
22 |
23 |
24 | 27 |
28 |
29 | 32 |
33 | 0.00 34 | 0/50 35 |
36 |
37 |
Countdown starts in 38 | -- seconds... 39 |
40 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 51 |
52 | <% config.global.jackpot.alertBar.forEach((alert)=>{ %> 53 |
54 | <%- alert.html %> 55 |
56 | <% }); %> 57 |
58 |
59 |
60 | <% if(user){ %> 61 | 62 | Deposit 63 | 64 | <% } else { %> 65 | 66 | Sign in to Deposit 67 | 68 | <% } %> 69 |
Max 70 | <%= config.offers.jackpot.maxItems %> items/trade - 71 | $<%= config.offers.jackpot.minTotalPrice / 100 %> minimum value 72 |
73 |
74 | <% if(!user){ %> 75 |
You need to be logged in to deposit items.
76 | <% } else { %> 77 |
78 |
    79 |
  • Total placed

    0

  • 80 |
  • Chances of winning

    0%

  • 81 |
82 |
83 | <% } %> 84 |
85 |
View details
86 |
87 |
88 |
89 |
Players odds
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | 98 | <% include chat %> 99 | <%- contentFor('scripts') %> 100 | <% include templates/jackpot-handlebars %> 101 | 107 | 108 | <% include templates/games-scripts %> 109 | -------------------------------------------------------------------------------- /public/js/md5.js: -------------------------------------------------------------------------------- 1 | var CryptoJS=CryptoJS||function(s,p){var m={},l=m.lib={},n=function(){},r=l.Base={extend:function(b){n.prototype=this;var h=new n;b&&h.mixIn(b);h.hasOwnProperty("init")||(h.init=function(){h.$super.init.apply(this,arguments)});h.init.prototype=h;h.$super=this;return h},create:function(){var b=this.extend();b.init.apply(b,arguments);return b},init:function(){},mixIn:function(b){for(var h in b)b.hasOwnProperty(h)&&(this[h]=b[h]);b.hasOwnProperty("toString")&&(this.toString=b.toString)},clone:function(){return this.init.prototype.extend(this)}},q=l.WordArray=r.extend({init:function(b,h){b=this.words=b||[];this.sigBytes=h!=p?h:4*b.length},toString:function(b){return(b||t).stringify(this)},concat:function(b){var h=this.words,a=b.words,j=this.sigBytes;b=b.sigBytes;this.clamp();if(j%4)for(var g=0;g>>2]|=(a[g>>>2]>>>24-8*(g%4)&255)<<24-8*((j+g)%4);else if(65535>>2]=a[g>>>2];else h.push.apply(h,a);this.sigBytes+=b;return this},clamp:function(){var b=this.words,h=this.sigBytes;b[h>>>2]&=4294967295<<32-8*(h%4);b.length=s.ceil(h/4)},clone:function(){var b=r.clone.call(this);b.words=this.words.slice(0);return b},random:function(b){for(var h=[],a=0;a>>2]>>>24-8*(j%4)&255;g.push((k>>>4).toString(16));g.push((k&15).toString(16))}return g.join("")},parse:function(b){for(var a=b.length,g=[],j=0;j>>3]|=parseInt(b.substr(j,2),16)<<24-4*(j%8);return new q.init(g,a/2)}},a=v.Latin1={stringify:function(b){var a=b.words;b=b.sigBytes;for(var g=[],j=0;j>>2]>>>24-8*(j%4)&255));return g.join("")},parse:function(b){for(var a=b.length,g=[],j=0;j>>2]|=(b.charCodeAt(j)&255)<<24-8*(j%4);return new q.init(g,a)}},u=v.Utf8={stringify:function(b){try{return decodeURIComponent(escape(a.stringify(b)))}catch(g){throw Error("Malformed UTF-8 data");}},parse:function(b){return a.parse(unescape(encodeURIComponent(b)))}},g=l.BufferedBlockAlgorithm=r.extend({reset:function(){this._data=new q.init;this._nDataBytes=0},_append:function(b){"string"==typeof b&&(b=u.parse(b));this._data.concat(b);this._nDataBytes+=b.sigBytes},_process:function(b){var a=this._data,g=a.words,j=a.sigBytes,k=this.blockSize,m=j/(4*k),m=b?s.ceil(m):s.max((m|0)-this._minBufferSize,0);b=m*k;j=s.min(4*b,j);if(b){for(var l=0;l>>32-j)+k}function m(a,k,b,h,l,j,m){a=a+(k&h|b&~h)+l+m;return(a<>>32-j)+k}function l(a,k,b,h,l,j,m){a=a+(k^b^h)+l+m;return(a<>>32-j)+k}function n(a,k,b,h,l,j,m){a=a+(b^(k|~h))+l+m;return(a<>>32-j)+k}for(var r=CryptoJS,q=r.lib,v=q.WordArray,t=q.Hasher,q=r.algo,a=[],u=0;64>u;u++)a[u]=4294967296*s.abs(s.sin(u+1))|0;q=q.MD5=t.extend({_doReset:function(){this._hash=new v.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(g,k){for(var b=0;16>b;b++){var h=k+b,w=g[h];g[h]=(w<<8|w>>>24)&16711935|(w<<24|w>>>8)&4278255360}var b=this._hash.words,h=g[k+0],w=g[k+1],j=g[k+2],q=g[k+3],r=g[k+4],s=g[k+5],t=g[k+6],u=g[k+7],v=g[k+8],x=g[k+9],y=g[k+10],z=g[k+11],A=g[k+12],B=g[k+13],C=g[k+14],D=g[k+15],c=b[0],d=b[1],e=b[2],f=b[3],c=p(c,d,e,f,h,7,a[0]),f=p(f,c,d,e,w,12,a[1]),e=p(e,f,c,d,j,17,a[2]),d=p(d,e,f,c,q,22,a[3]),c=p(c,d,e,f,r,7,a[4]),f=p(f,c,d,e,s,12,a[5]),e=p(e,f,c,d,t,17,a[6]),d=p(d,e,f,c,u,22,a[7]),c=p(c,d,e,f,v,7,a[8]),f=p(f,c,d,e,x,12,a[9]),e=p(e,f,c,d,y,17,a[10]),d=p(d,e,f,c,z,22,a[11]),c=p(c,d,e,f,A,7,a[12]),f=p(f,c,d,e,B,12,a[13]),e=p(e,f,c,d,C,17,a[14]),d=p(d,e,f,c,D,22,a[15]),c=m(c,d,e,f,w,5,a[16]),f=m(f,c,d,e,t,9,a[17]),e=m(e,f,c,d,z,14,a[18]),d=m(d,e,f,c,h,20,a[19]),c=m(c,d,e,f,s,5,a[20]),f=m(f,c,d,e,y,9,a[21]),e=m(e,f,c,d,D,14,a[22]),d=m(d,e,f,c,r,20,a[23]),c=m(c,d,e,f,x,5,a[24]),f=m(f,c,d,e,C,9,a[25]),e=m(e,f,c,d,q,14,a[26]),d=m(d,e,f,c,v,20,a[27]),c=m(c,d,e,f,B,5,a[28]),f=m(f,c,d,e,j,9,a[29]),e=m(e,f,c,d,u,14,a[30]),d=m(d,e,f,c,A,20,a[31]),c=l(c,d,e,f,s,4,a[32]),f=l(f,c,d,e,v,11,a[33]),e=l(e,f,c,d,z,16,a[34]),d=l(d,e,f,c,C,23,a[35]),c=l(c,d,e,f,w,4,a[36]),f=l(f,c,d,e,r,11,a[37]),e=l(e,f,c,d,u,16,a[38]),d=l(d,e,f,c,y,23,a[39]),c=l(c,d,e,f,B,4,a[40]),f=l(f,c,d,e,h,11,a[41]),e=l(e,f,c,d,q,16,a[42]),d=l(d,e,f,c,t,23,a[43]),c=l(c,d,e,f,x,4,a[44]),f=l(f,c,d,e,A,11,a[45]),e=l(e,f,c,d,D,16,a[46]),d=l(d,e,f,c,j,23,a[47]),c=n(c,d,e,f,h,6,a[48]),f=n(f,c,d,e,u,10,a[49]),e=n(e,f,c,d,C,15,a[50]),d=n(d,e,f,c,s,21,a[51]),c=n(c,d,e,f,A,6,a[52]),f=n(f,c,d,e,q,10,a[53]),e=n(e,f,c,d,y,15,a[54]),d=n(d,e,f,c,w,21,a[55]),c=n(c,d,e,f,v,6,a[56]),f=n(f,c,d,e,D,10,a[57]),e=n(e,f,c,d,t,15,a[58]),d=n(d,e,f,c,B,21,a[59]),c=n(c,d,e,f,r,6,a[60]),f=n(f,c,d,e,z,10,a[61]),e=n(e,f,c,d,j,15,a[62]),d=n(d,e,f,c,x,21,a[63]);b[0]=b[0]+c|0;b[1]=b[1]+d|0;b[2]=b[2]+e|0;b[3]=b[3]+f|0},_doFinalize:function(){var a=this._data,k=a.words,b=8*this._nDataBytes,h=8*a.sigBytes;k[h>>>5]|=128<<24-h%32;var l=s.floor(b/ 2 | 4294967296);k[(h+64>>>9<<4)+15]=(l<<8|l>>>24)&16711935|(l<<24|l>>>8)&4278255360;k[(h+64>>>9<<4)+14]=(b<<8|b>>>24)&16711935|(b<<24|b>>>8)&4278255360;a.sigBytes=4*(k.length+1);this._process();a=this._hash;k=a.words;for(b=0;4>b;b++)h=k[b],k[b]=(h<<8|h>>>24)&16711935|(h<<24|h>>>8)&4278255360;return a},clone:function(){var a=t.clone.call(this);a._hash=this._hash.clone();return a}});r.MD5=t._createHelper(q);r.HmacMD5=t._createHmacHelper(q)})(Math); -------------------------------------------------------------------------------- /models/helper.js: -------------------------------------------------------------------------------- 1 | const SteamID = require('steamid'), 2 | crypto = require('crypto'), 3 | config = require('../config'); 4 | 5 | const Helper = { 6 | Generate: {}, 7 | Steam: {}, 8 | User: {}, 9 | Item: {}, 10 | Offer: {}, 11 | Coinflip: {} 12 | }, 13 | ETradeOfferStates = { 14 | "1": "Invalid", 15 | "2": "Active", 16 | "3": "Accepted", 17 | "4": "Countered", 18 | "5": "Expired", 19 | "6": "Canceled", 20 | "7": "Declined", 21 | "8": "InvalidItems", 22 | "9": "NeedsConfirmation", 23 | "10": "Canceled (2FA)", 24 | "11": "InEscrow" 25 | }, 26 | ERoundStates = { 27 | "0": "WaitingForBets", 28 | "1": "EarlyTimer", 29 | "2": "FinalTimer", 30 | "3": "Over", 31 | }, 32 | ECoinflipRoundStates = { 33 | "1": "WaitingForJoiner", 34 | "2": "WaitingForJoinerConfirm", 35 | "3": "Over", 36 | }; 37 | 38 | /** 39 | * Global 40 | */ 41 | 42 | Helper.getSteamID3 = (steamid64) => { 43 | return new SteamID(steamid64).accountid 44 | }; 45 | 46 | Helper.isSteamID64 = string => { 47 | if (!string) 48 | return; 49 | try { 50 | return new SteamID(string).isValid(); 51 | } catch (err) { 52 | return false; 53 | } 54 | }; 55 | 56 | Helper.remove = (array, element) => { 57 | const index = array.indexOf(element); 58 | 59 | if (index !== -1) { 60 | array.splice(index, 1); 61 | } 62 | }; 63 | 64 | Helper.isset = variable => { 65 | return typeof variable !== 'undefined' && variable !== ""; 66 | }; 67 | 68 | Helper.toUSD = cents => { 69 | let usd = cents / 100; 70 | return Number.isInteger(usd) ? usd : parseFloat(usd.toFixed(2)); 71 | }; 72 | 73 | Helper.toCents = usd => { 74 | let cents = usd * 100; 75 | return Math.round(cents); 76 | }; 77 | 78 | Helper.roundStatus = status => { 79 | return ERoundStates[status]; 80 | }; 81 | 82 | Helper.coinflipRoundStatus = status => { 83 | return ECoinflipRoundStates[status]; 84 | }; 85 | 86 | Helper.playerItemsCount = (items, steamid) => { 87 | let count = 0; 88 | items.forEach(item => { 89 | if (item.from_steamID === steamid) 90 | count++; 91 | }); 92 | return count; 93 | }; 94 | 95 | Helper.sumArray = array => { 96 | let sum = 0; 97 | for (let num of array) { 98 | sum += parseInt(num); 99 | } 100 | return sum; 101 | }; 102 | 103 | Helper.delay = ms => new Promise(resolve => setTimeout(resolve, ms)); 104 | 105 | Helper.parseGetParameters = string => { 106 | let params = {}; 107 | let lets = string.split('?'); 108 | lets = lets[1].split('&'); 109 | for (let i = 0; i < lets.length; i++) { 110 | let pair = lets[i].split('='); 111 | params[pair[0]] = pair[1]; 112 | } 113 | return params; 114 | }; 115 | 116 | Helper.sortBy = (items, field = "price", asc = false) => { 117 | return items.sort(function (a, b) { 118 | if (!asc) 119 | return b[field] - a[field]; 120 | else 121 | return a[field] - b[field]; 122 | }); 123 | }; 124 | 125 | /** 126 | * Generate 127 | */ 128 | 129 | Helper.Generate.hash = () => { 130 | return crypto.randomBytes(20).toString('hex'); 131 | }; 132 | 133 | 134 | Helper.Generate.roundHashes = () => { 135 | let percentage = (Math.random() * 100).toFixed(14), 136 | secret = crypto.randomBytes(5).toString('hex'), 137 | hash = crypto.createHash('md5').update(percentage.toString() + ":" + secret).digest().toString('hex'); 138 | return { 139 | percentage, 140 | secret, 141 | hash 142 | } 143 | }; 144 | 145 | Helper.Generate.coinflipPercent = string =>{ 146 | string = crypto.createHash('sha256').update(string).digest().toString('hex'); 147 | return 100 * (parseInt(string, 16) / Math.pow(16, 64)); 148 | }; 149 | 150 | Helper.Generate.coinflipRoundHashes = () => { 151 | let seed = crypto.randomBytes(16).toString('hex'), 152 | secret = crypto.randomBytes(16).toString('hex'), 153 | hash = crypto.createHash('sha256').update(seed).digest().toString('hex'); 154 | return { 155 | seed, 156 | secret, 157 | hash 158 | } 159 | }; 160 | Helper.Generate.coinflipRoundHashes(); 161 | 162 | Helper.Generate.roundID = () => { 163 | return crypto.createHash('md5').update(Date.now().toString()).digest().toString('hex').slice(0, 24); 164 | }; 165 | 166 | Helper.Generate.randomNum = (from, to) => { 167 | return Math.floor(Math.random() * to) + from 168 | }; 169 | 170 | Helper.Generate.offerPin = () => { 171 | return crypto.createHash('md5').update(Date.now().toString() + crypto.randomBytes(5).toString('hex')).digest().toString('hex').slice(0, 8); 172 | }; 173 | 174 | /** 175 | * Steam 176 | */ 177 | 178 | Helper.Steam.ETradeOfferState = state => { 179 | return ETradeOfferStates[state]; 180 | }; 181 | 182 | /** 183 | * User 184 | */ 185 | 186 | Helper.User.buildAvatar = (hash, size = "") => { 187 | return config.steam.avatarStore + hash.slice(0, 2) + "/" + hash + (size !== "" ? "_" : "") + size + ".jpg"; 188 | }; 189 | 190 | Helper.User.computeLevel = (xp) => { 191 | return Math.floor(xp / config.global.xpPerLevel); 192 | }; 193 | 194 | /** 195 | * Item 196 | */ 197 | 198 | Helper.Item.buildImageURL = (classid, size = "120fx100", appid = config.global.siteAppID) => { 199 | return "https://steamcommunity-a.akamaihd.net/economy/image/class/" + appid + "/" + classid + "/" + size + "f"; 200 | }; 201 | 202 | Helper.Item.sumPrices = items => { 203 | let sum = 0; 204 | for (let item of items) { 205 | sum += parseInt(item.price) 206 | } 207 | return sum; 208 | }; 209 | 210 | 211 | /** 212 | * Offer 213 | */ 214 | 215 | Helper.Offer.isGift = offer => { 216 | return offer.itemsToGive.length === 0 && offer.itemsToReceive.length > 0 217 | }; 218 | 219 | /** 220 | * Coinflip 221 | */ 222 | 223 | Helper.Coinflip.isRoundID = input => { 224 | return (typeof input === "string") && input.length === 24 225 | && !isNaN(parseInt(input, 16)); 226 | }; 227 | 228 | module.exports = Helper; -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | function askTradeurl() { 2 | if (!window.Application.sid) 3 | return; 4 | if (!window.Application.ntd) 5 | return; 6 | var modal = new CModal(); 7 | modal.init({ 8 | title: "One more thing...", confirmLabel: "Save my trade url", closeOnConfirm: false, confirmCallback: function () { 9 | var resultDiv = modal.elem.find(".form-result").html(""); 10 | var turl = modal.elem.find("#tradeurl").val(); 11 | if (!turl) return resultDiv.html('
You need to enter something first.
'); 12 | var sbtn = modal.elem.find(".btn-confirm"); 13 | resultDiv.html(""); 14 | sbtn.button("loading"); 15 | $.ajax({ 16 | method: "POST", url: "/api/update-profile", dataType: "json", data: {tradeurl: turl, _csrf: $("#csrf").val()}, success: function (data) { 17 | if (data.result) { 18 | resultDiv.html('
Trade URL saved! Please wait...
') 19 | return setTimeout(function () { 20 | window.location.reload(); 21 | }, 1000); 22 | } 23 | resultDiv.html('
' + data.message + '
'); 24 | sbtn.button("reset"); 25 | }, error: function (xhr, status, err) { 26 | sbtn.button("reset"); 27 | var errorStr = err || ("HTTP error: " + status); 28 | resultDiv.html('
' + errorStr + '
'); 29 | } 30 | }); 31 | } 32 | }); 33 | var tpl = Handlebars.compile($("#turl-modal-content").html()); 34 | var content = tpl({steamid: window.Application.sid}); 35 | modal.setContent(content); 36 | modal.show(); 37 | } 38 | 39 | const LevelColors = { 40 | 0: {bg: "#3a3f44", txt: "#fff"}, 41 | 10: {bg: "#4E342E", txt: "#fff"}, 42 | 20: {bg: "#009688", txt: "#fff"}, 43 | 30: {bg: "#FF5722", txt: "#fff"}, 44 | 40: {bg: "#FF5722", txt: "#fff"}, 45 | 50: {bg: "#C2185B", txt: "#fff"}, 46 | 60: {bg: "#C2185B", txt: "#fff"}, 47 | 70: {bg: "#00695C", txt: "#fff"}, 48 | 80: {bg: "#00695C", txt: "#fff"}, 49 | 90: {bg: "#FFEB3B", txt: "#000"}, 50 | 100: {bg: "#4B69FF", txt: "#fff"}, 51 | 200: {bg: "#8847FF", txt: "#fff"}, 52 | 300: {bg: "#D32CE6", txt: "#fff"}, 53 | max: {bg: "#EB4B4B", txt: "#fff"} 54 | }; 55 | 56 | function getLevelClass(lvl) { 57 | var range = lvl - lvl % 100; 58 | if (range < 100) { 59 | var l = lvl % 100; 60 | range = l - l % 10; 61 | } 62 | return LevelColors.hasOwnProperty(range) ? LevelColors[range] : LevelColors.max; 63 | } 64 | 65 | Number.prototype.format = function (n, x) { 66 | var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\.' : '$') + ')'; 67 | return this.toFixed(Math.max(0, ~~n)).replace(new RegExp(re, 'g'), '$&,'); 68 | }; 69 | 70 | function shuffleArray(a) { 71 | var j, x, i; 72 | for (i = a.length; i; i -= 1) { 73 | j = Math.floor(Math.random() * i); 74 | x = a[i - 1]; 75 | a[i - 1] = a[j]; 76 | a[j] = x; 77 | } 78 | } 79 | 80 | Number.prototype.asCurrency = function () { 81 | return ' ' + this.toFixed(2); 82 | }; 83 | 84 | function toggleSidePanel() { 85 | $("#main-content").toggleClass("panel-open"); 86 | $("#right-panel").toggleClass("panel-open"); 87 | var sp = $("#sp-toggle i.arrow"); 88 | if (sp.hasClass("fa-chevron-right")) { 89 | sp.attr("class", "fa fa-chevron-left arrow"); 90 | } 91 | else sp.attr("class", "fa fa-chevron-right arrow"); 92 | } 93 | 94 | $(function () { 95 | $('[data-toggle="tooltip"]').tooltip(); 96 | var ws = io("/"); 97 | ws.on("currentlyPlayed", function (data) { 98 | var jtotal = $(".jackpot-total"); 99 | var cftotal = $(".cf-total"); 100 | if (data.jackpot_total !== jtotal.data("value")) { 101 | jtotal.data("value", data.jackpot_total); 102 | jtotal.html(data.jackpot_total / 100); 103 | if (data.jackpot_total === 0) 104 | return jtotal.fadeOut(); 105 | if (!jtotal.is(":visible")) return jtotal.fadeIn(); 106 | jtotal.effect("highlight", {color: '#f58380'}); 107 | } 108 | if (data.cf_total !== cftotal.data("value")) { 109 | cftotal.data("value", data.cf_total); 110 | cftotal.html(data.cf_total / 100); 111 | if (data.cf_total === 0) 112 | return cftotal.fadeOut(); 113 | if (!cftotal.is(":visible")) return cftotal.fadeIn(); 114 | cftotal.effect("highlight", {color: '#f58380'}); 115 | } 116 | }); 117 | var rp = $("#right-panel"); 118 | if (rp.length) { 119 | var w = $(window); 120 | var sp = $('#sp-toggle'); 121 | if (w.width() < 1380 && rp.hasClass("panel-open")) 122 | toggleSidePanel(); 123 | w.resize(function () { 124 | if (w.width() < 1380 && rp.hasClass("panel-open")) 125 | toggleSidePanel(); 126 | }); 127 | sp.on("click", function () { 128 | toggleSidePanel(); 129 | }); 130 | $("#minimize-chat").on("click", function () { 131 | toggleSidePanel(); 132 | }); 133 | } 134 | }); 135 | 136 | function openLoginModal() { 137 | var lmodal = new CModal(); 138 | lmodal.init({large: true, isConfirm: false}); 139 | var content = Handlebars.compile($("#tpl-login-modal").html()); 140 | lmodal.setContent(content); 141 | lmodal.show(); 142 | } 143 | 144 | function initiateAltLogin() { 145 | var req = $.ajax({method: "POST", body: {steamid: $("#login-steam-id").val()}, url: "/async/alt-login-init", dataType: "json"}); 146 | req.done(function (data) { 147 | var ctn = $("#alt-login-result"); 148 | if (data.error) return ctn.text(data.error); 149 | $("#login-init-btn").hide(); 150 | $("#login-verify-btn").show(); 151 | ctn.html("Please add " + data.auth_token + " to your name and click the verify button below to login."); 152 | }); 153 | } 154 | 155 | function verifyLogin() { 156 | var req = $.ajax({method: "get", url: "/async/verify-login", dataType: "json"}); 157 | req.done(function (data) { 158 | if (data.error) return $("#alt-login-result").text(data.error); 159 | console.log(data); 160 | }); 161 | } -------------------------------------------------------------------------------- /views/coinflip-faq.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

6 | Coinflip FAQ 7 |

8 |

What is this game?

9 |

CSGOHunt's coinflip a simple way to 1vs1 against other players and attempt to win their skins. To do so, all you have to do is either create a coinflip by 10 | picking the items you want to gamble, or join an existing one granted that you can place items valued at the same prices within a 10% margin. For example, if a 11 | round was created with items valued at 100.00 points, then you will have to place a bet between 90.00 and 110.00 points.

12 |

How to create a Coinflip?

13 |

When creating a round, simply click on "Create a coinflip". You will then see all the items from your current inventory and will be able to pick the ones you 14 | want to gamble. Note that the minimum bet is 5.00 points for a maximum of 10 items.

15 |

How to join a Coinflip?

16 |

Joining a game is as easy as creating one, just click on "Join" on a coinflip that you're interested in. You will then be presented a different kind of inventory 17 | window, this one will have more information about what you need to add or remove to reach the correct bet amount.

18 |

Once the offer is sent by one of our bot, a timer indicating that someone is trying to join will be triggered. It will last for 2 minutes to give time to the 19 | player to accept and confirm the offer. Past this time, the offer will be canceled and the round will once again become free for everyone to join.

20 |

What happen when a coinflip is over?

21 |

Once a round end, our bot will send you the trade offer right away. It is recommended to accept it as soon as possible to avoid any issue. Note that a commission 22 | of 0 to 5% may apply, up to 10% if no items matching 5% is available, however the items that the bot keep cannot be the one that you used to create the 23 | coinflip. If no items matches the commission range, then no commission will be applied.

24 |

25 | Provably fair 26 |

27 |

How is the winner picked?

28 |

Since every player can place a bet within a 10% of the total value, some coinflip may not be exactly 50/50 chance of winning. The chances will be affected by how 29 | much you place. For instance, if someone create a coinflip of 1000.00 points and a player join with 1100.00, the player that joined will have more chances of 30 | winning because his bet is higher. All the % are presented within the Join menu.

31 |

The player that picks CT side will own the EARLY numbers, the one that joined as T-Side will own the LATE numbers.

32 |

Here is a quick example: A coinflip valued at 1000.00 points is created. Then someone join it with 1000.00, in this case his chances of winning would be 50%. The 33 | player that created the round as CT will have number 0 to 50%, the player that join as T will have 51 to 100%.

34 |

Once the number generation is over, if the number is between 0 and 50, then the player that created the round will win! Otherwise, the player that joined wins 35 | the game.

36 |

How does the number generation works and what is provably fair?

37 |

To ensure that every coinflip is fair and cannot be tempered with, we are using a distributed RNG system.

38 |

The way it works is relatively simple: Each player generate a 'seed' (a string of random characters) and attach it to the create or join request. The server will 39 | also generate a seed and show its 'hash' (an encrypted version of the seed) to both players to garantee that it wasn't changed during any steps of the 40 | coinflip.

41 |

Once the coinflip is over, all three seeds will be used to compute the winning percentage and the secret used to create the server 'hash' will also be 42 | revealed.

43 |

In conclusion, this mean that each players will contribute to the result of the final number, therefore no one can predict, influance or know the number 44 | beforehand.

45 |

Verify a round fairness

46 |

You can use the form below to check the roll for every combination of seed, that is the server, player 1 and player 2 seeds. Remember that you can see all the 47 | seed only once a round is over!

48 |
49 | 50 | 51 | 52 | 53 |
54 |
55 |
56 |
57 |
58 | 59 | <%- contentFor('scripts') %> 60 | -------------------------------------------------------------------------------- /views/layouts/main.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CSGOHunt.com - <%= page.title %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <%- defineContent('styles') %> 19 | 20 | 21 | 22 | 26 | 27 | 28 | 79 |
80 | 81 | <%= config.website.name %> 82 | 83 | <% if(user){ %> 84 | <% include ../templates/side-menu-profile %> 85 | <% } else { %> 86 |

87 | 88 | Login with steam 89 | 90 |

91 | <% } %> 92 | 111 | 129 | 141 |
142 | <%- body %> 143 | 159 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | <%- defineContent('scripts') %> 179 | -------------------------------------------------------------------------------- /public/js/jquery.countdown360.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Countdown 360 - v0.1.9 3 | * This is a simple attractive circular countdown timer that counts down a number of seconds. The style is configurable and callbacks are supported on completion. 4 | * https://github.com/johnschult/jquery.countdown360 5 | * 6 | * Made by John Schult 7 | * Under MIT License 8 | */ 9 | ;(function ($, window, document, undefined) { 10 | var pluginName = "countdown360", 11 | defaults = { 12 | radius: 15.5, // radius of arc 13 | strokeStyle: "#477050", // the color of the stroke 14 | strokeWidth: undefined, // the stroke width, dynamically calulated if omitted in options 15 | fillStyle: "#8ac575", // the fill color 16 | fontColor: "#477050", // the font color 17 | fontFamily: "sans-serif", // the font family 18 | fontSize: undefined, // the font size, dynamically calulated if omitted in options 19 | fontWeight: 700, // the font weight 20 | autostart: true, // start the countdown automatically 21 | seconds: 10, // the number of seconds to count down 22 | label: ["second", "seconds"], // the label to use or false if none 23 | startOverAfterAdding: true, // Start the timer over after time is added with addSeconds 24 | smooth: false, // should the timer be smooth or stepping 25 | onComplete: function () {} 26 | }; 27 | 28 | function Plugin(element, options) { 29 | this.element = element; 30 | this.settings = $.extend({}, defaults, options); 31 | if (!this.settings.fontSize) { this.settings.fontSize = this.settings.radius/1.2; } 32 | if (!this.settings.strokeWidth) { this.settings.strokeWidth = this.settings.radius/4; } 33 | this._defaults = defaults; 34 | this._name = pluginName; 35 | this._init(); 36 | } 37 | 38 | Plugin.prototype = { 39 | getTimeRemaining: function() 40 | { 41 | 42 | var timeRemaining = this._secondsLeft(this.getElapsedTime()); 43 | return timeRemaining; 44 | }, 45 | getElapsedTime: function() 46 | { 47 | return Math.round((new Date().getTime() - this.startedAt.getTime())/1000); 48 | }, 49 | extendTimer: function (value) { 50 | var seconds = parseInt(value), 51 | secondsElapsed = Math.round((new Date().getTime() - this.startedAt.getTime())/1000); 52 | if ((this._secondsLeft(secondsElapsed) + seconds) <= this.settings.seconds) { 53 | this.startedAt.setSeconds(this.startedAt.getSeconds() + parseInt(value)); 54 | } 55 | }, 56 | 57 | addSeconds: function (value) { 58 | var secondsElapsed = Math.round((new Date().getTime() - this.startedAt.getTime())/1000); 59 | if (this.settings.startOverAfterAdding) { 60 | this.settings.seconds = this._secondsLeft(secondsElapsed) + parseInt(value); 61 | this.start(); 62 | } else { 63 | this.settings.seconds += parseInt(value); 64 | } 65 | }, 66 | 67 | start: function () { 68 | this.startedAt = new Date(); 69 | this._drawCountdownShape(Math.PI*3.5, true); 70 | this._drawCountdownLabel(0); 71 | var timerInterval = 1000; 72 | if (this.settings.smooth) { 73 | timerInterval = 16; 74 | } 75 | this.interval = setInterval(jQuery.proxy(this._draw, this), timerInterval); 76 | }, 77 | 78 | stop: function (cb) { 79 | clearInterval(this.interval); 80 | if (cb) { cb(); } 81 | }, 82 | 83 | _init: function () { 84 | this.settings.width = (this.settings.radius * 2) + (this.settings.strokeWidth * 2); 85 | this.settings.height = this.settings.width; 86 | this.settings.arcX = this.settings.radius + this.settings.strokeWidth; 87 | this.settings.arcY = this.settings.arcX; 88 | this._initPen(this._getCanvas()); 89 | if (this.settings.autostart) { this.start(); } 90 | }, 91 | 92 | _getCanvas: function () { 93 | var $canvas = $("" + 96 | ""); 97 | $(this.element).prepend($canvas[0]); 98 | return $canvas[0]; 99 | }, 100 | 101 | _initPen: function (canvas) { 102 | this.pen = canvas.getContext("2d"); 103 | this.pen.lineWidth = this.settings.strokeWidth; 104 | this.pen.strokeStyle = this.settings.strokeStyle; 105 | this.pen.fillStyle = this.settings.fillStyle; 106 | this.pen.textAlign = "center"; 107 | this.pen.textBaseline = "middle"; 108 | this.ariaText = $(canvas).children("#countdown-text"); 109 | this._clearRect(); 110 | }, 111 | 112 | _clearRect: function () { 113 | this.pen.clearRect(0, 0, this.settings.width, this.settings.height); 114 | }, 115 | 116 | _secondsLeft: function(secondsElapsed) { 117 | return this.settings.seconds - secondsElapsed; 118 | }, 119 | 120 | _drawCountdownLabel: function (secondsElapsed) { 121 | this.ariaText.text(secondsLeft); 122 | this.pen.font = this.settings.fontWeight + " " + this.settings.fontSize + "px " + this.settings.fontFamily; 123 | var secondsLeft = this._secondsLeft(secondsElapsed), 124 | label = secondsLeft === 1 ? this.settings.label[0] : this.settings.label[1], 125 | drawLabel = this.settings.label && this.settings.label.length === 2, 126 | x = this.settings.width/2; 127 | if (drawLabel) { 128 | y = this.settings.height/2 - (this.settings.fontSize/6.2); 129 | } else { 130 | y = this.settings.height/2; 131 | } 132 | this.pen.fillStyle = this.settings.fillStyle; 133 | this.pen.fillText(secondsLeft + 1, x, y); 134 | this.pen.fillStyle = this.settings.fontColor; 135 | this.pen.fillText(secondsLeft, x, y); 136 | if (drawLabel) { 137 | this.pen.font = "normal small-caps " + (this.settings.fontSize/3) + "px " + this.settings.fontFamily; 138 | this.pen.fillText(label, this.settings.width/2, this.settings.height/2 + (this.settings.fontSize/2.2)); 139 | } 140 | }, 141 | 142 | _drawCountdownShape: function (endAngle, drawStroke) { 143 | this.pen.fillStyle = this.settings.fillStyle; 144 | this.pen.beginPath(); 145 | this.pen.arc(this.settings.arcX, this.settings.arcY, this.settings.radius, Math.PI*1.5, endAngle, false); 146 | this.pen.fill(); 147 | if (drawStroke) { this.pen.stroke(); } 148 | }, 149 | 150 | _draw: function () { 151 | var millisElapsed, secondsElapsed; 152 | millisElapsed = new Date().getTime() - this.startedAt.getTime(); 153 | secondsElapsed = Math.floor((millisElapsed)/1000); 154 | endAngle = (Math.PI*3.5) - (((Math.PI*2)/(this.settings.seconds * 1000)) * millisElapsed); 155 | this._clearRect(); 156 | this._drawCountdownShape(Math.PI*3.5, false); 157 | if (secondsElapsed < this.settings.seconds) { 158 | this._drawCountdownShape(endAngle, true); 159 | this._drawCountdownLabel(secondsElapsed); 160 | } else { 161 | this._drawCountdownLabel(this.settings.seconds); 162 | this.stop(); 163 | this.settings.onComplete(); 164 | } 165 | } 166 | 167 | }; 168 | 169 | $.fn[pluginName] = function (options) { 170 | var plugin; 171 | this.each(function() { 172 | plugin = $.data(this, "plugin_" + pluginName); 173 | if (!plugin) { 174 | plugin = new Plugin(this, options); 175 | $.data(this, "plugin_" + pluginName, plugin); 176 | } 177 | }); 178 | return plugin; 179 | }; 180 | 181 | })(jQuery, window, document); -------------------------------------------------------------------------------- /views/templates/coinflip-handlebars.ejs: -------------------------------------------------------------------------------- 1 | 7 | 71 | 80 | 128 | 145 | 172 | 234 | -------------------------------------------------------------------------------- /public/css/coinflip.css: -------------------------------------------------------------------------------- 1 | .inv-content { 2 | padding: 5px; 3 | height: 528px; 4 | overflow-y: scroll; 5 | } 6 | .inv-content::-webkit-scrollbar-track { 7 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 8 | background-color: #222; 9 | } 10 | .inv-content::-webkit-scrollbar { 11 | width: 6px; 12 | height: 6px; 13 | background-color: #222; 14 | } 15 | .inv-content::-webkit-scrollbar-thumb { 16 | background-color: #555; 17 | } 18 | .inv-content .item { 19 | width: 19.6%; 20 | height: 130px; 21 | border: 1px solid #222; 22 | float: left; 23 | text-align: center; 24 | position: relative; 25 | margin: 1px; 26 | border-radius: 4px; 27 | } 28 | .inv-content .item .item-picture { 29 | width: 100%; 30 | height: 90px; 31 | background-color: #333; 32 | text-align: center; 33 | border-radius: 4px; 34 | } 35 | .inv-content .item .item-picture img { 36 | width: 110px; 37 | } 38 | .inv-content .item-name { 39 | font-size: 11px; 40 | padding: 5px; 41 | } 42 | .inv-content .item-price { 43 | position: absolute; 44 | top: 0; 45 | left: 0; 46 | padding: 5px 10px; 47 | font-size: 11px; 48 | color: #fff; 49 | background-color: #222; 50 | font-weight: bold; 51 | border-radius: 4px 0 0 0; 52 | } 53 | .inv-content .item.blacklisted { 54 | opacity: 0.4; 55 | } 56 | .inv-content .item.blacklisted:hover { 57 | cursor: default; 58 | } 59 | .inv-content .item.blacklisted:before, 60 | .inv-content .item.blacklisted:after { 61 | position: absolute; 62 | content: ""; 63 | background-color: red; 64 | display: block; 65 | width: 100%; 66 | height: 2px; 67 | transform: rotate(-45deg); 68 | left: 0; right: 0; 69 | top: 0; bottom: 0; 70 | margin: auto; 71 | } 72 | 73 | .inv-content .item:hover { 74 | cursor: pointer; 75 | border-color: #d9534f; 76 | } 77 | .inv-content .item.selected { 78 | border-color: #4CAF50; 79 | } 80 | .item-picker .to-hint { 81 | margin-left: 10px; 82 | font-weight: bold; 83 | font-size: smaller; 84 | } 85 | .item-picker .player-seed { 86 | margin-top: 5px; 87 | } 88 | .item-picker .player-seed label:hover { 89 | cursor: pointer; 90 | } 91 | .item-picker .player-seed a { 92 | color: #fff; 93 | } 94 | .item-picker .player-seed input { 95 | background-color: #333; 96 | border: #222; 97 | } 98 | .item-picker .join-info span, 99 | .item-picker .inv-total span { 100 | color: #fff; 101 | font-size: 15px; 102 | } 103 | .item-picker .side-info { 104 | margin: 15px 0; 105 | } 106 | .item-picker .side-info label > img { 107 | width: 64px; 108 | margin: 5px; 109 | } 110 | .item-picker .side-info label > input { 111 | visibility: hidden; 112 | position: absolute; 113 | } 114 | .item-picker .side-info label > input + img { 115 | cursor:pointer; 116 | box-shadow: none; 117 | border:2px solid transparent; 118 | } 119 | .item-picker .side-info label > input:checked + img { 120 | border-radius: 100%; 121 | box-shadow: 0 0 15px 0 #2ecc71; 122 | } 123 | 124 | 125 | #duel-game-ui .ui-info { 126 | margin-bottom: 25px; 127 | } 128 | #duel-game-ui .ui-content { 129 | margin-bottom: 25px; 130 | position: relative; 131 | } 132 | #duel-game-ui .ui-content .nr-txt { 133 | position: absolute; 134 | left: 0px; right: 0px; 135 | top: 0; 136 | text-align: center; 137 | z-index: 0; 138 | } 139 | #duel-game-ui .rounds { 140 | width: 100%; 141 | } 142 | #duel-game-ui .rounds thead { 143 | background-color: #333; 144 | box-shadow: -5px 5px 2px 0 rgba(0,0,0, 0.13); 145 | border: 1px solid rgba(255,255,255, 0.05); 146 | } 147 | #duel-game-ui .rounds thead th { 148 | text-align: center; 149 | font-weight: normal; 150 | padding: 5px; 151 | } 152 | #duel-game-ui .rounds tbody { 153 | border: 1px solid rgba(255,255,255, 0.05); 154 | box-shadow: -5px 5px 2px 0 rgba(0,0,0, 0.13); 155 | } 156 | 157 | #duel-game-ui .ui-info .btn-block { 158 | margin: 0; 159 | } 160 | #duel-game-ui .live-info { 161 | background-color: #333; 162 | box-shadow: -5px 5px 2px 0 rgba(0,0,0, 0.13); 163 | border: 1px solid rgba(255,255,255, 0.05); 164 | border-top: 3px solid #2ecc71; 165 | padding: 0; 166 | margin: 0; 167 | white-space: nowrap; 168 | } 169 | #duel-game-ui .live-info li { 170 | white-space: normal; 171 | width: 25%; 172 | text-align: center; 173 | } 174 | #duel-game-ui .live-info li h4 { 175 | font-weight: normal; 176 | font-size: 1.1em; 177 | } 178 | #duel-game-ui .live-info li > span { 179 | font-weight: normal; 180 | } 181 | #duel-game-ui .live-info img { 182 | margin-right: 5px; 183 | } 184 | #duel-game-ui .live-info .mvp img { 185 | width: 32px; 186 | } 187 | .duel { 188 | background-color: #212121; 189 | margin: 5px; 190 | transition-property: transform; 191 | transition-duration: 1250ms; 192 | transform: scale(0); 193 | } 194 | .duel.in { 195 | transform: scale(1); 196 | } 197 | .duel.out { 198 | transform: scale(0); 199 | } 200 | table.rounds > tbody.own .duel:last-of-type { 201 | border-bottom: 2px solid #c0392b; 202 | } 203 | .duel:last-child { 204 | border-bottom: none; 205 | } 206 | .duel:nth-of-type(even) { 207 | background-color: #333; 208 | } 209 | .duel:nth-of-type(odd) { 210 | background-color: #222; 211 | } 212 | .duel td { 213 | padding: 10px 0; 214 | } 215 | .duel .side { 216 | width: 32px; 217 | padding: 0 17px; 218 | text-align: center; 219 | } 220 | .duel .vs-inline { 221 | display: inline; 222 | padding: 0 10px; 223 | font-weight: bold; 224 | line-height: 40px; 225 | font-size: 0.8em; 226 | opacity: 0.7; 227 | } 228 | .duel .c-profile { 229 | width: 150px; 230 | } 231 | .duel .c-profile-wrp { 232 | position: relative; 233 | color: #fff; 234 | } 235 | .duel .c-profile-wrp:hover, 236 | .duel .c-profile-wrp:active, 237 | .duel .c-profile-wrp:focus { 238 | color: #fff; 239 | } 240 | .duel .c-profile-wrp img { 241 | width: 50px; 242 | } 243 | 244 | .duel .duel-winner.t-side .c-profile-wrp:after { 245 | display: block; 246 | content: ""; 247 | width: 20px; 248 | height: 20px; 249 | position: absolute; 250 | background-image: url("/img/t-side.png"); 251 | background-size: contain; 252 | top: -7px; 253 | left: -7px; 254 | } 255 | .duel .duel-winner.ct-side .c-profile-wrp:after { 256 | display: block; 257 | content: ""; 258 | width: 20px; 259 | height: 20px; 260 | position: absolute; 261 | background-image: url("/img/ct-side.png"); 262 | background-size: contain; 263 | top: -7px; 264 | left: -7px; 265 | } 266 | 267 | .duel .duel-winner .wanim-row { 268 | animation-name: wanimShowY; 269 | animation-iteration-count: 1; 270 | animation-timing-function: ease-out; 271 | animation-duration: 3s; 272 | } 273 | .duel .c-items { 274 | width: 40%; 275 | } 276 | .duel .c-items .item { 277 | min-width: 65px; 278 | width: 10%; 279 | position: relative; 280 | text-align: center; 281 | float: left; 282 | background-color: #1c1c1c; 283 | border: 1px solid rgba(255,255,255, 0.07); 284 | border-top: 3px solid #929191; 285 | box-shadow: -3px 3px 5px 0 rgba(0,0,0, 0.2); 286 | margin-right: 5px; 287 | } 288 | .duel .c-items .item img { 289 | width: 100%; 290 | } 291 | .duel .c-items .item .item-price { 292 | position: absolute; 293 | top: 0; left: 0; 294 | background-color: #111; 295 | font-size: 11px; 296 | padding: 2px 5px; 297 | color: #fff; 298 | } 299 | .duel .items-extra { 300 | line-height: 50px; 301 | padding: 5px; 302 | } 303 | .duel .c-items .item.knife { 304 | border-top: 3px solid #8650AC; 305 | } 306 | .duel .c-items .item.knife img { 307 | filter: drop-shadow(0px 0px 10px #8650AC); 308 | } 309 | .duel .c-items .item.stattrak { 310 | border-top: 3px solid #CF6A32; 311 | } 312 | .duel .c-items .item.stattrak img { 313 | filter: drop-shadow(0px 0px 10px #CF6A32); 314 | } 315 | .duel .c-items .item.hightier { 316 | border-top: 3px solid #c7413b; 317 | } 318 | .duel .c-items .item.hightier img { 319 | filter: drop-shadow(0px 0px 10px #c7413b); 320 | } 321 | 322 | .duel .duel-j { 323 | text-align: center; 324 | width: 12%; 325 | padding: 0; 326 | margin: 0; 327 | } 328 | .duel .duel-info { 329 | text-align: center; 330 | } 331 | .duel .duel-status { 332 | width: 10%; 333 | text-align: center; 334 | } 335 | .duel .duel-winner { 336 | position: relative; 337 | width: 35px; 338 | height: 35px; 339 | margin: 0 auto; 340 | } 341 | .duel .duel-winner img { 342 | position: absolute; 343 | left: 0; 344 | overflow: hidden; 345 | width: 35px; 346 | /*box-shadow: 0 0 10px 5px rgba(0, 255, 0, 0.3);*/ 347 | } 348 | 349 | .duel-details { 350 | position: relative; 351 | } 352 | .duel-details .dt-status { 353 | position: absolute; 354 | top: 25px; left: 0; right: 0; 355 | text-align: center; 356 | } 357 | .duel-details .profile { 358 | text-align: center; 359 | display: block; 360 | margin-bottom: 30px; 361 | position: relative; 362 | color: #fff; 363 | } 364 | .duel-details .profile:hover, 365 | .duel-details .profile:focus, 366 | .duel-details .profile:active { 367 | color: #fff; 368 | text-decoration: none; 369 | } 370 | .duel-details .profile .img-wrp { 371 | position: relative; 372 | display: inline-block; 373 | padding-bottom: 1px; 374 | } 375 | .duel-details .profile.ctside .img-wrp:after { 376 | display: block; 377 | content: ""; 378 | width: 30px; 379 | height: 30px; 380 | position: absolute; 381 | background-image: url("/img/ct-side.png"); 382 | background-size: contain; 383 | top: -12px; 384 | left: -12px; 385 | } 386 | .duel-details .profile.tside .img-wrp:after { 387 | display: block; 388 | content: ""; 389 | width: 30px; 390 | height: 30px; 391 | position: absolute; 392 | background-image: url("/img/t-side.png"); 393 | background-size: contain; 394 | top: -12px; 395 | left: -12px; 396 | } 397 | .duel-details .profile .img-wrp .p-level { 398 | position: absolute; 399 | bottom: 0px; 400 | width: 100%; 401 | background-color: #888; 402 | text-align: center; 403 | font-size: 11px; 404 | border: 1px solid black; 405 | border-radius: 0 0 5px 5px; 406 | } 407 | .duel-details .items { 408 | margin: 10px 0 0 0; 409 | padding: 5px; 410 | border-radius: 5px; 411 | background-color: #333; 412 | } 413 | .duel-details .items li { 414 | list-style-type: none; 415 | border-bottom: 1px solid #444; 416 | padding: 3px 0; 417 | } 418 | .duel-details .items li:last-child { 419 | border: 0; 420 | } 421 | .duel-details .items li:after { 422 | content: ""; 423 | display: block; 424 | clear: left; 425 | } 426 | .duel-details .items .row-item img { 427 | width: 90px; 428 | margin-right: 10px; 429 | display: block; 430 | float: left; 431 | } 432 | .duel-details .items .row-item .item-detail { 433 | width: 175px; 434 | float: left; 435 | } 436 | .duel-details .items .row-item .item-name { 437 | padding: 10px; 438 | } 439 | .duel-details .items .row-item .item-price { 440 | padding: 10px; 441 | } 442 | .duel-details .items p.total { 443 | border-bottom: 2px solid #444; 444 | } 445 | .duel-details .won .profile img { 446 | transition-property: box-shadow; 447 | transition-duration: 200ms; 448 | box-shadow: 0 0 25px 5px rgba(0, 255, 0, 0.5); 449 | } 450 | .duel-details .won .profile:after { 451 | content: "Winner!"; 452 | color: #0F0; 453 | font-weight: bold; 454 | text-align: center; 455 | left: 0; right: 0; 456 | bottom: -25px; 457 | position: absolute; 458 | } 459 | .duel-details .lost { 460 | transition-property: all; 461 | transition-duration: 200ms; 462 | opacity: 0.3; 463 | } 464 | .duel-details .lost .profile img { 465 | box-shadow: 0 0 25px 5px rgba(255, 0, 0, 0.5); 466 | } 467 | 468 | .duel-details .s-hash { 469 | margin: 0; 470 | margin-top: 10px; 471 | font-size: 12px; 472 | opacity: 0.3; 473 | text-align: center; 474 | } 475 | 476 | .duel-details .duel-status { 477 | position: relative; 478 | margin: 0 auto; 479 | width: 64px; 480 | height: 64px; 481 | } 482 | .duel-details .duel-status .wanim { 483 | position: relative; 484 | width: 60px; 485 | height: 60px; 486 | 487 | animation-name: wanimShow; 488 | animation-iteration-count: 1; 489 | animation-timing-function: ease-out; 490 | animation-duration: 4s; 491 | 492 | transform-style: preserve-3d; 493 | } 494 | .duel-details .duel-status .wanim-t { 495 | position: absolute; 496 | top: 0; left: 0; 497 | width: 60px; height: 60px; 498 | 499 | background-image: url("/img/t-side.png"); 500 | background-size: contain; 501 | 502 | z-index: 10; 503 | backface-visibility: hidden; 504 | -webkit-backface-visibility: hidden; 505 | } 506 | .duel-details .duel-status .wanim-t::after { 507 | content: ""; 508 | position: absolute; 509 | top: 0; left: 0; 510 | width: 60px; height: 60px; 511 | 512 | background-image: url("/img/ct-side.png"); 513 | background-size: contain; 514 | transform: rotateX(180deg); 515 | 516 | backface-visibility: hidden; 517 | -webkit-backface-visibility: hidden; 518 | } 519 | .duel-details .duel-status .wanim-ct { 520 | position: absolute; 521 | top: 0; left: 0; 522 | background-image: url("/img/ct-side.png"); 523 | background-size: contain; 524 | 525 | z-index: 10; 526 | backface-visibility: hidden; 527 | -webkit-backface-visibility: hidden; 528 | } 529 | .duel-details .duel-status .wanim-ct::after { 530 | content: ""; 531 | position: absolute; 532 | top: 0; left: 0; 533 | width: 60px; height: 60px; 534 | background-image: url("/img/t-side.png"); 535 | background-size: contain; 536 | transform: rotateX(180deg); 537 | 538 | backface-visibility: hidden; 539 | -webkit-backface-visibility: hidden; 540 | } 541 | .duel-details .hide-items .row-item { 542 | display: none; 543 | } 544 | .duel-details .hide-items li.text-center { 545 | display: block; 546 | } 547 | .duel-details .hide-items:after { 548 | content: "Joining..."; 549 | display: block; 550 | text-align: center; 551 | margin: 10px; 552 | } 553 | 554 | .h-ctn { 555 | height: 500px; 556 | overflow: auto; 557 | } 558 | .duel-history { 559 | width: 100%; 560 | } 561 | .duel-history td img { 562 | width: 40px; 563 | margin-right: 10px; 564 | box-shadow: -2px 2px 10px 0px rgba(0,0,0, 0.23); 565 | } 566 | .duel-history td { 567 | padding: 10px; 568 | } 569 | 570 | .duel-history .no-shadow { 571 | box-shadow: none; 572 | } 573 | 574 | .duel-details-winner { 575 | box-shadow: 0 0 15px 1px #2ecc71; 576 | margin-right: 10px; 577 | } 578 | 579 | @keyframes wanimShow { 580 | 0% { 581 | transform: scale(0.1) rotateX(1440deg); 582 | } 583 | 75% { 584 | transform: scale(1.5) rotateX(0); 585 | } 586 | 100% { 587 | transform: scale(1) rotateX(0); 588 | } 589 | } 590 | @keyframes wanimShowY { 591 | 0% { 592 | transform: scale(0.1) rotateY(720deg); 593 | } 594 | 75% { 595 | transform: scale(1.5) rotateY(0); 596 | } 597 | 100% { 598 | transform: scale(1) rotateY(0); 599 | } 600 | } 601 | 602 | 603 | @media screen and (min-width: 650px) { 604 | .duel .sw-items { 605 | display: none; 606 | } 607 | .duel .fw-items .items-extra { 608 | display: inline-block; 609 | width: 110px; 610 | } 611 | } 612 | @media screen and (max-width: 649px) { 613 | .duel .fw-items { 614 | display: none; 615 | } 616 | .duel .sw-items { 617 | text-align: center; 618 | } 619 | .duel .sw-items .items-extra { 620 | margin: auto; 621 | 622 | width: 85px; 623 | position: relative; 624 | text-align: center; 625 | 626 | background-color: #1c1c1c; 627 | border: 1px solid rgba(255,255,255, 0.07); 628 | border-top: 3px solid #929191; 629 | box-shadow: -3px 3px 5px 0 rgba(0,0,0, 0.2); 630 | } 631 | } -------------------------------------------------------------------------------- /public/js/chat.js: -------------------------------------------------------------------------------- 1 | var emots = [ 2 | { 3 | src: "/img/emots/fail.png", 4 | txt: ":fail:" 5 | }, 6 | { 7 | src: "/img/emots/hs.png", 8 | txt: ":hs:" 9 | }, 10 | { 11 | src: "/img/emots/rekt.png", 12 | txt: ":rekt:" 13 | }, 14 | { 15 | src: "/img/emots/dead.png", 16 | txt: ":dead:" 17 | }, 18 | { 19 | src: "/img/emots/lol.png", 20 | txt: ":lol:" 21 | }, 22 | { 23 | src: "/img/emots/gg.png", 24 | txt: ":gg:" 25 | }, 26 | { 27 | src: "/img/emots/rip.png", 28 | txt: ":rip:" 29 | }, 30 | { 31 | src: "/img/emots/duck.png", 32 | txt: ":duck:" 33 | }, 34 | { 35 | src: "/img/emots/lucky.png", 36 | txt: ":lucky:" 37 | }, 38 | { 39 | src: "/img/emots/dollars.png", 40 | txt: ":dollars:" 41 | }, { 42 | src: "/img/emots/chan.png", 43 | txt: ":chan:" 44 | }, 45 | { 46 | src: "/img/emots/salt.png", 47 | txt: ":salt:" 48 | }, 49 | { 50 | src: "/img/emots/facepalm.png", 51 | txt: ":facepalm:" 52 | }, 53 | { 54 | src: "/img/emots/kappa.png", 55 | txt: ":kappa:" 56 | }, 57 | { 58 | src: "/img/emots/illuminati.png", 59 | txt: ":illuminati:" 60 | } 61 | ]; 62 | String.prototype.replaceAll = function (str1, str2, ignore) { 63 | return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g, "\\$&"), (ignore ? "gi" : "g")), (typeof(str2) == "string") ? str2.replace(/\$/g, "$$$$") : str2); 64 | }; 65 | 66 | function sendMsg() { 67 | var cin = $("#chatInput"); 68 | var msg = cin.val(); 69 | if (!msg) return; 70 | if (msg.charAt(0) === "/") { 71 | var data = msg.substr(1, msg.length - 1).split(' '); 72 | switch (data[0]) { 73 | case'help': 74 | pushChatNotification("Available commands are: mute [steamid] / unmute [steamid]"); 75 | break; 76 | case'mute': 77 | if (!data[1]) return pushChatNotification("Usage: /mute STEAM_ID"); 78 | var sid = data[1].trim(); 79 | if (window.localStorage) { 80 | var ig = JSON.parse(localStorage.getItem("ignore_list")) || []; 81 | ig.push(sid); 82 | localStorage.setItem("ignore_list", JSON.stringify(ig)); 83 | pushChatNotification("Message from steam ID " + sid + " will be ignored."); 84 | } 85 | break; 86 | case'unmute': 87 | if (!data[1]) return pushChatNotification("Usage: /unmute STEAM_ID"); 88 | var sid = data[1].trim(); 89 | if (window.localStorage) { 90 | var ig = JSON.parse(localStorage.getItem("ignore_list")) || []; 91 | var idx = ig.indexOf(sid); 92 | if (idx !== -1) { 93 | ig.splice(idx, 1); 94 | localStorage.setItem("ignore_list", JSON.stringify(ig)); 95 | pushChatNotification("ID " + data[1] + " removed from ignore list."); 96 | } 97 | else pushChatNotification(sid + " is not in your ignore list."); 98 | } 99 | break; 100 | default: 101 | pushChatNotification("Unknown command, type /help for more informations"); 102 | } 103 | cin.val(""); 104 | return; 105 | } 106 | window.chatIO.emit("sendMessage", {message: msg}); 107 | cin.val(""); 108 | } 109 | 110 | function pushChatNotification(message) { 111 | var cMsg = $("#chatMessages"); 112 | var s = document.createElement("div"); 113 | s.setAttribute("class", "alert alert-info"); 114 | s.innerHTML = message; 115 | cMsg.append(s); 116 | scrollChat(); 117 | } 118 | 119 | function fMessage(elem) { 120 | var message = elem.find(".chat-content"); 121 | for (var t in emots) { 122 | if (message.html().indexOf(emots[t].txt) !== -1) { 123 | var img = ''; 124 | var html = message.html(); 125 | var fstr = html.replaceAll(emots[t].txt, img); 126 | message.html(fstr); 127 | } 128 | } 129 | var str_trim; 130 | var str = message.html(); 131 | str = str.replace(/ +(?= )/g, ''); 132 | str = str.replace(/(https:\/\/|http:\/\/)steamcommunity\.com\/tradeoffer\/new\/\?partner=\d+\&token=[a-zA-Z0-9._-]+/ig, "❤"); 133 | str = str.replace(/(?!csgohunt\.com)csgo\w*\.\w*|csgo\w*\. \w*|csgo\w* \.\w*|csgo\w* \. \w*|csgo-\w*\.\w*|skinarena|failsnipe|CSGET|reaper|bubble|csgobubble|r3aper|re4per|reap3r|csgowild|skinsbit|csgomaya|csgoreaper|casesfarm|СSВООМ|csgoshuffle|Snakego|winpot|w i n p o t|boom|skinsrate|skins-case|CS B OO M|ezskins\.com|loveskins\.gq|skinrush\.net|csgorigged\!com|skinsgambling\.com|www\.dotajackpots\.com/ig, "❤"); 134 | str_trim = str.replace(/\s/g, ''); 135 | str_trim = str_trim.replace(/[\u0080-\u00ff]/g, ''); 136 | if (/csget.*?org/ig.test(str_trim)) { 137 | str = "❤"; 138 | } 139 | if (/csgo.*?reaper/ig.test(str_trim)) { 140 | str = "❤"; 141 | } 142 | if (/http.*?deposit/ig.test(str_trim)) { 143 | str = "❤"; 144 | } 145 | if (/http.*?free/ig.test(str_trim)) { 146 | str = "❤"; 147 | } 148 | if (/http.*?dollar/ig.test(str_trim)) { 149 | str = "❤"; 150 | } 151 | if (/click.*?name/ig.test(str_trim)) { 152 | str = "❤"; 153 | } 154 | if (/click.*?profile/ig.test(str_trim)) { 155 | str = "❤"; 156 | } 157 | if (/goo.gl/ig.test(str_trim)) { 158 | str = "❤"; 159 | } 160 | if (/bit.ly/ig.test(str_trim)) { 161 | str = "❤"; 162 | } 163 | if (/reaper/ig.test(str_trim)) { 164 | str = "❤"; 165 | } 166 | if (/bubble/ig.test(str_trim)) { 167 | str = "❤"; 168 | } 169 | if (/shuffle.*?code|code.*?shuffle/ig.test(str)) 170 | str = "❤"; 171 | if (/boom.*?code|code.*?boom/ig.test(str)) 172 | str = "❤"; 173 | if (/hunt.*?info/ig.test(str)) 174 | str = "❤"; 175 | if (/cs.*?com/ig.test(str)) 176 | str = "❤"; 177 | if (/cs.*?B OO M/ig.test(str)) 178 | str = "❤"; 179 | if (/c s.*?B OO M/ig.test(str)) 180 | str = "❤"; 181 | if (/cs.*?B O O M/ig.test(str)) 182 | str = "❤"; 183 | if (/c s.*?B O O M/ig.test(str)) 184 | str = "❤"; 185 | if (/c s.*?B OO M/ig.test(str)) 186 | str = "❤"; 187 | if (/POT.*?p w/ig.test(str)) 188 | str = "❤"; 189 | if (/bit.*com/ig.test(str)) 190 | str = "❤"; 191 | if (/PO T.*?pw/ig.test(str)) 192 | str = "❤"; 193 | if (/get.*?org/ig.test(str)) 194 | str = "❤"; 195 | if (/cs.*?deposit/ig.test(str)) 196 | str = "❤"; 197 | if (/free.*?code/ig.test(str)) 198 | str = "❤"; 199 | if (/org.*?free/ig.test(str)) 200 | str = "❤"; 201 | if (/org.*deposit/ig.test(str)) 202 | str = "❤"; 203 | if (/c s.*free/ig.test(str)) 204 | str = "❤"; 205 | if (/cs.*free/ig.test(str)) 206 | str = "❤"; 207 | if (/bet.*?anomaly/ig.test(str)) 208 | str = "❤"; 209 | if (/P O T.*?pw/ig.test(str)) 210 | str = "❤"; 211 | if (/P O T.*?p w/ig.test(str)) 212 | str = "❤"; 213 | if (/PO T.*?p w/ig.test(str)) 214 | str = "❤"; 215 | if (/POT.*?p w/ig.test(str)) 216 | str = "❤"; 217 | if (/cases.*?com/ig.test(str)) 218 | str = "❤"; 219 | if (/case.*?com/ig.test(str)) 220 | str = "❤"; 221 | if (/csgo.*?wild/ig.test(str)) 222 | str = "❤"; 223 | if (/maya.*?com/ig.test(str)) 224 | str = "❤"; 225 | if (/maya.*?con/ig.test(str)) 226 | str = "❤"; 227 | if (/farm.*?com/ig.test(str)) 228 | str = "❤"; 229 | if (/POT.*?pw/ig.test(str)) 230 | str = "❤"; 231 | if (/P OT.*?pw/ig.test(str)) 232 | str = "❤"; 233 | if (/skins.*?promo/ig.test(str)) 234 | str = "❤"; 235 | if (/skins.*?case/ig.test(str)) 236 | str = "❤"; 237 | if (/skins.*?code/ig.test(str)) 238 | str = "❤"; 239 | if (/case.*?code/ig.test(str)) 240 | str = "❤"; 241 | if (/.com.*?code/ig.test(str)) 242 | str = "❤"; 243 | if (/B.O.O.M|B.O.OM|BO.OM|BOO.M|B.OOM/ig.test(str)) 244 | str = "❤"; 245 | if (/coinz.*?code|code.*?coinz/ig.test(str)) 246 | str = "❤"; 247 | if (/cs.*?code|code.*?cs/ig.test(str)) 248 | str = "❤"; 249 | if (/free.*?code|code.*?free/ig.test(str)) 250 | str = "❤"; 251 | if (/csgoshuffle.*?code|code.*?csgoshuffle/ig.test(str)) 252 | str = "❤"; 253 | if (/csgo.*?fight/ig.test(str)) 254 | str = "❤"; 255 | if (/new lottery/ig.test(str)) 256 | str = "❤"; 257 | if (/csgodouble/ig.test(str)) 258 | str = "❤"; 259 | if (/csgomode|csgo mode|\*LOWPOTS\*|0.10\$ Min Deposit|Jackpot Deisgn|VD8die5Ka5|New Awesome|ZeoASNTThF|jackpot design/ig.test(str)) 260 | str = "❤"; 261 | if (/unusual design|Min \$ 0\.1|Fight for skins|csgobull|Consumer Grade|Dreaming of expensive/ig.test(str)) 262 | str = "❤"; 263 | if (/msi-esl|msi -esl|msi- esl|msi - esl/ig.test(str)) 264 | str = "❤"; 265 | if (/csgorides/ig.test(str)) 266 | str = "❤"; 267 | if (/csgoskinswin/ig.test(str)) 268 | str = "❤"; 269 | if (/csgo.*?rides/ig.test(str)) 270 | str = "❤"; 271 | if (/csgo.*?ride/ig.test(str)) 272 | str = "❤"; 273 | if (/csgored/ig.test(str)) 274 | str = "❤"; 275 | if (/\(\.\)/ig.test(str)) 276 | str = "❤"; 277 | if (/get free/ig.test(str)) 278 | str = "❤"; 279 | if (/min deposit/ig.test(str)) 280 | str = "❤"; 281 | if (/csgo.*? com/ig.test(str)) 282 | str = "❤"; 283 | if (/http:\/\//ig.test(str)) 284 | str = "❤"; 285 | if (/fair new/ig.test(str)) 286 | str = "❤"; 287 | if (/site :\)/ig.test(str)) 288 | str = "❤"; 289 | if (/dune10/ig.test(str)) 290 | str = "❤"; 291 | if (/5\$ enjoy/ig.test(str)) 292 | str = "❤"; 293 | if (/! @@/ig.test(str)) 294 | str = "❤"; 295 | if (/remove spaces/ig.test(str)) 296 | str = "❤"; 297 | if (/CS BO OM/ig.test(str)) 298 | str = "❤"; 299 | if (/minimal deposit/ig.test(str)) 300 | str = "❤"; 301 | if (/new site/ig.test(str)) 302 | str = "❤"; 303 | if (/skins2\.com/ig.test(str)) 304 | str = "❤"; 305 | if (/skins2.*?com/ig.test(str)) 306 | str = "❤"; 307 | if (/skinsrumble.*?com/ig.test(str)) 308 | str = "❤"; 309 | if (/(?=.*C\s*S\s*|.*C\s*.\s*S\s*)(?=.*B\s*.\s*O\s*.\s*O\s*.M\s*|.*B\s*.\s*O\s*.\s*O\s*M\s*|.*B\s*O\s*\s*O\s*M\s*|.*B\s*O\s*O\s*.\s*M\s*|.*B\s*.\s*O\s*O\s*M\s*|.*B\s*O\s*O\s*M\s*|.*B\s*O\s*.\s*O\s*O\s*M|.*BOO.M|.*B\s*O\s*.\s*O\s*M\s*).*/ig.test(str)) 310 | str = "❤"; 311 | if (/[СВМℂṦᏰΌʍÇŜ฿ΘḾ]/ig.test(str)) 312 | str = "❤"; 313 | if (/[\u2E80-\u2FD5\u3400-\u4DBF\u4E00-\u9FCC]/.test(str)) { 314 | str = str; 315 | } 316 | else if (/[\u0250-\ue007]/ig.test(str)) { 317 | str = "❤"; 318 | } 319 | str = str.replace(/shuffle/ig, "❤"); 320 | message.html(str); 321 | } 322 | 323 | function scrollChat() { 324 | var elem = document.getElementById("chatMessages"); 325 | elem.scrollTop = elem.scrollHeight; 326 | } 327 | 328 | function renderMsg(data) { 329 | var src = $("#chat-message-tpl").html(); 330 | var tpl = Handlebars.compile(src); 331 | var tmp_name; 332 | data.from.displayName = data.from.displayName.replace(/(?!csgohunt\.com)csgo\w*\.\w*|csgo-\w*\.\w*|skinarena|failsnipe|reaper|boom|winpot|win pot|CSGOCrystal|skinsrate|ezskins\.com|loveskins\.gq|skinrush\.net|csgoraffle\.pl|csgorigged\!com|skinsgambling\.com|www\.dotajackpots\.com|csgoduck|c s g o r e d \. c o m|SkinsGambling\.com|kickback\.com|winaskin\.com/ig, "❤"); 333 | if (/sanehouse/ig.test(data.from.displayName)) 334 | data.message = "❤"; 335 | if (/POT.*?p w|PO T.*?pw|P O T.*?pw|P O T.*?p w|PO T.*?p w|POT.*?pw|P OT.*pw/ig.test(data.from.displayName)) 336 | data.message = "❤"; 337 | if (/cs.*?B OO M|c s.*?B OO M|cs.*?B O O M|c s.*?B O O M|c s.*?B OO M|cs.*bo om|csgohunt.*info|csgo hunt.*info/ig.test(data.from.displayName)) 338 | data.message = "❤"; 339 | if (/PUBGCasino/ig.test(data.from.displayName)) 340 | data.message = "❤"; 341 | tmp_name = data.from.displayName.replace(/\s/g, ''); 342 | if (/csget.*?org/ig.test(tmp_name)) { 343 | data.message = "❤"; 344 | data.from.displayName = "❤"; 345 | } 346 | var cMsg = $("#chatMessages"); 347 | var html = $(tpl(data)); 348 | if (data.privilege == "admin") 349 | html.find(".chat-user").attr("style", "color: #C9302C;"); 350 | fMessage(html); 351 | cMsg.append(html); 352 | } 353 | 354 | function appendSmiley(elm) { 355 | var cin = $("#chatInput"); 356 | cin.val(cin.val() + $(elm).data("txt")); 357 | } 358 | 359 | function showRules() { 360 | var src = $(".confirm-modal").html(); 361 | var tpl = Handlebars.compile(src); 362 | var modal = $(tpl()); 363 | modal.find("h4").html("Chat rules"); 364 | modal.find(".modal-body").append("

Not respecting the following rules will get you banned permanently.

  • No begging
  • No advertising/links
  • No spamming
"); 365 | modal.find(".modal-footer .btn-cancel").html("Close"); 366 | modal.find(".modal-footer .btn-confirm").remove(); 367 | modal.modal('show'); 368 | modal.on("hidden.bs.modal", function (e) { 369 | modal.remove(); 370 | }); 371 | } 372 | 373 | $(function () { 374 | var cw = $("#chatWindow"); 375 | var cbtn = $("#chatBtn"); 376 | $(cbtn).on("click", function (e) { 377 | cw.show(); 378 | cbtn.hide(); 379 | scrollChat(); 380 | }); 381 | $("#chatSendBtn").on("click", function (e) { 382 | sendMsg(); 383 | }); 384 | $('#chatInput').keypress(function (evt) { 385 | if (evt.which === 13) 386 | sendMsg(); 387 | }); 388 | var ems = '
'; 389 | for (var i = 0; i < emots.length; i++) { 390 | var em = emots[i]; 391 | ems += ''; 392 | } 393 | ems += "
"; 394 | $('#chatEmotsBtn').popover({content: ems, placement: "top", html: true}); 395 | var socket = window.chatIO = io(window.Application.sockets.chat); 396 | var cStatus = $("#chatStatus"); 397 | socket.on("connected", function (data) { 398 | var cMsg = $("#chatMessages").html(''); 399 | cStatus.attr("class", "online"); 400 | cStatus.find(".statusLine").html("Online"); 401 | for (var k in data) { 402 | renderMsg(data[k]); 403 | } 404 | scrollChat(); 405 | var uData = window.Application; 406 | if (uData.stk) socket.emit("authenticate", {stk: uData.stk, sid: uData.sid}); 407 | }); 408 | socket.on('connect_error', function () { 409 | cStatus.attr("class", "offline"); 410 | cStatus.find(".statusLine").html("Offline"); 411 | }); 412 | socket.on('reconnect_failed', function () { 413 | cStatus.attr("class", "offline"); 414 | cStatus.find(".statusLine").html("Offline"); 415 | }); 416 | socket.on('chatMsg', function (data) { 417 | if (window.localStorage) { 418 | var s = localStorage.getItem("ignore_list"); 419 | s = s ? JSON.parse(s) : []; 420 | if (s.indexOf(data.from.steamID) !== -1) return; 421 | } 422 | renderMsg(data); 423 | scrollChat(); 424 | }); 425 | socket.on("userCount", function (count) { 426 | cStatus.find(".statusLine").html("Online - " + count); 427 | }); 428 | socket.on('chatBroadcast', function (data) { 429 | var cMsg = $("#chatMessages"); 430 | var s = document.createElement("div"); 431 | s.setAttribute("class", "alert alert-info"); 432 | s.innerHTML = data.message; 433 | cMsg.append(s); 434 | scrollChat(); 435 | }); 436 | socket.on('chatError', function (data) { 437 | var cMsg = $("#chatMessages"); 438 | var s = document.createElement("div"); 439 | s.setAttribute("class", "alert alert-danger"); 440 | s.innerHTML = data.message; 441 | cMsg.append(s); 442 | scrollChat(); 443 | }); 444 | }); --------------------------------------------------------------------------------