2
0

utils.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import { _converse, api, converse } from '@converse/headless/core.js';
  2. import { isServerMessage, } from '@converse/headless/shared/parsers';
  3. import { parseMessage } from './parsers.js';
  4. import log from '@converse/headless/log.js';
  5. const { Strophe, sizzle, u } = converse.env;
  6. export function openChat (jid) {
  7. if (!u.isValidJID(jid)) {
  8. return log.warn(`Invalid JID "${jid}" provided in URL fragment`);
  9. }
  10. api.chats.open(jid);
  11. }
  12. export async function onClearSession () {
  13. if (_converse.shouldClearCache()) {
  14. await Promise.all(
  15. _converse.chatboxes.map(c => c.messages && c.messages.clearStore({ 'silent': true }))
  16. );
  17. const filter = o => o.get('type') !== _converse.CONTROLBOX_TYPE;
  18. _converse.chatboxes.clearStore({ 'silent': true }, filter);
  19. }
  20. }
  21. async function handleErrorMessage (stanza) {
  22. const from_jid = Strophe.getBareJidFromJid(stanza.getAttribute('from'));
  23. if (u.isSameBareJID(from_jid, _converse.bare_jid)) {
  24. return;
  25. }
  26. const chatbox = await api.chatboxes.get(from_jid);
  27. if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
  28. chatbox?.handleErrorMessageStanza(stanza);
  29. }
  30. }
  31. export function autoJoinChats () {
  32. // Automatically join private chats, based on the
  33. // "auto_join_private_chats" configuration setting.
  34. api.settings.get('auto_join_private_chats').forEach(jid => {
  35. if (_converse.chatboxes.where({ 'jid': jid }).length) {
  36. return;
  37. }
  38. if (typeof jid === 'string') {
  39. api.chats.open(jid);
  40. } else {
  41. log.error('Invalid jid criteria specified for "auto_join_private_chats"');
  42. }
  43. });
  44. /**
  45. * Triggered once any private chats have been automatically joined as
  46. * specified by the `auto_join_private_chats` setting.
  47. * See: https://conversejs.org/docs/html/configuration.html#auto-join-private-chats
  48. * @event _converse#privateChatsAutoJoined
  49. * @example _converse.api.listen.on('privateChatsAutoJoined', () => { ... });
  50. * @example _converse.api.waitUntil('privateChatsAutoJoined').then(() => { ... });
  51. */
  52. api.trigger('privateChatsAutoJoined');
  53. }
  54. export function registerMessageHandlers () {
  55. _converse.connection.addHandler(
  56. stanza => {
  57. if (sizzle(`message > result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop()) {
  58. // MAM messages are handled in converse-mam.
  59. // We shouldn't get MAM messages here because
  60. // they shouldn't have a `type` attribute.
  61. log.warn(`Received a MAM message with type "chat".`);
  62. return true;
  63. }
  64. _converse.handleMessageStanza(stanza);
  65. return true;
  66. },
  67. null,
  68. 'message',
  69. 'chat'
  70. );
  71. _converse.connection.addHandler(
  72. stanza => {
  73. // Message receipts are usually without the `type` attribute. See #1353
  74. if (stanza.getAttribute('type') !== null) {
  75. // TODO: currently Strophe has no way to register a handler
  76. // for stanzas without a `type` attribute.
  77. // We could update it to accept null to mean no attribute,
  78. // but that would be a backward-incompatible change
  79. return true; // Gets handled above.
  80. }
  81. _converse.handleMessageStanza(stanza);
  82. return true;
  83. },
  84. Strophe.NS.RECEIPTS,
  85. 'message'
  86. );
  87. _converse.connection.addHandler(
  88. stanza => {
  89. handleErrorMessage(stanza);
  90. return true;
  91. },
  92. null,
  93. 'message',
  94. 'error'
  95. );
  96. }
  97. /**
  98. * Handler method for all incoming single-user chat "message" stanzas.
  99. * @private
  100. * @param { MessageAttributes } attrs - The message attributes
  101. */
  102. export async function handleMessageStanza (stanza) {
  103. if (isServerMessage(stanza)) {
  104. // Prosody sends headline messages with type `chat`, so we need to filter them out here.
  105. const from = stanza.getAttribute('from');
  106. return log.info(`handleMessageStanza: Ignoring incoming server message from JID: ${from}`);
  107. }
  108. const attrs = await parseMessage(stanza, _converse);
  109. if (u.isErrorObject(attrs)) {
  110. attrs.stanza && log.error(attrs.stanza);
  111. return log.error(attrs.message);
  112. }
  113. const has_body = !!sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length;
  114. const chatbox = await api.chats.get(attrs.contact_jid, { 'nickname': attrs.nick }, has_body);
  115. await chatbox?.queueMessage(attrs);
  116. /**
  117. * @typedef { Object } MessageData
  118. * An object containing the original message stanza, as well as the
  119. * parsed attributes.
  120. * @property { XMLElement } stanza
  121. * @property { MessageAttributes } stanza
  122. * @property { ChatBox } chatbox
  123. */
  124. const data = { stanza, attrs, chatbox };
  125. /**
  126. * Triggered when a message stanza is been received and processed.
  127. * @event _converse#message
  128. * @type { object }
  129. * @property { module:converse-chat~MessageData } data
  130. */
  131. api.trigger('message', data);
  132. }