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/index.js CHANGED
@@ -26076,6 +26076,14 @@ var BaseDetection = class {
26076
26076
  return "Face n\xE3o centralizada, mova-se para cima";
26077
26077
  case "wrong_face_position_move_bottom_detected":
26078
26078
  return "Face n\xE3o centralizada, mova-se para baixo";
26079
+ case "face_turned_left_detected":
26080
+ return "Face virada para a esquerda, centralize-a";
26081
+ case "face_turned_right_detected":
26082
+ return "Face virada para a direita, centralize-a";
26083
+ case "face_turned_up_detected":
26084
+ return "Face virada para cima, centralize-a";
26085
+ case "face_turned_down_detected":
26086
+ return "Face virada para baixo, centralize-a";
26079
26087
  default:
26080
26088
  return description;
26081
26089
  }
@@ -26106,7 +26114,14 @@ function buildVideoPreview() {
26106
26114
  // src/modules/faceDetection.ts
26107
26115
  var FaceDetection = class extends BaseDetection {
26108
26116
  constructor(options, paramsConfig, classVideo = "videoPreviewFrameDetection", classDiv = "liveViewFrameDetection") {
26109
- 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);
26117
+ super(
26118
+ "FaceDetector",
26119
+ `https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite`,
26120
+ options,
26121
+ paramsConfig,
26122
+ classVideo,
26123
+ classDiv
26124
+ );
26110
26125
  this.emmitedPositionAlert = false;
26111
26126
  this.emmitedFaceAlert = false;
26112
26127
  }
@@ -26252,8 +26267,8 @@ var FaceDetection = class extends BaseDetection {
26252
26267
  for (const keypoint of detection.keypoints) {
26253
26268
  const keypointEl = document.createElement("span");
26254
26269
  keypointEl.className = "key-point";
26255
- let kpX = keypoint.x < 2 ? keypoint.x * videoElement.videoWidth : keypoint.x;
26256
- let kpY = keypoint.y < 2 ? keypoint.y * videoElement.videoHeight : keypoint.y;
26270
+ const kpX = keypoint.x < 2 ? keypoint.x * videoElement.videoWidth : keypoint.x;
26271
+ const kpY = keypoint.y < 2 ? keypoint.y * videoElement.videoHeight : keypoint.y;
26257
26272
  const mirroredKpX = videoElement.videoWidth - kpX;
26258
26273
  const screenX = mirroredKpX * scaleX;
26259
26274
  const screenY = kpY * scaleY;
@@ -26292,55 +26307,87 @@ var FaceDetection = class extends BaseDetection {
26292
26307
  }
26293
26308
  }
26294
26309
  if (result.detections.length === 0) return;
26295
- let face = result.detections[0].boundingBox;
26296
- let video = document.getElementById(this.classVideo);
26310
+ let failedFacePosition = false;
26311
+ const video = document.getElementById(this.classVideo);
26297
26312
  const imageWidth = video.videoWidth;
26298
26313
  const imageHeight = video.videoHeight;
26299
- let failedFacePosition = false;
26314
+ const detection = result.detections[0];
26315
+ const face = detection.boundingBox;
26316
+ const keypoints = detection.keypoints;
26317
+ const rightEye = keypoints[0];
26318
+ const leftEye = keypoints[1];
26319
+ const mouth = keypoints[3];
26320
+ const rightEar = keypoints[4];
26321
+ const leftEar = keypoints[5];
26322
+ const nose = keypoints[2];
26300
26323
  if (imageWidth > imageHeight) {
26301
26324
  if (face.height / imageHeight > 0.7) {
26302
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_size_detected", "position_detection_on_stream");
26325
+ !this.emmitedPositionAlert && this.handleAlert(
26326
+ "wrong_face_size_detected",
26327
+ "position_detection_on_stream"
26328
+ );
26303
26329
  failedFacePosition = true;
26304
26330
  this.emmitedPositionAlert = true;
26305
26331
  } else if (face.width / imageWidth > 0.7) {
26306
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_size_detected", "position_detection_on_stream");
26332
+ !this.emmitedPositionAlert && this.handleAlert(
26333
+ "wrong_face_size_detected",
26334
+ "position_detection_on_stream"
26335
+ );
26307
26336
  this.emmitedPositionAlert = true;
26308
26337
  failedFacePosition = true;
26309
26338
  }
26310
26339
  }
26311
- let start = [face.originX, face.originY];
26312
- let end = [face.originX + face.width, face.originY + face.height];
26340
+ const start = [face.originX, face.originY];
26341
+ const end = [face.originX + face.width, face.originY + face.height];
26313
26342
  if (start[0] < 0.1 * face.width || start[1] < 0.2 * face.height || end[0] > imageWidth - 0.1 * face.width || end[1] > imageHeight - 5) {
26314
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_edge_detected", "position_detection_on_stream");
26343
+ !this.emmitedPositionAlert && this.handleAlert(
26344
+ "wrong_face_position_edge_detected",
26345
+ "position_detection_on_stream"
26346
+ );
26315
26347
  this.emmitedPositionAlert = true;
26316
26348
  failedFacePosition = true;
26317
26349
  }
26318
- let leftGap = start[0];
26319
- let rightGap = imageWidth - end[0];
26320
- let topGap = start[1];
26321
- let bottomGap = imageHeight - end[1];
26350
+ const leftGap = start[0];
26351
+ const rightGap = imageWidth - end[0];
26352
+ const topGap = start[1];
26353
+ const bottomGap = imageHeight - end[1];
26322
26354
  if (leftGap > 2 * rightGap) {
26323
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_right_detected", "position_detection_on_stream");
26355
+ !this.emmitedPositionAlert && this.handleAlert(
26356
+ "wrong_face_position_move_right_detected",
26357
+ "position_detection_on_stream"
26358
+ );
26324
26359
  this.emmitedPositionAlert = true;
26325
26360
  failedFacePosition = true;
26326
26361
  }
26327
26362
  if (topGap > 4 * bottomGap) {
26328
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_top_detected", "position_detection_on_stream");
26363
+ !this.emmitedPositionAlert && this.handleAlert(
26364
+ "wrong_face_position_move_top_detected",
26365
+ "position_detection_on_stream"
26366
+ );
26329
26367
  this.emmitedPositionAlert = true;
26330
26368
  failedFacePosition = true;
26331
26369
  }
26332
26370
  if (rightGap > 2 * leftGap) {
26333
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_left_detected", "position_detection_on_stream");
26371
+ !this.emmitedPositionAlert && this.handleAlert(
26372
+ "wrong_face_position_move_left_detected",
26373
+ "position_detection_on_stream"
26374
+ );
26334
26375
  this.emmitedPositionAlert = true;
26335
26376
  failedFacePosition = true;
26336
26377
  }
26337
26378
  if (bottomGap > 3 * topGap) {
26338
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_bottom_detected", "position_detection_on_stream");
26379
+ !this.emmitedPositionAlert && this.handleAlert(
26380
+ "wrong_face_position_move_bottom_detected",
26381
+ "position_detection_on_stream"
26382
+ );
26339
26383
  this.emmitedPositionAlert = true;
26340
26384
  failedFacePosition = true;
26341
26385
  }
26342
26386
  if (failedFacePosition == false) {
26343
- this.emmitedPositionAlert && this.handleOk("ok_position_face_detected", "position_detection_on_stream");
26387
+ this.emmitedPositionAlert && this.handleOk(
26388
+ "ok_position_face_detected",
26389
+ "position_detection_on_stream"
26390
+ );
26344
26391
  this.emmitedPositionAlert = false;
26345
26392
  }
26346
26393
  }
@@ -29869,12 +29916,12 @@ var BackendService = class {
29869
29916
  });
