Code Monkey home page Code Monkey logo

osu-ahr's People

Contributors

dependabot[bot] avatar jramseygreen avatar meowhal avatar metacinnabar avatar xayanide avatar zeropyrozen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

osu-ahr's Issues

Can't start

ERROR:

:/home/container$ npm run start

[email protected] start /home/container
cross-env Node_ENV=development ts-node src/cli/index.ts

starting up...
/home/container/node_modules/config/lib/config.js:182
throw new Error('Configuration property "' + property + '" is not defined');
^
Error: Configuration property "AfkKicker" is not defined
at Config.get (/home/container/node_modules/config/lib/config.js:182:11)
at new AfkKicker (/home/container/src/plugins/AfkKicker.ts:81:26)
at new OahrBase (/home/container/src/cli/OahrBase.ts:55:22)
at new OahrCli (/home/container/src/cli/OahrCli.ts:41:5)
at Object. (/home/container/src/cli/index.ts:44:16)
at Module._compile (internal/modules/cjs/loader.js:1068:30)
at Module.m._compile (/home/container/node_modules/ts-node/src/index.ts:1310:23)
at Module._extensions..js (internal/modules/cjs/loader.js:1097:10)
at Object.require.extensions. [as .ts] (/home/container/node_modules/ts-node/src/index.ts:1313:12)
at Module.load (internal/modules/cjs/loader.js:933:32)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: cross-env Node_ENV=development ts-node src/cli/index.ts
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /home/container/.npm/_logs/2021-10-12T12_13_28_460Z-debug.log

map checking error? the error

[17:04:16.386][ERROR] his_repo - @updateToLatest : Request failed with status code 429

C:\Users\trand\Desktop\osu-ahr-dev\node_modules\axios\lib\core\createError.js:16
var error = new Error(message);
^
Error: Request failed with status code 429
at createError (C:\Users\trand\Desktop\osu-ahr-dev\node_modules\axios\lib\core\createError.js:16:15)
at settle (C:\Users\trand\Desktop\osu-ahr-dev\node_modules\axios\lib\core\settle.js:17:12)
at IncomingMessage.handleStreamEnd (C:\Users\trand\Desktop\osu-ahr-dev\node_modules\axios\lib\adapters\http.js:260:11)
at IncomingMessage.emit (node:events:406:35)
at IncomingMessage.emit (node:domain:470:12)
at endReadableNT (node:internal/streams/readable:1331:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)

How do I make this thing talk less?

I love the project and I am currently using it, but how do I make it not send so many messages?

It's extremely vocal/spammy. I only want it to announce the host, the host order, and display the link to the song being selected... is there a way to only have those sent in chat?

Running with discord integration.

Organize ESLint rules

Group: Refactorization

  • Organize ESLint rules, done by 91a3c3d

Size: Small

For they were inconsistently sorted at the moment. Not sure how is it going to be sorted or categorized yet.

If against, please send a concise statement why.

Deliberate and explicit usage of error properties

Group: Refactorization

For try/catches that only needs it. Shouldn't touch anything that aren't supposed to be changed (e.g tests). Should also check out for error.message that needs an optional chaining operator (.?)

Size: Medium

For end users to show more sorted & informative errors on their issues and also developers to track problems much less difficult than it already is.
This might be unnecessary.
e.g

-       logger.error('Error: ' + e);
+       logger.error(`Error:\n${e.message}\n${e.stack}`);

Will also deliberately use a newline character (\n) after any strings (Error:) for error readability.

[ERROR]: Error: Couldn't eat the fruit, apple was rotten... // long error stack
[ERROR]: Error:
Couldn't eat the fruit, apple was rotten
// long error stack

This area is quite broad, so, the try caught errors that were purposely unhandled, handled etc. will not be touched, in respect to the original intentions.

I will be making a PR for this sometime soon.
If against, please state a concise statement why.

Moving classes into individual files

Requesting an idea.
There are two (or possibly more) classes in a single file that I saw.

LobbyKeeper for example, which also has another class inside it called SlotKeeper, and possibly more on the other different class files.

