Skip to content

Commit

Permalink
Merge pull request #1 from cancng/master
Browse files Browse the repository at this point in the history
discord bot
  • Loading branch information
halilkocaoz authored Mar 14, 2022
2 parents 637688c + b950da5 commit 5320aab
Show file tree
Hide file tree
Showing 23 changed files with 1,759 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
.env
dist

# Visual Studio 6 build log
*.plg
Expand Down
1 change: 1 addition & 0 deletions Cu.Yemekhane.Bot.Discord/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
16 changes: 16 additions & 0 deletions Cu.Yemekhane.Bot.Discord/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module.exports = {
env: {
es2021: true,
node: true,
},
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["@typescript-eslint"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
},
};
3 changes: 3 additions & 0 deletions Cu.Yemekhane.Bot.Discord/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
# Cu.Yemekhane.Bot.Discord

## Invite URL

34 changes: 34 additions & 0 deletions Cu.Yemekhane.Bot.Discord/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "cu-yemekhane-bot-discord",
"version": "1.0.0",
"main": "dist/index.js",
"license": "MIT",
"scripts": {
"dev": "ts-node-dev --tree-kill --no-notify --respawn --exit-child src/index.ts",
"start": "node dist/index",
"clean": "rm -rf dist",
"build": "npm -s run clean && tsc"
},
"dependencies": {
"@discordjs/builders": "^0.12.0",
"@discordjs/rest": "^0.3.0",
"app-root-path": "^3.0.0",
"axios": "^0.26.1",
"dayjs": "^1.10.8",
"discord-api-types": "^0.29.0",
"discord.js": "^13.6.0",
"dotenv": "^16.0.0",
"glob": "^7.2.0"
},
"devDependencies": {
"@types/app-root-path": "^1.2.4",
"@types/glob": "^7.2.0",
"@types/node": "^17.0.21",
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
"eslint": "^8.11.0",
"ts-node": "^10.7.0",
"ts-node-dev": "^1.1.8",
"typescript": "^4.6.2"
}
}
5 changes: 5 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import axios from "axios";

export const api = axios.create({
baseURL: "https://cu-yemekhane.herokuapp.com",
});
52 changes: 52 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/commands/menu/date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { EmbedFieldData, MessageEmbed } from "discord.js";
import { api } from "../../api";
import { Command } from "../../structure/Command";
import { IMenuApiResponse } from "../../types/menu";

export default new Command({
name: "menu",
description: "Shows specific date's menu",
options: [
{
name: "target_date",
description: "DD.MM.YYYY specific date format",
required: true,
type: "STRING",
},
],
run: async ({ interaction, client, args }) => {
const specifiedDate = args.data[0].value;

if (!specifiedDate) {
return interaction.followUp(
"Please give valid date. Example Format: 23.03.2022"
);
}

try {
const res = await api.get<IMenuApiResponse>(`/Menu/${specifiedDate}`);

const mappedFoods: EmbedFieldData[] = res.data.data.foods.map((food) => ({
name: "Food",
value: `${food.name} - ${food.calories.toString()} kalori`,
}));

const exampleEmbed = new MessageEmbed()
.setTitle(`Menu ${res.data.data.date}`)
.setURL("https://yemekhane.cu.edu.tr")
.setThumbnail(
client.user?.avatarURL() ||
"https://www.cu.edu.tr/storage/anamenu_icerikleri/May2018/cukurova_logo.png"
)
.addFields(...mappedFoods)
.setTimestamp();

return interaction.followUp({
embeds: [exampleEmbed],
});
} catch (error) {
console.log("err :", error.message);
return interaction.followUp("Menu not found");
}
},
});
34 changes: 34 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/commands/menu/today.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { EmbedFieldData, MessageEmbed } from "discord.js";
import dayjs from "dayjs";
import { api } from "../../api";
import { Command } from "../../structure/Command";
import { IMenuApiResponse } from "../../types/menu";

export default new Command({
name: "menu_today",
description: "Shows today's menu",
run: async ({ interaction, client }) => {
const res = await api.get<IMenuApiResponse>(
`/Menu/${dayjs().format("DD.MM.YYYY")}`
);

const mappedFoods: EmbedFieldData[] = res.data.data.foods.map((food) => ({
name: "Food",
value: `${food.name} - ${food.calories.toString()} kalori`,
}));

const exampleEmbed = new MessageEmbed()
.setTitle(`Menu ${res.data.data.date}`)
.setURL("https://yemekhane.cu.edu.tr")
.setThumbnail(
client.user?.avatarURL() ||
"https://www.cu.edu.tr/storage/anamenu_icerikleri/May2018/cukurova_logo.png"
)
.addFields(...mappedFoods)
.setTimestamp();

await interaction.followUp({
embeds: [exampleEmbed],
});
},
});
34 changes: 34 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/commands/menu/tomorrow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { EmbedFieldData, MessageEmbed } from "discord.js";
import dayjs from "dayjs";
import { api } from "../../api";
import { Command } from "../../structure/Command";
import { IMenuApiResponse } from "../../types/menu";

