Files
Papo/src/events/guildMemberAdd.ts
Pepe44DEV aa246cd7ea
Some checks failed
Deploy Discord Bot / deploy (push) Failing after -1m12s
SonarQube / sonar (push) Successful in 4s
add badges, weekly plans, polls, suggestions, mod-case system, branding, partner system, alt-account detection, gallery, growth tracking
Two feature batches plus a deploy fix:

Moderation & engagement: persistent ModCase history (retrofits ban/kick/
mute/timeout/tempban to record cases), /warn, /watch watchlist with
automatic alerts on deletions/tickets/automod hits, /case file lookup,
achievement badges (/badges, /profile) with message/streak/ticket/
birthday/event/booster triggers, /weekplan RSVP boards, /poll with
anonymous mode and scheduled auto-close, /suggest with vote/comment/
decide flow.

Branding & growth: /branding (per-guild embed color/logo/footer/bot
name/theme, applied across logging/ticket/register/embed-builder
embeds and the dashboard sidebar/accent color at runtime), two new
/setup server-type presets (Roleplay, Creator), /rules generate
(template-based rule sets), /partner apply/list/config with invite
validation and showcase posting, alt-account suspicion scoring on
join (account age, avatar, name pattern, join bursts, ban-list
similarity), /gallery submit with voting and weekly-winner scheduler,
invite-attributed join/leave tracking with a new Wachstum dashboard
page, and an expanded Admin dashboard (guild list, feature-usage
counters).

Also fixes production: the Docker container never ran `prisma migrate
deploy`, so schema changes never reached the live database. The
compose command now applies pending migrations on every start.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 23:15:35 +02:00

72 lines
3.2 KiB
TypeScript

import { GuildMember, EmbedBuilder } from 'discord.js';
import { EventHandler } from '../utils/types';
import { context } from '../config/context';
import { settingsStore } from '../config/state';
const event: EventHandler = {
name: 'guildMemberAdd',
execute(member: GuildMember) {
if (context.lockdown.isActive(member.guild.id)) {
member.kick('Lockdown aktiv - neue Mitglieder werden abgewiesen').catch(() => undefined);
context.logging.logMemberJoin(member);
return;
}
context.altAccounts.check(member).then(({ score, reasons }) => {
if (context.altAccounts.isSuspicious(score)) context.logging.logSuspiciousJoin(member, reasons);
}).catch(() => undefined);
context.growth.resolveUsedInvite(member.guild).then((inviteCode) => {
context.growth.recordJoin(member.guild.id, member.id, inviteCode);
}).catch(() => undefined);
const guildConfig = settingsStore.get(member.guild.id);
const welcomeCfg = guildConfig?.welcomeConfig || guildConfig?.automodConfig?.welcomeConfig;
if (welcomeCfg?.enabled && welcomeCfg.channelId) {
const channel = member.guild.channels.cache.get(welcomeCfg.channelId);
if (channel && channel.isTextBased()) {
const files: any[] = [];
const colorVal = parseInt((welcomeCfg.embedColor || '00ff99').replace('#', ''), 16);
const embed = new EmbedBuilder()
.setTitle(welcomeCfg.embedTitle || 'Willkommen!')
.setDescription(welcomeCfg.embedDescription || `${member} ist beigetreten.`)
.setColor(isNaN(colorVal) ? 0x00ff99 : colorVal);
const footerText = (welcomeCfg.embedFooter || '').trim();
if (footerText) {
embed.setFooter({ text: footerText });
}
if (welcomeCfg.embedThumbnailData && welcomeCfg.embedThumbnailData.startsWith('data:')) {
const [meta, b64] = welcomeCfg.embedThumbnailData.split(',');
const ext = meta.includes('gif') ? 'gif' : 'png';
const buf = Buffer.from(b64, 'base64');
const name = `welcome-thumb.${ext}`;
files.push({ attachment: buf, name });
embed.setThumbnail(`attachment://${name}`);
} else if (welcomeCfg.embedThumbnail) {
embed.setThumbnail(welcomeCfg.embedThumbnail);
}
if (welcomeCfg.embedImageData && welcomeCfg.embedImageData.startsWith('data:')) {
const [meta, b64] = welcomeCfg.embedImageData.split(',');
const ext = meta.includes('gif') ? 'gif' : 'png';
const buf = Buffer.from(b64, 'base64');
const name = `welcome-image.${ext}`;
files.push({ attachment: buf, name });
embed.setImage(`attachment://${name}`);
} else if (welcomeCfg.embedImage) {
embed.setImage(welcomeCfg.embedImage);
}
channel.send({ embeds: [embed], files }).catch(() => undefined);
}
} else if (guildConfig?.welcomeChannelId) {
const channel = member.guild.channels.cache.get(guildConfig.welcomeChannelId);
if (channel && channel.isTextBased()) {
channel.send({ content: `Willkommen ${member}!` }).catch(() => undefined);
}
}
context.logging.logMemberJoin(member);
context.stats.refreshGuild(member.guild.id).catch(() => undefined);
}
};
export default event;