29870
29917
  return result.data;
29871
29918
  }
29872
- async verifyFace(proctoringId2, faceImage) {
29919
+ async verifyFace(proctoringId2, faceImage, retry) {
29873
29920
  const result = await this.makeRequestAxios({
29874
29921
  path: `/Realtime/verify-face`,
29875
29922
  method: "POST",
29876
29923
  jwt: this.token,
29877
- body: { "proctoringId": proctoringId2, "base64": faceImage }
29924
+ body: { "proctoringId": proctoringId2, "base64": faceImage, "retry": retry }
29878
29925
  });
29879
29926
  return result.data;
29880
29927
  }
@@ -30750,7 +30797,6 @@ var CameraRecorder = class {
30750
30797
  noiseLimit: 40
30751
30798
  },
30752
30799
  imageBehaviourParameters: {
30753
- frames: 40,
30754
30800
  useUploadImage: true,
30755
30801
  uploadInterval: 20,
30756
30802
  saveVideo: true
@@ -30762,7 +30808,6 @@ var CameraRecorder = class {
30762
30808
  }
30763
30809
  };
30764
30810
  // private imageParams: ImageParameters = {
30765
- // frames: 40,
30766
30811
  // useUploadImage: true,
30767
30812
  // uploadInterval: 20,
30768
30813
  // saveVideo: true
@@ -30854,7 +30899,7 @@ var CameraRecorder = class {
30854
30899
  }
30855
30900
  }