export class LobbyKeeper extends LobbyPlugin {
option: LobbyKeeperOption;
kickedUsers: Set<Player>;
mpKickedUsers: Set<Player>;
slotKeeper: SlotKeeper;
constructor(lobby: Lobby, option: Partial<LobbyKeeperOption> = {}) {
super(lobby, 'LobbyKeeper', 'keeper');
this.option = getConfig(this.pluginName, option, OPTION_TYPE_HINTS) as LobbyKeeperOption;
this.kickedUsers = new Set();
this.mpKickedUsers = new Set();
this.slotKeeper = new SlotKeeper(this.option.size, this.logger);
this.convertOptions();
this.registerEvents();
}
private registerEvents(): void {
this.lobby.JoinedLobby.on(a => this.onJoined());
this.lobby.HostChanged.on(a => this.onHostChanged(a.player));
this.lobby.MatchFinished.on(() => this.onMatchFinished());
this.lobby.ReceivedChatCommand.on(a => this.onChatCommand(a.player, a.command, a.param));
this.lobby.PlayerJoined.on(a => this.onPlayerJoined(a.slot));
this.lobby.PlayerLeft.on(a => this.onPlayerLeft(a.player, a.slot));
this.lobby.PlayerMoved.on(a => this.onPlayerMoved(a.from, a.to));
this.lobby.ParsedSettings.on(a => this.onParsedSettings(a.result));
this.lobby.historyRepository.kickedUser.on(a => this.onKickedPlayer(a.kickedUser));
this.lobby.historyRepository.finishedGame.on(a => this.onFinishedGame(a.game));
}
convertOptions() {
this.setModeOption(this.option.mode);
this.setSizeOption(this.option.size);
this.setModsOption(this.option.mods);
}
private setModeOption(mode: any) {
if (mode === null || mode === undefined || mode === 'null' || mode === '') {
this.option.mode = null;
return;
}
if (typeof mode === 'string') {
const r = this.tryParseModeParams(mode);
if (r) {
this.option.mode = r;
return;
} else {
throw new Error('Invalid Option. LobbyKeeper.mode : ' + mode);
}
}
if ('team' in mode && 'score' in mode) {
if ((mode.team instanceof TeamMode) && (mode.score instanceof ScoreMode)) {
this.option.mode = mode;
} else {
this.option.mode = {
team: TeamMode.from(mode.team.toString(), true),
score: ScoreMode.from(mode.score.toString(), true)
};
}
return;
}
throw new Error('Invalid Option. LobbyKeeper.mode : ' + mode);
}
private setSizeOption(size: any) {
if (size === null || size === undefined || size === 'null' || size === '') {
size = 0;
}
if (typeof size === 'string') {
size = parseInt(size);
}
if (typeof size !== 'number') {
throw new Error('invalid size ' + size);
}
if (size < 0 || size > 16 || isNaN(size)) {
throw new Error('invalid size ' + size);
}
this.option.size = size;
this.slotKeeper.size = size;
}
private setModsOption(mods: any) {
if (mods === null || mods === undefined || mods === 'null') {
this.option.mods = null;
return;
}
if (typeof mods === 'string') {
this.option.mods = Mod.parseMods(mods);
return;
}
if (Array.isArray(mods)) {
mods = mods.filter(m => m).map(m => Mod.from(m.toString()));
this.option.mods = Mod.removeInvalidCombinations(mods);
return;
}
throw new Error('Invalid Option. LobbyKeeper.mods : ' + mods);
}
private tryParseModeParams(param: string) {
const m1 = /^(.+),(.+)$/.exec(param);
if (m1) {
try {
const team = TeamMode.from(m1[1], true);
const score = ScoreMode.from(m1[2], true);
return { team, score };
} catch { /* continue to parse */ }
}
const m2 = /^(\S+)\s+(\S+)$/.exec(param);
if (m2) {
try {
const team = TeamMode.from(m2[1], true);
const score = ScoreMode.from(m2[2], true);
return { team, score };
} catch { /* continue to parse */ }
}
try {
const team = TeamMode.from(param, true);
return { team, score: this.option.mode?.score ?? ScoreMode.Score };
} catch { /* continue to parse */ }
try {
const score = ScoreMode.from(param, true);
return { team: this.option.mode?.team ?? TeamMode.HeadToHead, score };
} catch {
throw new Error('Invalid Option. LobbyKeeper.mode : ' + param);
}
}
private checkMode(teamMode: TeamMode, scoreMode: ScoreMode) {
if (this.option.mode === null) return false;
if (this.option.mode.score !== scoreMode || this.option.mode.team !== teamMode) {
return true;
} else {
return false;
}
}
private checkMods(mods: Mod[]) {
if (this.option.mods === null) return false;
const s = new Set(this.option.mods);
for (const m of mods) {
if (!s.delete(m)) {
return true;
}
}
return s.size !== 0;
}
private checkTitle(title: string | undefined) {
if (!title && (this.lobby.historyRepository.hasError || this.option.title !== this.lobby.lobbyName)) {
return true;
} else if (title !== this.option.title) {
return true;
}
return false;
}
private fixLobbyModeAndSize(): void {
if (this.option.mode !== null) {
if (this.option.size) {
this.lobby.SendMessage(`!mp set ${this.option.mode.team.value} ${this.option.mode.score.value} ${this.option.size}`);
} else {
this.lobby.SendMessage(`!mp set ${this.option.mode.team.value} ${this.option.mode.score.value}`);
}
} else {
if (this.option.size) {
this.lobby.SendMessage(`!mp size ${this.option.size}`);
}
}
this.slotKeeper.resetTimestamp();
}
private fixPassword(): void {
if (this.option.password !== null) {
this.lobby.SendMessage(`!mp password ${this.option.password}`);
}
}
private fixTitle(): void {
if (this.option.title === null) return;
//Set title length to max 50
this.option.title = this.option.title.substring(0, 50);
this.lobby.SendMessage(`!mp name ${this.option.title}`);
}
private fixMods(): void {
if (this.option.mods !== null) {
this.lobby.SendMessage(`!mp mods ${this.option.mods.map(m => m.value).join(' ')}`);
}
}
private onJoined(): void {
this.fixTitle();
this.fixLobbyModeAndSize();
this.fixMods();
this.fixPassword();
}
private onParsedSettings(result: MpSettingsResult) {
try {
const team = TeamMode.from(result.teamMode);
const score = ScoreMode.from(result.winCondition);
if (this.checkMode(team, score) || this.slotKeeper.checkMpSettings(result)) {
this.fixLobbyModeAndSize();
}
const mods = Mod.parseMods(result.activeMods);
if (this.checkMods(mods)) {
this.fixMods();
}
if (this.checkTitle(result.name)) {
this.fixTitle();
}
} catch (e: any) {
this.logger.error('@LobbyKeeper#onParsedSettings ' + e?.message);
}
}
private onPlayerJoined(toSlot: number) {
if (this.slotKeeper.checkJoin(toSlot)) {
this.fixLobbyModeAndSize();
}
}
private onPlayerLeft(player: Player, slot: number) {
if (this.slotKeeper.checkLeave(slot)) {
this.fixLobbyModeAndSize();
}
}
private onPlayerMoved(fromSlot: number, toSlot: number) {
if (this.slotKeeper.checkMove(fromSlot, toSlot)) {
this.fixLobbyModeAndSize();
}
}
private onKickedPlayer(u: User): void {
if (!this.option.hostkick_tolerance || !this.lobby.host) return;
const p = this.lobby.GetPlayer(u.username);
if (!p || this.mpKickedUsers.has(p)) return;
this.kickedUsers.add(p);
this.logger.debug(`added ${p.name} to kickedusers, count: ${this.kickedUsers.size}`);
if (this.option.hostkick_tolerance <= this.kickedUsers.size || p.isReferee || p.isAuthorized) {
this.kickedUsers.clear();
this.mpKickedUsers.clear();
this.logger.debug(`kick counter activated : ${this.lobby.host.name}`);
this.lobby.SendMessage(`!mp kick ${this.lobby.host.name}`);
}
}
private onHostChanged(host: Player): void {
this.kickedUsers.clear();
this.mpKickedUsers.clear();
}
private onChatCommand(player: Player, command: string, param: string): void {
if (player.isAuthorized) {
if (command.startsWith('*keep') || command.startsWith('*no')) {
const msg = this.processCommand(command, param);
if (msg) {
this.lobby.SendMessage(msg);
this.logger.info(msg);
}
}
}
}
private onMatchFinished(): void {
this.fixPassword();
if (this.slotKeeper.checkUnused()) {
this.fixLobbyModeAndSize();
}
if (this.option.title !== null || this.option.mode !== null || this.option.mods !== null) {
this.lobby.LoadMpSettingsAsync().catch((e: any) => {
this.logger.error('lobbyKeeper#onMatchFinished failed to LoadMpSettingsAsync');
});
}
}
private onFinishedGame(game: Game): any {
this.logger.trace(`hev finished game -> mode:${game.mode}, mode_int:${game.mode_int}, score:${game.scoring_type}, team:${game.team_type}, mods:${game.mods}`);
}
private processCommand(command: string, param: string): string | null {
if (command === '*keep') {
const matchMode = /^mode(\s+(.+))?\s*$/.exec(param);
if (matchMode) {
if (matchMode[2] === undefined) {
this.logger.warn('missing parameters. *keep mode [0-3] [0-3] e.g. *keep mode 0 0');
return null;
}
try {
this.setModeOption(matchMode[2]);
if (this.option.mode) {
this.fixLobbyModeAndSize();
return `Keep lobby mode ${this.option.mode.team.name}, ${this.option.mode.score.name}`;
} else {
return 'Disabled keeping lobby mode';
}
} catch (e: any) {
this.logger.warn(e?.message ?? 'failed to parse mode params');
return null;
}
}
const matchSize = /^size(\s+(\d+))?\s*$/.exec(param);
if (matchSize) {
if (matchSize[2] === undefined) {
this.logger.warn('missing parameter. *keep size [1-16]');
return null;
}
try {
this.setSizeOption(matchSize[2]);
if (this.option.size) {
this.fixLobbyModeAndSize();
return `Keep lobby size ${this.option.size}`;
} else {
return 'Disabled keeping lobby size.';
}
} catch (e: any) {
this.logger.warn(e?.message ?? 'failed to parse size params');
return null;
}
}
const matchMods = /^mods?(\s+(.+))?\s*$/.exec(param);
if (matchMods) {
if (matchMods[2] === undefined) {
this.logger.warn('missing parameters. *keep mods [mod] ([mod]) ([mod]) ...');
return null;
}
try {
this.setModsOption(matchMods[2]);
if (this.option.mods) {
this.fixMods();
return `Keep mods ${this.option.mods === null || this.option.mods.length === 0 ? 'None' : this.option.mods.map(m => m.name).join(', ')}`;
} else {
return 'Disabled keeping lobby mods.';
}
} catch (e: any) {
this.logger.warn(e?.message ?? 'failed to parse mods');
return null;
}
}
const matchPassword = /^password(\s+(.+))?\s*$/.exec(param);
if (matchPassword) {
this.option.password = matchPassword[2] !== undefined ? matchPassword[2] : '';
this.fixPassword();
return `Keep lobby password ${this.option.password !== '' ? this.option.password : '[empty]'}`;
}
const matchTitle = /^(title|name)(\s+(.+))?\s*$/.exec(param);
if (matchTitle) {
this.option.title = matchTitle[3] !== undefined ? matchTitle[3] : '';
this.fixTitle();
return `Keep lobby title ${this.option.title !== '' ? this.option.title : '[empty]'}`;
}
this.logger.warn('missing parameters. *keep <mode|size|mods|password|title> ...params');
}
if (command === '*no') {
if (param === 'keep mode' && this.option.mode !== null) {
this.setModeOption(null);
return 'disabled keeping teammode and scoremode';
}
if (param === 'keep size' && this.option.size !== 0) {
this.setSizeOption(0);
return 'disabled keeping lobby size';
}
if ((param === 'keep mod' || param === 'keep mods') && this.option.mods !== null) {
this.setModsOption(null);
return 'disabled keeping mods';
}
if (param === 'keep password' && this.option.password !== null) {
if (this.option.password !== '') {
this.option.password = '';
this.fixPassword();
}
this.option.password = null;
return 'disabled keeping lobby password';
}
if ((param === 'keep title' || param === 'keep name') && this.option.title !== null) {
this.option.title = null;
return 'disabled keeping room title';
}
if (param.startsWith('keep')) {
this.logger.warn('missing parameters. *no keep <mode|size|mods|password|title>');
}
}
return null;
}
getDescription(): string {
const keeps = [];
if (this.option.mode) {
keeps.push(`mode: ${this.option.mode.team.name}, ${this.option.mode.score.name}`);
}
if (this.option.size !== 0) {
keeps.push(`size: ${this.option.size}`);
}
if (this.option.password) {
keeps.push(`password: ${this.option.password !== '' ? this.option.password : '(empty)'}`);
}
if (this.option.mods) {
keeps.push(`mods: ${this.option.mods.map(m => m.value).join(' ')}`);
}
if (this.option.title) {
keeps.push(`title: ${this.option.title}`);
}
return keeps.join(', ');
}
GetPluginStatus(): string {
return `-- Lobby Keeper --
${this.getDescription()}`;
}
}

