refactor: przeniesienie konfiguracji kanału logów do dedykowanego modalu Ustawienia z wybierakiem per-serwer
This commit is contained in:
5 files changed
+269
-51
No files matched your search
@@ -124,7 +124,7 @@ class MusicBot(commands.Cog):
|
|||||||
print(f"Error loading config: {e}")
|
print(f"Error loading config: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def save_channel_id(self, guild_id: int, channel_id: int):
|
def save_channel_id(self, guild_id: int, channel_id: Optional[int]):
|
||||||
config_path = "config/config.json"
|
config_path = "config/config.json"
|
||||||
try:
|
try:
|
||||||
import json
|
import json
|
||||||
@@ -133,7 +133,11 @@ class MusicBot(commands.Cog):
|
|||||||
if os.path.exists(config_path):
|
if os.path.exists(config_path):
|
||||||
with open(config_path, "r") as f:
|
with open(config_path, "r") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
data[str(guild_id)] = channel_id
|
if channel_id is None:
|
||||||
|
if str(guild_id) in data:
|
||||||
|
del data[str(guild_id)]
|
||||||
|
else:
|
||||||
|
data[str(guild_id)] = channel_id
|
||||||
with open(config_path, "w") as f:
|
with open(config_path, "w") as f:
|
||||||
json.dump(data, f, indent=4)
|
json.dump(data, f, indent=4)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
+75
-34
@@ -16,8 +16,10 @@ let wsConnectedOnce = false;
|
|||||||
// DOM Elements
|
// DOM Elements
|
||||||
const statusDot = document.getElementById('status-dot');
|
const statusDot = document.getElementById('status-dot');
|
||||||
const statusText = document.getElementById('status-text');
|
const statusText = document.getElementById('status-text');
|
||||||
const voiceSelect = document.getElementById('voice-channel-select');
|
const btnOpenSettings = document.getElementById('btn-open-settings');
|
||||||
const textChannelSelect = document.getElementById('text-channel-select');
|
const btnCloseSettings = document.getElementById('btn-close-settings');
|
||||||
|
const settingsModal = document.getElementById('settings-modal');
|
||||||
|
const settingsGuildsList = document.getElementById('settings-guilds-list');
|
||||||
const btnConnect = document.getElementById('btn-connect');
|
const btnConnect = document.getElementById('btn-connect');
|
||||||
const trackTitle = document.getElementById('track-title');
|
const trackTitle = document.getElementById('track-title');
|
||||||
const trackArtist = document.getElementById('track-artist');
|
const trackArtist = document.getElementById('track-artist');
|
||||||
@@ -162,8 +164,14 @@ function bindEvents() {
|
|||||||
// Add Song Form
|
// Add Song Form
|
||||||
addForm.addEventListener('submit', addTrack);
|
addForm.addEventListener('submit', addTrack);
|
||||||
|
|
||||||
// Text Channel Select
|
// Settings Modal Events
|
||||||
textChannelSelect.addEventListener('change', changeTextChannel);
|
if (btnOpenSettings) btnOpenSettings.addEventListener('click', openSettingsModal);
|
||||||
|
if (btnCloseSettings) btnCloseSettings.addEventListener('click', closeSettingsModal);
|
||||||
|
if (settingsModal) {
|
||||||
|
settingsModal.addEventListener('click', (e) => {
|
||||||
|
if (e.target === settingsModal) closeSettingsModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Upload Events
|
// Upload Events
|
||||||
if (btnSelectFile && fileInput) {
|
if (btnSelectFile && fileInput) {
|
||||||
@@ -296,30 +304,9 @@ function updateUI(status) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text selector population (only if select doesn't have focus/interaction)
|
// Settings Guilds Population
|
||||||
if (document.activeElement !== textChannelSelect) {
|
if (status.guilds) {
|
||||||
const currentSelectedText = textChannelSelect.value;
|
renderSettingsGuilds(status.guilds);
|
||||||
textChannelSelect.innerHTML = '<option value="" disabled>Wybierz kanał...</option>';
|
|
||||||
|
|
||||||
if (status.text_channels && status.text_channels.length > 0) {
|
|
||||||
status.text_channels.forEach(channel => {
|
|
||||||
const opt = document.createElement('option');
|
|
||||||
opt.value = channel.channel_id;
|
|
||||||
opt.textContent = `${channel.guild_name} - #${channel.channel_name}`;
|
|
||||||
textChannelSelect.appendChild(opt);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (status.active_text_channel_id) {
|
|
||||||
textChannelSelect.value = status.active_text_channel_id;
|
|
||||||
} else if (currentSelectedText && status.text_channels.some(c => c.channel_id === currentSelectedText)) {
|
|
||||||
textChannelSelect.value = currentSelectedText;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const opt = document.createElement('option');
|
|
||||||
opt.value = "";
|
|
||||||
opt.textContent = "Brak dostępnych kanałów";
|
|
||||||
textChannelSelect.appendChild(opt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Playback State
|
// 2. Playback State
|
||||||
@@ -753,19 +740,73 @@ async function setVolume(vol) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change Text Notification Channel
|
// Settings Modal Functions
|
||||||
async function changeTextChannel() {
|
function openSettingsModal() {
|
||||||
const channelId = textChannelSelect.value;
|
if (settingsModal) settingsModal.style.display = 'flex';
|
||||||
if (!channelId) return;
|
}
|
||||||
|
|
||||||
|
function closeSettingsModal() {
|
||||||
|
if (settingsModal) settingsModal.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSettingsGuilds(guilds) {
|
||||||
|
if (!settingsGuildsList) return;
|
||||||
|
|
||||||
|
// Don't re-render if user is currently interacting with a select inside settings
|
||||||
|
if (settingsModal && settingsModal.style.display !== 'none' && document.activeElement && document.activeElement.classList.contains('guild-log-select')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!guilds || guilds.length === 0) {
|
||||||
|
settingsGuildsList.innerHTML = '<div class="empty-queue-message"><i data-lucide="server"></i><p>Brak serwerów Discord w pamięci bota.</p></div>';
|
||||||
|
lucide.createIcons();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = '';
|
||||||
|
guilds.forEach(guild => {
|
||||||
|
let optionsHtml = '';
|
||||||
|
if (guild.text_channels && guild.text_channels.length > 0) {
|
||||||
|
guild.text_channels.forEach(tc => {
|
||||||
|
const isSelected = guild.active_notification_channel_id === tc.channel_id ? 'selected' : '';
|
||||||
|
optionsHtml += `<option value="${tc.channel_id}" ${isSelected}>#${tc.channel_name}</option>`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
optionsHtml = '<option value="" disabled>Brak kanałów tekstowych</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += `
|
||||||
|
<div class="guild-setting-row glass-card-subtle">
|
||||||
|
<div class="guild-info">
|
||||||
|
<div class="guild-icon"><i data-lucide="shield"></i></div>
|
||||||
|
<div>
|
||||||
|
<div class="guild-name">${guild.guild_name}</div>
|
||||||
|
<div class="guild-id">ID: ${guild.guild_id}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="guild-control">
|
||||||
|
<select class="premium-select guild-log-select" data-guild-id="${guild.guild_id}" onchange="saveGuildLogChannel('${guild.guild_id}', this.value)">
|
||||||
|
<option value="">-- Brak / Domyślny --</option>
|
||||||
|
${optionsHtml}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
settingsGuildsList.innerHTML = html;
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveGuildLogChannel(guildId, channelId) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/settings/text_channel', {
|
const res = await fetch('/api/settings/text_channel', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ channel_id: channelId })
|
body: JSON.stringify({ guild_id: guildId, channel_id: channelId || "" })
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error('Settings change failed');
|
if (!res.ok) throw new Error('Settings change failed');
|
||||||
showToast('Zapisano kanał powiadomień na Discordzie!', 'success');
|
showToast('Zapisano kanał powiadomień dla serwera!', 'success');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast('Nie udało się zapisać kanału powiadomień', 'error');
|
showToast('Nie udało się zapisać kanału powiadomień', 'error');
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-12
@@ -27,10 +27,14 @@
|
|||||||
<h1>ŻyrarBOT</h1>
|
<h1>ŻyrarBOT</h1>
|
||||||
<span class="subtitle">Kontroler Web</span>
|
<span class="subtitle">Kontroler Web</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="header-actions" style="display: flex; align-items: center; gap: 16px;">
|
||||||
<div class="bot-status-badge" id="bot-status-container">
|
<button id="btn-open-settings" class="btn btn-secondary" style="display: flex; align-items: center; gap: 8px;">
|
||||||
<span class="status-indicator offline" id="status-dot"></span>
|
<i data-lucide="settings"></i> Ustawienia
|
||||||
<span id="status-text">Rozłączony</span>
|
</button>
|
||||||
|
<div class="bot-status-badge" id="bot-status-container">
|
||||||
|
<span class="status-indicator offline" id="status-dot"></span>
|
||||||
|
<span id="status-text">Rozłączony</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -41,7 +45,7 @@
|
|||||||
<!-- Connection & Settings Panel -->
|
<!-- Connection & Settings Panel -->
|
||||||
<div class="glass-card connection-panel">
|
<div class="glass-card connection-panel">
|
||||||
<h2 class="card-title"><i data-lucide="radio"></i> Kanał Głosowy</h2>
|
<h2 class="card-title"><i data-lucide="radio"></i> Kanał Głosowy</h2>
|
||||||
<div class="control-row" style="margin-bottom: 20px;">
|
<div class="control-row">
|
||||||
<select id="voice-channel-select" class="premium-select">
|
<select id="voice-channel-select" class="premium-select">
|
||||||
<option value="" disabled selected>Wybierz kanał...</option>
|
<option value="" disabled selected>Wybierz kanał...</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -49,13 +53,6 @@
|
|||||||
<i data-lucide="log-in"></i> Połącz
|
<i data-lucide="log-in"></i> Połącz
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="card-title" style="margin-top: 10px;"><i data-lucide="message-square"></i> Kanał Tekstowy Logów</h2>
|
|
||||||
<div class="control-row">
|
|
||||||
<select id="text-channel-select" class="premium-select">
|
|
||||||
<option value="" disabled selected>Wybierz kanał...</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Player Panel (Now Playing) -->
|
<!-- Player Panel (Now Playing) -->
|
||||||
@@ -217,6 +214,29 @@
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Settings Modal -->
|
||||||
|
<div class="modal-overlay" id="settings-modal" style="display: none;">
|
||||||
|
<div class="modal-card glass-card">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="card-title"><i data-lucide="settings"></i> Ustawienia ŻyrarBOT</h2>
|
||||||
|
<button class="btn-icon" id="btn-close-settings" title="Zamknij"><i data-lucide="x"></i></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3><i data-lucide="server"></i> Konfiguracja Serwerów Discord (Kanały Logów)</h3>
|
||||||
|
<p class="settings-desc">Wybierz kanał tekstowy na każdym serwerze Discord, na który bot ma wysyłać powiadomienia o odtwarzanych utworach i zmianach w kolejce.</p>
|
||||||
|
<div class="guilds-list" id="settings-guilds-list">
|
||||||
|
<!-- Populated via JS -->
|
||||||
|
<div class="empty-queue-message">
|
||||||
|
<i data-lucide="loader"></i>
|
||||||
|
<p>Ładowanie listy serwerów...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Notification Toast -->
|
<!-- Notification Toast -->
|
||||||
<div class="toast-container" id="toast-container"></div>
|
<div class="toast-container" id="toast-container"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -923,3 +923,121 @@ body {
|
|||||||
.toast.info i {
|
.toast.info i {
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Settings Modal */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
animation: fadeIn 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 650px;
|
||||||
|
max-height: 85vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border-glass);
|
||||||
|
border-radius: 24px;
|
||||||
|
box-shadow: var(--shadow-premium);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px 24px;
|
||||||
|
border-bottom: 1px solid var(--border-glass);
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 24px;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section h3 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
color: var(--text-main);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-desc {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guild-setting-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid var(--border-glass);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guild-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guild-icon {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: rgba(139, 92, 246, 0.15);
|
||||||
|
color: var(--primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guild-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-main);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guild-id {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.guild-control {
|
||||||
|
width: 240px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ class ConnectRequest(BaseModel):
|
|||||||
|
|
||||||
class TextChannelRequest(BaseModel):
|
class TextChannelRequest(BaseModel):
|
||||||
channel_id: str
|
channel_id: str
|
||||||
|
guild_id: Optional[str] = None
|
||||||
|
|
||||||
class PlayFileRequest(BaseModel):
|
class PlayFileRequest(BaseModel):
|
||||||
filename: str
|
filename: str
|
||||||
@@ -77,19 +78,39 @@ async def get_status():
|
|||||||
if not music_cog:
|
if not music_cog:
|
||||||
return {"error": "MusicBot cog not loaded"}
|
return {"error": "MusicBot cog not loaded"}
|
||||||
|
|
||||||
|
all_guilds = []
|
||||||
|
for g in bot.guilds:
|
||||||
|
g_tc = []
|
||||||
|
for tc in g.text_channels:
|
||||||
|
if tc.permissions_for(g.me).send_messages:
|
||||||
|
g_tc.append({
|
||||||
|
"channel_id": str(tc.id),
|
||||||
|
"channel_name": tc.name
|
||||||
|
})
|
||||||
|
active_notif = music_cog.get_notification_channel(g.id)
|
||||||
|
all_guilds.append({
|
||||||
|
"guild_id": str(g.id),
|
||||||
|
"guild_name": g.name,
|
||||||
|
"text_channels": g_tc,
|
||||||
|
"active_notification_channel_id": str(active_notif.id) if active_notif else None
|
||||||
|
})
|
||||||
|
|
||||||
guild = get_active_guild()
|
guild = get_active_guild()
|
||||||
if not guild:
|
if not guild:
|
||||||
return {
|
return {
|
||||||
"playing": False,
|
"playing": False,
|
||||||
"paused": False,
|
"paused": False,
|
||||||
"loop_single": False,
|
"loop_single": False,
|
||||||
|
"loop_playlist": False,
|
||||||
"volume": 100,
|
"volume": 100,
|
||||||
"current": None,
|
"current": None,
|
||||||
"queue": [],
|
"queue": [],
|
||||||
|
"history": [],
|
||||||
"voice_channels": [],
|
"voice_channels": [],
|
||||||
"active_voice_channel": None,
|
"active_voice_channel": None,
|
||||||
"text_channels": [],
|
"text_channels": [],
|
||||||
"active_text_channel_id": None
|
"active_text_channel_id": None,
|
||||||
|
"guilds": all_guilds
|
||||||
}
|
}
|
||||||
|
|
||||||
guild_id = guild.id
|
guild_id = guild.id
|
||||||
@@ -161,6 +182,7 @@ async def get_status():
|
|||||||
"active_voice_channel": active_vc,
|
"active_voice_channel": active_vc,
|
||||||
"text_channels": text_channels,
|
"text_channels": text_channels,
|
||||||
"active_text_channel_id": active_tc_id,
|
"active_text_channel_id": active_tc_id,
|
||||||
|
"guilds": all_guilds,
|
||||||
"history": [
|
"history": [
|
||||||
{
|
{
|
||||||
"id": item["id"],
|
"id": item["id"],
|
||||||
@@ -590,22 +612,35 @@ async def api_set_text_channel(req: TextChannelRequest):
|
|||||||
if not music_cog:
|
if not music_cog:
|
||||||
raise HTTPException(status_code=500, detail="MusicBot cog not loaded")
|
raise HTTPException(status_code=500, detail="MusicBot cog not loaded")
|
||||||
|
|
||||||
|
if not req.channel_id or req.channel_id == "0" or req.channel_id == "":
|
||||||
|
if not req.guild_id:
|
||||||
|
raise HTTPException(status_code=400, detail="guild_id required to reset channel")
|
||||||
|
guild_id = int(req.guild_id)
|
||||||
|
if guild_id in music_cog.last_channels:
|
||||||
|
del music_cog.last_channels[guild_id]
|
||||||
|
music_cog.save_channel_id(guild_id, None)
|
||||||
|
await broadcast_status()
|
||||||
|
return {"status": "success", "channel_id": None, "guild_id": str(guild_id)}
|
||||||
|
|
||||||
channel = bot.get_channel(int(req.channel_id))
|
channel = bot.get_channel(int(req.channel_id))
|
||||||
if not channel:
|
if not channel:
|
||||||
raise HTTPException(status_code=404, detail="Text channel not found")
|
raise HTTPException(status_code=404, detail="Text channel not found")
|
||||||
|
|
||||||
guild_id = channel.guild.id
|
guild_id = channel.guild.id
|
||||||
|
if req.guild_id and int(req.guild_id) != guild_id:
|
||||||
|
raise HTTPException(status_code=400, detail="Channel does not belong to the specified guild")
|
||||||
|
|
||||||
music_cog.last_channels[guild_id] = channel
|
music_cog.last_channels[guild_id] = channel
|
||||||
music_cog.save_channel_id(guild_id, int(req.channel_id))
|
music_cog.save_channel_id(guild_id, int(req.channel_id))
|
||||||
|
|
||||||
# Send test message to confirm
|
# Send test message to confirm
|
||||||
try:
|
try:
|
||||||
await channel.send(f"🔔 Ten kanał został ustawiony jako główny kanał powiadomień dla ŻyrarBOT z panelu webowego.")
|
await channel.send(f"🔔 Ten kanał został ustawiony jako główny kanał powiadomień dla ŻyrarBOT na serwerze **{channel.guild.name}** z panelu webowego.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to send test message: {e}")
|
print(f"Failed to send test message: {e}")
|
||||||
|
|
||||||
await broadcast_status()
|
await broadcast_status()
|
||||||
return {"status": "success", "channel_id": req.channel_id}
|
return {"status": "success", "channel_id": req.channel_id, "guild_id": str(guild_id)}
|
||||||
|
|
||||||
# WebSocket Background Loop and Startup Hook
|
# WebSocket Background Loop and Startup Hook
|
||||||
async def status_broadcast_loop():
|
async def status_broadcast_loop():
|
||||||
|
|||||||
Reference in new issue
Block a user