export default new Command({
name: "menu_tomorrow",
description: "Shows tomorrow's menu",
run: async ({ interaction, client }) => {
const res = await api.get<IMenuApiResponse>(
`/Menu/${dayjs().add(1, "days").format("DD.MM.YYYY")}`
);

const mappedFoods: EmbedFieldData[] = res.data.data.foods.map((food) => ({
name: "Food",
value: `${food.name} - ${food.calories.toString()} kalori`,
}));

const exampleEmbed = new MessageEmbed()
.setTitle(`Menu ${res.data.data.date}`)
.setURL("https://yemekhane.cu.edu.tr")
.setThumbnail(
client.user?.avatarURL() ||
"https://www.cu.edu.tr/storage/anamenu_icerikleri/May2018/cukurova_logo.png"
)
.addFields(...mappedFoods)
.setTimestamp();

await interaction.followUp({
embeds: [exampleEmbed],
});
},
});
20 changes: 20 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/events/interactionCreate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { CommandInteractionOptionResolver } from "discord.js";
import { client } from "..";
import { ExtendedInteraction } from "../types/Command";
import { Event } from "../structure/Event";

export default new Event("interactionCreate", async (interaction) => {
// Chat Input Commands
if (interaction.isCommand()) {
await interaction.deferReply();
const command = client.commands.get(interaction.commandName);
if (!command)
return interaction.followUp("You have used a non existent command");

return command.run({
args: interaction.options as CommandInteractionOptionResolver,
client,
interaction: interaction as ExtendedInteraction,
});
}
});
5 changes: 5 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/events/ready.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Event } from "../structure/Event";

export default new Event("ready", () => {
console.log("Bot is online");
});
6 changes: 6 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import "dotenv/config";
import DiscordClient from "./structure/DiscordClient";

export const client = new DiscordClient();

client.start();
7 changes: 7 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/structure/Command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { CommandType } from "../types/Command";

export class Command {
constructor(commandOptions: CommandType) {
Object.assign(this, commandOptions);
}
}
79 changes: 79 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/structure/DiscordClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
ApplicationCommandDataResolvable,
Client,
ClientEvents,
Collection,
Intents,
} from "discord.js";
import glob from "glob";
import { promisify } from "util";
import { RegisterCommandsOptions } from "../types/client";
import { CommandType } from "../types/Command";
import { Event } from "./Event";

const globPromise = promisify(glob);

class DiscordClient extends Client {
commands: Collection<string, any> = new Collection();

constructor() {
if (!process.env.DISCORD_BOT_TOKEN) {
throw new Error("DISCORD_BOT_TOKEN must be defined");
}
super({ intents: [Intents.FLAGS.GUILDS] });
}

start() {
this.login(process.env.DISCORD_BOT_TOKEN);
this.registerModules();
}

async importFile(filePath: string) {
return (await import(filePath))?.default;
}

async registerCommands({ commands, guildId }: RegisterCommandsOptions) {
if (guildId) {
this.guilds.cache.get(guildId)?.commands.set(commands);
console.log(`Registering commands to ${guildId}`);
} else {
this.application?.commands.set(commands);
console.log("Registering global commands");
}
}

async registerModules() {
const slashCommands: ApplicationCommandDataResolvable[] = [];
const commandFiles = await globPromise(
`${__dirname}/../commands/*/*{.ts,.js}`
);
commandFiles.forEach(async (filePath) => {
const command: CommandType = await this.importFile(filePath);
if (!command.name) return;
console.log(command);

this.commands.set(command.name, command);
slashCommands.push(command);
});

this.on("ready", () => {
console.log("Bot is ready");
});

this.on("guildCreate", (guild) => {
this.registerCommands({
commands: slashCommands,
guildId: guild.id,
});
});

// Event
const eventFiles = await globPromise(`${__dirname}/../events/*{.ts,.js}`);
eventFiles.forEach(async (filePath) => {
const event: Event<keyof ClientEvents> = await this.importFile(filePath);
this.on(event.event, event.run);
});
}
}

export default DiscordClient;
8 changes: 8 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/structure/Event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ClientEvents } from "discord.js";

export class Event<Key extends keyof ClientEvents> {
constructor(
public event: Key,
public run: (...args: ClientEvents[Key]) => any
) {}
}
25 changes: 25 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/types/Command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
ChatInputApplicationCommandData,
CommandInteraction,
CommandInteractionOptionResolver,
GuildMember,
PermissionResolvable,
} from "discord.js";
import DiscordClient from "../structure/DiscordClient";

export interface ExtendedInteraction extends CommandInteraction {
member: GuildMember;
}

interface RunOptions {
client: DiscordClient;
interaction: ExtendedInteraction;
args: CommandInteractionOptionResolver;
}

type RunFunction = (options: RunOptions) => any;

export type CommandType = {
userPermissions?: PermissionResolvable[];
run: RunFunction;
} & ChatInputApplicationCommandData;
6 changes: 6 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/types/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ApplicationCommandDataResolvable } from "discord.js";

export interface RegisterCommandsOptions {
guildId?: string;
commands: ApplicationCommandDataResolvable[];
}
6 changes: 6 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/types/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare namespace NodeJS {
export interface ProcessEnv extends NodeJS.ProcessEnv {
NODE_ENV?: "development" | "production";
DISCORD_BOT_TOKEN: string;
}
}
4 changes: 4 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/types/food.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IFood {
name: string;
calories: number;
}
20 changes: 20 additions & 0 deletions Cu.Yemekhane.Bot.Discord/src/types/menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { IFood } from "./food";

export interface IMenu {
date: string;
foods: IFood[];
totalCalories: number;
detail: string;
}

export interface IMenuApiResponse {
success: boolean;
errorMessage: string | null;
data: IMenu;
}

export interface IMenuListApiResponse {
success: boolean;
errorMessage: string | null;
data: IMenu[];
}
Loading

0 comments on commit 5320aab

Please sign in to comment.