initial commit
This commit is contained in:
13
src/bot.ts
Normal file
13
src/bot.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Events } from "tmi.js";
|
||||
import { client } from "./core/client.ts";
|
||||
import { events } from "./events/collection.ts";
|
||||
|
||||
// Register all events with the TMI client
|
||||
for (const [eventName, eventHandler] of events) {
|
||||
client.on(
|
||||
eventName as keyof Events,
|
||||
eventHandler.triggered.bind(null, client),
|
||||
);
|
||||
}
|
||||
|
||||
client.connect();
|
||||
7
src/commands/collection.ts
Normal file
7
src/commands/collection.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Collection } from "@discordjs/collection";
|
||||
import { SongCommand } from "./command-song.ts";
|
||||
import type { ICommand } from "./interface.ts";
|
||||
|
||||
export const commands = new Collection<string, ICommand>();
|
||||
|
||||
commands.set(SongCommand.name, new SongCommand());
|
||||
23
src/commands/command-song.ts
Normal file
23
src/commands/command-song.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { ChatUserstate, Client } from "tmi.js";
|
||||
import type { ICommand, ICommandRequirements } from "./interface.ts";
|
||||
|
||||
export class SongCommand implements ICommand {
|
||||
name = "song";
|
||||
cooldown = 0;
|
||||
enabled = true;
|
||||
|
||||
requirements: ICommandRequirements = {
|
||||
developer: true,
|
||||
mod: false,
|
||||
};
|
||||
|
||||
triggered = async (
|
||||
client: Client,
|
||||
channel: string,
|
||||
_state: ChatUserstate,
|
||||
_message: string,
|
||||
_args: Array<string>,
|
||||
) => {
|
||||
client.say(channel, "testing");
|
||||
};
|
||||
}
|
||||
12
src/commands/interface.ts
Normal file
12
src/commands/interface.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export interface ICommand {
|
||||
name: string;
|
||||
cooldown: number;
|
||||
enabled: boolean;
|
||||
triggered(...args: unknown[]): Promise<unknown>;
|
||||
requirements: ICommandRequirements;
|
||||
}
|
||||
|
||||
export interface ICommandRequirements {
|
||||
developer: boolean;
|
||||
mod: boolean;
|
||||
}
|
||||
11
src/config/config.ts
Normal file
11
src/config/config.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export const Config = {
|
||||
prefix: process.env.PREFIX || "",
|
||||
username: process.env.USERNAME || "",
|
||||
access_token: process.env.ACCESS_TOKEN || "",
|
||||
channels: [process.env.CHANNELS || ""],
|
||||
developers: [process.env.DEVELOPERS || ""],
|
||||
};
|
||||
11
src/core/client.ts
Normal file
11
src/core/client.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Client } from "tmi.js";
|
||||
import { Config } from "../config/config.ts";
|
||||
|
||||
export const client = new Client({
|
||||
connection: { reconnect: true, secure: true },
|
||||
identity: {
|
||||
username: Config.username,
|
||||
password: `oauth:${Config.access_token}`,
|
||||
},
|
||||
channels: Config.channels,
|
||||
});
|
||||
9
src/events/collection.ts
Normal file
9
src/events/collection.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Collection } from "@discordjs/collection";
|
||||
import ConnectedEvent from "./event-connected.ts";
|
||||
import MessageEvent from "./event-message.ts";
|
||||
import type { IEvent } from "./interface.ts";
|
||||
|
||||
export const events = new Collection<string, IEvent>();
|
||||
|
||||
events.set(ConnectedEvent.name, new ConnectedEvent());
|
||||
events.set(MessageEvent.name, new MessageEvent());
|
||||
11
src/events/event-connected.ts
Normal file
11
src/events/event-connected.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { Events } from "tmi.js";
|
||||
import { logSuccess } from "../logger/logger.ts";
|
||||
import type { IEvent } from "./interface.ts";
|
||||
|
||||
export default class ConnectedEvent implements IEvent {
|
||||
name: keyof Events = "connected";
|
||||
|
||||
triggered = async () => {
|
||||
logSuccess("connected");
|
||||
};
|
||||
}
|
||||
78
src/events/event-message.ts
Normal file
78
src/events/event-message.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Collection } from "@discordjs/collection";
|
||||
import type { ChatUserstate, Client, Events } from "tmi.js";
|
||||
import { commands } from "../commands/collection.ts";
|
||||
import type { ICommand } from "../commands/interface.ts";
|
||||
import { Config } from "../config/config.ts";
|
||||
import type { IEvent } from "./interface.ts";
|
||||
|
||||
const Cooldowns = new Collection<string, number>();
|
||||
|
||||
export default class MessageEvent implements IEvent {
|
||||
name: keyof Events = "message";
|
||||
|
||||
triggered = async (
|
||||
client: Client,
|
||||
channel: string,
|
||||
state: ChatUserstate,
|
||||
message: string,
|
||||
self: boolean,
|
||||
) => {
|
||||
if (self) return;
|
||||
await checkMessage(client, channel, state, message);
|
||||
};
|
||||
}
|
||||
|
||||
async function checkMessage(
|
||||
client: Client,
|
||||
channel: string,
|
||||
state: ChatUserstate,
|
||||
message: string,
|
||||
) {
|
||||
const prefix = Config.prefix;
|
||||
|
||||
if (!message.startsWith(prefix)) return;
|
||||
|
||||
const args = message.slice(prefix.length).trim().split(/ +/g);
|
||||
const commandName = args[0].toLowerCase();
|
||||
const command = commands.get(commandName);
|
||||
if (!command) return;
|
||||
if (!command.enabled) return;
|
||||
|
||||
const userId = state["user-id"] as string;
|
||||
if (command.requirements.developer && !isDeveloper(userId)) return;
|
||||
if (command.requirements.mod && !isMod(state)) return;
|
||||
|
||||
const timeLeft = checkCooldown(command);
|
||||
if (timeLeft > 0) {
|
||||
return client.say(
|
||||
channel,
|
||||
`you must wait ${timeLeft} more seconds to use the command again`,
|
||||
);
|
||||
}
|
||||
|
||||
await command.triggered(client, channel, state, message, args);
|
||||
}
|
||||
|
||||
function isDeveloper(userId: string): boolean {
|
||||
return Config.developers.includes(userId);
|
||||
}
|
||||
|
||||
function isMod(state: ChatUserstate): boolean {
|
||||
return state.mod as boolean;
|
||||
}
|
||||
|
||||
function checkCooldown(command: ICommand): number {
|
||||
const now = Date.now();
|
||||
if (command.cooldown > 0) {
|
||||
const cooldownTime = Cooldowns.get(command.name);
|
||||
if (cooldownTime) {
|
||||
if (cooldownTime < now) {
|
||||
const timeLeft = 0; // TODO
|
||||
return timeLeft;
|
||||
} else {
|
||||
Cooldowns.set(command.name, now + command.cooldown * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
6
src/events/interface.ts
Normal file
6
src/events/interface.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { Events } from "tmi.js";
|
||||
|
||||
export interface IEvent {
|
||||
name: keyof Events;
|
||||
triggered(...args: unknown[]): Promise<unknown>;
|
||||
}
|
||||
13
src/logger/logger.ts
Normal file
13
src/logger/logger.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import chalk from "chalk";
|
||||
|
||||
export function logError(...args: unknown[]) {
|
||||
console.log(chalk.red(args));
|
||||
}
|
||||
|
||||
export function logSuccess(...args: unknown[]) {
|
||||
console.log(chalk.green(args));
|
||||
}
|
||||
|
||||
export function logInfo(...args: unknown[]) {
|
||||
console.log(chalk.cyan(args));
|
||||
}
|
||||
Reference in New Issue
Block a user