30856
30901
  async startRecording(options) {
30857
- var _a2, _b, _c2, _d, _e3, _f, _g, _h, _i3;
30902
+ var _a2, _b, _c2, _d, _e3, _f, _g, _h;
30858
30903
  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)) {
30859
30904
  await this.initializeDetectors();
30860
30905
  }
@@ -30934,7 +30979,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
30934
30979
  this.filesToUpload = [];
30935
30980
  if (this.options.proctoringType == "REALTIME") {
30936
30981
  this.captureFrame();
30937
- this.sendFrameInterval = setInterval(() => this.captureFrame(), 1e3 * ((_i3 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _i3.realtimeUploadInterval));
30938
30982
  }
30939
30983
  this.packageCount = 0;
30940
30984
  }
@@ -30965,7 +31009,8 @@ Setting: ${JSON.stringify(settings, null, 2)}`
30965
31009
  clearInterval(this.imageInterval);
30966
31010
  clearInterval(this.sendFrameInterval);
30967
31011
  if (this.options.proctoringType == "REALTIME" && this.upload && this.backendToken) {
30968
- await this.sendPackage();
31012
+ await this.sendPackage(this.filesToUpload);
31013
+ await this.filesToUpload.splice(0, this.filesToUpload.length);
30969
31014
  }
30970
31015
  this.volumeMeter && this.volumeMeter.stop();
30971
31016
  this.intervalNoiseDetection && clearInterval(this.intervalNoiseDetection);
@@ -31035,23 +31080,29 @@ Setting: ${JSON.stringify(settings, null, 2)}`
31035
31080
  this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
31036
31081
  return this.canvas.toDataURL("image/jpeg");
31037
31082
  }
