Skip to content

Commit a05b8c2

Browse files
steveseguinactions-user
authored andcommitted
feat(poll): Add API for poll management
Implement new WebSocket API actions in `background.js` to control the poll feature. This allows external applications or tools to manage polls programmatically. Added actions include: - `resetpoll`: Resets the current poll, clearing votes. - `closepoll`: Closes the current poll, preventing new votes. - `loadpoll`: Loads a saved poll preset by its ID. - `setpollsettings`: Updates various settings of the current poll. - `getpollpresets`: Retrieves a list of saved poll presets. - `createpoll`: Creates a new poll with specified initial settings. Updated `api.md` to document the new poll API section, explaining each action and providing usage examples. Added corresponding UI elements and JavaScript logic in `sampleapi.html` to demonstrate how to call these new poll API actions, including loading presets and creating custom polls. Bumped extension version in `manifest.json` to reflect the added feature. [auto-enhanced]
1 parent 8bed7d8 commit a05b8c2

4 files changed

Lines changed: 274 additions & 2 deletions

File tree

api.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ When a message is sent, it goes to the specified output channel. Those who have
182182
14. **Draw Mode**
183183
- `{"action": "drawmode", "value": true}`
184184

185+
15. **Poll Operations**
186+
- Reset: `{"action": "resetpoll"}`
187+
- Close: `{"action": "closepoll"}`
188+
- Load Preset: `{"action": "loadpoll", "value": {"pollId": "poll-123456"}}`
189+
- Set Settings: `{"action": "setpollsettings", "value": {"pollQuestion": "What's your favorite color?", "pollType": "multiple", "multipleChoiceOptions": "Red\nBlue\nGreen"}}`
190+
- Get Presets: `{"action": "getpollpresets"}`
191+
- Create New: `{"action": "createpoll", "value": {"settings": {"pollQuestion": "New Poll", "pollType": "freeform"}}}`
192+
185193
### Channel-Specific Messaging
186194

187195
You can send messages to specific channels using the `content` action with a channel number:
@@ -762,7 +770,7 @@ This integration allows streamers or moderators to manage waitlists or giveaways
762770

763771
These pages may lack API support directly, however in some cases they can be controlled via the extension's API.
764772

765-
For example the waitlist has some functions that can be controlled via the extensiion:
773+
For example the waitlist has some functions that can be controlled via the extension:
766774

767775
```
768776
removefromwaitlist
@@ -773,6 +781,44 @@ downloadwaitlist
773781
selectwinner
774782
```
775783

784+
## Poll Control via API
785+
786+
The poll system can now be controlled through the API with the following actions:
787+
788+
### Basic Poll Controls
789+
- **Reset Poll**: `{"action": "resetpoll"}` - Resets the current poll, clearing all votes
790+
- **Close Poll**: `{"action": "closepoll"}` - Closes the current poll, preventing new votes
791+
792+
### Advanced Poll Controls
793+
- **Load Poll Preset**: `{"action": "loadpoll", "value": {"pollId": "poll-123456"}}` - Loads a previously saved poll preset by its ID
794+
- **Get Poll Presets**: `{"action": "getpollpresets"}` - Returns a list of all saved poll presets with their IDs and names
795+
- **Set Poll Settings**: `{"action": "setpollsettings", "value": {...}}` - Updates the current poll settings
796+
- Available settings: `pollType`, `pollQuestion`, `multipleChoiceOptions`, `pollStyle`, `pollTimer`, `pollTimerState`, `pollTally`, `pollSpam`
797+
- **Create New Poll**: `{"action": "createpoll", "value": {"settings": {...}}}` - Creates a new poll with specified settings
798+
799+
### Example Usage
800+
```javascript
801+
// Create a multiple choice poll
802+
ws.send(JSON.stringify({
803+
action: "createpoll",
804+
value: {
805+
settings: {
806+
pollType: "multiple",
807+
pollQuestion: "What's your favorite streaming platform?",
808+
multipleChoiceOptions: "Twitch\nYouTube\nFacebook\nOther",
809+
pollTimer: "120",
810+
pollTimerState: true
811+
}
812+
}
813+
}));
814+
815+
// Load a saved poll preset
816+
ws.send(JSON.stringify({
817+
action: "loadpoll",
818+
value: { pollId: "poll-1234567890" }
819+
}));
820+
```
821+
776822
Just to touch on the Battle Royal game though,
777823

778824
## Battle Page (battle.html)

