Compare commits
58 Commits
03654c56e2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a1f02ebd0 | |||
| f6bceb215c | |||
| 9914cd2bfb | |||
| fd2d5f5bff | |||
| 762bb989a2 | |||
| 4031291c38 | |||
| fca6927b08 | |||
| 56ec6a1ad8 | |||
| c1f0ae670d | |||
| 034d1c076c | |||
| 0d757fef79 | |||
| ee73dc140a | |||
| f77e292e27 | |||
| e10bbe4fb4 | |||
| 94cc914926 | |||
| 3b782c02fe | |||
| 307027846c | |||
| 75a2a9b94c | |||
| eabe37c280 | |||
| f6bbe10bb9 | |||
| 76525b73c9 | |||
| 3852ff922e | |||
| ddb85f10d8 | |||
| 3f7864be1b | |||
| 6193865220 | |||
| 50d8d9347c | |||
| 0341a70746 | |||
| 6f0a66d51d | |||
| 36353aa8a7 | |||
| 49338d1e5c | |||
| 44e6308cc2 | |||
| 2113755464 | |||
| 646227fab1 | |||
| 907c730172 | |||
| 0e0317ad6b | |||
| 1d64e38fd0 | |||
| b6d71eaac0 | |||
| 2022590fb8 | |||
| 6ca809b175 | |||
| f928b75678 | |||
| d902cd1042 | |||
| 6707a95648 | |||
| 02b67c6661 | |||
|
|
2f6182b588 | ||
| db6cdfcbb8 | |||
| b8365abce8 | |||
| 62b3eb070b | |||
| 78a65511c2 | |||
| 94d2fc40e6 | |||
| 07aa025ba0 | |||
| edacc747b3 | |||
| 2592aea260 | |||
| b46dc8d3ae | |||
| e7cd840c48 | |||
| 8935e141d2 | |||
|
|
182ce5af6b | ||
|
|
90341b277c | ||
|
|
03d36358bf |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
prod.sqlite
|
||||
mydb.sqlite
|
||||
48
.gitea/workflows/deploy.yml
Normal file
48
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
name: release-tag
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
release-image:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
# env:
|
||||
# DOCKER_ORG: teacup
|
||||
# DOCKER_LATEST: nightly
|
||||
# RUNNER_TOOL_CACHE: /toolcache
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker BuildX
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: git.unom.io # replace it with your local IP
|
||||
username: ${{ vars.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Get Meta
|
||||
id: meta
|
||||
run: |
|
||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
||||
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: |
|
||||
linux/amd64
|
||||
linux/arm64
|
||||
push: true
|
||||
tags: | # replace it with your local IP and tags
|
||||
git.unom.io/mo/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
|
||||
git.unom.io/mo/${{ steps.meta.outputs.REPO_NAME }}:latest
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -173,3 +173,7 @@ dist
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
|
||||
# DB
|
||||
avocadis_diary.sqlite
|
||||
prod.sqlite
|
||||
|
||||
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
||||
# use the official Bun image
|
||||
# see all versions at https://hub.docker.com/r/oven/bun/tags
|
||||
FROM oven/bun:1 AS base
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# install dependencies into temp directory
|
||||
# this will cache them and speed up future builds
|
||||
FROM base AS install
|
||||
# install with --production (exclude devDependencies)
|
||||
RUN mkdir -p /temp/prod
|
||||
COPY package.json bun.lockb /temp/prod/
|
||||
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
||||
|
||||
# copy node_modules from temp directory
|
||||
# then copy all (non-ignored) project files into the image
|
||||
FROM base AS prerelease
|
||||
COPY --from=install /temp/prod/node_modules node_modules
|
||||
COPY . .
|
||||
|
||||
# [optional] tests & build
|
||||
ENV NODE_ENV=production
|
||||
RUN bun test
|
||||
#RUN bun run build
|
||||
|
||||
# copy production dependencies and source code into final image
|
||||
FROM base AS release
|
||||
COPY --from=install /temp/prod/node_modules node_modules
|
||||
COPY --from=prerelease /usr/src/app/ .
|
||||
|
||||
# run the app
|
||||
USER bun
|
||||
ENTRYPOINT [ "bun", "run", "./src/index.ts" ]
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 moriese
|
||||
Copyright (c) 2024 mo (mo@unom.io)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
8
docker-compose.yml
Normal file
8
docker-compose.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
services:
|
||||
avocadi-bot:
|
||||
container_name: avocadi-bot
|
||||
image: git.unom.io/mo/avocadi-bot:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./.env:/usr/src/app/.env
|
||||
- ./prod.sqlite:/usr/src/app/prod.sqlite:rw
|
||||
17
drizzle.config.ts
Normal file
17
drizzle.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import "dotenv/config";
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
|
||||
const dbFileName = process.env.DB_FILE_NAME;
|
||||
|
||||
if (!dbFileName) {
|
||||
throw new Error("Die Umgebungsvariable DB_FILE_NAME ist nicht gesetzt.");
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
out: "./drizzle",
|
||||
schema: "./src/db/schema.ts",
|
||||
dialect: "sqlite",
|
||||
dbCredentials: {
|
||||
url: dbFileName,
|
||||
},
|
||||
});
|
||||
8
drizzle/0000_needy_nightshade.sql
Normal file
8
drizzle/0000_needy_nightshade.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE `users_table` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`discord_id` integer NOT NULL,
|
||||
`took_medication_today` integer DEFAULT 0 NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `users_table_discord_id_unique` ON `users_table` (`discord_id`);
|
||||
2
drizzle/0001_dusty_wolverine.sql
Normal file
2
drizzle/0001_dusty_wolverine.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `users_table` ADD `join_streak` integer;--> statement-breakpoint
|
||||
ALTER TABLE `users_table` ADD `last_joined_at` integer;
|
||||
65
drizzle/meta/0000_snapshot.json
Normal file
65
drizzle/meta/0000_snapshot.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "ddb7170b-7f66-4e3c-ab64-9069e760e09a",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"users_table": {
|
||||
"name": "users_table",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"discord_id": {
|
||||
"name": "discord_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"took_medication_today": {
|
||||
"name": "took_medication_today",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"users_table_discord_id_unique": {
|
||||
"name": "users_table_discord_id_unique",
|
||||
"columns": [
|
||||
"discord_id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
79
drizzle/meta/0001_snapshot.json
Normal file
79
drizzle/meta/0001_snapshot.json
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "e9592066-1da0-41af-9e25-e2672ebe256f",
|
||||
"prevId": "ddb7170b-7f66-4e3c-ab64-9069e760e09a",
|
||||
"tables": {
|
||||
"users_table": {
|
||||
"name": "users_table",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"discord_id": {
|
||||
"name": "discord_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"join_streak": {
|
||||
"name": "join_streak",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"last_joined_at": {
|
||||
"name": "last_joined_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"took_medication_today": {
|
||||
"name": "took_medication_today",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"users_table_discord_id_unique": {
|
||||
"name": "users_table_discord_id_unique",
|
||||
"columns": [
|
||||
"discord_id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
20
drizzle/meta/_journal.json
Normal file
20
drizzle/meta/_journal.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1735431085265,
|
||||
"tag": "0000_needy_nightshade",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "6",
|
||||
"when": 1736688734822,
|
||||
"tag": "0001_dusty_wolverine",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
icon/mo_Avocadi_Avatar_Closeup_2.png
Normal file
BIN
icon/mo_Avocadi_Avatar_Closeup_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
17
package.json
17
package.json
@@ -3,18 +3,27 @@
|
||||
"module": "src/index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun --watch src/index.ts"
|
||||
"dev": "bun --watch src/index.ts",
|
||||
"test": "bun test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
"@types/bun": "^1.1.14",
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"drizzle-kit": "^0.30.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordjs/rest": "^2.4.0",
|
||||
"cron": "^3.3.1",
|
||||
"discord.js": "^14.16.3",
|
||||
"dotenv": "^16.4.7",
|
||||
"drizzle-orm": "^0.38.3",
|
||||
"zod": "^3.24.1"
|
||||
}
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"better-sqlite3",
|
||||
"esbuild"
|
||||
]
|
||||
}
|
||||
13
src/actions/activity/activity.service.ts
Normal file
13
src/actions/activity/activity.service.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { CacheType, Client, Interaction } from "discord.js";
|
||||
import client from "lib/client";
|
||||
|
||||
export class ActivityService {
|
||||
|
||||
async setActivity(client: Client<boolean>, description: string, activity: string) {
|
||||
client.user?.setActivity(":3", { type: 4 });
|
||||
console.log("set activity");
|
||||
client.user?.setPresence({
|
||||
status: "online",
|
||||
});
|
||||
}
|
||||
}
|
||||
30
src/actions/customMessage/customMessage.components.ts
Normal file
30
src/actions/customMessage/customMessage.components.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import config from "config";
|
||||
import { EmbedBuilder } from "discord.js";
|
||||
export const customContent = `hey <@&${config.discord.roleStudy}>! meine [eigene website](https://avocadi.unom.io) ist endlich on :3\ngebe mir gerne rueckmeldung unter <#${config.discord.channelIdFeedback}>! <3`;
|
||||
|
||||
export function createEmbed(title: string, description: string, timestamp?: boolean) {
|
||||
// ({ embeds: [exampleEmbed] })
|
||||
console.log("createEmbed()");
|
||||
const customEmbed = (timestamp === true) ? new EmbedBuilder()
|
||||
.setColor(0x004400)
|
||||
.setAuthor({
|
||||
name: title,
|
||||
iconURL:
|
||||
"https://media.discordapp.net/attachments/1321933410188656693/1323447010380222474/mo_Avocadi_Avatar_Closeup_2.png?ex=67748b93&is=67733a13&hm=f48efb3523bca5f50e79144c7b41a127c94670e693e3da3dc2e6ffe62ad8a769&=&format=webp&quality=lossless&width=1524&height=1524",
|
||||
url: "https://avocadi.unom.io",
|
||||
})
|
||||
.setDescription(description)
|
||||
.setTimestamp() :
|
||||
new EmbedBuilder()
|
||||
.setColor(0x004400)
|
||||
.setAuthor({
|
||||
name: title,
|
||||
iconURL:
|
||||
"https://media.discordapp.net/attachments/1321933410188656693/1323447010380222474/mo_Avocadi_Avatar_Closeup_2.png?ex=67748b93&is=67733a13&hm=f48efb3523bca5f50e79144c7b41a127c94670e693e3da3dc2e6ffe62ad8a769&=&format=webp&quality=lossless&width=1524&height=1524",
|
||||
url: "https://avocadi.unom.io",
|
||||
})
|
||||
.setDescription(description);
|
||||
//.setFooter({ text: 'Some footer text here', iconURL: 'https://i.imgur.com/AfFp7pu.png' });
|
||||
|
||||
return customEmbed;
|
||||
}
|
||||
133
src/actions/customMessage/customMessage.service.ts
Normal file
133
src/actions/customMessage/customMessage.service.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import { customContent, createEmbed } from "./customMessage.components.ts";
|
||||
import {
|
||||
Client,
|
||||
EmbedBuilder,
|
||||
type Message,
|
||||
type CacheType,
|
||||
type GuildMember,
|
||||
type Interaction,
|
||||
type OmitPartialGroupDMChannel,
|
||||
type ChatInputCommandInteraction,
|
||||
ChannelType,
|
||||
} from "discord.js";
|
||||
import { type CommandsType, Commands } from "commands/index.ts";
|
||||
import { time } from "drizzle-orm/mysql-core";
|
||||
|
||||
export class CustomMessageService {
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await this.handleChatInputCommand(interaction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async handleChatInputCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
console.log("accept");
|
||||
const commandName = interaction.commandName as CommandsType;
|
||||
switch (commandName) {
|
||||
case Commands.Enum.embed:
|
||||
await this.customEmbed(interaction);
|
||||
return;
|
||||
case Commands.Enum.message:
|
||||
await this.customMessage(interaction);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async checkPermission(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
const userIdCommand = interaction.user.id;
|
||||
if (userIdCommand !== config.discord.myId) {
|
||||
await interaction.reply({
|
||||
content: "you have no permission for that command",
|
||||
ephemeral: true,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if command done in server
|
||||
async checkIfServer(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
const guild = interaction.guild;
|
||||
if (!guild) {
|
||||
await interaction.reply({
|
||||
content: "command can only be used on a server",
|
||||
ephemeral: true,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async customEmbed(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
const title = interaction.options.getString("title") || " ";
|
||||
const description = interaction.options.getString("description") || " ";
|
||||
const timestamp = interaction.options.getBoolean("timestamp") || false;
|
||||
// return the value
|
||||
console.log(title, description, timestamp);
|
||||
|
||||
// permission check
|
||||
// permission check
|
||||
if (await this.checkPermission(interaction) && await this.checkIfServer(interaction))
|
||||
try {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(interaction.channelId);
|
||||
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send({
|
||||
embeds: [createEmbed(title, description, timestamp)],
|
||||
});
|
||||
}
|
||||
await interaction.reply({
|
||||
content: "successfully created embed",
|
||||
ephemeral: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("error while creating embed:", error);
|
||||
await interaction.reply({
|
||||
content:
|
||||
"error while creating embed",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async customMessage(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
const input: string = interaction.options.getString("input") || "";
|
||||
const result = input.replaceAll(";", "\n");
|
||||
|
||||
// return the value
|
||||
console.log(input);
|
||||
|
||||
// permission check && server check
|
||||
if (await this.checkPermission(interaction) && await this.checkIfServer(interaction))
|
||||
try {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(interaction.channelId);
|
||||
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send({
|
||||
content: result,
|
||||
});
|
||||
}
|
||||
await interaction.reply({
|
||||
content: "successfully created message",
|
||||
ephemeral: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("error while creating message:", error);
|
||||
await interaction.reply({
|
||||
content:
|
||||
"error while creating message",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/actions/debug/debug.service.ts
Normal file
48
src/actions/debug/debug.service.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { type CommandsType, Commands } from "commands";
|
||||
import config from "config";
|
||||
import type { CacheType, ChatInputCommandInteraction, Interaction } from "discord.js";
|
||||
import { checkPermission } from "permissions";
|
||||
|
||||
export class DebugService {
|
||||
|
||||
async handleInteraction(
|
||||
interaction: Interaction<CacheType>
|
||||
) {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await this.handleChatInputCommand(interaction);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
async handleChatInputCommand(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
const commandName = interaction.commandName as CommandsType;
|
||||
switch (commandName) {
|
||||
case Commands.Enum.version:
|
||||
await this.version(interaction);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async version(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
try {
|
||||
console.log("version command");
|
||||
if (await checkPermission(interaction.member) !== true) {
|
||||
await interaction.reply({
|
||||
content: "du hast keine rechte fuer diesen befehl",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await interaction.reply({
|
||||
content: "version: " + config.discord.version,
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
console.error("error while sending version msg:", error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
5
src/actions/dm/dm.components.ts
Normal file
5
src/actions/dm/dm.components.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import config from "config";
|
||||
import { EmbedBuilder } from "discord.js";
|
||||
|
||||
export const dmWelcomeContent = `hey! ich bin avocadi von [avocadi-study](<https://discord.gg/kkryyeXu3S>)!\n\num auf den rest des servers zugreifen zu koennen, musst du dich noch vorstellen (unter <#${config.discord.channelIdIntroduction}>)!\n\n---\nname und alter:\npronomen:\nklasse/studiengang/beruf:\nhobby:\nueber mich:\n---\n\nsobald wir deine nachricht ueberprueft haben, bekommst du die rolle **lernende:r** :)`;
|
||||
export const dmAcceptedContent = `huhu! du wurdest als lernende:r akzeptiert :3\nsag gerne hallo: <#${config.discord.channelIdOffTopic}> <:avocadi_cute:1321893797138923602>`;
|
||||
104
src/actions/dm/dm.service.ts
Normal file
104
src/actions/dm/dm.service.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import { dmWelcomeContent, dmAcceptedContent } from "./dm.components.ts";
|
||||
import {
|
||||
Client,
|
||||
EmbedBuilder,
|
||||
type Message,
|
||||
type CacheType,
|
||||
type GuildMember,
|
||||
type Interaction,
|
||||
type OmitPartialGroupDMChannel,
|
||||
} from "discord.js";
|
||||
|
||||
export class DmService {
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
// todo
|
||||
}
|
||||
|
||||
async reminderPrivate(member: GuildMember) {
|
||||
console.log("reminder");
|
||||
try {
|
||||
const content = `hey, kleine erinnerung :)\nbitte stelle dich auf <#${config.discord.channelIdIntroduction}> vor, damit du alle kanaele ansehen und nutzen kannst! <3`
|
||||
await client.users.send(member, content);
|
||||
} catch (error) {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdNotification);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
|
||||
}
|
||||
console.error("error while sending a welcome msg:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async welcomePrivate(member: GuildMember) {
|
||||
console.log("welcome private");
|
||||
try {
|
||||
await client.users.send(member, dmWelcomeContent);
|
||||
} catch (error) {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdNotification);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
|
||||
}
|
||||
console.error("error while sending a welcome msg:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async forward(message: OmitPartialGroupDMChannel<Message<boolean>>) {
|
||||
if (message.channel.isDMBased()) {
|
||||
const author = message.author.id;
|
||||
const recipient = message.channel.recipient?.id;
|
||||
console.log("forward message");
|
||||
let context = "";
|
||||
|
||||
if (message.author.bot) {
|
||||
context = `<@${author}> hat an <@${recipient}> geschrieben:\n"${message.content}"`;
|
||||
}
|
||||
else {
|
||||
context = `<@${author}> hat geschrieben:\n"${message.content}"`;
|
||||
}
|
||||
|
||||
try {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdNotification);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(context);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("error while forwarding a msg:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async acceptDm(member: GuildMember) {
|
||||
console.log("accept dm");
|
||||
try {
|
||||
await client.users.send(member, dmAcceptedContent);
|
||||
} catch (error) {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdNotification);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
|
||||
}
|
||||
console.error("error while sending a accept msg:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async roleMentionDm(member: GuildMember, add: boolean) {
|
||||
console.log("rolementionadd dm");
|
||||
try {
|
||||
const contentRoleMentionDm = `du hast die rolle **streber:in** erfolgreich ** *${(add ? "zugeteilt" : "entfernt")}* ** bekommen :3 <#${config.discord.channelIdOffTopic}> <:avocadi_cute:1321893797138923602>`;
|
||||
client.users.send(member, contentRoleMentionDm);
|
||||
} catch (error) {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdNotification);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
|
||||
}
|
||||
console.error("error while sending a accept msg:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ export class GreetingService {
|
||||
async greet() {
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
const channel = channels.cache.get(config.discord.channelIdBot);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: "HALLOOOO" });
|
||||
60
src/actions/dynamicVChannel/dynamicVChannel.service.ts
Normal file
60
src/actions/dynamicVChannel/dynamicVChannel.service.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import { } from "./dynamicVChannel.components.ts";
|
||||
import {
|
||||
Client,
|
||||
EmbedBuilder,
|
||||
type Message,
|
||||
type CacheType,
|
||||
type GuildMember,
|
||||
type Interaction,
|
||||
type OmitPartialGroupDMChannel,
|
||||
type VoiceState,
|
||||
type VoiceChannel,
|
||||
type StageChannel,
|
||||
Events,
|
||||
type VoiceBasedChannel,
|
||||
} from "discord.js";
|
||||
|
||||
export class DynamicVChannelService {
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
// todo
|
||||
}
|
||||
|
||||
async createVChannel(
|
||||
newState: VoiceState,
|
||||
channel: VoiceBasedChannel
|
||||
): Promise<StageChannel | VoiceChannel> {
|
||||
//console.log("createChannel()");
|
||||
const newVChannel = await channel.clone({
|
||||
name: `${channel.name.substring(2)}; ${newState.member?.displayName}`,
|
||||
position: 100,
|
||||
});
|
||||
|
||||
return newVChannel;
|
||||
}
|
||||
|
||||
async deleteVChannel(
|
||||
oldState: VoiceState,
|
||||
newState: VoiceState,
|
||||
newChannel: StageChannel | VoiceChannel,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
channelListeners: Map<any, any>,
|
||||
channelListener: (oldState: VoiceState, newState: VoiceState) => void
|
||||
) {
|
||||
//console.log("deleteChannel()");
|
||||
if (
|
||||
oldState.channelId === newChannel.id ||
|
||||
newState.channelId === newChannel.id
|
||||
) {
|
||||
if (newChannel.members.size === 0) {
|
||||
newChannel.delete().catch(console.error);
|
||||
|
||||
client.removeListener(Events.VoiceStateUpdate, channelListener);
|
||||
channelListeners.delete(newChannel.id);
|
||||
}
|
||||
}
|
||||
return channelListeners;
|
||||
}
|
||||
}
|
||||
38
src/actions/greeting/greeting.components.ts
Normal file
38
src/actions/greeting/greeting.components.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import config from "config";
|
||||
import type { GuildMember } from "discord.js";
|
||||
|
||||
export const greetContent = ["HALLOOOO", "guten morgen! ich hoffe es geht euch gut <3"];
|
||||
export const sleepContent = ["gute nacht! ich muss jetzt schlafen gehen :c", "zzzzZZ..", "*schnarch*"];
|
||||
export const dmWelcomeContent = `hey! ich bin avocadi von [avocadi-study](<https://discord.gg/kkryyeXu3S>)!\n\num auf den rest des servers zugreifen zu koennen, musst du dich noch vorstellen (unter <#${config.discord.channelIdIntroduction}>)!\n\n---\nname und alter:\npronomen:\nklasse/studiengang/beruf:\nhobby:\nueber mich:\n---\n\nsobald wir deine nachricht ueberprueft haben, bekommst du die rolle **lernende:r** :)`;
|
||||
export const dmAcceptedContent = `huhu! du wurdest als lernende:r akzeptiert :3\nsag gerne hallo: <#${config.discord.channelIdOffTopic}> <:avocadi_cute:1321893797138923602>`;
|
||||
|
||||
export function getWelcomeContent(member: GuildMember) {
|
||||
const welcomeContents = [
|
||||
`willkommen auf dem server, ${member}! fuehl dich wie zuhause💕`,
|
||||
`hey ${member}! schoen, dass du hier bist! 😊`,
|
||||
`hi ${member}, willkommen! viel spass hier! 💖`,
|
||||
`willkommen, ${member}! schoen, dass du da bist! :3`,
|
||||
`moin ${member}! viel spass im server! c:`,
|
||||
`hey ${member}, herzlich willkommen! fuehl dich wie zu hause! <3`,
|
||||
`hi ${member}! cool, dass du da bist! <3`,
|
||||
`willkommen, ${member}! wir freuen uns, dass du hier bist! 💕`,
|
||||
`hey ${member}! schoen, dass du bei uns bist! :3`,
|
||||
`willkommen auf dem server, ${member}! viel spass hier! ✨`,
|
||||
`hi ${member}, super, dass du dabei bist! :3`,
|
||||
`hey ${member}, willkommen bei uns! 💖`,
|
||||
`moin ${member}! schoen, dass du dabei bist! :)`,
|
||||
`hi ${member}, willkommen in unserer kleinen community! ✨`,
|
||||
`willkommen, ${member}! fuehl dich wie zu hause! 💕`,
|
||||
`hey ${member}, schoen, dass du uns gefunden hast! <333`,
|
||||
`hi ${member}, willkommen in unserer runde! c:`,
|
||||
`willkommen, ${member}! schoen, dass du hier bist! 💖`,
|
||||
`moin ${member}! lass uns zusammen spass haben! ✨`,
|
||||
`hey ${member}, herzlich willkommen bei uns! 😊`,
|
||||
`hi ${member}! schoen, dass du dabei bist! 💕`,
|
||||
`willkommen auf dem server, ${member}! wir freuen uns auf dich! <3`,
|
||||
`hey ${member}, schoen, dass du da bist! ✨`,
|
||||
`hi ${member}, willkommen! fuehl dich wie zu hause! 💖`,
|
||||
`willkommen, ${member}! lass uns gemeinsam eine tolle zeit haben! :3`,
|
||||
];
|
||||
return welcomeContents[Math.floor(Math.random() * welcomeContents.length)];
|
||||
}
|
||||
339
src/actions/greeting/greeting.service.ts
Normal file
339
src/actions/greeting/greeting.service.ts
Normal file
@@ -0,0 +1,339 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import {
|
||||
getWelcomeContent,
|
||||
greetContent,
|
||||
sleepContent,
|
||||
} from "./greeting.components.ts";
|
||||
import {
|
||||
type ChatInputCommandInteraction,
|
||||
Client,
|
||||
EmbedBuilder,
|
||||
type CacheType,
|
||||
type GuildMember,
|
||||
type Interaction,
|
||||
GuildMemberRoleManager,
|
||||
type APIInteractionGuildMember,
|
||||
} from "discord.js";
|
||||
import { DmService } from "actions/dm/dm.service.ts";
|
||||
import { Commands, type CommandsType } from "commands/index.ts";
|
||||
import { checkPermission } from "permissions/index.ts";
|
||||
|
||||
export class GreetingService {
|
||||
dmService: DmService;
|
||||
|
||||
constructor() {
|
||||
this.dmService = new DmService();
|
||||
}
|
||||
|
||||
async handleInteraction(
|
||||
interaction: Interaction<CacheType>
|
||||
) {
|
||||
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await this.handleChatInputCommand(interaction);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
async handleChatInputCommand(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
const commandName = interaction.commandName as CommandsType;
|
||||
switch (commandName) {
|
||||
case Commands.Enum.accept:
|
||||
await this.acceptUser(interaction);
|
||||
return;
|
||||
case Commands.Enum.welcome:
|
||||
await this.welcomeCommand(interaction);
|
||||
return;
|
||||
case Commands.Enum.reminder:
|
||||
await this.reminderCommand(interaction);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async welcome(member: GuildMember) {
|
||||
console.log("welcome msg");
|
||||
const welcomeContent = getWelcomeContent(member);
|
||||
|
||||
try {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdWelcome);
|
||||
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(welcomeContent);
|
||||
}
|
||||
|
||||
await this.dmService.welcomePrivate(member);
|
||||
} catch (error) {
|
||||
console.error("error while sending a welcome msg:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async acceptUser(
|
||||
interaction: ChatInputCommandInteraction<CacheType>
|
||||
) {
|
||||
console.log("accept user");
|
||||
|
||||
// get the string option
|
||||
const input = interaction.options.getString("input") || "";
|
||||
// return the value
|
||||
//console.log(input);
|
||||
|
||||
// permission check
|
||||
if (await checkPermission(interaction.member) !== true) {
|
||||
await interaction.reply({
|
||||
content: "du hast keine rechte fuer diesen befehl",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// get user id from mentioning
|
||||
const userId = input.replace(/[<@!>]/g, "");
|
||||
console.log(userId.toString());
|
||||
const guild = interaction.guild;
|
||||
if (!guild) {
|
||||
await interaction.reply({
|
||||
content: "command can only be used on a server",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const member = await guild.members.fetch(userId);
|
||||
const username = member.user.username;
|
||||
console.log(username);
|
||||
|
||||
if (await this.checkRole(member) === true) {
|
||||
await interaction.reply({
|
||||
content: `${member.user.username} hat die rolle *lernende:r* schon!`,
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await member.roles.add(config.discord.roleStudy);
|
||||
|
||||
await interaction.reply({
|
||||
content: `die rolle *lernende:r* wurde erfolgreich an ${member.user.username} vergeben`,
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
this.dmService.acceptDm(member);
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Hinzufügen der Rolle:", error);
|
||||
await interaction.reply({
|
||||
content:
|
||||
"Es gab einen Fehler beim Hinzufügen der Rolle. Stelle sicher, dass du einen gültigen User erwähnt hast.",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// unused
|
||||
async greet() {
|
||||
client.user?.setActivity("guten morgen! :3", { type: 4 });
|
||||
console.log("set activity: awake");
|
||||
client.user?.setPresence({
|
||||
status: "online",
|
||||
});
|
||||
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelIdOffTopic);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: this.getContent(false) });
|
||||
}
|
||||
}
|
||||
|
||||
// unused
|
||||
async sleep() {
|
||||
client.user?.setActivity("zzzzZZ..", { type: 4 });
|
||||
console.log("set activity: asleep");
|
||||
client.user?.setPresence({
|
||||
status: "dnd",
|
||||
});
|
||||
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelIdOffTopic);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: this.getContent(true) });
|
||||
}
|
||||
}
|
||||
|
||||
async newYear() {
|
||||
client.user?.setActivity("frohes neues! :)", { type: 4 });
|
||||
console.log("set activity: happy new year");
|
||||
client.user?.setPresence({
|
||||
status: "online",
|
||||
});
|
||||
|
||||
// unused
|
||||
/*const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelIdOffTopic);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: "frohes neues! @everyone" });
|
||||
}*/
|
||||
}
|
||||
|
||||
getContent(asleep: boolean) {
|
||||
if (asleep) {
|
||||
return sleepContent[getRandomInt(0, sleepContent.length - 1)];
|
||||
}
|
||||
return greetContent[getRandomInt(0, greetContent.length - 1)];
|
||||
}
|
||||
|
||||
async reminderCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
console.log("remind user");
|
||||
|
||||
// get the string option
|
||||
const input = interaction.options.getString("input") || "";
|
||||
// return the value
|
||||
//console.log(input);
|
||||
|
||||
try {
|
||||
// permission check
|
||||
if (await checkPermission(interaction.member) !== true) {
|
||||
await interaction.reply({
|
||||
content: "du hast keine rechte fuer diesen befehl",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// get user id from mentioning
|
||||
const userId = input.replace(/[<@!>]/g, "");
|
||||
console.log(userId.toString());
|
||||
const guild = interaction.guild;
|
||||
if (!guild) {
|
||||
await interaction.reply({
|
||||
content: "command can only be used on a server",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const member = await guild.members.fetch(userId);
|
||||
const username = member.user.username;
|
||||
console.log(username);
|
||||
|
||||
if (await this.checkRole(member) === true) {
|
||||
await interaction.reply({
|
||||
content: `${member.user.username} hat die rolle *lernende:r* schon!`,
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await this.dmService.reminderPrivate(member);
|
||||
//await member.roles.add(config.discord.roleStudy);
|
||||
|
||||
await interaction.reply({
|
||||
content: `${member.user.username} wurde erfolgrich remindet`,
|
||||
ephemeral: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("error while reminding:", error);
|
||||
await interaction.reply({
|
||||
content:
|
||||
"Es gab einen Fehler beim reminden. Stelle sicher, dass du einen gültigen User erwähnt hast.",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async welcomeCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>
|
||||
) {
|
||||
console.log("welcome user");
|
||||
|
||||
// get the string option
|
||||
const input = interaction.options.getString("input") || "";
|
||||
// return the value
|
||||
//console.log(input);
|
||||
|
||||
try {
|
||||
// get user id from mentioning
|
||||
const userId = input.replace(/[<@!>]/g, "");
|
||||
console.log(userId.toString());
|
||||
const guild = interaction.guild;
|
||||
if (!guild) {
|
||||
await interaction.reply({
|
||||
content: "command can only be used on a server",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (await checkPermission(interaction.member) !== true) {
|
||||
await interaction.reply({
|
||||
content: "du hast keine rechte fuer diesen befehl",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// get member from id
|
||||
const member = await guild.members.fetch(userId);
|
||||
const username = member.user.username;
|
||||
console.log(username);
|
||||
|
||||
const welcomeContent = getWelcomeContent(member);
|
||||
|
||||
if (await this.checkRole(member) === true) {
|
||||
await interaction.reply({
|
||||
content: `${member.user.username} wurde schon begruesst!`,
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdWelcome);
|
||||
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(welcomeContent);
|
||||
}
|
||||
|
||||
await this.dmService.welcomePrivate(member);
|
||||
|
||||
} catch (error) {
|
||||
console.error("error while sending a welcome command msg:", error);
|
||||
}
|
||||
|
||||
await interaction.reply({
|
||||
content: `erfolgreich welcome command: ${member.user.username}`,
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("fehler bei welcome command", error);
|
||||
await interaction.reply({
|
||||
content:
|
||||
"fehler bei welcome command",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async checkRole(member: GuildMember) {
|
||||
let hasRole = false;
|
||||
if (member?.roles instanceof GuildMemberRoleManager) {
|
||||
if (member.roles.cache.has(config.discord.roleAdmin) || member.roles.cache.has(config.discord.roleMod)) {
|
||||
console.log("user has permission");
|
||||
hasRole = true;
|
||||
}
|
||||
}
|
||||
return hasRole;
|
||||
}
|
||||
}
|
||||
38
src/actions/greeting/greeting.task.ts
Normal file
38
src/actions/greeting/greeting.task.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { CronJob } from "cron";
|
||||
import { GreetingService } from "actions/greeting/greeting.service";
|
||||
|
||||
const greetingService = new GreetingService();
|
||||
/*
|
||||
new CronJob(
|
||||
"0 30 6 * * *", // cronTime
|
||||
async () => {
|
||||
console.log("good morning");
|
||||
await greetingService.greet();
|
||||
}, // onTick
|
||||
null, // onComplete
|
||||
true, // start
|
||||
"Europe/Berlin", // timeZone
|
||||
);
|
||||
// job.start() is optional here because of the fourth parameter set to true.
|
||||
|
||||
new CronJob(
|
||||
"0 30 22 * * *",
|
||||
async () => {
|
||||
console.log("good night");
|
||||
await greetingService.sleep();
|
||||
},
|
||||
null,
|
||||
true,
|
||||
"Europe/Berlin",
|
||||
);*/
|
||||
|
||||
new CronJob(
|
||||
"0 0 0 1 1 *",
|
||||
async () => {
|
||||
console.log("happy new year");
|
||||
await greetingService.newYear();
|
||||
},
|
||||
null,
|
||||
true,
|
||||
"Europe/Berlin",
|
||||
);
|
||||
26
src/actions/help/help.components.ts
Normal file
26
src/actions/help/help.components.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Commands, CommandsMeta } from 'commands';
|
||||
import { AttachmentBuilder, EmbedBuilder } from 'discord.js';
|
||||
|
||||
export default function createEmbed() { // ({ embeds: [exampleEmbed] })
|
||||
console.log("createHelpEmbed()");
|
||||
|
||||
const exampleEmbed = new EmbedBuilder()
|
||||
.setColor(0x004400)
|
||||
//.setTitle("/hilfe")
|
||||
//.setURL("")
|
||||
.setAuthor({ name: "avocadi - befehle", iconURL: "https://media.discordapp.net/attachments/1321933410188656693/1323447010380222474/mo_Avocadi_Avatar_Closeup_2.png?ex=67748b93&is=67733a13&hm=f48efb3523bca5f50e79144c7b41a127c94670e693e3da3dc2e6ffe62ad8a769&=&format=webp&quality=lossless&width=1524&height=1524", url: 'https://git.unom.io/mo/avocadi-bot' })
|
||||
.setDescription(" ")
|
||||
.addFields(
|
||||
{ name: `/${Commands.Enum.giessen}`, value: CommandsMeta.giessen.description },
|
||||
{ name: `/${Commands.Enum.medikamente}`, value: CommandsMeta.medikamente.description },
|
||||
{ name: `/${Commands.Enum.hilfe}`, value: CommandsMeta.hilfe.description },
|
||||
{ name: `/${Commands.Enum.support}`, value: CommandsMeta.support.description },
|
||||
{ name: `/${Commands.Enum.kofi}`, value: CommandsMeta.kofi.description },
|
||||
{ name: `/${Commands.Enum.disboard}`, value: CommandsMeta.disboard.description },
|
||||
{ name: `/${Commands.Enum.discadia}`, value: CommandsMeta.discadia.description },
|
||||
)
|
||||
.setTimestamp()
|
||||
//.setFooter({ text: 'Some footer text here', iconURL: 'https://i.imgur.com/AfFp7pu.png' });
|
||||
;
|
||||
return exampleEmbed;
|
||||
}
|
||||
22
src/actions/help/help.service.ts
Normal file
22
src/actions/help/help.service.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { CacheType, Interaction } from "discord.js";
|
||||
import createEmbed from "./help.components";
|
||||
|
||||
export class HelpService {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
exampleEmbed: any;
|
||||
|
||||
constructor() {
|
||||
this.exampleEmbed = createEmbed();
|
||||
}
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
console.log("help");
|
||||
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await interaction.reply({
|
||||
embeds: [this.exampleEmbed],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/actions/medication/medication.components.ts
Normal file
10
src/actions/medication/medication.components.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ButtonBuilder, ButtonStyle } from "discord.js";
|
||||
|
||||
export const yesButton = new ButtonBuilder()
|
||||
.setCustomId("yesMedication")
|
||||
.setLabel("ja")
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
export const noButton = new ButtonBuilder()
|
||||
.setCustomId("noMedication")
|
||||
.setLabel("noch nicht")
|
||||
.setStyle(ButtonStyle.Secondary);
|
||||
250
src/actions/medication/medication.service.ts
Normal file
250
src/actions/medication/medication.service.ts
Normal file
@@ -0,0 +1,250 @@
|
||||
import { CronJob } from "cron";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
type ButtonInteraction,
|
||||
ButtonStyle,
|
||||
type ChatInputCommandInteraction,
|
||||
type ModalSubmitInteraction,
|
||||
type CacheType,
|
||||
type Interaction,
|
||||
} from "discord.js";
|
||||
import { yesButton, noButton } from "./medication.components";
|
||||
import { db } from "db";
|
||||
import { usersTable } from "db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export class MedicationService {
|
||||
medication: string;
|
||||
tookMedication: boolean;
|
||||
|
||||
constructor() {
|
||||
this.medication = "";
|
||||
this.tookMedication = false;
|
||||
}
|
||||
|
||||
async askMedication() {
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelIdBot);
|
||||
|
||||
// funkt noch nicht, beides
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(yesButton);
|
||||
row.addComponents(noButton);
|
||||
|
||||
if (
|
||||
channel?.isTextBased &&
|
||||
channel?.isSendable() &&
|
||||
this.tookMedication === false
|
||||
) {
|
||||
await channel.send({
|
||||
content: "hast du schon deine medis genommen? :)",
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
components: [row as any],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getMedication() {
|
||||
// todo
|
||||
}
|
||||
|
||||
setMedication() {
|
||||
const reply = this.getReply();
|
||||
console.log("medication");
|
||||
|
||||
// this.medication = input von user:in hier rein;
|
||||
return {
|
||||
reply,
|
||||
};
|
||||
}
|
||||
|
||||
getReply() {
|
||||
return "medication reminder";
|
||||
}
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
if (interaction.isModalSubmit()) {
|
||||
await this.handleModalSubmit(interaction);
|
||||
return;
|
||||
}
|
||||
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await this.handleChatInputCommand(interaction);
|
||||
return;
|
||||
}
|
||||
if (interaction.isButton()) {
|
||||
await this.handleButton(interaction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async handleModalSubmit(interaction: ModalSubmitInteraction<CacheType>) {
|
||||
switch (interaction.customId) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async handleButton(interaction: ButtonInteraction<CacheType>) {
|
||||
console.log("button interaction");
|
||||
|
||||
const discordId = Number.parseInt(interaction.user.id);
|
||||
const name = interaction.user.displayName;
|
||||
|
||||
console.log(`userid: ${discordId}`);
|
||||
|
||||
try {
|
||||
const userId = await this.ensureUserExists(discordId, name);
|
||||
console.log(`userid: ${userId}`);
|
||||
|
||||
const tookMedication = interaction.customId === "yesMedication";
|
||||
|
||||
await interaction.reply({
|
||||
content: tookMedication
|
||||
? "das hast du toll gemacht <3 mach weiter so :3"
|
||||
: "das passiert mal... aber versuch sie heute noch zu nehmen, oki? :)",
|
||||
});
|
||||
await this.logMedication(userId, tookMedication);
|
||||
} catch (error) {
|
||||
console.error("error ensuring user exists:", error);
|
||||
await interaction.reply({
|
||||
content:
|
||||
"es gab einen fehler beim verarbeiten deiner anfrage :( versuch es bitte spaeter nochmal, oki? c:",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async handleChatInputCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
const result = this.setMedication();
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(yesButton);
|
||||
row.addComponents(noButton);
|
||||
await interaction.reply({
|
||||
content: result.reply,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
components: [row as any],
|
||||
});
|
||||
}
|
||||
|
||||
/**, um die Benutzerdaten in die Datenbank zu schreiben.
|
||||
* @param discordId unique user id
|
||||
* @param name name how the user wants to get called by avocadi
|
||||
* @param tookMedication if user took medication
|
||||
*/
|
||||
async logMedication(id: number, tookMedication: boolean) {
|
||||
try {
|
||||
await db
|
||||
.update(usersTable)
|
||||
.set({
|
||||
took_medication_today: Number(tookMedication),
|
||||
})
|
||||
.where(eq(usersTable.id, id));
|
||||
|
||||
/* await db.insert(usersTable).values({
|
||||
name: name,
|
||||
discord_id: discordId,
|
||||
took_medication_today: Number(tookMedication),
|
||||
}); */
|
||||
|
||||
console.log(`user with id ${id} saved`);
|
||||
} catch (error) {
|
||||
console.error("error while saving in db:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async getNameByDiscordId(discordId: number): Promise<string | null> {
|
||||
const result = await db
|
||||
.select({
|
||||
name: usersTable.name,
|
||||
})
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.discord_id, discordId))
|
||||
.limit(1);
|
||||
|
||||
if (result.length > 0) {
|
||||
console.log("user found");
|
||||
return result[0].name;
|
||||
}
|
||||
console.log("name not found");
|
||||
return "";
|
||||
}
|
||||
|
||||
async findUserIdByDiscordId(discordId: number): Promise<number | null> {
|
||||
try {
|
||||
const result = await db
|
||||
.select({
|
||||
id: usersTable.id,
|
||||
})
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.discord_id, discordId))
|
||||
.limit(1);
|
||||
|
||||
if (result.length > 0) {
|
||||
console.log(`ID für Discord-ID ${discordId} gefunden: ${result[0].id}`);
|
||||
return result[0].id;
|
||||
}
|
||||
console.log(`no id for discordId ${discordId} found`);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`error while calling id for discordId ${discordId}:`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async ensureUserExists(discordId: number, name: string): Promise<number> {
|
||||
try {
|
||||
const userId = await this.findUserIdByDiscordId(discordId);
|
||||
|
||||
if (userId !== null) {
|
||||
console.log(`entry for discordID ${discordId} already exists`);
|
||||
return userId;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`found no entry for discordID ${discordId}. creating new entry`,
|
||||
);
|
||||
|
||||
const result = await db
|
||||
.insert(usersTable)
|
||||
.values({
|
||||
name: name,
|
||||
discord_id: discordId,
|
||||
})
|
||||
.onConflictDoNothing()
|
||||
.returning({
|
||||
id: usersTable.id,
|
||||
});
|
||||
|
||||
if (result.length > 0) {
|
||||
const newUserId = result[0].id;
|
||||
console.log(
|
||||
`new user with discordId ${discordId} and name ${name} created. id: ${newUserId}`,
|
||||
);
|
||||
return newUserId;
|
||||
}
|
||||
|
||||
// check again if user is now existing
|
||||
const newUserId = await this.findUserIdByDiscordId(discordId);
|
||||
if (newUserId !== null) {
|
||||
console.log(`user created in parallel. fetched id: ${newUserId}`);
|
||||
return newUserId;
|
||||
}
|
||||
throw new Error(`creating a new user for discordId ${discordId} failed`);
|
||||
} catch (error) {
|
||||
console.error("error while creating or calling the user:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/actions/medication/medication.task.ts
Normal file
15
src/actions/medication/medication.task.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { CronJob } from "cron";
|
||||
import { MedicationService } from "actions/medication/medication.service";
|
||||
|
||||
const medicationService = new MedicationService();
|
||||
|
||||
new CronJob(
|
||||
"0 0 19 * * *", // cronTime
|
||||
async () => {
|
||||
console.log("askMedication()");
|
||||
await medicationService.askMedication();
|
||||
}, // onTick
|
||||
null, // onComplete
|
||||
true, // start
|
||||
"Europe/Berlin", // timeZone
|
||||
);
|
||||
0
src/actions/pomodoro/pomodoro.components.ts
Normal file
0
src/actions/pomodoro/pomodoro.components.ts
Normal file
42
src/actions/pomodoro/pomodoro.controller.ts
Normal file
42
src/actions/pomodoro/pomodoro.controller.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import config from "config";
|
||||
import { Events, NewsChannel, type VoiceState } from "discord.js";
|
||||
import client from "lib/client";
|
||||
import EventEmitter from "node:events";
|
||||
import { PomodoroService } from "actions/pomodoro/pomodoro.service";
|
||||
|
||||
export default class PomodoroController extends EventEmitter {
|
||||
private pomodoroService: PomodoroService;
|
||||
private activePomodoros = new Set<string>();
|
||||
private pomodoroChannels = [config.discord.vchannelIdPomodoro25, config.discord.vchannelIdPomodoro50];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.pomodoroService = new PomodoroService();
|
||||
|
||||
client.on(Events.VoiceStateUpdate, async (oldState, newState) => {
|
||||
const userId = newState.id;
|
||||
|
||||
const joinedPomodoroVC = newState.channelId != null && this.pomodoroChannels.includes(newState.channelId) &&
|
||||
oldState.channelId !== newState.channelId;
|
||||
|
||||
const leftPomodoroVC = oldState.channelId != null && this.pomodoroChannels.includes(oldState.channelId) &&
|
||||
newState.channelId !== oldState.channelId;
|
||||
|
||||
if (leftPomodoroVC && this.activePomodoros.has(userId)) {
|
||||
console.log("pomodoro left");
|
||||
this.pomodoroService.stopPomodoro(userId);
|
||||
this.activePomodoros.delete(userId);
|
||||
}
|
||||
|
||||
if (joinedPomodoroVC && !this.activePomodoros.has(userId)) {
|
||||
console.log("pomodoro join");
|
||||
const member = newState.member;
|
||||
const vchannel = newState.channel;
|
||||
if (!member || !vchannel) return;
|
||||
|
||||
this.activePomodoros.add(userId);
|
||||
this.pomodoroService.startPomodoroLoop(member, vchannel);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
67
src/actions/pomodoro/pomodoro.service.ts
Normal file
67
src/actions/pomodoro/pomodoro.service.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import type { GuildMember, VoiceBasedChannel } from "discord.js";
|
||||
import client from "lib/client";
|
||||
import config from "config";
|
||||
import { CustomMessageService } from "actions/customMessage/customMessage.service";
|
||||
|
||||
export class PomodoroService {
|
||||
customMessageService: CustomMessageService;
|
||||
private activeControllers = new Map<string, AbortController>();
|
||||
|
||||
constructor() {
|
||||
this.customMessageService = new CustomMessageService();
|
||||
}
|
||||
|
||||
public async startPomodoroLoop(member: GuildMember, vchannel: VoiceBasedChannel) {
|
||||
const userId = member.id;
|
||||
const controller = new AbortController();
|
||||
this.activeControllers.set(userId, controller);
|
||||
|
||||
const minutesWork = vchannel.id === config.discord.vchannelIdPomodoro25 ? 25 : 50;
|
||||
const minutesBreak = minutesWork / 5;//vchannel.id === config.discord.vchannelIdPomodoro25 ? 5 : 10;
|
||||
|
||||
const signal = controller.signal;
|
||||
|
||||
try {
|
||||
while (!signal.aborted) {
|
||||
await this.sendMessage(`<@${userId}> 🍅 **pomodoro gestartet!** ${minutesWork} minuten produktivitaet`);
|
||||
const finishedWork = await this.sleep(minutesWork * 60 * 1000, signal);
|
||||
if (!finishedWork) break;
|
||||
|
||||
await this.sendMessage(`<@${userId}> ☕ **pause!** ${minutesBreak} minuten chillen`);
|
||||
const finishedBreak = await this.sleep(minutesBreak * 60 * 1000, signal);
|
||||
if (!finishedBreak) break;
|
||||
}
|
||||
} catch (err) {
|
||||
if ((err as Error).name !== "AbortError") {
|
||||
console.error("pomodoro fehler:", err);
|
||||
}
|
||||
} finally {
|
||||
this.activeControllers.delete(userId);
|
||||
}
|
||||
}
|
||||
|
||||
public stopPomodoro(userId: string) {
|
||||
const controller = this.activeControllers.get(userId);
|
||||
if (controller) {
|
||||
controller.abort();
|
||||
this.activeControllers.delete(userId);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendMessage(text: string) {
|
||||
const channel = client.channels.cache.get(config.discord.channelIdPomodoro);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(text);
|
||||
}
|
||||
}
|
||||
|
||||
private sleep(ms: number, signal: AbortSignal): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => resolve(true), ms);
|
||||
signal.addEventListener("abort", () => {
|
||||
clearTimeout(timeout);
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
0
src/actions/reactRole/reactRoles.components.ts
Normal file
0
src/actions/reactRole/reactRoles.components.ts
Normal file
69
src/actions/reactRole/reactRoles.service.ts
Normal file
69
src/actions/reactRole/reactRoles.service.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { DmService } from "actions/dm/dm.service";
|
||||
import config from "config";
|
||||
import type { CacheType, ChatInputCommandInteraction, Guild, GuildMember, MessageReaction, PartialMessageReaction, PartialUser, User } from "discord.js";
|
||||
|
||||
export class ReactRolesService {
|
||||
dmService: DmService;
|
||||
|
||||
constructor() {
|
||||
this.dmService = new DmService();
|
||||
}
|
||||
|
||||
async roleMention(reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser, add: boolean) {
|
||||
if (!await this.validMsg(reaction.message.id)) return;
|
||||
try {
|
||||
await reaction.fetch();
|
||||
const guild = reaction.message.guild;
|
||||
if (!guild) return;
|
||||
const member = await this.getUser(guild, user);
|
||||
if (!member) return;
|
||||
add ? await this.giveRoleMention(member, guild, user) : await this.removeRoleMention(member, guild, user);
|
||||
await this.dmService.roleMentionDm(member, add);
|
||||
} catch (error) {
|
||||
console.error('smt went wring while roleMention():', error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async giveRoleMention(member: GuildMember, guild: Guild, user: User | PartialUser) {
|
||||
this.updateRoleMention(member, guild, user, true);
|
||||
}
|
||||
async removeRoleMention(member: GuildMember, guild: Guild, user: User | PartialUser) {
|
||||
this.updateRoleMention(member, guild, user, false);
|
||||
}
|
||||
|
||||
async updateRoleMention(member: GuildMember, guild: Guild, user: User | PartialUser, add: boolean) {
|
||||
try {
|
||||
|
||||
const role = guild.roles.cache.get(config.discord.roleMention);
|
||||
if (!role) {
|
||||
console.error("role ot found.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (add === member.roles.cache.has(role.id)) {
|
||||
console.log(`${member.user.tag} hat die Rolle *streber* bereits.`);
|
||||
return;
|
||||
}
|
||||
|
||||
await (add ? member.roles.add(role) : member.roles.remove(role));
|
||||
console.log(`role *streber* successfully ${add ? "added to" : "removed from"} ${member.user.tag}.`);
|
||||
} catch (error) {
|
||||
console.error(`error while ${add ? "added to" : "removed from"} RoleMention:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
async validMsg(id: string) {
|
||||
return id === config.discord.rolesMsg;
|
||||
}
|
||||
|
||||
async getUser(guild: Guild, user: User | PartialUser) {
|
||||
try {
|
||||
return await guild.members.fetch(user.id);
|
||||
} catch (error) {
|
||||
console.error("error fetching user:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/actions/support/support.components.ts
Normal file
18
src/actions/support/support.components.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createEmbed } from "actions/customMessage/customMessage.components";
|
||||
|
||||
const kofiTitle = "ko-fi";
|
||||
const kofiLink = "[ko-fi.com/avocadi](https://ko-fi.com/avocadi)";
|
||||
const kofiDesc = "gerne kannst du uns eine kleine spende ueber ko-fi zukommen lassen!";
|
||||
|
||||
const disboardTitle = "disboard";
|
||||
const disboardLink = "[disboard.org/de/server/1316153371899592774](https://disboard.org/de/server/1316153371899592774)";
|
||||
const disboardDesc = "hier kannst du eine bewertung schreiben <3";
|
||||
|
||||
const discadiaTitle = "discadia";
|
||||
const discadiaLink = "[discadia.com/server/avocadi](https://discadia.com/server/avocadi/)";
|
||||
const discadiaDesc = "du moechtest fuer uns voten? dann klicke auf den link :)";
|
||||
|
||||
export const supportContent = createEmbed("support", (`${kofiTitle}: ${kofiLink}\n${disboardTitle}: ${disboardLink}\n${discadiaTitle}: ${discadiaLink}`));
|
||||
export const kofiContent = createEmbed(kofiTitle, (`${kofiDesc}\n${kofiLink}`));
|
||||
export const disboardContent = createEmbed(disboardTitle, (`${disboardDesc}\n${disboardLink}`));
|
||||
export const discadiaContent = createEmbed(discadiaTitle, (`${discadiaDesc}\n${discadiaLink}`));
|
||||
93
src/actions/support/support.service.ts
Normal file
93
src/actions/support/support.service.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import {
|
||||
kofiContent,
|
||||
supportContent,
|
||||
discadiaContent,
|
||||
disboardContent,
|
||||
} from "./support.components.ts";
|
||||
import type {
|
||||
CacheType,
|
||||
Interaction,
|
||||
ChatInputCommandInteraction,
|
||||
} from "discord.js";
|
||||
import { Commands, type CommandsType } from "commands/index.ts";
|
||||
|
||||
export class SupportService {
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await this.handleChatInputCommand(interaction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async handleChatInputCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
const commandName = interaction.commandName as CommandsType;
|
||||
switch (commandName) {
|
||||
case Commands.Enum.support:
|
||||
await this.supportCommand(interaction);
|
||||
return;
|
||||
case Commands.Enum.kofi:
|
||||
await this.kofiCommand(interaction);
|
||||
return;
|
||||
case Commands.Enum.disboard:
|
||||
await this.disboardCommand(interaction);
|
||||
return;
|
||||
case Commands.Enum.discadia:
|
||||
await this.discadiaCommand(interaction);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
async supportCommand(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
console.log("supportCommand");
|
||||
try {
|
||||
await interaction.reply({
|
||||
embeds: [supportContent],
|
||||
ephemeral: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("error while sending supportCommand msg:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async kofiCommand(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
console.log("kofiCommand");
|
||||
try {
|
||||
await interaction.reply({
|
||||
embeds: [kofiContent],
|
||||
ephemeral: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("error while sending kofiCommand msg:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async disboardCommand(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
console.log("disboardCommand");
|
||||
try {
|
||||
await interaction.reply({
|
||||
embeds: [disboardContent],
|
||||
ephemeral: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("error while sending disboardCommand msg:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async discadiaCommand(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
console.log("discadiaCommand");
|
||||
try {
|
||||
await interaction.reply({
|
||||
embeds: [discadiaContent],
|
||||
ephemeral: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("error while sending discadiaCommand msg:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export class WaterMeService {
|
||||
async isThirsty() {
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
const channel = channels.cache.get(config.discord.channelIdBot);
|
||||
|
||||
if (
|
||||
channel?.isTextBased &&
|
||||
@@ -1,10 +1,10 @@
|
||||
import { CronJob } from "cron";
|
||||
import { WaterMeService } from "services/water-me.service";
|
||||
import { WaterMeService } from "actions/waterMe/waterMe.service";
|
||||
|
||||
const waterMeService = new WaterMeService();
|
||||
|
||||
/*
|
||||
new CronJob(
|
||||
"0 0 0 */1 * *", // cronTime
|
||||
"0 0 20 * * *", // cronTime
|
||||
async () => {
|
||||
console.log("isThirsty()");
|
||||
await waterMeService.isThirsty();
|
||||
@@ -12,5 +12,5 @@ new CronJob(
|
||||
null, // onComplete
|
||||
true, // start
|
||||
"Europe/Berlin", // timeZone
|
||||
);
|
||||
// job.start() is optional here because of the fourth parameter set to true.
|
||||
);*/
|
||||
// job.start() is optional here because of the fourth parameter set to true.
|
||||
123
src/commands/index.ts
Normal file
123
src/commands/index.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { SlashCommandBuilder, userMention } from "discord.js";
|
||||
import { z } from "zod";
|
||||
|
||||
export const Commands = z.enum(["giessen", "medikamente", "hilfe", "support", "kofi", "disboard", "discadia", "accept", "welcome", "embed", "message", "reminder", "version"]);
|
||||
|
||||
export const CommandsMeta: Record<z.output<typeof Commands>, { description: string }> = {
|
||||
giessen: {
|
||||
description: "giess mich mit etwas wasser :3"
|
||||
},
|
||||
medikamente: {
|
||||
description: "ich erinnere dich gerne daran, deine medikamente zu nehmen! :)"
|
||||
},
|
||||
hilfe: {
|
||||
description: "ich schreibe dir auf, was du alles mit mir machen kannst :)"
|
||||
},
|
||||
support: {
|
||||
description: "unterstuetze uns! link zu unserem ko-fi, disboard und discardia c:"
|
||||
},
|
||||
kofi: {
|
||||
description: "link zu unserem ko-fi (spendenportal):"
|
||||
},
|
||||
disboard: {
|
||||
description: "link zu disboard, hier kannst du uns bewerten!"
|
||||
},
|
||||
discadia: {
|
||||
description: "link zu discadia, hier kannst du fuer uns voten!"
|
||||
},
|
||||
accept: {
|
||||
description: "admin use only"
|
||||
},
|
||||
welcome: {
|
||||
description: "admin use only"
|
||||
},
|
||||
embed: {
|
||||
description: "admin use only"
|
||||
},
|
||||
message: {
|
||||
description: "admin use only"
|
||||
},
|
||||
reminder: {
|
||||
description: "admin use only"
|
||||
},
|
||||
version: {
|
||||
description: "admin use only"
|
||||
}
|
||||
}
|
||||
|
||||
export type CommandsType = z.output<typeof Commands>;
|
||||
|
||||
export default function getCommands() {
|
||||
const commands = [
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.giessen)
|
||||
.setDescription(CommandsMeta.giessen.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.medikamente)
|
||||
.setDescription(CommandsMeta.medikamente.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.hilfe)
|
||||
.setDescription(CommandsMeta.hilfe.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.support)
|
||||
.setDescription(CommandsMeta.support.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.kofi)
|
||||
.setDescription(CommandsMeta.kofi.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.disboard)
|
||||
.setDescription(CommandsMeta.disboard.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.discadia)
|
||||
.setDescription(CommandsMeta.discadia.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.accept)
|
||||
.setDescription(CommandsMeta.accept.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('input')
|
||||
.setDescription('input for bot')
|
||||
.setRequired(true)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.welcome)
|
||||
.setDescription(CommandsMeta.welcome.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('input')
|
||||
.setDescription('input for bot')
|
||||
.setRequired(true)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.embed)
|
||||
.setDescription(CommandsMeta.embed.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('title')
|
||||
.setDescription('title')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('description')
|
||||
.setDescription('description')
|
||||
.setRequired(true))
|
||||
.addBooleanOption(option =>
|
||||
option.setName('timestamp')
|
||||
.setDescription('timestamp bool')
|
||||
.setRequired(false)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.message)
|
||||
.setDescription(CommandsMeta.message.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('input')
|
||||
.setDescription('input for bot')
|
||||
.setRequired(true)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.reminder)
|
||||
.setDescription(CommandsMeta.reminder.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('input')
|
||||
.setDescription('input for bot')
|
||||
.setRequired(true)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.version)
|
||||
.setDescription(CommandsMeta.version.description),
|
||||
|
||||
].map((command) => command.toJSON());
|
||||
|
||||
return commands;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import { z } from "zod";
|
||||
|
||||
export const Commands = z.enum(["water-me"]);
|
||||
export type CommandsType = z.output<typeof Commands>;
|
||||
|
||||
export default function getCommands() {
|
||||
const commands = [
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum["water-me"])
|
||||
.setDescription("giess mich mit etwas wasser :3"),
|
||||
].map((command) => command.toJSON());
|
||||
|
||||
return commands;
|
||||
}
|
||||
24
src/components/index.ts
Normal file
24
src/components/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { EmbedBuilder } from "discord.js";
|
||||
import { Commands, CommandsMeta } from "commands";
|
||||
|
||||
export default function getEmbed() { // channel.send({ embeds: [exampleEmbed] });
|
||||
const exampleEmbed = new EmbedBuilder()
|
||||
.setColor(0x0099FF)
|
||||
.setTitle('Some title')
|
||||
.setURL('https://discord.js.org/')
|
||||
.setAuthor({ name: 'Some name', iconURL: 'https://i.imgur.com/AfFp7pu.png', url: 'https://discord.js.org' })
|
||||
.setDescription('Some description here')
|
||||
.setThumbnail('https://i.imgur.com/AfFp7pu.png')
|
||||
.addFields(
|
||||
{ name: Commands.Enum.giessen, value: CommandsMeta.giessen.description },
|
||||
{ name: Commands.Enum.medikamente, value: CommandsMeta.medikamente.description },
|
||||
{ name: Commands.Enum.hilfe, value: CommandsMeta.hilfe.description },
|
||||
{ name: '\u200B', value: '\u200B' },
|
||||
{ name: 'Inline field title', value: 'Some value here', inline: true },
|
||||
)
|
||||
.addFields({ name: 'Inline field title', value: 'Some value here', inline: true })
|
||||
.setImage('https://i.imgur.com/AfFp7pu.png')
|
||||
.setTimestamp()
|
||||
.setFooter({ text: 'Some footer text here', iconURL: 'https://i.imgur.com/AfFp7pu.png' });
|
||||
return exampleEmbed;
|
||||
}
|
||||
@@ -1,12 +1,40 @@
|
||||
const test = true;
|
||||
|
||||
export default {
|
||||
discord: {
|
||||
channelId:
|
||||
(test
|
||||
? process.env.DISCORD_TEST_CHANNEL_ID
|
||||
: process.env.DISCORD_CHANNEL_ID) || "",
|
||||
version: 250602.10,
|
||||
// avocadi
|
||||
serverID: process.env.DISCORD_SERVER_ID || "",
|
||||
// texxt channel
|
||||
channelIdBot: process.env.DISCORD_CHANNEL_ID_BOT || "",
|
||||
channelIdFeedback: process.env.DISCORD_CHANNEL_ID_FEEDBACK || "",
|
||||
channelIdNotification: process.env.DISCORD_CHANNEL_ID_NOTIFICATION || "",
|
||||
channelIdWelcome: process.env.DISCORD_CHANNEL_ID_WELCOME || "",
|
||||
channelIdRules: process.env.DISCORD_CHANNEL_ID_RULE || "",
|
||||
channelIdNews: process.env.DISCORD_CHANNEL_ID_NEWS || "",
|
||||
channelIdIntroduction: process.env.DISCORD_CHANNEL_ID_INTRODUCTION || "",
|
||||
channelIdOffTopic: process.env.DISCORD_CHANNEL_ID_OFF_TOPIC || "",
|
||||
channelIdHelp: process.env.DISCORD_CHANNEL_ID_HELP || "",
|
||||
channelIdPomodoro: process.env.DISCORD_CHANNEL_ID_POMODORO || "",
|
||||
// voice channel
|
||||
vchannelIdForTwo: process.env.DISCORD_VCHANNEL_ID_FOR_TWO || "",
|
||||
vchannelIdForThree: process.env.DISCORD_VCHANNEL_ID_FOR_THREE || "",
|
||||
vchannelIdForFour: process.env.DISCORD_VCHANNEL_ID_FOR_FOUR || "",
|
||||
vchannelIdForGroup: process.env.DISCORD_VCHANNEL_ID_FOR_GROUP || "",
|
||||
vchannelIdPomodoro25: process.env.DISCORD_VCHANNEL_ID_POMODORO_25_5 || "",
|
||||
vchannelIdPomodoro50: process.env.DISCORD_VCHANNEL_ID_POMODORO_50_10 || "",
|
||||
// roles
|
||||
roleStudy: process.env.PEOPLE || "",
|
||||
roleMod: process.env.MOD || "",
|
||||
roleAdmin: process.env.ADMIN || "",
|
||||
roleMention: process.env.MENTION || "",
|
||||
|
||||
rolesMsg: process.env.ROLES_NOTIFICATION_MSG || "",
|
||||
rolesMsgTest: process.env.ROLES_MSG_TEST || "",
|
||||
// other
|
||||
applicationId: process.env.DISCORD_APPLICATION_ID || "",
|
||||
token: process.env.DISCORD_TOKEN || "",
|
||||
|
||||
botId: process.env.BOT_ID || "",
|
||||
myId: process.env.MY_ID || "",
|
||||
enricoId: process.env.ENRICO_ID || "",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import { Commands, type CommandsType } from "components/commands.component";
|
||||
import type {
|
||||
ButtonInteraction,
|
||||
CacheType,
|
||||
ChatInputCommandInteraction,
|
||||
Interaction,
|
||||
ModalSubmitInteraction,
|
||||
} from "discord.js";
|
||||
import client from "lib/client";
|
||||
import EventEmitter from "node:events";
|
||||
import DiscordService from "services/discord.service";
|
||||
import { WaterMeService } from "services/water-me.service";
|
||||
import config from "config";
|
||||
|
||||
export default class DiscordController extends EventEmitter {
|
||||
private discordService!: DiscordService;
|
||||
waterMeService: WaterMeService;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.discordService = new DiscordService();
|
||||
this.waterMeService = new WaterMeService();
|
||||
// log when running
|
||||
client.once("ready", async () => {
|
||||
console.log("hello :)");
|
||||
|
||||
/*const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({
|
||||
content: "bin gerade erst aufgewacht :o",
|
||||
});
|
||||
}*/
|
||||
});
|
||||
// listen for interactions
|
||||
client.on("interactionCreate", this.handleInteraction.bind(this));
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.discordService.init();
|
||||
}
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
if (interaction.isModalSubmit()) {
|
||||
await this.handleModalSubmit(interaction);
|
||||
return;
|
||||
}
|
||||
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await this.handleChatInputCommand(interaction);
|
||||
return;
|
||||
}
|
||||
if (interaction.isButton()) {
|
||||
await this.handleButton(interaction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async handleButton(interaction: ButtonInteraction<CacheType>) {
|
||||
const { customId } = interaction;
|
||||
console.log(interaction.customId);
|
||||
|
||||
if (customId === "moreWater") {
|
||||
await this.waterMeService.handleInteraction(interaction);
|
||||
}
|
||||
}
|
||||
|
||||
async handleChatInputCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
const commandName = interaction.commandName as CommandsType;
|
||||
|
||||
// add commands
|
||||
switch (commandName) {
|
||||
case Commands.Enum["water-me"]:
|
||||
await this.waterMeService.handleInteraction(interaction);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// wenn neues fenster durch buttonclick or so
|
||||
async handleModalSubmit(interaction: ModalSubmitInteraction<CacheType>) {
|
||||
const { customId } = interaction;
|
||||
|
||||
switch (customId) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/db/index.ts
Normal file
36
src/db/index.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { drizzle } from "drizzle-orm/bun-sqlite";
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { usersTable } from './schema'; // Importiere die Tabelle hier
|
||||
|
||||
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
||||
const dbPath = process.env.DB_FILE_NAME!;
|
||||
const dbDir = path.dirname(dbPath);
|
||||
|
||||
// Erstelle das Verzeichnis, wenn es noch nicht existiert
|
||||
if (!fs.existsSync(dbDir)) {
|
||||
fs.mkdirSync(dbDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Wenn die Datenbankdatei nicht existiert, dann erstelle sie
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
fs.writeFileSync(dbPath, ''); // Leere Datei erstellen
|
||||
}
|
||||
|
||||
// Datenbankverbindung herstellen
|
||||
const client = new Database(dbPath);
|
||||
|
||||
// Stelle sicher, dass die Tabelle existiert
|
||||
client.run(`
|
||||
CREATE TABLE IF NOT EXISTS users_table (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
discord_id INTEGER NOT NULL UNIQUE,
|
||||
join_streak INTEGER,
|
||||
last_joined_at INTEGER,
|
||||
took_medication_today INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
`);
|
||||
|
||||
export const db = drizzle(client);
|
||||
10
src/db/schema.ts
Normal file
10
src/db/schema.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
|
||||
export const usersTable = sqliteTable("users_table", {
|
||||
id: integer().primaryKey({ autoIncrement: true }),
|
||||
name: text().notNull(),
|
||||
discord_id: integer().notNull().unique(),
|
||||
join_streak: integer(),
|
||||
last_joined_at: integer({ mode: "timestamp" }),
|
||||
took_medication_today: integer().notNull().default(0),
|
||||
});
|
||||
239
src/discord.controller.ts
Normal file
239
src/discord.controller.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import { Commands, type CommandsType } from "commands";
|
||||
import {
|
||||
ChannelType,
|
||||
Client,
|
||||
Events,
|
||||
IntentsBitField,
|
||||
type VoiceState,
|
||||
type ButtonInteraction,
|
||||
type CacheType,
|
||||
type ChatInputCommandInteraction,
|
||||
type Interaction,
|
||||
type ModalSubmitInteraction,
|
||||
} from "discord.js";
|
||||
import client from "lib/client";
|
||||
import EventEmitter from "node:events";
|
||||
import DiscordService from "discord.service";
|
||||
import { WaterMeService } from "actions/waterMe/waterMe.service";
|
||||
import { MedicationService } from "actions/medication/medication.service";
|
||||
import { HelpService } from "actions/help/help.service";
|
||||
import { SupportService } from "actions/support/support.service";
|
||||
import { GreetingService } from "actions/greeting/greeting.service";
|
||||
import { ActivityService } from "actions/activity/activity.service";
|
||||
import { DmService } from "actions/dm/dm.service";
|
||||
import { CustomMessageService } from "actions/customMessage/customMessage.service";
|
||||
import { DynamicVChannelService } from "actions/dynamicVChannel/dynamicVChannel.service";
|
||||
import { DebugService } from "actions/debug/debug.service";
|
||||
import { ReactRolesService } from "actions/reactRole/reactRoles.service";
|
||||
import config from "config";
|
||||
|
||||
export default class DiscordController extends EventEmitter {
|
||||
private discordService: DiscordService;
|
||||
private waterMeService: WaterMeService;
|
||||
private greetingService: GreetingService;
|
||||
private medicationService: MedicationService;
|
||||
private helpService: HelpService;
|
||||
private supportService: SupportService;
|
||||
private activityService: ActivityService;
|
||||
private dmService: DmService;
|
||||
private customMessageService: CustomMessageService;
|
||||
private dynamicVChannelService: DynamicVChannelService;
|
||||
private debugService: DebugService;
|
||||
|
||||
private channelListeners = new Map();
|
||||
private reactRolesService: ReactRolesService;
|
||||
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
let channelListeners = new Map();
|
||||
this.discordService = new DiscordService();
|
||||
this.waterMeService = new WaterMeService();
|
||||
this.greetingService = new GreetingService();
|
||||
this.medicationService = new MedicationService();
|
||||
this.helpService = new HelpService();
|
||||
this.supportService = new SupportService();
|
||||
this.activityService = new ActivityService();
|
||||
this.dmService = new DmService();
|
||||
this.customMessageService = new CustomMessageService();
|
||||
this.dynamicVChannelService = new DynamicVChannelService();
|
||||
this.debugService = new DebugService();
|
||||
this.reactRolesService = new ReactRolesService();
|
||||
|
||||
client.on("messageReactionAdd", async (reaction, user) => {
|
||||
await this.reactRolesService.roleMention(reaction, user, true);
|
||||
});
|
||||
|
||||
client.on("messageReactionRemove", async (reaction, user) => {
|
||||
await this.reactRolesService.roleMention(reaction, user, false);
|
||||
});
|
||||
|
||||
// log when running
|
||||
client.once("ready", async () => {
|
||||
await this.setActivity();
|
||||
console.log("ready");
|
||||
});
|
||||
|
||||
// listen for interactions
|
||||
client.on("interactionCreate", this.handleInteraction.bind(this));
|
||||
|
||||
client.on("messageCreate", async (message) => {
|
||||
console.log(message.id);
|
||||
if (message.channel.type === ChannelType.DM) {
|
||||
console.log("got msg");
|
||||
await this.dmService.forward(message);
|
||||
}
|
||||
});
|
||||
|
||||
client.on("guildMemberAdd", async (member) => {
|
||||
await this.greetingService.welcome(member);
|
||||
});
|
||||
|
||||
client.on(
|
||||
Events.VoiceStateUpdate,
|
||||
async (oldState: VoiceState, newState: VoiceState) => {
|
||||
// check if user joined a vc
|
||||
if (
|
||||
(!oldState.channelId && newState.channelId) ||
|
||||
oldState.channelId !== newState.channelId
|
||||
) {
|
||||
// check if right vc
|
||||
if (
|
||||
newState.channelId === config.discord.vchannelIdForTwo ||
|
||||
newState.channelId === config.discord.vchannelIdForThree ||
|
||||
newState.channelId === config.discord.vchannelIdForFour ||
|
||||
newState.channelId === config.discord.vchannelIdForGroup
|
||||
) {
|
||||
const channel = newState.channel;
|
||||
if (!channel) {
|
||||
console.error("channel not found");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const newChannel = await this.dynamicVChannelService.createVChannel(
|
||||
newState,
|
||||
channel,
|
||||
);
|
||||
// move user in new channel
|
||||
await newState.setChannel(newChannel);
|
||||
// create specific listener for channel
|
||||
const channelListener = async (
|
||||
oldState: VoiceState,
|
||||
newState: VoiceState,
|
||||
) => {
|
||||
channelListeners =
|
||||
await this.dynamicVChannelService.deleteVChannel(
|
||||
oldState,
|
||||
newState,
|
||||
newChannel,
|
||||
channelListeners,
|
||||
channelListener,
|
||||
);
|
||||
};
|
||||
// save listener in map
|
||||
channelListeners.set(newChannel.id, channelListener);
|
||||
// add listener
|
||||
client.on(Events.VoiceStateUpdate, channelListener);
|
||||
} catch (error) {
|
||||
console.error("error while duplicating channel", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
console.log(`----------------\nversion ${config.discord.version}\n----------------`);
|
||||
}
|
||||
|
||||
async setActivity() {
|
||||
client.user?.setActivity(":3", { type: 4 });
|
||||
console.log("set activity");
|
||||
client.user?.setPresence({
|
||||
status: "online",
|
||||
});
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.discordService.init();
|
||||
}
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
if (interaction.isModalSubmit()) {
|
||||
await this.handleModalSubmit(interaction);
|
||||
return;
|
||||
}
|
||||
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await this.handleChatInputCommand(interaction);
|
||||
return;
|
||||
}
|
||||
if (interaction.isButton()) {
|
||||
await this.handleButton(interaction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async handleButton(interaction: ButtonInteraction<CacheType>) {
|
||||
const { customId } = interaction;
|
||||
console.log(interaction.customId);
|
||||
|
||||
if (customId.toLowerCase().includes("moreWater")) {
|
||||
await this.waterMeService.handleInteraction(interaction);
|
||||
}
|
||||
if (customId.toLowerCase().includes("medication")) {
|
||||
await this.medicationService.handleInteraction(interaction);
|
||||
}
|
||||
}
|
||||
|
||||
async handleChatInputCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
const commandName = interaction.commandName as CommandsType;
|
||||
|
||||
// add commands
|
||||
switch (commandName) {
|
||||
case Commands.Enum.giessen:
|
||||
await this.waterMeService.handleInteraction(interaction); // zu chatinputcommand wechseln
|
||||
return;
|
||||
case Commands.Enum.medikamente:
|
||||
await this.medicationService.handleChatInputCommand(interaction);
|
||||
return;
|
||||
case Commands.Enum.hilfe:
|
||||
await this.helpService.handleInteraction(interaction); // zu chatinputcommand wechseln
|
||||
return;
|
||||
case Commands.Enum.support:
|
||||
case Commands.Enum.kofi:
|
||||
case Commands.Enum.disboard:
|
||||
case Commands.Enum.discadia:
|
||||
await this.supportService.handleInteraction(interaction);
|
||||
return;
|
||||
case Commands.Enum.accept:
|
||||
await this.greetingService.handleChatInputCommand(interaction);
|
||||
return;
|
||||
case Commands.Enum.welcome:
|
||||
await this.greetingService.handleChatInputCommand(interaction);
|
||||
return;
|
||||
case Commands.Enum.embed:
|
||||
case Commands.Enum.message:
|
||||
await this.customMessageService.handleChatInputCommand(interaction);
|
||||
return;
|
||||
case Commands.Enum.reminder:
|
||||
await this.greetingService.handleChatInputCommand(interaction);
|
||||
return;
|
||||
case Commands.Enum.version:
|
||||
await this.debugService.handleChatInputCommand(interaction);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// wenn neues fenster durch buttonclick or so
|
||||
async handleModalSubmit(interaction: ModalSubmitInteraction<CacheType>) {
|
||||
const { customId } = interaction;
|
||||
|
||||
switch (customId) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Routes } from "discord.js";
|
||||
import { REST } from "@discordjs/rest";
|
||||
import config from "config";
|
||||
import getCommands from "components/commands.component";
|
||||
import getCommands from "commands";
|
||||
|
||||
export default class DiscordService {
|
||||
rest: REST;
|
||||
17
src/index.ts
17
src/index.ts
@@ -1,11 +1,14 @@
|
||||
import "tasks/water-me.task";
|
||||
import "tasks/greeting.task";
|
||||
import "tasks/drink.task";
|
||||
import DiscordController from "controllers/discord.controller";
|
||||
import "actions/waterMe/waterMe.task";
|
||||
import "actions/greeting/greeting.task";
|
||||
import "actions/medication/medication.task";
|
||||
import "actions/drink/drink.task";
|
||||
import DiscordController from "discord.controller";
|
||||
import PomodoroController from "actions/pomodoro/pomodoro.controller";
|
||||
|
||||
// = main file
|
||||
import "dotenv/config";
|
||||
|
||||
// bootstrap application
|
||||
|
||||
const discordController = new DiscordController();
|
||||
discordController.init();
|
||||
const pomodoroController = new PomodoroController();
|
||||
|
||||
discordController.init();
|
||||
@@ -1,7 +1,21 @@
|
||||
import config from "config";
|
||||
import { Client, IntentsBitField } from "discord.js";
|
||||
import { Client, GatewayIntentBits, Partials, ChannelType, Events, IntentsBitField } from "discord.js";
|
||||
|
||||
const client = new Client({ intents: [IntentsBitField.Flags.Guilds] });
|
||||
const client = new Client({
|
||||
intents: [IntentsBitField.Flags.Guilds,
|
||||
IntentsBitField.Flags.GuildMembers,
|
||||
IntentsBitField.Flags.GuildModeration,
|
||||
IntentsBitField.Flags.GuildMessages,
|
||||
IntentsBitField.Flags.GuildMessageReactions,
|
||||
IntentsBitField.Flags.GuildMessagePolls,
|
||||
IntentsBitField.Flags.GuildVoiceStates,
|
||||
IntentsBitField.Flags.MessageContent,
|
||||
IntentsBitField.Flags.DirectMessages,
|
||||
IntentsBitField.Flags.DirectMessageReactions,
|
||||
IntentsBitField.Flags.DirectMessageTyping,
|
||||
IntentsBitField.Flags.DirectMessagePolls,],
|
||||
partials: [Partials.Channel, Partials.Message, Partials.Reaction]
|
||||
});
|
||||
|
||||
await client.login(config.discord.token);
|
||||
|
||||
|
||||
13
src/lib/utils.test.ts
Normal file
13
src/lib/utils.test.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { getRandomInt } from "./utils.ts";
|
||||
|
||||
describe("utils", () => {
|
||||
it("gets a random int", () => {
|
||||
const randomInt = getRandomInt(0, 10);
|
||||
|
||||
expect(randomInt).toBeDefined();
|
||||
expect(randomInt).toBeNumber();
|
||||
expect(randomInt).toBeLessThanOrEqual(10);
|
||||
expect(randomInt).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
13
src/permissions/index.ts
Normal file
13
src/permissions/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import config from "config";
|
||||
import { GuildMember, GuildMemberRoleManager, type APIInteractionGuildMember } from "discord.js";
|
||||
|
||||
export async function checkPermission(member: GuildMember | APIInteractionGuildMember | null) {
|
||||
let permission = false;
|
||||
if (member?.roles instanceof GuildMemberRoleManager) {
|
||||
if (member.roles.cache.has(config.discord.roleAdmin) || member.roles.cache.has(config.discord.roleMod)) {
|
||||
permission = true;
|
||||
}
|
||||
}
|
||||
console.log("user permission == " + permission);
|
||||
return permission;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
|
||||
export class GreetingService {
|
||||
private greetContent = ["HALLOOOO"] as const;
|
||||
private sleepContent = ["zzzzZZ..", "*schnarch*"] as const;
|
||||
getContent(asleep: boolean) {
|
||||
if (asleep) {
|
||||
return this.sleepContent[getRandomInt(0, this.sleepContent.length - 1)];
|
||||
}
|
||||
|
||||
return this.greetContent[getRandomInt(0, this.greetContent.length - 1)];
|
||||
}
|
||||
|
||||
async greet() {
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: "HALLOOOO" });
|
||||
}
|
||||
}
|
||||
|
||||
async sleep() {
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: this.getContent(true) });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { CronJob } from "cron";
|
||||
import { GreetingService } from "services/greeting.service";
|
||||
|
||||
const greetingService = new GreetingService();
|
||||
|
||||
new CronJob(
|
||||
"0 */30 7-23 * * *", // cronTime
|
||||
async () => {
|
||||
console.log("called greeting");
|
||||
await greetingService.greet();
|
||||
}, // onTick
|
||||
null, // onComplete
|
||||
true, // start
|
||||
"Europe/Berlin", // timeZone
|
||||
);
|
||||
|
||||
new CronJob(
|
||||
"0 */30 0-6 * * *", // cronTime
|
||||
async () => {
|
||||
console.log("called greeting");
|
||||
await greetingService.sleep();
|
||||
}, // onTick
|
||||
null, // onComplete
|
||||
true, // start
|
||||
"Europe/Berlin", // timeZone
|
||||
);
|
||||
// job.start() is optional here because of the fourth parameter set to true.
|
||||
Reference in New Issue
Block a user