31038
- // De um em um segundo captura um frame ligado de 30 em 30 segundos)
31083
+ // captura um frame a cada intervalo de tempo definido no paramsConfig.videoBehaviourParameters?.realtimeCaptureInterval!
31039
31084
  captureFrame() {
31040
- var _a2;
31085
+ var _a2, _b;
31041
31086
  let imageFile;
31042
31087
  this.configImageCapture();
31088
+ let newCanvasWidth = this.videoOptions.width / 2;
31089
+ let newCanvasHeight = this.videoOptions.height / 2;
31090
+ if (newCanvasWidth < 320) newCanvasWidth = 320;
31091
+ if (newCanvasHeight < 180) newCanvasHeight = 180;
31092
+ this.canvas.width = newCanvasWidth;
31093
+ this.canvas.height = newCanvasHeight;
31043
31094
  const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
31044
- let initSend = false;
31045
- this.imageCount++;
31095
+ this.imageCount = 0;
31046
31096
  this.imageInterval = setInterval(async () => {
31097
+ console.log("capturando frame " + this.imageCount);
31047
31098
  this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
31048
31099
  const image_data_url = this.canvas.toDataURL("image/jpeg");
31049
31100
  if (this.proctoringId == void 0) return;
31050
- if (initSend == false && packSize == this.imageCount) {
31051
- initSend = true;
31101
+ if (packSize == this.imageCount) {
31052
31102
  this.imageCount = 0;
31053
- clearInterval(this.imageInterval);
31054
- this.sendPackage();
31103
+ const framesToSend = [...this.filesToUpload];
31104
+ this.sendPackage(framesToSend);
31105
+ await this.filesToUpload.splice(0, this.filesToUpload.length);
31055
31106
  }
31056
31107
  let imageName = `${this.proctoringId}_${this.imageCount + 1}.jpg`;
31057
31108
  imageFile = await this.getFile(image_data_url, imageName, "image/jpeg");
@@ -31059,22 +31110,24 @@ Setting: ${JSON.stringify(settings, null, 2)}`
31059
31110
  this.filesToUpload.push(imageFile);
31060
31111
  this.imageCount++;
31061
31112
  }
31062
- }, 1e3);
31113
+ }, ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) * 1e3);
31063
31114
  }
31064
31115
  // envia pacote de imagens
31065
- async sendPackage() {
31116
+ async sendPackage(framesToSend) {
31117
+ var _a2, _b;
31066
31118
  let pending = false;
31067
31119
  let undeliveredPackagesCount = 0;
31120
+ const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
31121
+ const packCaptureInterval = ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) + 1;
31068
31122
  if (this.upload && this.backendToken && !pending && this.filesToUpload.length > 0) {
31069
31123
  undeliveredPackagesCount = 0;
31070
31124
  pending = true;
31071
31125
  const zip = new import_jszip_min.default();
31072
- const files = this.filesToUpload;
31073
- for (const file of files) {
31126
+ for (const file of framesToSend) {
31074
31127
  zip.file(file.name, file);
31075
31128
  }
31076
31129
  const blob = await zip.generateAsync({ type: "blob" });
31077
- let packageName = "realtime_package_" + 30 * this.packageCount + ".zip";
31130
+ let packageName = "realtime_package_" + packSize * packCaptureInterval * this.packageCount + ".zip";
31078
31131
  const myPackage = new File(
31079
31132
  [blob],
31080
31133
  packageName,
@@ -31087,7 +31140,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
31087
31140
  this.backendToken
31088
31141
  );
31089
31142
  if (uploadResult.uploaded == true) {
31090
- await this.filesToUpload.splice(0, this.filesToUpload.length);
31091
31143
  this.packageCount++;
31092
31144
  } else {
31093
31145
  console.log("erro no upload do pacote");
@@ -31671,8 +31723,20 @@ var DeviceCheckerUI = class {
31671
31723
  );
31672
31724
  checkmark_FacePosition.appendChild(checkmark_stem_FacePosition);
31673
31725
  checkmark_FacePosition.appendChild(checkmark_kick_FacePosition);
31726
+ const info_FacePosition = document.createElement("span");
31727
+ const info_stem_FacePosition = document.createElement("div");
31728
+ const info_kick_FacePosition = document.createElement("div");
31729
+ info_FacePosition.style.marginLeft = "10px";
31730
+ info_FacePosition.setAttribute("class", "info");
31731
+ info_FacePosition.setAttribute("id", "info_FacePosition");
31732
+ info_FacePosition.style.display = "none";
31733
+ info_stem_FacePosition.setAttribute("class", "info_stem");
31734
+ info_kick_FacePosition.setAttribute("class", "info_kick");
31735
+ info_FacePosition.appendChild(info_stem_FacePosition);
31736
+ info_FacePosition.appendChild(info_kick_FacePosition);
31674
31737
  alertDivFacePosition.appendChild(checkmark_FacePosition);
31675
31738
  alertDivFacePosition.appendChild(facePositionAlert);
31739
+ alertDivFacePosition.appendChild(info_FacePosition);
31676
31740
  const alertDivAmbientVerify = document.createElement("div");
31677
31741
  alertDivAmbientVerify.setAttribute("class", "alert-div");
31678
31742
  alertDivAmbientVerify.setAttribute("id", "alertDivAmbientVerify");
@@ -32003,6 +32067,8 @@ var DeviceCheckerUI = class {
32003
32067
  realtimeAlertsUI(response) {
32004
32068
  const facePositionAlert = document.getElementById("facePositionAlert");
32005
32069
  const ambientVerifyAlert = document.getElementById("ambientVerifyAlert");
32070
+ const alertDivFacePosition = document.getElementById("alertDivFacePosition");
32071
+ const info_FacePosition = document.getElementById("info_FacePosition");
32006
32072
  const checkmark_FacePosition = document.getElementById("checkmark_FacePosition");
32007
32073
  const checkmark_stem_FacePosition = document.getElementById("checkmark_stem_FacePosition");
32008
32074
  const checkmark_kick_FacePosition = document.getElementById("checkmark_kick_FacePosition");
@@ -32013,6 +32079,8 @@ var DeviceCheckerUI = class {
32013
32079
  if (response.status === "OK") {
32014
32080
  facePositionAlert && (facePositionAlert.style.color = "#16A34A");
32015
32081
  ambientVerifyAlert && (ambientVerifyAlert.style.color = "#16A34A");
32082
+ alertDivFacePosition && alertDivFacePosition.setAttribute("title", ``);
32083
+ info_FacePosition && (info_FacePosition.style.display = "none");
32016
32084
  alertDivAmbientVerify && alertDivAmbientVerify.setAttribute("title", ``);
32017
32085
  if (checkmark_FacePosition) {
32018
32086
  checkmark_FacePosition.setAttribute("class", "checkmark");
@@ -32027,6 +32095,8 @@ var DeviceCheckerUI = class {
32027
32095
  } else {
32028
32096
  if (checkmark_FacePosition && (response.type === "position_detection_on_stream" || response.type === "face_detection_on_stream")) {
32029
32097
  facePositionAlert && (facePositionAlert.style.color = "#FF0000");
32098
+ info_FacePosition && (info_FacePosition.style.display = "block");
32099
+ alertDivFacePosition && alertDivFacePosition.setAttribute("title", response.description);
32030
32100
  checkmark_FacePosition.setAttribute("class", "checkmark_error");
32031
32101
  checkmark_stem_FacePosition.setAttribute("class", "checkmark_stem_error");
32032
32102
  checkmark_kick_FacePosition.setAttribute("class", "checkmark_kick_error");
@@ -33137,64 +33207,191 @@ var ProctoringUploader = class {
33137
33207
  var AlertRecorder = class {
33138
33208
  constructor(options, optionsProctoring) {
33139
33209
  this.alerts = [];
33210
+ // Variáveis para controle de inatividade
33211
+ this.lastActivityTime = Date.now();
33212
+ this.IDLE_THRESHOLD_MS = 3e4;
33213
+ // ==========================================
33214
+ // HANDLERS (Funções Arrow para preservar o 'this')
33215
+ // ==========================================
33216
+ // 1. LOST / RETURN FOCUS
33217
+ this.handleVisibilityChange = () => {
33218
+ if (document.visibilityState === "visible") {
33219
+ this.handleReturnFocus();
33220
+ } else {
33221
+ this.handleLostFocus();
33222
+ }
33223
+ };
33224
+ this.handleLostFocus = () => {
33225
+ if (this.getRelativeTime() > 1e4) {
33226
+ const alertPayload = {
33227
+ begin: this.getRelativeTime(),
33228
+ end: 0,
33229
+ alert: 25 /* FocusOff */,
33230
+ type: 3 /* Screen */
33231
+ };
33232
+ this.onLostFocusCallback(alertPayload);
33233
+ this.alerts.push(alertPayload);
33234
+ }
33235
+ };
33236
+ this.handleReturnFocus = () => {
33237
+ const lastAlert = this.alerts[this.alerts.length - 1];
33238
+ if (lastAlert) {
33239
+ this.onFocusCallback({
33240
+ begin: lastAlert.begin,
33241
+ end: this.getRelativeTime(),
33242
+ alert: 25 /* FocusOff */,
33243
+ type: 3 /* Screen */
33244
+ });
33245
+ lastAlert.end = this.getRelativeTime();
33246
+ }
33247
+ };
33248
+ // 2. SPLIT SCREEN DETECTION
33249
+ this.handleResize = () => {
33250
+ clearTimeout(this.resizeTimeout);
33251
+ this.resizeTimeout = setTimeout(() => {
33252
+ const screenWidth = window.screen.availWidth;
33253
+ const windowWidth = window.outerWidth;
33254
+ if (windowWidth < screenWidth * 0.85) {
33255
+ const msg = `Split Screen Detectado: Janela ocupa ${(windowWidth / screenWidth * 100).toFixed(0)}% da tela`;
33256
+ this.createAlert(43 /* SplitScreen */, 3 /* Screen */, msg);
33257
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33258
+ status: "ALERT",
33259
+ description: msg,
33260
+ type: "split_screen"
33261
+ });
33262
+ }
33263
+ }, 1e3);
33264
+ };
33265
+ // 3. DETECÇÃO DE INATIVIDADE (NO INPUT)
33266
+ this.handleUserActivity = (e3) => {
33267
+ this.lastActivityTime = Date.now();
33268
+ console.log("\u{1F449} handleUserActivity", e3);
33269
+ };
33270
+ // 5. CLIPBOARD HANDLERS
33271
+ this.handleCopy = (e3) => {
33272
+ e3.preventDefault();
33273
+ const msg = "Tentativa de Copiar (Clipboard)";
33274
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
33275
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33276
+ status: "ALERT",
33277
+ description: msg,
33278
+ type: "clipboard_copy"
33279
+ });
33280
+ };
33281
+ this.handleCut = (e3) => {
33282
+ e3.preventDefault();
33283
+ const msg = "Tentativa de Recortar (Clipboard)";
33284
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
33285
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33286
+ status: "ALERT",
33287
+ description: msg,
33288
+ type: "clipboard_cut"
33289
+ });
33290
+ };
33291
+ this.handlePaste = (e3) => {
33292
+ e3.preventDefault();
33293
+ const msg = "Tentativa de Colar (Clipboard)";
33294
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
33295
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33296
+ status: "ALERT",
33297
+ description: msg,
33298
+ type: "clipboard_paste"
33299
+ });
33300
+ };
33140
33301
  this.onLostFocusCallback = options.onLostFocusCallback;
33141
33302
  this.onFocusCallback = options.onFocusCallback;
33303
+ this.onRealtimeAlertCallback = options.onRealtimeAlertCallback;
33142
33304
  this.optionsProctoring = optionsProctoring;
33143
33305
  }
33144
33306
  async startRecording() {
33145
33307
  this.startTime = new Date(Date.now());
33146
- if (this.optionsProctoring.captureScreen) {
33147
- window.addEventListener("blur", () => this.onLostFocus());
33148
- window.addEventListener("focus", () => this.onReturnFocus());
33149
- }
33308
+ this.alerts = [];
33309
+ this.attachListeners();
33150
33310
  }
33151
33311
  async pauseRecording() {
33152
- window.removeEventListener("blur", () => this.onLostFocus());
33153
- window.removeEventListener("focus", () => this.onReturnFocus());
33312
+ this.detachListeners();
33154
33313
  }
33155
33314
  async resumeRecording() {
33156
- if (this.optionsProctoring.captureScreen) {
33157
- window.addEventListener("blur", () => this.onLostFocus());
33158
- window.addEventListener("focus", () => this.onReturnFocus());
33159
- }
33315
+ this.attachListeners();
33160
33316
  }
33161
33317
  async stopRecording() {
33162
- window.removeEventListener("blur", () => this.onLostFocus());
33163
- window.removeEventListener("focus", () => this.onReturnFocus());
33318
+ this.detachListeners();
33164
33319
  }
33165
33320
  async saveOnSession(session) {
33166
33321
  this.alerts.forEach((alert) => {
33167
33322
  session.addAlert(alert);
33168
33323
  });
33169
33324
  }
33170
- onLostFocus() {
33171
- var _a2;
33172
- if (Date.now() - ((_a2 = this.startTime) == null ? void 0 : _a2.getTime()) > 1e4) {
33173
- this.onLostFocusCallback({
33174
- begin: Date.now() - this.startTime.getTime(),
33175
- end: 0,
33176
- alert: 25 /* FocusOff */,
33177
- type: 3 /* Screen */
33178
- });
33179
- this.alerts.push({
33180
- begin: Date.now() - this.startTime.getTime(),
33181
- end: 0,
33182
- alert: 25 /* FocusOff */,
33183
- type: 3 /* Screen */
33184
- });
33185
- }
33325
+ // ==========================================
33326
+ // GERENCIAMENTO DE LISTENERS
33327
+ // ==========================================
33328
+ attachListeners() {
33329
+ if (this.optionsProctoring.captureScreen) {
33330
+ window.addEventListener("visibilitychange", this.handleVisibilityChange);
33331
+ window.addEventListener("resize", this.handleResize);
33332
+ window.document.addEventListener("copy", this.handleCopy);
33333
+ window.document.addEventListener("cut", this.handleCut);
33334
+ window.document.addEventListener("paste", this.handlePaste);
33335
+ }
33336
+ }
33337
+ detachListeners() {
33338
+ window.removeEventListener("visibilitychange", this.handleVisibilityChange);
33339
+ window.removeEventListener("resize", this.handleResize);
33340
+ window.document.removeEventListener("copy", this.handleCopy);
33341
+ window.document.removeEventListener("cut", this.handleCut);
33342
+ window.document.removeEventListener("paste", this.handlePaste);
33343
+ }
33344
+ // private startIdleChecker() {
33345
+ // this.stopIdleChecker(); // Garante que não tenha duplicação
33346
+ // this.idleCheckInterval = setInterval(() => {
33347
+ // const idleTime = Date.now() - this.lastActivityTime;
33348
+ // if (idleTime > this.IDLE_THRESHOLD_MS) {
33349
+ // // Verifica se já não mandamos esse alerta recentemente para não floodar
33350
+ // const lastAlert = this.alerts[this.alerts.length - 1];
33351
+ // // Se o último alerta não foi de inatividade ou foi há muito tempo, cria novo
33352
+ // if (!lastAlert || lastAlert.alert !== AlertCategory.ForbiddenAction || (this.getRelativeTime() - lastAlert.begin > 5000)) {
33353
+ // const msg = "Usuário inativo por muito tempo";
33354
+ // // Assumindo ForbiddenAction ou outra categoria adequada
33355
+ // this.createAlert(AlertCategory.ForbiddenAction, AlertType.System, msg);
33356
+ // this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33357
+ // status: 'ALERT',
33358
+ // description: msg,
33359
+ // type: 'user_idle'
33360
+ // });
33361
+ // }
33362
+ // }
33363
+ // }, 5000); // Checa a cada 5 segundos
33364
+ // }
33365
+ // private stopIdleChecker() {
33366
+ // if (this.idleCheckInterval) {
33367
+ // clearInterval(this.idleCheckInterval);
33368
+ // this.idleCheckInterval = null;
33369
+ // }
33370
+ // }
33371
+ // 4. BACKGROUND EVENTS (Eventos disparados manualmente)
33372
+ addBackgroundEvent(description, category = 200 /* System */) {
33373
+ this.createAlert(category, 3 /* Screen */, description);
33374
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33375
+ status: "ALERT",
33376
+ description,
33377
+ type: "background_event"
33378
+ });
33186
33379
  }
33187
- onReturnFocus() {
33188
- const lastAlert = this.alerts[this.alerts.length - 1];
33189
- if (lastAlert) {
33190
- this.onFocusCallback({
33191
- begin: lastAlert.begin,
33192
- end: Date.now() - this.startTime.getTime(),
33193
- alert: 25 /* FocusOff */,
33194
- type: 3 /* Screen */
33195
- });
33196
- lastAlert.end = Date.now() - this.startTime.getTime();
33197
- }
33380
+ // ==========================================
33381
+ // HELPERS
33382
+ // ==========================================
33383
+ getRelativeTime() {
33384
+ return Date.now() - this.startTime.getTime();
33385
+ }
33386
+ createAlert(category, type, description) {
33387
+ this.alerts.push({
33388
+ begin: this.getRelativeTime(),
33389
+ end: this.getRelativeTime(),
33390
+ // Eventos pontuais começam e terminam no mesmo ms (exceto focus)
33391
+ alert: category,
33392
+ type
33393
+ // description: description // Se sua interface Alert suportar descrição, descomente
33394
+ });
33198
33395
  }
33199
33396
  addAlert({ alert, type }) {
33200
33397
  this.alerts.push({
@@ -36563,7 +36760,8 @@ var _ExternalCameraChecker = class _ExternalCameraChecker {
36563
36760
  this.connection = new import_signalr.HubConnectionBuilder().withUrl(hubUrl, {
36564
36761
  accessTokenFactory: () => this.context.token,
36565
36762
  transport: import_signalr.HttpTransportType.WebSockets,
36566
- withCredentials: false
36763
+ withCredentials: false,
36764
+ skipNegotiation: true
36567
36765
  }).withAutomaticReconnect().configureLogging(import_signalr.LogLevel.Information).build();
36568
36766
  this.connection.on(
36569
36767
  "ReceiveMessage",
@@ -36769,7 +36967,6 @@ var Proctoring = class {
36769
36967
  noiseLimit: 40
36770
36968
  },
36771
36969
  imageBehaviourParameters: {
36772
- frames: 40,
36773
36970
  useUploadImage: true,
36774
36971
  uploadInterval: 20,
36775
36972
  saveVideo: true
@@ -36780,8 +36977,8 @@ var Proctoring = class {
36780
36977
  detectCellPhone: false,
36781
36978
  detectNoise: false,
36782
36979
  detectSpeech: false,
36783
- realtimePackageSize: 20,
36784
- realtimeUploadInterval: 30
36980
+ realtimePackageSize: 10,
36981
+ realtimeCaptureInterval: 2
36785
36982
  }
36786
36983
  };
36787
36984
  this.proctoringId = "";
@@ -36917,6 +37114,29 @@ var Proctoring = class {
36917
37114
  return null;
36918
37115
  }
36919
37116
  }
37117
+ // metodo para fechar o alerta realtime e fica tentando verificar a face até 5 vezes
37118
+ async stopRealtimeAlert(alert) {
37119
+ const verifyMaxRetries = 3;
37120
+ const verifyFace = async (verifyCount) => {
37121
+ if (verifyCount > verifyMaxRetries) return;
37122
+ try {
37123
+ var response = await this.backend.stopRealtimeAlert({
37124
+ proctoringId: this.proctoringId,
37125
+ begin: alert.begin,
37126
+ end: alert.end,
37127
+ warningCategoryEnum: this.convertRealtimeTypeToWarningType(alert.type),
37128
+ alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64(),
37129
+ retry: verifyCount < verifyMaxRetries - 1 ? true : false
37130
+ });
37131
+ console.log("response stopRealtimeAlert", response);
37132
+ return response;
37133
+ } catch (error) {
37134
+ console.log("error stopRealtimeAlert", error);
37135
+ return verifyFace(verifyCount + 1);
37136
+ }
37137
+ };
37138
+ await verifyFace(1);
37139
+ }
36920
37140
  async onRealtimeAlerts(options = {}) {
36921
37141
  this.setOnLostFocusAlertRecorderCallback();
36922
37142
  this.setOnFocusAlertRecorderCallback();
@@ -36931,13 +37151,7 @@ var Proctoring = class {
36931
37151
  alert: this.convertRealtimeCategoryToAlertCategory(response.category)
36932
37152
  });
36933
37153
  } else if (response.status === "OK") {
36934
- await this.backend.stopRealtimeAlert({
36935
- proctoringId: this.proctoringId,
36936
- begin: response.begin,
36937
- end: response.end,
36938
- warningCategoryEnum: this.convertRealtimeTypeToWarningType(response.type),
36939
- alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64()
36940
- });
37154
+ await this.stopRealtimeAlert(response);
36941
37155
  }
36942
37156
  }
36943
37157
  };
@@ -36987,7 +37201,8 @@ var Proctoring = class {
36987
37201
  const alertRecorder = new AlertRecorder(
36988
37202
  {
36989
37203
  onFocusCallback: (response) => this.onFocusAlertRecorderCallback(response),
36990
- onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response)
37204
+ onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response),
37205
+ onRealtimeAlertCallback: (response) => this.onRealtimeAlertsCallback(response)
36991
37206
  },
36992
37207
  options
36993
37208
  );
@@ -37095,11 +37310,23 @@ Navigator: ${JSON.stringify(_navigator2)}`
37095
37310
  startResponse.screenStream = this.allRecorders.screenRecorder.screenStream;
37096
37311
  }
37097
37312
  this.state = "Recording" /* Recording */;
37098
- setTimeout(async () => {
37313
+ let verifyFirstFaceIntervalCount = 0;
37314
+ let verifyingFace = false;
37315
+ this.verifyFirstFaceInterval = setInterval(async () => {
37099
37316
  if (this.sessionOptions.proctoringType === "REALTIME") {
37100
- await this.backend.verifyFace(this.proctoringId, await this.allRecorders.cameraRecorder.getCurrentImageBase64());
37317
+ if (verifyingFace) return;
37318
+ verifyingFace = true;
37319
+ verifyFirstFaceIntervalCount++;
37320
+ try {
37321
+ var response = await this.backend.verifyFace(this.proctoringId, await this.allRecorders.cameraRecorder.getCurrentImageBase64(), verifyFirstFaceIntervalCount > 5 ? false : true);
37322
+ verifyingFace = false;
37323
+ clearInterval(this.verifyFirstFaceInterval);
37324
+ } catch (error) {
37325
+ verifyingFace = false;
37326
+ return;
37327
+ }
37101
37328
  }
37102
- }, 1e3);
37329
+ }, 1500);
37103
37330
  return startResponse;
37104
37331
  } catch (error) {
37105
37332
  console.log(error);
@@ -37236,6 +37463,9 @@ Upload Services: ${uploaderServices}`,
37236
37463
  await this.backend.externalCameraFinish(externalSessionId);
37237
37464
  }
37238
37465
  }
37466
+ if (this.verifyFirstFaceInterval) {
37467
+ clearInterval(this.verifyFirstFaceInterval);
37468
+ }
37239
37469
  this.state = "Stop" /* Stop */;
37240
37470
  } catch (error) {
37241
37471
  await this.cancel();
@@ -37570,12 +37800,12 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
37570
37800
  const photo = new CapturePhoto();
37571
37801
  const login = proctoring.login.bind(proctoring);
37572
37802
  const originalStart = proctoring.start.bind(proctoring);
37573
- const start = async (parameters2) => {
37803
+ const start = async (parameters2, videoOptions) => {
37574
37804
  const deviceResult = checker.getDeviceCheckResult();
37575
37805
  if (deviceResult) {
37576
37806
  proctoring.setDeviceCheckData(deviceResult);
37577
37807
  }
37578
- return originalStart(parameters2);
37808
+ return originalStart(parameters2, videoOptions);
37579
37809
  };
37580
37810
  const finish = proctoring.finish.bind(proctoring);
37581
37811
  const pause = proctoring.pause.bind(proctoring);