easyproctor 2.5.2 → 2.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.js CHANGED
@@ -8791,6 +8791,14 @@ var BaseDetection = class {
8791
8791
  return "Face n\xE3o centralizada, mova-se para cima";
8792
8792
  case "wrong_face_position_move_bottom_detected":
8793
8793
  return "Face n\xE3o centralizada, mova-se para baixo";
8794
+ case "face_turned_left_detected":
8795
+ return "Face virada para a esquerda, centralize-a";
8796
+ case "face_turned_right_detected":
8797
+ return "Face virada para a direita, centralize-a";
8798
+ case "face_turned_up_detected":
8799
+ return "Face virada para cima, centralize-a";
8800
+ case "face_turned_down_detected":
8801
+ return "Face virada para baixo, centralize-a";
8794
8802
  default:
8795
8803
  return description;
8796
8804
  }
@@ -8821,7 +8829,14 @@ function buildVideoPreview() {
8821
8829
  // src/modules/faceDetection.ts
8822
8830
  var FaceDetection = class extends BaseDetection {
8823
8831
  constructor(options, paramsConfig, classVideo = "videoPreviewFrameDetection", classDiv = "liveViewFrameDetection") {
8824
- super("FaceDetector", `https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite`, options, paramsConfig, classVideo, classDiv);
8832
+ super(
8833
+ "FaceDetector",
8834
+ `https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite`,
8835
+ options,
8836
+ paramsConfig,
8837
+ classVideo,
8838
+ classDiv
8839
+ );
8825
8840
  this.emmitedPositionAlert = false;
8826
8841
  this.emmitedFaceAlert = false;
8827
8842
  }
@@ -8967,8 +8982,8 @@ var FaceDetection = class extends BaseDetection {
8967
8982
  for (const keypoint of detection.keypoints) {
8968
8983
  const keypointEl = document.createElement("span");
8969
8984
  keypointEl.className = "key-point";
8970
- let kpX = keypoint.x < 2 ? keypoint.x * videoElement.videoWidth : keypoint.x;
8971
- let kpY = keypoint.y < 2 ? keypoint.y * videoElement.videoHeight : keypoint.y;
8985
+ const kpX = keypoint.x < 2 ? keypoint.x * videoElement.videoWidth : keypoint.x;
8986
+ const kpY = keypoint.y < 2 ? keypoint.y * videoElement.videoHeight : keypoint.y;
8972
8987
  const mirroredKpX = videoElement.videoWidth - kpX;
8973
8988
  const screenX = mirroredKpX * scaleX;
8974
8989
  const screenY = kpY * scaleY;
@@ -9007,55 +9022,87 @@ var FaceDetection = class extends BaseDetection {
9007
9022
  }
9008
9023
  }
9009
9024
  if (result.detections.length === 0) return;
9010
- let face = result.detections[0].boundingBox;
9011
- let video = document.getElementById(this.classVideo);
9025
+ let failedFacePosition = false;
9026
+ const video = document.getElementById(this.classVideo);
9012
9027
  const imageWidth = video.videoWidth;
9013
9028
  const imageHeight = video.videoHeight;
9014
- let failedFacePosition = false;
9029
+ const detection = result.detections[0];
9030
+ const face = detection.boundingBox;
9031
+ const keypoints = detection.keypoints;
9032
+ const rightEye = keypoints[0];
9033
+ const leftEye = keypoints[1];
9034
+ const mouth = keypoints[3];
9035
+ const rightEar = keypoints[4];
9036
+ const leftEar = keypoints[5];
9037
+ const nose = keypoints[2];
9015
9038
  if (imageWidth > imageHeight) {
9016
9039
  if (face.height / imageHeight > 0.7) {
9017
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_size_detected", "position_detection_on_stream");
9040
+ !this.emmitedPositionAlert && this.handleAlert(
9041
+ "wrong_face_size_detected",
9042
+ "position_detection_on_stream"
9043
+ );
9018
9044
  failedFacePosition = true;
9019
9045
  this.emmitedPositionAlert = true;
9020
9046
  } else if (face.width / imageWidth > 0.7) {
9021
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_size_detected", "position_detection_on_stream");
9047
+ !this.emmitedPositionAlert && this.handleAlert(
9048
+ "wrong_face_size_detected",
9049
+ "position_detection_on_stream"
9050
+ );
9022
9051
  this.emmitedPositionAlert = true;
9023
9052
  failedFacePosition = true;
9024
9053
  }
9025
9054
  }
9026
- let start = [face.originX, face.originY];
9027
- let end = [face.originX + face.width, face.originY + face.height];
9055
+ const start = [face.originX, face.originY];
9056
+ const end = [face.originX + face.width, face.originY + face.height];
9028
9057
  if (start[0] < 0.1 * face.width || start[1] < 0.2 * face.height || end[0] > imageWidth - 0.1 * face.width || end[1] > imageHeight - 5) {
9029
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_edge_detected", "position_detection_on_stream");
9058
+ !this.emmitedPositionAlert && this.handleAlert(
9059
+ "wrong_face_position_edge_detected",
9060
+ "position_detection_on_stream"
9061
+ );
9030
9062
  this.emmitedPositionAlert = true;
9031
9063
  failedFacePosition = true;
9032
9064
  }
9033
- let leftGap = start[0];
9034
- let rightGap = imageWidth - end[0];
9035
- let topGap = start[1];
9036
- let bottomGap = imageHeight - end[1];
9065
+ const leftGap = start[0];
9066
+ const rightGap = imageWidth - end[0];
9067
+ const topGap = start[1];
9068
+ const bottomGap = imageHeight - end[1];
9037
9069
  if (leftGap > 2 * rightGap) {
9038
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_right_detected", "position_detection_on_stream");
9070
+ !this.emmitedPositionAlert && this.handleAlert(
9071
+ "wrong_face_position_move_right_detected",
9072
+ "position_detection_on_stream"
9073
+ );
9039
9074
  this.emmitedPositionAlert = true;
9040
9075
  failedFacePosition = true;
9041
9076
  }
9042
9077
  if (topGap > 4 * bottomGap) {
9043
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_top_detected", "position_detection_on_stream");
9078
+ !this.emmitedPositionAlert && this.handleAlert(
9079
+ "wrong_face_position_move_top_detected",
9080
+ "position_detection_on_stream"
9081
+ );
9044
9082
  this.emmitedPositionAlert = true;
9045
9083
  failedFacePosition = true;
9046
9084
  }
9047
9085
  if (rightGap > 2 * leftGap) {
9048
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_left_detected", "position_detection_on_stream");
9086
+ !this.emmitedPositionAlert && this.handleAlert(
9087
+ "wrong_face_position_move_left_detected",
9088
+ "position_detection_on_stream"
9089
+ );
9049
9090
  this.emmitedPositionAlert = true;
9050
9091
  failedFacePosition = true;
9051
9092
  }
9052
9093
  if (bottomGap > 3 * topGap) {
9053
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_bottom_detected", "position_detection_on_stream");
9094
+ !this.emmitedPositionAlert && this.handleAlert(
9095
+ "wrong_face_position_move_bottom_detected",
9096
+ "position_detection_on_stream"
9097
+ );
9054
9098
  this.emmitedPositionAlert = true;
9055
9099
  failedFacePosition = true;
9056
9100
  }
9057
9101
  if (failedFacePosition == false) {
9058
- this.emmitedPositionAlert && this.handleOk("ok_position_face_detected", "position_detection_on_stream");
9102
+ this.emmitedPositionAlert && this.handleOk(
9103
+ "ok_position_face_detected",
9104
+ "position_detection_on_stream"
9105
+ );
9059
9106
  this.emmitedPositionAlert = false;
9060
9107
  }
9061
9108
  }
@@ -11772,12 +11819,12 @@ var BackendService = class {
11772
11819
  });
11773
11820
  return result.data;
11774
11821
  }
