├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .whitesource
├── LICENSE
├── README.md
├── example.env
├── index.js
├── package-lock.json
├── package.json
└── src
├── classes
├── App.js
└── Router.js
├── database
├── mongo.js
├── redis.js
└── setup.sql
├── public
├── css
│ ├── bootstrap.min.css
│ ├── fontawesome-min.css
│ └── style.css
├── js
│ ├── bots.js
│ ├── home.js
│ ├── rooms.js
│ ├── statistics.js
│ └── utils.js
└── static
│ └── moving-bg.png
├── routes
└── api.js
├── util
├── Logger.js
├── connect.js
├── cron.js
└── stats.js
└── views
├── Components
├── Footer.ejs
├── Head.ejs
├── Navbar.ejs
└── Noscript.ejs
├── bots.ejs
├── index.ejs
├── rooms.ejs
└── scheduled.ejs
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Production Deploy
2 | on:
3 | push:
4 | branches:
5 | - master
6 | jobs:
7 |
8 | build:
9 | name: Build
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: executing remote ssh commands using password
13 | uses: appleboy/ssh-action@master
14 | with:
15 | host: ${{ secrets.IP }}
16 | username: ${{ secrets.USER }}
17 | password: ${{ secrets.PRIVATE_KEY }}
18 | port: ${{ secrets.PORT }}
19 | script: |
20 | nvm use 14
21 | cd /opt/dogetracker
22 | git fetch --all
23 | git reset --hard origin/master
24 | npm install --save
25 | pm2 restart dogehouse-stats
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # Snowpack dependency directory (https://snowpack.dev/)
45 | web_modules/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 | .parcel-cache
78 |
79 | # Next.js build output
80 | .next
81 | out
82 |
83 | # Nuxt.js build / generate output
84 | .nuxt
85 | dist
86 |
87 | # Gatsby files
88 | .cache/
89 | # Comment in the public line in if your project uses Gatsby and not Next.js
90 | # https://nextjs.org/blog/next-9-1#public-directory-support
91 | # public
92 |
93 | # vuepress build output
94 | .vuepress/dist
95 |
96 | # Serverless directories
97 | .serverless/
98 |
99 | # FuseBox cache
100 | .fusebox/
101 |
102 | # DynamoDB Local files
103 | .dynamodb/
104 |
105 | # TernJS port file
106 | .tern-port
107 |
108 | # Stores VSCode versions used for testing VSCode extensions
109 | .vscode-test
110 |
111 | # yarn v2
112 | .yarn/cache
113 | .yarn/unplugged
114 | .yarn/build-state.yml
115 | .yarn/install-state.gz
116 | .pnp.*
--------------------------------------------------------------------------------
/.whitesource:
--------------------------------------------------------------------------------
1 | {
2 | "scanSettings": {
3 | "baseBranches": []
4 | },
5 | "checkRunSettings": {
6 | "vulnerableCheckRunConclusionLevel": "failure",
7 | "displayMode": "diff"
8 | },
9 | "issueSettings": {
10 | "minSeverityLevel": "LOW"
11 | }
12 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | An open-source website for viewing, managing and exploring statistics and historical data from DogeHouse 🐶
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
23 |
24 | ---
25 |
26 | ## Installation
27 |
28 | - You need a MySQL Server to run the /src/database/setup.sql database setup.
29 |
30 | 1. git clone https://github.com/dogegarden/dogetracker
31 | 2. Install [NodeJS](https://nodejs.org/en/) and [NPM](https://www.npmjs.com/).
32 | 3. Install depends: `npm install --save`
33 | 4. Fill out example.env
34 | 5. Run index.js
35 |
--------------------------------------------------------------------------------
/example.env:
--------------------------------------------------------------------------------
1 | MYSQL_HOST=localhost
2 | MYSQL_PORT=3306
3 | MYSQL_USER=root
4 | MYSQL_PASS=
5 | MYSQL_DB=dogestats
6 | HOST_URL=https://stats.dogegarden.net
7 |
8 | EXPRESS_PORT=7000
9 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const App = new (require('./src/classes/App'));
2 | const Logger = require('./src/util/Logger');
3 | const Cron = require('./src/util/cron');
4 |
5 | (async function () {
6 | await App.registerRoutes();
7 | await App.listen(() => {
8 | Logger.info(`Express started on on port ${process.env.EXPRESS_PORT}`);
9 | }, true);
10 | Cron.setCron();
11 | // Cron.autoRunMYSQL();
12 | })()
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dogegarden-stats",
3 | "version": "1.0.6",
4 | "description": "An open-source site to provide data and statistics about dogehouse.tv.",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "nodemon index.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "axios": "^0.21.1",
14 | "chalk": "^4.1.0",
15 | "dateformat": "^4.5.1",
16 | "ejs": "^3.1.6",
17 | "express": "^4.17.1",
18 | "fs": "0.0.1-security",
19 | "ioredis": "^4.24.2",
20 | "monk": "^7.3.3",
21 | "mysql": "^2.18.1",
22 | "node-cron": "^3.0.0",
23 | "util": "^0.12.3"
24 | },
25 | "devDependencies": {
26 | "body-parser": "^1.19.0",
27 | "cookie-parser": "^1.4.5",
28 | "cors": "^2.8.5",
29 | "dotenv": "^8.2.0",
30 | "nodemon": "^2.0.7",
31 | "path": "^0.12.7"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/classes/App.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const cors = require('cors')
3 | const cookieParser = require('cookie-parser')
4 | const bodyParser = require('body-parser')
5 | const path = require('path')
6 | const Router = require('./Router');
7 | const Logger = require('../util/Logger')
8 | const fs = require('fs').promises;
9 | const axios = require('axios')
10 | require('dotenv').config()
11 |
12 |
13 | class App {
14 | server;
15 | constructor() {
16 | this.app = express()
17 | this.server = require('http').createServer(this.app)
18 | this.app.engine('e', require('ejs').renderFile)
19 | this.app.set('view engine', 'ejs')
20 | this.app.set('views', path.join(__dirname, '..', 'views'))
21 | this.app.use(cors({
22 | origin: "*"
23 | }));
24 | this.app.use('/public', express.static(path.join(__dirname, '..', 'public')));
25 | this.app.use(cookieParser());
26 | this.app.use(express.json())
27 | this.app.use(express.urlencoded({ extended: true }))
28 | }
29 | /**
30 | * @param {express.Request} req
31 | * @param {express.Response} res
32 | * @param {function()} next
33 | */
34 |
35 | async registerRoutes() {
36 |
37 | const filePath = path.join(__dirname, '..', 'routes');
38 | const files = await fs.readdir(filePath);
39 | for await (const file of files) {
40 | if (file.endsWith('.js')) {
41 | const router = require(path.join(filePath, file));
42 | if (router.prototype instanceof Router) {
43 | const instance = new router(this);
44 | Logger.route(`Route File ${instance.path} running.`);
45 | if (instance.auth) {
46 | this.app.use(instance.path, this.Authentication, instance.createRoute());
47 | } else {
48 | this.app.use(instance.path, instance.createRoute());
49 | }
50 | }
51 | }
52 | }
53 |
54 | // routes below...
55 |
56 | let packageConf = require('../../package.json')
57 |
58 | this.app.get('/', function (req, res) {
59 | res.render('index.ejs', {
60 | path: req.path,
61 | version: packageConf.version
62 | })
63 | })
64 |
65 | // this.app.get('/bots', function(req, res) {
66 | // res.render('bots.ejs', {
67 | // path: req.path,
68 | // })
69 | // })
70 |
71 | this.app.get('/rooms', async function (req, res) {
72 | try {
73 | const rooms = await axios.get('https://api.dogegarden.net/v1/popularRooms');
74 | console.log(rooms.data.rooms.rooms)
75 | res.render('rooms.ejs', {
76 | path: req.path,
77 | rooms: rooms.data.rooms.rooms
78 | })
79 | } catch (err) {
80 | console.log(err)
81 | }
82 | })
83 |
84 | // this.app.get('/scheduled', async function(req, res) {
85 | // try {
86 | // const rooms = await axios.get('https://api.dogegarden.net/v1/scheduledRooms');
87 | // console.log(rooms.data.rooms)
88 | // res.render('scheduled.ejs', {
89 | // path: req.path,
90 | // rooms: rooms.data.rooms
91 | // })
92 | // } catch(err) {
93 | // console.log(err)
94 | // }
95 | // })
96 |
97 | // this.app.get('/statistics', async function(req, res) {
98 | // res.render('statistics.ejs', {
99 | // path: req.path
100 | // })
101 | // })
102 |
103 | // this.app.get('/bots', function(req, res) {
104 | // res.render('bots.ejs', {
105 | // path: req.path,
106 | // })
107 | // })
108 |
109 | } // end listener
110 |
111 |
112 |
113 | async listen(fn) {
114 | this.server.listen(process.env.EXPRESS_PORT, fn);
115 | }
116 |
117 | }
118 |
119 | module.exports = App;
--------------------------------------------------------------------------------
/src/classes/Router.js:
--------------------------------------------------------------------------------
1 | const Route = require('express').Router;
2 | class Router {
3 | /**
4 | *
5 | * @param {import('./App')} client
6 | * @param {string} path
7 | * @param {boolean} auth
8 | */
9 | constructor(client, path, auth = false) {
10 | this.client = client;
11 | this.path = path;
12 | this.auth = auth;
13 | this.router = Route();
14 | }
15 | createRoute() {
16 | return this.router;
17 | }
18 | }
19 | module.exports = Router;
20 |
--------------------------------------------------------------------------------
/src/database/mongo.js:
--------------------------------------------------------------------------------
1 | const monk = require('monk');
2 | const Logger = require('../util/Logger')
3 | require('dotenv').config()
4 | const db = monk(process.env.MONGO_URL);
5 | Logger.mongo('Connected to mongodb.')
6 |
7 | class Calls {
8 | static async getUser(id) {
9 | const collection = db.get('users')
10 | return (await collection.findOne({ user_id: id }))
11 | }
12 |
13 | static async addRoom(room) {
14 | const collection = db.get('rooms')
15 | return (await collection.insert({
16 | room_id: room.id,
17 | room_name: room.name
18 | }))
19 | }
20 |
21 | static async getRoom(id) {
22 | const collection = db.get('rooms')
23 | return (await collection.findOne({
24 | room_id: room.id,
25 | }))
26 | }
27 |
28 |
29 |
30 | // static async addUsersToRoom(room_id, users) {
31 | // const collection = db.get('rooms')
32 | // return (await collection.findOne({
33 | // room_id: room.id,
34 | // }))
35 | // }
36 | }
37 |
38 | module.exports = Calls;
--------------------------------------------------------------------------------
/src/database/redis.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Redis = require('ioredis');
4 | const Logger = require('../util/Logger')
5 | // const axios = require('axios')
6 | require('dotenv').config()
7 |
8 | Logger.reddis('Connected to reddis.')
9 |
10 | const redisOptions = {
11 | host: process.env.REDIS_HOST,
12 | port: process.env.REDIS_PORT,
13 | password: process.env.REDIS_PASS,
14 | db: 0,
15 | connectionName: `dogegarden-stats`,
16 | };
17 |
18 | function createClient(prefix, options = {}) {
19 | const allOptions = {
20 | ...redisOptions,
21 | ...options,
22 |
23 | keyPrefix: `dogestats:${prefix}:`
24 | };
25 |
26 | return new Redis(allOptions);
27 | }
28 |
29 | // const redisDemo = async () => {
30 | // const clientTest = createClient(`test1`);
31 | // const redis = clientTest.multi();
32 | // const reply = await redis.get('foo');
33 | // console.log(reply);
34 | // };
35 |
36 | // redisDemo();
37 |
38 |
39 | // const test = async () => {
40 | // const clientTest = createClient(`test1`);
41 | // const multi = clientTest.multi();
42 | // // multi.set(`a`, 1);
43 | // // multi.get(`a`);
44 | // multi.get(`dogestats`);
45 | // return await multi.exec();
46 | // }
47 |
48 | // test().then((...args) => console.log(...args));
49 |
50 | // const test = async () => {
51 | // let data = await axios.get('http://localhost:7000/api/statistics')
52 | // // console.log(data.data)
53 | // const clientTest = createClient(`test1`);
54 | // const multi = clientTest.multi();
55 | // multi.set(`first-test`, 123)
56 | // multi.set(`a`, JSON.stringify(data));
57 | // multi.get(`a`);
58 | // multi.get(`first-test`);
59 | // return await multi.exec();
60 | // }
61 |
62 | // const test = async () => {
63 | // const clientTest = createClient(`test1`);
64 | // const multi = clientTest.multi();
65 | // multi.get(`dogestats`);
66 | // return await multi.exec();
67 | // }
68 |
69 | // try {
70 | // test().then((...args) => console.log(...args));
71 | // } catch (e) {
72 | // console.log("Error in redis", e)
73 | // }
74 |
75 | module.exports = {
76 | createClient,
77 | // test,
78 | };
79 |
--------------------------------------------------------------------------------
/src/database/setup.sql:
--------------------------------------------------------------------------------
1 | CREATE DATABASE IF NOT EXISTS dogestats;
2 |
3 | USE dogestats;
4 |
5 | -- CREATE TABLE IF NOT EXISTS users (
6 | -- uuid VARCHAR(36) NOT NULL PRIMARY KEY,
7 | -- isBot BOOLEAN NOT NULL DEFAULT FALSE,
8 | -- username TEXT(15),
9 | -- bio TEXT(160),
10 | -- numFollowers INT NOT NULL DEFAULT 0,
11 | -- numFollowing INT NOT NULL DEFAULT 0,
12 | -- displayName TEXT(50) NOT NULL,
13 | -- avatar TEXT,
14 | -- inRoom BOOLEAN NOT NULL DEFAULT FALSE
15 | -- );
16 |
17 | CREATE TABLE IF NOT EXISTS rooms (
18 | id VARCHAR(36) NOT NULL PRIMARY KEY,
19 | creatorId TEXT NOT NULL,
20 | roomDescription TEXT,
21 | insertedAt TIMESTAMP NOT NULL,
22 | roomName TEXT NOT NULL,
23 | numPeopleInside INT NOT NULL DEFAULT 0
24 | );
25 |
26 | CREATE TABLE IF NOT EXISTS stats (
27 | id INT(11) AUTO_INCREMENT NOT NULL PRIMARY KEY,
28 | totalRooms INT(11) NOT NULL DEFAULT 0,
29 | totalScheduledRooms INT(11) NOT NULL DEFAULT 0,
30 | totalOnline INT(11) NOT NULL DEFAULT 0,
31 | totalBotsOnline INT(11) NOT NULL DEFAULT 0,
32 | totalBotsSendingTelemetry INT(11) NOT NULL DEFAULT 0,
33 | totalRegistered INT(11) NOT NULL DEFAULT 0,
34 | activeInLastTwoDays INT(11) NOT NULL DEFAULT 0,
35 | topRoomID VARCHAR(36),
36 | newestRoomID VARCHAR(36),
37 | longestRoomID VARCHAR(36),
38 | statsTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
39 | );
40 |
--------------------------------------------------------------------------------
/src/public/css/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --background: rgba(20,25,32);
3 | --nav: #22272E;
4 | }
5 |
6 | @import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@500&display=swap");
7 | @import url("https://fonts.googleapis.com/css2?family=Rubik&display=swap");
8 | html,
9 | body {
10 | height: 150vh;
11 | margin: 0;
12 | padding: 0;
13 | font-family: Rubik, -apple-system, BlinkMacSystemFont, "Helvetica Neue",
14 | Roboto, system-ui, sans-serif;
15 | background: var(--background) !important;
16 | }
17 |
18 |
19 | #api-alert:not(.show), #ver-alert:not(.show) {
20 | opacity: 1;
21 | }
22 |
23 | .card-stat-count {
24 | font-size: 3em;
25 | }
26 | z #botsOnline {
27 | width: 4500px !important;
28 | max-width: 4500px !important;
29 | }
30 |
31 | .fade-in {
32 | animation: fadeIn ease 1s;
33 | -webkit-animation: fadeIn ease 1s;
34 | -moz-animation: fadeIn ease 1s;
35 | -o-animation: fadeIn ease 1s;
36 | -ms-animation: fadeIn ease 1s;
37 | }
38 |
39 | .moving-bg {
40 | background-image: url("../static/moving-bg.png");
41 | background-color: rgba(134, 134, 134, 0.075) !important;
42 | background-size: 50%;
43 | animation: bganim 40s linear 0s infinite;
44 | -webkit-animation: bganim 40s linear 0s infinite;
45 | }
46 |
47 | .zoom-in:hover {
48 | transform: scale(1.01);
49 | transition-timing-function: ease-in-out;
50 | }
51 |
52 | .extra-btn-width {
53 | width: 140px;
54 | }
55 |
56 | .hover-detect:hover {
57 | transform: scale(1.01);
58 | transition-timing-function: ease-in-out;
59 | }
60 |
61 | .zoom-in {
62 | transition: 0.15s;
63 | }
64 |
65 | .show {
66 | display: none;
67 | }
68 | /* CLEAR - DEFAULT */
69 |
70 | .ex-c {
71 | border: 3px solid #f4055f;
72 | background-color: rgba(20,25,32) !important;
73 | }
74 |
75 | .ex-c:hover {
76 | background-color: rgba(16, 16, 16, 0.788) !important
77 | }
78 |
79 | /* PINK */
80 | .ex-pink {
81 | background-color: #f405615b !important;
82 | }
83 |
84 | .ex-pink:hover {
85 | background-color: #df337567 !important;
86 | }
87 |
88 | .border-p {
89 | border: 3px solid #f4055f;
90 | transition: 0.5s;
91 | }
92 |
93 | .border-p:hover {
94 | border: 3px solid #f4055f;
95 | }
96 |
97 | /* BLUE */
98 | .ex-blue {
99 | background-color: #0559f446 !important;
100 | }
101 |
102 | .ex-blue:hover {
103 | background-color: #1361f15b !important;
104 | }
105 |
106 | .border-b {
107 | border: 3px solid #c0c0c04b;
108 | transition: 0.5s;
109 | }
110 |
111 | .border-b:hover {
112 | border: 3px solid #0559f446;
113 | }
114 |
115 | /* ORANGE */
116 | .ex-orange {
117 | background-color: #c7880046 !important;
118 | }
119 |
120 | .ex-orange:hover {
121 | background-color: #ffae0046 !important;
122 | }
123 |
124 | .border-o {
125 | border: 3px solid #c0c0c04b;
126 | transition: 0.5s;
127 | }
128 |
129 | .border-o:hover {
130 | border: 3px solid #c7880046;
131 | }
132 |
133 | /* //////////////////////////////////// */
134 |
135 | @media screen and (max-width: 992px) {
136 | .card-stat-count {
137 | font-size: 2em;
138 | }
139 |
140 | .p-text {
141 | display: none;
142 | }
143 | }
144 |
145 | @media screen and (max-width: 600px) {
146 | .cover-container {
147 | margin-top: 20px;
148 | }
149 | .col-md-6 {
150 | margin-bottom: 10px;
151 | }
152 | }
153 |
154 | .remove-bg {
155 | background-color: rgba(255, 255, 255, 0.041);
156 | }
157 |
158 | @keyframes bganim {
159 | 0% {
160 | background-position: 0 0;
161 | }
162 | 50% {
163 | background-position: 500px -500px;
164 | }
165 | to {
166 | background-position: 1000px -1000px;
167 | }
168 | }
169 |
170 | .blured {
171 | color: transparent;
172 | text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
173 | }
174 |
175 | .hidden {
176 | -webkit-text-security: disc;
177 | }
178 |
179 | .snow-lower {
180 | bottom: unset;
181 | position: absolute;
182 | top: 70vh;
183 | }
184 |
185 | .title-text {
186 | margin: 20px;
187 | text-align: center;
188 | vertical-align: middle;
189 | }
190 |
191 | .logo {
192 | height: 200px;
193 | }
194 |
195 | .lower-content {
196 | height: 200px;
197 | background: #f2f2f2;
198 | }
199 |
200 | .white-bg {
201 | background: #f2f2f2;
202 | }
203 |
204 | .thing1 {
205 | text-shadow: 0px 1.4px 10px rgba(0, 0, 0, 0.829);
206 | }
207 |
208 | input {
209 | writing-mode: horizontal-tb !important;
210 | -webkit-writing-mode: horizontal-tb !important;
211 | text-rendering: auto;
212 | color: -internal-light-dark(black, white);
213 | letter-spacing: normal;
214 | word-spacing: normal;
215 | text-transform: none;
216 | text-indent: 0px;
217 | text-shadow: none;
218 | display: inline-block;
219 | text-align: start;
220 | appearance: textfield;
221 | background-color: -internal-light-dark(rgb(255, 255, 255), rgb(59, 59, 59));
222 | -webkit-rtl-ordering: logical;
223 | cursor: text;
224 | margin: 0em;
225 | font: 400 13.3333px Arial;
226 | padding: 1px 2px;
227 | border-width: 2px;
228 | border-style: inset;
229 | border-color: -internal-light-dark(rgb(118, 118, 118), rgb(133, 133, 133));
230 | border-image: initial;
231 | }
232 |
233 | [type="text"]:focus,
234 | [type="email"]:focus,
235 | [type="url"]:focus,
236 | [type="password"]:focus,
237 | [type="number"]:focus,
238 | [type="date"]:focus,
239 | [type="datetime-local"]:focus,
240 | [type="month"]:focus,
241 | [type="search"]:focus,
242 | [type="tel"]:focus,
243 | [type="time"]:focus,
244 | [type="week"]:focus,
245 | [multiple]:focus,
246 | textarea:focus,
247 | select:focus {
248 | outline: transparent solid 2px;
249 | outline-offset: 2px;
250 | --tw-ring-inset: var(--tw-empty);
251 | --tw-ring-offset-width: 0px;
252 | --tw-ring-offset-color: #fff;
253 | --tw-ring-color: #2563eb;
254 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0
255 | var(--tw-ring-offset-width) var(--tw-ring-offset-color);
256 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0
257 | calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
258 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
259 | var(--tw-shadow, 0 0 #0000);
260 | border-color: #099aa5 !important;
261 | }
262 |
263 | .secondnav {
264 | width: 100%;
265 | --tw-bg-opacity: 1;
266 | background-color: rgba(62.73, 76.959, 90.27, var(--tw-bg-opacity));
267 | --tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
268 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
269 | var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
270 | overflow-x: auto;
271 | }
272 |
273 | .status-card {
274 | width: 300px !important;
275 | }
276 |
277 | .nav-item-new {
278 | --tw-text-opacity: 1;
279 | color: rgba(154.148, 165.363, 177.352, var(--tw-text-opacity)) !important;
280 | align-items: center;
281 | height: 100%;
282 | margin: 0 5px 0 5px;
283 | }
284 |
285 | .min-w {
286 | min-width: 90vw !important;
287 | }
288 | /*
289 | .right-fixed {
290 | position: fixed;
291 | bottom: 0px;
292 | right: 0px;
293 | padding: 30px;
294 | } */
295 |
296 | a.active,
297 | div.active {
298 | --tw-text-opacity: 1;
299 | color: rgba(228.608, 231.591, 235.493, var(--tw-text-opacity)) !important;
300 | box-shadow: rgb(9, 154, 165) 0px -2px inset;
301 | }
302 |
303 | .buttonload {
304 | background-color: #4caf50;
305 | /* Green background */
306 | border: none;
307 | /* Remove borders */
308 | color: white;
309 | /* White text */
310 | padding: 12px 16px;
311 | /* Some padding */
312 | font-size: 16px; /* Set a font size */
313 | }
314 |
315 | .morenav {
316 | display: inline-block;
317 | font-size: 0.93rem !important;
318 | padding: 0.75rem 1rem;
319 | --tw-text-opacity: 1;
320 | color: rgba(154.148, 165.363, 177.352, var(--tw-text-opacity)) !important;
321 | text-decoration: none;
322 | white-space: nowrap;
323 | transition-property: all;
324 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
325 | transition-duration: 150ms;
326 | }
327 |
328 | .secondnav-items {
329 | display: flex;
330 | -webkit-box-align: center;
331 | align-items: center;
332 | font-size: 0.875rem;
333 | line-height: 1.25rem;
334 | margin-left: auto;
335 | margin-right: auto;
336 | padding-left: 0.5rem;
337 | padding-right: 0.5rem;
338 | max-width: 1200px;
339 | }
340 |
341 | .console-area:focus {
342 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0
343 | var(--tw-ring-offset-width) var(--tw-ring-offset-color);
344 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0
345 | calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
346 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
347 | var(--tw-shadow, 0 0 #0000);
348 | }
349 |
350 | .home-card {
351 | border-radius: 0.25rem !important;
352 | }
353 |
354 | .console-input {
355 | --tw-bg-opacity: 1;
356 | background-color: rgba(31.008, 40.8, 50.592, var(--tw-bg-opacity));
357 | --tw-text-opacity: 1;
358 | color: rgba(228.608, 231.591, 235.493, var(--tw-text-opacity));
359 | display: flex;
360 | -webkit-box-align: baseline;
361 | align-items: baseline;
362 | height: 40px;
363 | border-top: none;
364 | border-left: none;
365 | border-right: none;
366 | border-color: transparent;
367 | }
368 |
369 | .console-input-dollar {
370 | flex-shrink: 0;
371 | padding: 0.5rem;
372 | font-weight: 700;
373 | }
374 |
375 | .logo-text {
376 | font-size: 1.6rem !important;
377 | line-height: 2rem;
378 | font-family: "IBM Plex Sans", Roboto, system-ui, sans-serif;
379 | padding-left: 1rem !important;
380 | padding-right: 1rem !important;
381 | text-decoration: none !important;
382 | --tw-text-opacity: 1 !important;
383 | color: rgba(201.756, 209.1, 216.444, var(--tw-text-opacity)) !important;
384 | transition-property: background-color, border-color, color, fill, stroke;
385 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important;
386 | transition-duration: 150ms;
387 | }
388 |
389 | .card {
390 | border: none !important;
391 | }
392 |
393 | .border-r {
394 | border-radius: 0.25rem !important;
395 | }
396 |
397 | .card-body {
398 | --tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
399 | 0 2px 4px -1px rgba(0, 0, 0, 0.06);
400 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
401 | var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
402 | --tw-bg-opacity: 1;
403 | background-color: var(--nav);
404 | border-radius: 0.25rem 0.25rem 0.25rem 0.25rem;
405 | padding: 0.75rem;
406 | display: flex;
407 | font-size: 0.75rem;
408 | line-height: 1rem;
409 | }
410 | .card-body.fixed-height {
411 | height: 120px;
412 | }
413 |
414 | .card-body > p {
415 | align-self: center;
416 | }
417 |
418 | .card-body > ul {
419 | list-style-type: circle;
420 | }
421 |
422 | .hide-div {
423 | display: none;
424 | }
425 |
426 | #faq-links {
427 | justify-content: space-evenly;
428 | display: flex;
429 | padding-top: 1rem;
430 | }
431 |
432 | #faq-links i {
433 | margin-right: 0.25rem;
434 | }
435 |
436 | #accordionFAQ button {
437 | text-align: left;
438 | color: #333333;
439 | }
440 |
441 | #accordionFAQ .card-header {
442 | background-color: white;
443 | }
444 |
445 | .fix-bg {
446 | background-size: cover;
447 | }
448 |
449 | h1 {
450 | color: rgba(255, 255, 255, 0.979);
451 | }
452 |
453 | .bg {
454 | background: url(https://cdn.discordapp.com/attachments/783703146484465756/801638287302983700/unknown.png);
455 | object-fit: cover;
456 | background-size: auto;
457 | background-repeat: no-repeat;
458 | }
459 |
460 | .profile-rank {
461 | display: block;
462 | }
463 |
464 | .vl {
465 | border-left: 4px solid #515151;
466 | height: 15px;
467 | width: 0px;
468 | margin-left: auto;
469 | margin-right: auto;
470 | }
471 |
472 | .card-header > button {
473 | font-size: 1.2em;
474 | font-weight: 300;
475 | padding: 0px;
476 | width: 100%;
477 | text-decoration: none !important;
478 | }
479 |
480 | [data-tooltip]:before {
481 | position: absolute;
482 | content: attr(data-tooltip);
483 | opacity: 0;
484 | }
485 |
486 | [data-tooltip]:hover:before {
487 | opacity: 1;
488 | background: rgb(15, 15, 15);
489 | border: 1px solid rgba(45, 45, 45, 0);
490 | position: relative;
491 | border-radius: 30px;
492 | padding-left: 10px;
493 | padding-right: 10px;
494 | }
495 |
496 | [data-tooltip]:not([data-tooltip-persistent]):before {
497 | pointer-events: none;
498 | }
499 |
500 | .search {
501 | height: 60px !important;
502 | }
503 |
504 | .user-list {
505 | height: 100px;
506 | margin: 5px;
507 | }
508 |
509 | .profile-img2 {
510 | height: 60px;
511 | }
512 |
513 | .title-main2 {
514 | display: inline;
515 | vertical-align: middle;
516 | margin-left: 10px;
517 | font-size: 25px;
518 | }
519 |
520 | .user-list:hover {
521 | transform: scale(1.01);
522 | transition: 0.1s;
523 | }
524 |
525 | .bg-main {
526 | height: 50vh;
527 | background: #131313;
528 | box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.2);
529 | -ms-interpolation-mode: bicubic;
530 | }
531 |
532 | .wave {
533 | content: url(/public/img/wave.svg);
534 | position: absolute;
535 | top: 0;
536 | left: 0;
537 | width: 100%;
538 | }
539 |
540 | a {
541 | color: #ffffff !important;
542 | text-decoration: none !important;
543 | }
544 |
545 | a:hover {
546 | color: #ffffff !important;
547 | text-decoration: none !important;
548 | cursor: pointer !important;
549 | }
550 |
551 | a > button:hover {
552 | color: #ffffff !important;
553 | }
554 |
555 | .border1 {
556 | border: 5px solid rgba(45, 45, 45, 0);
557 | }
558 |
559 | .stats-leader {
560 | font-size: 18px;
561 | }
562 |
563 | .badge1 {
564 | height: 23px;
565 | margin-left: 2px;
566 | }
567 |
568 | .achivement {
569 | margin: 5px;
570 | }
571 |
572 | @media (max-width: 510px) {
573 | .title-main {
574 | font-size: 21px;
575 | }
576 | .achivement {
577 | transform: scale(0.7);
578 | }
579 | }
580 |
581 | @media (max-width: 350px) {
582 | .achivement {
583 | transform: scale(0.5);
584 | }
585 | }
586 |
587 | @media (max-width: 300px) {
588 | .profile-img {
589 | height: 50px;
590 | }
591 | }
592 |
593 | @media (max-width: 250px) {
594 | .achivement {
595 | transform: scale(0.4);
596 | }
597 | }
598 |
599 | @media (max-width: 400px) {
600 | .profile-right {
601 | display: none;
602 | }
603 | }
604 |
605 | .navbar-avatar {
606 | height: 40px;
607 | border-radius: 50%;
608 | }
609 |
610 | a:hover {
611 | color: white !important;
612 | }
613 |
614 | .icon-top {
615 | margin-left: 10px;
616 | }
617 |
618 | .accounts-card {
619 | display: grid;
620 | grid-template-columns: repeat(12, minmax(0px, 1fr));
621 | gap: 1rem;
622 | position: relative;
623 | height: 80px;
624 | }
625 |
626 | .modal-content {
627 | background: #33404d !important;
628 | }
629 |
630 | .accounts-wrapper {
631 | margin-bottom: 0px !important;
632 | line-height: 10px !important;
633 | display: inline;
634 | }
635 |
636 | .hub-text {
637 | text-align: right !important;
638 | float: right !important;
639 | -webkit-box-align: right !important;
640 | align-items: right !important;
641 | width: 100% !important;
642 | -webkit-box-align: center;
643 | align-items: center;
644 | }
645 |
646 | .status-on-bar {
647 | width: 0.5rem;
648 | position: absolute;
649 | right: 0px;
650 | z-index: 20;
651 | border-radius: 9999px;
652 | margin: 0.25rem;
653 | opacity: 0.5;
654 | transition-property: all;
655 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
656 | transition-duration: 150ms;
657 | height: calc(100% - 0.5rem);
658 | --tw-bg-opacity: 1;
659 | background-color: rgba(24.0975, 154.402, 28.441, var(--tw-bg-opacity));
660 | }
661 |
662 | .right-col-rooms {
663 | width: 3.5rem;
664 | position: absolute;
665 | right: 10px;
666 | z-index: 20;
667 | font-size: 1.3em;
668 | border-radius: 9999px;
669 | margin: 0.25rem;
670 | transition-property: all;
671 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
672 | transition-duration: 150ms;
673 | }
674 |
675 | .status-off-bar {
676 | width: 0.5rem;
677 | position: absolute;
678 | right: 0px;
679 | z-index: 20;
680 | border-radius: 9999px;
681 | margin: 0.25rem;
682 | opacity: 0.5;
683 | transition-property: all;
684 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
685 | transition-duration: 150ms;
686 | height: calc(100% - 0.5rem);
687 | --tw-bg-opacity: 1;
688 | background-color: rgba(225.038, 45.2625, 57.2475, var(--tw-bg-opacity));
689 | }
690 |
691 | .account-avatar {
692 | height: 48px;
693 | width: 48px;
694 | overflow: visible !important;
695 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
696 | var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
697 | }
698 |
699 | .con-grid1 {
700 | display: flex;
701 | -webkit-box-align: center;
702 | align-items: center;
703 | grid-column: span 12 / span 12;
704 | overflow: visible !important;
705 | }
706 |
707 | .account-text {
708 | font-size: 1.325rem;
709 | line-height: 1.75rem;
710 | overflow-wrap: break-word;
711 | margin: 10px;
712 | }
713 |
714 | .card-header {
715 | --tw-bg-opacity: 1 !important;
716 | background-color: var(--nav) !important;
717 | border-top-left-radius: 0.25rem !important;
718 | border-top-right-radius: 0.25rem !important;
719 | padding: 0.75rem !important;
720 | border-bottom-width: 1px !important;
721 | --tw-border-opacity: 1 !important;
722 | border-color: rgba(18.615, 25.5, 32.385, var(--tw-border-opacity)) !important;
723 | }
724 |
725 | .card-header-text {
726 | font-size: 0.875rem;
727 | line-height: 1.25rem;
728 | text-transform: uppercase;
729 | text-align: left;
730 | }
731 |
732 | .service-text {
733 | text-align: left;
734 | font-size: 0.85rem;
735 | line-height: 1rem;
736 | margin-bottom: -1rem;
737 | }
738 |
739 | #console-window-inner {
740 | overflow-y: scroll;
741 | max-height: 100%;
742 | width: 100%;
743 | }
744 |
745 | .controls {
746 | margin-left: 3px;
747 | margin-right: 3px;
748 | line-height: 1 !important;
749 | }
750 |
751 | .card-body-text {
752 | line-height: 0;
753 | margin-bottom: 15px;
754 | }
755 |
756 | .after-cbt {
757 | margin: 5px;
758 | --tw-text-opacity: 1;
759 | color: rgba(96.492, 109.211, 122.808, var(--tw-text-opacity));
760 | }
761 |
762 | .mid {
763 | text-align: center;
764 | align-items: center;
765 | align-self: center;
766 | }
767 |
768 | p {
769 | --tw-text-opacity: 1;
770 | line-height: 1.375;
771 | font-family: Rubik, -apple-system, BlinkMacSystemFont, "Helvetica Neue",
772 | Roboto, system-ui, sans-serif;
773 | }
774 |
775 | h3 {
776 | --tw-text-opacity: 1;
777 | color: rgba(201.756, 209.1, 216.444, var(--tw-text-opacity));
778 | line-height: 1.375;
779 | font-family: Rubik, -apple-system, BlinkMacSystemFont, "Helvetica Neue",
780 | Roboto, system-ui, sans-serif;
781 | }
782 |
783 | .card-header > button:hover {
784 | text-decoration: none;
785 | }
786 |
787 | .card-header > button:active {
788 | text-decoration: none;
789 | }
790 |
791 | .card-header > button:focus {
792 | text-decoration: none;
793 | }
794 |
795 | .category-card-row .card {
796 | transition: 0.45s;
797 | }
798 |
799 | .category-card-row .card:hover {
800 | box-shadow: 0 5px 10px 0 rgb(0 0 0 / 30%);
801 | }
802 |
803 | .text {
804 | font-size: 2em;
805 | }
806 |
807 | .more {
808 | text-align: center !important;
809 | }
810 |
811 | .row {
812 | justify-content: center !important;
813 | }
814 |
815 | .title-main {
816 | display: inline;
817 | vertical-align: middle;
818 | margin-left: 10px;
819 | }
820 |
821 | .status-on-circle {
822 | color: rgb(5, 153, 37);
823 | }
824 |
825 | .status-off-circle {
826 | color: rgb(225, 45, 57);
827 | }
828 |
829 | .about {
830 | background: rgb(20, 20, 20);
831 | padding: 20px;
832 | }
833 |
834 | .profile-right {
835 | float: right;
836 | margin: 25px;
837 | }
838 |
839 | .cookies {
840 | overflow: hidden;
841 | position: relative;
842 | text-align: center;
843 | width: auto !important;
844 | justify-content: center !important;
845 | box-shadow: 0px 5px #ff8800bd;
846 | background: #ff9216bd !important;
847 | }
848 |
849 | .cookies:active:after {
850 | opacity: 0;
851 | }
852 |
853 | /* width */
854 |
855 | ::-webkit-scrollbar {
856 | width: 10px;
857 | }
858 |
859 | /* Handle */
860 |
861 | ::-webkit-scrollbar-thumb {
862 | box-shadow: rgb(123 135 147) 0px 0px 0px 1px inset,
863 | rgb(63 77 90) 0px 0px 0px 4px inset;
864 | border-radius: 0.9rem;
865 | border: 1px solid black;
866 | }
867 |
868 | .cookies:after {
869 | animation: shine 4s ease-in-out infinite;
870 | animation-fill-mode: forwards;
871 | content: "";
872 | position: absolute;
873 | top: -110%;
874 | left: -210%;
875 | width: 200%;
876 | height: 200%;
877 | opacity: 0;
878 | transform: rotate(30deg);
879 | background: rgba(255, 255, 255, 0.13);
880 | background: linear-gradient(
881 | to right,
882 | rgba(255, 255, 255, 0.13) 0%,
883 | rgba(255, 255, 255, 0.13) 77%,
884 | rgba(255, 255, 255, 0.5) 92%,
885 | rgba(255, 255, 255, 0) 100%
886 | );
887 | }
888 |
889 | .cookies-count {
890 | font-size: 30px;
891 | }
892 |
893 | .cookies-count2 {
894 | font-size: 16px;
895 | }
896 |
897 | .number {
898 | padding: 15px;
899 | background: #383838b6;
900 | margin: 10px;
901 | text-align: center;
902 | width: 170px;
903 | border-radius: 5px;
904 | }
905 |
906 | .text-center {
907 | text-align: center;
908 | position: absolute;
909 | top: 50%;
910 | left: 50%;
911 | transform: translate(-50%, -50%);
912 | }
913 |
914 | .category-bottom {
915 | height: 60px;
916 | border-bottom-right-radius: 20px !important;
917 | border-bottom-left-radius: 20px !important;
918 | display: flex;
919 | justify-content: center;
920 | align-items: center;
921 | font-weight: 600 !important;
922 | font-size: large;
923 | color: #646464;
924 | }
925 |
926 | .category-bottom-creator {
927 | color: white;
928 | }
929 |
930 | .category {
931 | object-fit: cover;
932 | height: 220px;
933 | }
934 |
935 | .card-1:hover {
936 | box-shadow: inset 0px 33px 25px 0 rgba(15, 164, 250, 0.712),
937 | inset 0 66px 15px 0px rgba(255, 28, 244, 0.644),
938 | inset 0 99px 5px 0px rgba(138, 0, 252, 0.733);
939 | transform: scale(1.1);
940 | z-index: 999;
941 | }
942 |
943 | .card-img-top {
944 | border-radius: 1.5rem !important;
945 | filter: blur(1.5px);
946 | -webkit-filter: blur(1.5px);
947 | }
948 |
949 | .card-img-top:hover {
950 | filter: blur(0px);
951 | -webkit-filter: blur(0px);
952 | transition: 0.5s;
953 | }
954 |
955 | .category-countdown {
956 | color: white !important;
957 | border-radius: 5px;
958 | background-color: #db4040 !important;
959 | }
960 |
961 | .card-hover:hover {
962 | transform: scale(1.1);
963 | z-index: 999;
964 | transition: 0.7s;
965 | }
966 |
967 | .flip {
968 | transform: scaleY(-1);
969 | }
970 |
971 | .spacer {
972 | margin-bottom: 20px;
973 | }
974 |
975 | .card-a {
976 | padding: 20px;
977 | border: 0px;
978 | border-radius: 0.5rem !important;
979 | z-index: 1000;
980 | }
981 |
982 | .thing2 {
983 | font-weight: 500;
984 | letter-spacing: 1.25px;
985 | }
986 |
987 | .card .red-button {
988 | font-weight: 500;
989 | border-radius: 0px;
990 | letter-spacing: 1.25px;
991 | }
992 |
993 | @keyframes fadeIn {
994 | 0% {
995 | opacity: 0;
996 | }
997 | 100% {
998 | opacity: 1;
999 | }
1000 | }
1001 |
1002 | @-moz-keyframes fadeIn {
1003 | 0% {
1004 | opacity: 0;
1005 | }
1006 | 100% {
1007 | opacity: 1;
1008 | }
1009 | }
1010 |
1011 | @-webkit-keyframes fadeIn {
1012 | 0% {
1013 | opacity: 0;
1014 | }
1015 | 100% {
1016 | opacity: 1;
1017 | }
1018 | }
1019 |
1020 | @-o-keyframes fadeIn {
1021 | 0% {
1022 | opacity: 0;
1023 | }
1024 | 100% {
1025 | opacity: 1;
1026 | }
1027 | }
1028 |
1029 | @-ms-keyframes fadeIn {
1030 | 0% {
1031 | opacity: 0;
1032 | }
1033 | 100% {
1034 | opacity: 1;
1035 | }
1036 | }
1037 |
1038 | .title {
1039 | font-size: 1.5em;
1040 | text-shadow: 0px 1.4px 15px rgba(0, 0, 0, 0.775);
1041 | }
1042 |
1043 | .snow-lower {
1044 | top: 30vh;
1045 | bottom: unset;
1046 | position: absolute;
1047 | width: 100%;
1048 | height: 277px;
1049 | object-fit: cover;
1050 | object-position: left;
1051 | }
1052 |
1053 | .snow-lower-1 {
1054 | top: 1160px;
1055 | bottom: unset;
1056 | position: absolute;
1057 | width: 100%;
1058 | height: 277px;
1059 | object-fit: cover;
1060 | object-position: left;
1061 | }
1062 |
1063 | .navbar-brand {
1064 | height: 15% !important;
1065 | width: 5%;
1066 | }
1067 |
1068 | .badtag {
1069 | border: solid 1px red !important;
1070 | background-color: #d24a4a !important;
1071 | color: white !important;
1072 | }
1073 |
1074 | .badtag a {
1075 | color: #ad2b2b !important;
1076 | }
1077 |
1078 | .notyou {
1079 | font-size: 10px;
1080 | text-decoration-style: wavy;
1081 | position: absolute;
1082 | top: 15px;
1083 | color: gray;
1084 | }
1085 |
1086 | .blacklisted_save {
1087 | border: 1px solid rgba(0, 0, 0, 0.125);
1088 | margin-left: 5px;
1089 | color: white !important;
1090 | }
1091 |
1092 | .maw img {
1093 | position: absolute;
1094 | top: 0;
1095 | left: 0;
1096 | right: 0;
1097 | margin: auto;
1098 | animation-name: stretch;
1099 | animation-duration: 2s;
1100 | animation-timing-function: ease-out;
1101 | animation-direction: alternate;
1102 | animation-iteration-count: infinite;
1103 | animation-play-state: running;
1104 | }
1105 |
1106 | .bit {
1107 | margin-top: 30px;
1108 | }
1109 |
1110 | .text {
1111 | text-align: center;
1112 | }
1113 |
1114 | @keyframes stretch {
1115 | 0% {
1116 | transform: scale(0.7);
1117 | border-radius: 100%;
1118 | }
1119 | 100% {
1120 | transform: scale(0.9);
1121 | }
1122 | }
1123 |
1124 | .status.true:before {
1125 | background-color: #94e185;
1126 | border-color: #78d965;
1127 | box-shadow: 0px 0px 4px 1px #94e185;
1128 | }
1129 |
1130 | .status.false:before {
1131 | background-color: #fc6868;
1132 | border-color: #fc6868;
1133 | box-shadow: 0px 0px 4px 1px #e18885;
1134 | }
1135 |
1136 | .checked {
1137 | font-style: italic;
1138 | color: gray;
1139 | font-size: 13px;
1140 | }
1141 |
1142 | .status:before {
1143 | content: " ";
1144 | display: inline-block;
1145 | width: 7px;
1146 | height: 7px;
1147 | border: 1px solid #000;
1148 | border-radius: 7px;
1149 | margin-bottom: 1.5px;
1150 | }
1151 |
1152 | .status-text {
1153 | font-size: 13px;
1154 | font-style: normal;
1155 | }
1156 |
1157 | .nav-item {
1158 | padding: 0 15px;
1159 | border-radius: 15px;
1160 | margin: 0 10px;
1161 | background-color: rgb(66, 64, 64);
1162 | }
1163 |
1164 | .black {
1165 | color: #00000040;
1166 | }
1167 |
1168 | .white {
1169 | color: #ffffffda;
1170 | }
1171 |
1172 | nav {
1173 | background-color: var(--nav);
1174 | z-index: 9999;
1175 | }
1176 |
1177 | #dummy-target {
1178 | position: relative;
1179 | width: 600px;
1180 | height: 300px;
1181 | border-style: solid;
1182 | }
1183 |
1184 | .position-absolute {
1185 | position: absolute;
1186 | }
1187 |
1188 | .user_avatar {
1189 | border-radius: 100px;
1190 | margin-top: -200px;
1191 | height: 160px;
1192 | background-repeat: repeat-y;
1193 | margin: -200px 20px 0 10px;
1194 | object-fit: contain;
1195 | align-self: flex-start;
1196 | }
1197 |
1198 | .user_avatar_img {
1199 | -webkit-box-shadow: 0 0 5px rgb(31, 30, 30);
1200 | box-shadow: 0 0 5px rgb(31, 30, 30);
1201 | }
1202 |
1203 | .golden {
1204 | animation: glow 1s ease-in-out infinite alternate;
1205 | }
1206 |
1207 | .blue {
1208 | animation: blue 1s ease-in-out infinite alternate;
1209 | }
1210 |
1211 | .di {
1212 | background: rgba(15, 15, 15, 0.678);
1213 | border: 1px solid rgba(45, 45, 45, 0);
1214 | border-radius: 3px;
1215 | padding: 10px;
1216 | width: 410px;
1217 | margin: 30px auto 0 190px;
1218 | z-index: 999;
1219 | transform: scale(1.15);
1220 | }
1221 |
1222 | .grow,
1223 | .mainmenu-submenu {
1224 | transition: all 0.2s ease-in-out;
1225 | }
1226 |
1227 | .di:hover {
1228 | transition: all 0.2s ease-in-out;
1229 | transform: scale(1.18);
1230 | background: rgba(15, 15, 15, 0.788);
1231 | }
1232 |
1233 | .leaderboard-item {
1234 | width: 200px;
1235 | }
1236 |
1237 | .leaderboard-item-indiv {
1238 | margin-bottom: 5px;
1239 | padding: 0 0 20px 20px;
1240 | background: rgba(15, 15, 15, 0.678);
1241 | border: 1px solid rgba(45, 45, 45, 0);
1242 | border-radius: 6px;
1243 | vertical-align: middle;
1244 | }
1245 |
1246 | .leaderboard-item-avatar {
1247 | height: 50px;
1248 | border-radius: 50px;
1249 | display: flex;
1250 | flex-direction: column;
1251 | }
1252 |
1253 | .di-button {
1254 | align-items: center;
1255 | align-self: center;
1256 | background-color: #43b581;
1257 | border: none;
1258 | border-radius: 3px;
1259 | color: #fff;
1260 | cursor: pointer;
1261 | display: flex;
1262 | font-size: 14px;
1263 | font-weight: 500;
1264 | height: 40px;
1265 | justify-content: center;
1266 | line-height: 20px;
1267 | margin-left: 10px;
1268 | min-width: 90px;
1269 | outline: 0;
1270 | padding: 2px 0;
1271 | width: auto;
1272 | transition: background-color 0.17s ease, color 0.17s ease;
1273 | -webkit-user-select: none;
1274 | -moz-user-select: none;
1275 | -ms-user-select: none;
1276 | user-select: none;
1277 | position: relative;
1278 | top: -11px;
1279 | }
1280 |
1281 | .di-details div {
1282 | overflow: hidden;
1283 | text-overflow: ellipsis;
1284 | white-space: nowrap;
1285 | }
1286 |
1287 | .di-details {
1288 | align-items: stretch;
1289 | flex: 1 1 auto;
1290 | flex-direction: column;
1291 | flex-wrap: nowrap;
1292 | justify-content: center;
1293 | max-width: 223px;
1294 | }
1295 |
1296 | .di-flex {
1297 | display: flex;
1298 | }
1299 |
1300 | .di-d-details {
1301 | color: #72767d;
1302 | font-weight: 600;
1303 | font-size: 12px;
1304 | line-height: 16px;
1305 | }
1306 |
1307 | .di-d-details span {
1308 | margin-right: 8px;
1309 | outline: 0;
1310 | }
1311 |
1312 | .di-d-details i {
1313 | border-radius: 50%;
1314 | display: inline-block;
1315 | height: 8px;
1316 | margin-right: 4px;
1317 | width: 8px;
1318 | }
1319 |
1320 | .di-d-d-online {
1321 | background-color: #43b581;
1322 | }
1323 |
1324 | .di-d-details i {
1325 | border-radius: 50%;
1326 | display: inline-block;
1327 | height: 8px;
1328 | margin-right: 4px;
1329 | width: 8px;
1330 | }
1331 |
1332 | .di-d-d-offline {
1333 | background-color: #747f8d;
1334 | }
1335 |
1336 | .di-d-name {
1337 | color: #f6f6f6;
1338 | font-weight: 600;
1339 | font-size: 16px;
1340 | line-height: 20px;
1341 | margin-bottom: 4px;
1342 | }
1343 |
1344 | .di-icon {
1345 | background-clip: padding-box;
1346 | background-position: 50%;
1347 | background-size: 100% 100%;
1348 | border-radius: 15px;
1349 | flex: 0 0 auto;
1350 | height: 50px;
1351 | margin-right: 10px;
1352 | width: 50px;
1353 | }
1354 |
1355 | .di-title {
1356 | color: #8e9297;
1357 | font-weight: 600;
1358 | font-size: 12px;
1359 | line-height: 16px;
1360 | margin-top: 0;
1361 | margin-bottom: 8px;
1362 | overflow: hidden;
1363 | text-overflow: ellipsis;
1364 | text-transform: uppercase;
1365 | white-space: nowrap;
1366 | }
1367 |
1368 | .di-button {
1369 | color: #fff;
1370 | background-color: #3ca374;
1371 | }
1372 |
1373 | [data-tooltip]:before {
1374 | position: absolute;
1375 | content: attr(data-tooltip);
1376 | opacity: 0;
1377 | }
1378 |
1379 | [data-tooltip]:hover:before {
1380 | opacity: 1;
1381 | background: rgb(15, 15, 15);
1382 | border: 1px solid rgba(45, 45, 45, 0);
1383 | border-radius: 30px;
1384 | text-align: center;
1385 | margin-top: 35px;
1386 | padding-left: 10px;
1387 | padding-right: 10px;
1388 | vertical-align: middle;
1389 | }
1390 |
1391 | [data-tooltip]:not([data-tooltip-persistent]):before {
1392 | pointer-events: none;
1393 | }
1394 |
1395 | i {
1396 | font-style: normal;
1397 | }
1398 |
1399 | @keyframes shine {
1400 | 10% {
1401 | opacity: 1;
1402 | top: -30%;
1403 | left: 30%;
1404 | transition-property: left, top, opacity;
1405 | transition-duration: 0.7s, 0.7s, 0.15s;
1406 | transition-timing-function: ease;
1407 | }
1408 | 100% {
1409 | opacity: 0;
1410 | top: -30%;
1411 | left: 30%;
1412 | transition-property: left, top, opacity;
1413 | }
1414 | }
1415 |
1416 | .text {
1417 | text-shadow: 0px 1px 7px rgb(0, 0, 0);
1418 | }
1419 |
1420 | @media only screen and (max-width: 414px) {
1421 | .right-fixed-fullscreen {
1422 | display: none;
1423 | }
1424 | .di {
1425 | display: none;
1426 | }
1427 | }
1428 |
1429 | @media only screen and (max-width: 1331px) {
1430 | .di {
1431 | display: none;
1432 | }
1433 | }
1434 |
1435 | @keyframes glow {
1436 | from {
1437 | box-shadow: 0 0 2px #fff, 0 0 15px #fff, 0 0 5px #e2e600, 0 0 10px #e2e600,
1438 | 0 0 45px #e2e600, 0 0 5px #e2e600, 0 0 5px #e2e600;
1439 | }
1440 | to {
1441 | box-shadow: 0 0 5px #fff, 0 0 10px #edff4d, 0 0 35px #c4ff55,
1442 | 0 0 45px #d8ff4d, 0 0 55px #ffe44d, 0 0 5px #d8ff4d, 0 0 5px #deff4d;
1443 | }
1444 | }
1445 |
1446 | @keyframes blue {
1447 | from {
1448 | box-shadow: 0 0 2px #fff, 0 0 15px #fff, 0 0 5px #006be6, 0 0 10px #006be6,
1449 | 0 0 45px #006be6, 0 0 5px #006be6, 0 0 5px #006be6;
1450 | }
1451 | to {
1452 | box-shadow: 0 0 5px #fff, 0 0 10px #4ddbff, 0 0 35px #55c9ff,
1453 | 0 0 45px #55c9ff, 0 0 55px #55c9ff, 0 0 5px #55c9ff, 0 0 5px #55c9ff;
1454 | }
1455 | }
1456 |
1457 | .user_text {
1458 | display: inline;
1459 | position: absolute;
1460 | top: 250px;
1461 | margin-bottom: -360px;
1462 | font-size: 40px;
1463 | }
1464 |
1465 | .bio {
1466 | text-align: center;
1467 | vertical-align: middle;
1468 | margin-bottom: 30px;
1469 | }
1470 |
1471 | .badges {
1472 | display: flex;
1473 | padding: 0.5rem;
1474 | margin-left: 181px;
1475 | margin-top: -85px;
1476 | }
1477 |
1478 | .badge-a {
1479 | height: 26px;
1480 | margin: 1px;
1481 | }
1482 |
1483 | .badge-b {
1484 | height: 20px;
1485 | margin: 1px;
1486 | }
1487 |
1488 | .user_text_2 {
1489 | display: inline;
1490 | position: absolute;
1491 | top: 250px;
1492 | margin-bottom: -360px;
1493 | font-size: 40px;
1494 | }
1495 |
1496 | .user-container {
1497 | height: 70vh;
1498 | }
1499 |
1500 | .user_banner_img {
1501 | height: 40vh;
1502 | width: 100%;
1503 | box-shadow: 0px 2px 25px 0px rgba(0, 0, 0, 0.562);
1504 | -ms-interpolation-mode: bicubic;
1505 | }
1506 |
1507 | .user_banner {
1508 | min-height: 500px;
1509 | background-attachment: fixed;
1510 | background-position: center;
1511 | background-repeat: no-repeat;
1512 | background-size: cover;
1513 | }
1514 |
1515 | .tooltip {
1516 | position: relative;
1517 | display: inline-block;
1518 | border-bottom: 1px dotted black;
1519 | }
1520 |
1521 | .tooltip .tooltiptext {
1522 | visibility: hidden;
1523 | width: 120px;
1524 | background-color: black;
1525 | color: #fff;
1526 | text-align: center;
1527 | border-radius: 6px;
1528 | padding: 5px 0;
1529 | /* Position the tooltip */
1530 | position: absolute;
1531 | z-index: 1;
1532 | top: 100%;
1533 | left: 50%;
1534 | margin-left: -60px;
1535 | }
1536 |
1537 | .tooltip:hover .tooltiptext {
1538 | visibility: visible;
1539 | }
1540 |
1541 | .pyro > .before,
1542 | .pyro > .after {
1543 | position: absolute;
1544 | width: 5px;
1545 | height: 5px;
1546 | border-radius: 50%;
1547 | box-shadow: -120px -218.66667px blue, 248px -16.66667px #00ff84,
1548 | 190px 16.33333px #002bff, -113px -308.66667px #ff009d,
1549 | -109px -287.66667px #ffb300, -50px -313.66667px #ff006e,
1550 | 226px -31.66667px #ff4000, 180px -351.66667px #ff00d0,
1551 | -12px -338.66667px #00f6ff, 220px -388.66667px #99ff00,
1552 | -69px -27.66667px #ff0400, -111px -339.66667px #6200ff,
1553 | 155px -237.66667px #00ddff, -152px -380.66667px #00ffd0,
1554 | -50px -37.66667px #00ffdd, -95px -175.66667px #a6ff00,
1555 | -88px 10.33333px #0d00ff, 112px -309.66667px #005eff,
1556 | 69px -415.66667px #ff00a6, 168px -100.66667px #ff004c,
1557 | -244px 24.33333px #ff6600, 97px -325.66667px #ff0066,
1558 | -211px -182.66667px #00ffa2, 236px -126.66667px #b700ff,
1559 | 140px -196.66667px #9000ff, 125px -175.66667px #00bbff,
1560 | 118px -381.66667px #ff002f, 144px -111.66667px #ffae00,
1561 | 36px -78.66667px #f600ff, -63px -196.66667px #c800ff,
1562 | -218px -227.66667px #d4ff00, -134px -377.66667px #ea00ff,
1563 | -36px -412.66667px #ff00d4, 209px -106.66667px #00fff2,
1564 | 91px -278.66667px #000dff, -22px -191.66667px #9dff00,
1565 | 139px -392.66667px #a6ff00, 56px -2.66667px #0099ff,
1566 | -156px -276.66667px #ea00ff, -163px -233.66667px #00fffb,
1567 | -238px -346.66667px #00ff73, 62px -363.66667px #0088ff,
1568 | 244px -170.66667px #0062ff, 224px -142.66667px #b300ff,
1569 | 141px -208.66667px #9000ff, 211px -285.66667px #ff6600,
1570 | 181px -128.66667px #1e00ff, 90px -123.66667px #c800ff,
1571 | 189px 70.33333px #00ffc8, -18px -383.66667px #00ff33,
1572 | 100px -6.66667px #ff008c;
1573 | -moz-animation: 1s bang ease-out infinite backwards,
1574 | 1s gravity ease-in infinite backwards, 5s position linear infinite backwards;
1575 | -webkit-animation: 1s bang ease-out infinite backwards,
1576 | 1s gravity ease-in infinite backwards, 5s position linear infinite backwards;
1577 | -o-animation: 1s bang ease-out infinite backwards,
1578 | 1s gravity ease-in infinite backwards, 5s position linear infinite backwards;
1579 | -ms-animation: 1s bang ease-out infinite backwards,
1580 | 1s gravity ease-in infinite backwards, 5s position linear infinite backwards;
1581 | animation: 1s bang ease-out infinite backwards,
1582 | 1s gravity ease-in infinite backwards, 5s position linear infinite backwards;
1583 | }
1584 |
1585 | .pyro > .after {
1586 | -moz-animation-delay: 1.25s, 1.25s, 1.25s;
1587 | -webkit-animation-delay: 1.25s, 1.25s, 1.25s;
1588 | -o-animation-delay: 1.25s, 1.25s, 1.25s;
1589 | -ms-animation-delay: 1.25s, 1.25s, 1.25s;
1590 | animation-delay: 1.25s, 1.25s, 1.25s;
1591 | -moz-animation-duration: 1.25s, 1.25s, 6.25s;
1592 | -webkit-animation-duration: 1.25s, 1.25s, 6.25s;
1593 | -o-animation-duration: 1.25s, 1.25s, 6.25s;
1594 | -ms-animation-duration: 1.25s, 1.25s, 6.25s;
1595 | animation-duration: 1.25s, 1.25s, 6.25s;
1596 | }
1597 |
1598 | @-webkit-keyframes bang {
1599 | from {
1600 | box-shadow: 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1601 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1602 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1603 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1604 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1605 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1606 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1607 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1608 | 0 0 white, 0 0 white, 0 0 white;
1609 | }
1610 | }
1611 |
1612 | @-moz-keyframes bang {
1613 | from {
1614 | box-shadow: 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1615 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1616 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1617 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1618 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1619 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1620 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1621 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1622 | 0 0 white, 0 0 white, 0 0 white;
1623 | }
1624 | }
1625 |
1626 | @-o-keyframes bang {
1627 | from {
1628 | box-shadow: 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1629 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1630 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1631 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1632 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1633 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1634 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1635 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1636 | 0 0 white, 0 0 white, 0 0 white;
1637 | }
1638 | }
1639 |
1640 | @-ms-keyframes bang {
1641 | from {
1642 | box-shadow: 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1643 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1644 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1645 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1646 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1647 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1648 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1649 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1650 | 0 0 white, 0 0 white, 0 0 white;
1651 | }
1652 | }
1653 |
1654 | @keyframes bang {
1655 | from {
1656 | box-shadow: 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1657 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1658 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1659 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1660 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1661 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1662 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1663 | 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white, 0 0 white,
1664 | 0 0 white, 0 0 white, 0 0 white;
1665 | }
1666 | }
1667 |
1668 | @-webkit-keyframes gravity {
1669 | to {
1670 | transform: translateY(200px);
1671 | -moz-transform: translateY(200px);
1672 | -webkit-transform: translateY(200px);
1673 | -o-transform: translateY(200px);
1674 | -ms-transform: translateY(200px);
1675 | opacity: 0;
1676 | }
1677 | }
1678 |
1679 | @-moz-keyframes gravity {
1680 | to {
1681 | transform: translateY(200px);
1682 | -moz-transform: translateY(200px);
1683 | -webkit-transform: translateY(200px);
1684 | -o-transform: translateY(200px);
1685 | -ms-transform: translateY(200px);
1686 | opacity: 0;
1687 | }
1688 | }
1689 |
1690 | @-o-keyframes gravity {
1691 | to {
1692 | transform: translateY(200px);
1693 | -moz-transform: translateY(200px);
1694 | -webkit-transform: translateY(200px);
1695 | -o-transform: translateY(200px);
1696 | -ms-transform: translateY(200px);
1697 | opacity: 0;
1698 | }
1699 | }
1700 |
1701 | @-ms-keyframes gravity {
1702 | to {
1703 | transform: translateY(200px);
1704 | -moz-transform: translateY(200px);
1705 | -webkit-transform: translateY(200px);
1706 | -o-transform: translateY(200px);
1707 | -ms-transform: translateY(200px);
1708 | opacity: 0;
1709 | }
1710 | }
1711 |
1712 | @keyframes gravity {
1713 | to {
1714 | transform: translateY(200px);
1715 | -moz-transform: translateY(200px);
1716 | -webkit-transform: translateY(200px);
1717 | -o-transform: translateY(200px);
1718 | -ms-transform: translateY(200px);
1719 | opacity: 0;
1720 | }
1721 | }
1722 |
1723 | @-webkit-keyframes position {
1724 | 0%,
1725 | 19.9% {
1726 | margin-top: 10%;
1727 | margin-left: 40%;
1728 | }
1729 | 20%,
1730 | 39.9% {
1731 | margin-top: 40%;
1732 | margin-left: 30%;
1733 | }
1734 | 40%,
1735 | 59.9% {
1736 | margin-top: 20%;
1737 | margin-left: 70%;
1738 | }
1739 | 60%,
1740 | 79.9% {
1741 | margin-top: 30%;
1742 | margin-left: 20%;
1743 | }
1744 | 80%,
1745 | 99.9% {
1746 | margin-top: 30%;
1747 | margin-left: 80%;
1748 | }
1749 | }
1750 |
1751 | @-moz-keyframes position {
1752 | 0%,
1753 | 19.9% {
1754 | margin-top: 10%;
1755 | margin-left: 40%;
1756 | }
1757 | 20%,
1758 | 39.9% {
1759 | margin-top: 40%;
1760 | margin-left: 30%;
1761 | }
1762 | 40%,
1763 | 59.9% {
1764 | margin-top: 20%;
1765 | margin-left: 70%;
1766 | }
1767 | 60%,
1768 | 79.9% {
1769 | margin-top: 30%;
1770 | margin-left: 20%;
1771 | }
1772 | 80%,
1773 | 99.9% {
1774 | margin-top: 30%;
1775 | margin-left: 80%;
1776 | }
1777 | }
1778 |
1779 | @-o-keyframes position {
1780 | 0%,
1781 | 19.9% {
1782 | margin-top: 10%;
1783 | margin-left: 40%;
1784 | }
1785 | 20%,
1786 | 39.9% {
1787 | margin-top: 40%;
1788 | margin-left: 30%;
1789 | }
1790 | 40%,
1791 | 59.9% {
1792 | margin-top: 20%;
1793 | margin-left: 70%;
1794 | }
1795 | 60%,
1796 | 79.9% {
1797 | margin-top: 30%;
1798 | margin-left: 20%;
1799 | }
1800 | 80%,
1801 | 99.9% {
1802 | margin-top: 30%;
1803 | margin-left: 80%;
1804 | }
1805 | }
1806 |
1807 | @-ms-keyframes position {
1808 | 0%,
1809 | 19.9% {
1810 | margin-top: 10%;
1811 | margin-left: 40%;
1812 | }
1813 | 20%,
1814 | 39.9% {
1815 | margin-top: 40%;
1816 | margin-left: 30%;
1817 | }
1818 | 40%,
1819 | 59.9% {
1820 | margin-top: 20%;
1821 | margin-left: 70%;
1822 | }
1823 | 60%,
1824 | 79.9% {
1825 | margin-top: 30%;
1826 | margin-left: 20%;
1827 | }
1828 | 80%,
1829 | 99.9% {
1830 | margin-top: 30%;
1831 | margin-left: 80%;
1832 | }
1833 | }
1834 |
1835 | @keyframes position {
1836 | 0%,
1837 | 19.9% {
1838 | margin-top: 10%;
1839 | margin-left: 40%;
1840 | }
1841 | 20%,
1842 | 39.9% {
1843 | margin-top: 40%;
1844 | margin-left: 30%;
1845 | }
1846 | 40%,
1847 | 59.9% {
1848 | margin-top: 20%;
1849 | margin-left: 70%;
1850 | }
1851 | 60%,
1852 | 79.9% {
1853 | margin-top: 30%;
1854 | margin-left: 20%;
1855 | }
1856 | 80%,
1857 | 99.9% {
1858 | margin-top: 30%;
1859 | margin-left: 80%;
1860 | }
1861 | }
1862 |
--------------------------------------------------------------------------------
/src/public/js/bots.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function () {
2 | // Chart Setup
3 |
4 | var labels = [];
5 | var config = {
6 | type: 'line',
7 | data: {
8 |
9 | labels: labels,
10 | datasets: [{
11 | label: 'Bots',
12 | backgroundColor: 'rgba(244, 5, 95, 0.55)',
13 | borderColor: '#f4055f',
14 | data: [],
15 | fill: true,
16 | lineTension: 0,
17 | }]
18 | },
19 | options: {
20 | responsive: true,
21 | bezierCurve: false,
22 | tooltips: {
23 | mode: 'index',
24 | intersect: false,
25 | },
26 | elements: {
27 | point: {
28 | radius: 0
29 | }
30 | },
31 | animation: {
32 | duration: 0
33 | },
34 | legend: {
35 | display: false
36 | },
37 | hover: {
38 | mode: 'nearest',
39 | intersect: true
40 | },
41 | tooltips: {
42 | callbacks: {
43 | title: function(t, d) {
44 | return "Time: "+d.labels[t[0].index]+" seconds";
45 | }
46 | }
47 | },
48 |
49 | scales: {
50 | xAxes: [{
51 | display: true,
52 | scaleLabel: {
53 | display: true,
54 | labelString: 'Seconds',
55 | fontColor: '#cacaca'
56 | },
57 | ticks: {
58 | fontColor: '#cacaca'
59 | }
60 | }],
61 | yAxes: [{
62 | display: true,
63 | scaleLabel: {
64 | display: true,
65 | labelString: 'Online Bots',
66 | fontColor: '#cacaca'
67 | },
68 | ticks: {
69 | suggestedMin: 0,
70 | suggestedMax: 20,
71 | fontColor: '#cacaca'
72 | }
73 | }]
74 | }
75 | }
76 | };
77 |
78 | var ctx = document.getElementById('botsChart').getContext('2d');
79 | window.chart = new Chart(ctx, config);
80 |
81 | // Rooms
82 | var labelsRooms = [];
83 | var config = {
84 | type: 'line',
85 | data: {
86 | labels: labelsRooms,
87 | datasets: [{
88 | label: 'Rooms',
89 | backgroundColor: 'rgba(244, 5, 95, 0.55)',
90 | borderColor: '#f4055f',
91 | data: [],
92 | fill: true,
93 | lineTension: 0,
94 | }]
95 | },
96 | options: {
97 | responsive: true,
98 | bezierCurve: false,
99 | tooltips: {
100 | mode: 'index',
101 | intersect: false,
102 | },
103 | elements: {
104 | point: {
105 | radius: 0
106 | }
107 | },
108 | animation: {
109 | duration: 0
110 | },
111 | legend: {
112 | display: false
113 | },
114 | hover: {
115 | mode: 'nearest',
116 | intersect: true
117 | },
118 | tooltips: {
119 | callbacks: {
120 | title: function(t, d) {
121 | return "Time: "+d.labels[t[0].index]+" seconds";
122 | }
123 | }
124 | },
125 | scales: {
126 | xAxes: [{
127 | display: true,
128 | scaleLabel: {
129 | display: true,
130 | labelString: 'Seconds',
131 | fontColor: '#cacaca'
132 | },
133 | ticks: {
134 | fontColor: '#cacaca'
135 | }
136 | }],
137 | yAxes: [{
138 | display: true,
139 | scaleLabel: {
140 | display: true,
141 | labelString: 'Public Rooms',
142 | fontColor: '#cacaca'
143 | },
144 | ticks: {
145 | suggestedMin: 0,
146 | suggestedMax: 10,
147 | fontColor: '#cacaca'
148 | }
149 | }]
150 | }
151 | }
152 | };
153 |
154 | var ctxRooms = document.getElementById('roomBotsChart').getContext('2d');
155 | window.chartRooms = new Chart(ctxRooms, config);
156 |
157 |
158 |
159 | function update() {
160 | $.ajax({
161 | url: '/api/statistics',
162 | success: (payload) => {
163 | console.log(payload)
164 | if (payload.totalBots == 1) {
165 | document.getElementById('botsOnline').innerHTML = payload.totalBots + ' Bot Online';
166 | } else if (payload.totalBots == 0) {
167 | document.getElementById('botsOnline').innerHTML = 'No bots online :(';
168 | } else {
169 | document.getElementById('botsOnline').innerHTML = payload.totalBots + ' Bots Online';
170 | }
171 | // Set start date for client
172 | if (window.chartConfig == undefined) {
173 | window.chartConfig = {
174 | 'start' : new Date().valueOf(),
175 | 'step' : 0,
176 | 'limit' : 100
177 | }
178 | };
179 |
180 | // User Acitivty Chart
181 | var time = window.chartConfig.step * 5;
182 | window.chart.config.data.labels.push(time);
183 | window.chartConfig.step++;
184 | window.chart.config.data.datasets.forEach(function (dataset) {
185 | dataset.data.push(payload.totalBots);
186 | });
187 | // Check if datapoints need to be removed
188 | // if (window.chart.data.datasets[0].data.length > 20) {
189 | // // window.chart.data.datasets[0].data = window.chart.data.datasets[0].data.slice(1);
190 | // // window.chart.data.labels = window.chart.data.labels.slice(1);
191 | // }
192 | // window.chart.options.scales.xAxes[0].scaleLabel.labelString = "Minutes"
193 | // window.chartRooms.options.scales.xAxes[0].scaleLabel.labelString = "Minutes"
194 | if (window.chart.data.datasets[0].data.length >= window.chartConfig.limit) {
195 | window.chart.data.datasets[0].data = window.chart.data.datasets[0].data.slice(1);
196 | window.chart.data.labels = window.chart.data.labels.slice(1);
197 | window.chartRooms.data.datasets[0].data = window.chartRooms.data.datasets[0].data.slice(1);
198 | window.chartRooms.data.labels = window.chartRooms.data.labels.slice(1);
199 | }
200 |
201 | // Room Acitivty Chart
202 | window.chartRooms.config.data.labels.push(time);
203 |
204 | window.chartRooms.config.data.datasets.forEach(function (dataset) {
205 | dataset.data.push(payload.totalRooms);
206 | });
207 | window.chart.update();
208 | window.chartRooms.update();
209 |
210 | }
211 | });
212 | }
213 |
214 | update();
215 | setInterval(update, 5000);
216 | });
--------------------------------------------------------------------------------
/src/public/js/home.js:
--------------------------------------------------------------------------------
1 | let lookup = ['Live', '24h', 'Week', 'Month', 'All Time'];
2 | let validOptions = ['Show Average','Show Minimum','Show Maximum'];
3 | let globalVersion;
4 |
5 | $('.dropdown-menu.keep-open').on('click', function (e) {
6 | e.stopPropagation();
7 | });
8 |
9 | window.onload = function () {
10 | var loadTime = window.performance.timing.domContentLoadedEventEnd-window.performance.timing.navigationStart;
11 | console.log('Page load time is '+ loadTime);
12 | document.getElementById('loadTime').innerHTML = ' ' + loadTime + 'ms';
13 | // Set start date for client
14 | if (window.chartConfig == undefined) {
15 | window.chartConfig = {
16 | 'start': new Date().valueOf(),
17 | 'step': 0,
18 | 'limit': 100,
19 | 'version' : globalVersion,
20 | 'options' : {
21 | 'ave' : true,
22 | 'min' : false,
23 | 'max' : false,
24 | 'aveR' : true,
25 | 'minR' : false,
26 | 'maxR' : false
27 | }
28 | }
29 | };
30 | saveSettings(window.chartConfig);
31 | }
32 |
33 | function compare(previousVersion, currentVersion){
34 | var previousVerArr = previousVersion.split('.').map(Number);
35 | var currentVerArr = currentVersion.split('.').map(Number);
36 |
37 | if (currentVerArr[0] > previousVerArr[0]) {
38 | return "major";
39 | } else if (currentVerArr[0] == previousVerArr[0] && currentVerArr[1] > previousVerArr[1]) {
40 | return "minor";
41 | } else if (currentVerArr[0] == previousVerArr[0] && currentVerArr[1] == previousVerArr[1] && currentVerArr[2] > previousVerArr[2]) {
42 | return "patch";
43 | } else {
44 | return "none";
45 | };
46 | };
47 |
48 | function saveSettings(settings) {
49 | if (localStorage && typeof settings == 'object') localStorage.setItem('dhSettings', JSON.stringify(settings));
50 | };
51 |
52 | function toggleFullScreen() {
53 | if (!document.fullscreenElement) {
54 | document.documentElement.requestFullscreen();
55 | $(".fsl").addClass('min-w');
56 |
57 | } else {
58 | if (document.exitFullscreen) {
59 | document.exitFullscreen();
60 | $(".fsl").removeClass('min-w');
61 |
62 | }
63 | }
64 | }
65 |
66 | function readDropDownSettings() {
67 | document.getElementById('dropOptAve').checked = window.chartConfig.options.ave;
68 | document.getElementById('dropOptMin').checked = window.chartConfig.options.min;
69 | document.getElementById('dropOptMax').checked = window.chartConfig.options.max;
70 | document.getElementById('dropOptAveR').checked = window.chartConfig.options.aveR;
71 | document.getElementById('dropOptMinR').checked = window.chartConfig.options.minR;
72 | document.getElementById('dropOptMaxR').checked = window.chartConfig.options.maxR;
73 | // Room Chart
74 | }
75 |
76 | function verReload() {
77 | window.chartConfig.version = globalVersion;
78 | saveSettings(window.chartConfig);
79 | location.reload(true);
80 | }
81 |
82 | function dropdownUpdate(element) {
83 | document.getElementById(element.parentElement.getAttribute('aria-labelledby')).innerText = element.innerText;
84 | changeDataset(((element.parentElement.getAttribute('aria-labelledby') == 'userActivityChartTimeframe') ? 'statsChart' : 'roomsChart'), element.innerText)
85 | }
86 |
87 | function optionUpdate(element) {
88 | if (validOptions.indexOf(element.innerText.trim()) == -1) {
89 | console.log(`ERROR: Received: ${element.innerText}`);
90 | } else {
91 | // console.log(element.parentElement.parentElement.parentElement.children[0].innerText.trim())
92 | // console.log(chartConfig.options["_"+element.parentElement.parentElement.parentElement.children[0].innerText.trim()])
93 | let canvasID = 'statsChart';
94 | let isRoomCheck = '';
95 | if (element.parentElement.getAttribute('aria-labelledby') == 'roomActivityChartOptions') {
96 | canvasID = 'roomsChart';
97 | isRoomCheck = 'R';
98 | }
99 | let configIndex = validOptions.indexOf(element.innerText.trim());
100 | let miniOpt = validOptions[configIndex].slice(5,8)+isRoomCheck;
101 | // console.log(`MiniOpt: ${miniOpt} | miniOptD: dropOpt${miniOpt}`)
102 | window.chartConfig.options[miniOpt.charAt(0).toLowerCase() + miniOpt.slice(1)] = document.getElementById("dropOpt"+miniOpt).checked;
103 | // Add or remove
104 | let dataName = ``;
105 | if (canvasID == 'statsChart') {
106 | dataName = 'totalOnline'
107 | metaIndex = 0;
108 | curChart = window.chart;
109 | } else {
110 | dataName = 'totalRooms'
111 | metaIndex = 1;
112 | curChart = window.chartRooms;
113 | }
114 | let isHidden = !(document.getElementById("dropOpt"+miniOpt).checked);
115 | curChart.data.datasets[configIndex]._meta[metaIndex].hidden = isHidden;
116 | saveSettings(window.chartConfig);
117 | curChart.update();
118 | }
119 | }
120 |
121 | function changeDataset(canvasID, value) {
122 | // console.log(`Canvas ${canvasID} | value ${value}`)
123 | let curChart;
124 | let lookupIndex = lookup.indexOf(value);
125 | let roomCheck = '';
126 | if (canvasID == 'statsChart') {
127 | curChart = window.chart;
128 | liveDataName = 'totalOnline';
129 | } else {
130 | curChart = window.chartRooms;
131 | liveDataName = 'totalRooms';
132 | roomCheck = 'R';
133 | }
134 | // Update chart
135 | // What data do we want to update
136 | if (lookupIndex != -1) {
137 | // (new Date).toLocaleTimeString()
138 | curChart.options.scales.xAxes[0].scaleLabel.labelString = "Time";
139 | if (lookupIndex > 0) {
140 | curChart.data.datasets[0].data = statsConfig[lookupIndex].map(({ [`ave${roomCheck}`]: val }) => val);
141 | curChart.data.datasets[1].data = statsConfig[lookupIndex].map(({ [`min${roomCheck}`]: val }) => val);
142 | curChart.data.datasets[2].data = statsConfig[lookupIndex].map(({ [`max${roomCheck}`]: val }) => val);
143 | } else {
144 | curChart.data.datasets[0].data = statsConfig[lookupIndex].map(({ [liveDataName]: val }) => val);
145 | curChart.data.datasets[1].data = [];
146 | curChart.data.datasets[2].data = [];
147 | }
148 | }
149 |
150 | if (lookupIndex == 0) {
151 | // Live
152 | curChart.data.labels = statsConfig[0].map(({ [`statsTime`]: val }) => val);
153 | curChart.options.scales.xAxes[0].scaleLabel.labelString = "Seconds";
154 | curChart.options.tooltips.callbacks.title = function(t, d) {
155 | return "Time: "+d.labels[t[0].index]+" seconds";
156 | }
157 | // window.chartConfig.step = 0;
158 | // Consider storing this data in the background
159 | } else if (lookupIndex == 1) {
160 | // 24h
161 | // Slice as "2021-04-18-02" is given to get day, then add hours
162 | curChart.data.labels = statsConfig[1].map(({ [`queryDay`]: val }) => parseInt(val.slice(-2)));
163 | curChart.options.tooltips.callbacks.title = function(t, d) {
164 | return "Time: "+d.labels[t[0].index]+" hours";
165 | }
166 | } else if (lookupIndex == 2) {
167 | // Weeks
168 | let days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
169 | curChart.data.labels = statsConfig[2].map(({ [`queryDay`]: val }) => days[new Date(val).getDay()]);
170 | curChart.options.tooltips.callbacks.title = function(t, d) {
171 | return "Time: "+d.labels[t[0].index];
172 | }
173 | } else if (lookupIndex == 3) {
174 | // Month
175 | curChart.data.labels = statsConfig[3].map(({ [`queryDay`]: val }) => new Date(val).toLocaleDateString());
176 | curChart.options.tooltips.callbacks.title = function(t, d) {
177 | return "Time: "+d.labels[t[0].index];
178 | }
179 | } else if (lookupIndex == 4) {
180 | // All Time
181 | // Store start date as value and work out day, week, month, year
182 | curChart.data.labels = statsConfig[4].map(({ [`queryDay`]: val }) => new Date(val).toLocaleDateString());
183 | curChart.options.tooltips.callbacks.title = function(t, d) {
184 | return "Time: "+d.labels[t[0].index];
185 | }
186 | } else {
187 | console.log(`Error | lookupIndex ${lookupIndex} | Input: canvasID ${canvasID} , value ${value}`);
188 | }
189 | curChart.update();
190 | }
191 |
192 | $(document).ready(function () {
193 |
194 | // Patreon Update
195 | // Update every now and then, patreon oauth kind of a pain to work with. May change to github sponsorships
196 | const users = ['Sean McGinty'];
197 | const activeUser = users[Math.floor(Math.random() * users.length)]
198 | $('#sponsor-main').text(activeUser);
199 | $('#sponsor-sub').text(activeUser);
200 |
201 | // Load settings from localStorage
202 | if (localStorage) {
203 | var savedExploitSettings = localStorage.getItem('dhSettings');
204 | if (savedExploitSettings != null) {
205 | window.chartConfig = JSON.parse(localStorage.getItem('dhSettings'));
206 | window.chartConfig.start = new Date().valueOf();
207 | window.chartConfig.step = 0;
208 | }
209 | }
210 |
211 | // Get version info
212 | $.ajax({
213 | url: '/api/version?dogestats',
214 | success: (payload) => {
215 | globalVersion = payload.version;
216 | if (localStorage) {
217 | if (savedExploitSettings != null) {
218 | // Check version
219 | if (window.chartConfig != null) {
220 | if (window.chartConfig.version != globalVersion) {
221 | // Show message to update / reload page.
222 | // location.reload(true);
223 | updateType = compare(window.chartConfig.version, globalVersion);
224 | console.log(`Latest Version: ${globalVersion} | Current Version: ${window.chartConfig.version} | UpdateType: ${updateType}`);
225 | document.getElementById("ver-alert").classList.remove("show");
226 | }
227 | }
228 | }
229 | }
230 | }
231 | })
232 |
233 | // [ [ live ], [ 24h - average ], [ week - average ], [ month - average ], [ alltime - average ], [ 24h - min ], [ 24h - max ], [ week - min ], [ week - max ], [ month - min ], [ month - max ], [ alltime - min ], [ alltime - max ] ]
234 | // window.statsConfig = [[],[],[],[],[],[],[],[],[],[],[],[],[]];
235 | window.statsConfig = [[],[],[],[],[]];
236 |
237 | function getLongTermData() {
238 | $.ajax({
239 | url: '/api/mysql?time=24h',
240 | success: (payload) => {
241 | statsConfig[1] = payload;
242 |
243 | // Setup
244 | chart.data.datasets[0]._meta[0].hidden = !window.chartConfig.options.ave;
245 | chart.data.datasets[1]._meta[0].hidden = !window.chartConfig.options.min;
246 | chart.data.datasets[2]._meta[0].hidden = !window.chartConfig.options.max;
247 | chartRooms.data.datasets[0]._meta[1].hidden = !window.chartConfig.options.aveR;
248 | chartRooms.data.datasets[1]._meta[1].hidden = !window.chartConfig.options.minR;
249 | chartRooms.data.datasets[2]._meta[1].hidden = !window.chartConfig.options.maxR;
250 | changeDataset('statsChart', '24h');
251 | changeDataset('roomsChart', '24h');
252 | }
253 | })
254 | $.ajax({
255 | url: '/api/mysql?time=week',
256 | success: (payload) => {
257 | statsConfig[2] = payload;
258 | }
259 | })
260 | $.ajax({
261 | url: '/api/mysql?time=month',
262 | success: (payload) => {
263 | statsConfig[3] = payload;
264 | }
265 | })
266 | $.ajax({
267 | url: '/api/mysql?time=alltime',
268 | success: (payload) => {
269 | statsConfig[4] = payload;
270 | }
271 | })
272 | }
273 |
274 | getLongTermData();
275 |
276 | function update() {
277 | $.ajax({
278 | url: '/api/statistics',
279 | success: (payload) => {
280 | // console.log(payload)
281 | // Total Rooms
282 | document.getElementById('roomCount').innerHTML = payload.totalRooms;
283 | document.getElementById('roomFix').innerText = (payload.totalRooms == 1) ? '' : 's';
284 | // Total Online People in all Rooms
285 | document.getElementById('userCount').innerHTML = payload.totalOnline;
286 | // Scheduled Rooms Count
287 | document.getElementById('scheduledCount').innerHTML = payload.totalScheduled;
288 | document.getElementById('scheduledFix').innerText = (payload.totalScheduled == 1) ? '' : 's';
289 | // Largest Room Name
290 | document.getElementById('topRoomName').innerHTML = payload.topRoom.name;
291 | document.getElementById('topUserFix').innerText = (payload.topRoom.listeners == 1) ? '' : 's';
292 | // Largest Room Listners
293 | document.getElementById('topUserCount').innerHTML = payload.topRoom.listeners;
294 | // Longest Room
295 | document.getElementById('longestRoom').innerHTML = payload.longestRoom.name;
296 | document.getElementById('longestUserCount').innerHTML = payload.longestRoom.listeners;
297 | document.getElementById('longestUserFix').innerText = (payload.longestRoom.listeners == 1) ? '' : 's';
298 | // Newest Room
299 | document.getElementById('newestRoom').innerHTML = payload.newestRoom.name;
300 | document.getElementById('newestUserCount').innerHTML = payload.newestRoom.listeners;
301 | document.getElementById('newestUserFix').innerText = (payload.newestRoom.listeners == 1) ? '' : 's';
302 | document.getElementById('botsProvidingTelem').innerHTML = payload.totalBotsSendingTelemetry + '/' + payload.totalBotsOnline + ' bots providing telemetry via dogehouse.js & dogehouse.py>'
303 |
304 | if (payload.totalBotsOnline == 1) {
305 | document.getElementById('botsOnline').innerHTML = payload.totalBotsOnline + ' Bot Online';
306 | } else if (payload.totalBotsOnline == 0) {
307 | document.getElementById('botsOnline').innerHTML = 'No bots online :(';
308 | } else {
309 | document.getElementById('botsOnline').innerHTML = payload.totalBotsOnline + ' Bots Online';
310 | }
311 |
312 |
313 | // Get room status
314 | function getStatus(element, payloadCreation) {
315 |
316 | let currentTime = new Date().valueOf()
317 | // let serverOffset = (1000*60*60*-12)
318 | let roomCreation = new Date(payloadCreation).valueOf();
319 | // let timeDiff = (currentTime - roomCreation - serverOffset) / (1000 * 60) ; // minutes
320 | let timeDiff = (currentTime - roomCreation) / (1000 * 60); // minutes
321 |
322 | // let timeDiffHours = timeDiff / 60
323 | // let shortenedText = ~~timeDiffHours;
324 |
325 | // shortenedText === 24 ? shortenedText = 0 : console.log(shortenedText)
326 |
327 | // let minutes = ~~((timeDiffHours - ~~timeDiffHours) * 60)
328 | // let days = 0;
329 | // if (timeDiffHours > 23) {
330 | // days = ~~(timeDiff / 60 / 24);
331 | // }
332 | // let hours = shortenedText - days * 24;
333 |
334 | let days = ~~(timeDiff / 60 / 24)
335 | let minutes = ~~(timeDiff % 60)
336 | let hours = ~~(timeDiff / 60 % 24)
337 |
338 | function changeText(text = 'Generating...') {
339 |
340 | element.innerHTML = text + " (" + (days === 0 ? "" : "Days: " + days + " | ") + "Hours: " + hours + " | Minutes: " + minutes + ")";
341 |
342 | // shortened text is 217
343 |
344 | }
345 |
346 | switch (true) {
347 |
348 | case (timeDiff < 30): {
349 | changeText("⛽️ Fueling Rocket")
350 | break;
351 | }
352 | case (timeDiff < 60): {
353 | changeText("🚀 Taking Off")
354 | break;
355 | }
356 | case (timeDiff < 240): {
357 | changeText("🚀✨ In Space")
358 | break;
359 | }
360 | case (timeDiff < 480): {
361 | changeText("🚀🌕 Approaching Moon")
362 | break;
363 | }
364 | case (timeDiff < 1440): {
365 | changeText("🌕🐕 Lunar Doge")
366 | break;
367 | }
368 | case (timeDiff < 2880): {
369 | changeText("🚀☀️ Approaching Sun")
370 | break;
371 | }
372 | case (timeDiff < 5760): {
373 | changeText("☀️🐕 Solar Doge")
374 | break;
375 | }
376 | case (timeDiff < 11520): {
377 | changeText("🚀🌌 Approaching Galaxy")
378 | break;
379 | }
380 | case (timeDiff < 23040): {
381 | changeText("🌌🐕 Galatic Doge")
382 | break;
383 | }
384 | case (timeDiff < 23041): {
385 | changeText("🪐👾 Spotted Life")
386 | break;
387 | }
388 | }
389 |
390 | }
391 | getStatus(document.getElementById('timeOnline'), payload.topRoom.created_at)
392 | getStatus(document.getElementById('newestTimeOnline'), payload.newestRoom.created_at)
393 | getStatus(document.getElementById('longestTimeOnline'), payload.longestRoom.created_at)
394 |
395 | // Read settings
396 | readDropDownSettings();
397 |
398 | // Stats config
399 | statsConfig[0].push({
400 | 'totalRooms' : payload.totalRooms,
401 | 'totalOnline' : payload.totalOnline,
402 | 'statsTime' : window.chartConfig.step * 5
403 | });
404 |
405 | window.chartConfig.step++;
406 | var time = window.chartConfig.step * 5;
407 |
408 | // Check for removal
409 | if (window.chart.data.datasets[0].data.length >= window.chartConfig.limit) {
410 | window.chart.data.datasets[0].data = window.chart.data.datasets[0].data.slice(1);
411 | }
412 | if (statsConfig.length >= window.chartConfig.limit) {
413 | statsConfig[0] = statsConfig[0].slice(1);
414 | }
415 |
416 | // User Acitivty Chart
417 | changeDataset('statsChart',document.getElementById("userActivityChartTimeframe").innerText.trim())
418 |
419 | // Room Acitivty Chart
420 | changeDataset('roomsChart',document.getElementById("roomActivityChartTimeframe").innerText.trim())
421 |
422 | // Update
423 | window.chart.update();
424 | window.chartRooms.update();
425 |
426 | var userRoomChart = document.getElementById("botuserChart");
427 | var userRoomData = {
428 | labels: [
429 | "Users Online",
430 | "Bots Online"
431 | ],
432 | datasets: [
433 |
434 | {
435 | borderColor: false,
436 | data: [payload.totalOnline - payload.totalBotsOnline, payload.totalBotsOnline],
437 | backgroundColor: [
438 | "rgba(244, 5, 95, 0.55)",
439 | "rgba(5, 89, 244, 0.55)",
440 | "rgba(244, 140, 5, 0.55)"
441 | ]
442 | }]
443 | };
444 |
445 | let UserBotChart = new Chart(userRoomChart, {
446 | type: 'doughnut',
447 | data: userRoomData,
448 | options: {
449 | animation: false,
450 | legend: {
451 | color: '#cacaca'
452 | },
453 | defaultFontColor: '#000'
454 | }
455 | });
456 |
457 | // rooms / users
458 | var userRoomChart = document.getElementById("userroomChart");
459 | var userRoomData = {
460 | labels: [
461 | "Users Online",
462 | "Rooms",
463 | "Bots Online"
464 | ],
465 | datasets: [
466 |
467 | {
468 | borderColor: false,
469 | data: [payload.totalOnline - payload.totalBotsOnline, payload.totalRooms, payload.totalBotsOnline],
470 | backgroundColor: [
471 | "rgba(244, 5, 95, 0.55)",
472 | "rgba(5, 89, 244, 0.55)",
473 | "rgba(244, 140, 5, 0.55)"
474 | ]
475 | }]
476 | };
477 |
478 | let UserRoomDoughnutChart = new Chart(userRoomChart, {
479 | type: 'doughnut',
480 | data: userRoomData,
481 | options: {
482 | segmentShowStroke: false,
483 | animation: false,
484 | legend: {
485 | color: '#cacaca'
486 | },
487 | }
488 | });
489 |
490 | }
491 | });
492 | $.ajax({
493 | url: '/api/bots',
494 | success: (payload) => {
495 | if (payload.bots != undefined) {
496 | // console.log(payload)
497 | let uniqueBots = [];
498 | for (let i = 0; i < payload.bots.length; i++) {
499 | if (payload.bots[i].bot != undefined) {
500 | uniqueBots.push(payload.bots[i]);
501 | }
502 | }
503 | // console.log(uniqueBots)
504 | if (document.getElementsByClassName("uuid").length > uniqueBots.length) {
505 | // Remove all for now
506 | document.getElementById('testBots').innerHTML = "";
507 | }
508 | for (i = 0; i < uniqueBots.length; i++) {
509 | var botExists = false;
510 | if (document.getElementById("testBots").innerText.indexOf(uniqueBots[i].bot.uuid) == -1) {
511 | document.getElementById('testBots').innerHTML +=
512 | `
513 |
514 |
515 |
516 |
517 |
518 | ${uniqueBots[i].bot.username}
519 | ${uniqueBots[i].bot.uuid}
520 | ${(uniqueBots[i].room.name == null) ? "" : uniqueBots[i].room.name}
521 |
522 |
523 |
524 |
525 | ${(uniqueBots[i].room.listening == null) ? 0 : uniqueBots[i].room.listening}
526 |
527 |
528 |
529 |
530 |
531 | `
532 | }
533 | for (let j = 0; j < uniqueBots.length; j++) {
534 | if (document.getElementsByClassName("uuid")[j] != undefined) {
535 | if (uniqueBots[i].bot.uuid == document.getElementsByClassName("uuid")[j].innerText) {
536 | botExists = true;
537 | document.getElementsByClassName("uuid")[j].parentElement.parentElement.children[2].innerHTML = ` ` + ((uniqueBots[i].room.listening == "No Room") ? 0 : uniqueBots[i].room.listening);
538 | document.getElementsByClassName("uuid")[j].parentElement.children[2].innerText = (uniqueBots[i].room.name == null) ? "" : uniqueBots[i].room.name;
539 | // document.getElementsByClassName("uuid")[j].parentElement.parentElement.children[0].src = uniqueBots[i].bot.avatar;
540 | }
541 | }
542 | }
543 | if (i == (uniqueBots.length - 1) && botExists == false) {
544 | document.getElementById('testBots').children[i].remove();
545 | document.getElementById('testBots').children[i].remove();
546 | }
547 | }
548 | }
549 | }
550 | })
551 | }
552 |
553 | function checkAPIStatus() {
554 | try {
555 | $.ajax({
556 | url: 'https://api.dogegarden.net',
557 | timeout: 1500,
558 | error: function() {
559 | $("#api-alert").removeClass('show');
560 | },
561 | })
562 | } catch(err) {
563 | //no 1 cares about err so we ignore it :)
564 | }
565 | }
566 | checkAPIStatus()
567 | update();
568 | setInterval(update, 5000);
569 | setInterval(checkAPIStatus, 10000);
570 | });
571 |
--------------------------------------------------------------------------------
/src/public/js/rooms.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function(){
2 | function update() {
3 | $.ajax({
4 | url: '/api/rooms',
5 | success: (payload) => {
6 | let rooms = payload.rooms
7 | console.log(rooms)
8 | document.getElementById("roomCount").innerText = rooms.length;
9 | for (i=0; i
14 |
15 |
16 |
17 |
18 |
19 | ${rooms[i].name}
20 | ${rooms[i].id}
21 |
22 |
23 |
24 | ${rooms[i].numPeopleInside}
25 |
26 |
27 |
28 |
29 |
30 | `
31 | }
32 | document.getElementsByClassName("right-col-rooms")[i].innerHTML = ` `+rooms[i].numPeopleInside;
33 | if (rooms.some(rooms => rooms.id == document.getElementsByClassName("account-text")[i].children[1].innerText)) {
34 | // Room Exists
35 | console.log(`Valid Room at Index ${i}`)
36 | } else {
37 | // Remove Room
38 | // Not working?
39 | console.log(`Remove ${i}`)
40 | // document.getElementsByClassName("accounts-wrapper")[0].children[i*2-1].remove();
41 | // document.getElementsByClassName("accounts-wrapper")[0].children[i*2-1].remove();
42 | }
43 | };
44 | }
45 | });
46 | }
47 |
48 | update();
49 | setInterval(update, 10000);
50 | });
51 |
--------------------------------------------------------------------------------
/src/public/js/statistics.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function(){
2 | // User Activity
3 |
4 | var labels = [];
5 | var config = {
6 | type: 'line',
7 |
8 | data: {
9 |
10 | labels: labels,
11 | datasets: [{
12 | label: 'Average Users',
13 | backgroundColor: 'rgba(244, 5, 95, 0.55)',
14 | borderColor: '#f4055f',
15 | data: [],
16 | fill: false,
17 | lineTension: 0,
18 | },
19 | {
20 | label: 'Minimum Users',
21 | fill: false,
22 | backgroundColor: 'rgba(5, 89, 244, 0.55)',
23 | borderColor: 'rgba(5, 89, 244, 0.55)',
24 | borderDash: [5, 5],
25 | data: [],
26 | lineTension: 0,
27 |
28 | },
29 | {
30 | label: 'Maximum Users',
31 | fill: false,
32 | backgroundColor: 'rgba(244, 140, 5, 0.55)',
33 | borderColor: 'rgba(244, 140, 5, 0.55)',
34 | borderDash: [5, 5],
35 | data: [],
36 | lineTension: 0,
37 |
38 | }
39 | ]
40 | },
41 | options: {
42 | responsive: true,
43 | bezierCurve: false,
44 | tooltips: {
45 | mode: 'index',
46 | intersect: false,
47 | },
48 | elements: {
49 | point: {
50 | radius: 0
51 | }
52 | },
53 | animation: {
54 | duration: 0
55 | },
56 | legend: {
57 | display: true,
58 | labels: {
59 | filter: (legendItem, data) => data.datasets[0].data[legendItem.index] != 0
60 | },
61 | onClick: function(e,l) {
62 | window.chartConfig.options[validOptions[l.datasetIndex].slice(5,8).toLowerCase()] = l.hidden;
63 | window.chart.data.datasets[l.datasetIndex]._meta[0].hidden = !(l.hidden);
64 | saveSettings(window.chartConfig);
65 | readDropDownSettings();
66 | window.chart.update();
67 | }
68 | },
69 | hover: {
70 | mode: 'nearest',
71 | intersect: false
72 | },
73 | tooltips: {
74 | callbacks: {
75 | title: function(t, d) {
76 | return "Time: "+d.labels[t[0].index]+" seconds";
77 | }
78 | }
79 | },
80 |
81 | scales: {
82 | xAxes: [{
83 | display: true,
84 | scaleLabel: {
85 | display: true,
86 | labelString: 'Seconds',
87 | fontColor: '#cacaca'
88 | },
89 | ticks: {
90 | fontColor: '#cacaca'
91 | }
92 | }],
93 | yAxes: [{
94 | display: true,
95 | scaleLabel: {
96 | display: true,
97 | labelString: 'Online Users',
98 | fontColor: '#cacaca'
99 | },
100 | ticks: {
101 | suggestedMin: 0,
102 | suggestedMax: 30,
103 | fontColor: '#cacaca'
104 | }
105 | }]
106 | }
107 | }
108 | };
109 |
110 | var ctx = document.getElementById('statsChart').getContext('2d');
111 | window.chart = new Chart(ctx, config);
112 |
113 | // Rooms
114 | var labelsRooms = [];
115 | var config = {
116 | type: 'line',
117 | data: {
118 | labels: labelsRooms,
119 | datasets: [{
120 | label: 'Average Rooms',
121 | backgroundColor: 'rgba(244, 5, 95, 0.55)',
122 | borderColor: '#f4055f',
123 | data: [],
124 | fill: false,
125 | lineTension: 0,
126 | },
127 | {
128 | label: 'Minimum Rooms',
129 | fill: false,
130 | backgroundColor: 'rgba(5, 89, 244, 0.55)',
131 | borderColor: 'rgba(5, 89, 244, 0.55)',
132 | borderDash: [5, 5],
133 | data: [],
134 | lineTension: 0,
135 |
136 | },
137 | {
138 | label: 'Maximum Rooms',
139 | fill: false,
140 | backgroundColor: 'rgba(244, 140, 5, 0.55)',
141 | borderColor: 'rgba(244, 140, 5, 0.55)',
142 | borderDash: [5, 5],
143 | data: [],
144 | lineTension: 0,
145 |
146 | }
147 | ]
148 | },
149 | options: {
150 | responsive: true,
151 | bezierCurve: false,
152 | tooltips: {
153 | mode: 'index',
154 | intersect: false,
155 | },
156 | elements: {
157 | point: {
158 | radius: 0
159 | }
160 | },
161 | animation: {
162 | duration: 0
163 | },
164 | legend: {
165 | display: true,
166 | labels: {
167 | filter: (legendItem, data) => data.datasets[0].data[legendItem.index] != 0
168 | },
169 | onClick: function(e,l) {
170 | window.chartConfig.options[validOptions[l.datasetIndex].slice(5,8).toLowerCase()+"R"] = l.hidden;
171 | window.chartRooms.data.datasets[l.datasetIndex]._meta[1].hidden = !(l.hidden);
172 | saveSettings(window.chartConfig);
173 | readDropDownSettings();
174 | window.chartRooms.update();
175 | }
176 | },
177 | hover: {
178 | mode: 'nearest',
179 | intersect: false
180 | },
181 | tooltips: {
182 | callbacks: {
183 | title: function(t, d) {
184 | return "Time: "+d.labels[t[0].index]+" seconds";
185 | }
186 | }
187 | },
188 | scales: {
189 | xAxes: [{
190 | display: true,
191 | scaleLabel: {
192 | display: true,
193 | labelString: 'Seconds',
194 | fontColor: '#cacaca'
195 | },
196 | ticks: {
197 | fontColor: '#cacaca'
198 | }
199 | }],
200 | yAxes: [{
201 | display: true,
202 | scaleLabel: {
203 | display: true,
204 | labelString: 'Public Rooms',
205 | fontColor: '#cacaca'
206 | },
207 | ticks: {
208 | suggestedMin: 0,
209 | suggestedMax: 10,
210 | fontColor: '#cacaca'
211 | }
212 | }]
213 | }
214 | }
215 | };
216 |
217 | var ctxRooms = document.getElementById('roomsChart').getContext('2d');
218 | window.chartRooms = new Chart(ctxRooms, config);
219 |
220 | // // Outages
221 | // var labelsOutages = [];
222 | // var config = {
223 | // type: 'line',
224 | // data: {
225 | // labels: labelsOutages,
226 | // datasets: [{
227 | // label: 'Outages',
228 | // backgroundColor: 'rgba(244, 5, 95, 0.55)',
229 | // borderColor: '#f4055f',
230 | // data: [],
231 | // fill: true,
232 | // lineTension: 0,
233 | // }]
234 | // },
235 | // options: {
236 | // responsive: true,
237 | // bezierCurve: false,
238 | // tooltips: {
239 | // mode: 'index',
240 | // intersect: false,
241 | // },
242 | // animation: {
243 | // duration: 0
244 | // },
245 | // legend: {
246 | // display: false
247 | // },
248 | // hover: {
249 | // mode: 'nearest',
250 | // intersect: true
251 | // },
252 | // tooltips: {
253 | // callbacks: {
254 | // title: function(t, d) {
255 | // return "Time: "+d.labels[t[0].index]+" hours";
256 | // }
257 | // }
258 | // },
259 | // scales: {
260 | // xAxes: [{
261 | // display: true,
262 | // scaleLabel: {
263 | // display: true,
264 | // labelString: 'Hours',
265 | // fontColor: '#cacaca'
266 | // },
267 | // ticks: {
268 | // fontColor: '#cacaca'
269 | // }
270 | // }],
271 | // yAxes: [{
272 | // display: true,
273 | // scaleLabel: {
274 | // display: true,
275 | // labelString: 'Outages',
276 | // fontColor: '#cacaca'
277 | // },
278 | // ticks: {
279 | // suggestedMin: 0,
280 | // suggestedMax: 10,
281 | // fontColor: '#cacaca'
282 | // }
283 | // }]
284 | // }
285 | // }
286 | // };
287 |
288 | // var ctxOutages = document.getElementById('outageChart').getContext('2d');
289 | // window.chartOutages = new Chart(ctxOutages, config);
290 |
291 |
292 | });
293 |
--------------------------------------------------------------------------------
/src/public/js/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | window.chartColors = {
4 | red: 'rgb(255, 99, 132)',
5 | orange: 'rgb(255, 159, 64)',
6 | yellow: 'rgb(255, 205, 86)',
7 | green: 'rgb(75, 192, 192)',
8 | blue: '#f4055f',
9 | purple: 'rgb(153, 102, 255)',
10 | grey: 'rgb(201, 203, 207)'
11 | };
12 |
13 | (function(global) {
14 | var MONTHS = [
15 | 'January',
16 | 'February',
17 | 'March',
18 | 'April',
19 | 'May',
20 | 'June',
21 | 'July',
22 | 'August',
23 | 'September',
24 | 'October',
25 | 'November',
26 | 'December'
27 | ];
28 |
29 | var COLORS = [
30 | '#4dc9f6',
31 | '#f67019',
32 | '#f53794',
33 | '#537bc4',
34 | '#acc236',
35 | '#166a8f',
36 | '#00a950',
37 | '#58595b',
38 | '#8549ba'
39 | ];
40 |
41 | var Samples = global.Samples || (global.Samples = {});
42 | var Color = global.Color;
43 |
44 | Samples.utils = {
45 | // Adapted from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
46 | srand: function(seed) {
47 | this._seed = seed;
48 | },
49 |
50 | rand: function(min, max) {
51 | var seed = this._seed;
52 | min = min === undefined ? 0 : min;
53 | max = max === undefined ? 1 : max;
54 | this._seed = (seed * 9301 + 49297) % 233280;
55 | return min + (this._seed / 233280) * (max - min);
56 | },
57 |
58 | numbers: function(config) {
59 | var cfg = config || {};
60 | var min = cfg.min || 0;
61 | var max = cfg.max || 1;
62 | var from = cfg.from || [];
63 | var count = cfg.count || 8;
64 | var decimals = cfg.decimals || 8;
65 | var continuity = cfg.continuity || 1;
66 | var dfactor = Math.pow(10, decimals) || 0;
67 | var data = [];
68 | var i, value;
69 |
70 | for (i = 0; i < count; ++i) {
71 | value = (from[i] || 0) + this.rand(min, max);
72 | if (this.rand() <= continuity) {
73 | data.push(Math.round(dfactor * value) / dfactor);
74 | } else {
75 | data.push(null);
76 | }
77 | }
78 |
79 | return data;
80 | },
81 |
82 | labels: function(config) {
83 | var cfg = config || {};
84 | var min = cfg.min || 0;
85 | var max = cfg.max || 100;
86 | var count = cfg.count || 8;
87 | var step = (max - min) / count;
88 | var decimals = cfg.decimals || 8;
89 | var dfactor = Math.pow(10, decimals) || 0;
90 | var prefix = cfg.prefix || '';
91 | var values = [];
92 | var i;
93 |
94 | for (i = min; i < max; i += step) {
95 | values.push(prefix + Math.round(dfactor * i) / dfactor);
96 | }
97 |
98 | return values;
99 | },
100 |
101 | months: function(config) {
102 | var cfg = config || {};
103 | var count = cfg.count || 12;
104 | var section = cfg.section;
105 | var values = [];
106 | var i, value;
107 |
108 | for (i = 0; i < count; ++i) {
109 | value = MONTHS[Math.ceil(i) % 12];
110 | values.push(value.substring(0, section));
111 | }
112 |
113 | return values;
114 | },
115 |
116 | color: function(index) {
117 | return COLORS[index % COLORS.length];
118 | },
119 |
120 | transparentize: function(color, opacity) {
121 | var alpha = opacity === undefined ? 0.5 : 1 - opacity;
122 | return Color(color).alpha(alpha).rgbString();
123 | }
124 | };
125 |
126 | // DEPRECATED
127 | window.randomScalingFactor = function() {
128 | return Math.round(Samples.utils.rand(-100, 100));
129 | };
130 |
131 | // INITIALIZATION
132 |
133 | Samples.utils.srand(Date.now());
134 |
135 | // Google Analytics
136 | /* eslint-disable */
137 | if (document.location.hostname.match(/^(www\.)?chartjs\.org$/)) {
138 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
139 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
140 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
141 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
142 | ga('create', 'UA-28909194-3', 'auto');
143 | ga('send', 'pageview');
144 | }
145 | /* eslint-enable */
146 |
147 | }(this));
--------------------------------------------------------------------------------
/src/public/static/moving-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dogegarden/dogetracker/81d9e1a193d6896e9c40c188b3753efeb06b7d25/src/public/static/moving-bg.png
--------------------------------------------------------------------------------
/src/routes/api.js:
--------------------------------------------------------------------------------
1 | const Router = require('../classes/Router');
2 | const axios = require('axios')
3 | const Stats = require('../util/stats');
4 |
5 | class API extends Router {
6 | constructor(client) {
7 | super(client, '/api');
8 | }
9 | createRoute() {
10 |
11 | this.router.get('/statistics', async (req, res) => {
12 | try {
13 | let statistics = await axios.get('https://api.dogegarden.net/v1/statistics?dogestats')
14 | let initRooms = await axios.get('https://api.dogegarden.net/v1/popularRooms?dogestats')
15 | let topRoom = initRooms.data.rooms[0]
16 |
17 | let newestRoom = initRooms.data.rooms.find(rooms => rooms.inserted_at == initRooms.data.rooms.map(it => it.inserted_at).sort()[initRooms.data.rooms.length - 1])
18 | let longestRoom = initRooms.data.rooms.find(rooms => rooms.inserted_at == initRooms.data.rooms.map(it => it.inserted_at).sort()[0])
19 | let serverTime = new Date().valueOf();
20 | let data = {
21 | totalRooms: statistics.data.totalRooms,
22 | totalOnline: statistics.data.totalOnline,
23 | totalScheduled: statistics.data.totalScheduledRooms,
24 | topRoom: {
25 | name: topRoom.name,
26 | description: topRoom.description,
27 | listeners: topRoom.numPeopleInside,
28 | id: topRoom.id,
29 | created_at: topRoom.inserted_at
30 | },
31 | newestRoom: {
32 | name: newestRoom.name,
33 | description: newestRoom.description,
34 | listeners: newestRoom.numPeopleInside,
35 | id: newestRoom.id,
36 | created_at: newestRoom.inserted_at
37 | },
38 | longestRoom: {
39 | name: longestRoom.name,
40 | description: longestRoom.description,
41 | listeners: longestRoom.numPeopleInside,
42 | id: longestRoom.id,
43 | created_at: longestRoom.inserted_at
44 | },
45 | totalBotsOnline: statistics.data.totalBotsOnline,
46 | totalBotsSendingTelemetry: statistics.data.totalBotsSendingTelemetry,
47 | totalRegistered: statistics.data.totalRegistered,
48 | activeInLastTwoDays: statistics.data.activeInLastTwoDays,
49 | id: statistics.data.pid,
50 | serverTime: serverTime
51 | }
52 | res.json(data);
53 | } catch (e) {
54 | console.log(e)
55 | }
56 | })
57 |
58 | this.router.get('/version', async (req, res) => {
59 | try {
60 | let packageConf = require('../../package.json')
61 | res.json(packageConf)
62 |
63 | } catch (e) {
64 | console.log(e)
65 | }
66 | })
67 |
68 | this.router.get('/bots', async (req, res) => {
69 | try {
70 | let bots = await axios.get('https://api.dogegarden.net/v1/bots?dogestats')
71 | res.json(bots.data);
72 | } catch (e) {
73 | console.log(e)
74 | }
75 | })
76 |
77 | this.router.get('/mysql', async (req, res) => {
78 | // console.log(req.query.time)
79 | let validQueries = ['24h', 'week', 'month', 'alltime', 'custom'];
80 | if (validQueries.indexOf(req.query.time) != -1) {
81 |
82 | try {
83 | let data = await Stats.getData(req.query.time);
84 | // console.log(data)
85 | res.json(data);
86 | } catch (e) {
87 | console.log(e);
88 | res.status(500).json({ error: e });
89 | }
90 | }
91 |
92 | })
93 |
94 | return this.router
95 | }
96 | }
97 |
98 | module.exports = API;
99 |
--------------------------------------------------------------------------------
/src/util/Logger.js:
--------------------------------------------------------------------------------
1 | const chalk = require('chalk');
2 | const dateFormat = require('dateformat');
3 | const util = require('util')
4 |
5 | class Logger {
6 | static get prefix() {
7 | return chalk.gray(dateFormat(Date.now(), 'ddd HH:MM:ss:l'))
8 | }
9 |
10 | static formatInput(args) {
11 | return args.map((arg) => arg instanceof Object ? util.inspect(arg) : arg)
12 | }
13 |
14 | static info(...args) {
15 | args = this.formatInput(args)
16 | console.log(this.prefix + ' ' + chalk.green('[INFO]') + ' ' + args.join(' '))
17 | }
18 |
19 | static error(...args) {
20 | args = this.formatInput(args)
21 | console.log(this.prefix + ' ' + chalk.red('>> [ERROR]') + ' ' + args.join(' '))
22 | }
23 |
24 | static route(...args) {
25 | args = this.formatInput(args)
26 | console.log(this.prefix + ' ' + chalk.blue('[ROUTE]') + ' ' + args.join(' '))
27 | }
28 |
29 | static mysql(...args) {
30 | args = this.formatInput(args)
31 | console.log(this.prefix + ' ' + chalk.cyan('[MYSQL]') + ' ' + args.join(' '))
32 | }
33 |
34 | static API(...args) {
35 | args = this.formatInput(args)
36 | console.log(this.prefix + ' ' + chalk.cyan('[API]') + ' ' + args.join(' '))
37 | }
38 |
39 | }
40 |
41 | module.exports = Logger;
--------------------------------------------------------------------------------
/src/util/connect.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const http = require('http');
4 |
5 | const defaultOptions = {
6 | // "port": ,
7 | "host": "http://localhost",
8 | "method": `GET`,
9 | "path": `/v1`,
10 | "headers": {
11 | "Content-Type": `application/json`,
12 | "Accept": `application/json`,
13 | }
14 | };
15 |
16 | function request ( options = {} ) {
17 | const promise = new Promise(( resolve, reject ) => {
18 | let {
19 | method = `GET`,
20 | path,
21 | body,
22 | } = options;
23 |
24 | if ( !path ) {
25 | throw new Error(`Missing path or index + action for DB connection`);
26 | }
27 |
28 | const reqOpts = {
29 | ...defaultOptions,
30 | method,
31 | path,
32 | };
33 |
34 | if ( isDev ) {
35 | // console.table(reqOpts);
36 | console.log(path);
37 | }
38 |
39 | const req = http.request(reqOpts, ( res ) => {
40 |
41 | const bufferArray = [];
42 |
43 | // res.setEncoding(`utf8`);
44 |
45 | res.on(`data`, ( chunk ) => {
46 | bufferArray[ bufferArray.length ] = chunk;
47 | });
48 |
49 | res.on(`end`, () => {
50 | const data = Buffer.concat(bufferArray);
51 | const obj = JSON.parse(data);
52 | resolve(obj);
53 | });
54 | });
55 |
56 | req.on('error', ( e ) => {
57 | reject(`Request to elasticsearch failed: ${e.message}`);
58 | });
59 |
60 | req.end(typeof body === `object` ? JSON.stringify(body) : body || undefined);
61 | });
62 |
63 | return promise //.catch(e => console.trace(e));
64 | }
--------------------------------------------------------------------------------
/src/util/cron.js:
--------------------------------------------------------------------------------
1 | const cron = require('node-cron');
2 | const axios = require('axios')
3 | const mysql = require('mysql');
4 | const Logger = require('../util/Logger')
5 |
6 | function setCron() {
7 | // Execute every 10 minutes
8 | cron.schedule('*/10 * * * *', saveMYSQL);
9 | }
10 |
11 | function autoRunMYSQL() {
12 | Logger.info(`Starting with autorun MYSQL`);
13 | saveMYSQL();
14 | }
15 |
16 | function saveMYSQL() {
17 | var con = mysql.createConnection({
18 | host: process.env.MYSQL_HOST,
19 | port: process.env.MYSQL_PORT,
20 | user: process.env.MYSQL_USER,
21 | password: process.env.MYSQL_PASS,
22 | database: process.env.MYSQL_DB
23 | });
24 |
25 | con.connect(async function (err) {
26 | if (err) throw err;
27 | Logger.mysql(`Connected`);
28 | let data;
29 | try {
30 | data = await axios.get('https://api.dogegarden.net/v1/popularRooms?mysql')
31 | data = data.data;
32 | // let sql = 'INSERT IGNORE INTO users (uuid, numFollowers, displayName) VALUES ';
33 | // for (j = 0; j < data.rooms.length; j++) {
34 | // for (i = 0; i < data.rooms[j].peoplePreviewList.length; i++) {
35 | // sql += `('${data.rooms[j].peoplePreviewList[i].id}', '${data.rooms[j].peoplePreviewList[i].numFollowers}', '${data.rooms[j].peoplePreviewList[i].displayName.replace(/"/g, '\\\"').replace(/'/g, '\\\'')}'), `;
36 | // }
37 | // }
38 | // sql = sql.slice(0, sql.length - 2);
39 |
40 | // con.query(sql, function (err, result) {
41 | // if (err) throw err;
42 | // console.log("users inserted");
43 | // });
44 | if (data.rooms === undefined || data.rooms.rooms) {
45 | Logger.mysql("Rooms undefined, check API endpoint");
46 | } else {
47 | sql = 'INSERT IGNORE INTO rooms (id, creatorId, roomDescription, insertedAt, roomName, numPeopleInside) VALUES ';
48 | for (i = 0; i < data.rooms.length; i++) {
49 | sql += `('${data.rooms[i].id}', '${data.rooms[i].creatorId}', '${data.rooms[i].description.replace(/"/g, '\\\"').replace(/'/g, '\\\'')}', '${data.rooms[i].inserted_at}', '${data.rooms[i].name.replace(/"/g, '\\\"').replace(/'/g, '\\\'')}', '${data.rooms[i].numPeopleInside}'), `;
50 | }
51 | sql = sql.slice(0, sql.length - 2);
52 |
53 | con.query(sql, function (err, result) {
54 | if (err) throw err;
55 | Logger.mysql("Rooms inserted");
56 | });
57 | }
58 |
59 | data = await axios.get('https://stats.dogegarden.net/api/statistics?mysql')
60 | data = data.data;
61 | if (data.totalRooms === undefined) {
62 | Logger.mysql("Total Rooms undefined, check API endpoint");
63 | } else {
64 | sql = `INSERT INTO stats (totalRooms, totalScheduledRooms, totalOnline, totalBotsOnline, totalBotsSendingTelemetry, totalRegistered, activeInLastTwoDays, topRoomID, newestRoomID, longestRoomID) VALUES (${data.totalRooms}, ${data.totalScheduled}, ${data.totalOnline}, ${data.totalBotsOnline}, ${data.totalBotsSendingTelemetry}, ${data.totalRegistered}, ${data.activeInLastTwoDays}, '${data.topRoom.id}', '${data.newestRoom.id}', '${data.longestRoom.id}')`;
65 | con.query(sql, function (err, result) {
66 | if (err) throw err;
67 | Logger.mysql("Stats inserted");
68 | });
69 | }
70 | } catch (e) {
71 | return console.error('Error in getting data from api', e)
72 | }
73 | });
74 | // Bots
75 | /*
76 | let uniqueBots = [];
77 |
78 | for (i=0;i}
19 | */
20 |
21 | function getData(timeOption) {
22 | let sql;
23 | // select * from table where table.id mod 5 = 0;
24 | if (timeOption == '24h') {
25 | // sql = 'SELECT totalRooms, totalOnline, statsTime FROM stats WHERE statsTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY) and id mod 2 = 0';
26 | // sql = 'SET @Hours = HOUR(NOW()); SET @StartTime = DATE_SUB(CURDATE(), INTERVAL 1 DAY); SELECT totalRooms, totalOnline, statsTime FROM stats WHERE statsTime > DATE_ADD(@startTime, INTERVAL @Hours HOUR) AND id mod 2 = 0;';
27 | sql = 'SELECT DATE_FORMAT(statsTime, "%Y-%m-%d-%H") as queryDay, avg(totalOnline) as ave, min(totalOnline) as min, max(totalOnline) as max, avg(totalRooms) as aveR, min(totalRooms) as minR, max(totalRooms) as maxR FROM stats WHERE statsTime > DATE_ADD(DATE_SUB(CURDATE(), INTERVAL 1 DAY), INTERVAL HOUR(NOW()) HOUR) GROUP BY DATE_FORMAT(statsTime, "%Y-%m-%d-%H"); ';
28 | } else if (timeOption == 'week') {
29 | sql = 'SELECT DATE_FORMAT(statsTime, "%Y-%m-%d") as queryDay, avg(totalOnline) as ave, min(totalOnline) as min, max(totalOnline) as max, avg(totalRooms) as aveR, min(totalRooms) as minR, max(totalRooms) as maxR FROM stats WHERE statsTime > DATE_ADD(DATE_SUB(CURDATE(), INTERVAL 7 DAY), INTERVAL HOUR(NOW()) HOUR) GROUP BY DATE_FORMAT(statsTime, "%Y-%m-%d"); ';
30 | } else if (timeOption == 'month') {
31 | sql = 'SELECT DATE_FORMAT(statsTime, "%Y-%m-%d") as queryDay, avg(totalOnline) as ave, min(totalOnline) as min, max(totalOnline) as max, avg(totalRooms) as aveR, min(totalRooms) as minR, max(totalRooms) as maxR FROM stats WHERE statsTime > DATE_ADD(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), INTERVAL HOUR(NOW()) HOUR) GROUP BY DATE_FORMAT(statsTime, "%Y-%m-%d"); ';
32 | } else if (timeOption == 'alltime') {
33 | // All time
34 | let daySplit = Math.ceil((Date.now()-new Date('4 Apr 2021').valueOf())/86400000/30); // /30 for number of days for modulus
35 | // let days = Math.ceil((Date.now() - startTime)/1000/60/60/24)
36 | // sql = `SELECT totalRooms, totalOnline, statsTime FROM stats WHERE id mod ${days * 2} = 0`;
37 |
38 | // New version, for every 30 days increase modulus by one so, <30, 1/month, 2/month, 3/month etc. >= 30 && < 60, 2/month, 4/month, etc. 3/m, 6/m .. 4/m, 8/m
39 | sql = 'SELECT DATE_FORMAT(statsTime, "%Y-%m-%d") as queryDay, avg(totalOnline) as ave, min(totalOnline) as min, max(totalOnline) as max, avg(totalRooms) as aveR, min(totalRooms) as minR, max(totalRooms) as maxR FROM stats GROUP BY CONCAT(DATE_FORMAT(statsTime, "%Y-%m-"), (DATE_FORMAT(statsTime, "%d") - DATE_FORMAT(statsTime, "%d") mod '+daySplit+')) ORDER BY queryDay ASC; ';
40 | // Todo: decide between days, weeks, months, even years
41 | } else {
42 | sql = 'SELECT * FROM stats; ';
43 | }
44 |
45 | // const result = query(sql);
46 | // console.log("Ran Get Data | " + timeOption);
47 | // console.log("Ran Get Data RESULT: ", result);
48 | return query(sql);
49 | }
50 |
51 | module.exports = {
52 | getData,
53 | };
54 |
--------------------------------------------------------------------------------
/src/views/Components/Footer.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | v<%= version %>
4 |
5 |
--------------------------------------------------------------------------------
/src/views/Components/Head.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
34 |
35 |
40 |
45 |
46 |
47 |
51 |
55 |
56 | DogeTracker | <%- title %>
57 |
58 |
--------------------------------------------------------------------------------
/src/views/Components/Navbar.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
30 |
31 |
32 |
40 |
--------------------------------------------------------------------------------
/src/views/Components/Noscript.ejs:
--------------------------------------------------------------------------------
1 |
2 |
14 | <%- include('./Navbar') %>
15 |
16 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/views/bots.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%- include('./Components/Head', { title: 'Bots' }) %>
5 | <%- include('./Components/Navbar') %>
6 |
7 |
8 |
9 |
10 |
11 |
DogeTracker
12 |
13 |
View, manage and explore statistics and historical data from dogehouse.tv
14 |
15 |
16 |
17 |
18 |
36 |
37 |
38 |
39 |
40 |
41 |
44 |
total served users
45 |
46 |
47 |
50 |
bots sending telemetry
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Trending Bot
61 |
62 |
63 |
64 |
65 |
66 | serving
67 | users . Gathering Time...
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | Newest Room
103 |
104 |
105 |
106 |
107 |
108 | containing
109 | users . Gathering Time...
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | Longest Active Room
121 |
122 |
123 |
124 |
125 |
126 | containing
127 | users . Gathering Time...
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | <%- include('./Components/Footer') %>
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/src/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%- include('./Components/Head', { title: 'Home' }) %>
5 | <%- include('./Components/Navbar') %>
6 | <%- include('./Components/Noscript') %>
7 |
8 |
9 |
10 |
11 |
12 |
DogeTracker
13 |
14 |
View, manage and explore statistics and historical data from dogehouse.tv Made possible by dogehouse-api
17 |
18 |
19 |
20 |
21 |
22 | ×
23 |
24 |
API Offline...
25 |
We have detected that the API is currently offline. While you can still view old and historical data, no new data will be available while this issue lasts.
26 |
See more at our Status Page .
28 |
29 |
30 |
31 |
32 |
33 | ×
34 |
35 |
New Version Available
36 |
We have detected that you may be using an older version of the website.
37 |
To load the newer version click here .
39 |
40 |
41 |
42 |
43 |
44 |
46 |
47 |
50 |
public rooms
51 |
52 |
53 |
56 |
online across site
57 |
58 |
59 |
62 |
scheduled rooms
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | Trending
74 |
75 |
76 |
77 |
79 |
80 |
81 | containing
82 | users . Gathering
85 | Time...
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
219 |
220 |
221 |
222 |
224 |
225 |
226 | Newest Room
228 |
229 |
230 |
231 |
233 |
234 |
235 | containing
236 | users . Gathering Time...
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
249 |
250 |
251 | Longest Active Room
253 |
254 |
255 |
256 |
258 |
259 |
260 | containing
261 | users . Gathering Time...
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 | Online Bot List
326 |
327 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
345 |
346 |
347 |
Supported by your name... .
348 |
Thank you for supporting us on patreon , . Get your name here by selecting a tier on our patreon.
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 | <%- include('./Components/Footer') %>
361 |
362 |
363 |
364 |
365 |
--------------------------------------------------------------------------------
/src/views/rooms.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%- include('./Components/Head', { title: 'Rooms'}) %> <%-
4 | include('./Components/Navbar') %> <%- include('./Components/Noscript') %>
5 |
6 |
7 |
11 |
12 |
13 |
DogeTracker
14 |
15 |
16 | View, manage and explore statistics and historical data from
17 | dogehouse.tv
18 |
19 |
20 |
21 |
22 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/src/views/scheduled.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%- include('./Components/Head', { title: 'Scheduled'}) %> <%-
4 | include('./Components/Navbar') %>
5 |
6 |
7 |
11 |
12 |
13 |
DogeTracker
14 |
15 |
16 | View, manage and explore statistics and historical data from
17 | dogehouse.tv
18 |
19 |
20 |
21 |
22 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------