···161161- `/send` - Post the next image in the queue immediately
162162- `/schedule [cron]` - Set posting schedule using cron syntax
163163- `/setcount [number]` - Set number of images per post interval
164164+- `/shuffle` - Toggle shuffle mode (randomizes queue after each post)
164165- `/clear` - Clear the entire queue
165166- `/cleancache` - Clean expired items from media cache
166167- `/announce` - Create a new announcement
···179180- **Item Removal**: Remove specific items from the queue with a single click
180181- **Reordering**: Move any item to the top of the queue to be posted next
181182- **Pagination**: Easily navigate through pages of queued items
183183+- **Shuffle Mode**: Automatically randomize the queue after each post with `/shuffle` command
182184183185The interface shows important information about each queued item including:
184186- Item position in queue
···206208- [x] SoFurry Scraper
207209- [x] Weasyl Scraper
208210- [x] Interactive Graphical Queue Manager
211211+- [x] Add shuffle mode for queue
209212- [ ] Add perceptual hashing
210213- [ ] Redo Queue Manager
211214- [ ] Redo Bluesky Module
+22-3
bot/telegramBot.js
···4747/cleancache - Clean expired items from media cache
4848/announce - Create a new announcement
4949/announcements - Manage existing announcements
5050+/shuffle - Toggle shuffle mode (shuffles queue after each post)
5051/update - Update bot from GitHub repository (owner only)
51525253Send any link to a supported site to add it to the queue.
···243244244245 this.bot.sendMessage(chatId, `Queue cleared (${queueLength} items removed).`);
245246 });
246246-247247+248248+ // Command to toggle shuffle mode
249249+ this.bot.onText(/\/shuffle/, async (msg) => {
250250+ const chatId = msg.chat.id;
251251+252252+ if (!this.isAuthorized(msg.from.id)) {
253253+ this.bot.sendMessage(chatId, 'You are not authorized to use this command.');
254254+ return;
255255+ }
256256+257257+ const isEnabled = queueManager.toggleShuffleMode();
258258+259259+ if (isEnabled) {
260260+ this.bot.sendMessage(chatId, '🔀 Shuffle mode enabled! Queue will be randomized after each post.');
261261+ } else {
262262+ this.bot.sendMessage(chatId, '📋 Shuffle mode disabled. Queue will maintain its order.');
263263+ }
264264+ });
265265+247266 // Command to manually trigger an update from GitHub
248267 this.bot.onText(/\/update/, async (msg) => {
249268 const chatId = msg.chat.id;
···360379 chatId,
361380 "Here's a preview of your announcement with formatting:",
362381 { parse_mode: 'Markdown' }
363363- );
382382+ ),
364383365384 // Send the actual preview
366385 await this.bot.sendMessage(
···815834 ]
816835 }
817836 }
818818- );
837837+ );
819838 }
820839 break;
821840 }
+58-1
queue/queueManager.js
···1414 this.autoSaveInterval = null;
1515 this.queueData = { queue: [] };
1616 this.postServices = ['telegram'];
1717+ this.shuffleMode = false; // Whether queue should be shuffled after item removal
17181819 // Add Discord to services if enabled
1920 if (config.discord?.enabled) {
···233234 if (allPosted) {
234235 const removed = this.queueData.queue.splice(index, 1)[0];
235236 console.log(`All services posted item: ${removed.title}, removed from queue`);
237237+238238+ // If shuffle mode is enabled and there are items remaining in the queue, shuffle them
239239+ if (this.shuffleMode && this.queueData.queue.length > 1) {
240240+ await this.shuffleQueue();
241241+ } else {
242242+ await this.saveQueueToDisk();
243243+ }
244244+ } else {
245245+ await this.saveQueueToDisk();
236246 }
237247238238- await this.saveQueueToDisk();
239248 return true;
240249 } catch (error) {
241250 console.error('Error marking item as posted:', error);
···409418 async shutdown() {
410419 this.stopScheduler();
411420 return this.saveQueueToDisk();
421421+ }
422422+423423+ /**
424424+ * Toggle shuffle mode on/off
425425+ * @returns {boolean} - New state of shuffle mode
426426+ */
427427+ toggleShuffleMode() {
428428+ this.shuffleMode = !this.shuffleMode;
429429+ console.log(`Queue shuffle mode ${this.shuffleMode ? 'enabled' : 'disabled'}`);
430430+ return this.shuffleMode;
431431+ }
432432+433433+ /**
434434+ * Get current state of shuffle mode
435435+ * @returns {boolean} - Current state of shuffle mode
436436+ */
437437+ isShuffleModeEnabled() {
438438+ return this.shuffleMode;
439439+ }
440440+441441+ /**
442442+ * Shuffle the remaining items in the queue using Fisher-Yates algorithm
443443+ * @returns {Promise<boolean>} - Whether shuffle was successful
444444+ */
445445+ async shuffleQueue() {
446446+ try {
447447+ if (this.queueData.queue.length <= 1) {
448448+ // No need to shuffle if queue is empty or has only one item
449449+ return true;
450450+ }
451451+452452+ const queue = this.queueData.queue;
453453+454454+ // Fisher-Yates (Knuth) shuffle algorithm
455455+ for (let i = queue.length - 1; i > 0; i--) {
456456+ // Generate random index between 0 and i (inclusive)
457457+ const j = Math.floor(Math.random() * (i + 1));
458458+ // Swap elements at i and j
459459+ [queue[i], queue[j]] = [queue[j], queue[i]];
460460+ }
461461+462462+ console.log(`Queue shuffled (${queue.length} items)`);
463463+ await this.saveQueueToDisk();
464464+ return true;
465465+ } catch (error) {
466466+ console.error('Error shuffling queue:', error);
467467+ return false;
468468+ }
412469 }
413470}
414471