background.js

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5457,6 +5457,36 @@ function setupSocket() {
54575457
} else if (data.action && data.action === "closepoll") {
54585458
sendTargetP2P({cmd:"closepoll"},"poll");
54595459
resp = true;
5460+
} else if (data.action && data.action === "loadpoll") {
5461+
// Load a saved poll preset by ID
5462+
if (data.value && data.value.pollId) {
5463+
loadPollPreset(data.value.pollId);
5464+
resp = true;
5465+
}
5466+
} else if (data.action && data.action === "setpollsettings") {
5467+
// Directly set poll settings via API
5468+
if (data.value && typeof data.value === 'object') {
5469+
updatePollSettings(data.value);
5470+
resp = true;
5471+
}
5472+
} else if (data.action && data.action === "getpollpresets") {
5473+
// Return list of saved poll presets
5474+
getPollPresets(function(presets) {
5475+
if (data.get && e.data.UUID) {
5476+
var ret = {};
5477+
ret.callback = {};
5478+
ret.callback.get = data.get;
5479+
ret.callback.result = presets;
5480+
socketserver.send(JSON.stringify(ret));
5481+
}
5482+
});
5483+
resp = true;
5484+
} else if (data.action && data.action === "createpoll") {
5485+
// Create a new poll with specific settings
5486+
if (data.value && data.value.settings) {
5487+
createNewPoll(data.value.settings);
5488+
resp = true;
5489+
}
54605490
} else if (data.action && data.action === "stopentries") {
54615491
toggleEntries(false);
54625492
resp = true;
@@ -6474,6 +6504,95 @@ function initializePoll() {
64746504
} catch (e) {}
64756505
}
64766506

