Action

Send brrr

Last update 22 days ago - Unlisted

Steps

  • script

    // brrr Notification – Drafts Action (Advanced)
    // Replace with your personal webhook from the brrr app
    const WEBHOOK_URL = "xx";
    
    const SOUNDS = [
      "default", "system", "brrr", "bell_ringing", "bubble_ding",
      "bubbly_success_ding", "cat_meow", "calm1", "calm2", "cha_ching",
      "dog_barking", "door_bell", "duck_quack", "short_triple_blink",
      "upbeat_bells", "warm_soft_error"
    ];
    
    // Apple enforces 4096 bytes total. brrr uses some overhead (~200 bytes safe estimate).
    const MAX_PAYLOAD_BYTES = 3800;
    
    function byteSize(str) {
      var bytes = 0;
      for (var i = 0; i < str.length; i++) {
        var code = str.charCodeAt(i);
        if (code < 0x80)        bytes += 1;
        else if (code < 0x800)  bytes += 2;
        else if (code < 0xD800 || code >= 0xE000) bytes += 3;
        else {
          // Surrogate pair (emoji etc.) = 4 bytes
          i++;
          bytes += 4;
        }
      }
      return bytes;
    }
    
    
    function run() {
      // --- Prompt ---
      const p = Prompt.create();
      p.title = "Send brrr Notification";
    
      p.addTextField("title",    "Title",    "", { placeholder: "Notification title" });
      p.addTextField("subtitle", "Subtitle", "", { placeholder: "Optional subtitle" });
      p.addTextView("message", "Message", draft.content, { height: 80 });
      p.addSelect("sound", "Sound", SOUNDS, ["default"], false);
      p.addTextField("open_url",  "Open URL",  "", { placeholder: "https://... (optional)" });
      p.addTextField("image_url", "Image URL", "", { placeholder: "https://... (iPhone/iPad only)" });
      p.addSwitch("useExpiry", "Set Expiration Date?", false);
      p.addDatePicker("expiry", "Expiration", new Date(), { mode: "dateAndTime" });
      p.addButton("Send 🔔");
    
      if (!p.show() || p.buttonPressed !== "Send 🔔") {
        context.cancel("Cancelled");
        return;
      }
    
      // --- Read values ---
      const title     = p.fieldValues["title"].trim();
      const subtitle  = p.fieldValues["subtitle"].trim();
      const message   = p.fieldValues["message"].trim();
      const sound     = p.fieldValues["sound"][0];
      const open_url  = p.fieldValues["open_url"].trim();
      const image_url = p.fieldValues["image_url"].trim();
    
      // --- Build payload ---
      const payload = {};
    
      if (title)     payload.title     = title;
      if (subtitle)  payload.subtitle  = subtitle;
      if (message)   payload.message   = message;
      if (sound)     payload.sound     = sound;
      if (open_url)  payload.open_url  = open_url;
      if (image_url) payload.image_url = image_url;
    
      if (p.fieldValues["useExpiry"]) {
        payload.expiration_date = p.fieldValues["expiry"].toISOString();
      }
    
      // --- Guard: check total payload size against Apple's 4 KB limit ---
      const payloadJSON  = JSON.stringify(payload);
      const payloadBytes = byteSize(payloadJSON);
    
      if (payloadBytes > MAX_PAYLOAD_BYTES) {
        const warn = Prompt.create();
        warn.title   = "⚠️ Payload Too Large";
        warn.message = `Your payload is ${payloadBytes} bytes, but the limit is ~${MAX_PAYLOAD_BYTES} bytes (Apple enforces 4 KB total).\n\nPlease shorten your title, subtitle, or message and try again.`;
        warn.addButton("OK");
        warn.show();
        context.cancel("Payload too large");
        return;
      }
    
      // --- Send request ---
      const http     = HTTP.create();
      const response = http.request({
        url:     WEBHOOK_URL,
        method:  "POST",
        headers: { "Content-Type": "application/json" },
        data:    payload
      });
    
      if (response.success && response.statusCode < 300) {
        app.displaySuccessMessage(`Notification sent! 🔔 (${payloadBytes} bytes)`);
      } else {
        let errorDetail = "";
    
        if (response.error) {
          errorDetail = response.error;
        } else if (response.responseText) {
          try {
            const parsed = JSON.parse(response.responseText);
            errorDetail  = parsed.error || parsed.message || response.responseText;
          } catch (_) {
            errorDetail = response.responseText;
          }
        } else {
          errorDetail = "Unknown error";
        }
    
        const fullError = `HTTP ${response.statusCode}: ${errorDetail}`;
        context.fail(fullError);
    
        const errPrompt = Prompt.create();
        errPrompt.title   = "❌ brrr Failed";
        errPrompt.message = fullError;
        errPrompt.addButton("OK");
        errPrompt.show();
      }
    }
    
    run();
    

Options

  • After Success Default
    Notification Info
    Log Level Info
Items available in the Drafts Directory are uploaded by community members. Use appropriate caution reviewing downloaded items before use.