Discord: eigener Kanal fuer Beitritte, Spielvorschlaege und Reactions als Forum

Beitrittsmeldungen liefen nach #allgemein, weil Discord ohne Angabe den
ersten Kanal nimmt. Jetzt gehen sie nach #begruessung (SYSTEM_CHANNEL).

Die von Abdu angelegten Textkanaele spielvorschlaege und
reaction-einsendungen sind jetzt Foren im Bauplan: ein Beitrag je Spiel
bzw. Clip, 👍 als Standard-Reaktion, Stand-Tags nur fuer Mods. apply.ts
kann dafuer Tags und Standard-Reaktion anlegen und nachziehen; fremde
Tags bleiben mit ihrer ID stehen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZeasyAlexandClaude Opus 5.5 committed 2026-10-17 21:52:02 +02:00
1 parent 81ab865de0
commit 5494c6d24d
7 files changed
+211 -9

No files matched your search

+41 -5
View File
@@ -11,14 +11,14 @@
import { logger } from '#core/logger.ts';
import { updateSecrets } from '#twitch/secrets.ts';
import {
CH, type ApiChannel, type ApiGuild, type ApiOverwrite, type ApiRole, type ApiUser,
CH, type ApiChannel, type ApiGuild, type ApiOverwrite, type ApiRole, type ApiTag, type ApiUser,
type ApiWebhook, type Rest,
} from './api.ts';
import { str } from './permissions.ts';
import { selfRoleComponents } from './selfroles.ts';
import {
AUTOMOD, AUTOMOD_BLOCK, CATEGORIES, CHANNELS, EVERYONE, GUILD_SETTINGS, ROLES, WELCOME,
type CategoryPlan, type ChannelPlan, type OverwritePlan, type WebhookSlot,
AUTOMOD, AUTOMOD_BLOCK, CATEGORIES, CHANNELS, EVERYONE, GUILD_SETTINGS, ROLES, SYSTEM_CHANNEL, WELCOME,
type CategoryPlan, type ChannelPlan, type OverwritePlan, type TagPlan, type WebhookSlot,
} from './plan.ts';
const log = logger('discord:setup');
@@ -241,7 +241,30 @@ function kindOf(type: number): string {
}
/** Kategorie und Kanal unterscheiden sich fuers Auftragen nur im Typ. */
type AnyPlan = CategoryPlan & { type: number; topic?: string };
type AnyPlan = CategoryPlan & { type: number; topic?: string; tags?: TagPlan[]; reaction?: string };
/**
* Tags eines Forums: die aus dem Plan nachziehen, fremde stehen lassen.
* Discord ersetzt beim PATCH die ganze Liste, deshalb gehen die vorhandenen
* mit ihrer ID wieder mit - ohne ID waere es ein neuer Tag, und Beitraege
* verloeren ihre Markierung. Gibt null zurueck, wenn alles passt.
*/
export function mergeTags(existing: ApiTag[], plan: TagPlan[]): ApiTag[] | null {
let changed = false;
const out = existing.map((t) => ({ ...t }));
for (const p of plan) {
const want = { name: p.name, emoji_name: p.emoji, moderated: p.moderated ?? false };
const hit = out.find((t) => eq(t.name, p.name));
if (!hit) {
out.push(want);
changed = true;
} else if (hit.name !== want.name || hit.emoji_name !== want.emoji_name || hit.moderated !== want.moderated) {
Object.assign(hit, want, { emoji_id: null });
changed = true;
}
}
return changed ? out : null;
}
async function applyChannel(
api: Rest, guild: ApiGuild, existing: ApiChannel[], steps: Step[], dry: boolean,
@@ -268,6 +291,8 @@ async function applyChannel(
...(parentId ? { parent_id: parentId } : {}),
...(plan.topic ? { topic: plan.topic } : {}),
...(overwrites.length ? { permission_overwrites: overwrites } : {}),
...(plan.tags?.length ? { available_tags: mergeTags([], plan.tags) } : {}),
...(plan.reaction ? { default_reaction_emoji: { emoji_id: null, emoji_name: plan.reaction } } : {}),
}, 'Kanal aus dem Bauplan');
steps.push({ kind, name: plan.name, change: 'erstellt' });
return made;
@@ -280,6 +305,11 @@ async function applyChannel(
if (parentId && found.parent_id !== parentId) patch['parent_id'] = parentId;
const merged = mergeOverwrites(found.permission_overwrites ?? [], plan.overwrites ?? [], roleId);
if (merged) patch['permission_overwrites'] = merged;
const tags = plan.tags?.length ? mergeTags(found.available_tags ?? [], plan.tags) : null;
if (tags) patch['available_tags'] = tags;
if (plan.reaction && found.default_reaction_emoji?.emoji_name !== plan.reaction) {
patch['default_reaction_emoji'] = { emoji_id: null, emoji_name: plan.reaction };
}
if (Object.keys(patch).length === 0) {
steps.push({ kind, name: plan.name, change: 'unveraendert' });
@@ -525,7 +555,13 @@ export async function applyPlan(
await applyWebhooks(api, channel, plan, steps, dry, urls);
}
// 4. AutoMod und Begruessung - beides braucht Kanaele, die es geben muss.
// 4. Beitrittsmeldungen, AutoMod und Begruessung - alles braucht Kanaele,
// die es geben muss.
const system = made.get(SYSTEM_CHANNEL);
if (system && guild.system_channel_id !== system.id) {
if (!dry) await api.patch(`/guilds/${guild.id}`, { system_channel_id: system.id }, 'Beitrittsmeldungen aus dem Bauplan');
steps.push({ kind: 'Server', name: `Beitrittsmeldungen nach ${system.name}`, change: dry ? 'erstellt' : 'angepasst' });
}
await applyAutomod(api, guild, made.get('technik-log')?.id ?? null, steps, dry);
const welcome = made.get('willkommen');
if (welcome) {