export class SlotKeeper {
size: number;
slots: { timestamp: number, hasPlayer: boolean }[];
logger?: Logger;
/**
* ใ‚นใƒญใƒƒใƒˆใŒใƒญใƒƒใ‚ฏใ•ใ‚Œใฆใ„ใ‚‹ใจใฟใชใ™ใพใงใฎใƒŸใƒช็ง’ๆ™‚้–“
*/
timeToConsiderAsLockedSlotMS = 10 * 60 * 1000;
constructor(size: number = 16, logger?: Logger) {
this.size = size;
this.slots = new Array(16).fill(null).map(_ => ({ timestamp: Date.now(), hasPlayer: false }));
this.logger = logger;
}
checkJoin(slot: number) {
let result = false;
const idx = slot - 1;
if (this.size === 0) {
// do nothing
} else if (this.size <= idx) {
result = true;
// Slots larger than the specified size are open
this.logger?.trace(`Detected slot expansion. actual size:${slot}, specified size:${this.size}`);
} else { // ไธ€ๅบฆใซ่ค‡ๆ•ฐใฎใ‚คใƒ™ใƒณใƒˆใ‚’็™บ็”Ÿใ•ใ›ใชใ„ใŸใ‚ใซ elseๅฅใ‚’ไฝฟใ†
for (let i = 0; i < idx; i++) {
// the player didn't enter the slot that shold be empty
if (!this.slots[i].hasPlayer) {
result = true;
this.logger?.trace(`Detected locked slot ${i + 1}`);
break;
}
}
}
this.slots[idx].hasPlayer = true;
this.slots[idx].timestamp = Date.now();
return result;
}
checkLeave(slot: number) {
const idx = slot - 1;
this.slots[idx].hasPlayer = false;
this.slots[idx].timestamp = Date.now();
return false;
}
checkMove(fromSlot: number, toSlot: number) {
let result = false;
const fromIdx = fromSlot - 1;
this.slots[fromIdx].hasPlayer = false;
this.slots[fromIdx].timestamp = Date.now();
const toIdx = toSlot - 1;
if (this.size !== 0 && this.size <= toIdx) {
// Slots larger than the specified size are open
result = true;
this.logger?.trace(`Detected slot expansion. actual size:${toSlot}, specified size:${this.size}`);
}
this.slots[toIdx].hasPlayer = true;
this.slots[toIdx].timestamp = Date.now();
return result;
}
checkUnused() {
if (this.size === 0) return false;
const now = Date.now();
let estematedSize = -1;
let lockedSlot = -1;
for (let i = 0; i < this.size; i++) {
if (!this.slots[i].hasPlayer) {
const durationEmpty = now - this.slots[i].timestamp;
if (lockedSlot === -1 && this.timeToConsiderAsLockedSlotMS <= durationEmpty) {
lockedSlot = i + 1;
continue;
}
}
estematedSize = i + 1;
}
if (lockedSlot !== -1) {
this.logger?.trace(`Detected locked slot ${lockedSlot}`);
return true;
} else {
return false;
}
}
resetTimestamp() {
const now = Date.now();
for (let i = 0; i < 16; i++) {
this.slots[i].timestamp = now;
}
}
checkMpSettings(setting: MpSettingsResult) {
for (let idx = 0; idx < 16; idx++) {
this.slots[idx].hasPlayer = false;
}
let result = false;
for (const ps of setting.players) {
const idx = ps.slot - 1;
this.slots[idx].hasPlayer = true;
this.slots[idx].timestamp = Date.now();
if (this.size !== 0 && this.size < ps.slot) {
result = true;
}
}
return result;
}
}

