UNPKG

converse.js

Version:
223 lines (197 loc) 7.33 kB
import { Model } from "@converse/skeletor"; import log from "@converse/log"; import api from "../../shared/api/index.js"; import _converse from "../../shared/_converse.js"; import converse from "../../shared/api/public.js"; import ColorAwareModel from "../../shared/color.js"; import ModelWithMessages from "../../shared/model-with-messages.js"; import ModelWithVCard from "../../shared/model-with-vcard"; import { AFFILIATIONS, ROLES } from "./constants.js"; import MUCMessages from "./messages.js"; import u from "../../utils/index.js"; import { shouldCreateGroupchatMessage } from "./utils"; import { sendChatState } from "../../shared/actions"; const { Strophe, stx } = converse.env; /** * Represents a participant in a MUC */ class MUCOccupant extends ModelWithVCard(ModelWithMessages(ColorAwareModel(Model))) { /** * @typedef {import('../../shared/types').MessageAttributes} MessageAttributes * @typedef {import('../../shared/errors').StanzaParseError} StanzaParseError */ async initialize() { this.lazy_load_vcard = true; super.initialize(); await this.fetchMessages(); this.on("change:nick", () => this.setColor()); this.on("change:jid", () => this.setColor()); this.on("change:chat_state", () => sendChatState(this.get("jid"), this.get("chat_state"))); } defaults() { return { hats: [], presence: 'offline', show: undefined, states: [], hidden: true, num_unread: 0, message_type: "chat", }; } save(key, val, options) { let attrs; if (key == null) { // eslint-disable-line no-eq-null return super.save(key, val, options); } else if (typeof key === "object") { attrs = key; options = val; } else { (attrs = {})[key] = val; } if (attrs.occupant_id) { attrs.id = attrs.occupant_id; } return super.save(attrs, options); } getMessagesCacheKey() { const id = this.get('occupant_id') || this.get('jid') || this.get('nick'); return `converse.muc-private-msgs-${id}-${_converse.session.get('bare_jid')}`; } getMessagesCollection() { return new MUCMessages(); } /** * Handler for all MUC private messages sent to this occupant. * This method houldn't be called directly, instead {@link MUC#queueMessage} should be called. * @param {MessageAttributes|StanzaParseError} attrs_or_error */ async onMessage(attrs_or_error) { if (u.isErrorObject(attrs_or_error)) { const { stanza, message } = /** @type {StanzaParseError} */ (attrs_or_error); if (stanza) log.error(stanza); return log.error(message); } const attrs = /** @type {MessageAttributes} */ (attrs_or_error); if (attrs.type === "error" && !(await this.shouldShowErrorMessage(attrs))) { return; } const message = this.getDuplicateMessage(attrs); if (message) { this.updateMessage(message, attrs); return; } else if (await this.handleRetraction(attrs)) { return; } this.setEditable(attrs, attrs.time); if (shouldCreateGroupchatMessage(attrs)) { const msg = (await this.handleCorrection(attrs)) || (await this.createMessage(attrs)); this.handleUnreadMessage(msg); } } /** * @returns {string} */ getDisplayName() { return this.get("nick") || this.get("jid") || ""; } /** * Return roles which may be assigned to this occupant * @returns {typeof ROLES} - An array of assignable roles */ getAssignableRoles() { let disabled = api.settings.get("modtools_disable_assign"); if (!Array.isArray(disabled)) { disabled = disabled ? ROLES : []; } if (this.get("role") === "moderator") { return ROLES.filter((r) => !disabled.includes(r)); } else { return []; } } /** * Return affiliations which may be assigned by this occupant * @returns {typeof AFFILIATIONS} An array of assignable affiliations */ getAssignableAffiliations() { let disabled = api.settings.get("modtools_disable_assign"); if (!Array.isArray(disabled)) { disabled = disabled ? AFFILIATIONS : []; } if (this.get("affiliation") === "owner") { return AFFILIATIONS.filter((a) => !disabled.includes(a)); } else if (this.get("affiliation") === "admin") { return AFFILIATIONS.filter((a) => !["owner", "admin", ...disabled].includes(a)); } else { return []; } } isMember() { return ["admin", "owner", "member"].includes(this.get("affiliation")); } isModerator() { return ["admin", "owner"].includes(this.get("affiliation")) || this.get("role") === "moderator"; } isSelf() { return this.get("states").includes("110"); } /** * @param {MessageAttributes} [attrs] * @return {Promise<MessageAttributes>} */ async getOutgoingMessageAttributes(attrs) { const origin_id = u.getUniqueId(); const text = attrs?.body; const body = text ? u.shortnamesToUnicode(text) : undefined; const muc = this.collection.chatroom; const own_occupant = muc.getOwnOccupant(); attrs = Object.assign( {}, attrs, { body, from: own_occupant.get("from"), fullname: _converse.state.profile.get("fullname"), id: origin_id, jid: this.get("jid"), message: body, msgid: origin_id, nick: own_occupant.get("nickname"), origin_id, sender: "me", time: new Date().toISOString(), to: this.get("from") ?? `${muc.get("jid")}/${this.get("nick")}`, type: "chat", }, await u.getMediaURLsMetadata(text) ); /** * *Hook* which allows plugins to update the attributes of an outgoing message. * These attributes get set on the {@link Message} and persisted. * @event _converse#getOutgoingMessageAttributes * @param {MUCOccupant} chat * The chat from which this message will be sent. * @param {MessageAttributes} attrs * The message attributes, from which the stanza will be created. */ attrs = await api.hook("getOutgoingMessageAttributes", this, attrs); return attrs; } /** * @param {import('../chat/message').default} message - The message object */ async createMessageStanza(message) { const stanza = await super.createMessageStanza(message); stanza.cnode(stx`<x xmlns="${Strophe.NS.MUC}#user"/>`).root(); return stanza; } /** * @param {import('../../shared/message').default} message */ isChatMessage(message) { return message.get('type') === this.get('message_type'); } } export default MUCOccupant;