agent-poller-policy.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. 'use strict';
  2. const PERSONAL_INTAKE_MODES = ['allowlist_only', 'auto_enroll_review', 'auto_enroll_autopilot'];
  3. function normalizePersonalIntakeMode(value) {
  4. const mode = String(value || '').trim();
  5. return PERSONAL_INTAKE_MODES.includes(mode) ? mode : 'allowlist_only';
  6. }
  7. function messageTimestamp(raw) {
  8. const parsed = Number(raw);
  9. if (!Number.isFinite(parsed)) return 0;
  10. return parsed > 1e12 ? Math.floor(parsed / 1000) : parsed;
  11. }
  12. function roomIdOf(message = {}) {
  13. const candidates = [
  14. message.fromRoomId,
  15. message.roomId,
  16. message.chatRoomId,
  17. message.room?.id,
  18. message.msgData?.fromRoomId,
  19. message.msgData?.roomId,
  20. ];
  21. for (const value of candidates) {
  22. const normalized = String(value ?? '').trim();
  23. if (normalized && !/^(?:0|null|undefined)$/i.test(normalized)) return normalized;
  24. }
  25. return '';
  26. }
  27. function isGroupMessage(message = {}) {
  28. if (roomIdOf(message)) return true;
  29. if (message.isRoom === true || Number(message.isRoomNotice) === 1) return true;
  30. return /^(?:group|room)$/i.test(String(message.chatType || '').trim());
  31. }
  32. function messageContent(message = {}) {
  33. const data = message.msgData || {};
  34. const candidates = [message.content, data.content, data.text, data.value, data.content?.text, data.content?.content];
  35. for (const value of candidates) {
  36. if (typeof value === 'string' && value.trim()) return value.trim();
  37. }
  38. return '';
  39. }
  40. function evaluatePolledMessage(message = {}, config = {}) {
  41. if (![0, 1, 2].includes(Number(message.msgType))) return { eligible: false, reason: 'unsupported_type' };
  42. if (isGroupMessage(message)) return { eligible: false, reason: 'group_message', roomId: roomIdOf(message) };
  43. const content = messageContent(message);
  44. const senderId = String(message.senderId || '');
  45. if (!content || !senderId) return { eligible: false, reason: 'empty_message' };
  46. if (senderId === String(config.selfUserId || '')) return { eligible: false, reason: 'self_message' };
  47. const allowlisted = (config.allowedSenders || []).map(String).includes(senderId);
  48. const intakeMode = normalizePersonalIntakeMode(config.intakeMode);
  49. if (!allowlisted && intakeMode === 'allowlist_only') return { eligible: false, reason: 'not_allowlisted' };
  50. const timestamp = messageTimestamp(message.timestamp);
  51. if (!timestamp) return { eligible: false, reason: 'invalid_timestamp' };
  52. // 使用持久化游标恢复停机期间的积压消息;不要再按本次进程启动时间丢弃。
  53. return { eligible: true, content, senderId, timestamp, allowlisted, unknownContact: !allowlisted, intakeMode };
  54. }
  55. module.exports = {
  56. PERSONAL_INTAKE_MODES,
  57. normalizePersonalIntakeMode,
  58. messageTimestamp,
  59. roomIdOf,
  60. isGroupMessage,
  61. messageContent,
  62. evaluatePolledMessage,
  63. };