Der Server ist kein Tool aus der Neunerliste, aber Broadcaster, Recap und Clipper posten dorthin. Deshalb steht die Struktur als Code im Repo und nicht nur in einer Oberflaeche. packages/discord: - permissions.ts Rechtebits als BigInt, das Feld ist 64 Bit breit - api.ts REST-Client: Warteschlange, Rate Limits, lesbare Fehler - plan.ts Rollen, Kanaele, Rechte, AutoMod, Willkommenstext - apply.ts traegt den Bauplan auf einen bestehenden Server auf Neuer Befehl `abdulive discord:setup [--server ID] [--vorschau]`. Zugeschnitten auf 5 bis 30 gleichzeitige Zuschauer: fuenf sichtbare Kanaele statt zwanzig, und eine selbst vergebene Rolle Live-Ping statt eines Rundrufs an alle bei jedem Stream. Wiederholbar statt einmalig: gefunden wird ueber Namen und frueheren Namen, danach wird angeglichen. Der zweite Lauf aendert nichts. Geloescht wird nie, und von Hand gesetzte Rechte-Ausnahmen bleiben stehen. Drei Dinge, die am echten Server nachgemessen und nicht angenommen wurden: - Discord schreibt Textkanaele klein und macht aus Leerzeichen Bindestriche; Emoji, Umlaute und U+2502 bleiben. normalizeName() bildet das ab, sonst geht bei jedem Lauf dieselbe Umbenennung erneut raus. - Kanalnamen lassen sich nur zweimal je zehn Minuten aendern, danach verlangt Discord bis zu neun Minuten Pause. Der Lauf wartet hoechstens eine Minute und meldet den Rest. - Eine Rolle kann nur Rollen unterhalb der eigenen verschieben, auch mit Administrator. rolePositions() rechnet den Platz unter der Bot-Rolle aus, statt blind 3, 2, 1 zu verlangen. Zugangsdaten laufen ueber den verschluesselten Speicher (ADR-0005): der Bot-Token wird verdeckt abgefragt, die drei Webhook-URLs werden nie ausgegeben und nie geloggt. Im Quelltext bleibt es bei ae/oe/ue, alles was ein Mitglied auf Discord liest steht in richtigem Deutsch. Ein Test haelt das fest. Begruendung, Fallstricke und die Schritte von Hand: docs/projects/discord-community.md Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
// Discord-Rechtebits. Die Werte sind ein 64-Bit-Feld und werden in der API
|
|
// als Dezimalstring uebertragen - deshalb BigInt und nicht number.
|
|
//
|
|
// Kein `enum`: Node fuehrt die .ts-Dateien direkt aus und entfernt nur Typen,
|
|
// ein enum waere zur Laufzeit weg (ADR-0001, `erasableSyntaxOnly`).
|
|
|
|
export const P = {
|
|
CREATE_INSTANT_INVITE: 1n << 0n,
|
|
KICK_MEMBERS: 1n << 1n,
|
|
BAN_MEMBERS: 1n << 2n,
|
|
ADMINISTRATOR: 1n << 3n,
|
|
MANAGE_CHANNELS: 1n << 4n,
|
|
MANAGE_GUILD: 1n << 5n,
|
|
ADD_REACTIONS: 1n << 6n,
|
|
VIEW_AUDIT_LOG: 1n << 7n,
|
|
PRIORITY_SPEAKER: 1n << 8n,
|
|
STREAM: 1n << 9n,
|
|
VIEW_CHANNEL: 1n << 10n,
|
|
SEND_MESSAGES: 1n << 11n,
|
|
SEND_TTS_MESSAGES: 1n << 12n,
|
|
MANAGE_MESSAGES: 1n << 13n,
|
|
EMBED_LINKS: 1n << 14n,
|
|
ATTACH_FILES: 1n << 15n,
|
|
READ_MESSAGE_HISTORY: 1n << 16n,
|
|
MENTION_EVERYONE: 1n << 17n,
|
|
USE_EXTERNAL_EMOJIS: 1n << 18n,
|
|
VIEW_GUILD_INSIGHTS: 1n << 19n,
|
|
CONNECT: 1n << 20n,
|
|
SPEAK: 1n << 21n,
|
|
MUTE_MEMBERS: 1n << 22n,
|
|
DEAFEN_MEMBERS: 1n << 23n,
|
|
MOVE_MEMBERS: 1n << 24n,
|
|
USE_VAD: 1n << 25n,
|
|
CHANGE_NICKNAME: 1n << 26n,
|
|
MANAGE_NICKNAMES: 1n << 27n,
|
|
MANAGE_ROLES: 1n << 28n,
|
|
MANAGE_WEBHOOKS: 1n << 29n,
|
|
MANAGE_GUILD_EXPRESSIONS: 1n << 30n,
|
|
USE_APPLICATION_COMMANDS: 1n << 31n,
|
|
REQUEST_TO_SPEAK: 1n << 32n,
|
|
MANAGE_EVENTS: 1n << 33n,
|
|
MANAGE_THREADS: 1n << 34n,
|
|
CREATE_PUBLIC_THREADS: 1n << 35n,
|
|
CREATE_PRIVATE_THREADS: 1n << 36n,
|
|
USE_EXTERNAL_STICKERS: 1n << 37n,
|
|
SEND_MESSAGES_IN_THREADS: 1n << 38n,
|
|
USE_EMBEDDED_ACTIVITIES: 1n << 39n,
|
|
MODERATE_MEMBERS: 1n << 40n,
|
|
USE_SOUNDBOARD: 1n << 42n,
|
|
CREATE_EVENTS: 1n << 44n,
|
|
USE_EXTERNAL_SOUNDS: 1n << 45n,
|
|
SEND_VOICE_MESSAGES: 1n << 46n,
|
|
SEND_POLLS: 1n << 49n,
|
|
} as const;
|
|
|
|
export type PermName = keyof typeof P;
|
|
|
|
/** Vereinigt mehrere Rechte zu einem Bitfeld. */
|
|
export function bits(...names: PermName[]): bigint {
|
|
let out = 0n;
|
|
for (const n of names) out |= P[n];
|
|
return out;
|
|
}
|
|
|
|
/** Wie die API es erwartet: Dezimalstring. */
|
|
export const str = (v: bigint): string => v.toString();
|
|
|
|
/** Namen eines Bitfelds - nur fuer die Vorschau und Fehlermeldungen. */
|
|
export function names(value: bigint): PermName[] {
|
|
const out: PermName[] = [];
|
|
for (const [name, bit] of Object.entries(P) as Array<[PermName, bigint]>) {
|
|
if ((value & bit) === bit) out.push(name);
|
|
}
|
|
return out;
|
|
}
|