generated from nhcarrigan/template
feat: migrate from github
This commit is contained in:
commit
9def06d149
3
.eslintrc.json
Normal file
3
.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "@nhcarrigan"
|
||||||
|
}
|
8
.gitattributes
vendored
Normal file
8
.gitattributes
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text eol=LF
|
||||||
|
*.ts text
|
||||||
|
*.spec.ts text
|
||||||
|
|
||||||
|
# Ignore binary files >:(
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/node_modules/
|
||||||
|
/prod/
|
||||||
|
.env
|
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
"@nhcarrigan/prettier-config"
|
3
CODE_OF_CONDUCT.md
Normal file
3
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Code of Conduct
|
||||||
|
|
||||||
|
Our Code of Conduct can be found here: https://docs.nhcarrigan.com/#/coc
|
3
CONTRIBUTING.md
Normal file
3
CONTRIBUTING.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
Our contributing guidelines can be found here: https://docs.nhcarrigan.com/#/contributing
|
5
LICENSE.md
Normal file
5
LICENSE.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# License
|
||||||
|
|
||||||
|
This software is licensed under our [global software license](https://docs.nhcarrigan.com/#/license).
|
||||||
|
|
||||||
|
Copyright held by Naomi Carrigan.
|
3
PRIVACY.md
Normal file
3
PRIVACY.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Privacy Policy
|
||||||
|
|
||||||
|
Our privacy policy can be found here: https://docs.nhcarrigan.com/#/discord-boost-monitor/privacy
|
31
README.md
Normal file
31
README.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Discord Boost Monitor
|
||||||
|
|
||||||
|
A bot to remove special roles from people when they stop boosting a server.
|
||||||
|
|
||||||
|
## Live Version
|
||||||
|
|
||||||
|
This page is not yet deployed. Want to [run it yourself?](https://docs.nhcarrigan.com/#/discord-boost-monitor/usage)
|
||||||
|
|
||||||
|
<!--This page is currently deployed. [View the live website.]()-->
|
||||||
|
|
||||||
|
## Feedback and Bugs
|
||||||
|
|
||||||
|
If you have feedback or a bug report, please feel free to open a GitHub issue!
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
If you would like to contribute to the project, you may create a Pull Request containing your proposed changes and we will review it as soon as we are able! Please review our [contributing guidelines](CONTRIBUTING.md) first.
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
Before interacting with our community, please read our [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This software is licensed under our [global software license](https://docs.nhcarrigan.com/#/license).
|
||||||
|
|
||||||
|
Copyright held by Naomi Carrigan.
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via `contact@nhcarrigan.com`.
|
3
SECURITY.md
Normal file
3
SECURITY.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
Our security policy can be found here: https://docs.nhcarrigan.com/#/security
|
3
TERMS.md
Normal file
3
TERMS.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Terms of Service
|
||||||
|
|
||||||
|
Our Terms of Service can be found here: https://docs.nhcarrigan.com/#/terms
|
48
package.json
Normal file
48
package.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "oogie-woogie-boostie-woostie",
|
||||||
|
"version": "1.0.1",
|
||||||
|
"description": "A bot to remove booster perks when someone stops boosting.",
|
||||||
|
"main": "prod/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"prebuild": "rm -rf ./prod",
|
||||||
|
"build": "tsc",
|
||||||
|
"lint": "eslint src --max-warnings 0",
|
||||||
|
"start": "node -r dotenv/config prod/index.js",
|
||||||
|
"test": "echo 'No tests yet!'"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "20",
|
||||||
|
"pnpm": "8"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/nhcarrigan/oogie-woogie-boostie-woostie.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"discord",
|
||||||
|
"bot",
|
||||||
|
"typescript",
|
||||||
|
"discord.js"
|
||||||
|
],
|
||||||
|
"author": "Naomi Carrigan",
|
||||||
|
"license": "SEE LICENSE IN https://docs.nhcarrigan.com/#/license",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/nhcarrigan/oogie-woogie-boostie-woostie/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/nhcarrigan/oogie-woogie-boostie-woostie#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"@nhcarrigan/eslint-config": "1.1.3",
|
||||||
|
"@nhcarrigan/prettier-config": "1.0.1",
|
||||||
|
"@nhcarrigan/typescript-config": "1.0.1",
|
||||||
|
"eslint": "8.57.0",
|
||||||
|
"prettier": "2.8.8",
|
||||||
|
"typescript": "5.4.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/integrations": "7.114.0",
|
||||||
|
"@sentry/node": "7.114.0",
|
||||||
|
"discord.js": "14.15.2",
|
||||||
|
"dotenv": "16.4.5",
|
||||||
|
"winston": "3.13.0"
|
||||||
|
}
|
||||||
|
}
|
2156
pnpm-lock.yaml
generated
Normal file
2156
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
3
sample.env
Normal file
3
sample.env
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
DISCORD_TOKEN=""
|
||||||
|
SENTRY_DSN=""
|
||||||
|
DEBUG_HOOK=""
|
12
src/config/roles.ts
Normal file
12
src/config/roles.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export const colourRoles = [
|
||||||
|
"883281643098484736",
|
||||||
|
"883283836887261204",
|
||||||
|
"883282544655753268",
|
||||||
|
"883283096378683402",
|
||||||
|
"883283115265642547",
|
||||||
|
"883283650794360852",
|
||||||
|
"883284117343584298",
|
||||||
|
"883284381937065984",
|
||||||
|
];
|
||||||
|
|
||||||
|
export const boosterRole = "712431541531181177";
|
49
src/index.ts
Normal file
49
src/index.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { RewriteFrames } from "@sentry/integrations";
|
||||||
|
import * as Sentry from "@sentry/node";
|
||||||
|
import { Client, GatewayIntentBits, WebhookClient } from "discord.js";
|
||||||
|
|
||||||
|
import { manageRoles } from "./modules/manageRoles";
|
||||||
|
import { errorHandler } from "./utils/errorHandler";
|
||||||
|
|
||||||
|
Sentry.init({
|
||||||
|
dsn: process.env.SENTRY_DSN,
|
||||||
|
tracesSampleRate: 1.0,
|
||||||
|
integrations: [
|
||||||
|
new RewriteFrames({
|
||||||
|
root: global.__dirname,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const bot = new Client({
|
||||||
|
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.on("ready", async () => {
|
||||||
|
const hook = new WebhookClient({ url: process.env.DEBUG_HOOK as string });
|
||||||
|
await hook.send({
|
||||||
|
content: "Oogie boogie boostie woostie online!",
|
||||||
|
username: "Boost Monitor",
|
||||||
|
avatarURL: "https://cdn.nhcarrigan.com/avatars/nhcarrigan.png",
|
||||||
|
});
|
||||||
|
|
||||||
|
const guild = bot.guilds.cache.map((el) => el)[0];
|
||||||
|
const members = await guild.members.fetch();
|
||||||
|
await hook.send({
|
||||||
|
content: `Loaded ${members.size} members from ${guild.name}`,
|
||||||
|
username: "Boost Monitor",
|
||||||
|
avatarURL: "https://cdn.nhcarrigan.com/avatars/nhcarrigan.png",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.on("guildMemberUpdate", async (_oldMember, newMember) => {
|
||||||
|
await manageRoles(newMember);
|
||||||
|
});
|
||||||
|
|
||||||
|
await bot.login(process.env.DISCORD_TOKEN);
|
||||||
|
} catch (err) {
|
||||||
|
await errorHandler("index", err);
|
||||||
|
}
|
||||||
|
})();
|
26
src/modules/manageRoles.ts
Normal file
26
src/modules/manageRoles.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { GuildMember } from "discord.js";
|
||||||
|
|
||||||
|
import { boosterRole, colourRoles } from "../config/roles";
|
||||||
|
import { errorHandler } from "../utils/errorHandler";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches to the memberUpdate event, and checks if the member is no longer boosting
|
||||||
|
* the server (based on the premiumSubscriberRole). If true, then removes any of the configured
|
||||||
|
* booster-only roles.
|
||||||
|
*
|
||||||
|
* @param {GuildMember} newMember The state of the member after the update.
|
||||||
|
*/
|
||||||
|
export const manageRoles = async (newMember: GuildMember): Promise<void> => {
|
||||||
|
try {
|
||||||
|
if (!newMember.roles.cache.has(boosterRole)) {
|
||||||
|
for (const roleId of colourRoles) {
|
||||||
|
const target = newMember.roles.cache.find((role) => role.id === roleId);
|
||||||
|
if (target) {
|
||||||
|
await newMember.roles.remove(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
await errorHandler("manageRoles", err);
|
||||||
|
}
|
||||||
|
};
|
39
src/utils/errorHandler.ts
Normal file
39
src/utils/errorHandler.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import * as Sentry from "@sentry/node";
|
||||||
|
import { EmbedBuilder, WebhookClient } from "discord.js";
|
||||||
|
|
||||||
|
import { logHandler } from "./logHandler";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard error handling module to pipe errors to Sentry and
|
||||||
|
* format the error for logging.
|
||||||
|
*
|
||||||
|
* @param {string} context A description of where the error occurred.
|
||||||
|
* @param {Error} err The error object.
|
||||||
|
*/
|
||||||
|
export const errorHandler = async (context: string, err: unknown) => {
|
||||||
|
const error = err as Error;
|
||||||
|
logHandler.log("error", `There was an error in the ${context}:`);
|
||||||
|
logHandler.log(
|
||||||
|
"error",
|
||||||
|
JSON.stringify({ errorMessage: error.message, errorStack: error.stack })
|
||||||
|
);
|
||||||
|
Sentry.captureException(error);
|
||||||
|
|
||||||
|
const hook = new WebhookClient({ url: process.env.DEBUG_HOOK as string });
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder();
|
||||||
|
embed.setTitle(`There was an error in the ${context}`);
|
||||||
|
embed.setDescription(error.message.slice(0, 2000));
|
||||||
|
embed.addFields([
|
||||||
|
{
|
||||||
|
name: "Stack",
|
||||||
|
value: `\`\`\`${error.stack?.slice(0, 1000) || "no stack"}\`\`\``,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await hook.send({
|
||||||
|
embeds: [embed],
|
||||||
|
username: "Boost Monitor",
|
||||||
|
avatarURL: "https://cdn.nhcarrigan.com/avatars/nhcarrigan.png",
|
||||||
|
});
|
||||||
|
};
|
24
src/utils/logHandler.ts
Normal file
24
src/utils/logHandler.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { createLogger, format, transports, config } from "winston";
|
||||||
|
|
||||||
|
const { combine, timestamp, colorize, printf } = format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard log handler, using winston to wrap and format
|
||||||
|
* messages. Call with `logHandler.log(level, message)`.
|
||||||
|
*
|
||||||
|
* @param {string} level - The log level to use.
|
||||||
|
* @param {string} message - The message to log.
|
||||||
|
*/
|
||||||
|
export const logHandler = createLogger({
|
||||||
|
levels: config.npm.levels,
|
||||||
|
level: "silly",
|
||||||
|
transports: [new transports.Console()],
|
||||||
|
format: combine(
|
||||||
|
timestamp({
|
||||||
|
format: "YYYY-MM-DD HH:mm:ss",
|
||||||
|
}),
|
||||||
|
colorize(),
|
||||||
|
printf((info) => `${info.level}: ${[info.timestamp]}: ${info.message}`)
|
||||||
|
),
|
||||||
|
exitOnError: false,
|
||||||
|
});
|
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "@nhcarrigan/typescript-config",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./prod"
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user