While there is no problem with this at all, like classes that are small that doesn't need to be in a new file, this is still refactorable, I think one class per file, moving all classes into their own files makes it look neat, organized, less difficult to find(to potential future readers), and could probably help a bit on debugging as well.

This doesn't have to be done right away.

Got absurdly skipped in an Auto Host Rotation lobby

Describe the bug

Update: Fixed by commit fb7c410

I got skipped in an auto host rotation lobby once, kind of rare.

The bot has a flaw when a host leaves right before the game starts.

I was not the one using the bot though.

Affected version
1.5.7

Steps to reproduce
How it happened:

Let's call the guy as Player B, the host that left right before the game starts with their pick.
Let's call the guy who gets host after player B. Player A. Player A gets the host after Player B left.


  1. Player B left as his pick is about to start.

  2. Player A receives host as the game starts with Player B's pick.

  3. After the game, the bot switches the host to the next player before Player A gets to pick a map.


Expected behavior
Player A should've been able to pick a map after playing Player B's pick when Player B left while his pick started.

Actual behavior
The bot probably thought Player B's pick was Player A's pick when Player B left as the game started, so, after the game with Player B's pick, it switched the host to the next player after Player A.

[enhancement] Colaboratory ?

Hello ! First of all, I want to thanks you @Meowhal for this magnifique BOT (it's a french word, you know...).
Yesterday I was asking me if this bot can run on Google Colaboratory ?

So I did it ! And it can ! Here the link , I added the same step as the readme.

At stage 2 when installing libraries, warnings appears and I thinks it's because the version of Node.js is to old to work with the discord package:
image
But if I update to lastet version of node it say that the version is not compatible so I didn't change the version.

To finish, I asking if this could have any interest to add it to project ?

Bye.

DiscordAPIError: Bots cannot use this endpoint

When the Bot starts it crashes immediately with the following error message:

C:\Users\Administrator\Documents\OSU! Bot V2\osu-ahr\node_modules\discord.js\src\rest\RequestHandler.js:350
      throw new DiscordAPIError(data, res.status, request);
            ^

DiscordAPIError: Bots cannot use this endpoint
    at RequestHandler.execute (C:\Users\Administrator\Documents\OSU! Bot V2\osu-ahr\node_modules\discord.js\src\rest\RequestHandler.js:350:13)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async RequestHandler.push (C:\Users\Administrator\Documents\OSU! Bot V2\osu-ahr\node_modules\discord.js\src\rest\RequestHandler.js:51:14)
    at async ApplicationCommandPermissionsManager.set (C:\Users\Administrator\Documents\OSU! Bot V2\osu-ahr\node_modules\discord.js\src\managers\ApplicationCommandPermissionsManager.js:168:20) {
  method: 'put',
  path: '/applications/942876754987597884/guilds/841375885500350464/commands/972782584100696084/permissions',
  code: 20001,
  httpStatus: 403,
  requestData: {
    json: {
      permissions: [ { id: '952590967888814091', permission: true, type: 1 } ]
    },
    files: []
  }
}

2022-05-08 10_56_03-Administrator_ C__Windows_System32_cmd exe

Can someone help me with this?

title - heh - idk -

disclaimer : I'm not natural english speaker, so please do not trustfully copy paste anything
also these are just suggestions because I have a lot of ideas, but I've been running 1.3.7 for weeks now (trying 1.4.1 now) and it is already good enough for me.
oh, and sorry, I'm very bad at making anything easy to read xD

1- texts

legend :

currently
targeted
translation proposition
commentary

Translation is not that bad. Mainly lacks informations
for example :

  • (on website and typing !info on #multiplayer in osu! client)
    " osu-ahr
    irc bot for osu! multi lobby auto host rotation.
    The order of hosts is managed by queue. Added last when a player joins the lobby."

The host rotation is managed by a list. Player is queued at the bottom when joining lobby or when his map pick was played.

Gives better intel on how bot works

!queue | Shows host queue.
!skip | Votes to skip current host.
!start | Votes to start the match.
!abort | Votes to abort the match. Use when the match is stuck.

!update | Updates current selected map to the latest version. Use when has host old map.
!regulation | check current regulation.

!skip | Triggers vote to skip current host / Votes to skip current host.
same for abort and start
Use when host pick an old map

for the third command table for owner : add s at the end of verbs ( to keep coherence with the two other tables )

             legend does not apply anymore after this
  • in osu! client
    uwuwiwiwwiuw
    this is default output when typing !info on #multiplayer
    most ppl don't understand that the link sends you to command list, so people tend to not open it/ open it but don't scroll down to readme part, I suggest modifying default message to make it more clear

  • appartรฉ and "bugs"
    I can't find patchnotes, and there are issues I found where on 1.3.7. and I have no idea if they are fixed now as I started using 1.4.1. recently
    also I have connexion issues, knowing this might help you figure how these happened.

"ghost" player can be in queue (1.3.7)

occured while I was asleep, and as I was modifying queue order and someone left at about the same time.
but basically a player is not removed from rotations, and list will be up to 17 players long. On ghost player's turn, will give host to the person after him and freezes the rotation as bot fails to detect the map was picked by current host and will remain on ghost player's turn
sorry I failed to reproduce this on purpose and didn't manage to find it in logs (just discovered they exist) so I don't have a deep understanding of it and how it occurs

not changing map exploit

I think If I just don't change map from previous host, I can just start the map over and over again and keep the host forever.
This killed several lobbies

other occurence of host apparently failing to rotate

3- host passation and idle mode

Two ahr users can swap who is using the bot but you have to manually shared the host rotation, and one have to manually terminate his bot. doing otherwise can lead to bot spamming #multiplayer and may break rotations.
A good improvement would be for an ahr bot entering a lobby to be able to check via irc if there is another bot running in the lobby, and if there is, turn it off after taking the queue list.

this lead to the next point that would be an amazing upgrade aswell :
what if I don't want the rotations to be managed by the bot, but I still want to be able to restrict difficulty range and make use of !update !abort !skip commands ?
I probably could find a way to do that but I havn't found the information so far, so making some explanations on this accessible could be useful

4- General communication

this bot is not doing good at making himself known.
Very young people play osu! aswell as people with not fully wired brain ( like myself ) and ahr bot is doing a very bad job at explaining its features and why it is a good thing for multiplayer.
Some options could be added ( and even some of them could probably be default settings )

- !abort !skip !start

when stuck in a game, !abort command is almost never excecuted. Even if I decide to show the example and do !abort instead of !mp abort, nobody follows the vote. same goes for the two mentionned commands and !start [secs] for host command

This could be improved by sending a message when the vote starts,

so for example with the !abort command instead of :
dude1 : !abort
ahr-bot : abort progress : 1/4
dude2 : wtf is going on ? I'm stuck

we could have :
dude1 : !abort
ahr-bot : stuck ? dude1 has started a vote to abort the game. vote progress : 1/4
dude2 : firetruck what's going on
dude3 : !abort
dude2 : oh
dude2 : !abort

- ahr-bot !start countdown isn't as good as bancho's countdown

so why doesn't this command just trigger the ahr-bot-user to say in #multiplayer : !mp start

- ahr-bot and ahr-bot-user are pretty much indifferenciable, ahr-bot doesn't give tips :

so here's my ideas : why doesn't bot say that it's a bot talking whenever these are not commands ?
for example :
owuwiwiwwiuw
could be
!mp map 987987
[bot] picked map : star=987 lenght=987
[bot] Violation of regulation blabla
[bot] please pick another map

oh and about this, map picker should get skipped after failing a certain amount of time to pick a map that is in range.

untitled feature idea that is probably not worth spending the time

in many games, on loading screen, you have actual tips about the game that help the huge majority of newcomers to understand elementary mechanic
similar feature here would be for the bot to give tips such as :
" [bot] stuck in a game ? try the command "!abort" to start a vote to abort the game ! "
" [bot] want to run auto host rotations bot on your own lobbies ? check <url's youtube video> for EASY setup "
" [bot] I have been running for "
" [bot] Meowhal needs your help ! please send any translation error you may find at "
" [bot] Can't download a map ? try the "!update" command ! "
these could be plugged with a long cooldown when host do !start countdown for example.

that's about it ! not that I have any others things to say, but I spent a lot of time doing this already :D

oh, one last thing

Thanks for amazing tool that enhance significantly my multiplayer experience :3

Do not include logging environment keys inside logs file

Description
I don't think it is a good idea to include logging the credentials inside the .logs file.

Because it can be easily be seen in ./logs/channels folder then ahr.log file.
We can still log them in the console, but just not inside a file, everything else should remain as is, only the credentials should not be inside a file.

[2022-06-03T12:21:24.547] [INFO] pre - Starting up...
[2022-06-03T12:21:24.554] [INFO] cfg - Loaded environment key: ahr_irc_nick=NicknameHere
[2022-06-03T12:21:24.554] [INFO] cfg - Loaded environment key: ahr_irc_password=IRCPasswordHere
[2022-06-03T12:21:24.687] [INFO] cfg - Loaded environment key: ahr_Discord_token=MySuperVulnerableDiscordBotToken
[2022-06-03T12:21:35.070] [INFO] discord_bot - The discord bot is ready.
[2022-06-03T12:21:35.071] [INFO] discord_bot - Invite link => https://discord.com/api/oauth2/authorize?client_id=970171005362397274&scope=bot+applications.commands&permissions=268443664

Regulation always violated

I'm trying to make an ahr room but every map picked always cause regulation violation, causing unable to pick any map

image
image

Map repeat detection

Detect if a map is picked twice in the same hour or so (configurable ?) and forbid it

Suggestions for improvement

A couple of suggestions for improvement for the bot to function better in multiplayer lobbies.

  • Password change tolerance
  • DT/NC stopper
  • Map rotate

1st. Password change tolerance
If someone spam changes the password to try and get the bot spamming !mp password, silencing the bot and player, the bot should have a tolerance to check who is the host when changing passwords and ban the changer of passwords.

2nd. DT/NC/HR stopper
When a map is picked, and the star rating after adding DT/NC or HR* (*in nomod mode) is higher than the set regulation, the mods will be disabled and reset back to freemod or nomod. If the player constantly tries to redo the mods back into DT/NC or HR, the bot will do number 3.

3rd. Map rotating
If a player continually picks maps higher than the current regulation star maximum/minimum or length maximum/minimum, the host will auto-rotate the host to the next person in the queue.

Error: connect ETIMEDOUT

Error: connect ETIMEDOUT 192.241.219.151:6667 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1146:16). Port Issue?

Markdown formatting can be parsed with transferred logs in-game in a discord text channel

Description

  1. Run discord CLI
  2. Create or enter a lobby
  3. Go the #matches text channel
  4. Transfer in-game chat to another text channel
    Suppose we have a player that joined the lobby named _User_

When they chat on IRC, it will be shown something like this as well as in the console logs
_User_: Message here.

However, in discord, it will be shown like this
User: Message here.

User is italicized because a text wrapped with "_" is a form of markdown formatting being parsed by Discord.

There should some sort of way to prevent things like that from happening.

I think this applies to any messages, bold, italics, and underlines.

Discord Integration

Description

Integration with a Discord channel through a bot, and allow commands to be run through the bot the execute for the lobby.

Features

Chat bridge between Discord and in-game osu! chat.
Executing commands through Discord such as starting the game, acting like a console. (with only specified user ids to be able to run these commands)

Dont work?

I did everything according to the instructions.
image

Do not include logging credentials inside logs file

Description
I don't think it is a good idea to include logging the credentials inside the .logs file.

Because it can be easily be seen in ./logs/channels folder then ahr.log file.

[2022-06-03T12:21:24.547] [INFO] pre - Starting up...
[2022-06-03T12:21:24.554] [INFO] cfg - Loaded environment key: ahr_irc_nick=NicknameHere
[2022-06-03T12:21:24.554] [INFO] cfg - Loaded environment key: ahr_irc_password=IRCPasswordHere
[2022-06-03T12:21:24.687] [INFO] cfg - Loaded environment key: ahr_Discord_token=MySuperVulnerableDiscordBotToken
[2022-06-03T12:21:35.070] [INFO] discord_bot - The discord bot is ready.
[2022-06-03T12:21:35.071] [INFO] discord_bot - Invite link => https://discord.com/api/oauth2/authorize?client_id=970171005362397274&scope=bot+applications.commands&permissions=268443664

Permanent locked lobbies

I accidentally used the arrow keys to rename the lobby as I was typing the lobby name in the command prompt and forgot that the terminal takes all of those keyboard presses as input for the name, so the lobby with this name attempted to be created:
image
this resulted in that error popping up and the lobby being created with who knows what password and I can't seem to close the lobby as the command "close" wasn't working D:
(UPDATE) the lobby did close itself eventually after 30 minutes or so.

Add Task to Remove All Expired Cached Player and Beatmap Info

Description

Add automated or manual task to remove all expired cached player and beatmap info to reduce RAM usage. We can add this task to Discord and console command and after it finish running, it gives the log message. I kinda run out of memory wwwww~~~
๐Ÿ˜„

mirror site link can become a single function

I want to choose a map that has removed from the official website, but It can still be downloaded from the mirror site(many mania maps have such problem).
When I disable mapchecker, the map can be chosen, but can't show the mirror site link, so i think it can become a single function. Only links not include map information.

Issue with bot spam

So in the following link, I highlight that the bot has an issue when it can't decide whether there is a host change in the lobby, I looked through the older logs and found nothing like this back then. But I'm sure no owner of the bot would like to get punished for using the bot, however, I do not think there is a solution if the bot owner or an authorised user is in the lobby. Perhaps there could be a fix added in?
https://user-images.githubusercontent.com/79675527/111874165-da14da00-89ce-11eb-917d-1256efc03be7.mp4

Bot don't give a host

I must say right away that itโ€™s possible that something I am doing wrong or did not understand is wrong and the bot is working.
It's my problem i can make a room commands is working but if i join bot dont give me a host i'm trying with another player ,i see massage on YouTube bot is working if people more that one this dosen't help also trying commands like !abort and !skip.
https://imgur.com/U33oKge

Map Checker Issue?

Hi.
First, I really appreciate for your contribution. and I can't use English very well, sorry for poor English.

I think mapChecker doesn't work properly.
and I also enabled regulation (command: regulation enable)

[WARN] mapChecker - couldn't find map id:<map id>, title:<map title>
This is what console said, so server don't(can't) regulate beatmap's difficulty, length.

