send.js 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. /**
  2. * @typedef {import('strophe.js/src/builder.js').Builder} Strophe.Builder
  3. */
  4. import _converse from '../_converse.js';
  5. import log from '../../log.js';
  6. import { Strophe, toStanza } from 'strophe.js';
  7. import { TimeoutError } from '../errors.js';
  8. export default {
  9. /**
  10. * Allows you to send XML stanzas.
  11. * @method _converse.api.send
  12. * @param {Element|Strophe.Builder} stanza
  13. * @return {void}
  14. * @example
  15. * const msg = converse.env.$msg({
  16. * 'from': 'juliet@example.com/balcony',
  17. * 'to': 'romeo@example.net',
  18. * 'type':'chat'
  19. * });
  20. * _converse.api.send(msg);
  21. */
  22. send (stanza) {
  23. const { api } = _converse;
  24. if (!api.connection.connected()) {
  25. log.warn("Not sending stanza because we're not connected!");
  26. log.warn(Strophe.serialize(stanza));
  27. return;
  28. }
  29. if (typeof stanza === 'string') {
  30. stanza = toStanza(stanza);
  31. } else if (stanza?.tree) {
  32. stanza = stanza.tree();
  33. }
  34. if (stanza.tagName === 'iq') {
  35. return api.sendIQ(stanza);
  36. } else {
  37. api.connection.get().send(stanza);
  38. api.trigger('send', stanza);
  39. }
  40. },
  41. /**
  42. * Send an IQ stanza
  43. * @method _converse.api.sendIQ
  44. * @param {Element|Strophe.Builder} stanza
  45. * @param {number} [timeout] - The default timeout value is taken from
  46. * the `stanza_timeout` configuration setting.
  47. * @param {boolean} [reject=true] - Whether an error IQ should cause the promise
  48. * to be rejected. If `false`, the promise will resolve instead of being rejected.
  49. * @returns {Promise} A promise which resolves (or potentially rejected) once we
  50. * receive a `result` or `error` stanza or once a timeout is reached.
  51. * If the IQ stanza being sent is of type `result` or `error`, there's
  52. * nothing to wait for, so an already resolved promise is returned.
  53. */
  54. sendIQ (stanza, timeout, reject=true) {
  55. const { api } = _converse;
  56. if (!api.connection.connected()) {
  57. throw new Error("Not sending IQ stanza because we're not connected!");
  58. }
  59. const connection = api.connection.get();
  60. let promise;
  61. stanza = stanza.tree?.() ?? stanza;
  62. if (['get', 'set'].includes(stanza.getAttribute('type'))) {
  63. timeout = timeout || api.settings.get('stanza_timeout');
  64. if (reject) {
  65. promise = new Promise((resolve, reject) => connection.sendIQ(stanza, resolve, reject, timeout));
  66. promise.catch((e) => {
  67. if (e === null) {
  68. throw new TimeoutError(
  69. `Timeout error after ${timeout}ms for the following IQ stanza: ${Strophe.serialize(stanza)}`
  70. );
  71. }
  72. });
  73. } else {
  74. promise = new Promise((resolve) => connection.sendIQ(stanza, resolve, resolve, timeout));
  75. }
  76. } else {
  77. connection.sendIQ(stanza);
  78. promise = Promise.resolve();
  79. }
  80. api.trigger('send', stanza);
  81. return promise;
  82. }
  83. }