6507+
function loadPollPreset(pollId) {
6508+
chrome.storage.local.get(['savedPolls'], function(result) {
6509+
if (result.savedPolls) {
6510+
try {
6511+
const savedPolls = JSON.parse(result.savedPolls);
6512+
const poll = savedPolls.find(p => p.id === pollId);
6513+
if (poll && poll.settings) {
6514+
// Update settings with the loaded poll
6515+
Object.keys(poll.settings).forEach(key => {
6516+
if (settings.hasOwnProperty(key)) {
6517+
settings[key] = poll.settings[key];
6518+
}
6519+
});
6520+
// Send updated settings to poll overlay
6521+
sendTargetP2P({settings:settings}, "poll");
6522+
// Save updated settings
6523+
chrome.storage.local.set({settings: settings});
6524+
}
6525+
} catch (e) {
6526+
log("Error loading poll preset: " + e.message);
6527+
}
6528+
}
6529+
});
6530+
}
6531+
6532+
function updatePollSettings(newSettings) {
6533+
try {
6534+
// Update poll-related settings
6535+
const pollKeys = ['pollType', 'pollQuestion', 'multipleChoiceOptions',
6536+
'pollStyle', 'pollTimer', 'pollTimerState', 'pollTally', 'pollSpam'];
6537+
6538+
pollKeys.forEach(key => {
6539+
if (newSettings.hasOwnProperty(key)) {
6540+
settings[key] = newSettings[key];
6541+
}
6542+
});
6543+
6544+
// Send updated settings to poll overlay
6545+
sendTargetP2P({settings:settings}, "poll");
6546+
// Save settings
6547+
chrome.storage.local.set({settings: settings});
6548+
} catch (e) {
6549+
log("Error updating poll settings: " + e.message);
6550+
}
6551+
}
6552+
6553+
function getPollPresets(callback) {
6554+
chrome.storage.local.get(['savedPolls'], function(result) {
6555+
try {
6556+
if (result.savedPolls) {
6557+
const savedPolls = JSON.parse(result.savedPolls);
6558+
// Return simplified list with id and name
6559+
const presets = savedPolls.map(poll => ({
6560+
id: poll.id,
6561+
name: poll.name
6562+
}));
6563+
callback(presets);
6564+
} else {
6565+
callback([]);
6566+
}
6567+
} catch (e) {
6568+
log("Error getting poll presets: " + e.message);
6569+
callback([]);
6570+
}
6571+
});
6572+
}
6573+
6574+
function createNewPoll(pollSettings) {
6575+
try {
6576+
// Reset to default poll settings
6577+
const defaultSettings = {
6578+
pollType: 'freeform',
6579+
pollQuestion: '',
6580+
multipleChoiceOptions: '',
6581+
pollStyle: 'default',
6582+
pollTimer: '60',
6583+
pollTimerState: false,
6584+
pollTally: true,
6585+
pollSpam: false
6586+
};
6587+
6588+
// Merge with provided settings
6589+
const finalSettings = {...defaultSettings, ...pollSettings};
6590+
updatePollSettings(finalSettings);
6591+
} catch (e) {
6592+
log("Error creating new poll: " + e.message);
6593+
}
6594+
}
6595+
64776596
function initializeWaitlist() {
64786597
try {
64796598
if (!settings.waitlistmode) { // stop and clear

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "Social Stream Ninja",
33
"description": "Powerful tooling to engage live chat on Youtube, Twitch, Zoom, and more.",
44
"manifest_version": 3,
5-
"version": "3.24.2",
5+
"version": "3.24.3",
66
"homepage_url": "http://socialstream.ninja/",
77
"icons": {
88
"128": "icons/icon-128.png"

sampleapi.html

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,10 +363,27 @@ <h2>Waitlist Commands</h2>
363363

364364
<div class="section">
365365
<h2>Poll Commands</h2>
366+
<div class="mb-3">
367+
<select id="pollPresetSelect" class="form-control">
368+
<option value="">Select a poll preset...</option>
369+
</select>
370+
<button class="btn btn-info btn-sm mt-2" onclick="refreshPollPresets()">Refresh Presets</button>
371+
</div>
372+
<div class="mb-3">
373+
<input type="text" id="pollQuestion" class="form-control" placeholder="Poll question">
374+
<select id="pollType" class="form-control mt-2">
375+
<option value="freeform">Freeform</option>
376+
<option value="multiple">Multiple Choice</option>
377+
</select>
378+
<textarea id="pollOptions" class="form-control mt-2" placeholder="Enter options (one per line)" rows="4" style="display:none;"></textarea>
379+
</div>
366380
<div class="btn-group" role="group">
367381
<button class="btn btn-warning" onclick="sendCommand('resetpoll')">Reset Poll</button>
368382
<button class="btn btn-warning" onclick="sendCommand('closepoll')">Close Poll</button>
383+
<button class="btn btn-warning" onclick="loadPollPreset()">Load Preset</button>
384+
<button class="btn btn-warning" onclick="createCustomPoll()">Create Custom Poll</button>
369385
</div>
386+
<p class="note">Extended poll controls allow you to load saved presets, create custom polls, and manage poll settings via the API.</p>
370387
</div>
371388

372389
<div class="section">
@@ -771,6 +788,96 @@ <h2>Incoming Messages</h2>
771788
log.textContent = JSON.stringify(requestData, null, 2);
772789
log.style.display = 'block';
773790
}
791+
792+
// Poll-specific functions
793+
function refreshPollPresets() {
794+
if (!socket || socket.readyState !== WebSocket.OPEN) {
795+
alert('Please connect first');
796+
return;
797+
}
798+
799+
// Generate a unique callback ID
800+
const callbackId = 'cb_' + Date.now();
801+
802+
// Send request with callback
803+
socket.send(JSON.stringify({
804+
action: 'getpollpresets',
805+
apiid: sessionID,
806+
get: callbackId
807+
}));
808+
809+
// Listen for response
810+
socket.addEventListener('message', function handlePresets(event) {
811+
try {
812+
const data = JSON.parse(event.data);
813+
if (data.callback && data.callback.get === callbackId) {
814+
// Remove this listener
815+
socket.removeEventListener('message', handlePresets);
816+
817+
// Update the select dropdown
818+
const select = document.getElementById('pollPresetSelect');
819+
select.innerHTML = '<option value="">Select a poll preset...</option>';
820+
821+
if (data.callback.result && Array.isArray(data.callback.result)) {
822+
data.callback.result.forEach(preset => {
823+
const option = document.createElement('option');
824+
option.value = preset.id;
825+
option.textContent = preset.name;
826+
select.appendChild(option);
827+
});
828+
}
829+
}
830+
} catch (e) {
831+
console.error('Error parsing preset response:', e);
832+
}
833+
});
834+
}
835+
836+
function loadPollPreset() {
837+
const presetId = document.getElementById('pollPresetSelect').value;
838+
if (!presetId) {
839+
alert('Please select a poll preset');
840+
return;
841+
}
842+
843+
sendCommand('loadpoll', { pollId: presetId });
844+
}
845+
846+
function createCustomPoll() {
847+
const question = document.getElementById('pollQuestion').value;
848+
const type = document.getElementById('pollType').value;
849+
const optionsText = document.getElementById('pollOptions').value;
850+
851+
if (!question) {
852+
alert('Please enter a poll question');
853+
return;
854+
}
855+
856+
const settings = {
857+
pollQuestion: question,
858+
pollType: type,
859+
pollTimer: '120',
860+
pollTimerState: true,
861+
pollTally: true,
862+
pollSpam: false
863+
};
864+
865+
if (type === 'multiple' && optionsText) {
866+
settings.multipleChoiceOptions = optionsText;
867+
}
868+
869+
sendCommand('createpoll', { settings });
870+
}
871+
872+
// Show/hide options textarea based on poll type
873+
document.getElementById('pollType').addEventListener('change', function() {
874+
const optionsTextarea = document.getElementById('pollOptions');
875+
if (this.value === 'multiple') {
876+
optionsTextarea.style.display = 'block';
877+
} else {
878+
optionsTextarea.style.display = 'none';
879+
}
880+
});
774881
</script>
775882
</body>
776883
</html>

0 commit comments

Comments
 (0)