I think I missed something. But I can't really find solution.
Is there any method to solve this issue?

about "announcement" function

Hi, Meowhal.
I always appreciate your contribution, and I'm really happy that your work getting famous.

There's a Lobby section in local.json(default.json)
It looks like it can send message to lobby periodically, but I never saw it worked in any AHR lobby.

Does Lobby plugin have announcement function or not?
If it doesn't, Announcement function would be great to manage lobby.
I know there are many tasks you should add/fix first, so take it easy on this.

Always take care of COVID, Thank you.

Regulation settings aren't enforced

Apologies if this issue exists already in the TODO section, it's not translated to English.

Regulation settings are able to be set, but not currently enforced.

How to recreate:
1.Create a lobby with any name
2.Set regulations, for example regulation star_min 3 star_max=5 len_max=300
3.Enter the lobby and pick a map outside of the 3.00 to 5.00 range, or have another player in the lobby pick a map outside of that range.
4. Notice that the map is able to be selected and played without any enforcement of the regulation setting.

Single logger instance to each type of logger

Resolved by #99

Requesting an idea.
Have only one instance of each type of logger.
e.g cli, discord, irc, PMLogger, etc
instead of importing log4js and configuring the path everytime they are going to be used.

Doesn't have to be done right away. Can also take a different but similar approach from what I will show below.

