implement slash commands
This commit is contained in:
parent
8959c0d380
commit
5125eaf1dc
|
@ -5,6 +5,7 @@ import { QuickReplyConfig } from './src/QuickReplyConfig.js';
|
|||
import { QuickReplyContextLink } from './src/QuickReplyContextLink.js';
|
||||
import { QuickReplySet } from './src/QuickReplySet.js';
|
||||
import { QuickReplySettings } from './src/QuickReplySettings.js';
|
||||
import { SlashCommandHandler } from './src/SlashCommandHandler.js';
|
||||
import { ButtonUi } from './src/ui/ButtonUi.js';
|
||||
import { SettingsUi } from './src/ui/SettingsUi.js';
|
||||
|
||||
|
@ -12,7 +13,6 @@ import { SettingsUi } from './src/ui/SettingsUi.js';
|
|||
|
||||
|
||||
//TODO move advanced QR options into own UI class
|
||||
//TODO slash commands
|
||||
//TODO easy way to CRUD QRs and sets
|
||||
//TODO easy way to set global and chat sets
|
||||
|
||||
|
@ -151,6 +151,9 @@ const init = async () => {
|
|||
await qr.onExecute();
|
||||
}
|
||||
}
|
||||
|
||||
const slash = new SlashCommandHandler(settings);
|
||||
slash.init();
|
||||
};
|
||||
eventSource.on(event_types.APP_READY, init);
|
||||
|
||||
|
|
|
@ -108,9 +108,6 @@ export class QuickReply {
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* @param {any} idx
|
||||
*/
|
||||
renderSettings(idx) {
|
||||
if (!this.settingsDom) {
|
||||
const item = document.createElement('div'); {
|
||||
|
@ -194,44 +191,6 @@ export class QuickReply {
|
|||
this.settingsDom?.remove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
delete() {
|
||||
if (this.onDelete) {
|
||||
this.unrender();
|
||||
this.unrenderSettings();
|
||||
this.onDelete(this);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {string} value
|
||||
*/
|
||||
updateMessage(value) {
|
||||
if (this.onUpdate) {
|
||||
this.message = value;
|
||||
this.updateRender();
|
||||
this.onUpdate(this);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {string} value
|
||||
*/
|
||||
updateLabel(value) {
|
||||
if (this.onUpdate) {
|
||||
this.label = value;
|
||||
this.updateRender();
|
||||
this.onUpdate(this);
|
||||
}
|
||||
}
|
||||
|
||||
updateContext() {
|
||||
if (this.onUpdate) {
|
||||
this.updateRender();
|
||||
this.onUpdate(this);
|
||||
}
|
||||
}
|
||||
|
||||
async showOptions() {
|
||||
const response = await fetch('/scripts/extensions/quick-reply/html/qrOptions.html', { cache: 'no-store' });
|
||||
if (response.ok) {
|
||||
|
@ -359,6 +318,63 @@ export class QuickReply {
|
|||
|
||||
|
||||
|
||||
delete() {
|
||||
if (this.onDelete) {
|
||||
this.unrender();
|
||||
this.unrenderSettings();
|
||||
this.onDelete(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
*/
|
||||
updateMessage(value) {
|
||||
if (this.onUpdate) {
|
||||
this.message = value;
|
||||
this.updateRender();
|
||||
this.onUpdate(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
*/
|
||||
updateLabel(value) {
|
||||
if (this.onUpdate) {
|
||||
this.label = value;
|
||||
this.updateRender();
|
||||
this.onUpdate(this);
|
||||
}
|
||||
}
|
||||
|
||||
updateContext() {
|
||||
if (this.onUpdate) {
|
||||
this.updateRender();
|
||||
this.onUpdate(this);
|
||||
}
|
||||
}
|
||||
addContextLink(cl) {
|
||||
this.contextList.push(cl);
|
||||
this.updateContext();
|
||||
}
|
||||
removeContextLink(setName) {
|
||||
const idx = this.contextList.findIndex(it=>it.set.name == setName);
|
||||
if (idx > -1) {
|
||||
this.contextList.splice(idx, 1);
|
||||
this.updateContext();
|
||||
}
|
||||
}
|
||||
clearContextLinks() {
|
||||
if (this.contextList.length) {
|
||||
this.contextList.splice(0, this.contextList.length);
|
||||
this.updateContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
|
|
|
@ -30,27 +30,52 @@ export class QuickReplyConfig {
|
|||
}
|
||||
|
||||
|
||||
hasSet(qrs) {
|
||||
return this.setList.find(it=>it.set == qrs) != null;
|
||||
}
|
||||
addSet(qrs, isVisible = true) {
|
||||
if (!this.hasSet(qrs)) {
|
||||
const qrl = new QuickReplySetLink();
|
||||
qrl.set = qrs;
|
||||
qrl.isVisible = isVisible;
|
||||
this.setList.push(qrl);
|
||||
this.update();
|
||||
this.updateSetListDom();
|
||||
}
|
||||
}
|
||||
removeSet(qrs) {
|
||||
const idx = this.setList.findIndex(it=>it.set == qrs);
|
||||
if (idx > -1) {
|
||||
this.setList.splice(idx, 1);
|
||||
this.update();
|
||||
this.updateSetListDom();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
renderSettingsInto(/**@type {HTMLElement}*/root) {
|
||||
/**@type {HTMLElement}*/
|
||||
const setList = root.querySelector('.qr--setList');
|
||||
this.setListDom = setList;
|
||||
setList.innerHTML = '';
|
||||
this.setListDom = root.querySelector('.qr--setList');
|
||||
root.querySelector('.qr--setListAdd').addEventListener('click', ()=>{
|
||||
const qrl = new QuickReplySetLink();
|
||||
qrl.set = QuickReplySet.list[0];
|
||||
this.hookQuickReplyLink(qrl);
|
||||
this.setList.push(qrl);
|
||||
setList.append(qrl.renderSettings(this.setList.length - 1));
|
||||
this.setListDom.append(qrl.renderSettings(this.setList.length - 1));
|
||||
this.update();
|
||||
});
|
||||
this.updateSetListDom();
|
||||
}
|
||||
updateSetListDom() {
|
||||
this.setListDom.innerHTML = '';
|
||||
// @ts-ignore
|
||||
$(setList).sortable({
|
||||
$(this.setListDom).sortable({
|
||||
delay: getSortableDelay(),
|
||||
stop: ()=>this.onSetListSort(),
|
||||
});
|
||||
this.setList.filter(it=>!it.set.isDeleted).forEach((qrl,idx)=>setList.append(qrl.renderSettings(idx)));
|
||||
this.setList.filter(it=>!it.set.isDeleted).forEach((qrl,idx)=>this.setListDom.append(qrl.renderSettings(idx)));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
import { registerSlashCommand } from '../../../slash-commands.js';
|
||||
import { QuickReplyContextLink } from './QuickReplyContextLink.js';
|
||||
import { QuickReplySet } from './QuickReplySet.js';
|
||||
import { QuickReplySettings } from './QuickReplySettings.js';
|
||||
|
||||
export class SlashCommandHandler {
|
||||
/**@type {QuickReplySettings}*/ settings;
|
||||
|
||||
|
||||
|
||||
|
||||
constructor(/**@type {QuickReplySettings}*/settings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
init() {
|
||||
registerSlashCommand('qr', (_, value) => this.executeQuickReplyByIndex(Number(value)), [], '<span class="monospace">(number)</span> – activates the specified Quick Reply', true, true);
|
||||
registerSlashCommand('qrset', ()=>toastr.warning('The command /qrset has been deprecated. Use /qr-set, /qr-set-on, and /qr-set-off instead.'), [], '<strong>DEPRECATED</strong> – The command /qrset has been deprecated. Use /qr-set, /qr-set-on, and /qr-set-off instead.', true, true);
|
||||
registerSlashCommand('qr-set', (args, value)=>this.toggleGlobalSet(value, args), [], '<span class="monospace">[visible=true] (number)</span> – toggle global QR set', true, true);
|
||||
registerSlashCommand('qr-set-on', (args, value)=>this.addGlobalSet(value, args), [], '<span class="monospace">[visible=true] (number)</span> – activate global QR set', true, true);
|
||||
registerSlashCommand('qr-set-off', (_, value)=>this.removeGlobalSet(value), [], '<span class="monospace">(number)</span> – deactivate global QR set', true, true);
|
||||
registerSlashCommand('qr-chat-set', (args, value)=>this.toggleChatSet(value, args), [], '<span class="monospace">[visible=true] (number)</span> – toggle chat QR set', true, true);
|
||||
registerSlashCommand('qr-chat-set-on', (args, value)=>this.addChatSet(value, args), [], '<span class="monospace">[visible=true] (number)</span> – activate chat QR set', true, true);
|
||||
registerSlashCommand('qr-chat-set-off', (_, value)=>this.removeChatSet(value), [], '<span class="monospace">(number)</span> – deactivate chat QR set', true, true);
|
||||
|
||||
const qrArgs = `
|
||||
label - string - text on the button, e.g., label=MyButton
|
||||
set - string - name of the QR set, e.g., set=PresetName1
|
||||
hidden - bool - whether the button should be hidden, e.g., hidden=true
|
||||
startup - bool - auto execute on app startup, e.g., startup=true
|
||||
user - bool - auto execute on user message, e.g., user=true
|
||||
bot - bool - auto execute on AI message, e.g., bot=true
|
||||
load - bool - auto execute on chat load, e.g., load=true
|
||||
title - bool - title / tooltip to be shown on button, e.g., title="My Fancy Button"
|
||||
`.trim();
|
||||
const qrUpdateArgs = `
|
||||
newlabel - string - new text fort the button, e.g. newlabel=MyRenamedButton
|
||||
${qrArgs}
|
||||
`.trim();
|
||||
registerSlashCommand('qr-create', (args, message)=>this.createQuickReply(args, message), [], `<span class="monospace" style="white-space:pre-line;">(arguments [message])\n arguments:\n ${qrArgs}</span> – creates a new Quick Reply, example: <tt>/qr-create set=MyPreset label=MyButton /echo 123</tt>`, true, true);
|
||||
registerSlashCommand('qr-update', (args, message)=>this.updateQuickReply(args, message), [], `<span class="monospace" style="white-space:pre-line;">(arguments [message])\n arguments:\n ${qrUpdateArgs}</span> – updates Quick Reply, example: <tt>/qr-update set=MyPreset label=MyButton newlabel=MyRenamedButton /echo 123</tt>`, true, true);
|
||||
registerSlashCommand('qr-delete', (args, name)=>this.deleteQuickReply(args, name), [], '<span class="monospace">(set=string [label])</span> – deletes Quick Reply', true, true);
|
||||
registerSlashCommand('qr-contextadd', (args, name)=>this.createContextItem(args, name), [], '<span class="monospace">(set=string label=string chain=bool [preset name])</span> – add context menu preset to a QR, example: <tt>/qr-contextadd set=MyPreset label=MyButton chain=true MyOtherPreset</tt>', true, true);
|
||||
registerSlashCommand('qr-contextdel', (args, name)=>this.deleteContextItem(args, name), [], '<span class="monospace">(set=string label=string [preset name])</span> – remove context menu preset from a QR, example: <tt>/qr-contextdel set=MyPreset label=MyButton MyOtherPreset</tt>', true, true);
|
||||
registerSlashCommand('qr-contextclear', (args, label)=>this.clearContextMenu(args, label), [], '<span class="monospace">(set=string [label])</span> – remove all context menu presets from a QR, example: <tt>/qr-contextclear set=MyPreset MyButton</tt>', true, true);
|
||||
const presetArgs = `
|
||||
enabled - bool - enable or disable the preset
|
||||
nosend - bool - disable send / insert in user input (invalid for slash commands)
|
||||
before - bool - place QR before user input
|
||||
slots - int - number of slots
|
||||
inject - bool - inject user input automatically (if disabled use {{input}})
|
||||
`.trim();
|
||||
registerSlashCommand('qr-set-create', (args, name)=>this.createSet(name, args), ['qr-presetadd'], `<span class="monospace" style="white-space:pre-line;">(arguments [label])\n arguments:\n ${presetArgs}</span> – create a new preset (overrides existing ones), example: <tt>/qr-presetadd slots=3 MyNewPreset</tt>`, true, true);
|
||||
registerSlashCommand('qr-set-update', (args, name)=>this.updateSet(name, args), ['qr-presetupdate'], `<span class="monospace" style="white-space:pre-line;">(arguments [label])\n arguments:\n ${presetArgs}</span> – update an existing preset, example: <tt>/qr-presetupdate enabled=false MyPreset</tt>`, true, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
getSetByName(name) {
|
||||
const set = QuickReplySet.get(name);
|
||||
if (!set) {
|
||||
toastr.error(`No Quick Reply Set with the name "${name}" could be found.`);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
getQrByLabel(setName, label) {
|
||||
const set = this.getSetByName(setName);
|
||||
if (!set) return;
|
||||
const qr = set.qrList.find(it=>it.label == label);
|
||||
if (!qr) {
|
||||
toastr.error(`No Quick Reply with the label "${label}" could be found in the set "${set.name}"`);
|
||||
}
|
||||
return qr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async executeQuickReplyByIndex(idx) {
|
||||
const qr = [...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? [])]
|
||||
.map(it=>it.set.qrList)
|
||||
.flat()[idx]
|
||||
;
|
||||
if (qr) {
|
||||
return await qr.onExecute();
|
||||
} else {
|
||||
toastr.error(`No Quick Reply at index "${idx}"`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toggleGlobalSet(name, args = {}) {
|
||||
const set = this.getSetByName(name);
|
||||
if (!set) return;
|
||||
if (this.settings.config.hasSet(set)) {
|
||||
this.settings.config.removeSet(set);
|
||||
} else {
|
||||
this.settings.config.addSet(set, JSON.parse(args.visible ?? 'true'));
|
||||
}
|
||||
}
|
||||
addGlobalSet(name, args = {}) {
|
||||
const set = this.getSetByName(name);
|
||||
if (!set) return;
|
||||
this.settings.config.addSet(set, JSON.parse(args.visible ?? 'true'));
|
||||
}
|
||||
removeGlobalSet(name) {
|
||||
const set = this.getSetByName(name);
|
||||
if (!set) return;
|
||||
this.settings.config.removeSet(set);
|
||||
}
|
||||
|
||||
|
||||
toggleChatSet(name, args = {}) {
|
||||
if (!this.settings.chatConfig) return;
|
||||
const set = this.getSetByName(name);
|
||||
if (!set) return;
|
||||
if (this.settings.chatConfig.hasSet(set)) {
|
||||
this.settings.chatConfig.removeSet(set);
|
||||
} else {
|
||||
this.settings.chatConfig.addSet(set, JSON.parse(args.visible ?? 'true'));
|
||||
}
|
||||
}
|
||||
addChatSet(name, args = {}) {
|
||||
if (!this.settings.chatConfig) return;
|
||||
const set = this.getSetByName(name);
|
||||
if (!set) return;
|
||||
this.settings.chatConfig.addSet(set, JSON.parse(args.visible ?? 'true'));
|
||||
}
|
||||
removeChatSet(name) {
|
||||
if (!this.settings.chatConfig) return;
|
||||
const set = this.getSetByName(name);
|
||||
if (!set) return;
|
||||
this.settings.chatConfig.removeSet(set);
|
||||
}
|
||||
|
||||
|
||||
createQuickReply(args, message) {
|
||||
const set = this.getSetByName(args.set);
|
||||
if (!set) return;
|
||||
const qr = set.addQuickReply();
|
||||
qr.label = args.label ?? '';
|
||||
qr.message = message ?? '';
|
||||
qr.title = args.title ?? '';
|
||||
qr.isHidden = JSON.parse(args.hidden ?? 'false') === true;
|
||||
qr.executeOnStartup = JSON.parse(args.startup ?? 'false') === true;
|
||||
qr.executeOnUser = JSON.parse(args.user ?? 'false') === true;
|
||||
qr.executeOnAi = JSON.parse(args.bot ?? 'false') === true;
|
||||
qr.executeOnChatChange = JSON.parse(args.load ?? 'false') === true;
|
||||
qr.onUpdate();
|
||||
}
|
||||
updateQuickReply(args, message) {
|
||||
const qr = this.getQrByLabel(args.set, args.label);
|
||||
if (!qr) return;
|
||||
qr.message = (message ?? '').trim().length > 0 ? message : qr.message;
|
||||
qr.label = args.newlabel !== undefined ? (args.newlabel ?? '') : qr.label;
|
||||
qr.title = args.title !== undefined ? (args.title ?? '') : qr.title;
|
||||
qr.isHidden = args.hidden !== undefined ? (JSON.parse(args.hidden ?? 'false') === true) : qr.isHidden;
|
||||
qr.executeOnStartup = args.startup !== undefined ? (JSON.parse(args.startup ?? 'false') === true) : qr.executeOnStartup;
|
||||
qr.executeOnUser = args.user !== undefined ? (JSON.parse(args.user ?? 'false') === true) : qr.executeOnUser;
|
||||
qr.executeOnAi = args.bot !== undefined ? (JSON.parse(args.bot ?? 'false') === true) : qr.executeOnAi;
|
||||
qr.executeOnChatChange = args.load !== undefined ? (JSON.parse(args.load ?? 'false') === true) : qr.executeOnChatChange;
|
||||
qr.onUpdate();
|
||||
}
|
||||
deleteQuickReply(args, label) {
|
||||
const qr = this.getQrByLabel(args.set, args.label ?? label);
|
||||
if (!qr) return;
|
||||
qr.delete();
|
||||
}
|
||||
|
||||
|
||||
createContextItem(args, name) {
|
||||
const qr = this.getQrByLabel(args.set, args.label);
|
||||
const set = this.getSetByName(name);
|
||||
if (!qr || !set) return;
|
||||
const cl = new QuickReplyContextLink();
|
||||
cl.set = set;
|
||||
cl.isChained = JSON.parse(args.chain ?? 'false') ?? false;
|
||||
qr.addContextLink(cl);
|
||||
}
|
||||
deleteContextItem(args, name) {
|
||||
const qr = this.getQrByLabel(args.set, args.label);
|
||||
const set = this.getSetByName(name);
|
||||
if (!qr || !set) return;
|
||||
qr.removeContextLink(set.name);
|
||||
}
|
||||
clearContextMenu(args, label) {
|
||||
const qr = this.getQrByLabel(args.set, args.label ?? label);
|
||||
if (!qr) return;
|
||||
qr.clearContextLinks();
|
||||
}
|
||||
|
||||
|
||||
createSet(name, args) {
|
||||
const set = new QuickReplySet();
|
||||
set.name = args.name ?? name;
|
||||
set.disableSend = JSON.parse(args.nosend ?? 'false') === true;
|
||||
set.placeBeforeInput = JSON.parse(args.before ?? 'false') === true;
|
||||
set.injectInput = JSON.parse(args.inject ?? 'false') === true;
|
||||
QuickReplySet.list.push(set);
|
||||
set.save();
|
||||
//TODO settings UI must be updated
|
||||
}
|
||||
updateSet(name, args) {
|
||||
const set = this.getSetByName(args.name ?? name);
|
||||
if (!set) return;
|
||||
set.disableSend = args.nosend !== undefined ? (JSON.parse(args.nosend ?? 'false') === true) : set.disableSend;
|
||||
set.placeBeforeInput = args.before !== undefined ? (JSON.parse(args.before ?? 'false') === true) : set.placeBeforeInput;
|
||||
set.injectInput = args.inject !== undefined ? (JSON.parse(args.inject ?? 'false') === true) : set.injectInput;
|
||||
set.save();
|
||||
//TODO settings UI must be updated
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue