mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
feat: fetch remote i18n
This commit is contained in:
parent
08b1deae50
commit
85cc85e4db
@ -72,4 +72,4 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
@ -26,7 +26,7 @@ import ChannelHandler from './lib/Handler';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ChannelService {
|
export class ChannelService {
|
||||||
private registry: Map<string, ChannelHandler> = new Map();
|
private registry: Map<string, ChannelHandler<string>> = new Map();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly logger: LoggerService,
|
private readonly logger: LoggerService,
|
||||||
@ -40,7 +40,10 @@ export class ChannelService {
|
|||||||
* @param channel - The channel handler associated with the channel name.
|
* @param channel - The channel handler associated with the channel name.
|
||||||
* @typeParam C The channel handler's type that extends `ChannelHandler`.
|
* @typeParam C The channel handler's type that extends `ChannelHandler`.
|
||||||
*/
|
*/
|
||||||
public setChannel<C extends ChannelHandler>(name: string, channel: C) {
|
public setChannel<T extends string, C extends ChannelHandler<T>>(
|
||||||
|
name: T,
|
||||||
|
channel: C,
|
||||||
|
) {
|
||||||
this.registry.set(name, channel);
|
this.registry.set(name, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +74,9 @@ export class ChannelService {
|
|||||||
* @param channelName - The name of the channel (messenger, offline, ...).
|
* @param channelName - The name of the channel (messenger, offline, ...).
|
||||||
* @returns The handler for the specified channel.
|
* @returns The handler for the specified channel.
|
||||||
*/
|
*/
|
||||||
public getChannelHandler<C extends ChannelHandler>(name: string): C {
|
public getChannelHandler<T extends string, C extends ChannelHandler<T>>(
|
||||||
|
name: T,
|
||||||
|
): C {
|
||||||
const handler = this.registry.get(name);
|
const handler = this.registry.get(name);
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
throw new Error(`Channel ${name} not found`);
|
throw new Error(`Channel ${name} not found`);
|
||||||
@ -98,8 +103,8 @@ export class ChannelService {
|
|||||||
* @param req - The websocket request object.
|
* @param req - The websocket request object.
|
||||||
* @param res - The websocket response object.
|
* @param res - The websocket response object.
|
||||||
*/
|
*/
|
||||||
@SocketGet('/webhook/offline/')
|
@SocketGet(`/webhook/${OFFLINE_CHANNEL_NAME}/`)
|
||||||
@SocketPost('/webhook/offline/')
|
@SocketPost(`/webhook/${OFFLINE_CHANNEL_NAME}/`)
|
||||||
handleWebsocketForOffline(
|
handleWebsocketForOffline(
|
||||||
@SocketReq() req: SocketRequest,
|
@SocketReq() req: SocketRequest,
|
||||||
@SocketRes() res: SocketResponse,
|
@SocketRes() res: SocketResponse,
|
||||||
@ -116,8 +121,8 @@ export class ChannelService {
|
|||||||
* @param req - The websocket request object.
|
* @param req - The websocket request object.
|
||||||
* @param res - The websocket response object.
|
* @param res - The websocket response object.
|
||||||
*/
|
*/
|
||||||
@SocketGet('/webhook/live-chat-tester/')
|
@SocketGet(`/webhook/${LIVE_CHAT_TEST_CHANNEL_NAME}/`)
|
||||||
@SocketPost('/webhook/live-chat-tester/')
|
@SocketPost(`/webhook/${LIVE_CHAT_TEST_CHANNEL_NAME}/`)
|
||||||
async handleWebsocketForLiveChatTester(
|
async handleWebsocketForLiveChatTester(
|
||||||
@SocketReq() req: SocketRequest,
|
@SocketReq() req: SocketRequest,
|
||||||
@SocketRes() res: SocketResponse,
|
@SocketRes() res: SocketResponse,
|
||||||
|
@ -18,35 +18,57 @@ import {
|
|||||||
import { LoggerService } from '@/logger/logger.service';
|
import { LoggerService } from '@/logger/logger.service';
|
||||||
import BaseNlpHelper from '@/nlp/lib/BaseNlpHelper';
|
import BaseNlpHelper from '@/nlp/lib/BaseNlpHelper';
|
||||||
import { NlpService } from '@/nlp/services/nlp.service';
|
import { NlpService } from '@/nlp/services/nlp.service';
|
||||||
import { SettingCreateDto } from '@/setting/dto/setting.dto';
|
|
||||||
import { SettingService } from '@/setting/services/setting.service';
|
import { SettingService } from '@/setting/services/setting.service';
|
||||||
import { SocketRequest } from '@/websocket/utils/socket-request';
|
import { SocketRequest } from '@/websocket/utils/socket-request';
|
||||||
import { SocketResponse } from '@/websocket/utils/socket-response';
|
import { SocketResponse } from '@/websocket/utils/socket-response';
|
||||||
|
|
||||||
import { ChannelService } from '../channel.service';
|
import { ChannelService } from '../channel.service';
|
||||||
|
import { ChannelSetting } from '../types';
|
||||||
|
|
||||||
|
import EventWrapper from './EventWrapper';
|
||||||
|
|
||||||
import EventWrapper from './EventWrapper';
|
import EventWrapper from './EventWrapper';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default abstract class ChannelHandler {
|
export default abstract class ChannelHandler<N extends string = string> {
|
||||||
protected settings: SettingCreateDto[] = [];
|
private readonly name: N;
|
||||||
|
|
||||||
|
private readonly settings: ChannelSetting<N>[];
|
||||||
|
|
||||||
protected NLP: BaseNlpHelper;
|
protected NLP: BaseNlpHelper;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
name: N,
|
||||||
|
settings: ChannelSetting<N>[],
|
||||||
protected readonly settingService: SettingService,
|
protected readonly settingService: SettingService,
|
||||||
private readonly channelService: ChannelService,
|
private readonly channelService: ChannelService,
|
||||||
protected readonly nlpService: NlpService,
|
protected readonly nlpService: NlpService,
|
||||||
protected readonly logger: LoggerService,
|
protected readonly logger: LoggerService,
|
||||||
) {}
|
) {
|
||||||
|
this.name = name;
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
this.channelService.setChannel(this.getChannel(), this);
|
this.channelService.setChannel(
|
||||||
|
this.getChannel(),
|
||||||
|
this as unknown as ChannelHandler<N>,
|
||||||
|
);
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getGroup() {
|
||||||
|
return this.getChannel().replaceAll('-', '_');
|
||||||
|
}
|
||||||
|
|
||||||
async setup() {
|
async setup() {
|
||||||
await this.settingService.seedIfNotExist(this.getChannel(), this.settings);
|
await this.settingService.seedIfNotExist(
|
||||||
|
this.getChannel(),
|
||||||
|
this.settings.map((s, i) => ({
|
||||||
|
...s,
|
||||||
|
weight: i + 1,
|
||||||
|
})),
|
||||||
|
);
|
||||||
const nlp = this.nlpService.getNLP();
|
const nlp = this.nlpService.getNLP();
|
||||||
this.setNLP(nlp);
|
this.setNLP(nlp);
|
||||||
this.init();
|
this.init();
|
||||||
@ -61,30 +83,32 @@ export default abstract class ChannelHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the channel specific settings
|
* Returns the channel's name
|
||||||
|
* @returns Channel's name
|
||||||
*/
|
*/
|
||||||
async getSettings<S>() {
|
getChannel() {
|
||||||
const settings = await this.settingService.getSettings();
|
return this.name;
|
||||||
return settings[this.getChannel()] as S;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the channel's name
|
* Returns the channel's settings
|
||||||
* @returns {String}
|
* @returns Channel's settings
|
||||||
*/
|
*/
|
||||||
abstract getChannel(): string;
|
async getSettings() {
|
||||||
|
const settings = await this.settingService.getSettings();
|
||||||
|
return settings[this.getGroup()];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform any initialization needed
|
* Perform any initialization needed
|
||||||
* @returns
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
abstract init(): void;
|
abstract init(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Process incoming channel data via POST/GET methods
|
||||||
|
*
|
||||||
* @param {module:Controller.req} req
|
* @param {module:Controller.req} req
|
||||||
* @param {module:Controller.res} res
|
* @param {module:Controller.res} res
|
||||||
* Process incoming channel data via POST/GET methods
|
|
||||||
*/
|
*/
|
||||||
abstract handle(
|
abstract handle(
|
||||||
req: Request | SocketRequest,
|
req: Request | SocketRequest,
|
||||||
@ -93,26 +117,28 @@ export default abstract class ChannelHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a text message that will be sent to the channel
|
* Format a text message that will be sent to the channel
|
||||||
|
*
|
||||||
* @param message - A text to be sent to the end user
|
* @param message - A text to be sent to the end user
|
||||||
* @param options - might contain additional settings
|
* @param options - might contain additional settings
|
||||||
* @returns {Object} - A text message in the channel specific format
|
* @returns {Object} - A text message in the channel specific format
|
||||||
|
|
||||||
*/
|
*/
|
||||||
abstract _textFormat(message: StdOutgoingMessage, options?: any): any;
|
abstract _textFormat(message: StdOutgoingMessage, options?: any): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Format a text + quick replies message that can be sent to the channel
|
||||||
|
*
|
||||||
* @param message - A text + quick replies to be sent to the end user
|
* @param message - A text + quick replies to be sent to the end user
|
||||||
* @param options - might contain additional settings
|
* @param options - might contain additional settings
|
||||||
* @returns {Object} - A quick replies message in the channel specific format
|
* @returns {Object} - A quick replies message in the channel specific format
|
||||||
* Format a text + quick replies message that can be sent to the channel
|
|
||||||
*/
|
*/
|
||||||
abstract _quickRepliesFormat(message: StdOutgoingMessage, options?: any): any;
|
abstract _quickRepliesFormat(message: StdOutgoingMessage, options?: any): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* From raw buttons, construct a channel understable message containing those buttons
|
||||||
|
*
|
||||||
* @param message - A text + buttons to be sent to the end user
|
* @param message - A text + buttons to be sent to the end user
|
||||||
* @param options - Might contain additional settings
|
* @param options - Might contain additional settings
|
||||||
* @returns {Object} - A buttons message in the format required by the channel
|
* @returns {Object} - A buttons message in the format required by the channel
|
||||||
* From raw buttons, construct a channel understable message containing those buttons
|
|
||||||
*/
|
*/
|
||||||
abstract _buttonsFormat(
|
abstract _buttonsFormat(
|
||||||
message: StdOutgoingMessage,
|
message: StdOutgoingMessage,
|
||||||
@ -121,27 +147,29 @@ export default abstract class ChannelHandler {
|
|||||||
): any;
|
): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Format an attachment + quick replies message that can be sent to the channel
|
||||||
|
*
|
||||||
* @param message - An attachment + quick replies to be sent to the end user
|
* @param message - An attachment + quick replies to be sent to the end user
|
||||||
* @param options - Might contain additional settings
|
* @param options - Might contain additional settings
|
||||||
* @returns {Object} - An attachment message in the format required by the channel
|
* @returns {Object} - An attachment message in the format required by the channel
|
||||||
* Format an attachment + quick replies message that can be sent to the channel
|
|
||||||
*/
|
*/
|
||||||
abstract _attachmentFormat(message: StdOutgoingMessage, options?: any): any;
|
abstract _attachmentFormat(message: StdOutgoingMessage, options?: any): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Format a collection of items to be sent to the channel in carousel/list format
|
||||||
|
*
|
||||||
* @param data - A list of data items to be sent to the end user
|
* @param data - A list of data items to be sent to the end user
|
||||||
* @param options - Might contain additional settings
|
* @param options - Might contain additional settings
|
||||||
* @returns {Object[]} - An array of element objects
|
* @returns {Object[]} - An array of element objects
|
||||||
* Format a collection of items to be sent to the channel in carousel/list format
|
|
||||||
*/
|
*/
|
||||||
abstract _formatElements(data: any[], options: any, ...args: any): any[];
|
abstract _formatElements(data: any[], options: any, ...args: any): any[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a list of elements
|
* Format a list of elements
|
||||||
|
*
|
||||||
* @param message - Contains elements to be sent to the end user
|
* @param message - Contains elements to be sent to the end user
|
||||||
* @param options - Might contain additional settings
|
* @param options - Might contain additional settings
|
||||||
* @returns {Object} - A ready to be sent list template message in the format required by the channel
|
* @returns {Object} - A ready to be sent list template message in the format required by the channel
|
||||||
|
|
||||||
*/
|
*/
|
||||||
abstract _listFormat(
|
abstract _listFormat(
|
||||||
message: StdOutgoingMessage,
|
message: StdOutgoingMessage,
|
||||||
|
8
api/src/channel/types.ts
Normal file
8
api/src/channel/types.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { SettingCreateDto } from '@/setting/dto/setting.dto';
|
||||||
|
|
||||||
|
export type ChannelSetting<N extends string = string> = Omit<
|
||||||
|
SettingCreateDto,
|
||||||
|
'group' | 'weight'
|
||||||
|
> & {
|
||||||
|
group: HyphenToUnderscore<N>;
|
||||||
|
};
|
@ -34,7 +34,6 @@ import { I18nService } from '@/i18n/services/i18n.service';
|
|||||||
import { LanguageService } from '@/i18n/services/language.service';
|
import { LanguageService } from '@/i18n/services/language.service';
|
||||||
import { LoggerService } from '@/logger/logger.service';
|
import { LoggerService } from '@/logger/logger.service';
|
||||||
import { PluginService } from '@/plugins/plugins.service';
|
import { PluginService } from '@/plugins/plugins.service';
|
||||||
import { Settings } from '@/setting/schemas/types';
|
|
||||||
import { SettingService } from '@/setting/services/setting.service';
|
import { SettingService } from '@/setting/services/setting.service';
|
||||||
import {
|
import {
|
||||||
blockFixtures,
|
blockFixtures,
|
||||||
@ -67,6 +66,9 @@ import { FileType } from '../schemas/types/attachment';
|
|||||||
import { Context } from '../schemas/types/context';
|
import { Context } from '../schemas/types/context';
|
||||||
import { PayloadType, StdOutgoingListMessage } from '../schemas/types/message';
|
import { PayloadType, StdOutgoingListMessage } from '../schemas/types/message';
|
||||||
import { SubscriberContext } from '../schemas/types/subscriberContext';
|
import { SubscriberContext } from '../schemas/types/subscriberContext';
|
||||||
|
import { CategoryRepository } from './../repositories/category.repository';
|
||||||
|
import { BlockService } from './block.service';
|
||||||
|
import { CategoryService } from './category.service';
|
||||||
|
|
||||||
import { CategoryRepository } from './../repositories/category.repository';
|
import { CategoryRepository } from './../repositories/category.repository';
|
||||||
import { BlockService } from './block.service';
|
import { BlockService } from './block.service';
|
||||||
|
@ -18,7 +18,6 @@ import { LoggerService } from '@/logger/logger.service';
|
|||||||
import { Nlp } from '@/nlp/lib/types';
|
import { Nlp } from '@/nlp/lib/types';
|
||||||
import { PluginService } from '@/plugins/plugins.service';
|
import { PluginService } from '@/plugins/plugins.service';
|
||||||
import { PluginType } from '@/plugins/types';
|
import { PluginType } from '@/plugins/types';
|
||||||
import { Settings } from '@/setting/schemas/types';
|
|
||||||
import { SettingService } from '@/setting/services/setting.service';
|
import { SettingService } from '@/setting/services/setting.service';
|
||||||
import { BaseService } from '@/utils/generics/base-service';
|
import { BaseService } from '@/utils/generics/base-service';
|
||||||
import { getRandom } from '@/utils/helpers/safeRandom';
|
import { getRandom } from '@/utils/helpers/safeRandom';
|
||||||
|
@ -11,7 +11,6 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
|
|||||||
|
|
||||||
import EventWrapper from '@/channel/lib/EventWrapper';
|
import EventWrapper from '@/channel/lib/EventWrapper';
|
||||||
import { LoggerService } from '@/logger/logger.service';
|
import { LoggerService } from '@/logger/logger.service';
|
||||||
import { Settings } from '@/setting/schemas/types';
|
|
||||||
import { SettingService } from '@/setting/services/setting.service';
|
import { SettingService } from '@/setting/services/setting.service';
|
||||||
|
|
||||||
import { MessageCreateDto } from '../dto/message.dto';
|
import { MessageCreateDto } from '../dto/message.dto';
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"verification_token": "Verification Token",
|
||||||
|
"allowed_domains": "Allowed Domains",
|
||||||
|
"start_button": "Enable `Get Started`",
|
||||||
|
"input_disabled": "Disable Input",
|
||||||
|
"persistent_menu": "Display Persistent Menu",
|
||||||
|
"greeting_message": "Greeting Message",
|
||||||
|
"theme_color": "Widget Theme",
|
||||||
|
"theme_color_options": {
|
||||||
|
"orange": "Orange",
|
||||||
|
"red": "Red",
|
||||||
|
"green": "Green",
|
||||||
|
"blue": "Blue",
|
||||||
|
"dark": "Dark"
|
||||||
|
},
|
||||||
|
"window_title": "Chat Window Title",
|
||||||
|
"avatar_url": "Chatbot Avatar URL",
|
||||||
|
"show_emoji": "Enable Emoji Picker",
|
||||||
|
"show_file": "Enable Attachment Uploader",
|
||||||
|
"show_location": "Enable Geolocation Share",
|
||||||
|
"allowed_upload_size": "Max Upload Size (in bytes)",
|
||||||
|
"allowed_upload_types": "Allowed Upload Mime Types (comma separated)"
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"live_chat_tester": "Live Chat Tester"
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"verification_token": "Jeton de vérification",
|
||||||
|
"allowed_domains": "Domaines autorisés",
|
||||||
|
"start_button": "Activer `Démarrer`",
|
||||||
|
"input_disabled": "Désactiver la saisie",
|
||||||
|
"persistent_menu": "Afficher le menu persistent",
|
||||||
|
"greeting_message": "Message de bienvenue",
|
||||||
|
"theme_color": "Thème du widget",
|
||||||
|
"theme_color_options": {
|
||||||
|
"orange": "Orange",
|
||||||
|
"red": "Rouge",
|
||||||
|
"green": "Vert",
|
||||||
|
"blue": "Bleu",
|
||||||
|
"dark": "Sombre"
|
||||||
|
},
|
||||||
|
"window_title": "Titre de la fenêtre de chat",
|
||||||
|
"avatar_url": "Avatar du chatbot (URL)",
|
||||||
|
"show_emoji": "Activer le sélecteur d'Emojis",
|
||||||
|
"show_file": "Activer l'upload de fichiers",
|
||||||
|
"show_location": "Activer le partage de géolocalisation",
|
||||||
|
"allowed_upload_size": "Taille maximale de téléchargement (en octets)",
|
||||||
|
"allowed_upload_types": "Types MIME autorisés pour le téléchargement (séparés par des virgules)"
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"live_chat_tester": "Testeur Live Chat"
|
||||||
|
}
|
@ -17,11 +17,10 @@ import { MenuService } from '@/cms/services/menu.service';
|
|||||||
import { I18nService } from '@/i18n/services/i18n.service';
|
import { I18nService } from '@/i18n/services/i18n.service';
|
||||||
import { LoggerService } from '@/logger/logger.service';
|
import { LoggerService } from '@/logger/logger.service';
|
||||||
import { NlpService } from '@/nlp/services/nlp.service';
|
import { NlpService } from '@/nlp/services/nlp.service';
|
||||||
import { SettingCreateDto } from '@/setting/dto/setting.dto';
|
|
||||||
import { SettingService } from '@/setting/services/setting.service';
|
import { SettingService } from '@/setting/services/setting.service';
|
||||||
import { WebsocketGateway } from '@/websocket/websocket.gateway';
|
import { WebsocketGateway } from '@/websocket/websocket.gateway';
|
||||||
|
|
||||||
import OfflineHandler from '../offline/index.channel';
|
import BaseWebChannelHandler from '../offline/base-web-channel';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_LIVE_CHAT_TEST_SETTINGS,
|
DEFAULT_LIVE_CHAT_TEST_SETTINGS,
|
||||||
@ -29,9 +28,9 @@ import {
|
|||||||
} from './settings';
|
} from './settings';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class LiveChatTesterHandler extends OfflineHandler {
|
export default class LiveChatTesterHandler extends BaseWebChannelHandler<
|
||||||
protected settings: SettingCreateDto[] = DEFAULT_LIVE_CHAT_TEST_SETTINGS;
|
typeof LIVE_CHAT_TEST_CHANNEL_NAME
|
||||||
|
> {
|
||||||
constructor(
|
constructor(
|
||||||
settingService: SettingService,
|
settingService: SettingService,
|
||||||
channelService: ChannelService,
|
channelService: ChannelService,
|
||||||
@ -46,6 +45,8 @@ export default class LiveChatTesterHandler extends OfflineHandler {
|
|||||||
websocketGateway: WebsocketGateway,
|
websocketGateway: WebsocketGateway,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
LIVE_CHAT_TEST_CHANNEL_NAME,
|
||||||
|
DEFAULT_LIVE_CHAT_TEST_SETTINGS,
|
||||||
settingService,
|
settingService,
|
||||||
channelService,
|
channelService,
|
||||||
nlpService,
|
nlpService,
|
||||||
@ -59,12 +60,4 @@ export default class LiveChatTesterHandler extends OfflineHandler {
|
|||||||
websocketGateway,
|
websocketGateway,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the channel's name
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
getChannel() {
|
|
||||||
return LIVE_CHAT_TEST_CHANNEL_NAME;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
18
api/src/extensions/channels/live-chat-tester/index.d.ts
vendored
Normal file
18
api/src/extensions/channels/live-chat-tester/index.d.ts
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import {
|
||||||
|
DEFAULT_LIVE_CHAT_TEST_SETTINGS,
|
||||||
|
LIVE_CHAT_TEST_CHANNEL_NAME,
|
||||||
|
} from './settings';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Settings
|
||||||
|
extends SettingTree<typeof DEFAULT_LIVE_CHAT_TEST_SETTINGS> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@nestjs/event-emitter' {
|
||||||
|
interface IHookSettingsGroupLabelOperationMap {
|
||||||
|
[name: HyphenToUnderscore<typeof LIVE_CHAT_TEST_CHANNEL_NAME>]: TDefinition<
|
||||||
|
object,
|
||||||
|
SettingObject<typeof DEFAULT_LIVE_CHAT_TEST_SETTINGS>
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
}
|
@ -6,99 +6,89 @@
|
|||||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ChannelSetting } from '@/channel/types';
|
||||||
import { config } from '@/config';
|
import { config } from '@/config';
|
||||||
import { SettingCreateDto } from '@/setting/dto/setting.dto';
|
|
||||||
import { SettingType } from '@/setting/schemas/types';
|
import { SettingType } from '@/setting/schemas/types';
|
||||||
|
|
||||||
import { Offline } from '../offline/types';
|
import { Offline } from '../offline/types';
|
||||||
|
|
||||||
export const LIVE_CHAT_TEST_CHANNEL_NAME = 'live-chat-tester';
|
export const LIVE_CHAT_TEST_CHANNEL_NAME = 'live-chat-tester';
|
||||||
|
|
||||||
export const DEFAULT_LIVE_CHAT_TEST_SETTINGS: SettingCreateDto[] = [
|
export const LIVE_CHAT_TEST_GROUP_NAME = 'live_chat_tester';
|
||||||
|
|
||||||
|
export const DEFAULT_LIVE_CHAT_TEST_SETTINGS = [
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.verification_token,
|
label: Offline.SettingLabel.verification_token,
|
||||||
value: 'test',
|
value: 'test',
|
||||||
type: SettingType.text,
|
type: SettingType.text,
|
||||||
weight: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.allowed_domains,
|
label: Offline.SettingLabel.allowed_domains,
|
||||||
value: config.frontendPath,
|
value: config.frontendPath,
|
||||||
type: SettingType.text,
|
type: SettingType.text,
|
||||||
weight: 3,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.start_button,
|
label: Offline.SettingLabel.start_button,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 4,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.input_disabled,
|
label: Offline.SettingLabel.input_disabled,
|
||||||
value: false,
|
value: false,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 5,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.persistent_menu,
|
label: Offline.SettingLabel.persistent_menu,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 6,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.greeting_message,
|
label: Offline.SettingLabel.greeting_message,
|
||||||
value: 'Welcome! Ready to start a conversation with our chatbot?',
|
value: 'Welcome! Ready to start a conversation with our chatbot?',
|
||||||
type: SettingType.textarea,
|
type: SettingType.textarea,
|
||||||
weight: 7,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.theme_color,
|
label: Offline.SettingLabel.theme_color,
|
||||||
value: 'teal',
|
value: 'teal',
|
||||||
type: SettingType.select,
|
type: SettingType.select,
|
||||||
options: ['teal', 'orange', 'red', 'green', 'blue', 'dark'],
|
options: ['teal', 'orange', 'red', 'green', 'blue', 'dark'],
|
||||||
weight: 8,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.show_emoji,
|
label: Offline.SettingLabel.show_emoji,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 11,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.show_file,
|
label: Offline.SettingLabel.show_file,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 12,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.show_location,
|
label: Offline.SettingLabel.show_location,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 13,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.allowed_upload_size,
|
label: Offline.SettingLabel.allowed_upload_size,
|
||||||
value: 2500000,
|
value: 2500000,
|
||||||
type: SettingType.number,
|
type: SettingType.number,
|
||||||
weight: 14,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: LIVE_CHAT_TEST_CHANNEL_NAME,
|
group: LIVE_CHAT_TEST_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.allowed_upload_types,
|
label: Offline.SettingLabel.allowed_upload_types,
|
||||||
value:
|
value:
|
||||||
'audio/mpeg,audio/x-ms-wma,audio/vnd.rn-realaudio,audio/x-wav,image/gif,image/jpeg,image/png,image/tiff,image/vnd.microsoft.icon,image/vnd.djvu,image/svg+xml,text/css,text/csv,text/html,text/plain,text/xml,video/mpeg,video/mp4,video/quicktime,video/x-ms-wmv,video/x-msvideo,video/x-flv,video/web,application/msword,application/vnd.ms-powerpoint,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.presentation,application/vnd.oasis.opendocument.tex,application/vnd.oasis.opendocument.spreadsheet,application/vnd.oasis.opendocument.graphics,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
'audio/mpeg,audio/x-ms-wma,audio/vnd.rn-realaudio,audio/x-wav,image/gif,image/jpeg,image/png,image/tiff,image/vnd.microsoft.icon,image/vnd.djvu,image/svg+xml,text/css,text/csv,text/html,text/plain,text/xml,video/mpeg,video/mp4,video/quicktime,video/x-ms-wmv,video/x-msvideo,video/x-flv,video/web,application/msword,application/vnd.ms-powerpoint,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.presentation,application/vnd.oasis.opendocument.tex,application/vnd.oasis.opendocument.spreadsheet,application/vnd.oasis.opendocument.graphics,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
type: SettingType.textarea,
|
type: SettingType.textarea,
|
||||||
weight: 15,
|
|
||||||
},
|
},
|
||||||
];
|
] as const satisfies ChannelSetting[];
|
||||||
|
1301
api/src/extensions/channels/offline/base-web-channel.ts
Normal file
1301
api/src/extensions/channels/offline/base-web-channel.ts
Normal file
File diff suppressed because it is too large
Load Diff
23
api/src/extensions/channels/offline/i18n/en/label.json
Normal file
23
api/src/extensions/channels/offline/i18n/en/label.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"verification_token": "Verification Token",
|
||||||
|
"allowed_domains": "Allowed Domains",
|
||||||
|
"start_button": "Enable `Get Started`",
|
||||||
|
"input_disabled": "Disable Input",
|
||||||
|
"persistent_menu": "Display Persistent Menu",
|
||||||
|
"greeting_message": "Greeting Message",
|
||||||
|
"theme_color": "Widget Theme",
|
||||||
|
"theme_color_options": {
|
||||||
|
"orange": "Orange",
|
||||||
|
"red": "Red",
|
||||||
|
"green": "Green",
|
||||||
|
"blue": "Blue",
|
||||||
|
"dark": "Dark"
|
||||||
|
},
|
||||||
|
"window_title": "Chat Window Title",
|
||||||
|
"avatar_url": "Chatbot Avatar URL",
|
||||||
|
"show_emoji": "Enable Emoji Picker",
|
||||||
|
"show_file": "Enable Attachment Uploader",
|
||||||
|
"show_location": "Enable Geolocation Share",
|
||||||
|
"allowed_upload_size": "Max Upload Size (in bytes)",
|
||||||
|
"allowed_upload_types": "Allowed Upload Mime Types (comma separated)"
|
||||||
|
}
|
3
api/src/extensions/channels/offline/i18n/en/title.json
Normal file
3
api/src/extensions/channels/offline/i18n/en/title.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"offline": "Canal Web"
|
||||||
|
}
|
23
api/src/extensions/channels/offline/i18n/fr/label.json
Normal file
23
api/src/extensions/channels/offline/i18n/fr/label.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"verification_token": "Jeton de vérification",
|
||||||
|
"allowed_domains": "Domaines autorisés",
|
||||||
|
"start_button": "Activer `Démarrer`",
|
||||||
|
"input_disabled": "Désactiver la saisie",
|
||||||
|
"persistent_menu": "Afficher le menu persistent",
|
||||||
|
"greeting_message": "Message de bienvenue",
|
||||||
|
"theme_color": "Thème du widget",
|
||||||
|
"theme_color_options": {
|
||||||
|
"orange": "Orange",
|
||||||
|
"red": "Rouge",
|
||||||
|
"green": "Vert",
|
||||||
|
"blue": "Bleu",
|
||||||
|
"dark": "Sombre"
|
||||||
|
},
|
||||||
|
"window_title": "Titre de la fenêtre de chat",
|
||||||
|
"avatar_url": "Avatar du chatbot (URL)",
|
||||||
|
"show_emoji": "Activer le sélecteur d'Emojis",
|
||||||
|
"show_file": "Activer l'upload de fichiers",
|
||||||
|
"show_location": "Activer le partage de géolocalisation",
|
||||||
|
"allowed_upload_size": "Taille maximale de téléchargement (en octets)",
|
||||||
|
"allowed_upload_types": "Types MIME autorisés pour le téléchargement (séparés par des virgules)"
|
||||||
|
}
|
3
api/src/extensions/channels/offline/i18n/fr/title.json
Normal file
3
api/src/extensions/channels/offline/i18n/fr/title.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"live_chat_tester": "Testeur Live Chat"
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
14
api/src/extensions/channels/offline/index.d.ts
vendored
Normal file
14
api/src/extensions/channels/offline/index.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { DEFAULT_OFFLINE_SETTINGS, OFFLINE_CHANNEL_NAME } from './settings';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Settings extends SettingTree<typeof DEFAULT_OFFLINE_SETTINGS> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@nestjs/event-emitter' {
|
||||||
|
interface IHookSettingsGroupLabelOperationMap {
|
||||||
|
[key: HyphenToUnderscore<typeof OFFLINE_CHANNEL_NAME>]: TDefinition<
|
||||||
|
object,
|
||||||
|
SettingObject<typeof DEFAULT_OFFLINE_SETTINGS>
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
}
|
@ -6,112 +6,100 @@
|
|||||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { SettingCreateDto } from '@/setting/dto/setting.dto';
|
import { ChannelSetting } from '@/channel/types';
|
||||||
import { SettingType } from '@/setting/schemas/types';
|
import { SettingType } from '@/setting/schemas/types';
|
||||||
|
|
||||||
import { Offline } from './types';
|
import { Offline } from './types';
|
||||||
|
|
||||||
export const OFFLINE_CHANNEL_NAME = 'offline';
|
export const OFFLINE_CHANNEL_NAME = 'offline' as const;
|
||||||
|
|
||||||
export const DEFAULT_OFFLINE_SETTINGS: SettingCreateDto[] = [
|
export const OFFLINE_GROUP_NAME = OFFLINE_CHANNEL_NAME;
|
||||||
|
|
||||||
|
export const DEFAULT_OFFLINE_SETTINGS = [
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.verification_token,
|
label: Offline.SettingLabel.verification_token,
|
||||||
value: 'token123',
|
value: 'token123',
|
||||||
type: SettingType.secret,
|
type: SettingType.secret,
|
||||||
weight: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.allowed_domains,
|
label: Offline.SettingLabel.allowed_domains,
|
||||||
value: 'http://localhost:8080,http://localhost:4000',
|
value: 'http://localhost:8080,http://localhost:4000',
|
||||||
type: SettingType.text,
|
type: SettingType.text,
|
||||||
weight: 3,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.start_button,
|
label: Offline.SettingLabel.start_button,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 4,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.input_disabled,
|
label: Offline.SettingLabel.input_disabled,
|
||||||
value: false,
|
value: false,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 5,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.persistent_menu,
|
label: Offline.SettingLabel.persistent_menu,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 6,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.greeting_message,
|
label: Offline.SettingLabel.greeting_message,
|
||||||
value: 'Welcome! Ready to start a conversation with our chatbot?',
|
value: 'Welcome! Ready to start a conversation with our chatbot?',
|
||||||
type: SettingType.textarea,
|
type: SettingType.textarea,
|
||||||
weight: 7,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.theme_color,
|
label: Offline.SettingLabel.theme_color,
|
||||||
value: 'teal',
|
value: 'teal',
|
||||||
type: SettingType.select,
|
type: SettingType.select,
|
||||||
options: ['teal', 'orange', 'red', 'green', 'blue', 'dark'],
|
options: ['teal', 'orange', 'red', 'green', 'blue', 'dark'],
|
||||||
weight: 8,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.window_title,
|
label: Offline.SettingLabel.window_title,
|
||||||
value: 'Widget Title',
|
value: 'Widget Title',
|
||||||
type: SettingType.text,
|
type: SettingType.text,
|
||||||
weight: 9,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.avatar_url,
|
label: Offline.SettingLabel.avatar_url,
|
||||||
value: 'https://eu.ui-avatars.com/api/?name=Hexa+Bot&size=64',
|
value: 'https://eu.ui-avatars.com/api/?name=Hexa+Bot&size=64',
|
||||||
type: SettingType.text,
|
type: SettingType.text,
|
||||||
weight: 10,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.show_emoji,
|
label: Offline.SettingLabel.show_emoji,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 11,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.show_file,
|
label: Offline.SettingLabel.show_file,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 12,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.show_location,
|
label: Offline.SettingLabel.show_location,
|
||||||
value: true,
|
value: true,
|
||||||
type: SettingType.checkbox,
|
type: SettingType.checkbox,
|
||||||
weight: 13,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.allowed_upload_size,
|
label: Offline.SettingLabel.allowed_upload_size,
|
||||||
value: 2500000,
|
value: 2500000,
|
||||||
type: SettingType.number,
|
type: SettingType.number,
|
||||||
weight: 14,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: OFFLINE_CHANNEL_NAME,
|
group: OFFLINE_GROUP_NAME,
|
||||||
label: Offline.SettingLabel.allowed_upload_types,
|
label: Offline.SettingLabel.allowed_upload_types,
|
||||||
value:
|
value:
|
||||||
'audio/mpeg,audio/x-ms-wma,audio/vnd.rn-realaudio,audio/x-wav,image/gif,image/jpeg,image/png,image/tiff,image/vnd.microsoft.icon,image/vnd.djvu,image/svg+xml,text/css,text/csv,text/html,text/plain,text/xml,video/mpeg,video/mp4,video/quicktime,video/x-ms-wmv,video/x-msvideo,video/x-flv,video/web,application/msword,application/vnd.ms-powerpoint,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.presentation,application/vnd.oasis.opendocument.tex,application/vnd.oasis.opendocument.spreadsheet,application/vnd.oasis.opendocument.graphics,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
'audio/mpeg,audio/x-ms-wma,audio/vnd.rn-realaudio,audio/x-wav,image/gif,image/jpeg,image/png,image/tiff,image/vnd.microsoft.icon,image/vnd.djvu,image/svg+xml,text/css,text/csv,text/html,text/plain,text/xml,video/mpeg,video/mp4,video/quicktime,video/x-ms-wmv,video/x-msvideo,video/x-flv,video/web,application/msword,application/vnd.ms-powerpoint,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.presentation,application/vnd.oasis.opendocument.tex,application/vnd.oasis.opendocument.spreadsheet,application/vnd.oasis.opendocument.graphics,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
type: SettingType.textarea,
|
type: SettingType.textarea,
|
||||||
weight: 15,
|
|
||||||
},
|
},
|
||||||
];
|
] as const satisfies ChannelSetting<typeof OFFLINE_CHANNEL_NAME>[];
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
} from '@/chat/schemas/types/message';
|
} from '@/chat/schemas/types/message';
|
||||||
import { Payload } from '@/chat/schemas/types/quick-reply';
|
import { Payload } from '@/chat/schemas/types/quick-reply';
|
||||||
|
|
||||||
import OfflineHandler from './index.channel';
|
import BaseWebChannelHandler from './base-web-channel';
|
||||||
import { Offline } from './types';
|
import { Offline } from './types';
|
||||||
|
|
||||||
type OfflineEventAdapter =
|
type OfflineEventAdapter =
|
||||||
@ -66,10 +66,9 @@ type OfflineEventAdapter =
|
|||||||
raw: Offline.IncomingMessage<Offline.IncomingAttachmentMessage>;
|
raw: Offline.IncomingMessage<Offline.IncomingAttachmentMessage>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class OfflineEventWrapper extends EventWrapper<
|
export default class OfflineEventWrapper<
|
||||||
OfflineEventAdapter,
|
T extends BaseWebChannelHandler<string> = BaseWebChannelHandler<string>,
|
||||||
Offline.Event
|
> extends EventWrapper<OfflineEventAdapter, Offline.Event> {
|
||||||
> {
|
|
||||||
/**
|
/**
|
||||||
* Constructor : channel's event wrapper
|
* Constructor : channel's event wrapper
|
||||||
*
|
*
|
||||||
@ -77,7 +76,7 @@ export default class OfflineEventWrapper extends EventWrapper<
|
|||||||
* @param event - The message event received
|
* @param event - The message event received
|
||||||
* @param channelData - Channel's specific extra data {isSocket, ipAddress}
|
* @param channelData - Channel's specific extra data {isSocket, ipAddress}
|
||||||
*/
|
*/
|
||||||
constructor(handler: OfflineHandler, event: Offline.Event, channelData: any) {
|
constructor(handler: T, event: Offline.Event, channelData: any) {
|
||||||
super(handler, event, channelData);
|
super(handler, event, channelData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,9 +152,12 @@ describe('NLP Default Helper', () => {
|
|||||||
const nlp = nlpService.getNLP();
|
const nlp = nlpService.getNLP();
|
||||||
const results = nlp.bestGuess(nlpParseResult, true);
|
const results = nlp.bestGuess(nlpParseResult, true);
|
||||||
const settings = await settingService.getSettings();
|
const settings = await settingService.getSettings();
|
||||||
|
const threshold = settings.nlp_settings.threshold;
|
||||||
const thresholdGuess = {
|
const thresholdGuess = {
|
||||||
entities: nlpBestGuess.entities.filter(
|
entities: nlpBestGuess.entities.filter(
|
||||||
(g) => g.confidence > parseFloat(settings.nlp_settings.threshold),
|
(g) =>
|
||||||
|
g.confidence >
|
||||||
|
(typeof threshold === 'string' ? parseFloat(threshold) : threshold),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
expect(results).toEqual(thresholdGuess);
|
expect(results).toEqual(thresholdGuess);
|
||||||
|
@ -135,7 +135,11 @@ export default class DefaultNlpHelper extends BaseNlpHelper {
|
|||||||
entities: nlp.entities.slice(),
|
entities: nlp.entities.slice(),
|
||||||
};
|
};
|
||||||
if (threshold) {
|
if (threshold) {
|
||||||
minConfidence = Number.parseFloat(this.settings.threshold);
|
const threshold = this.settings.threshold;
|
||||||
|
minConfidence =
|
||||||
|
typeof threshold === 'string'
|
||||||
|
? Number.parseFloat(threshold)
|
||||||
|
: threshold;
|
||||||
guess.entities = guess.entities
|
guess.entities = guess.entities
|
||||||
.map((e) => {
|
.map((e) => {
|
||||||
e.confidence =
|
e.confidence =
|
||||||
|
28
api/src/i18n/controllers/i18n.controller.ts
Normal file
28
api/src/i18n/controllers/i18n.controller.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2024 Hexastack. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||||
|
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||||
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Controller, Get, UseInterceptors } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { CsrfInterceptor } from '@/interceptors/csrf.interceptor';
|
||||||
|
|
||||||
|
import { I18nService } from '../services/i18n.service';
|
||||||
|
|
||||||
|
@UseInterceptors(CsrfInterceptor)
|
||||||
|
@Controller('i18n')
|
||||||
|
export class I18nController {
|
||||||
|
constructor(private readonly i18nService: I18nService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves translations of all the installed extensions.
|
||||||
|
* @returns An nested object that holds the translations grouped by language and extension name.
|
||||||
|
*/
|
||||||
|
@Get()
|
||||||
|
getTranslations() {
|
||||||
|
return this.i18nService.getExtensionI18nTranslations();
|
||||||
|
}
|
||||||
|
}
|
@ -46,10 +46,10 @@ export class LanguageController extends BaseController<Language> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a paginated list of categories based on provided filters and pagination settings.
|
* Retrieves a paginated list of languages based on provided filters and pagination settings.
|
||||||
* @param pageQuery - The pagination settings.
|
* @param pageQuery - The pagination settings.
|
||||||
* @param filters - The filters to apply to the language search.
|
* @param filters - The filters to apply to the language search.
|
||||||
* @returns A Promise that resolves to a paginated list of categories.
|
* @returns A Promise that resolves to a paginated list of languages.
|
||||||
*/
|
*/
|
||||||
@Get()
|
@Get()
|
||||||
async findPage(
|
async findPage(
|
||||||
@ -61,8 +61,8 @@ export class LanguageController extends BaseController<Language> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts the filtered number of categories.
|
* Counts the filtered number of languages.
|
||||||
* @returns A promise that resolves to an object representing the filtered number of categories.
|
* @returns A promise that resolves to an object representing the filtered number of languages.
|
||||||
*/
|
*/
|
||||||
@Get('count')
|
@Get('count')
|
||||||
async filterCount(
|
async filterCount(
|
||||||
|
@ -26,6 +26,7 @@ import { Observable } from 'rxjs';
|
|||||||
|
|
||||||
import { ChatModule } from '@/chat/chat.module';
|
import { ChatModule } from '@/chat/chat.module';
|
||||||
|
|
||||||
|
import { I18nController } from './controllers/i18n.controller';
|
||||||
import { LanguageController } from './controllers/language.controller';
|
import { LanguageController } from './controllers/language.controller';
|
||||||
import { TranslationController } from './controllers/translation.controller';
|
import { TranslationController } from './controllers/translation.controller';
|
||||||
import { LanguageRepository } from './repositories/language.repository';
|
import { LanguageRepository } from './repositories/language.repository';
|
||||||
@ -62,6 +63,7 @@ export class I18nModule extends NativeI18nModule {
|
|||||||
controllers: (controllers || []).concat([
|
controllers: (controllers || []).concat([
|
||||||
LanguageController,
|
LanguageController,
|
||||||
TranslationController,
|
TranslationController,
|
||||||
|
I18nController,
|
||||||
]),
|
]),
|
||||||
providers: providers.concat([
|
providers: providers.concat([
|
||||||
I18nService,
|
I18nService,
|
||||||
|
@ -6,8 +6,13 @@
|
|||||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { promises as fs } from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
|
I18nJsonLoader,
|
||||||
|
I18nTranslation,
|
||||||
I18nService as NativeI18nService,
|
I18nService as NativeI18nService,
|
||||||
Path,
|
Path,
|
||||||
PathValue,
|
PathValue,
|
||||||
@ -19,11 +24,22 @@ import { config } from '@/config';
|
|||||||
import { Translation } from '@/i18n/schemas/translation.schema';
|
import { Translation } from '@/i18n/schemas/translation.schema';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class I18nService<
|
export class I18nService<K = Record<string, unknown>>
|
||||||
K = Record<string, unknown>,
|
extends NativeI18nService<K>
|
||||||
> extends NativeI18nService<K> {
|
implements OnModuleInit
|
||||||
|
{
|
||||||
private dynamicTranslations: Record<string, Record<string, string>> = {};
|
private dynamicTranslations: Record<string, Record<string, string>> = {};
|
||||||
|
|
||||||
|
private extensionTranslations: I18nTranslation = {};
|
||||||
|
|
||||||
|
onModuleInit() {
|
||||||
|
this.loadExtensionI18nTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
|
getExtensionI18nTranslations() {
|
||||||
|
return this.extensionTranslations;
|
||||||
|
}
|
||||||
|
|
||||||
t<P extends Path<K> = any, R = PathValue<K, P>>(
|
t<P extends Path<K> = any, R = PathValue<K, P>>(
|
||||||
key: P,
|
key: P,
|
||||||
options?: TranslateOptions,
|
options?: TranslateOptions,
|
||||||
@ -66,4 +82,48 @@ export class I18nService<
|
|||||||
return acc;
|
return acc;
|
||||||
}, this.dynamicTranslations);
|
}, this.dynamicTranslations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadExtensionI18nTranslations() {
|
||||||
|
const extensionsDir = path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'extensions',
|
||||||
|
'channels',
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
const extensionFolders = await fs.readdir(extensionsDir, {
|
||||||
|
withFileTypes: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const folder of extensionFolders) {
|
||||||
|
if (folder.isDirectory()) {
|
||||||
|
const i18nPath = path.join(extensionsDir, folder.name, 'i18n');
|
||||||
|
const extensionName = folder.name.replaceAll('-', '_');
|
||||||
|
try {
|
||||||
|
// Check if the i18n directory exists
|
||||||
|
await fs.access(i18nPath);
|
||||||
|
|
||||||
|
// Load and merge translations
|
||||||
|
const i18nLoader = new I18nJsonLoader({ path: i18nPath });
|
||||||
|
const translations = await i18nLoader.load();
|
||||||
|
for (const lang in translations) {
|
||||||
|
if (!this.extensionTranslations[lang]) {
|
||||||
|
this.extensionTranslations[lang] = {
|
||||||
|
[extensionName]: translations[lang],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.extensionTranslations[lang][extensionName] =
|
||||||
|
translations[lang];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// If the i18n folder does not exist or error in reading, skip this folder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to read extensions directory: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
import { I18nService } from '@/i18n/services/i18n.service';
|
import { I18nService } from '@/i18n/services/i18n.service';
|
||||||
import { Settings } from '@/setting/schemas/types';
|
|
||||||
import { SettingService } from '@/setting/services/setting.service';
|
import { SettingService } from '@/setting/services/setting.service';
|
||||||
|
|
||||||
import { Block } from '../../chat/schemas/block.schema';
|
import { Block } from '../../chat/schemas/block.schema';
|
||||||
@ -64,7 +63,7 @@ describe('TranslationService', () => {
|
|||||||
global_fallback: true,
|
global_fallback: true,
|
||||||
fallback_message: ['Global fallback message'],
|
fallback_message: ['Global fallback message'],
|
||||||
},
|
},
|
||||||
} as Settings),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
10
api/src/index.d.ts
vendored
10
api/src/index.d.ts
vendored
@ -9,9 +9,9 @@
|
|||||||
import 'mongoose';
|
import 'mongoose';
|
||||||
import { SubscriberStub } from './chat/schemas/subscriber.schema';
|
import { SubscriberStub } from './chat/schemas/subscriber.schema';
|
||||||
import {
|
import {
|
||||||
WithoutGenericAny,
|
|
||||||
RecursivePartial,
|
|
||||||
ObjectWithNestedKeys,
|
ObjectWithNestedKeys,
|
||||||
|
RecursivePartial,
|
||||||
|
WithoutGenericAny,
|
||||||
} from './utils/types/filter.types';
|
} from './utils/types/filter.types';
|
||||||
|
|
||||||
type TOmitId<T> = Omit<T, 'id'>;
|
type TOmitId<T> = Omit<T, 'id'>;
|
||||||
@ -63,3 +63,9 @@ declare module 'mongoose' {
|
|||||||
|
|
||||||
type THydratedDocument<T> = TOmitId<HydratedDocument<T>>;
|
type THydratedDocument<T> = TOmitId<HydratedDocument<T>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
type HyphenToUnderscore<S extends string> = S extends `${infer P}-${infer Q}`
|
||||||
|
? `${P}_${HyphenToUnderscore<Q>}`
|
||||||
|
: S;
|
||||||
|
}
|
||||||
|
@ -31,7 +31,6 @@ import {
|
|||||||
NlpValueDocument,
|
NlpValueDocument,
|
||||||
NlpValueFull,
|
NlpValueFull,
|
||||||
} from '@/nlp/schemas/nlp-value.schema';
|
} from '@/nlp/schemas/nlp-value.schema';
|
||||||
import { Settings } from '@/setting/schemas/types';
|
|
||||||
|
|
||||||
import { NlpEntityService } from '../services/nlp-entity.service';
|
import { NlpEntityService } from '../services/nlp-entity.service';
|
||||||
import { NlpSampleService } from '../services/nlp-sample.service';
|
import { NlpSampleService } from '../services/nlp-sample.service';
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import { BlockCreateDto } from '@/chat/dto/block.dto';
|
import { BlockCreateDto } from '@/chat/dto/block.dto';
|
||||||
import { Block } from '@/chat/schemas/block.schema';
|
import { Block } from '@/chat/schemas/block.schema';
|
||||||
import { Conversation } from '@/chat/schemas/conversation.schema';
|
import { Conversation } from '@/chat/schemas/conversation.schema';
|
||||||
import { Setting } from '@/setting/schemas/setting.schema';
|
import { SettingCreateDto } from '@/setting/dto/setting.dto';
|
||||||
|
|
||||||
export enum PluginType {
|
export enum PluginType {
|
||||||
event = 'event',
|
event = 'event',
|
||||||
@ -22,7 +22,7 @@ export interface CustomBlocks {}
|
|||||||
type ChannelEvent = any;
|
type ChannelEvent = any;
|
||||||
type BlockAttrs = Partial<BlockCreateDto> & { name: string };
|
type BlockAttrs = Partial<BlockCreateDto> & { name: string };
|
||||||
|
|
||||||
export type PluginSetting = Omit<Setting, 'id' | 'createdAt' | 'updatedAt'>;
|
export type PluginSetting = SettingCreateDto;
|
||||||
|
|
||||||
export type PluginBlockTemplate = Omit<
|
export type PluginBlockTemplate = Omit<
|
||||||
BlockAttrs,
|
BlockAttrs,
|
||||||
|
@ -22,7 +22,7 @@ import { nlpEntityModels } from './nlp/seeds/nlp-entity.seed-model';
|
|||||||
import { NlpValueSeeder } from './nlp/seeds/nlp-value.seed';
|
import { NlpValueSeeder } from './nlp/seeds/nlp-value.seed';
|
||||||
import { nlpValueModels } from './nlp/seeds/nlp-value.seed-model';
|
import { nlpValueModels } from './nlp/seeds/nlp-value.seed-model';
|
||||||
import { SettingSeeder } from './setting/seeds/setting.seed';
|
import { SettingSeeder } from './setting/seeds/setting.seed';
|
||||||
import { settingModels } from './setting/seeds/setting.seed-model';
|
import { DEFAULT_SETTINGS } from './setting/seeds/setting.seed-model';
|
||||||
import { ModelSeeder } from './user/seeds/model.seed';
|
import { ModelSeeder } from './user/seeds/model.seed';
|
||||||
import { modelModels } from './user/seeds/model.seed-model';
|
import { modelModels } from './user/seeds/model.seed-model';
|
||||||
import { PermissionSeeder } from './user/seeds/permission.seed';
|
import { PermissionSeeder } from './user/seeds/permission.seed';
|
||||||
@ -106,7 +106,7 @@ export async function seedDatabase(app: INestApplicationContext) {
|
|||||||
}
|
}
|
||||||
// Seed users
|
// Seed users
|
||||||
try {
|
try {
|
||||||
await settingSeeder.seed(settingModels);
|
await settingSeeder.seed(DEFAULT_SETTINGS);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Unable to seed the database with settings!');
|
logger.error('Unable to seed the database with settings!');
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -11,25 +11,30 @@ import {
|
|||||||
IsArray,
|
IsArray,
|
||||||
IsIn,
|
IsIn,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsString,
|
|
||||||
IsOptional,
|
IsOptional,
|
||||||
|
IsString,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|
||||||
import { SettingType } from '../schemas/types';
|
import { SettingType } from '../schemas/types';
|
||||||
|
|
||||||
export class SettingCreateDto {
|
export class SettingCreateDto {
|
||||||
@ApiProperty({ description: 'Setting group of setting', type: String })
|
@ApiProperty({ description: 'Setting group', type: String })
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsString()
|
@IsString()
|
||||||
group: string;
|
group: string;
|
||||||
|
|
||||||
@ApiProperty({ description: 'Setting label of setting', type: String })
|
@ApiProperty({ description: 'Setting subgroup', type: String })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
subgroup?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Setting label (system name)', type: String })
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsString()
|
@IsString()
|
||||||
label: string;
|
label: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Setting type of the setting',
|
description: 'Setting type',
|
||||||
enum: [
|
enum: [
|
||||||
'text',
|
'text',
|
||||||
'multiple_text',
|
'multiple_text',
|
||||||
@ -44,12 +49,12 @@ export class SettingCreateDto {
|
|||||||
@IsIn(Object.values(SettingType))
|
@IsIn(Object.values(SettingType))
|
||||||
type: SettingType;
|
type: SettingType;
|
||||||
|
|
||||||
@ApiProperty({ description: 'Setting value of the setting' })
|
@ApiProperty({ description: 'Setting value' })
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
value: any;
|
value: any;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: 'Setting options',
|
description: 'Setting options (required when type is select)',
|
||||||
isArray: true,
|
isArray: true,
|
||||||
type: Array,
|
type: Array,
|
||||||
})
|
})
|
||||||
|
31
api/src/setting/index.d.ts
vendored
Normal file
31
api/src/setting/index.d.ts
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { DEFAULT_SETTINGS } from './seeds/setting.seed-model';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
type TNativeType<T> = T extends string
|
||||||
|
? string
|
||||||
|
: T extends number
|
||||||
|
? number
|
||||||
|
: T extends boolean
|
||||||
|
? boolean
|
||||||
|
: T extends Array<infer U>
|
||||||
|
? TNativeType<U>[]
|
||||||
|
: T extends object
|
||||||
|
? { [K in keyof T]: TNativeType<T[K]> }
|
||||||
|
: T;
|
||||||
|
|
||||||
|
type SettingObject<
|
||||||
|
T extends Omit<Setting, 'id' | 'createdAt' | 'updatedAt'>[],
|
||||||
|
> = {
|
||||||
|
[K in T[number] as K['label']]: TNativeType<K['value']>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SettingTree<
|
||||||
|
T extends Omit<Setting, 'id' | 'createdAt' | 'updatedAt'>[],
|
||||||
|
> = {
|
||||||
|
[G in T[number] as G['group']]: {
|
||||||
|
[K in T[number] as K['label']]: TNativeType<K['value']>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Settings extends SettingTree<typeof DEFAULT_SETTINGS> {}
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ModelDefinition, Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
import { ModelDefinition, Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||||
|
import { Transform } from 'class-transformer';
|
||||||
import { IsArray, IsIn } from 'class-validator';
|
import { IsArray, IsIn } from 'class-validator';
|
||||||
|
|
||||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||||
@ -22,6 +23,13 @@ export class Setting extends BaseSchema {
|
|||||||
})
|
})
|
||||||
group: string;
|
group: string;
|
||||||
|
|
||||||
|
@Prop({
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
})
|
||||||
|
@Transform(({ obj }) => obj.subgroup || undefined)
|
||||||
|
subgroup?: string;
|
||||||
|
|
||||||
@Prop({
|
@Prop({
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -94,18 +94,3 @@ export type AnySetting =
|
|||||||
| MultipleAttachmentSetting;
|
| MultipleAttachmentSetting;
|
||||||
|
|
||||||
export type SettingDict = { [group: string]: Setting[] };
|
export type SettingDict = { [group: string]: Setting[] };
|
||||||
|
|
||||||
export type Settings = {
|
|
||||||
nlp_settings: {
|
|
||||||
threshold: string;
|
|
||||||
provider: string;
|
|
||||||
endpoint: string;
|
|
||||||
token: string;
|
|
||||||
};
|
|
||||||
contact: { [key: string]: string };
|
|
||||||
chatbot_settings: {
|
|
||||||
global_fallback: boolean;
|
|
||||||
fallback_message: string[];
|
|
||||||
fallback_block: string;
|
|
||||||
};
|
|
||||||
} & Record<string, any>;
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import { SettingCreateDto } from '../dto/setting.dto';
|
import { SettingCreateDto } from '../dto/setting.dto';
|
||||||
import { SettingType } from '../schemas/types';
|
import { SettingType } from '../schemas/types';
|
||||||
|
|
||||||
export const settingModels: SettingCreateDto[] = [
|
export const DEFAULT_SETTINGS = [
|
||||||
{
|
{
|
||||||
group: 'chatbot_settings',
|
group: 'chatbot_settings',
|
||||||
label: 'global_fallback',
|
label: 'global_fallback',
|
||||||
@ -38,7 +38,7 @@ export const settingModels: SettingCreateDto[] = [
|
|||||||
value: [
|
value: [
|
||||||
"Sorry but i didn't understand your request. Maybe you can check the menu",
|
"Sorry but i didn't understand your request. Maybe you can check the menu",
|
||||||
"I'm really sorry but i don't quite understand what you are saying :(",
|
"I'm really sorry but i don't quite understand what you are saying :(",
|
||||||
],
|
] as string[],
|
||||||
type: SettingType.multiple_text,
|
type: SettingType.multiple_text,
|
||||||
weight: 3,
|
weight: 3,
|
||||||
},
|
},
|
||||||
@ -146,4 +146,4 @@ export const settingModels: SettingCreateDto[] = [
|
|||||||
type: SettingType.text,
|
type: SettingType.text,
|
||||||
weight: 10,
|
weight: 10,
|
||||||
},
|
},
|
||||||
];
|
] as const satisfies SettingCreateDto[];
|
||||||
|
@ -21,7 +21,6 @@ import { BaseService } from '@/utils/generics/base-service';
|
|||||||
import { SettingCreateDto } from '../dto/setting.dto';
|
import { SettingCreateDto } from '../dto/setting.dto';
|
||||||
import { SettingRepository } from '../repositories/setting.repository';
|
import { SettingRepository } from '../repositories/setting.repository';
|
||||||
import { Setting } from '../schemas/setting.schema';
|
import { Setting } from '../schemas/setting.schema';
|
||||||
import { Settings } from '../schemas/types';
|
|
||||||
import { SettingSeeder } from '../seeds/setting.seed';
|
import { SettingSeeder } from '../seeds/setting.seed';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -53,7 +53,7 @@ export class Ability implements CanActivate {
|
|||||||
|
|
||||||
if (user?.roles?.length) {
|
if (user?.roles?.length) {
|
||||||
if (
|
if (
|
||||||
['/auth/logout', '/logout', '/auth/me', '/channel'].includes(
|
['/auth/logout', '/logout', '/auth/me', '/channel', '/i18n'].includes(
|
||||||
_parsedUrl.pathname,
|
_parsedUrl.pathname,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -17,9 +17,11 @@
|
|||||||
"strictBindCallApply": false,
|
"strictBindCallApply": false,
|
||||||
"forceConsistentCasingInFileNames": false,
|
"forceConsistentCasingInFileNames": false,
|
||||||
"noFallthroughCasesInSwitch": false,
|
"noFallthroughCasesInSwitch": false,
|
||||||
|
"resolveJsonModule": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.json"]
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"title": {
|
|
||||||
"live-chat-tester": "Live Chat Tester"
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"verification_token": "Verification Token",
|
|
||||||
"allowed_domains": "Allowed Domains",
|
|
||||||
"start_button": "Enable `Get Started`",
|
|
||||||
"input_disabled": "Disable Input",
|
|
||||||
"persistent_menu": "Display Persistent Menu",
|
|
||||||
"greeting_message": "Greeting Message",
|
|
||||||
"theme_color": "Widget Theme",
|
|
||||||
"theme_color_options": {
|
|
||||||
"orange": "Orange",
|
|
||||||
"red": "Red",
|
|
||||||
"green": "Green",
|
|
||||||
"blue": "Blue",
|
|
||||||
"dark": "Dark"
|
|
||||||
},
|
|
||||||
"window_title": "Chat Window Title",
|
|
||||||
"avatar_url": "Chatbot Avatar URL",
|
|
||||||
"show_emoji": "Enable Emoji Picker",
|
|
||||||
"show_file": "Enable Attachment Uploader",
|
|
||||||
"show_location": "Enable Geolocation Share",
|
|
||||||
"allowed_upload_size": "Max Upload Size (in bytes)",
|
|
||||||
"allowed_upload_types": "Allowed Upload Mime Types (comma separated)"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"title": {
|
|
||||||
"offline": "Web Channel"
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"verification_token": "Verification Token",
|
|
||||||
"allowed_domains": "Allowed Domains",
|
|
||||||
"start_button": "Enable `Get Started`",
|
|
||||||
"input_disabled": "Disable Input",
|
|
||||||
"persistent_menu": "Display Persistent Menu",
|
|
||||||
"greeting_message": "Greeting Message",
|
|
||||||
"theme_color": "Widget Theme",
|
|
||||||
"theme_color_options": {
|
|
||||||
"orange": "Orange",
|
|
||||||
"red": "Red",
|
|
||||||
"green": "Green",
|
|
||||||
"blue": "Blue",
|
|
||||||
"dark": "Dark"
|
|
||||||
},
|
|
||||||
"window_title": "Chat Window Title",
|
|
||||||
"avatar_url": "Chatbot Avatar URL",
|
|
||||||
"show_emoji": "Enable Emoji Picker",
|
|
||||||
"show_file": "Enable Attachment Uploader",
|
|
||||||
"show_location": "Enable Geolocation Share",
|
|
||||||
"allowed_upload_size": "Max Upload Size (in bytes)",
|
|
||||||
"allowed_upload_types": "Allowed Upload Mime Types (comma separated)"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"title": {
|
|
||||||
"live-chat-tester": "Testeur Live Chat"
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"verification_token": "Jeton de vérification",
|
|
||||||
"allowed_domains": "Domaines autorisés",
|
|
||||||
"start_button": "Activer `Démarrer`",
|
|
||||||
"input_disabled": "Désactiver la saisie",
|
|
||||||
"persistent_menu": "Afficher le menu persistent",
|
|
||||||
"greeting_message": "Message de bienvenue",
|
|
||||||
"theme_color": "Thème du widget",
|
|
||||||
"theme_color_options": {
|
|
||||||
"orange": "Orange",
|
|
||||||
"red": "Rouge",
|
|
||||||
"green": "Vert",
|
|
||||||
"blue": "Bleu",
|
|
||||||
"dark": "Sombre"
|
|
||||||
},
|
|
||||||
"window_title": "Titre de la fenêtre de chat",
|
|
||||||
"avatar_url": "Avatar du chatbot (URL)",
|
|
||||||
"show_emoji": "Activer le sélecteur d'Emojis",
|
|
||||||
"show_file": "Activer l'upload de fichiers",
|
|
||||||
"show_location": "Activer le partage de géolocalisation",
|
|
||||||
"allowed_upload_size": "Taille maximale de téléchargement (en octets)",
|
|
||||||
"allowed_upload_types": "Types MIME autorisés pour le téléchargement (séparés par des virgules)"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"title": {
|
|
||||||
"offline": "Canal Web"
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"verification_token": "Jeton de vérification",
|
|
||||||
"allowed_domains": "Domaines autorisés",
|
|
||||||
"start_button": "Activer `Démarrer`",
|
|
||||||
"input_disabled": "Désactiver la saisie",
|
|
||||||
"persistent_menu": "Afficher le menu persistent",
|
|
||||||
"greeting_message": "Message de bienvenue",
|
|
||||||
"theme_color": "Thème du widget",
|
|
||||||
"theme_color_options": {
|
|
||||||
"orange": "Orange",
|
|
||||||
"red": "Rouge",
|
|
||||||
"green": "Vert",
|
|
||||||
"blue": "Bleu",
|
|
||||||
"dark": "Sombre"
|
|
||||||
},
|
|
||||||
"window_title": "Titre de la fenêtre de chat",
|
|
||||||
"avatar_url": "Avatar du chatbot (URL)",
|
|
||||||
"show_emoji": "Activer le sélecteur d'Emojis",
|
|
||||||
"show_file": "Activer l'upload de fichiers",
|
|
||||||
"show_location": "Activer le partage de géolocalisation",
|
|
||||||
"allowed_upload_size": "Taille maximale de téléchargement (en octets)",
|
|
||||||
"allowed_upload_types": "Types MIME autorisés pour le téléchargement (séparés par des virgules)"
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,6 +10,7 @@ import { createContext, ReactNode } from "react";
|
|||||||
|
|
||||||
import { Progress } from "@/app-components/displays/Progress";
|
import { Progress } from "@/app-components/displays/Progress";
|
||||||
import { useLoadSettings } from "@/hooks/entities/auth-hooks";
|
import { useLoadSettings } from "@/hooks/entities/auth-hooks";
|
||||||
|
import { useRemoteI18n } from "@/hooks/useRemoteI18n";
|
||||||
import { ISetting } from "@/types/setting.types";
|
import { ISetting } from "@/types/setting.types";
|
||||||
|
|
||||||
export const SettingsContext = createContext<{
|
export const SettingsContext = createContext<{
|
||||||
@ -27,6 +28,9 @@ export const SettingsProvider = ({
|
|||||||
}: SettingsProviderProps): JSX.Element => {
|
}: SettingsProviderProps): JSX.Element => {
|
||||||
const { data, isLoading } = useLoadSettings();
|
const { data, isLoading } = useLoadSettings();
|
||||||
|
|
||||||
|
// Load API i18n Translations (extensions, ...)
|
||||||
|
useRemoteI18n();
|
||||||
|
|
||||||
if (isLoading) return <Progress />;
|
if (isLoading) return <Progress />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
49
frontend/src/hooks/useRemoteI18n.ts
Normal file
49
frontend/src/hooks/useRemoteI18n.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2024 Hexastack. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||||
|
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||||
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
import i18n from "@/i18n/config";
|
||||||
|
|
||||||
|
import { useApiClient } from "./useApiClient";
|
||||||
|
import { useAuth } from "./useAuth";
|
||||||
|
|
||||||
|
export const useRemoteI18n = () => {
|
||||||
|
const { isAuthenticated } = useAuth();
|
||||||
|
const { apiClient } = useApiClient();
|
||||||
|
const isRemoteI18nLoaded = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchRemoteI18n = async () => {
|
||||||
|
try {
|
||||||
|
const additionalTranslations = await apiClient.fetchRemoteI18n();
|
||||||
|
// Assuming additionalTranslations is an object like { en: { translation: { key: 'value' } } }
|
||||||
|
|
||||||
|
Object.keys(additionalTranslations).forEach((lang) => {
|
||||||
|
Object.keys(additionalTranslations[lang]).forEach((namespace) => {
|
||||||
|
i18n.addResourceBundle(
|
||||||
|
lang,
|
||||||
|
namespace,
|
||||||
|
additionalTranslations[lang][namespace],
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("Failed to fetch remote i18n translations:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isAuthenticated && !isRemoteI18nLoaded.current) {
|
||||||
|
fetchRemoteI18n();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isAuthenticated]);
|
||||||
|
};
|
@ -26,14 +26,7 @@ i18n
|
|||||||
backend: {
|
backend: {
|
||||||
loadPath: "/locales/{{lng}}/{{ns}}.json",
|
loadPath: "/locales/{{lng}}/{{ns}}.json",
|
||||||
},
|
},
|
||||||
ns: [
|
ns: ["translation", "chatbot_settings", "contact", "nlp_settings"],
|
||||||
"translation",
|
|
||||||
"chatbot_settings.json",
|
|
||||||
"contact",
|
|
||||||
"nlp_settings",
|
|
||||||
"offline",
|
|
||||||
"live-chat-tester",
|
|
||||||
],
|
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
},
|
},
|
||||||
|
@ -32,6 +32,7 @@ export const ROUTES = {
|
|||||||
CSRF: "/csrftoken",
|
CSRF: "/csrftoken",
|
||||||
BOTSTATS: "/botstats",
|
BOTSTATS: "/botstats",
|
||||||
REFRESH_TRANSLATIONS: "/translation/refresh",
|
REFRESH_TRANSLATIONS: "/translation/refresh",
|
||||||
|
FETCH_REMOTE_I18N: "/i18n",
|
||||||
RESET: "/user/reset",
|
RESET: "/user/reset",
|
||||||
NLP_SAMPLE_IMPORT: "/nlpsample/import",
|
NLP_SAMPLE_IMPORT: "/nlpsample/import",
|
||||||
NLP_SAMPLE_PREDICT: "/nlpsample/message",
|
NLP_SAMPLE_PREDICT: "/nlpsample/message",
|
||||||
@ -190,6 +191,12 @@ export class ApiClient {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fetchRemoteI18n() {
|
||||||
|
const { data } = await this.request.get(ROUTES.FETCH_REMOTE_I18N);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
async reset(token: string, payload: IResetPayload) {
|
async reset(token: string, payload: IResetPayload) {
|
||||||
const { data } = await this.request.post<
|
const { data } = await this.request.post<
|
||||||
IResetPayload,
|
IResetPayload,
|
||||||
|
Loading…
Reference in New Issue
Block a user