It might look something like this:

Before:

// file where cli logger is used
import log4js from 'log4js';
const logger = log4js.getLogger('cli');
log4js.configure('./config/log_cli_prod.json');

logger.info('Hello world.');

After:

// file where cli logger is used
import { cliLogger } from '../cliLogger';

cliLogger.info('Hello world.');

Less lines used just by using log4js's logger, and straightforward than it already is.

// cliLogger.ts
import log4js from 'log4js';

// directly putting the configuration
log4js.configure({
// the whole config here
});

// or by using the json config path
// log4js.configure(configPathHere);

export const cliLogger = log4js.getLogger('cli');

// -------------------------------
// another way of implementation
import log4js from 'log4js';
export const cliLogger = log4js.configure({configHere}).getLogger('cli');
// ------------------------------

In the case for discord appender, I think it is possible to do it something like this

// discordLogger.ts
import log4js from 'log4js';
import path from 'path';

// example only, this is not the whole config, might not work as intended
log4js.configure({
  appenders: {
    discord: {
      type: `${path.join(__dirname, 'discord', 'DiscordAppender')}`,
      layout: {
        type: 'pattern',
        pattern: '%m',
      },
    },
  },
  categories: {
    discord: {
      appenders: [
        'console_f',
        'file_app',
      ],
      level: 'all',
    },
  },
});

