Rollen-Ping beim Devlog: Erwähnung + Abo-Button
- Setting devlog_ping_role_id (Rollen-Dropdown auf der Setup-Seite, leer = aus) - Devlog-Post erwähnt die Rolle (allowedMentions gescoped) und trägt einen 🔔-Button, über den Member die Rolle selbst an-/abmelden (ephemere Bestätigung) - API liefert vergebbare Rollen (ohne @everyone/managed), PUT validiert Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -152,6 +152,23 @@ export default function Settings({ me }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Devlog */}
|
||||||
|
<div className="settings-section">
|
||||||
|
<h2 className="settings-title">// Devlog</h2>
|
||||||
|
<div className="field">
|
||||||
|
<label>Ping-Rolle <span className="field-hint">wird bei jedem Devlog-Post erwähnt; Member abonnieren sie über den 🔔-Button am Post — leer = kein Ping</span></label>
|
||||||
|
<select
|
||||||
|
value={form.devlog_ping_role_id ?? ''}
|
||||||
|
onChange={(e) => setForm({ ...form, devlog_ping_role_id: e.target.value })}
|
||||||
|
>
|
||||||
|
<option value="">— kein Ping —</option>
|
||||||
|
{(data.roles ?? []).map((r) => (
|
||||||
|
<option key={r.id} value={r.id}>@{r.name} — {r.guild}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Commit-Feed */}
|
{/* Commit-Feed */}
|
||||||
<div className="settings-section">
|
<div className="settings-section">
|
||||||
<h2 className="settings-title">// Commit-Feed</h2>
|
<h2 className="settings-title">// Commit-Feed</h2>
|
||||||
|
|||||||
+34
-2
@@ -1,7 +1,7 @@
|
|||||||
// Discord-Client: Commands laden, registrieren und Interactions verarbeiten
|
// Discord-Client: Commands laden, registrieren und Interactions verarbeiten
|
||||||
import { Client, Collection, Events, GatewayIntentBits, Partials, REST, Routes } from 'discord.js';
|
import { Client, Collection, Events, GatewayIntentBits, MessageFlags, Partials, REST, Routes } from 'discord.js';
|
||||||
import { config } from '../config.js';
|
import { config } from '../config.js';
|
||||||
import { devlogChannelId } from '../runtime-settings.js';
|
import { devlogChannelId, devlogPingRoleId } from '../runtime-settings.js';
|
||||||
import { archiveDevlogMessage, removeDevlog } from './devlog-archive.js';
|
import { archiveDevlogMessage, removeDevlog } from './devlog-archive.js';
|
||||||
import * as ping from './commands/ping.js';
|
import * as ping from './commands/ping.js';
|
||||||
import * as devlogBackfill from './commands/devlog-backfill.js';
|
import * as devlogBackfill from './commands/devlog-backfill.js';
|
||||||
@@ -58,6 +58,38 @@ export async function startBot() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
client.on(Events.InteractionCreate, async (interaction) => {
|
client.on(Events.InteractionCreate, async (interaction) => {
|
||||||
|
// 🔔-Button unterm Devlog: Ping-Rolle selbst an-/abmelden
|
||||||
|
if (interaction.isButton() && interaction.customId === 'devlog_ping_toggle') {
|
||||||
|
const roleId = devlogPingRoleId();
|
||||||
|
try {
|
||||||
|
if (!roleId || !interaction.inGuild()) {
|
||||||
|
throw new Error('Keine Ping-Rolle konfiguriert');
|
||||||
|
}
|
||||||
|
const member = interaction.member;
|
||||||
|
const hasRole = member.roles.cache.has(roleId);
|
||||||
|
if (hasRole) {
|
||||||
|
await member.roles.remove(roleId);
|
||||||
|
} else {
|
||||||
|
await member.roles.add(roleId);
|
||||||
|
}
|
||||||
|
await interaction.reply({
|
||||||
|
content: hasRole
|
||||||
|
? '🔕 Devlog-Benachrichtigungen **abbestellt**.'
|
||||||
|
: '🔔 Devlog-Benachrichtigungen **abonniert** — du wirst ab jetzt gepingt.',
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[ping-rolle] Toggle fehlgeschlagen:', error);
|
||||||
|
await interaction
|
||||||
|
.reply({
|
||||||
|
content: '❌ Konnte die Rolle nicht ändern — dem Bot fehlt vermutlich „Rollen verwalten" oder die Rolle steht über seiner.',
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!interaction.isChatInputCommand()) return;
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
const command = client.commands.get(interaction.commandName);
|
const command = client.commands.get(interaction.commandName);
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ export function releaseChannelId() {
|
|||||||
return getSetting('release_channel_id') || null;
|
return getSetting('release_channel_id') || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Rolle, die bei Devlog-Posts gepingt wird — leer = kein Ping */
|
||||||
|
export function devlogPingRoleId() {
|
||||||
|
return getSetting('devlog_ping_role_id') || null;
|
||||||
|
}
|
||||||
|
|
||||||
/** Commit-Feed global an/aus (Default: an). Aus = weiter archivieren, nur nicht posten. */
|
/** Commit-Feed global an/aus (Default: an). Aus = weiter archivieren, nur nicht posten. */
|
||||||
export function commitFeedEnabled() {
|
export function commitFeedEnabled() {
|
||||||
return getSetting('commit_feed_enabled') !== '0';
|
return getSetting('commit_feed_enabled') !== '0';
|
||||||
|
|||||||
+29
-1
@@ -3,7 +3,7 @@ import { EmbedBuilder } from 'discord.js';
|
|||||||
import { listDevlogs, searchDevlogs, listCommits, listReleases, archiveStats, getSetting, setSetting } from '../db.js';
|
import { listDevlogs, searchDevlogs, listCommits, listReleases, archiveStats, getSetting, setSetting } from '../db.js';
|
||||||
import { config } from '../config.js';
|
import { config } from '../config.js';
|
||||||
import { removeDevlog } from '../bot/devlog-archive.js';
|
import { removeDevlog } from '../bot/devlog-archive.js';
|
||||||
import { commitChannelId, devlogChannelId, releaseChannelId } from '../runtime-settings.js';
|
import { commitChannelId, devlogChannelId, releaseChannelId, devlogPingRoleId } from '../runtime-settings.js';
|
||||||
import { getSessionUser, isAdmin } from './auth.js';
|
import { getSessionUser, isAdmin } from './auth.js';
|
||||||
|
|
||||||
const PAGE_SIZE = 20;
|
const PAGE_SIZE = 20;
|
||||||
@@ -116,11 +116,25 @@ ${rssItems}
|
|||||||
return channels.sort((a, b) => a.name.localeCompare(b.name));
|
return channels.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Rollen, die der Bot vergeben kann (unter seiner höchsten Rolle, nicht managed) */
|
||||||
|
function listRoles() {
|
||||||
|
const roles = [];
|
||||||
|
for (const guild of client.guilds.cache.values()) {
|
||||||
|
for (const role of guild.roles?.cache?.values() ?? []) {
|
||||||
|
if (role.id === guild.id) continue; // @everyone
|
||||||
|
if (role.managed) continue; // Bot-/Integrations-Rollen
|
||||||
|
roles.push({ id: role.id, name: role.name, guild: guild.name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return roles.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
}
|
||||||
|
|
||||||
function currentSettings() {
|
function currentSettings() {
|
||||||
return {
|
return {
|
||||||
commit_channel_id: commitChannelId(),
|
commit_channel_id: commitChannelId(),
|
||||||
devlog_channel_id: devlogChannelId(),
|
devlog_channel_id: devlogChannelId(),
|
||||||
release_channel_id: releaseChannelId() ?? '',
|
release_channel_id: releaseChannelId() ?? '',
|
||||||
|
devlog_ping_role_id: devlogPingRoleId() ?? '',
|
||||||
commit_feed_enabled: getSetting('commit_feed_enabled') !== '0',
|
commit_feed_enabled: getSetting('commit_feed_enabled') !== '0',
|
||||||
commit_branch_filter: getSetting('commit_branch_filter') ?? '',
|
commit_branch_filter: getSetting('commit_branch_filter') ?? '',
|
||||||
ignored_repos: getSetting('ignored_repos') ?? '',
|
ignored_repos: getSetting('ignored_repos') ?? '',
|
||||||
@@ -133,6 +147,7 @@ ${rssItems}
|
|||||||
const stats = archiveStats();
|
const stats = archiveStats();
|
||||||
return {
|
return {
|
||||||
channels: listChannels(),
|
channels: listChannels(),
|
||||||
|
roles: listRoles(),
|
||||||
settings: currentSettings(),
|
settings: currentSettings(),
|
||||||
status: {
|
status: {
|
||||||
botTag: client.user?.tag ?? null,
|
botTag: client.user?.tag ?? null,
|
||||||
@@ -162,6 +177,19 @@ ${rssItems}
|
|||||||
}
|
}
|
||||||
setSetting(key, value);
|
setSetting(key, value);
|
||||||
}
|
}
|
||||||
|
// Ping-Rolle: muss existieren ('' = kein Ping)
|
||||||
|
if (body.devlog_ping_role_id !== undefined) {
|
||||||
|
const value = String(body.devlog_ping_role_id);
|
||||||
|
if (value !== '') {
|
||||||
|
const exists = [...client.guilds.cache.values()].some((g) =>
|
||||||
|
g.roles?.cache?.has?.(value)
|
||||||
|
);
|
||||||
|
if (!exists) {
|
||||||
|
return reply.code(400).send({ error: 'devlog_ping_role_id: Rolle nicht gefunden' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSetting('devlog_ping_role_id', value);
|
||||||
|
}
|
||||||
if (body.commit_feed_enabled !== undefined) {
|
if (body.commit_feed_enabled !== undefined) {
|
||||||
setSetting('commit_feed_enabled', body.commit_feed_enabled ? '1' : '0');
|
setSetting('commit_feed_enabled', body.commit_feed_enabled ? '1' : '0');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { join } from 'node:path';
|
|||||||
import { ActionRowBuilder, AttachmentBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from 'discord.js';
|
import { ActionRowBuilder, AttachmentBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from 'discord.js';
|
||||||
import { config } from '../config.js';
|
import { config } from '../config.js';
|
||||||
import { saveDevlog } from '../db.js';
|
import { saveDevlog } from '../db.js';
|
||||||
import { devlogChannelId } from '../runtime-settings.js';
|
import { devlogChannelId, devlogPingRoleId } from '../runtime-settings.js';
|
||||||
import { imagesDir } from '../bot/devlog-archive.js';
|
import { imagesDir } from '../bot/devlog-archive.js';
|
||||||
|
|
||||||
const BRAND_YELLOW = 0xf5c518;
|
const BRAND_YELLOW = 0xf5c518;
|
||||||
@@ -123,7 +123,7 @@ export function registerDevlogEndpoint(app, client) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Link-Buttons unterm Embed: Archiv-Seite + Repo
|
// Link-Buttons unterm Embed: Archiv-Seite + Repo + Abo-Button für die Ping-Rolle
|
||||||
const buttons = new ActionRowBuilder().addComponents(
|
const buttons = new ActionRowBuilder().addComponents(
|
||||||
new ButtonBuilder().setStyle(ButtonStyle.Link).setLabel('Devlog-Archiv').setEmoji('🗂️').setURL(groupUrl)
|
new ButtonBuilder().setStyle(ButtonStyle.Link).setLabel('Devlog-Archiv').setEmoji('🗂️').setURL(groupUrl)
|
||||||
);
|
);
|
||||||
@@ -133,7 +133,26 @@ export function registerDevlogEndpoint(app, client) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = await channel.send({ embeds, files: attachments, components: [buttons] });
|
// Ping-Rolle: Rolle erwähnen + Abo-Button anbieten (Setup-Seite)
|
||||||
|
const pingRole = devlogPingRoleId();
|
||||||
|
if (pingRole) {
|
||||||
|
buttons.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
.setLabel('Benachrichtigungen')
|
||||||
|
.setEmoji('🔔')
|
||||||
|
.setCustomId('devlog_ping_toggle')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = await channel.send({
|
||||||
|
embeds,
|
||||||
|
files: attachments,
|
||||||
|
components: [buttons],
|
||||||
|
...(pingRole
|
||||||
|
? { content: `<@&${pingRole}>`, allowedMentions: { roles: [pingRole] } }
|
||||||
|
: {}),
|
||||||
|
});
|
||||||
|
|
||||||
// Direkt archivieren (der Live-Listener ignoriert eigene Bot-Posts bewusst)
|
// Direkt archivieren (der Live-Listener ignoriert eigene Bot-Posts bewusst)
|
||||||
const imageFiles = [];
|
const imageFiles = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user