123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- import ModeratorToolsModal from './modals/moderator-tools.js';
- import log from "@converse/headless/log";
- import tpl_spinner from 'templates/spinner.js';
- import { Model } from '@converse/skeletor/src/model.js';
- import { __ } from 'i18n';
- import { _converse, api, converse } from "@converse/headless/core";
- import { html } from "lit";
- import { parseMessageForCommands } from 'plugins/chatview/utils.js';
- import { setAffiliation } from '@converse/headless/plugins/muc/affiliations/utils.js';
- const { Strophe, $pres, $iq, sizzle, u } = converse.env;
- const COMMAND_TO_AFFILIATION = {
- 'admin': 'admin',
- 'ban': 'outcast',
- 'member': 'member',
- 'owner': 'owner',
- 'revoke': 'none'
- };
- const COMMAND_TO_ROLE = {
- 'deop': 'participant',
- 'kick': 'none',
- 'mute': 'visitor',
- 'op': 'moderator',
- 'voice': 'participant'
- };
- function setMUCDomain (domain, controlboxview) {
- controlboxview.querySelector('converse-rooms-list')
- .model.save('muc_domain', Strophe.getDomainFromJid(domain));
- }
- function setMUCDomainFromDisco (controlboxview) {
- /* Check whether service discovery for the user's domain
- * returned MUC information and use that to automatically
- * set the MUC domain in the "Add groupchat" modal.
- */
- function featureAdded (feature) {
- if (!feature) {
- return;
- }
- if (feature.get('var') === Strophe.NS.MUC) {
- feature.entity.getIdentity('conference', 'text').then(identity => {
- if (identity) {
- setMUCDomain(feature.get('from'), controlboxview);
- }
- });
- }
- }
- api.waitUntil('discoInitialized')
- .then(() => {
- api.listen.on('serviceDiscovered', featureAdded);
- // Features could have been added before the controlbox was
- // initialized. We're only interested in MUC
- _converse.disco_entities.each(entity => featureAdded(entity.features.findWhere({ 'var': Strophe.NS.MUC })));
- })
- .catch(e => log.error(e));
- }
- export function fetchAndSetMUCDomain (controlboxview) {
- if (controlboxview.model.get('connected')) {
- if (!controlboxview.querySelector('converse-rooms-list').model.get('muc_domain')) {
- if (api.settings.get('muc_domain') === undefined) {
- setMUCDomainFromDisco(controlboxview);
- } else {
- setMUCDomain(api.settings.get('muc_domain'), controlboxview);
- }
- }
- }
- }
- export function getNicknameRequiredTemplate (model) {
- const jid = model.get('jid');
- if (api.settings.get('muc_show_logs_before_join')) {
- return html`<converse-muc-chatarea jid="${jid}"></converse-muc-chatarea>`;
- } else {
- return html`<converse-muc-nickname-form jid="${jid}"></converse-muc-nickname-form>`;
- }
- }
- export function getChatRoomBodyTemplate (o) {
- const view = o.model.session.get('view');
- const jid = o.model.get('jid');
- const RS = converse.ROOMSTATUS;
- const conn_status = o.model.session.get('connection_status');
- if (view === converse.MUC.VIEWS.CONFIG) {
- return html`<converse-muc-config-form class="muc-form-container" jid="${jid}"></converse-muc-config-form>`;
- } else if (view === converse.MUC.VIEWS.BOOKMARK) {
- return html`<converse-muc-bookmark-form class="muc-form-container" jid="${jid}"></converse-muc-bookmark-form>`;
- } else {
- return html`
- ${ conn_status == RS.PASSWORD_REQUIRED ? html`<converse-muc-password-form class="muc-form-container" jid="${jid}"></converse-muc-password-form>` : '' }
- ${ conn_status == RS.ENTERED ? html`<converse-muc-chatarea jid="${jid}"></converse-muc-chatarea>` : '' }
- ${ conn_status == RS.CONNECTING ? tpl_spinner() : '' }
- ${ conn_status == RS.NICKNAME_REQUIRED ? getNicknameRequiredTemplate(o.model) : '' }
- ${ conn_status == RS.DISCONNECTED ? html`<converse-muc-disconnected jid="${jid}"></converse-muc-disconnected>` : '' }
- ${ conn_status == RS.BANNED ? html`<converse-muc-disconnected jid="${jid}"></converse-muc-disconnected>` : '' }
- ${ conn_status == RS.DESTROYED ? html`<converse-muc-destroyed jid="${jid}"></converse-muc-destroyed>` : '' }
- `;
- }
- }
- export function getAutoCompleteListItem (text, input) {
- input = input.trim();
- const element = document.createElement('li');
- element.setAttribute('aria-selected', 'false');
- if (api.settings.get('muc_mention_autocomplete_show_avatar')) {
- const img = document.createElement('img');
- let dataUri = 'data:' + _converse.DEFAULT_IMAGE_TYPE + ';base64,' + _converse.DEFAULT_IMAGE;
- if (_converse.vcards) {
- const vcard = _converse.vcards.findWhere({ 'nickname': text });
- if (vcard) dataUri = 'data:' + vcard.get('image_type') + ';base64,' + vcard.get('image');
- }
- img.setAttribute('src', dataUri);
- img.setAttribute('width', '22');
- img.setAttribute('class', 'avatar avatar-autocomplete');
- element.appendChild(img);
- }
- const regex = new RegExp('(' + input + ')', 'ig');
- const parts = input ? text.split(regex) : [text];
- parts.forEach(txt => {
- if (input && txt.match(regex)) {
- const match = document.createElement('mark');
- match.textContent = txt;
- element.appendChild(match);
- } else {
- element.appendChild(document.createTextNode(txt));
- }
- });
- return element;
- }
- export async function getAutoCompleteList () {
- const models = [...(await api.rooms.get()), ...(await api.contacts.get())];
- const jids = [...new Set(models.map(o => Strophe.getDomainFromJid(o.get('jid'))))];
- return jids;
- }
- export async function fetchCommandForm (command) {
- const node = command.node;
- const jid = command.jid;
- const stanza = $iq({
- 'type': 'set',
- 'to': jid
- }).c('command', {
- 'xmlns': Strophe.NS.ADHOC,
- 'node': node,
- 'action': 'execute'
- });
- try {
- const iq = await api.sendIQ(stanza);
- const cmd_el = sizzle(`command[xmlns="${Strophe.NS.ADHOC}"]`, iq).pop();
- command.sessionid = cmd_el.getAttribute('sessionid');
- command.instructions = sizzle('x[type="form"][xmlns="jabber:x:data"] instructions', cmd_el).pop()?.textContent;
- command.fields = sizzle('x[type="form"][xmlns="jabber:x:data"] field', cmd_el)
- .map(f => u.xForm2TemplateResult(f, cmd_el));
- } catch (e) {
- if (e === null) {
- log.error(`Error: timeout while trying to execute command for ${jid}`);
- } else {
- log.error(`Error while trying to execute command for ${jid}`);
- log.error(e);
- }
- command.fields = [];
- }
- }
- function setRole (muc, command, args, required_affiliations = [], required_roles = []) {
- const role = COMMAND_TO_ROLE[command];
- if (!role) {
- throw Error(`ChatRoomView#setRole called with invalid command: ${command}`);
- }
- if (!muc.verifyAffiliations(required_affiliations) || !muc.verifyRoles(required_roles)) {
- return false;
- }
- if (!muc.validateRoleOrAffiliationChangeArgs(command, args)) {
- return false;
- }
- const nick_or_jid = muc.getNickOrJIDFromCommandArgs(args);
- if (!nick_or_jid) {
- return false;
- }
- const reason = args.split(nick_or_jid, 2)[1].trim();
- // We're guaranteed to have an occupant due to getNickOrJIDFromCommandArgs
- const occupant = muc.getOccupant(nick_or_jid);
- muc.setRole(occupant, role, reason, undefined, e => muc.onCommandError(e));
- return true;
- }
- function verifyAndSetAffiliation (muc, command, args, required_affiliations) {
- const affiliation = COMMAND_TO_AFFILIATION[command];
- if (!affiliation) {
- throw Error(`verifyAffiliations called with invalid command: ${command}`);
- }
- if (!muc.verifyAffiliations(required_affiliations)) {
- return false;
- }
- if (!muc.validateRoleOrAffiliationChangeArgs(command, args)) {
- return false;
- }
- const nick_or_jid = muc.getNickOrJIDFromCommandArgs(args);
- if (!nick_or_jid) {
- return false;
- }
- let jid;
- const reason = args.split(nick_or_jid, 2)[1].trim();
- const occupant = muc.getOccupant(nick_or_jid);
- if (occupant) {
- jid = occupant.get('jid');
- } else {
- if (u.isValidJID(nick_or_jid)) {
- jid = nick_or_jid;
- } else {
- const message = __(
- "Couldn't find a participant with that nickname. " + 'They might have left the groupchat.'
- );
- muc.createMessage({ message, 'type': 'error' });
- return;
- }
- }
- const attrs = { jid, reason };
- if (occupant && api.settings.get('auto_register_muc_nickname')) {
- attrs['nick'] = occupant.get('nick');
- }
- setAffiliation(affiliation, muc.get('jid'), [attrs])
- .then(() => muc.occupants.fetchMembers())
- .catch(err => muc.onCommandError(err));
- }
- export function showModeratorToolsModal (muc, affiliation) {
- if (!muc.verifyRoles(['moderator'])) {
- return;
- }
- let modal = api.modal.get(ModeratorToolsModal.id);
- if (modal) {
- modal.model.set({ affiliation });
- } else {
- const model = new Model({ affiliation });
- modal = api.modal.create(ModeratorToolsModal, { model, muc });
- }
- modal.show();
- }
- export function parseMessageForMUCCommands (muc, text) {
- if (
- api.settings.get('muc_disable_slash_commands') &&
- !Array.isArray(api.settings.get('muc_disable_slash_commands'))
- ) {
- return parseMessageForCommands(muc, text);
- }
- text = text.replace(/^\s*/, '');
- const command = (text.match(/^\/([a-zA-Z]*) ?/) || ['']).pop().toLowerCase();
- if (!command) {
- return false;
- }
- const args = text.slice(('/' + command).length + 1).trim();
- if (!muc.getAllowedCommands().includes(command)) {
- return false;
- }
- switch (command) {
- case 'admin': {
- verifyAndSetAffiliation(muc, command, args, ['owner']);
- break;
- }
- case 'ban': {
- verifyAndSetAffiliation(muc, command, args, ['admin', 'owner']);
- break;
- }
- case 'modtools': {
- showModeratorToolsModal(muc, args);
- break;
- }
- case 'deop': {
- // FIXME: /deop only applies to setting a moderators
- // role to "participant" (which only admin/owner can
- // do). Moderators can however set non-moderator's role
- // to participant (e.g. visitor => participant).
- // Currently we don't distinguish between these two
- // cases.
- setRole(muc, command, args, ['admin', 'owner']);
- break;
- }
- case 'destroy': {
- if (!muc.verifyAffiliations(['owner'])) {
- break;
- }
- const chatview = _converse.chatboxviews.get(muc.get('jid'));
- chatview.destroy().catch(e => muc.onCommandError(e));
- break;
- }
- case 'help': {
- muc.set({ 'show_help_messages': false }, { 'silent': true });
- muc.set({ 'show_help_messages': true });
- break;
- }
- case 'kick': {
- setRole(muc, command, args, [], ['moderator']);
- break;
- }
- case 'mute': {
- setRole(muc, command, args, [], ['moderator']);
- break;
- }
- case 'member': {
- verifyAndSetAffiliation(muc, command, args, ['admin', 'owner']);
- break;
- }
- case 'nick': {
- if (!muc.verifyRoles(['visitor', 'participant', 'moderator'])) {
- break;
- } else if (args.length === 0) {
- // e.g. Your nickname is "coolguy69"
- const message = __('Your nickname is "%1$s"', muc.get('nick'));
- muc.createMessage({ message, 'type': 'error' });
- } else {
- const jid = Strophe.getBareJidFromJid(muc.get('jid'));
- api.send(
- $pres({
- from: _converse.connection.jid,
- to: `${jid}/${args}`,
- id: u.getUniqueId()
- }).tree()
- );
- }
- break;
- }
- case 'owner':
- verifyAndSetAffiliation(muc, command, args, ['owner']);
- break;
- case 'op': {
- setRole(muc, command, args, ['admin', 'owner']);
- break;
- }
- case 'register': {
- if (args.length > 1) {
- muc.createMessage({
- 'message': __('Error: invalid number of arguments'),
- 'type': 'error'
- });
- } else {
- muc.registerNickname().then(err_msg => {
- err_msg && muc.createMessage({ 'message': err_msg, 'type': 'error' });
- });
- }
- break;
- }
- case 'revoke': {
- verifyAndSetAffiliation(muc, command, args, ['admin', 'owner']);
- break;
- }
- case 'topic':
- case 'subject':
- muc.setSubject(args);
- break;
- case 'voice': {
- setRole(muc, command, args, [], ['moderator']);
- break;
- }
- default:
- return parseMessageForCommands(muc, text);
- }
- return true;
- }
|