export const discordLogger = log4js.getLogger('discord');

// -------------------------------
// another way of implementation
import log4js from 'log4js';
import path from 'path';
export const discordLogger = log4js.configure({configHere}).getLogger('discord');
// ------------------------------
// file where discord logger is used
import { discordLogger } from '../discordLogger';

discordLogger.info('Hello world.');

Discord Bot // PM2

Hello,

is it possible to start the discord bot with PM2? How do i do that?

Full and complete usage of template literals

Group: Refactorization

  • #86
  • #91 Full and complete usage of template literals.

Size: Medium

For us to take more advantage of ES6's template literals, string interpolation instead of using string concatenation, I thought that they're less difficult to read compared to string concat, I saw both of these were used inconsistently or maybe that was preferred?
Will involve 2 ESLint rules for refactoring purposes.
template-curly-spacing
prefer-template

 template-curly-spacing:
   - "error"
 prefer-template:
   - "error"

Before:

try {
 fruit = apple;
 eat(fruit)
} catch (err: any) {
  console.log('Argh! This fruit called %s is rotten! Error:\n%s\n%s', fruit, err.message, err.stack);
}

After:

try {
 fruit = apple;
 eat(fruit)
} catch (err: any) {
  console.log(`Argh! This fruit called ${fruit} is rotten! Error:\n${err.message}\n${err.stack}`);
}

and 1 extra rule just in-case.
no-useless-concat

 no-useless-concat:
   - "error"

return 'a' + 'b' + 'c';

// equivalent
        return 'abc';

I will be making a PR for this sometime soon.
If against, please state a concise statement why.

Name not found error

Not sure what is causing this but when the lobby is created, I join and then I leave, it says error no host found and the bot doesn't give host to anybody after they join:

image

I closed the lobby, tried to replicate it and happened again. I joined, left (so lobby was empty) and then the ERROR {} pops up.

image

This bot is awesome thanks for your hard work :D

Version can be shown as undefined in-game

Resolved by #69

Describe the bug
Affects cli and discord cli, everytime a user runs osu-ahr directly with node (e.g node dist/cli/index.js) instead of the npm script (e.g npm start or npm start:discord), the version that will be shown in-game is undefined.

I see in code that there was a nullish coalescing operator that would show it as 0.0.0, but it on other conditions it was shown as undefined which was unintended.

I think the reason why it is shown as undefined is because the version shown depends on the process' npm script being run at.

A quick solution would be to depend the version shown using the package.json file, but I think that is unsafe and not good overall to risk it unless it is acceptable in this case.

Affected version
1.5.17

Steps to reproduce
If haven't compiled yet run.

npm run clean
npm run build
  1. Run osu-ahr from compiled dist directly instead of the npm script.

Cli

node dist/cli/index.js

Discord Cli

node dist/discord/index.js
  1. Make or enter a lobby

  2. Use !version or !info command in-game.

Expected behavior
Show the correct version or 0.0.0 if unknown.

Actual behavior
Shown as undefined to the end user.

Ability to enforce osu!mania key count

Ignoring the ability to choose autoconvert (because mostly osu!mania players want to play maps specific to that mode), is there any way to enforce key count? For example, consider this situation:

Room: Force players to choose 4K
Host: Chooses 5K map
osu-ahr: Checks the map's keycount, compare with the rule. If not same: reverts choice to last played map, warn user for choosing wrong keycount

This can be done just like how MapChecker do, if violated for certain amount, host will be skipped.

Improve texts & grammaticalization to all texts on strings, and error messages

Group: Refactorization

  • #88
  • Improve texts & grammaticalization to all texts on strings, and error messages.
    • cli #93
    • discord #94
    • dummies No changes needed.
    • libs No changes needed.
    • parsers No changes needed.
    • plugins
      • AfkKicker #97
      • AutoHostSelector #102
      • AutoStartTimer #103
      • CacheCleaner #104
      • HistoryLoader #105
      • HostSkipper #106
      • InOutLogger #107
      • LobbyKeeper #108
      • LobbyPlugin No changes needed.
      • LobbyTerminator #109
      • MapChecker #110
      • MapRecaster No changes needed.
      • MatchAborter #111
      • MatchStarter #113
      • MiscLoader #115
      • ProfileFecher #114
      • VoteCounter No changes needed.
      • WordCounter #116
    • tests No changes needed.
    • trials No changes needed.
    • web No changes needed.
    • webapi #117
    • src
      • IIrcClient #118
      • Lobby #119
      • Loggers No changes needed.
      • Modes No changes needed.
      • Player Doubt.
      • TypedConfig #120

File by file or folder by folder pr

Should not touch anything that aren't supposed to be changed
(e.g purposely unhandled errors, tests, etc.)

Size: Large

For to improve end user and debugging experience.
e.g
Using and taking advantage of parenthetical plurals

// before
   this.logger.info(`match finished (${count} players)`);
// potential output
// 1 is singular denoting one player, but 'players' is plural as seen here
// matches can finish with only 1 player
[11:10:20.359][INFO] lobby - match finished (1 players)

// after
   this.logger.info(`match finished (${count} player(s))`);