11775
- async verifyFace(proctoringId2, faceImage) {
11822
+ async verifyFace(proctoringId2, faceImage, retry) {
11776
11823
  const result = await this.makeRequestAxios({
11777
11824
  path: `/Realtime/verify-face`,
11778
11825
  method: "POST",
11779
11826
  jwt: this.token,
11780
- body: { "proctoringId": proctoringId2, "base64": faceImage }
11827
+ body: { "proctoringId": proctoringId2, "base64": faceImage, "retry": retry }
11781
11828
  });
11782
11829
  return result.data;
11783
11830
  }
@@ -12653,7 +12700,6 @@ var CameraRecorder = class {
12653
12700
  noiseLimit: 40
12654
12701
  },
12655
12702
  imageBehaviourParameters: {
12656
- frames: 40,
12657
12703
  useUploadImage: true,
12658
12704
  uploadInterval: 20,
12659
12705
  saveVideo: true
@@ -12665,7 +12711,6 @@ var CameraRecorder = class {
12665
12711
  }
12666
12712
  };
12667
12713
  // private imageParams: ImageParameters = {
12668
- // frames: 40,
12669
12714
  // useUploadImage: true,
12670
12715
  // uploadInterval: 20,
12671
12716
  // saveVideo: true
@@ -12757,7 +12802,7 @@ var CameraRecorder = class {
12757
12802
  }
12758
12803
  }
