import 'shared/autocomplete/index.js';
import log from "@converse/headless/log";
import { CustomElement } from 'components/element.js';
import { __ } from 'i18n';
import { api, converse } from "@converse/headless/core";
import { html } from "lit-html";
const { Strophe, $iq, sizzle } = converse.env;
const u = converse.env.utils;
const tpl_command_form = (o, command) => {
const i18n_hide = __('Hide');
const i18n_run = __('Execute');
return html`
`;
}
const tpl_command = (o, command) => html`
${ command.node === o.showform ? tpl_command_form(o, command) : '' }
`;
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;
}
const tpl_adhoc = (o) => {
const i18n_choose_service = __('On which entity do you want to run commands?');
const i18n_choose_service_instructions = __(
'Certain XMPP services and entities allow privileged users to execute ad-hoc commands on them.');
const i18n_commands_found = __('Commands found');
const i18n_fetch_commands = __('List available commands');
const i18n_jid_placeholder = __('XMPP Address');
const i18n_no_commands_found = __('No commands found');
return html`
${ o.alert ? html`${o.alert}
` : '' }
`;
}
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 = [];
}
}
export default class AdHocCommands extends CustomElement {
static get properties () {
return {
'alert': { type: String },
'alert_type': { type: String },
'nonce': { type: String }, // Used to force re-rendering
'showform': { type: String },
'view': { type: String },
}
}
constructor () {
super();
this.view = 'choose-service';
this.showform = '';
this.commands = [];
}
render () {
return tpl_adhoc({
'alert': this.alert,
'alert_type': this.alert_type,
'commands': this.commands,
'fetchCommands': ev => this.fetchCommands(ev),
'hideCommandForm': ev => this.hideCommandForm(ev),
'runCommand': ev => this.runCommand(ev),
'showform': this.showform,
'toggleCommandForm': ev => this.toggleCommandForm(ev),
'view': this.view,
});
}
async fetchCommands (ev) {
ev.preventDefault();
delete this.alert_type;
delete this.alert;
const form_data = new FormData(ev.target);
const jid = form_data.get('jid').trim();
let supported;
try {
supported = await api.disco.supports(Strophe.NS.ADHOC, jid)
} catch (e) {
log.error(e);
}
if (supported) {
try {
this.commands = await api.adhoc.getCommands(jid);
this.view = 'list-commands';
} catch (e) {
log.error(e);
this.alert_type = 'danger';
this.alert = __('Sorry, an error occurred while looking for commands on that entity.');
this.commands = [];
log.error(e);
return;
}
} else {
this.alert_type = 'danger';
this.alert = __("The specified entity doesn't support ad-hoc commands");
}
}
async toggleCommandForm (ev) {
ev.preventDefault();
const node = ev.target.getAttribute('data-command-node');
const cmd = this.commands.filter(c => c.node === node)[0];
this.showform !== node && await fetchCommandForm(cmd);
this.showform = node;
}
hideCommandForm (ev) {
ev.preventDefault();
this.showform = ''
}
async runCommand (ev) {
ev.preventDefault();
const form_data = new FormData(ev.target);
const jid = form_data.get('command_jid').trim();
const node = form_data.get('command_node').trim();
const cmd = this.commands.filter(c => c.node === node)[0];
cmd.alert = null;
this.nonce = u.getUniqueId();
const inputs = sizzle(':input:not([type=button]):not([type=submit])', ev.target);
const config_array = inputs
.filter(i => !['command_jid', 'command_node'].includes(i.getAttribute('name')))
.map(u.webForm2xForm)
.filter(n => n);
const iq = $iq({to: jid, type: "set"})
.c("command", {
'sessionid': cmd.sessionid,
'node': cmd.node,
'xmlns': Strophe.NS.ADHOC
}).c("x", {xmlns: Strophe.NS.XFORM, type: "submit"});
config_array.forEach(node => iq.cnode(node).up());
let result;
try {
result = await api.sendIQ(iq);
} catch (e) {
cmd.alert_type = 'danger';
cmd.alert = __('Sorry, an error occurred while trying to execute the command. See the developer console for details');
log.error('Error while trying to execute an ad-hoc command');
log.error(e);
}
if (result) {
cmd.alert = result.querySelector('note')?.textContent;
} else {
cmd.alert = 'Done';
}
cmd.alert_type = 'primary';
this.nonce = u.getUniqueId();
}
}
api.elements.define('converse-adhoc-commands', AdHocCommands);