// potential output
// use parenthetical plurals for nouns that are probable to be singular or plural for that category
[11:10:20.359][INFO] lobby - match finished (1 player(s))

Explicitly showing the class and the method used on error messages that needs it

-       this.logger.error('@addTaskQueueIfNeeded' + e);
+       this.logger.error(`@ProfileFecher#addTaskQueueIfNeeded\n${e.message}\n${e.stack}`);

I will be making a PR for this sometime soon.
If against, please state a concise statement why.

Discord integration not working

Tried to run start:discord but it's not working with Unhandled Promise Rejection Warning

(node:10948) UnhandledPromiseRejectionWarning: ReferenceError: AbortController is not defined
at RequestHandler.execute (C:\Users\test\Desktop\osu-ahr-master\node_modules\discord.js\src\rest\RequestHandler.js:172:15)
at RequestHandler.execute (C:\Users\test\Desktop\osu-ahr-master\node_modules\discord.js\src\rest\RequestHandler.js:176:19)
at RequestHandler.push (C:\Users\test\Desktop\osu-ahr-master\node_modules\discord.js\src\rest\RequestHandler.js:50:25)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at WebSocketManager.connect (C:\Users\test\Desktop\osu-ahr-master\node_modules\discord.js\src\client\websocket\WebSocketManager.js:128:9)
at Client.login (C:\Users\test\Desktop\osu-ahr-master\node_modules\discord.js\src\client\Client.js:245:7)
at DiscordBot.start (C:\Users\test\Desktop\osu-ahr-master\src\discord\DiscordBot.ts:87:5)
(Use node --trace-warnings ... to show where the warning was created)
(node:10948) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:10948) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Any fixes?

npm run start:discord not working.

I updated the ahr osu dev and it still didnt work. I am also using the dev branch and it still doesnt work. It keeps saying
Loading log4js configuration from ./config/log_discord.json
[15:35:08.042][INFO] pre - Starting up...
[15:35:08.876][ERROR] discord_bot - @DiscordBot#start
Privileged intent provided is not enabled or whitelisted.
Error [DISALLOWED_INTENTS]: Privileged intent provided is not enabled or whitelisted.
at WebSocketManager.createShards (C:\Users\paral\Documents\osu-ahr-dev\osu-ahr-dev\node_modules\discord.js\src\client\websocket\WebSocketManager.js:250:15)
at async Client.login (C:\Users\paral\Documents\osu-ahr-dev\osu-ahr-dev\node_modules\discord.js\src\client\Client.js:254:7)
at async DiscordBot.start (C:\Users\paral\Documents\osu-ahr-dev\osu-ahr-dev\dist\discord\DiscordBot.js:63:13)
image

Add a kick checker to the bot

Even with all these measures that have been implemented, people can still force close the lobby by simply kicking everyone fast enough so that the referees/owner can not do anything about it or the people in the room are not able to !skip fast enough. I recommend for the bot to have the ability to check how many people the current host has kicked and if they pass a certain number, the host will get kicked by the bot and open up the room to the specific size again. I'm not sure how hard this is to do but I hope this is something that can be implemented.

Add new features

Kick host if trying to open/close slots
Prefix to bot messages. For example: [Bot]

After the discord bot connects, disconnects with DiscordAPIError: Bots cannot use this endpoint

Fixed by #81

Describe the bug
Discord bot logins then abruptly disconnects after a few seconds.

I think something is going on around here, trace it from there I guess.
Or there was a change in Discord that made this occur.
It seems like Discord just rolled out some changes.

More info:
https://discord.com/blog/slash-commands-permissions-discord-apps-bots
discord/discord-api-docs#4830
https://github.com/oceanroleplay/discord.ts/issues/661

for (let g of cl.guilds.cache.values()) {
await this.registerCommandsAndRoles(g);
}

c.permissions.add({ permissions });

dist

for (let g of cl.guilds.cache.values()) {
await this.registerCommandsAndRoles(g);
}

c.permissions.add({ permissions });

Tried with all 3 intents in the Developer portal.

  • Presence Intent
  • Server Members Intent
  • Message Content Intent

Prior to this issue, I did not encounter a problem even with all these 3 intents disabled.

Affected version
1.5.17

Steps to reproduce
If haven't compiled yet run.

npm run clean
npm run build
  1. Run osu-ahr discord cli from compiled dist
node dist/discord/index.js

Expected behavior
Cleanly continue operating.

Actual behavior
Logins then disconnects after a few seconds.

Event type:
unhandledRejection

PS C:\Users\Ava\Documents\GitHub\osu-ahr> node dist/discord/index.js
starting up...
[02:35:22.117][INFO] discord - discord bot is ready.
[02:35:22.124][INFO] discord - invite link => https://discord.com/api/oauth2/authorize?client_id=869623432512413795&scope=bot+applications.commands&permissions=268443664
C:\Users\Ava\Documents\GitHub\osu-ahr\node_modules\discord.js\src\rest\RequestHandler.js:350   
      throw new DiscordAPIError(data, res.status, request);
            ^

DiscordAPIError: Bots cannot use this endpoint
    at RequestHandler.execute (C:\Users\Ava\Documents\GitHub\osu-ahr\node_modules\discord.js\src\rest\RequestHandler.js:350:13)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async RequestHandler.push (C:\Users\Ava\Documents\GitHub\osu-ahr\node_modules\discord.js\src\rest\RequestHandler.js:51:14)
    at async ApplicationCommandPermissionsManager.set (C:\Users\Ava\Documents\GitHub\osu-ahr\node_modules\discord.js\src\managers\ApplicationCommandPermissionsManager.js:168:20) {
  method: 'put',
  path: '/applications/869623432512413795/guilds/785457308054061085/commands/917607543033970809/permissions',
  code: 20001,
  httpStatus: 403,
  requestData: {
    json: {
      permissions: [ { id: '917607544921395211', permission: true, type: 1 } ]
    },
    files: []
  }
}

Node.js v17.9.0
PS C:\Users\Ava\Documents\GitHub\osu-ahr> 

Bot crash after startup

The bot crashes after startup instantly, no logs recorded or anything, tried to boot from new download with clean configs aswell, it just refuses to boot.
image
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.