diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d52e27f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +# credits: +# - CI with Jest: https://joelhooks.com/jest-and-github-actions +name: CI +on: pull_request +jobs: + test: + name: Test + # setup global configurations + strategy: + matrix: + node-version: [12.x] + platform: [ubuntu-latest, windows-latest, macos-latest] + # Build OS + runs-on: ${{ matrix.platform }} + # CI + steps: + - uses: actions/checkout@v2 + # select nodejs v12 + - name: Test using Node.js v${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + # install packages + - name: Install Packages + run: npm i + # create a config file + # - name: Create Config File + # uses: "DamianReeves/write-file-action@v1.0" + # with: + # path: ./config.json + # contents: '{"BITLY_TOKEN": "${{ secrets.BITLY_TOKEN }}","TWITCH_TOKEN": "${{ secrets.TWITCH_TOKEN }}","DISCORD_TOKEN": "${{ secrets.DISCORD_TOKEN }}"}' + # write-mode: overwrite + # # run tests + # - name: Run tests + # run: npm run test:ci + # # conditions with test fails or succeeds + # - name: Tests ✅ + # shell: bash + # if: ${{ success() }} + # run: | + # curl \ + # -X POST \ + # -H 'Accept: application/vnd.github.v3+json' \ + # -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \ + # -d '{ + # "context": "tests", + # "state": "success", + # "description": "Tests passed", + # "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + # }' \ + # https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} + # - name: Tests 🚨 + # if: ${{ failure() }} + # shell: bash + # run: | + # curl \ + # -X POST \ + # -H 'Accept: application/vnd.github.v3+json' \ + # -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \ + # -d '{ + # "context": "tests", + # "state": "failure", + # "description": "Tests failed", + # "target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + # }' \ + # https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} + # Code Coverage + # - name: Code Coverage + # uses: codecov/codecov-action@v1 + # with: + # token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos + # remove the config file + # - name: Remove Config File + # uses: JesseTG/rm@v1.0.2 + # with: + # path: "config.json" diff --git a/package.json b/package.json index a9c9f5c..46c7718 100644 --- a/package.json +++ b/package.json @@ -7,12 +7,12 @@ "test": "tests" }, "scripts": { - "dev": "prettier --write app/* --tab-width 4 && nodemon app/index", + "dev": "prettier --write src/* --tab-width 4", "start": "node server", - "dep": "pm2 start app/index", + "dep": "pm2 start src/index", "test": "jest --watch", "test:ci": "jest --ci --reporters='default' --reporters='./github-actions-reporter' --coverage && codecov", - "stop": "pm2 stop app/index.js" + "stop": "pm2 stop src/index.js" }, "repository": { "type": "git", diff --git a/src/ai/BlendGAN/README.md b/src/ai/BlendGAN/README.md index 30eb83a..5cda867 100644 --- a/src/ai/BlendGAN/README.md +++ b/src/ai/BlendGAN/README.md @@ -6,10 +6,9 @@ Y-tech, Kuaishou Technology +### [Project page](https://onion-liu.github.io/BlendGAN) | [Paper](https://arxiv.org/abs/2110.11728) -### [Project page](https://onion-liu.github.io/BlendGAN) | [Paper](https://arxiv.org/abs/2110.11728) - -Abstract: *Generative Adversarial Networks (GANs) have made a dramatic leap in high-fidelity image synthesis and stylized face generation. Recently, a layer-swapping mechanism has been developed to improve the stylization performance. However, this method is incapable of fitting arbitrary styles in a single model and requires hundreds of style-consistent training images for each style. To address the above issues, we propose BlendGAN for arbitrary stylized face generation by leveraging a flexible blending strategy and a generic artistic dataset. Specifically, we first train a self-supervised style encoder on the generic artistic dataset to extract the representations of arbitrary styles. In addition, a weighted blending module (WBM) is proposed to blend face and style representations implicitly and control the arbitrary stylization effect. By doing so, BlendGAN can gracefully fit arbitrary styles in a unified model while avoiding case-by-case preparation of style-consistent training images. To this end, we also present a novel large-scale artistic face dataset AAHQ. Extensive experiments demonstrate that BlendGAN outperforms state-of-the-art methods in terms of visual quality and style diversity for both latent-guided and reference-guided stylized face synthesis.* +Abstract: _Generative Adversarial Networks (GANs) have made a dramatic leap in high-fidelity image synthesis and stylized face generation. Recently, a layer-swapping mechanism has been developed to improve the stylization performance. However, this method is incapable of fitting arbitrary styles in a single model and requires hundreds of style-consistent training images for each style. To address the above issues, we propose BlendGAN for arbitrary stylized face generation by leveraging a flexible blending strategy and a generic artistic dataset. Specifically, we first train a self-supervised style encoder on the generic artistic dataset to extract the representations of arbitrary styles. In addition, a weighted blending module (WBM) is proposed to blend face and style representations implicitly and control the arbitrary stylization effect. By doing so, BlendGAN can gracefully fit arbitrary styles in a unified model while avoiding case-by-case preparation of style-consistent training images. To this end, we also present a novel large-scale artistic face dataset AAHQ. Extensive experiments demonstrate that BlendGAN outperforms state-of-the-art methods in terms of visual quality and style diversity for both latent-guided and reference-guided stylized face synthesis._ ### Updates @@ -19,7 +18,7 @@ Abstract: *Generative Adversarial Networks (GANs) have made a dramatic leap in h :heavy_check_mark: (2021-11-19) a web demo is integrated to [Huggingface Spaces](https://huggingface.co/spaces) with [Gradio](https://github.com/gradio-app/gradio). See demo: [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/akhaliq/BlendGAN) -:heavy_check_mark: (2021-11-19) Inference code and pretrained models have been released! +:heavy_check_mark: (2021-11-19) Inference code and pretrained models have been released! ![000041](https://user-images.githubusercontent.com/6346064/142623312-3e6f09aa-ce88-465c-b956-a8b4db95b4da.gif) ![000021](https://user-images.githubusercontent.com/6346064/142621044-086cde48-8604-467b-8c43-8768b6670ec2.gif) @@ -28,25 +27,25 @@ Abstract: *Generative Adversarial Networks (GANs) have made a dramatic leap in h You can download the following pretrained models to ./pretrained_models: -| Model | Discription | -| ---- | ---- | -| [blendgan](https://drive.google.com/file/d/1eF04jKMLAb9DvzI72m8Akn5ykWf3EafE/view?usp=sharing) | BlendGAN model (together with style_encoder) | -| [psp_encoder](https://drive.google.com/file/d/14nevG94hNkkwaoK5eJLF1iv78cv5O8fN/view?usp=sharing) | PSP Encoder model | -| [style_encoder](https://drive.google.com/file/d/1EaM0ZYsAMdPkbRz0smLNIlJ1rxVAhbEz/view?usp=sharing) | Individual Style Encoder model (optional) | +| Model | Discription | +| --------------------------------------------------------------------------------------------------- | -------------------------------------------- | +| [blendgan](https://drive.google.com/file/d/1eF04jKMLAb9DvzI72m8Akn5ykWf3EafE/view?usp=sharing) | BlendGAN model (together with style_encoder) | +| [psp_encoder](https://drive.google.com/file/d/14nevG94hNkkwaoK5eJLF1iv78cv5O8fN/view?usp=sharing) | PSP Encoder model | +| [style_encoder](https://drive.google.com/file/d/1EaM0ZYsAMdPkbRz0smLNIlJ1rxVAhbEz/view?usp=sharing) | Individual Style Encoder model (optional) | ## Inference -*Note: If you dislike the deformation in the generated images, `add_weight_index=7` may be a better choice.* +_Note: If you dislike the deformation in the generated images, `add_weight_index=7` may be a better choice._ ### 1. Generate image pairs with random face codes -- for latent-guided generation, run: +- for latent-guided generation, run: ```bash python generate_image_pairs.py --size 1024 --pics N_PICS --ckpt ./pretrained_models/blendgan.pt --outdir results/generated_pairs/latent_guided/ ``` -- for reference-guided generation, run: +- for reference-guided generation, run: ```bash python generate_image_pairs.py --size 1024 --pics N_PICS --ckpt ./pretrained_models/blendgan.pt --style_img ./test_imgs/style_imgs/100036.png --outdir results/generated_pairs/reference_guided/ @@ -73,7 +72,9 @@ jupyter notebook --notebook-dir=./ ![demo](./index_files/demo.jpg) ## Bibtex + If you use this code for your research, please cite our paper: + ``` @inproceedings{liu2021blendgan, title = {BlendGAN: Implicitly GAN Blending for Arbitrary Stylized Face Generation}, @@ -84,30 +85,29 @@ If you use this code for your research, please cite our paper: ``` ## Credits + **StyleGAN2 model and implementation:** https://github.com/rosinality/stylegan2-pytorch Copyright (c) 2019 Kim Seonghyeon -License (MIT) https://github.com/rosinality/stylegan2-pytorch/blob/master/LICENSE +License (MIT) https://github.com/rosinality/stylegan2-pytorch/blob/master/LICENSE **IR-SE50 model and implementations:** https://github.com/TreB1eN/InsightFace_Pytorch Copyright (c) 2018 TreB1eN License (MIT) https://github.com/TreB1eN/InsightFace_Pytorch/blob/master/LICENSE -**pSp model and implementation:** +**pSp model and implementation:** https://github.com/eladrich/pixel2style2pixel Copyright (c) 2020 Elad Richardson, Yuval Alaluf License (MIT) https://github.com/eladrich/pixel2style2pixel/blob/master/LICENSE **Please Note**: -- The CUDA files under the [StyleGAN2 ops directory](./op) are made available under the [Nvidia Source Code License-NC](https://nvlabs.github.io/stylegan2/license.html) -- The face images under the [test_imgs](./test_imgs/face_imgs) directory are selected from the [FFHQ](https://github.com/NVlabs/ffhq-dataset) dataset, which is made available under [Creative Commons BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) license by NVIDIA Corporation. -- The artistic images under the [test_imgs](./test_imgs/style_imgs) directory are collected from [Artstation](https://www.artstation.com), and the copyright remains with the original owners. - +- The CUDA files under the [StyleGAN2 ops directory](./op) are made available under the [Nvidia Source Code License-NC](https://nvlabs.github.io/stylegan2/license.html) +- The face images under the [test_imgs](./test_imgs/face_imgs) directory are selected from the [FFHQ](https://github.com/NVlabs/ffhq-dataset) dataset, which is made available under [Creative Commons BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) license by NVIDIA Corporation. +- The artistic images under the [test_imgs](./test_imgs/style_imgs) directory are collected from [Artstation](https://www.artstation.com), and the copyright remains with the original owners. ## Acknowledgements We sincerely thank all the reviewers for their comments. We also thank Zhenyu Guo for help in preparing the comparison to StarGANv2. This code borrows heavily from the pytorch re-implementation of StyleGAN2 by [rosinality](https://github.com/rosinality/stylegan2-pytorch). - diff --git a/src/ai/BlendGAN/ffhq_dataset/README.md b/src/ai/BlendGAN/ffhq_dataset/README.md index 6927c63..bcf7c1b 100644 --- a/src/ai/BlendGAN/ffhq_dataset/README.md +++ b/src/ai/BlendGAN/ffhq_dataset/README.md @@ -1,4 +1,4 @@ -Download *shape_predictor_68_face_landmarks.dat* here +Download _shape_predictor_68_face_landmarks.dat_ here ```bash wget http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 diff --git a/src/api/index.js b/src/api/index.js index a203948..7219152 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,6 +1,6 @@ -const express = require('express'), - mongoose = require('mongoose'), - ejs = require('ejs'); +const express = require("express"), + mongoose = require("mongoose"), + ejs = require("ejs"); const router = require("./routes/index"), streamer_router = require("./routes/streamer"), @@ -8,35 +8,36 @@ const router = require("./routes/index"), validate_router = require("./routes/validate"), ai_router = require("./routes/ai"); -require('dotenv').config({path:'../../.env'}); +require("dotenv").config({ path: "../../.env" }); const app = express(), port = process.env.PORT || 4000; -app.use(express.json()) -app.use(express.static(__dirname + '/public')); //Serves resources from public folder -app.set('view engine', 'ejs'); +app.use(express.json()); +app.use(express.static(__dirname + "/public")); //Serves resources from public folder +app.set("view engine", "ejs"); -main().catch(err => console.log(err)); +main().catch((err) => console.log(err)); async function main() { - await mongoose.connect(process.env.DATABASE_URL, { - useNewUrlParser: true, - useUnifiedTopology: true, - tlsCAFile: "../../ca-certificate.crt" - }).catch(err => { - console.error(err.stack) - process.exit(1) - }); + await mongoose + .connect(process.env.DATABASE_URL, { + useNewUrlParser: true, + useUnifiedTopology: true, + tlsCAFile: "../../ca-certificate.crt", + }) + .catch((err) => { + console.error(err.stack); + process.exit(1); + }); } -app.use('/streamer', streamer_router) -app.use('/rank', rank_router) -app.use('/validate', validate_router) -app.use('/ai', ai_router) -app.use('/', router) - +app.use("/streamer", streamer_router); +app.use("/rank", rank_router); +app.use("/validate", validate_router); +app.use("/ai", ai_router); +app.use("/", router); app.listen(port, () => { - console.log(`localhost:${port}`) -}); \ No newline at end of file + console.log(`localhost:${port}`); +}); diff --git a/src/api/models/Rank.js b/src/api/models/Rank.js index ac30c0a..b6e68ba 100644 --- a/src/api/models/Rank.js +++ b/src/api/models/Rank.js @@ -1,10 +1,10 @@ -const mongoose = require('mongoose'); +const mongoose = require("mongoose"); const rankSchema = new mongoose.Schema({ name: { type: String, required: true }, roleId: { type: String, required: true }, channelId: { type: String, required: true }, - threshold: { type: Number, required: true } + threshold: { type: Number, required: true }, }); // streamerSchema.methods.stream = function stream() { @@ -14,6 +14,6 @@ const rankSchema = new mongoose.Schema({ // console.log(greeting); // }; -module.exports = { - Rank: mongoose.model('Ranks', rankSchema) -} +module.exports = { + Rank: mongoose.model("Ranks", rankSchema), +}; diff --git a/src/api/models/Streamer.js b/src/api/models/Streamer.js index 43f09bc..eebc873 100644 --- a/src/api/models/Streamer.js +++ b/src/api/models/Streamer.js @@ -1,48 +1,48 @@ -const mongoose = require('mongoose'); +const mongoose = require("mongoose"); -const twitchInfo = { - twitchName: { type: String, required: true, }, +const twitchInfo = { + twitchName: { type: String, required: true }, twitchId: { type: String, required: true }, profileImg: { type: String, default: "" }, -} +}; -const twitchStats = { +const twitchStats = { streams: { type: Number, default: 0 }, hosts: { type: Number, default: 0 }, raids: { type: Number, default: 0 }, chats: { type: Number, default: 0 }, views: { type: Number, required: false }, - follows: { type: Number, required: false } -} + follows: { type: Number, required: false }, +}; -const rankInfo = { +const rankInfo = { rankName: { type: String, default: "" }, points: { type: Number, default: 0 }, activityPoints: { type: Number, default: 0 }, - streamsBeforeDemotion: { type: Number, default: 0 } -} + streamsBeforeDemotion: { type: Number, default: 0 }, +}; -const invitationInfo = { +const invitationInfo = { invitedUsers: { type: [Number], - default: [] + default: [], }, invites: { type: Number, - default: 0 + default: 0, }, eventIds: { type: [String], - default: [] - } -} + default: [], + }, +}; const streamerSchema = new mongoose.Schema({ discordId: { type: String, required: true }, ...twitchInfo, ...twitchStats, ...rankInfo, - ...invitationInfo + ...invitationInfo, }); // streamerSchema.methods.stream = function stream() { @@ -52,6 +52,6 @@ const streamerSchema = new mongoose.Schema({ // console.log(greeting); // }; -module.exports = { - Streamer: mongoose.model('Streamers', streamerSchema) -} +module.exports = { + Streamer: mongoose.model("Streamers", streamerSchema), +}; diff --git a/src/api/routes/ai.js b/src/api/routes/ai.js index 72e7dc3..b6bb990 100644 --- a/src/api/routes/ai.js +++ b/src/api/routes/ai.js @@ -1,109 +1,93 @@ -const express = require('express'), +const express = require("express"), router = express.Router(); -const python_path = '/home/alotaima/miniconda3/bin/python' +const python_path = "/home/alotaima/miniconda3/bin/python"; function getProgramPath(project) { - const rootFolder = __dirname.split('/'); + const rootFolder = __dirname.split("/"); rootFolder.splice(-2, 2, `ai/${project}/main.py`); - return rootFolder.join('/'); + return rootFolder.join("/"); } function launchProcess(res, args) { const spawn = require("child_process").spawn, process = spawn(python_path, args); - let output = ''; + let output = ""; - process.stdout.on('data', function(data) { + process.stdout.on("data", function (data) { output += data.toString(); - }) + }); - process.stderr.on('data', (data) => { + process.stderr.on("data", (data) => { console.error(`stderr: ${data}`); }); - process.on('close', (code) => { + process.on("close", (code) => { console.log(output); res.send(JSON.stringify(output)); }); } -router.post('/style_transfer', (req, res) => { +router.post("/style_transfer", (req, res) => { const args = [ - getProgramPath('BlendGAN'), + getProgramPath("BlendGAN"), `--url`, `${req.body.url}`, `--style_selected`, `${req.body.style_selected}`, `--filename`, `${req.body.filename}`, - ] + ]; - launchProcess(res, args) + launchProcess(res, args); }); -router.post('/arcane', (req, res) => { +router.post("/arcane", (req, res) => { const args = [ - getProgramPath('ArcaneGAN'), + getProgramPath("ArcaneGAN"), `--url`, `${req.body.url}`, `--filename`, `${req.body.filename}`, - ] + ]; - launchProcess(res, args) + launchProcess(res, args); }); -router.post('/chat', (req, res) => { - const args = [ - getProgramPath('chat'), - `--message`, - `${req.body.message}`, - ] +router.post("/chat", (req, res) => { + const args = [getProgramPath("chat"), `--message`, `${req.body.message}`]; - launchProcess(res, args) + launchProcess(res, args); }); -router.post('/wolf', (req, res) => { - const args = [ - getProgramPath('wolf'), - `--message`, - `${req.body.message}`, - ] +router.post("/wolf", (req, res) => { + const args = [getProgramPath("wolf"), `--message`, `${req.body.message}`]; - launchProcess(res, args) + launchProcess(res, args); }); -router.post('/ar', (req, res) => { - const args = [ - getProgramPath('ar'), - `--message`, - `${req.body.message}`, - ] +router.post("/ar", (req, res) => { + const args = [getProgramPath("ar"), `--message`, `${req.body.message}`]; - launchProcess(res, args) + launchProcess(res, args); }); -router.post('/en', (req, res) => { - const args = [ - getProgramPath('en'), - `--message`, - `${req.body.message}`, - ] +router.post("/en", (req, res) => { + const args = [getProgramPath("en"), `--message`, `${req.body.message}`]; - launchProcess(res, args) + launchProcess(res, args); }); -router.post('/anime', (req, res) => { +router.post("/anime", (req, res) => { const args = [ - getProgramPath('anime'), + getProgramPath("anime"), `--url`, `${req.body.url}`, `--filename`, `${req.body.filename}`, - ] + ]; - launchProcess(res, args) + launchProcess(res, args); }); module.exports = router; diff --git a/src/api/routes/index.js b/src/api/routes/index.js index 66f8725..0f1ea2a 100644 --- a/src/api/routes/index.js +++ b/src/api/routes/index.js @@ -1,15 +1,15 @@ -const express = require('express'), +const express = require("express"), router = express.Router(); -router.get('/style', (req, res) => { - res.render('style'); -}) -router.post('/', (req, res) => { - console.log(JSON.stringify('Hello World!')); - res.send(JSON.stringify('Hello World!')); -}) -router.get('/', (req, res) => { - res.send(JSON.stringify('Hello World!')); -}) +router.get("/style", (req, res) => { + res.render("style"); +}); +router.post("/", (req, res) => { + console.log(JSON.stringify("Hello World!")); + res.send(JSON.stringify("Hello World!")); +}); +router.get("/", (req, res) => { + res.send(JSON.stringify("Hello World!")); +}); module.exports = router; diff --git a/src/api/routes/rank.js b/src/api/routes/rank.js index 5381c9c..2a7289f 100644 --- a/src/api/routes/rank.js +++ b/src/api/routes/rank.js @@ -1,45 +1,45 @@ -const express = require('express'), +const express = require("express"), router = express.Router(); const { Rank } = require("../models/Rank"); -router.post('/', async (req, res) => { +router.post("/", async (req, res) => { const rank = new Rank({ text: req.body.text }); await rank.save(); // rank.stream() - - res.send({rank}) -}) -router.put('/:rankId', async (req, res) => { + res.send({ rank }); +}); + +router.put("/:rankId", async (req, res) => { const rankId = req.params.rankId; const text = req.body.text; - + const rank = await Rank.findByIdAndUpdate({ _id: rankId }, { text }); - res.json({rank}) -}) + res.json({ rank }); +}); -router.delete('/:rankId', async (req, res) => { +router.delete("/:rankId", async (req, res) => { const rankId = req.params.rankId; - + const rank = await Rank.deleteOne({ _id: rankId }); - res.json({rank}) -}) + res.json({ rank }); +}); -router.get('/:rankId', async (req, res) => { +router.get("/:rankId", async (req, res) => { const rankId = req.params.rankId; const rank = await Rank.find({ _id: rankId }); - res.json({ rank }) -}) + res.json({ rank }); +}); -router.get('/', async (req, res) => { +router.get("/", async (req, res) => { const ranks = await Rank.find(); - res.json({ ranks }) -}) + res.json({ ranks }); +}); module.exports = router; diff --git a/src/api/routes/streamer.js b/src/api/routes/streamer.js index fc6e32a..947268f 100644 --- a/src/api/routes/streamer.js +++ b/src/api/routes/streamer.js @@ -1,42 +1,45 @@ -const express = require('express'), +const express = require("express"), router = express.Router(); const { Streamer } = require("../models/Streamer"); -router.post('/', async (req, res) => { +router.post("/", async (req, res) => { const streamer = new Streamer({ text: req.body.text }); await streamer.save(); - streamer.stream() - - res.send({streamer}) -}) + streamer.stream(); -router.put('/:streamerId', async (req, res) => { + res.send({ streamer }); +}); + +router.put("/:streamerId", async (req, res) => { const streamerId = req.params.streamerId; const text = req.body.text; - - const streamer = await Streamer.findByIdAndUpdate({ _id: streamerId },{ text }); - res.json({streamer}) -}) + const streamer = await Streamer.findByIdAndUpdate( + { _id: streamerId }, + { text } + ); + + res.json({ streamer }); +}); -router.delete('/:streamerId', async (req, res) => { +router.delete("/:streamerId", async (req, res) => { const streamerId = req.params.streamerId; - + const streamer = await Streamer.deleteOne({ _id: streamerId }); - res.json({streamer}) -}) + res.json({ streamer }); +}); -router.get('/:streamerId', async (req, res) => { +router.get("/:streamerId", async (req, res) => { const streamerId = req.params.streamerId; const streamer = await Streamer.find({ _id: streamerId }); - res.json({ streamer }) -}) + res.json({ streamer }); +}); -router.get('/', async (req, res) => { +router.get("/", async (req, res) => { const streamers = await Streamer.find(); - res.json({ streamers }) -}) + res.json({ streamers }); +}); module.exports = router; diff --git a/src/api/routes/validate.js b/src/api/routes/validate.js index 033f4c2..be095dd 100644 --- a/src/api/routes/validate.js +++ b/src/api/routes/validate.js @@ -1,83 +1,95 @@ -const express = require('express'), +const express = require("express"), router = express.Router(); -require('dotenv').config({path:'../../../.env'}); +require("dotenv").config({ path: "../../../.env" }); -router.get('/discord', async ({ query }, response) => { - const { code } = query; +router.get("/discord", async ({ query }, response) => { + const { code } = query; - if (code) { - try { - const oauthResult = await fetch('https://discord.com/api/oauth2/token', { - method: 'POST', - body: new URLSearchParams({ - client_id: process.env.DISCORD_CLIENT_ID, - client_secret: process.env.DISCORD_CLIENT_SECRET, - code, - grant_type: 'authorization_code', - redirect_uri: `http://localhost:${port}/validate/discord`, - scope: 'identify', - }), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }); + if (code) { + try { + const oauthResult = await fetch( + "https://discord.com/api/oauth2/token", + { + method: "POST", + body: new URLSearchParams({ + client_id: process.env.DISCORD_CLIENT_ID, + client_secret: process.env.DISCORD_CLIENT_SECRET, + code, + grant_type: "authorization_code", + redirect_uri: `http://localhost:${port}/validate/discord`, + scope: "identify", + }), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + } + ); - const oauthData = await oauthResult.json(); + const oauthData = await oauthResult.json(); console.log(oauthData); - const userResult = await fetch('https://discord.com/api/users/@me', { - headers: { - authorization: `${oauthData.token_type} ${oauthData.access_token}`, - }, - }); + const userResult = await fetch( + "https://discord.com/api/users/@me", + { + headers: { + authorization: `${oauthData.token_type} ${oauthData.access_token}`, + }, + } + ); - console.log(await userResult.json()); - } catch (error) { - // NOTE: An unauthorized token will not throw an error; - // it will return a 401 Unauthorized response in the try block above - console.error(error); - } - } + console.log(await userResult.json()); + } catch (error) { + // NOTE: An unauthorized token will not throw an error; + // it will return a 401 Unauthorized response in the try block above + console.error(error); + } + } - return response.sendFile('index.html', { root: './views' }); + return response.sendFile("index.html", { root: "./views" }); }); -router.get('/twitch', async ({ query }, response) => { - const { code } = query; - - if (code) { - try { - const oauthResult = await fetch('https://discord.com/api/oauth2/token', { - method: 'POST', - body: new URLSearchParams({ - client_id: process.env.DISCORD_CLIENT_ID, - client_secret: process.env.DISCORD_CLIENT_SECRET, - code, - grant_type: 'authorization_code', - redirect_uri: `http://localhost:${port}`, - scope: 'identify', - }), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }); +router.get("/twitch", async ({ query }, response) => { + const { code } = query; - const oauthData = await oauthResult.json(); + if (code) { + try { + const oauthResult = await fetch( + "https://discord.com/api/oauth2/token", + { + method: "POST", + body: new URLSearchParams({ + client_id: process.env.DISCORD_CLIENT_ID, + client_secret: process.env.DISCORD_CLIENT_SECRET, + code, + grant_type: "authorization_code", + redirect_uri: `http://localhost:${port}`, + scope: "identify", + }), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + } + ); - const userResult = await fetch('https://discord.com/api/users/@me', { - headers: { - authorization: `${oauthData.token_type} ${oauthData.access_token}`, - }, - }); + const oauthData = await oauthResult.json(); - console.log(await userResult.json()); - } catch (error) { - // NOTE: An unauthorized token will not throw an error; - // it will return a 401 Unauthorized response in the try block above - console.error(error); - } - } + const userResult = await fetch( + "https://discord.com/api/users/@me", + { + headers: { + authorization: `${oauthData.token_type} ${oauthData.access_token}`, + }, + } + ); - return response.sendFile('index.html', { root: './views' }); + console.log(await userResult.json()); + } catch (error) { + // NOTE: An unauthorized token will not throw an error; + // it will return a 401 Unauthorized response in the try block above + console.error(error); + } + } + + return response.sendFile("index.html", { root: "./views" }); }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/src/api/views/index.html b/src/api/views/index.html index 4746b6b..66d2fda 100644 --- a/src/api/views/index.html +++ b/src/api/views/index.html @@ -1,53 +1,71 @@ - - My Discord OAuth2 App - - -
- Hoi! -
- - - + fetch("https://discord.com/api/users/@me", { + headers: { + authorization: `${tokenType} ${accessToken}`, + }, + }) + .then((result) => result.json()) + .then((response) => { + const { username, discriminator } = response; + document.getElementById( + "info" + ).innerText += ` ${username}#${discriminator}`; + }) + .catch(console.error); + }; + + diff --git a/src/discord_bot/commands/ar.js b/src/discord_bot/commands/ar.js index d699eda..c4d5d1a 100644 --- a/src/discord_bot/commands/ar.js +++ b/src/discord_bot/commands/ar.js @@ -1,27 +1,35 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const { SlashCommandBuilder } = require("@discordjs/builders"); -const { postRequest } = require('../utils/request'); +const { postRequest } = require("../utils/request"); module.exports = { - data: new SlashCommandBuilder() - .setName('ar') - .setDescription('Repliddd!') - .addStringOption(option => { + data: new SlashCommandBuilder() + .setName("ar") + .setDescription("Repliddd!") + .addStringOption((option) => { return option - .setName('message') - .setDescription('The user\'s avatar to show') - .setRequired(true) + .setName("message") + .setDescription("The user's avatar to show") + .setRequired(true); }), - async execute(client, interaction) { - const message = interaction.options.getString('message'); - const target_url = '/ai/ar', + async execute(client, interaction) { + const message = interaction.options.getString("message"); + const target_url = "/ai/ar", target_args = { - message + message, + }; + await interaction.deferReply(); + postRequest( + client, + interaction, + target_url, + target_args, + function (data) { + return interaction.editReply({ + content: data, + ephemeral: true, + }); } - await interaction.deferReply(); - postRequest(client, interaction, target_url, target_args, function (data) { - return interaction.editReply( { content: data, ephemeral: true } ); - - }); - }, -}; \ No newline at end of file + ); + }, +}; diff --git a/src/discord_bot/commands/arcane.js b/src/discord_bot/commands/arcane.js index eb58883..3bd5a2e 100644 --- a/src/discord_bot/commands/arcane.js +++ b/src/discord_bot/commands/arcane.js @@ -1,27 +1,35 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const { SlashCommandBuilder } = require("@discordjs/builders"); -const { postRequest } = require('../utils/request'); +const { postRequest } = require("../utils/request"); module.exports = { - data: new SlashCommandBuilder() - .setName('arcane') - .setDescription('Repliddd!') - .addStringOption(option => { + data: new SlashCommandBuilder() + .setName("arcane") + .setDescription("Repliddd!") + .addStringOption((option) => { return option - .setName('message') - .setDescription('The user\'s avatar to show') - .setRequired(true) + .setName("message") + .setDescription("The user's avatar to show") + .setRequired(true); }), - async execute(client, interaction) { - const message = interaction.options.getString('message'); - const target_url = '/ai/ar', + async execute(client, interaction) { + const message = interaction.options.getString("message"); + const target_url = "/ai/ar", target_args = { - message + message, + }; + await interaction.deferReply(); + postRequest( + client, + interaction, + target_url, + target_args, + function (data) { + return interaction.editReply({ + content: data, + ephemeral: true, + }); } - await interaction.deferReply(); - postRequest(client, interaction, target_url, target_args, function (data) { - return interaction.editReply( { content: data, ephemeral: true } ); - - }); - }, -}; \ No newline at end of file + ); + }, +}; diff --git a/src/discord_bot/commands/avatar.js b/src/discord_bot/commands/avatar.js index ae56f18..b5b45ec 100644 --- a/src/discord_bot/commands/avatar.js +++ b/src/discord_bot/commands/avatar.js @@ -1,25 +1,27 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const { SlashCommandBuilder } = require("@discordjs/builders"); module.exports = { - data: new SlashCommandBuilder() - .setName('avatar') - .setDescription('Get the avatar URL of the selected user, or your own avatar.') - .addUserOption(option => { + data: new SlashCommandBuilder() + .setName("avatar") + .setDescription( + "Get the avatar URL of the selected user, or your own avatar." + ) + .addUserOption((option) => { return option - .setName('target') - .setDescription('The user\'s avatar to show') + .setName("target") + .setDescription("The user's avatar to show"); }), - async execute(client, interaction) { - const user = interaction.options.getUser('target'); - if (user) { + async execute(client, interaction) { + const user = interaction.options.getUser("target"); + if (user) { return interaction.reply( `${user.username}'s avatar: ` + - `${user.displayAvatarURL({ dynamic: true })}` + `${user.displayAvatarURL({ dynamic: true })}` ); } return interaction.reply( `Your avatar: ` + - `${interaction.user.displayAvatarURL({ dynamic: true })}` + `${interaction.user.displayAvatarURL({ dynamic: true })}` ); - }, -}; \ No newline at end of file + }, +}; diff --git a/src/discord_bot/commands/ping.js b/src/discord_bot/commands/ping.js index fcaf1c0..6c2380f 100644 --- a/src/discord_bot/commands/ping.js +++ b/src/discord_bot/commands/ping.js @@ -1,19 +1,18 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'), - wait = require('util').promisify(setTimeout); +const { SlashCommandBuilder } = require("@discordjs/builders"), + wait = require("util").promisify(setTimeout); module.exports = { - data: new SlashCommandBuilder() - .setName('ping') - .setDescription('Replies with Pong!'), - async execute(client, interaction) { - // await interaction.deferReply(); - // await wait(4000); - // await interaction.editReply('Pong'); - // await wait(2000); - // await interaction.editReply('Pong again!'); - // await wait(2000); - // // await interaction.followUp('???Pong again!'); + data: new SlashCommandBuilder() + .setName("ping") + .setDescription("Replies with Pong!"), + async execute(client, interaction) { + // await interaction.deferReply(); + // await wait(4000); + // await interaction.editReply('Pong'); + // await wait(2000); + // await interaction.editReply('Pong again!'); + // await wait(2000); + // // await interaction.followUp('???Pong again!'); // await interaction.deleteReply(); await interaction.reply(`Websocket heartbeat: ${client.ws.ping}ms.`); - - }, -}; \ No newline at end of file + }, +}; diff --git a/src/discord_bot/commands/register.js b/src/discord_bot/commands/register.js index 16a7313..5785a69 100644 --- a/src/discord_bot/commands/register.js +++ b/src/discord_bot/commands/register.js @@ -1,18 +1,18 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const { SlashCommandBuilder } = require("@discordjs/builders"); module.exports = { - data: new SlashCommandBuilder() - .setName('register') - .setDescription('dd') - .addUserOption(option => { + data: new SlashCommandBuilder() + .setName("register") + .setDescription("dd") + .addUserOption((option) => { return option - .setName('username') + .setName("username") .setRequired(true) - .setDescription('The username of the user to register'); + .setDescription("The username of the user to register"); }), - async execute(client, interaction) { - const role = interaction.options.getRole('streamer'); - const member = interaction.options.getMember('target'); + async execute(client, interaction) { + const role = interaction.options.getRole("streamer"); + const member = interaction.options.getMember("target"); member.roles.add(role); - }, -}; \ No newline at end of file + }, +}; diff --git a/src/discord_bot/commands/reset.js b/src/discord_bot/commands/reset.js index c32d206..afc46bb 100644 --- a/src/discord_bot/commands/reset.js +++ b/src/discord_bot/commands/reset.js @@ -1,19 +1,20 @@ -require('dotenv').config({path: `../../.env`}); -const { SlashCommandBuilder } = require('@discordjs/builders'); +require("dotenv").config({ path: `../../.env` }); +const { SlashCommandBuilder } = require("@discordjs/builders"); // Turn bot off (destroy), then turn it back on function resetBot(channel) { // send channel a message that you're resetting bot [optional] - channel.send('Resetting...') - .then(msg => client.destroy()) - .then(() => client.login(process.env.DISCORD_BOT_TOKEN)); + channel + .send("Resetting...") + .then((msg) => client.destroy()) + .then(() => client.login(process.env.DISCORD_BOT_TOKEN)); } module.exports = { - data: new SlashCommandBuilder() - .setName('reset') - .setDescription('Replies with Pong!'), - async execute(client, interaction) { + data: new SlashCommandBuilder() + .setName("reset") + .setDescription("Replies with Pong!"), + async execute(client, interaction) { resetBot(interaction.channel); - }, -}; \ No newline at end of file + }, +}; diff --git a/src/discord_bot/commands/server.js b/src/discord_bot/commands/server.js index a24f8ab..4194bad 100644 --- a/src/discord_bot/commands/server.js +++ b/src/discord_bot/commands/server.js @@ -1,13 +1,13 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const { SlashCommandBuilder } = require("@discordjs/builders"); module.exports = { - data: new SlashCommandBuilder() - .setName('server') - .setDescription('Display info about this server.'), - async execute(client, interaction) { - return interaction.reply( + data: new SlashCommandBuilder() + .setName("server") + .setDescription("Display info about this server."), + async execute(client, interaction) { + return interaction.reply( `Server name: ` + - `${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}` + `${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}` ); - }, -}; \ No newline at end of file + }, +}; diff --git a/src/discord_bot/commands/test.js b/src/discord_bot/commands/test.js deleted file mode 100644 index 44a5c2f..0000000 --- a/src/discord_bot/commands/test.js +++ /dev/null @@ -1,25 +0,0 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); - -module.exports = { - data: new SlashCommandBuilder() - .setName('test') - .setDescription('Get the avatar URL of the selected user, or your own avatar.') - .addUserOption(option => { - return option - .setName('target') - .setDescription('The user\'s avatar to show') - }), - async execute(client, interaction) { - const user = interaction.options.getUser('target'); - if (user) { - return interaction.reply( - `${user.username}'s avatar: ` + - `${user.displayAvatarURL({ dynamic: true })}` - ); - } - return interaction.reply( - `Your avatar: ` + - `${interaction.user.displayAvatarURL({ dynamic: true })}` - ); - }, -}; \ No newline at end of file diff --git a/src/discord_bot/commands/user.js b/src/discord_bot/commands/user.js index 6dfc305..15a07b4 100644 --- a/src/discord_bot/commands/user.js +++ b/src/discord_bot/commands/user.js @@ -1,14 +1,14 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const { SlashCommandBuilder } = require("@discordjs/builders"); module.exports = { - data: new SlashCommandBuilder() - .setName('user') - .setDescription('Display info about yourself.'), - async execute(client, interaction) { - return interaction.reply( - `Your username: ` + - `${interaction.user.username}\n` + - `Your ID: ${interaction.user.id}` + data: new SlashCommandBuilder() + .setName("user") + .setDescription("Display info about yourself."), + async execute(client, interaction) { + return interaction.reply( + `Your username: ` + + `${interaction.user.username}\n` + + `Your ID: ${interaction.user.id}` ); - }, -}; \ No newline at end of file + }, +}; diff --git a/src/discord_bot/deploy-commands.js b/src/discord_bot/deploy-commands.js index 2de33dd..fe28624 100644 --- a/src/discord_bot/deploy-commands.js +++ b/src/discord_bot/deploy-commands.js @@ -1,35 +1,35 @@ -require('dotenv').config({path:'../../.env'}); -const fs = require('fs'), - { REST } = require('@discordjs/rest'), - { Routes } = require('discord-api-types/v9'); +require("dotenv").config({ path: "../../.env" }); +const fs = require("fs"), + { REST } = require("@discordjs/rest"), + { Routes } = require("discord-api-types/v9"); const clientId = process.env.DISCORD_CLIENT_ID, guildId = process.env.DISCORD_SUDOGROUP_ID; const commands = []; const commandFiles = fs - .readdirSync('./commands') - .filter(file => file.endsWith('.js')); + .readdirSync("./commands") + .filter((file) => file.endsWith(".js")); for (const file of commandFiles) { - const command = require(`./commands/${file}`); - commands.push(command.data.toJSON()); + const command = require(`./commands/${file}`); + commands.push(command.data.toJSON()); } -const rest = new REST({ version: '9' }).setToken(process.env.DISCORD_TOKEN); +const rest = new REST({ version: "9" }).setToken(process.env.DISCORD_TOKEN); (async () => { - try { - console.log('Started refreshing application (/) commands.'); + try { + console.log("Started refreshing application (/) commands."); - await rest.put( - Routes.applicationGuildCommands(clientId, guildId), - // Routes.applicationCommands(clientId), - { body: commands }, - ); + await rest.put( + Routes.applicationGuildCommands(clientId, guildId), + // Routes.applicationCommands(clientId), + { body: commands } + ); - console.log('Successfully reloaded application (/) commands.'); - } catch (error) { - console.error(error); - } -})(); \ No newline at end of file + console.log("Successfully reloaded application (/) commands."); + } catch (error) { + console.error(error); + } +})(); diff --git a/src/discord_bot/events/interactionCraete.js b/src/discord_bot/events/interactionCraete.js index 49472d0..8e7b21b 100644 --- a/src/discord_bot/events/interactionCraete.js +++ b/src/discord_bot/events/interactionCraete.js @@ -1,6 +1,6 @@ module.exports = { - name: 'interactionCreate', - async execute(client, interaction) { + name: "interactionCreate", + async execute(client, interaction) { if (!interaction.isCommand()) return; const command = client.commands.get(interaction.commandName); @@ -11,7 +11,10 @@ module.exports = { await command.execute(client, interaction); } catch (error) { console.error(error); - await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true }); + await interaction.reply({ + content: "There was an error while executing this command!", + ephemeral: true, + }); } - }, -}; \ No newline at end of file + }, +}; diff --git a/src/discord_bot/events/messageCreate.js b/src/discord_bot/events/messageCreate.js index 11e89f6..51529f8 100644 --- a/src/discord_bot/events/messageCreate.js +++ b/src/discord_bot/events/messageCreate.js @@ -1,7 +1,7 @@ -require('dotenv').config({path: '../../.env'}); +require("dotenv").config({ path: "../../.env" }); module.exports = { - name: 'messageCreate', - async execute(client, msg) { + name: "messageCreate", + async execute(client, msg) { // console.log(msg.content); - }, -}; \ No newline at end of file + }, +}; diff --git a/src/discord_bot/events/ready.js b/src/discord_bot/events/ready.js index e0dbc17..a1315dc 100644 --- a/src/discord_bot/events/ready.js +++ b/src/discord_bot/events/ready.js @@ -1,7 +1,7 @@ module.exports = { - name: 'ready', - once: true, - execute(client, mgs) { - console.log(`Ready! Logged in as ${client.user.tag}`); - }, -}; \ No newline at end of file + name: "ready", + once: true, + execute(client, mgs) { + console.log(`Ready! Logged in as ${client.user.tag}`); + }, +}; diff --git a/src/discord_bot/index.js b/src/discord_bot/index.js index 1b7eee0..e85c406 100644 --- a/src/discord_bot/index.js +++ b/src/discord_bot/index.js @@ -1,44 +1,43 @@ -require('dotenv').config({path:'../../.env'}); +require("dotenv").config({ path: "../../.env" }); -const fs = require('fs'), - { Client, Collection, Intents } = require('discord.js'); +const fs = require("fs"), + { Client, Collection, Intents } = require("discord.js"); // client -const client = new Client({ - intents: [ - Intents.FLAGS.GUILDS, - Intents.FLAGS.GUILD_MESSAGES - ] +const client = new Client({ + intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES], }); // commands client.commands = new Collection(); -const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js')); +const commandFiles = fs + .readdirSync("./commands") + .filter((file) => file.endsWith(".js")); // const permissions = require('./permissions'); for (const file of commandFiles) { - const command = require(`./commands/${file}`); + const command = require(`./commands/${file}`); // await command.permissions.add({ permissions }); - client.commands.set(command.data.name, command); + client.commands.set(command.data.name, command); } // events const eventFiles = fs - .readdirSync('./events') - .filter(file => file.endsWith('.js')); + .readdirSync("./events") + .filter((file) => file.endsWith(".js")); for (const file of eventFiles) { - const event = require(`./events/${file}`); - if (event.once) { - client.once(event.name, (...args) => { - event.execute(client,...args) + const event = require(`./events/${file}`); + if (event.once) { + client.once(event.name, (...args) => { + event.execute(client, ...args); }); - } else { - client.on(event.name, (...args) => { - event.execute(client,...args) + } else { + client.on(event.name, (...args) => { + event.execute(client, ...args); }); - } + } } client.login(process.env.DISCORD_TOKEN); diff --git a/src/discord_bot/permissions.js b/src/discord_bot/permissions.js index 30b45d7..00207ce 100644 --- a/src/discord_bot/permissions.js +++ b/src/discord_bot/permissions.js @@ -1,7 +1,7 @@ const permissions = [ - { - id: '464464090157416448', - type: 'ROLE', - permission: false, - }, -]; \ No newline at end of file + { + id: "464464090157416448", + type: "ROLE", + permission: false, + }, +]; diff --git a/src/discord_bot/templates/command.js b/src/discord_bot/templates/command.js index 9f344d9..7b4f2af 100644 --- a/src/discord_bot/templates/command.js +++ b/src/discord_bot/templates/command.js @@ -1,13 +1,11 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const { SlashCommandBuilder } = require("@discordjs/builders"); module.exports = { - data: new SlashCommandBuilder() - .setName('') - .setDescription('') - .addUserOption(option => { - return option.setName('') - .setDescription('') + data: new SlashCommandBuilder() + .setName("") + .setDescription("") + .addUserOption((option) => { + return option.setName("").setDescription(""); }), - async execute(interaction) { - }, -}; \ No newline at end of file + async execute(interaction) {}, +}; diff --git a/src/discord_bot/utils/request.js b/src/discord_bot/utils/request.js index 293a9b4..6ad0b01 100644 --- a/src/discord_bot/utils/request.js +++ b/src/discord_bot/utils/request.js @@ -1,33 +1,34 @@ -require('dotenv').config({path:'../../.env'}); +require("dotenv").config({ path: "../../.env" }); -const fetch = require('node-fetch'); +const fetch = require("node-fetch"); async function postRequest(client, interaction, url, args, calllbacks) { - console.log(`http://localhost:${process.env.PORT}${url}`) + console.log(`http://localhost:${process.env.PORT}${url}`); await fetch(`http://localhost:${process.env.PORT}${url}`, { - method: 'POST', + method: "POST", body: JSON.stringify(args), - headers: { 'Content-Type': 'application/json' } - }) - .then(async res => { - calllbacks(await res.json()); - }) - .catch(error => { - calllbacks(error) + headers: { "Content-Type": "application/json" }, }) + .then(async (res) => { + calllbacks(await res.json()); + }) + .catch((error) => { + calllbacks(error); + }); } function postRequestMore(client, interaction, url, args, msg, calllbacks) { - axios.post(`http://localhost:${process.env.PORT}${url}`, args) - .then(res => { - calllbacks(msg[1]); - }) - .catch(error => { - calllbacks(error); - }) + axios + .post(`http://localhost:${process.env.PORT}${url}`, args) + .then((res) => { + calllbacks(msg[1]); + }) + .catch((error) => { + calllbacks(error); + }); calllbacks(msg[0]); } module.exports = { postRequest, - postRequestMore -} \ No newline at end of file + postRequestMore, +}; diff --git a/src/twitch_bot/commands/anime.js b/src/twitch_bot/commands/anime.js index 52bef6a..19b9759 100644 --- a/src/twitch_bot/commands/anime.js +++ b/src/twitch_bot/commands/anime.js @@ -1,33 +1,32 @@ -require('dotenv').config({path:'../../../.env'}); +require("dotenv").config({ path: "../../../.env" }); -const { makeid } = require('../utils/utils'); -const { postRequestMore } = require('../utils/request'); +const { makeid } = require("../utils/utils"); +const { postRequestMore } = require("../utils/request"); module.exports = { name: "anime", description: "Ping!", async execute(client, channel, context, msg, args) { const filename = `${makeid(5)}.jpg`, - streamer = channel.replace('#',''); - let url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${streamer}.jpg` - if(args.length != 0){ - if(args[0].includes('http://') || args[0].includes('https://')){ + streamer = channel.replace("#", ""); + let url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${streamer}.jpg`; + if (args.length != 0) { + if (args[0].includes("http://") || args[0].includes("https://")) { url = args[0]; - } - else{ - url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${args[0]}.jpg` + } else { + url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${args[0]}.jpg`; } } - const target_url = '/ai/anime', + const target_url = "/ai/anime", target_args = { url, - filename + filename, }, target_msg = [ `Processing: ${filename} - style=anime`, - `Check: ${process.env.BASE_URL}${target_url}/${filename}` - ] - postRequestMore(client, channel, target_url, target_args, target_msg) - } + `Check: ${process.env.BASE_URL}${target_url}/${filename}`, + ]; + postRequestMore(client, channel, target_url, target_args, target_msg); + }, }; diff --git a/src/twitch_bot/commands/ar.js b/src/twitch_bot/commands/ar.js index 6cc8d80..da49de9 100644 --- a/src/twitch_bot/commands/ar.js +++ b/src/twitch_bot/commands/ar.js @@ -1,15 +1,15 @@ -const { postRequest } = require('../utils/request'); +const { postRequest } = require("../utils/request"); module.exports = { name: "ar", description: "Ping!", async execute(client, channel, context, msg, args) { - const message = args.join(' '); + const message = args.join(" "); - const target_url = '/ai/ar', + const target_url = "/ai/ar", target_args = { - message - } - postRequest(client, channel, target_url, target_args) + message, + }; + postRequest(client, channel, target_url, target_args); }, }; diff --git a/src/twitch_bot/commands/arcane.js b/src/twitch_bot/commands/arcane.js index c7a4fb8..83df13f 100644 --- a/src/twitch_bot/commands/arcane.js +++ b/src/twitch_bot/commands/arcane.js @@ -1,33 +1,32 @@ -require('dotenv').config({path:'../../../.env'}); +require("dotenv").config({ path: "../../../.env" }); -const { makeid } = require('../utils/utils'); -const { postRequestMore } = require('../utils/request'); +const { makeid } = require("../utils/utils"); +const { postRequestMore } = require("../utils/request"); module.exports = { name: "arcane", description: "Ping!", async execute(client, channel, context, msg, args) { const filename = `${makeid(5)}.jpg`, - streamer = channel.replace('#',''); - let url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${streamer}.jpg` - if(args.length != 0){ - if(args[0].includes('http://') || args[0].includes('https://')){ + streamer = channel.replace("#", ""); + let url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${streamer}.jpg`; + if (args.length != 0) { + if (args[0].includes("http://") || args[0].includes("https://")) { url = args[0]; - } - else{ - url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${args[0]}.jpg` + } else { + url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${args[0]}.jpg`; } } - const target_url = '/ai/arcane', + const target_url = "/ai/arcane", target_args = { url, - filename + filename, }, target_msg = [ `Processing: ${filename} - style=arcane`, - `Check: ${process.env.BASE_URL}${target_url}/${filename}` - ] - postRequestMore(client, channel, target_url, target_args, target_msg) - } + `Check: ${process.env.BASE_URL}${target_url}/${filename}`, + ]; + postRequestMore(client, channel, target_url, target_args, target_msg); + }, }; diff --git a/src/twitch_bot/commands/art.js b/src/twitch_bot/commands/art.js index 717440f..123886a 100644 --- a/src/twitch_bot/commands/art.js +++ b/src/twitch_bot/commands/art.js @@ -1,40 +1,39 @@ -require('dotenv').config({path:'../../../.env'}); +require("dotenv").config({ path: "../../../.env" }); -const { makeid, getRandomInt } = require('../utils/utils'); -const { postRequestMore } = require('../utils/request'); +const { makeid, getRandomInt } = require("../utils/utils"); +const { postRequestMore } = require("../utils/request"); module.exports = { name: "art", description: "Ping!", async execute(client, channel, context, msg, args) { const filename = `${makeid(5)}.jpg`, - streamer = channel.replace('#',''); - + streamer = channel.replace("#", ""); + let style_selected = getRandomInt(30); - - let url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${streamer}.jpg` - if(args.length > 0){ - if(args[0].includes('http://') || args[0].includes('https://')){ + + let url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${streamer}.jpg`; + if (args.length > 0) { + if (args[0].includes("http://") || args[0].includes("https://")) { url = args[0]; - } - else{ - url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${args[0]}.jpg` + } else { + url = `https://static-cdn.jtvnw.net/previews-ttv/live_user_${args[0]}.jpg`; } } - if(args.length > 1){ + if (args.length > 1) { style_selected = args[1]; } - const target_url = '/ai/style_transfer', + const target_url = "/ai/style_transfer", target_args = { url, style_selected, - filename + filename, }, target_msg = [ `Processing: ${filename} - style=${style_selected}`, - `Check: ${process.env.BASE_URL}${target_url}/${filename}` - ] - postRequestMore(client, channel, target_url, target_args, target_msg) - } + `Check: ${process.env.BASE_URL}${target_url}/${filename}`, + ]; + postRequestMore(client, channel, target_url, target_args, target_msg); + }, }; diff --git a/src/twitch_bot/commands/bye.js b/src/twitch_bot/commands/bye.js index 8e0590b..70a1829 100644 --- a/src/twitch_bot/commands/bye.js +++ b/src/twitch_bot/commands/bye.js @@ -2,9 +2,6 @@ module.exports = { name: "bye", description: "Ping!", async execute(client, channel, context, msg, args) { - client.say( - channel, - `Bye chat!` - ); + client.say(channel, `Bye chat!`); }, }; diff --git a/src/twitch_bot/commands/chat.js b/src/twitch_bot/commands/chat.js index 4b04fd9..9d2bd19 100644 --- a/src/twitch_bot/commands/chat.js +++ b/src/twitch_bot/commands/chat.js @@ -1,15 +1,15 @@ -const { postRequest } = require('../utils/request'); +const { postRequest } = require("../utils/request"); module.exports = { name: "chat", description: "Ping!", async execute(client, channel, context, msg, args) { - const message = args.join(' '); + const message = args.join(" "); - const target_url = '/ai/chat', + const target_url = "/ai/chat", target_args = { - message - } - postRequest(client, channel, target_url, target_args) + message, + }; + postRequest(client, channel, target_url, target_args); }, }; diff --git a/src/twitch_bot/commands/en.js b/src/twitch_bot/commands/en.js index 168911e..dc84f2f 100644 --- a/src/twitch_bot/commands/en.js +++ b/src/twitch_bot/commands/en.js @@ -1,15 +1,15 @@ -const { postRequest } = require('../utils/request'); +const { postRequest } = require("../utils/request"); module.exports = { name: "en", description: "Ping!", async execute(client, channel, context, msg, args) { - const message = args.join(' '); + const message = args.join(" "); - const target_url = '/ai/en', + const target_url = "/ai/en", target_args = { - message - } - postRequest(client, channel, target_url, target_args) + message, + }; + postRequest(client, channel, target_url, target_args); }, }; diff --git a/src/twitch_bot/commands/hi.js b/src/twitch_bot/commands/hi.js index ad4ec7e..b024626 100644 --- a/src/twitch_bot/commands/hi.js +++ b/src/twitch_bot/commands/hi.js @@ -4,8 +4,8 @@ module.exports = { async execute(client, channel, context, msg, args) { client.say( channel, - `Hi @${channel.replace('#','')}, ` + - `@${context.username} brought me here! I'm a bot and I am alive!!` + `Hi @${channel.replace("#", "")}, ` + + `@${context.username} brought me here! I'm a bot and I am alive!!` ); }, }; diff --git a/src/twitch_bot/commands/ping.js b/src/twitch_bot/commands/ping.js index a05a712..da8c07d 100644 --- a/src/twitch_bot/commands/ping.js +++ b/src/twitch_bot/commands/ping.js @@ -2,6 +2,6 @@ module.exports = { name: "ping", description: "Ping!", async execute(client, channel, context, msg, args) { - client.say(channel,"Pong."); + client.say(channel, "Pong."); }, }; diff --git a/src/twitch_bot/commands/wolf.js b/src/twitch_bot/commands/wolf.js index 3363eaa..048df0c 100644 --- a/src/twitch_bot/commands/wolf.js +++ b/src/twitch_bot/commands/wolf.js @@ -1,15 +1,15 @@ -const { postRequest } = require('../utils/request'); +const { postRequest } = require("../utils/request"); module.exports = { name: "wolf", description: "Ping!", async execute(client, channel, context, msg, args) { - const message = args.join(' '); + const message = args.join(" "); - const target_url = '/ai/wolf', + const target_url = "/ai/wolf", target_args = { - message - } - postRequest(client, channel, target_url, target_args) + message, + }; + postRequest(client, channel, target_url, target_args); }, }; diff --git a/src/twitch_bot/events/connected.js b/src/twitch_bot/events/connected.js index 5d2dff8..9616f4e 100644 --- a/src/twitch_bot/events/connected.js +++ b/src/twitch_bot/events/connected.js @@ -1,7 +1,7 @@ -module.exports = { - name: 'connected', +module.exports = { + name: "connected", once: true, async execute(client, address, port) { - console.log(`Logged in as ${address, port}!`); - } + console.log(`Logged in as ${(address, port)}!`); + }, }; diff --git a/src/twitch_bot/events/message.js b/src/twitch_bot/events/message.js index b8f0437..cf9bde4 100644 --- a/src/twitch_bot/events/message.js +++ b/src/twitch_bot/events/message.js @@ -1,11 +1,16 @@ -require('dotenv').config({path:'../../.env'}); +require("dotenv").config({ path: "../../.env" }); -module.exports = { - name: 'message', +module.exports = { + name: "message", execute(client, channel, context, msg) { - if(!msg.startsWith(process.env.PREFIX) || context.username.toLowerCase() === process.env.TWITCH_BOT_USERNAME.toLowerCase()) return; - console.log(context.username) - if (context.username != 'sudomaze') return; + if ( + !msg.startsWith(process.env.PREFIX) || + context.username.toLowerCase() === + process.env.TWITCH_BOT_USERNAME.toLowerCase() + ) + return; + console.log(context.username); + if (context.username != "sudomaze") return; // pre-process the command const args = msg.substring(process.env.PREFIX.length).split(/ +/), // remove process.env.PREFIX and split the argument has multiple arguments commandName = args.shift().toLowerCase(); // get command and turn it to lowercase @@ -30,7 +35,7 @@ module.exports = { reply += `\nThe proper usage would be: \`${process.env.PREFIX}${command.name} ${command.usage}\``; } - return client.say(channel,reply); + return client.say(channel, reply); } // execute @@ -38,9 +43,10 @@ module.exports = { command.execute(client, channel, context, msg, args); } catch (error) { console.error(error); - client.say(channel, + client.say( + channel, "there was an error trying to execute that command!" ); } - } -}; \ No newline at end of file + }, +}; diff --git a/src/twitch_bot/index.js b/src/twitch_bot/index.js index b32cdf3..dac051d 100644 --- a/src/twitch_bot/index.js +++ b/src/twitch_bot/index.js @@ -1,27 +1,29 @@ -require('dotenv').config({path:'../../.env'}); +require("dotenv").config({ path: "../../.env" }); const fs = require("fs"), - { Client } = require('tmi.js'), - { Collection } = require("discord.js"); + { Client } = require("tmi.js"), + { Collection } = require("discord.js"); // client const client = new Client({ - options: { debug: true, messagesLogLevel: "info" }, - connection: { - reconnect: true, - secure: true - }, - identity: { - username: `${process.env.TWITCH_BOT_USERNAME}`, - password: `oauth:${process.env.TWITCH_ACCESS_TOKEN}` - }, - channels: [`${process.env.TWITCH_CHANNEL}`, 'migren2009'] + options: { debug: true, messagesLogLevel: "info" }, + connection: { + reconnect: true, + secure: true, + }, + identity: { + username: `${process.env.TWITCH_BOT_USERNAME}`, + password: `oauth:${process.env.TWITCH_ACCESS_TOKEN}`, + }, + channels: [`${process.env.TWITCH_CHANNEL}`, "migren2009"], }); // commands client.commands = new Collection(); -const commandFiles = fs.readdirSync("./commands").filter((file) => file.endsWith(".js")); +const commandFiles = fs + .readdirSync("./commands") + .filter((file) => file.endsWith(".js")); for (const file of commandFiles) { const command = require(`./commands/${file}`); @@ -29,15 +31,17 @@ for (const file of commandFiles) { } // events -const eventFiles = fs.readdirSync('./events').filter(file => file.endsWith('.js')); +const eventFiles = fs + .readdirSync("./events") + .filter((file) => file.endsWith(".js")); for (const file of eventFiles) { - const event = require(`./events/${file}`); - if (event.once) { - client.once(event.name, (...args) => event.execute(client,...args)); - } else { - client.on(event.name, (...args) => event.execute(client,...args)); - } + const event = require(`./events/${file}`); + if (event.once) { + client.once(event.name, (...args) => event.execute(client, ...args)); + } else { + client.on(event.name, (...args) => event.execute(client, ...args)); + } } -client.connect().catch(console.error); \ No newline at end of file +client.connect().catch(console.error); diff --git a/src/twitch_bot/utils/request.js b/src/twitch_bot/utils/request.js index c333c28..d61fee6 100644 --- a/src/twitch_bot/utils/request.js +++ b/src/twitch_bot/utils/request.js @@ -1,28 +1,30 @@ -require('dotenv').config({path:'../../.env'}); +require("dotenv").config({ path: "../../.env" }); -const axios = require('axios'); +const axios = require("axios"); function postRequest(client, channel, url, args) { - axios.post(`http://localhost:${process.env.PORT}${url}`, args) - .then(res => { - client.say(channel, res.data); - }) - .catch(error => { - console.error(error) - }) + axios + .post(`http://localhost:${process.env.PORT}${url}`, args) + .then((res) => { + client.say(channel, res.data); + }) + .catch((error) => { + console.error(error); + }); } function postRequestMore(client, channel, url, args, msg) { - axios.post(`http://localhost:${process.env.PORT}${url}`, args) - .then(res => { - client.say(channel, msg[1]); - }) - .catch(error => { - console.error(error) - }) + axios + .post(`http://localhost:${process.env.PORT}${url}`, args) + .then((res) => { + client.say(channel, msg[1]); + }) + .catch((error) => { + console.error(error); + }); client.say(channel, msg[0]); } module.exports = { postRequest, - postRequestMore -} \ No newline at end of file + postRequestMore, +}; diff --git a/src/twitch_bot/utils/utils.js b/src/twitch_bot/utils/utils.js index 2c4a0f2..8b59086 100644 --- a/src/twitch_bot/utils/utils.js +++ b/src/twitch_bot/utils/utils.js @@ -1,10 +1,12 @@ function makeid(length) { - var result = ''; - var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var result = ""; + var characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var charactersLength = characters.length; - for ( var i = 0; i < length; i++ ) { - result += characters.charAt(Math.floor(Math.random() * - charactersLength)); + for (var i = 0; i < length; i++) { + result += characters.charAt( + Math.floor(Math.random() * charactersLength) + ); } return result; } @@ -16,4 +18,4 @@ function getRandomInt(max) { module.exports = { makeid, getRandomInt, -} \ No newline at end of file +}; diff --git a/test/twitch_bot/commands/ping.test.js b/test/twitch_bot/commands/ping.test.js new file mode 100644 index 0000000..18036df --- /dev/null +++ b/test/twitch_bot/commands/ping.test.js @@ -0,0 +1,168 @@ +require("dotenv").config({ path: ".env" }); +jest.useFakeTimers(); +const fs = require("fs"), + tmi = require("tmi.js"), + { Collection } = require("discord.js"); + +const WebSocketServer = require('ws').Server; +const noop = function() {}; + +const catchConnectError = err => { + if(err !== 'Connection closed.') { + console.error(err); + } +}; + +const tests = [ + { + command: 'ping', + inputParams: [], + returnedParams: [], + serverTest: '/ping', + serverCommand: 'ping', + } +]; +let user, guild, channel, channel_projects, msg; +describe("testing commands", () => { + beforeEach(function() { + // Initialize websocket server + this.server = new WebSocketServer({ port: 7000 }); + this.client = new tmi.client({ + connection: { + server: 'localhost', + port: 7000, + timeout: 1, + reconnect: false + } + }); + }); + + afterEach(function() { + // Shut down websocket server + this.server.close(); + this.client = null; + }); + + it('handles commands when disconnected', function(cb) { + this.client.subscribers('local7000').then(noop, err => { + err.should.containEql('Not connected to server.'); + cb(); + }); + }); + tests.forEach(test => { + it(`should handle ${test.command}`, function(cb) { + const { client, server } = this; + + server.on('connection', ws => { + ws.on('message', message => { + // Ensure that the message starts with NICK + if(!message.indexOf('NICK')) { + const user = client.getUsername(); + ws.send(`:${user}! JOIN #local7000`); + return; + } + // Otherwise, send the command + if(~message.indexOf(test.serverTest)) { + if(typeof test.serverCommand === 'function') { + test.serverCommand(client, ws); + } + else { + ws.send(test.serverCommand); + } + } + }); + }); + + client.on('join', function() { + client[test.command].apply(this, test.inputParams).then(data => { + test.returnedParams.forEach((param, index) => { + data[index].should.eql(param); + }); + client.disconnect(); + cb(); + }); + }); + + client.connect().catch(catchConnectError); + }); + + if(test.errorCommands) { + test.errorCommands.forEach(error => { + it(`should handle ${test.command} errors`, function(cb) { + const { client, server } = this; + + server.on('connection', ws => { + ws.on('message', message => { + // Ensure that the message starts with NICK + if(!message.indexOf('NICK')) { + const user = client.getUsername(); + ws.send(`:${user}! JOIN #local7000`); + return; + } + // Otherwise, send the command + if(~message.indexOf(test.serverTest)) { + ws.send(error); + } + }); + }); + + client.on('join', function() { + client[test.command].apply(this, test.inputParams).then(noop, err => { + err.should.be.ok(); + client.disconnect(); + cb(); + }); + }); + + client.connect().catch(catchConnectError); + }); + }); + } + + if(test.testTimeout) { + it(`should handle ${test.command} timeout`, function(cb) { + const { client, server } = this; + + server.on('connection', ws => { + ws.on('message', message => { + // Ensure that the message starts with NICK + if(!message.indexOf('NICK')) { + ws.send('dummy'); + return; + } + }); + }); + + client.on('logon', function() { + client[test.command].apply(this, test.inputParams).then(noop, err => { + err.should.be.ok(); + client.disconnect(); + cb(); + }); + }); + + client.connect().catch(catchConnectError); + }); + } + }); +}); + // tests.forEach(test => { + // events + // const eventFiles = fs + // .readdirSync("./src/twitch_bot/events") + // .filter((file) => file.endsWith(".js")); + + // for (const file of eventFiles) { + // const event = require(`../../../src/twitch_bot/events/${file}`); + // if (event.once) { + // this.client.once(event.name, (...args) => event.execute(this.client, ...args)); + // } else { + // this.client.on(event.name, (...args) => event.execute(this.client, ...args)); + // } + // } + // this.client.connect().catch(console.error); + // var cmd = this.client.commands.get("ping"); + // it(`test ${cmd.name}`, async () => { + // msg.channel.send = jest.fn(); + // cmd.execute(msg); + // }); \ No newline at end of file diff --git a/tests/test_twitch.js b/tests/test_twitch.js deleted file mode 100644 index e69de29..0000000