/ clis / discord-app / delete.js
delete.js
  1  import { cli, Strategy } from '@jackwener/opencli/registry';
  2  import { CommandExecutionError } from '@jackwener/opencli/errors';
  3  
  4  function buildDeleteScript(messageId) {
  5      return `(async () => {
  6        try {
  7            const messageId = ${JSON.stringify(messageId)};
  8  
  9            // Find the message element by its ID attribute (format: chat-messages-{channelId}-{messageId})
 10            const msgEl = document.querySelector('[id$="-' + messageId + '"]');
 11            if (!msgEl) {
 12                return { ok: false, message: 'Could not find a message with ID ' + messageId + ' in the current channel.' };
 13            }
 14  
 15            // Find the closest list item wrapper that Discord uses for messages
 16            const listItem = msgEl.closest('[id^="chat-messages-"]') || msgEl;
 17  
 18            // Hover over the message to reveal the action toolbar
 19            listItem.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
 20            listItem.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
 21            await new Promise(r => setTimeout(r, 500));
 22  
 23            // Look for the "More" button in the message toolbar
 24            // Discord shows a toolbar with buttons when hovering over a message
 25            const toolbar = listItem.querySelector('[class*="toolbar"]') ||
 26                document.querySelector('[id^="message-actions-"]');
 27            if (!toolbar) {
 28                return { ok: false, message: 'Could not find the message action toolbar. Try scrolling so the message is fully visible.' };
 29            }
 30  
 31            const buttons = Array.from(toolbar.querySelectorAll('button, [role="button"], div[class*="button"]'));
 32            const moreBtn = buttons.find(btn => {
 33                const label = (btn.getAttribute('aria-label') || '').toLowerCase();
 34                return label === 'more' || label.includes('more');
 35            });
 36            if (!moreBtn) {
 37                return { ok: false, message: 'Could not find the "More" button on the message toolbar.' };
 38            }
 39  
 40            moreBtn.click();
 41            await new Promise(r => setTimeout(r, 500));
 42  
 43            // Find "Delete Message" in the context menu
 44            const menuItems = Array.from(document.querySelectorAll('[role="menuitem"], [id*="message-actions"]'));
 45            const deleteItem = menuItems.find(item => {
 46                const text = (item.textContent || '').trim().toLowerCase();
 47                return text.includes('delete message') || text === 'delete';
 48            });
 49  
 50            if (!deleteItem) {
 51                // Close the menu by pressing Escape
 52                document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
 53                return { ok: false, message: 'No "Delete Message" option found. You may not have permission to delete this message.' };
 54            }
 55  
 56            deleteItem.click();
 57            await new Promise(r => setTimeout(r, 500));
 58  
 59            // Confirm deletion in the modal dialog
 60            const confirmBtn = document.querySelector('[type="submit"], button[class*="colorRed"], button[class*="danger"]');
 61            if (!confirmBtn) {
 62                return { ok: false, message: 'Delete confirmation dialog did not appear.' };
 63            }
 64  
 65            confirmBtn.click();
 66            return { ok: true, message: 'Message ' + messageId + ' deleted successfully.' };
 67        } catch (e) {
 68            return { ok: false, message: e.toString() };
 69        }
 70    })()`;
 71  }
 72  
 73  cli({
 74      site: 'discord-app',
 75      name: 'delete',
 76      description: 'Delete a message by its ID in the active Discord channel',
 77      domain: 'localhost',
 78      strategy: Strategy.UI,
 79      browser: true,
 80      args: [
 81          {
 82              name: 'message_id',
 83              type: 'string',
 84              required: true,
 85              positional: true,
 86              help: 'The ID of the message to delete (visible via Developer Mode or the read command)',
 87          },
 88      ],
 89      columns: ['status', 'message'],
 90      func: async (page, kwargs) => {
 91          if (!page)
 92              throw new CommandExecutionError('Browser session required for discord-app delete');
 93          const messageId = kwargs.message_id;
 94          if (!/^\d+$/.test(messageId)) {
 95              throw new CommandExecutionError(
 96                  `Invalid message ID: "${messageId}". A Discord message ID is a numeric snowflake (e.g. 1234567890123456789).`
 97              );
 98          }
 99          // Wait a moment for the chat to be fully loaded
100          await page.wait(0.5);
101          const result = await page.evaluate(buildDeleteScript(messageId));
102          if (result.ok) {
103              await page.wait(1);
104          }
105          return [{
106              status: result.ok ? 'success' : 'failed',
107              message: result.message,
108          }];
109      },
110  });
111  
112  export const __test__ = {
113      buildDeleteScript,
114  };