Browse Source
* Made some minor changes, still need to make bug fixes * Added some minor changes, still need to make bug fixes * Added some minor changes, still need to make bug fixes * Still need to fix auth issue with Docker * Major changes: going to detail more * Major changes detailed in PR, also added functionality that makes it so you have to react to previous question before requesting another * Changes are detailed in PR * Fixed minor bugs with packages Co-authored-by: Abheek <67982792+ADawesomeguy@users.noreply.github.com>pull/8/head
TejasOS
4 years ago
committed by
GitHub
8 changed files with 4019 additions and 67 deletions
@ -1,76 +1,551 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
import * as Discord from 'discord.js'; |
|||
import { execSync } from 'child_process'; |
|||
import fetch from 'node-fetch'; |
|||
import * as fs from 'fs'; |
|||
import * as path from 'path'; |
|||
import * as commands from './commands.js'; |
|||
import * as Discord from "discord.js"; |
|||
import { execSync } from "child_process"; |
|||
const client = new Discord.Client({ |
|||
partials: ["MESSAGE", "CHANNEL", "REACTION"], |
|||
}); |
|||
import fetch from "node-fetch"; |
|||
import * as fs from "fs"; |
|||
import axios from "axios"; |
|||
import userScore from "./mongooseModels/mongooseUserScoreModel.js"; |
|||
import {} from "dotenv/config.js"; |
|||
import mongoose from "mongoose"; |
|||
|
|||
const helpMessage = |
|||
"`do be helping`: display this help message\n`do be roundgen`: send a pdf round to the channel\n`do be roundgen dm`: dm a pdf round to you\n`do be scoring`: start a scoring session\n > `do be scoring (a/b)(4/10)`: add points to Team A or Team B\n > `do be scoring stop`: end scoring session and post final points\n > `do be servers`: send the number of servers this bot is a part of\n > `do be iss`: show the current location of the International Space Station\n`do be training`: send a quick practice problem (you **must** react to your answer, or the bot will yell at you)\n > subject options: astro, phys, chem, math, bio, ess, energy\n`do be top`: list cross-server top 10 players\n `do be about`: List people who contributed to this bot\n Source Code: https://github.com/ADawesomeguy/AwesomeSciBo (don't forget to star!)"; |
|||
|
|||
const client = new Discord.Client({ partials: ['MESSAGE', 'CHANNEL', 'REACTION'] }); |
|||
client.once("ready", () => { |
|||
mongoose |
|||
.connect(process.env.MONGO_URI, { |
|||
useUnifiedTopology: true, |
|||
useNewUrlParser: true, |
|||
}) |
|||
.then(() => { |
|||
console.log(client.user.username); |
|||
client.user.setActivity( |
|||
'for "do be helping" | Add me to your own server: adat.link/awscibo', |
|||
{ type: "WATCHING" } |
|||
); |
|||
}) |
|||
.catch((err) => console.log(err)); |
|||
}); |
|||
|
|||
let hits = 0; |
|||
client.on("guildCreate", (guild) => { |
|||
guild.channels.cache |
|||
.find( |
|||
(channel) => |
|||
channel.name === process.env.WELCOME_CHANNEL && channel.type === "text" |
|||
) |
|||
.send("'Sup, I'm the AwesomeSciBo bot!") |
|||
.catch(console.error); |
|||
}); |
|||
|
|||
if (fs.existsSync('numhits.txt')) { |
|||
fs.readFile('numhits.txt', 'utf8', function (err, data) { |
|||
hits = data; |
|||
if (err) { |
|||
function getSubjectUrl(subject) { |
|||
return `https://moose.lcsrc.org/subjects/${subject}.json`; |
|||
} |
|||
|
|||
async function updateScore(isCorrect, score, authorId) { |
|||
if (!isCorrect) { |
|||
return `Nice try! Your score is still ${score}.`; |
|||
} else { |
|||
score += 4; |
|||
if (score == 4) { |
|||
const newUserScore = new userScore({ |
|||
authorID: authorId, |
|||
score: score, |
|||
}); |
|||
newUserScore.save((err) => |
|||
err |
|||
? console.log("Error creating new user for scoring") |
|||
: console.log("Sucessfully created user to score.") |
|||
); |
|||
} else { |
|||
const doc = await userScore.findOne({ |
|||
authorID: authorId, |
|||
}); |
|||
doc.score = doc.score + 4; |
|||
doc.save(); |
|||
console.log("Succesfully updated score."); |
|||
} |
|||
|
|||
return `Great job! Your score is now ${score}.`; |
|||
} |
|||
} |
|||
|
|||
async function otherCommands(message) { |
|||
if ( |
|||
message.content.toLowerCase().startsWith("do be announcing") && |
|||
(message.author.id === process.env.ABHEEK_USER_ID || |
|||
message.author.id === process.env.TEJAS_USER_ID) |
|||
) { |
|||
const announcement = message.content.substring(17); |
|||
client.guilds.cache.forEach((guild) => { |
|||
const channel = guild.channels.cache.find( |
|||
(channelGeneral) => |
|||
channelGeneral.name === process.env.ANNOUNCING_CHANNEL |
|||
); |
|||
if (channel) { |
|||
if (channel.type === "text") { |
|||
channel.send(announcement).catch(console.error); |
|||
} |
|||
} |
|||
}); |
|||
} else if (message.content.toLowerCase().startsWith("do be training")) { |
|||
const authorId = message.author.id; |
|||
let score; |
|||
userScore |
|||
.findOne({ authorID: authorId }) |
|||
.lean() |
|||
.then((obj, err) => { |
|||
if (!obj) { |
|||
score = 0; |
|||
} else if (obj) { |
|||
score = obj.score; |
|||
} else { |
|||
console.log(err); |
|||
} |
|||
}); |
|||
if (message.content === "do be training") { |
|||
axios.get("https://scibowldb.com/api/questions/random").then((res) => { |
|||
const data = res.data; |
|||
const messageAuthorFilter = (m) => m.author.id === message.author.id; |
|||
message.reply(data.question.tossup_question).then(() => { |
|||
message.channel |
|||
.awaitMessages(messageAuthorFilter, { |
|||
max: 1, |
|||
time: 30000, |
|||
errors: ["time"], |
|||
}) |
|||
.then((answerMsg) => { |
|||
answerMsg = answerMsg.first(); |
|||
let predicted = null; |
|||
|
|||
if (data.question.tossup_format === "Multiple Choice") { |
|||
if ( |
|||
answerMsg.content.charAt(0).toLowerCase() === |
|||
data.question.tossup_answer.charAt(0).toLowerCase() |
|||
) { |
|||
predicted = "correct"; |
|||
} else { |
|||
fs.writeFile('numhits.txt', hits.toString(), (error) => { if (error) { console.log(error); } }); |
|||
predicted = "incorrect"; |
|||
} |
|||
} else { |
|||
if ( |
|||
answerMsg.content.toLowerCase() === |
|||
data.question.tossup_answer.toLowerCase() |
|||
) { |
|||
predicted = "correct"; |
|||
} else { |
|||
predicted = "incorrect"; |
|||
} |
|||
} |
|||
|
|||
client.once('ready', () => { |
|||
console.log(client.user.username); |
|||
client.user.setActivity("for \"do be helping\" | Add me to your own server: adat.link/awscibo", { type: "WATCHING" }); |
|||
answerMsg.channel.send( |
|||
`Correct answer: **${data.question.tossup_answer}**. Predicted: **${predicted}**. Please react to your answer!` |
|||
); |
|||
answerMsg.react("✅"); |
|||
answerMsg.react("❌"); |
|||
const filter = (reaction, user) => { |
|||
return ( |
|||
["❌", "✅"].includes(reaction.emoji.name) && |
|||
user.id === answerMsg.author.id |
|||
); |
|||
}; |
|||
answerMsg |
|||
.awaitReactions(filter, { |
|||
max: 1, |
|||
time: 600000, |
|||
errors: ["time"], |
|||
}) |
|||
.then((userReaction) => { |
|||
const reaction = userReaction.first(); |
|||
|
|||
if (reaction.emoji.name === "❌") { |
|||
updateScore(false, score, authorId).then((msgToReply) => |
|||
answerMsg.reply(msgToReply) |
|||
); |
|||
} else { |
|||
updateScore(true, score, authorId).then((msgToReply) => |
|||
answerMsg.reply(msgToReply) |
|||
); |
|||
} |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
} else { |
|||
const subject = message.content.substring(15); |
|||
let subjectURL; |
|||
|
|||
switch (subject) { |
|||
case "astro": |
|||
case "astronomy": |
|||
subjectURL = getSubjectUrl("astronomy"); |
|||
break; |
|||
case "bio": |
|||
case "biology": |
|||
subjectURL = getSubjectUrl("biology"); |
|||
break; |
|||
case "ess": |
|||
case "earth science": |
|||
case "es": |
|||
subjectURL = getSubjectUrl("ess"); |
|||
break; |
|||
case "chem": |
|||
case "chemistry": |
|||
subjectURL = getSubjectUrl("astronomy"); |
|||
break; |
|||
case "phys": |
|||
case "physics": |
|||
subjectURL = getSubjectUrl("physics"); |
|||
break; |
|||
case "math": |
|||
subjectURL = getSubjectUrl("math"); |
|||
break; |
|||
case "energy": |
|||
subjectURL = getSubjectUrl("energy"); |
|||
break; |
|||
default: |
|||
message.channel.send("Not a valid subject!"); |
|||
return; |
|||
} |
|||
|
|||
axios |
|||
.get(subjectURL) |
|||
.then((res) => { |
|||
const data = res.data; |
|||
const questionNum = Math.floor(Math.random() * data.length); |
|||
const messageFilter = (m) => m.author.id === authorId; |
|||
message.reply(data[questionNum].tossup_question).then(() => { |
|||
message.channel |
|||
.awaitMessages(messageFilter, { |
|||
max: 1, |
|||
time: 30000, |
|||
errors: ["time"], |
|||
}) |
|||
.then((answerMsg) => { |
|||
answerMsg = answerMsg.first(); |
|||
|
|||
let predicted = null; |
|||
if (data[questionNum].tossup_format === "Multiple Choice") { |
|||
if ( |
|||
answerMsg.content.charAt(0).toLowerCase() === |
|||
data[questionNum].tossup_answer.charAt(0).toLowerCase() |
|||
) { |
|||
predicted = "correct"; |
|||
} else { |
|||
predicted = "incorrect"; |
|||
} |
|||
} else { |
|||
if ( |
|||
answerMsg.content.toLowerCase() === |
|||
data[questionNum].tossup_answer.toLowerCase() |
|||
) { |
|||
predicted = "correct"; |
|||
} else { |
|||
predicted = "incorrect"; |
|||
} |
|||
} |
|||
answerMsg.channel.send( |
|||
`Correct answer: **${data[questionNum].tossup_answer}**. Predicted: **${predicted}**. Please react to your answer!` |
|||
); |
|||
answerMsg.react("✅"); |
|||
answerMsg.react("❌"); |
|||
const reactionFilter = (reaction, user) => { |
|||
return ( |
|||
["❌", "✅"].includes(reaction.emoji.name) && |
|||
user.id === answerMsg.author.id |
|||
); |
|||
}; |
|||
answerMsg |
|||
.awaitReactions(reactionFilter, { |
|||
max: 1, |
|||
time: 600000, |
|||
errors: ["time"], |
|||
}) |
|||
.then(async (userReaction) => { |
|||
const reaction = userReaction.first(); |
|||
if (reaction.emoji.name == "❌") { |
|||
updateScore(false, score, authorId).then((msgToReply) => |
|||
answerMsg.reply(msgToReply) |
|||
); |
|||
} else { |
|||
updateScore(true, score, authorId).then((msgToReply) => |
|||
answerMsg.reply(msgToReply) |
|||
); |
|||
} |
|||
}) |
|||
.catch((collected) => {}); // Reaction message filter
|
|||
}) |
|||
.catch((collected, error) => { |
|||
message.reply("\n**ANSWER TIMEOUT**"); |
|||
}); |
|||
}); |
|||
}) |
|||
.catch(console.error); |
|||
} |
|||
} else { |
|||
// Not any of the commands supported
|
|||
message.channel.send( |
|||
"That didn't quite make sense! Please use `do be helping` to see the available commands." |
|||
); |
|||
} |
|||
} |
|||
|
|||
function sendHelpMessage(message) { |
|||
message.channel.send( |
|||
new Discord.MessageEmbed().setTitle("Help").setDescription(helpMessage) |
|||
); |
|||
} |
|||
|
|||
client.on('guildCreate', guild => { |
|||
guild.channels.cache.find(channel => channel.name === 'general' && channel.type === 'text').send("'Sup, I'm the AwesomeSciBo bot!").catch(console.error); |
|||
async function generateRound(message, isDM) { |
|||
fs.writeFile("index.html", "<h1>Here's your round!</h1>", (error) => { |
|||
if (error) { |
|||
console.log(error); |
|||
} |
|||
}); |
|||
let i; |
|||
let generatingMsg = await message.channel.send("Generating..."); |
|||
for (i = 1; i < 26; i++) { |
|||
let tossup_question; |
|||
let question_category; |
|||
let tossup_format; |
|||
let tossup_answer; |
|||
let bonus_question; |
|||
let bonus_format; |
|||
let bonus_answer; |
|||
let htmlContent = ""; |
|||
await fetch("https://scibowldb.com/api/questions/random") |
|||
.then((response) => response.json()) |
|||
.then((data) => { |
|||
tossup_question = data.question.tossup_question; |
|||
tossup_answer = data.question.tossup_answer; |
|||
question_category = data.question.category; |
|||
tossup_format = data.question.tossup_format; |
|||
bonus_question = data.question.bonus_question; |
|||
bonus_answer = data.question.bonus_answer; |
|||
bonus_format = data.question.bonus_format; |
|||
htmlContent = |
|||
`<br><br>${i}. Tossup\n<br><br>` + |
|||
`<strong>${question_category}</strong>` + |
|||
" " + |
|||
`<em>${tossup_format}</em>` + |
|||
" " + |
|||
tossup_question + |
|||
"<br><br>" + |
|||
"<strong>ANSWER:</strong> " + |
|||
tossup_answer + |
|||
"<br><br>"; |
|||
htmlContent += |
|||
"<br><br>Bonus\n<br><br>" + |
|||
`<strong>${question_category}</strong>` + |
|||
" " + |
|||
`<em>${bonus_format}</em>` + |
|||
" " + |
|||
bonus_question + |
|||
"<br><br>" + |
|||
"<strong>ANSWER:</strong> " + |
|||
bonus_answer + |
|||
"<br><br>"; |
|||
htmlContent = htmlContent.replace(/\n/g, "<br>"); |
|||
fs.appendFile("index.html", htmlContent, (error) => { |
|||
if (error) { |
|||
console.log(error); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
if (generatingMsg) { |
|||
generatingMsg.delete({ timeout: 100 }).catch(console.error); |
|||
} |
|||
execSync( |
|||
"curl --request POST --url https://localhost:3136/convert/html --header 'Content-Type: multipart/form-data' --form files=@index.html -o round.pdf", |
|||
{ encoding: "utf-8" } |
|||
); |
|||
if (isDM) { |
|||
client.users.cache |
|||
.get(message.author.id) |
|||
.send( |
|||
new Discord.MessageEmbed() |
|||
.setTitle("Here's your round!") |
|||
.attachFiles("round.pdf") |
|||
) |
|||
.catch(() => |
|||
message.reply( |
|||
"Unable to DM you! Make sure DMs from server members are allowed." |
|||
) |
|||
); |
|||
} else { |
|||
message.channel.send( |
|||
new Discord.MessageEmbed() |
|||
.setTitle("Here's your round!") |
|||
.attachFiles("round.pdf") |
|||
); |
|||
} |
|||
} |
|||
|
|||
async function startScoring(message) { |
|||
let scoreA = 0; |
|||
let scoreB = 0; |
|||
await message.channel |
|||
.send(`Here's the score:\nTeam A: ${scoreA}\nTeam B: ${scoreB}`) |
|||
.then((scoreboard) => { |
|||
const filter = (m) => m.content.includes("do be"); |
|||
const collector = message.channel.createMessageCollector(filter, { |
|||
time: 1500000, |
|||
}); |
|||
collector.on("collect", (m) => { |
|||
if (m.content.toLowerCase() === "do be scoring a 4") { |
|||
// A team gets toss-up
|
|||
m.delete({ timeout: 1000 }).catch(console.error); |
|||
scoreA += 4; |
|||
scoreboard.channel.send( |
|||
`Here's the score:\nTeam A: ${scoreA}\nTeam B: ${scoreB}` |
|||
); |
|||
} else if (m.content.toLowerCase() === "do be scoring a 10") { |
|||
// A team gets bonus
|
|||
m.delete({ timeout: 1000 }).catch(console.error); |
|||
scoreA += 10; |
|||
scoreboard.channel.send( |
|||
`Here's the score:\nTeam A: ${scoreA}\nTeam B: ${scoreB}` |
|||
); |
|||
} else if (m.content.toLowerCase() === "do be scoring b 4") { |
|||
// B team gets toss up
|
|||
m.delete({ timeout: 1000 }).catch(console.error); |
|||
scoreB += 4; |
|||
scoreboard.channel.send( |
|||
`Here's the score:\nTeam A: ${scoreA}\nTeam B: ${scoreB}` |
|||
); |
|||
} else if (m.content.toLowerCase() === "do be scoring b 10") { |
|||
// B team gets bonus
|
|||
m.delete({ timeout: 1000 }).catch(console.error); |
|||
scoreB += 10; |
|||
scoreboard.channel.send( |
|||
`Here's the score:\nTeam A: ${scoreA}\nTeam B: ${scoreB}` |
|||
); |
|||
} else if (m.content === "do be scoring stop") { |
|||
m.delete({ timeout: 1000 }).catch(console.error); |
|||
scoreboard.delete({ timeout: 1000 }); |
|||
m.channel.send( |
|||
`**FINAL SCORE:**\nTeam A: ${scoreA}\nTeam B: ${scoreB}` |
|||
); |
|||
collector.stop(); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
function dontWorryBeHappy(message) { |
|||
message.channel.send( |
|||
new Discord.MessageEmbed() |
|||
.setTitle(`Don't Worry Be Happy!`) |
|||
.setImage("https://media.giphy.com/media/7OKC8ZpTT0PVm/giphy.gif") |
|||
.setURL("https://youtu.be/d-diB65scQU") |
|||
); |
|||
} |
|||
|
|||
client.on('message', async message => { |
|||
function showServerNumber(message) { |
|||
message.channel.send(client.guilds.cache.size); |
|||
} |
|||
|
|||
async function showIssLocation(message) { |
|||
await fetch("http://api.open-notify.org/iss-now.json") |
|||
.then((request) => request.json()) |
|||
.then((data) => { |
|||
message.channel.send( |
|||
new Discord.MessageEmbed() |
|||
.setTitle("The current location of the ISS!") |
|||
.setImage( |
|||
`https://api.mapbox.com/styles/v1/mapbox/light-v10/static/pin-s+000(${data.iss_position.longitude},${data.iss_position.latitude})/-87.0186,20,1/1000x1000?access_token=pk.eyJ1IjoiYWRhd2Vzb21lZ3V5IiwiYSI6ImNrbGpuaWdrYzJ0bGYydXBja2xsNmd2YTcifQ.Ude0UFOf9lFcQ-3BANWY5A` |
|||
) |
|||
.setURL("https://spotthestation.nasa.gov/tracking_map.cfm") |
|||
); |
|||
}); |
|||
} |
|||
|
|||
function showLeaderboard(message) { |
|||
let messageContent = ""; |
|||
userScore |
|||
.find({}) |
|||
.sort({ score: -1 }) // Sort by descending order
|
|||
.exec((err, obj) => { |
|||
if (err) { |
|||
console.log(err); |
|||
return message.reply( |
|||
"Uh oh! :( There was an internal error. Please try again." |
|||
); |
|||
} |
|||
if (obj.length < 10) { |
|||
// Need at least 10 scores for top 10
|
|||
return message.reply( |
|||
`There are only ${obj.length} users, we need at least 10!` |
|||
); |
|||
} |
|||
for (let i = 0; i < 10; i++) { |
|||
messageContent += `${i + 1}: <@${obj[i].authorID}>: ${obj[i].score}\n`; // Loop through each user and add their name and score to leaderboard content
|
|||
} |
|||
message.channel.send( |
|||
new Discord.MessageEmbed() |
|||
.setTitle("Top Ten!") |
|||
.setDescription(messageContent) |
|||
); |
|||
}); |
|||
} |
|||
|
|||
function aboutMessage(message) { |
|||
message.channel.send( |
|||
new Discord.MessageEmbed().setTitle("Contributors: ").setDescription(` |
|||
<@745063586422063214> |
|||
<@650525101048987649> |
|||
`) // Add more contributors here, first one is Abheek, second one is Tejas
|
|||
); |
|||
} |
|||
|
|||
client.on("message", async (message) => { |
|||
if (message.author.bot) { |
|||
return; |
|||
} |
|||
|
|||
const formattedMessage = message.content.toLowerCase().replace(/\s+/g, ''); |
|||
const formattedMessage = message.content.toLowerCase().replace(" ", ""); |
|||
if (formattedMessage.startsWith("dobe")) { |
|||
// Bot prefix is "do be"
|
|||
switch (formattedMessage) { |
|||
case 'dobehits': |
|||
commands.checkHits(message, hits); |
|||
case "dobehelping": // Display help message
|
|||
sendHelpMessage(message); |
|||
break; |
|||
case 'dobehelping': |
|||
commands.sendHelpMessage(message); |
|||
case "doberoundgen": // Generate round publicly
|
|||
generateRound(message, false); |
|||
break; |
|||
case 'doberoundgen': |
|||
commands.generateRound(message, false); |
|||
case "doberoundgendm": // Generate round through DM
|
|||
generateRound(message, true); |
|||
break; |
|||
case 'doberoundgendm': |
|||
commands.generateRound(message, true); |
|||
case "dobescoring": // Start scoring
|
|||
startScoring(message); |
|||
break; |
|||
case 'dobescoring': |
|||
commands.startScoring(message); |
|||
case "dobetop": // Top 10 scores
|
|||
showLeaderboard(message); |
|||
break; |
|||
case 'dobetop': |
|||
commands.showLeaderboard(message); |
|||
case "dobehappy": // Send happy message
|
|||
dontWorryBeHappy(message); |
|||
break; |
|||
case 'dobehappy': |
|||
commands.dontWorryBeHappy(message); |
|||
case "dobeservers": // Shows number of servers bot is in
|
|||
showServerNumber(message); |
|||
break; |
|||
case 'dobeservers': |
|||
commands.showServerNumber(message); |
|||
case "dobeiss": // Show location of ISS
|
|||
showIssLocation(message); |
|||
break; |
|||
case 'dobeiss': |
|||
commands.showIssLocation(message); |
|||
case "dobeabout": // Show about message of bot
|
|||
aboutMessage(message); |
|||
break; |
|||
default: |
|||
commands.otherCommands(message); |
|||
// Do be training
|
|||
otherCommands(message); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
client.login(process.argv[2]).catch(error => console.log(error)); |
|||
client |
|||
.login(process.env.TOKEN) |
|||
.then(() => console.log("Running!")) |
|||
.catch((error) => console.log(error)); |
|||
|
@ -0,0 +1,14 @@ |
|||
const mongoose = require("mongoose"); |
|||
|
|||
const userScoreSchema = new mongoose.Schema({ |
|||
authorID: { |
|||
type: String, |
|||
required: true, |
|||
}, |
|||
score: { |
|||
type: Number, |
|||
required: true, |
|||
}, |
|||
}); |
|||
|
|||
module.exports = mongoose.model("UserScore", userScoreSchema); |
@ -1 +0,0 @@ |
|||
0 |
File diff suppressed because it is too large
@ -0,0 +1,10 @@ |
|||
{ |
|||
"dependencies": { |
|||
"axios": "^0.21.1", |
|||
"dotenv": "^8.2.0", |
|||
"mongoose": "^5.12.4" |
|||
}, |
|||
"devDependencies": { |
|||
"nodemon": "^2.0.7" |
|||
} |
|||
} |
Loading…
Reference in new issue