feat: add streamer and bot chat sending

This commit is contained in:
2026-10-25 04:48:17 +01:00
parent 93d34306bc
commit 2eb050294b
2 changed files with 44 additions and 3 deletions
+24 -1
View File
@@ -29,6 +29,10 @@
let categorySearch = '';
let categoryOptions: Array<{ id: string; name: string; box_art_url: string }> = [];
let categorySearchTimer: number | undefined;
let chatIdentity: 'streamer' | 'bot' = 'streamer';
let chatMessage = '';
let chatBusy = false;
let chatError = '';
let oauthLink = '';
let oauthIdentity: 'streamer' | 'bot' = 'streamer';
let oauthBusy = false;
@@ -231,6 +235,25 @@
}
}
async function sendChatMessage() {
if (!chatMessage.trim() || chatMessage.length > 500) return;
chatBusy = true;
chatError = '';
try {
const response = await fetch(`${serverAddress}/api/v1/twitch/chat/message`, {
method: 'POST',
headers: { 'content-type': 'application/json', authorization: `Bearer ${sessionToken}` },
body: JSON.stringify({ identity: chatIdentity, message: chatMessage }),
});
if (!response.ok) throw new Error('The selected Twitch identity could not send the message.');
chatMessage = '';
} catch (error) {
chatError = error instanceof Error ? error.message : 'Chat message failed.';
} finally {
chatBusy = false;
}
}
async function createOAuthLink(identity: 'streamer' | 'bot') {
oauthBusy = true;
oauthError = '';
@@ -339,7 +362,7 @@
<div class="dashboard-grid">
<section class="panel preview-panel"><div class="panel-header"><span>STREAM PREVIEW</span><span class="panel-menu">⋮</span></div>{#if playerSrc}<iframe class="twitch-player" title={`Twitch player for ${streamerName}`} src={playerSrc} allowfullscreen></iframe>{:else}<div class="preview-stage"><div class="offline-mark">STREAM OFFLINE</div><div class="offline-subtitle">CONNECT TWITCH TO BEGIN</div></div>{/if}<div class="panel-footer"><span class="micro">VIDEO PLAYER</span><span class="badge">{playerSrc ? streamerName : 'OFFLINE'}</span></div></section>
<section class="panel activity-panel"><div class="panel-header"><span>ACTIVITY FEED</span><span class="panel-menu">⋮</span></div><div class="panel-tabs"><button class="tab active">ALL ACTIVITY</button><button class="tab">MODERATION</button></div>{#if activityEvents.length}<div class="activity-list">{#each activityEvents as event}<div class="activity-row"><span class="activity-type">{event.event_type}</span><span class="activity-time">{new Date(event.occurred_at).toLocaleTimeString()}</span></div>{/each}</div>{:else}<div class="empty-state"><div class="empty-glyph">◌</div><strong>Nothing here yet</strong><span>Follows, subscriptions, raids, and server events will appear here.</span></div>{/if}</section>
<section class="panel chat-panel"><div class="panel-header"><span>MY CHAT</span><span class="panel-menu">⋮</span></div><div class="chat-empty"><div class="empty-glyph">⌁</div><strong>Chat is waiting</strong><span>Connect a streamer or bot account to begin.</span></div><div class="chat-composer"><input class="control" placeholder="Send a message" disabled /><button class="primary" disabled>SEND</button></div></section>
<section class="panel chat-panel"><div class="panel-header"><span>MY CHAT</span><span class="panel-menu">⋮</span></div><div class="chat-empty"><div class="empty-glyph">⌁</div><strong>Chat is waiting</strong><span>Connect a streamer or bot account to begin.</span></div><div class="chat-composer"><div class="chat-identity-toggle"><button class:active={chatIdentity === 'streamer'} class="chat-identity" onclick={() => (chatIdentity = 'streamer')}>STREAMER</button><button class:active={chatIdentity === 'bot'} class="chat-identity" onclick={() => (chatIdentity = 'bot')}>BOT</button></div><div class="chat-send-row"><input class="control" bind:value={chatMessage} maxlength="500" placeholder={`Send as ${chatIdentity}`} onkeydown={(event) => event.key === 'Enter' && sendChatMessage()} /><button class="primary" onclick={sendChatMessage} disabled={chatBusy || !chatMessage.trim()}>SEND</button></div>{#if chatError}<div class="auth-error">{chatError}</div>{/if}</div></section>
<section class="panel quick-panel"><div class="panel-header"><span>QUICK ACTIONS</span><span class="panel-menu">⋮</span></div><div class="quick-grid"><button class="quick-action accent" onclick={() => (showStreamEditor = !showStreamEditor)}>EDIT STREAM INFO</button><button class="quick-action">MODERATION</button><button class="quick-action">BOT COMMANDS</button><button class="quick-action">OVERLAYS</button></div>{#if showStreamEditor}<div class="stream-editor"><label class="micro" for="stream-title">TITLE</label><input id="stream-title" class="control" bind:value={streamTitle} maxlength="140" /><label class="micro" for="stream-category">CATEGORY</label><input id="stream-category" class="control" value={categorySearch} oninput={(event) => searchCategories(event.currentTarget.value)} autocomplete="off" placeholder="Search Twitch categories" />{#if categoryOptions.length}<div class="category-options">{#each categoryOptions as category}<button class="category-option" onclick={() => { streamGameId = category.id; categorySearch = category.name; categoryOptions = []; }}><span>{category.name}</span><span class="mono">{category.id}</span></button>{/each}</div>{/if}<label class="micro" for="stream-language">LANGUAGE</label><input id="stream-language" class="control" bind:value={streamLanguage} maxlength="10" /><label class="micro" for="stream-tags">TAGS</label><input id="stream-tags" class="control" bind:value={streamTags} placeholder="comma, separated" /><label class="micro" for="stream-labels">CLASSIFICATION LABELS</label><input id="stream-labels" class="control" bind:value={streamClassificationLabels} placeholder="comma, separated" /><button class="primary control-button" onclick={saveStreamInfo} disabled={streamBusy}>SAVE STREAM INFO</button>{#if streamError}<div class="auth-error">{streamError}</div>{/if}</div>{/if}</section>
</div>
{/if}
+20 -2
View File
@@ -452,11 +452,29 @@ label {
color: var(--ed-dim);
}
.chat-composer {
display: flex;
gap: 5px;
display: grid;
gap: 6px;
padding: 8px;
border-top: 1px solid var(--ui-divider);
}
.chat-identity-toggle,
.chat-send-row {
display: flex;
gap: 5px;
}
.chat-identity {
flex: 1;
color: var(--ed-dim);
font-size: 9px;
}
.chat-identity.active {
border-color: var(--ui-line-focus);
background: var(--ui-surface-hi);
color: var(--ed-accent);
}
.chat-send-row .control {
min-width: 0;
}
.chat-composer .control {
min-width: 0;
}