Compare commits
52 Commits
b46dc8d3ae
...
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 |
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
|
||||
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;
|
||||
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": {}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,13 @@
|
||||
"when": 1735431085265,
|
||||
"tag": "0000_needy_nightshade",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "6",
|
||||
"when": 1736688734822,
|
||||
"tag": "0001_dusty_wolverine",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
mydb.sqlite
BIN
mydb.sqlite
Binary file not shown.
@@ -3,10 +3,12 @@
|
||||
"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": "^1.1.14",
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"drizzle-kit": "^0.30.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -14,7 +16,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordjs/rest": "^2.4.0",
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"cron": "^3.3.1",
|
||||
"discord.js": "^14.16.3",
|
||||
"dotenv": "^16.4.7",
|
||||
|
||||
0
src/actions/activity/activity.components.ts
Normal file
0
src/actions/activity/activity.components.ts
Normal file
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;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +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)];
|
||||
}
|
||||
@@ -1,21 +1,140 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import { greetContent, sleepContent } from "./greeting.components.ts";
|
||||
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 {
|
||||
async customMessage() {
|
||||
console.log("custom message");
|
||||
dmService: DmService;
|
||||
|
||||
const channels = client.channels;
|
||||
constructor() {
|
||||
this.dmService = new DmService();
|
||||
}
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
async handleInteraction(
|
||||
interaction: Interaction<CacheType>
|
||||
) {
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: " " });
|
||||
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");
|
||||
@@ -25,13 +144,14 @@ export class GreetingService {
|
||||
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
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");
|
||||
@@ -41,7 +161,7 @@ export class GreetingService {
|
||||
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
const channel = channels.cache.get(config.discord.channelIdOffTopic);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: this.getContent(true) });
|
||||
@@ -50,14 +170,15 @@ export class GreetingService {
|
||||
|
||||
async newYear() {
|
||||
client.user?.setActivity("frohes neues! :)", { type: 4 });
|
||||
console.log("set activity: happy new Year");
|
||||
console.log("set activity: happy new year");
|
||||
client.user?.setPresence({
|
||||
status: "online",
|
||||
});
|
||||
|
||||
// unused
|
||||
/*const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
const channel = channels.cache.get(config.discord.channelIdOffTopic);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: "frohes neues! @everyone" });
|
||||
@@ -70,4 +191,149 @@ export class GreetingService {
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CronJob } from "cron";
|
||||
import { GreetingService } from "actions/greeting/greeting.service";
|
||||
|
||||
const greetingService = new GreetingService();
|
||||
|
||||
/*
|
||||
new CronJob(
|
||||
"0 30 6 * * *", // cronTime
|
||||
async () => {
|
||||
@@ -24,7 +24,7 @@ new CronJob(
|
||||
null,
|
||||
true,
|
||||
"Europe/Berlin",
|
||||
);
|
||||
);*/
|
||||
|
||||
new CronJob(
|
||||
"0 0 0 1 1 *",
|
||||
|
||||
@@ -2,18 +2,22 @@ import { Commands, CommandsMeta } from 'commands';
|
||||
import { AttachmentBuilder, EmbedBuilder } from 'discord.js';
|
||||
|
||||
export default function createEmbed() { // ({ embeds: [exampleEmbed] })
|
||||
console.log("createEmbed()");
|
||||
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/moriese/avocadi-bot' })
|
||||
.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.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' });
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { CacheType, Interaction } from "discord.js";
|
||||
import createEmbed from "./help.components";
|
||||
|
||||
export class HelpService {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
exampleEmbed: any;
|
||||
|
||||
constructor() {
|
||||
@@ -14,6 +15,7 @@ export class HelpService {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await interaction.reply({
|
||||
embeds: [this.exampleEmbed],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export class MedicationService {
|
||||
async askMedication() {
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
const channel = channels.cache.get(config.discord.channelIdBot);
|
||||
|
||||
// funkt noch nicht, beides
|
||||
|
||||
@@ -50,7 +50,7 @@ export class MedicationService {
|
||||
}
|
||||
|
||||
getMedication() {
|
||||
throw new Error("Method not implemented.");
|
||||
// todo
|
||||
}
|
||||
|
||||
setMedication() {
|
||||
@@ -104,7 +104,7 @@ export class MedicationService {
|
||||
|
||||
const tookMedication = interaction.customId === "yesMedication";
|
||||
|
||||
interaction.reply({
|
||||
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? :)",
|
||||
@@ -115,6 +115,7 @@ export class MedicationService {
|
||||
await interaction.reply({
|
||||
content:
|
||||
"es gab einen fehler beim verarbeiten deiner anfrage :( versuch es bitte spaeter nochmal, oki? c:",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -134,8 +135,7 @@ export class MedicationService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode, um die Benutzerdaten in die Datenbank zu schreiben.
|
||||
/**, 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
|
||||
@@ -155,9 +155,9 @@ export class MedicationService {
|
||||
took_medication_today: Number(tookMedication),
|
||||
}); */
|
||||
|
||||
console.log(`Benutzer mit ID ${id} wurde in der Datenbank gespeichert.`);
|
||||
console.log(`user with id ${id} saved`);
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Speichern in der Datenbank:", error);
|
||||
console.error("error while saving in db:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,11 +192,11 @@ export class MedicationService {
|
||||
console.log(`ID für Discord-ID ${discordId} gefunden: ${result[0].id}`);
|
||||
return result[0].id;
|
||||
}
|
||||
console.log(`Keine ID für Discord-ID ${discordId} gefunden.`);
|
||||
console.log(`no id for discordId ${discordId} found`);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Fehler beim Abrufen der ID für Discord-ID ${discordId}:`,
|
||||
`error while calling id for discordId ${discordId}:`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
@@ -213,7 +213,7 @@ export class MedicationService {
|
||||
}
|
||||
|
||||
console.log(
|
||||
`found no entry for discordID ${discordId}. creating new entry.`,
|
||||
`found no entry for discordID ${discordId}. creating new entry`,
|
||||
);
|
||||
|
||||
const result = await db
|
||||
|
||||
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 &&
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CronJob } from "cron";
|
||||
import { WaterMeService } from "actions/waterMe/waterMe.service";
|
||||
|
||||
const waterMeService = new WaterMeService();
|
||||
|
||||
/*
|
||||
new CronJob(
|
||||
"0 0 20 * * *", // cronTime
|
||||
async () => {
|
||||
@@ -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.
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import { SlashCommandBuilder, userMention } from "discord.js";
|
||||
import { z } from "zod";
|
||||
|
||||
export const Commands = z.enum(["giessen", "medikamente", "hilfe"]);
|
||||
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: {
|
||||
@@ -12,6 +12,36 @@ export const CommandsMeta: Record<z.output<typeof Commands>, { description: stri
|
||||
},
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +58,65 @@ export default function getCommands() {
|
||||
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,12 +1,40 @@
|
||||
const test = false;
|
||||
|
||||
export default {
|
||||
discord: {
|
||||
channelId:
|
||||
(test
|
||||
? process.env.DISCORD_TEST_CHANNEL_ID
|
||||
: process.env.DISCORD_CHANNEL_ID_BOT) || "",
|
||||
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,118 +0,0 @@
|
||||
import { Commands, type CommandsType } from "commands";
|
||||
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 config from "config";
|
||||
import { WaterMeService } from "actions/waterMe/waterMe.service";
|
||||
import { MedicationService } from "actions/medication/medication.service";
|
||||
import { HelpService } from "actions/help/help.service";
|
||||
import { custom } from "zod";
|
||||
|
||||
export default class DiscordController extends EventEmitter {
|
||||
private discordService!: DiscordService;
|
||||
waterMeService: WaterMeService;
|
||||
medicationService: MedicationService;
|
||||
helpService: HelpService;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.discordService = new DiscordService();
|
||||
this.waterMeService = new WaterMeService();
|
||||
this.medicationService = new MedicationService();
|
||||
this.helpService = new HelpService();
|
||||
// log when running
|
||||
client.once("ready", async () => {
|
||||
this.setActivity();
|
||||
|
||||
/*const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelId);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({
|
||||
content: "bin wieder da :o war kurz weg :3",
|
||||
});
|
||||
}*/
|
||||
});
|
||||
|
||||
// listen for interactions
|
||||
client.on("interactionCreate", this.handleInteraction.bind(this));
|
||||
}
|
||||
|
||||
async setActivity() {
|
||||
client.user?.setActivity("frohes neues :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);
|
||||
return;
|
||||
case Commands.Enum.medikamente:
|
||||
await this.medicationService.handleInteraction(interaction);
|
||||
return;
|
||||
case Commands.Enum.hilfe:
|
||||
await this.helpService.handleInteraction(interaction);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// wenn neues fenster durch buttonclick or so
|
||||
async handleModalSubmit(interaction: ModalSubmitInteraction<CacheType>) {
|
||||
const { customId } = interaction;
|
||||
|
||||
switch (customId) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +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
|
||||
|
||||
export const db = drizzle(process.env.DB_FILE_NAME!);
|
||||
// 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);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
|
||||
export const usersTable = sqliteTable("users_table", {
|
||||
id: int().primaryKey({ autoIncrement: true }),
|
||||
name: text().notNull(),
|
||||
discord_id: int().notNull().unique(),
|
||||
took_medication_today: int().notNull().default(0),
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/index.ts
49
src/index.ts
@@ -2,52 +2,13 @@ import "actions/waterMe/waterMe.task";
|
||||
import "actions/greeting/greeting.task";
|
||||
import "actions/medication/medication.task";
|
||||
import "actions/drink/drink.task";
|
||||
import DiscordController from "controllers/discord.controller";
|
||||
import DiscordController from "discord.controller";
|
||||
import PomodoroController from "actions/pomodoro/pomodoro.controller";
|
||||
|
||||
import "dotenv/config";
|
||||
/*import { drizzle } from 'drizzle-orm/bun-sqlite';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { usersTable } from './db/schema';
|
||||
import { db } from "./db" // from index
|
||||
|
||||
|
||||
async function main() {
|
||||
const user: typeof usersTable.$inferInsert = {
|
||||
name: 'John',
|
||||
age: 30,
|
||||
email: 'john@example.com',
|
||||
};
|
||||
|
||||
await db.insert(usersTable).values(user);
|
||||
console.log('New user created!')
|
||||
|
||||
const users = await db.select().from(usersTable);
|
||||
console.log('Getting all users from the database: ', users)
|
||||
/*
|
||||
const users: {
|
||||
id: number;
|
||||
name: string;
|
||||
age: number;
|
||||
email: string;
|
||||
}[]
|
||||
*/
|
||||
/*
|
||||
await db
|
||||
.update(usersTable)
|
||||
.set({
|
||||
age: 31,
|
||||
})
|
||||
.where(eq(usersTable.email, user.email));
|
||||
console.log('User info updated!')
|
||||
|
||||
await db.delete(usersTable).where(eq(usersTable.email, user.email));
|
||||
console.log('User deleted!')
|
||||
}
|
||||
|
||||
main();*/
|
||||
|
||||
// = main file
|
||||
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user