12759
12804
  async startRecording(options) {
12760
- var _a2, _b, _c2, _d, _e3, _f, _g, _h, _i3;
12805
+ var _a2, _b, _c2, _d, _e3, _f, _g, _h;
12761
12806
  if ((((_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.detectPerson) || ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.detectCellPhone) || ((_c2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _c2.detectFace)) && !(options == null ? void 0 : options.retry)) {
12762
12807
  await this.initializeDetectors();
12763
12808
  }
@@ -12837,7 +12882,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
12837
12882
  this.filesToUpload = [];
12838
12883
  if (this.options.proctoringType == "REALTIME") {
12839
12884
  this.captureFrame();
12840
- this.sendFrameInterval = setInterval(() => this.captureFrame(), 1e3 * ((_i3 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _i3.realtimeUploadInterval));
12841
12885
  }
12842
12886
  this.packageCount = 0;
12843
12887
  }
@@ -12868,7 +12912,8 @@ Setting: ${JSON.stringify(settings, null, 2)}`
12868
12912
  clearInterval(this.imageInterval);
12869
12913
  clearInterval(this.sendFrameInterval);
12870
12914
  if (this.options.proctoringType == "REALTIME" && this.upload && this.backendToken) {
12871
- await this.sendPackage();
12915
+ await this.sendPackage(this.filesToUpload);
12916
+ await this.filesToUpload.splice(0, this.filesToUpload.length);
12872
12917
  }
12873
12918
  this.volumeMeter && this.volumeMeter.stop();
12874
12919
  this.intervalNoiseDetection && clearInterval(this.intervalNoiseDetection);
@@ -12938,23 +12983,29 @@ Setting: ${JSON.stringify(settings, null, 2)}`
12938
12983
  this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
12939
12984
  return this.canvas.toDataURL("image/jpeg");
12940
12985
  }
12941
- // De um em um segundo captura um frame ligado de 30 em 30 segundos)
12986
+ // captura um frame a cada intervalo de tempo definido no paramsConfig.videoBehaviourParameters?.realtimeCaptureInterval!
12942
12987
  captureFrame() {
12943
- var _a2;
12988
+ var _a2, _b;
12944
12989
  let imageFile;
12945
12990
  this.configImageCapture();
12991
+ let newCanvasWidth = this.videoOptions.width / 2;
12992
+ let newCanvasHeight = this.videoOptions.height / 2;
12993
+ if (newCanvasWidth < 320) newCanvasWidth = 320;
12994
+ if (newCanvasHeight < 180) newCanvasHeight = 180;
12995
+ this.canvas.width = newCanvasWidth;
12996
+ this.canvas.height = newCanvasHeight;
12946
12997
  const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
12947
- let initSend = false;
12948
- this.imageCount++;
12998
+ this.imageCount = 0;
12949
12999
  this.imageInterval = setInterval(async () => {
13000
+ console.log("capturando frame " + this.imageCount);
12950
13001
  this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
12951
13002
  const image_data_url = this.canvas.toDataURL("image/jpeg");
12952
13003
  if (this.proctoringId == void 0) return;
12953
- if (initSend == false && packSize == this.imageCount) {
12954
- initSend = true;
13004
+ if (packSize == this.imageCount) {
12955
13005
  this.imageCount = 0;
12956
- clearInterval(this.imageInterval);
12957
- this.sendPackage();
13006
+ const framesToSend = [...this.filesToUpload];
13007
+ this.sendPackage(framesToSend);
13008
+ await this.filesToUpload.splice(0, this.filesToUpload.length);
12958
13009
  }
12959
13010
  let imageName = `${this.proctoringId}_${this.imageCount + 1}.jpg`;
12960
13011
  imageFile = await this.getFile(image_data_url, imageName, "image/jpeg");
@@ -12962,22 +13013,24 @@ Setting: ${JSON.stringify(settings, null, 2)}`
12962
13013
  this.filesToUpload.push(imageFile);
12963
13014
  this.imageCount++;
12964
13015
  }
12965
- }, 1e3);
13016
+ }, ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) * 1e3);
12966
13017
  }
12967
13018
  // envia pacote de imagens
12968
- async sendPackage() {
13019
+ async sendPackage(framesToSend) {
13020
+ var _a2, _b;
12969
13021
  let pending = false;
12970
13022
  let undeliveredPackagesCount = 0;
13023
+ const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
13024
+ const packCaptureInterval = ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) + 1;
12971
13025
  if (this.upload && this.backendToken && !pending && this.filesToUpload.length > 0) {
12972
13026
  undeliveredPackagesCount = 0;
12973
13027
  pending = true;
12974
13028
  const zip = new import_jszip_min.default();
12975
- const files = this.filesToUpload;
12976
- for (const file of files) {
13029
+ for (const file of framesToSend) {
12977
13030
  zip.file(file.name, file);
12978
13031
  }
12979
13032
  const blob = await zip.generateAsync({ type: "blob" });
12980
- let packageName = "realtime_package_" + 30 * this.packageCount + ".zip";
13033
+ let packageName = "realtime_package_" + packSize * packCaptureInterval * this.packageCount + ".zip";
12981
13034
  const myPackage = new File(
12982
13035
  [blob],
12983
13036
  packageName,
@@ -12990,7 +13043,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
12990
13043
  this.backendToken
12991
13044
  );
12992
13045
  if (uploadResult.uploaded == true) {
12993
- await this.filesToUpload.splice(0, this.filesToUpload.length);
12994
13046
  this.packageCount++;
12995
13047
  } else {
12996
13048
  console.log("erro no upload do pacote");
@@ -13574,8 +13626,20 @@ var DeviceCheckerUI = class {
13574
13626
  );
13575
13627
  checkmark_FacePosition.appendChild(checkmark_stem_FacePosition);
13576
13628
  checkmark_FacePosition.appendChild(checkmark_kick_FacePosition);
13629
+ const info_FacePosition = document.createElement("span");
13630
+ const info_stem_FacePosition = document.createElement("div");
13631
+ const info_kick_FacePosition = document.createElement("div");
13632
+ info_FacePosition.style.marginLeft = "10px";
13633
+ info_FacePosition.setAttribute("class", "info");
13634
+ info_FacePosition.setAttribute("id", "info_FacePosition");
13635
+ info_FacePosition.style.display = "none";
13636
+ info_stem_FacePosition.setAttribute("class", "info_stem");
13637
+ info_kick_FacePosition.setAttribute("class", "info_kick");
13638
+ info_FacePosition.appendChild(info_stem_FacePosition);
13639
+ info_FacePosition.appendChild(info_kick_FacePosition);
13577
13640
  alertDivFacePosition.appendChild(checkmark_FacePosition);
13578
13641
  alertDivFacePosition.appendChild(facePositionAlert);
13642
+ alertDivFacePosition.appendChild(info_FacePosition);
13579
13643
  const alertDivAmbientVerify = document.createElement("div");
13580
13644
  alertDivAmbientVerify.setAttribute("class", "alert-div");
13581
13645
  alertDivAmbientVerify.setAttribute("id", "alertDivAmbientVerify");
@@ -13906,6 +13970,8 @@ var DeviceCheckerUI = class {
13906
13970
  realtimeAlertsUI(response) {
13907
13971
  const facePositionAlert = document.getElementById("facePositionAlert");
13908
13972
  const ambientVerifyAlert = document.getElementById("ambientVerifyAlert");
13973
+ const alertDivFacePosition = document.getElementById("alertDivFacePosition");
13974
+ const info_FacePosition = document.getElementById("info_FacePosition");
13909
13975
  const checkmark_FacePosition = document.getElementById("checkmark_FacePosition");
13910
13976
  const checkmark_stem_FacePosition = document.getElementById("checkmark_stem_FacePosition");
13911
13977
  const checkmark_kick_FacePosition = document.getElementById("checkmark_kick_FacePosition");
@@ -13916,6 +13982,8 @@ var DeviceCheckerUI = class {
13916
13982
  if (response.status === "OK") {
13917
13983
  facePositionAlert && (facePositionAlert.style.color = "#16A34A");
13918
13984
  ambientVerifyAlert && (ambientVerifyAlert.style.color = "#16A34A");
13985
+ alertDivFacePosition && alertDivFacePosition.setAttribute("title", ``);
13986
+ info_FacePosition && (info_FacePosition.style.display = "none");
13919
13987
  alertDivAmbientVerify && alertDivAmbientVerify.setAttribute("title", ``);
13920
13988
  if (checkmark_FacePosition) {
13921
13989
  checkmark_FacePosition.setAttribute("class", "checkmark");
@@ -13930,6 +13998,8 @@ var DeviceCheckerUI = class {
13930
13998
  } else {
13931
13999
  if (checkmark_FacePosition && (response.type === "position_detection_on_stream" || response.type === "face_detection_on_stream")) {
13932
14000
  facePositionAlert && (facePositionAlert.style.color = "#FF0000");
14001
+ info_FacePosition && (info_FacePosition.style.display = "block");
14002
+ alertDivFacePosition && alertDivFacePosition.setAttribute("title", response.description);
13933
14003
  checkmark_FacePosition.setAttribute("class", "checkmark_error");
13934
14004
  checkmark_stem_FacePosition.setAttribute("class", "checkmark_stem_error");
13935
14005
  checkmark_kick_FacePosition.setAttribute("class", "checkmark_kick_error");
@@ -15040,64 +15110,191 @@ var ProctoringUploader = class {
15040
15110
  var AlertRecorder = class {
15041
15111
  constructor(options, optionsProctoring) {
15042
15112
  this.alerts = [];
15113
+ // Variáveis para controle de inatividade
15114
+ this.lastActivityTime = Date.now();
15115
+ this.IDLE_THRESHOLD_MS = 3e4;
15116
+ // ==========================================
15117
+ // HANDLERS (Funções Arrow para preservar o 'this')
15118
+ // ==========================================
15119
+ // 1. LOST / RETURN FOCUS
15120
+ this.handleVisibilityChange = () => {
15121
+ if (document.visibilityState === "visible") {
15122
+ this.handleReturnFocus();
15123
+ } else {
15124
+ this.handleLostFocus();
15125
+ }
15126
+ };
15127
+ this.handleLostFocus = () => {
15128
+ if (this.getRelativeTime() > 1e4) {
15129
+ const alertPayload = {
15130
+ begin: this.getRelativeTime(),
15131
+ end: 0,
15132
+ alert: 25 /* FocusOff */,
15133
+ type: 3 /* Screen */
15134
+ };
15135
+ this.onLostFocusCallback(alertPayload);
15136
+ this.alerts.push(alertPayload);
15137
+ }
15138
+ };
15139
+ this.handleReturnFocus = () => {
15140
+ const lastAlert = this.alerts[this.alerts.length - 1];
15141
+ if (lastAlert) {
15142
+ this.onFocusCallback({
15143
+ begin: lastAlert.begin,
15144
+ end: this.getRelativeTime(),
15145
+ alert: 25 /* FocusOff */,
15146
+ type: 3 /* Screen */
15147
+ });
15148
+ lastAlert.end = this.getRelativeTime();
15149
+ }
15150
+ };
15151
+ // 2. SPLIT SCREEN DETECTION
15152
+ this.handleResize = () => {
15153
+ clearTimeout(this.resizeTimeout);
15154
+ this.resizeTimeout = setTimeout(() => {
15155
+ const screenWidth = window.screen.availWidth;
15156
+ const windowWidth = window.outerWidth;
15157
+ if (windowWidth < screenWidth * 0.85) {
15158
+ const msg = `Split Screen Detectado: Janela ocupa ${(windowWidth / screenWidth * 100).toFixed(0)}% da tela`;
15159
+ this.createAlert(43 /* SplitScreen */, 3 /* Screen */, msg);
15160
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15161
+ status: "ALERT",
15162
+ description: msg,
15163
+ type: "split_screen"
15164
+ });
15165
+ }
15166
+ }, 1e3);
15167
+ };
15168
+ // 3. DETECÇÃO DE INATIVIDADE (NO INPUT)
15169
+ this.handleUserActivity = (e3) => {
15170
+ this.lastActivityTime = Date.now();
15171
+ console.log("\u{1F449} handleUserActivity", e3);
15172
+ };
15173
+ // 5. CLIPBOARD HANDLERS
15174
+ this.handleCopy = (e3) => {
15175
+ e3.preventDefault();
15176
+ const msg = "Tentativa de Copiar (Clipboard)";
15177
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
15178
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15179
+ status: "ALERT",
15180
+ description: msg,
15181
+ type: "clipboard_copy"
15182
+ });
15183
+ };
15184
+ this.handleCut = (e3) => {
15185
+ e3.preventDefault();
15186
+ const msg = "Tentativa de Recortar (Clipboard)";
15187
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
15188
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15189
+ status: "ALERT",
15190
+ description: msg,
15191
+ type: "clipboard_cut"
15192
+ });
15193
+ };
15194
+ this.handlePaste = (e3) => {
15195
+ e3.preventDefault();
15196
+ const msg = "Tentativa de Colar (Clipboard)";
15197
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
15198
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15199
+ status: "ALERT",
15200
+ description: msg,
15201
+ type: "clipboard_paste"
15202
+ });
15203
+ };
15043
15204
  this.onLostFocusCallback = options.onLostFocusCallback;
15044
15205
  this.onFocusCallback = options.onFocusCallback;
15206
+ this.onRealtimeAlertCallback = options.onRealtimeAlertCallback;
15045
15207
  this.optionsProctoring = optionsProctoring;
15046
15208
  }
15047
15209
  async startRecording() {
15048
15210
  this.startTime = new Date(Date.now());
15049
- if (this.optionsProctoring.captureScreen) {
15050
- window.addEventListener("blur", () => this.onLostFocus());
15051
- window.addEventListener("focus", () => this.onReturnFocus());
15052
- }
15211
+ this.alerts = [];
15212
+ this.attachListeners();
15053
15213
  }
15054
15214
  async pauseRecording() {
15055
- window.removeEventListener("blur", () => this.onLostFocus());
15056
- window.removeEventListener("focus", () => this.onReturnFocus());
15215
+ this.detachListeners();
15057
15216
  }
15058
15217
  async resumeRecording() {
15059
- if (this.optionsProctoring.captureScreen) {
15060
- window.addEventListener("blur", () => this.onLostFocus());
15061
- window.addEventListener("focus", () => this.onReturnFocus());
15062
- }
15218
+ this.attachListeners();
15063
15219
  }
15064
15220
  async stopRecording() {
15065
- window.removeEventListener("blur", () => this.onLostFocus());
15066
- window.removeEventListener("focus", () => this.onReturnFocus());
15221
+ this.detachListeners();
15067
15222
  }
15068
15223
  async saveOnSession(session) {
15069
15224
  this.alerts.forEach((alert) => {
15070
15225
  session.addAlert(alert);
15071
15226
  });
15072
15227
  }
15073
- onLostFocus() {
15074
- var _a2;
15075
- if (Date.now() - ((_a2 = this.startTime) == null ? void 0 : _a2.getTime()) > 1e4) {
15076
- this.onLostFocusCallback({
15077
- begin: Date.now() - this.startTime.getTime(),
15078
- end: 0,
15079
- alert: 25 /* FocusOff */,
15080
- type: 3 /* Screen */
15081
- });
15082
- this.alerts.push({
15083
- begin: Date.now() - this.startTime.getTime(),
15084
- end: 0,
15085
- alert: 25 /* FocusOff */,
15086
- type: 3 /* Screen */
15087
- });
15088
- }
15228
+ // ==========================================
15229
+ // GERENCIAMENTO DE LISTENERS
15230
+ // ==========================================
15231
+ attachListeners() {
15232
+ if (this.optionsProctoring.captureScreen) {
15233
+ window.addEventListener("visibilitychange", this.handleVisibilityChange);
15234
+ window.addEventListener("resize", this.handleResize);
15235
+ window.document.addEventListener("copy", this.handleCopy);
15236
+ window.document.addEventListener("cut", this.handleCut);
15237
+ window.document.addEventListener("paste", this.handlePaste);
15238
+ }
15239
+ }
15240
+ detachListeners() {
15241
+ window.removeEventListener("visibilitychange", this.handleVisibilityChange);
15242
+ window.removeEventListener("resize", this.handleResize);
15243
+ window.document.removeEventListener("copy", this.handleCopy);
15244
+ window.document.removeEventListener("cut", this.handleCut);
15245
+ window.document.removeEventListener("paste", this.handlePaste);
15246
+ }
15247
+ // private startIdleChecker() {
15248
+ // this.stopIdleChecker(); // Garante que não tenha duplicação
15249
+ // this.idleCheckInterval = setInterval(() => {
15250
+ // const idleTime = Date.now() - this.lastActivityTime;
15251
+ // if (idleTime > this.IDLE_THRESHOLD_MS) {
15252
+ // // Verifica se já não mandamos esse alerta recentemente para não floodar
15253
+ // const lastAlert = this.alerts[this.alerts.length - 1];
15254
+ // // Se o último alerta não foi de inatividade ou foi há muito tempo, cria novo
15255
+ // if (!lastAlert || lastAlert.alert !== AlertCategory.ForbiddenAction || (this.getRelativeTime() - lastAlert.begin > 5000)) {
15256
+ // const msg = "Usuário inativo por muito tempo";
15257
+ // // Assumindo ForbiddenAction ou outra categoria adequada
15258
+ // this.createAlert(AlertCategory.ForbiddenAction, AlertType.System, msg);
15259
+ // this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15260
+ // status: 'ALERT',
15261
+ // description: msg,
15262
+ // type: 'user_idle'
15263
+ // });
15264
+ // }
15265
+ // }
15266
+ // }, 5000); // Checa a cada 5 segundos
15267
+ // }
15268
+ // private stopIdleChecker() {
15269
+ // if (this.idleCheckInterval) {
15270
+ // clearInterval(this.idleCheckInterval);
15271
+ // this.idleCheckInterval = null;
15272
+ // }
15273
+ // }
15274
+ // 4. BACKGROUND EVENTS (Eventos disparados manualmente)
15275
+ addBackgroundEvent(description, category = 200 /* System */) {
15276
+ this.createAlert(category, 3 /* Screen */, description);
15277
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15278
+ status: "ALERT",
15279
+ description,
15280
+ type: "background_event"
15281
+ });
15089
15282
  }
15090
- onReturnFocus() {
15091
- const lastAlert = this.alerts[this.alerts.length - 1];
15092
- if (lastAlert) {
15093
- this.onFocusCallback({
15094
- begin: lastAlert.begin,
15095
- end: Date.now() - this.startTime.getTime(),
15096
- alert: 25 /* FocusOff */,
15097
- type: 3 /* Screen */
15098
- });
15099
- lastAlert.end = Date.now() - this.startTime.getTime();
15100
- }
15283
+ // ==========================================
15284
+ // HELPERS
15285
+ // ==========================================
15286
+ getRelativeTime() {
15287
+ return Date.now() - this.startTime.getTime();
15288
+ }
15289
+ createAlert(category, type, description) {
15290
+ this.alerts.push({
15291
+ begin: this.getRelativeTime(),
15292
+ end: this.getRelativeTime(),
15293
+ // Eventos pontuais começam e terminam no mesmo ms (exceto focus)
15294
+ alert: category,
15295
+ type
15296
+ // description: description // Se sua interface Alert suportar descrição, descomente
15297
+ });
15101
15298
  }
15102
15299
  addAlert({ alert, type }) {
15103
15300
  this.alerts.push({
@@ -21314,7 +21511,8 @@ var _ExternalCameraChecker = class _ExternalCameraChecker {
21314
21511
  this.connection = new HubConnectionBuilder().withUrl(hubUrl, {
21315
21512
  accessTokenFactory: () => this.context.token,
21316
21513
  transport: HttpTransportType.WebSockets,
21317
- withCredentials: false
21514
+ withCredentials: false,
21515
+ skipNegotiation: true
21318
21516
  }).withAutomaticReconnect().configureLogging(LogLevel.Information).build();
21319
21517
  this.connection.on(
21320
21518
  "ReceiveMessage",
@@ -21520,7 +21718,6 @@ var Proctoring = class {
21520
21718
  noiseLimit: 40
21521
21719
  },
21522
21720
  imageBehaviourParameters: {
21523
- frames: 40,
21524
21721
  useUploadImage: true,
21525
21722
  uploadInterval: 20,
21526
21723
  saveVideo: true
@@ -21531,8 +21728,8 @@ var Proctoring = class {
21531
21728
  detectCellPhone: false,
21532
21729
  detectNoise: false,
21533
21730
  detectSpeech: false,
21534
- realtimePackageSize: 20,
21535
- realtimeUploadInterval: 30
21731
+ realtimePackageSize: 10,
21732
+ realtimeCaptureInterval: 2
21536
21733
  }
21537
21734
  };
21538
21735
  this.proctoringId = "";
@@ -21668,6 +21865,29 @@ var Proctoring = class {
21668
21865
  return null;
21669
21866
  }
21670
21867
  }
21868
+ // metodo para fechar o alerta realtime e fica tentando verificar a face até 5 vezes
21869
+ async stopRealtimeAlert(alert) {
21870
+ const verifyMaxRetries = 3;
21871
+ const verifyFace = async (verifyCount) => {
21872
+ if (verifyCount > verifyMaxRetries) return;
21873
+ try {
21874
+ var response = await this.backend.stopRealtimeAlert({
21875
+ proctoringId: this.proctoringId,
21876
+ begin: alert.begin,
21877
+ end: alert.end,
21878
+ warningCategoryEnum: this.convertRealtimeTypeToWarningType(alert.type),
21879
+ alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64(),
21880
+ retry: verifyCount < verifyMaxRetries - 1 ? true : false
21881
+ });
21882
+ console.log("response stopRealtimeAlert", response);
21883
+ return response;
21884
+ } catch (error) {
21885
+ console.log("error stopRealtimeAlert", error);
21886
+ return verifyFace(verifyCount + 1);
21887
+ }
21888
+ };
21889
+ await verifyFace(1);
21890
+ }
21671
21891
  async onRealtimeAlerts(options = {}) {
21672
21892
  this.setOnLostFocusAlertRecorderCallback();
21673
21893
  this.setOnFocusAlertRecorderCallback();
@@ -21682,13 +21902,7 @@ var Proctoring = class {
21682
21902
  alert: this.convertRealtimeCategoryToAlertCategory(response.category)
21683
21903
  });
21684
21904
  } else if (response.status === "OK") {
21685
- await this.backend.stopRealtimeAlert({
21686
- proctoringId: this.proctoringId,
21687
- begin: response.begin,
21688
- end: response.end,
21689
- warningCategoryEnum: this.convertRealtimeTypeToWarningType(response.type),
21690
- alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64()
21691
- });
21905
+ await this.stopRealtimeAlert(response);
21692
21906
  }
21693
21907
  }
21694
21908
  };
@@ -21738,7 +21952,8 @@ var Proctoring = class {
21738
21952
  const alertRecorder = new AlertRecorder(
21739
21953
  {
21740
21954
  onFocusCallback: (response) => this.onFocusAlertRecorderCallback(response),
21741
- onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response)
21955
+ onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response),
21956
+ onRealtimeAlertCallback: (response) => this.onRealtimeAlertsCallback(response)
21742
21957
  },
21743
21958
  options
21744
21959
  );
@@ -21846,11 +22061,23 @@ Navigator: ${JSON.stringify(_navigator2)}`
21846
22061
  startResponse.screenStream = this.allRecorders.screenRecorder.screenStream;
21847
22062
  }
21848
22063
  this.state = "Recording" /* Recording */;
21849
- setTimeout(async () => {
22064
+ let verifyFirstFaceIntervalCount = 0;
22065
+ let verifyingFace = false;
22066
+ this.verifyFirstFaceInterval = setInterval(async () => {
21850
22067
  if (this.sessionOptions.proctoringType === "REALTIME") {
21851
- await this.backend.verifyFace(this.proctoringId, await this.allRecorders.cameraRecorder.getCurrentImageBase64());
22068
+ if (verifyingFace) return;
22069
+ verifyingFace = true;
22070
+ verifyFirstFaceIntervalCount++;
22071
+ try {
22072
+ var response = await this.backend.verifyFace(this.proctoringId, await this.allRecorders.cameraRecorder.getCurrentImageBase64(), verifyFirstFaceIntervalCount > 5 ? false : true);
22073
+ verifyingFace = false;
22074
+ clearInterval(this.verifyFirstFaceInterval);
22075
+ } catch (error) {
22076
+ verifyingFace = false;
22077
+ return;
22078
+ }
21852
22079
  }
21853
- }, 1e3);
22080
+ }, 1500);
21854
22081
  return startResponse;
21855
22082
  } catch (error) {
21856
22083
  console.log(error);
@@ -21987,6 +22214,9 @@ Upload Services: ${uploaderServices}`,
21987
22214
  await this.backend.externalCameraFinish(externalSessionId);
21988
22215
  }
21989
22216
  }
22217
+ if (this.verifyFirstFaceInterval) {
22218
+ clearInterval(this.verifyFirstFaceInterval);
22219
+ }
21990
22220
  this.state = "Stop" /* Stop */;
21991
22221
  } catch (error) {
21992
22222
  await this.cancel();
@@ -22321,12 +22551,12 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
22321
22551
  const photo = new CapturePhoto();
22322
22552
  const login = proctoring.login.bind(proctoring);
22323
22553
  const originalStart = proctoring.start.bind(proctoring);
22324
- const start = async (parameters2) => {
22554
+ const start = async (parameters2, videoOptions) => {
22325
22555
  const deviceResult = checker.getDeviceCheckResult();
22326
22556
  if (deviceResult) {
22327
22557
  proctoring.setDeviceCheckData(deviceResult);
22328
22558
  }
22329
- return originalStart(parameters2);
22559
+ return originalStart(parameters2, videoOptions);
22330
22560
  };
22331
22561
  const finish = proctoring.finish.bind(proctoring);
22332
22562
  const pause = proctoring.pause.bind(proctoring);