function sendAlerts(callRoster, rosterSettings)
{
  let dirPath = window.opener.GT.scriptDir;
  let scriptExists = false;
  let script = "cr-alert.sh";

  let shouldAlert = 0;

  for (const entry in callRoster)
  {
    let callObj = callRoster[entry].callObj;

    // chrbayer: what does the tx field mean? no alerts are generated (at all) if this is in place...
    // if it's "not visible in the roster, don't put it in the report!"
    if (callRoster[entry].tx == false) continue;

    let call = callObj.DEcall;
    CR.scriptReport[call] = Object.assign({}, callObj);
    CR.scriptReport[call].dxccName = window.opener.GT.dxccToAltName[callObj.dxcc];
    CR.scriptReport[call].distance = (callObj.distance > 0) ? parseInt(callObj.distance * MyCircle.validateRadius(window.opener.distanceUnit.value)) : 0;

    delete CR.scriptReport[call].DEcall;
    CR.scriptReport[call].rect = null;
    delete CR.scriptReport[call].rect;
    delete CR.scriptReport[call].style;
    delete CR.scriptReport[call].wspr;
    delete CR.scriptReport[call].qso;
    delete CR.scriptReport[call].instance;

    if (rosterSettings.callMode != "all")
    {
      CR.scriptReport[call].shouldAlert = true;
      CR.scriptReport[call].reason.push(CR.rosterSettings.hunting);
    }

    if (callObj.alerted == false && rosterSettings.callMode == "all" && callObj.shouldAlert == true)
    {
      callObj.alerted = true;
      shouldAlert++;
    }
    else if (callObj.alerted == false && rosterSettings.callMode != "all")
    {
      callObj.alerted = true;
      shouldAlert++;
    }

    callObj.shouldAlert = false;
  }

  // NOTE: Ring alerts if needed
  try
  {
    if (fs.existsSync(dirPath))
    {
      if (window.opener.GT.platform == "windows")
      {
        script = "cr-alert.bat";
      }
      if (fs.existsSync(dirPath + script))
      {
        scriptExists = true;
        scriptIcon.innerHTML =
          "<div class='buttonScript' onclick='window.opener.toggleCRScript();'>" +
          (window.opener.GT.crScript == 1
            ? `<font color='lightgreen'>${$.i18n("sendAlerts.scriptEnabled")}</font>`
            : `<font color='yellow'>${$.i18n("sendAlerts.scriptDisabled")}</font>`) +
          "</div>";
        scriptIcon.style.display = "block";
      }
      else
      {
        scriptIcon.style.display = "none";
      }
    }
  }
  catch (e) {}

  if (shouldAlert > 0)
  {
    if (window.opener.GT.classicAlerts.huntRoster == true)
    {
      let notify = window.opener.huntRosterNotify.value;
      if (notify == "0")
      {
        let media = window.opener.huntRosterNotifyMedia.value;
        if (media != "none") window.opener.playAlertMediaFile(media);
      }
      else if (notify == "1")
      {
        window.opener.speakAlertString(window.opener.huntRosterNotifyWord.value);
      }
    }

    if (scriptExists && window.opener.GT.crScript == 1)
    {
      try
      {
        fs.writeFileSync(dirPath + "cr-alert.json", JSON.stringify(CR.scriptReport, null, 2));

        let thisProc = dirPath + script;
        let cp = require("child_process");
        let child = cp.spawn(thisProc, [], {
          detached: true,
          cwd: dirPath.slice(0, -1),
          stdio: ["ignore", "ignore", "ignore"]
        });
        child.unref();
      }
      catch (e)
      {
        conosle.log(e);
      }
      CR.scriptReport = Object();
    }
    else CR.scriptReport = Object();
  }
}
