mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
feat: add plugins settings i18n + typing
This commit is contained in:
@@ -24,7 +24,7 @@ export interface ChannelModuleOptions {
|
||||
folder: string;
|
||||
}
|
||||
|
||||
@InjectDynamicProviders('dist/**/*.channel.js')
|
||||
@InjectDynamicProviders('dist/extensions/**/*.channel.js')
|
||||
@Module({
|
||||
controllers: [WebhookController, ChannelController],
|
||||
providers: [ChannelService],
|
||||
|
||||
@@ -19,6 +19,7 @@ import { LoggerService } from '@/logger/logger.service';
|
||||
import BaseNlpHelper from '@/nlp/lib/BaseNlpHelper';
|
||||
import { NlpService } from '@/nlp/services/nlp.service';
|
||||
import { SettingService } from '@/setting/services/setting.service';
|
||||
import { hyphenToUnderscore } from '@/utils/helpers/misc';
|
||||
import { SocketRequest } from '@/websocket/utils/socket-request';
|
||||
import { SocketResponse } from '@/websocket/utils/socket-response';
|
||||
|
||||
@@ -56,7 +57,7 @@ export default abstract class ChannelHandler<N extends string = string> {
|
||||
}
|
||||
|
||||
protected getGroup() {
|
||||
return this.getChannel().replaceAll('-', '_') as ChannelSetting<N>['group'];
|
||||
return hyphenToUnderscore(this.getChannel()) as ChannelSetting<N>['group'];
|
||||
}
|
||||
|
||||
async setup() {
|
||||
|
||||
@@ -59,7 +59,7 @@ export class BlockController extends BaseController<
|
||||
private readonly categoryService: CategoryService,
|
||||
private readonly labelService: LabelService,
|
||||
private readonly userService: UserService,
|
||||
private pluginsService: PluginService<BaseBlockPlugin>,
|
||||
private pluginsService: PluginService<BaseBlockPlugin<any>>,
|
||||
) {
|
||||
super(blockService);
|
||||
}
|
||||
@@ -122,8 +122,7 @@ export class BlockController extends BaseController<
|
||||
const plugins = this.pluginsService
|
||||
.getAllByType(PluginType.block)
|
||||
.map((p) => ({
|
||||
title: p.title,
|
||||
name: p.id,
|
||||
id: p.id,
|
||||
template: {
|
||||
...p.template,
|
||||
message: {
|
||||
|
||||
@@ -116,4 +116,23 @@ export class MessageService extends BaseService<
|
||||
limit,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the latest messages for a given subscriber
|
||||
*
|
||||
* @param subscriber - The subscriber whose message history is being retrieved.
|
||||
* @param limit - The maximum number of messages to return (defaults to 5).
|
||||
*
|
||||
* @returns The message history since the specified date.
|
||||
*/
|
||||
async findLastMessages(subscriber: Subscriber, limit: number = 5) {
|
||||
const lastMessages = await this.findPage(
|
||||
{
|
||||
$or: [{ sender: subscriber.id }, { recipient: subscriber.id }],
|
||||
},
|
||||
{ sort: ['createdAt', 'desc'], skip: 0, limit },
|
||||
);
|
||||
|
||||
return lastMessages.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
0
api/src/extensions/plugins/.gitkeep
Normal file
0
api/src/extensions/plugins/.gitkeep
Normal file
@@ -22,6 +22,7 @@ import { IfAnyOrNever } from 'nestjs-i18n/dist/types';
|
||||
|
||||
import { config } from '@/config';
|
||||
import { Translation } from '@/i18n/schemas/translation.schema';
|
||||
import { hyphenToUnderscore } from '@/utils/helpers/misc';
|
||||
|
||||
@Injectable()
|
||||
export class I18nService<K = Record<string, unknown>>
|
||||
@@ -84,41 +85,40 @@ export class I18nService<K = Record<string, unknown>>
|
||||
}
|
||||
|
||||
async loadExtensionI18nTranslations() {
|
||||
const extensionsDir = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'extensions',
|
||||
'channels',
|
||||
);
|
||||
const baseDir = path.join(__dirname, '..', '..', 'extensions');
|
||||
const extensionTypes = ['channels', 'plugins'];
|
||||
|
||||
try {
|
||||
const extensionFolders = await fs.readdir(extensionsDir, {
|
||||
withFileTypes: true,
|
||||
});
|
||||
for (const type of extensionTypes) {
|
||||
const extensionsDir = path.join(baseDir, type);
|
||||
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);
|
||||
for (const folder of extensionFolders) {
|
||||
if (folder.isDirectory()) {
|
||||
const i18nPath = path.join(extensionsDir, folder.name, 'i18n');
|
||||
const namespace = hyphenToUnderscore(folder.name);
|
||||
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];
|
||||
// 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] = {
|
||||
[namespace]: translations[lang],
|
||||
};
|
||||
} else {
|
||||
this.extensionTranslations[lang][namespace] =
|
||||
translations[lang];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// If the i18n folder does not exist or error in reading, skip this folder
|
||||
}
|
||||
} catch (error) {
|
||||
// If the i18n folder does not exist or error in reading, skip this folder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import { NlpSampleService } from './services/nlp-sample.service';
|
||||
import { NlpValueService } from './services/nlp-value.service';
|
||||
import { NlpService } from './services/nlp.service';
|
||||
|
||||
@InjectDynamicProviders('dist/**/*.nlp.helper.js')
|
||||
@InjectDynamicProviders('dist/extensions/**/*.nlp.helper.js')
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forFeature([
|
||||
|
||||
@@ -22,17 +22,22 @@ import {
|
||||
} from './types';
|
||||
|
||||
@Injectable()
|
||||
export abstract class BaseBlockPlugin extends BasePlugin {
|
||||
export abstract class BaseBlockPlugin<
|
||||
T extends PluginSetting[],
|
||||
> extends BasePlugin {
|
||||
public readonly type: PluginType = PluginType.block;
|
||||
|
||||
constructor(id: string, pluginService: PluginService<BasePlugin>) {
|
||||
public readonly settings: T;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
settings: T,
|
||||
pluginService: PluginService<BasePlugin>,
|
||||
) {
|
||||
super(id, pluginService);
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
title: string;
|
||||
|
||||
settings: PluginSetting[];
|
||||
|
||||
template: PluginBlockTemplate;
|
||||
|
||||
effects?: PluginEffects;
|
||||
@@ -42,4 +47,11 @@ export abstract class BaseBlockPlugin extends BasePlugin {
|
||||
context: Context,
|
||||
convId?: string,
|
||||
): Promise<StdOutgoingEnvelope>;
|
||||
|
||||
protected getArguments(block: Block) {
|
||||
if ('args' in block.message) {
|
||||
return block.message.args as SettingObject<T>;
|
||||
}
|
||||
throw new Error(`Block "${block.name}" does not have any arguments.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import { ContentModel } from '@/cms/schemas/content.schema';
|
||||
|
||||
import { PluginService } from './plugins.service';
|
||||
|
||||
@InjectDynamicProviders('dist/**/*.plugin.js')
|
||||
@InjectDynamicProviders('dist/extensions/**/*.plugin.js')
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
import { DummyPlugin } from '@/extensions/plugins/dummy.plugin';
|
||||
import { LoggerModule } from '@/logger/logger.module';
|
||||
import { DummyPlugin } from '@/utils/test/dummy/dummy.plugin';
|
||||
|
||||
import { BaseBlockPlugin } from './base-block-plugin';
|
||||
import { PluginService } from './plugins.service';
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface CustomBlocks {}
|
||||
type ChannelEvent = any;
|
||||
type BlockAttrs = Partial<BlockCreateDto> & { name: string };
|
||||
|
||||
export type PluginSetting = SettingCreateDto;
|
||||
export type PluginSetting = Omit<SettingCreateDto, 'weight'>;
|
||||
|
||||
export type PluginBlockTemplate = Omit<
|
||||
BlockAttrs,
|
||||
|
||||
@@ -9,3 +9,7 @@
|
||||
export const isEmpty = (value: string): boolean => {
|
||||
return value === undefined || value === null || value === '';
|
||||
};
|
||||
|
||||
export const hyphenToUnderscore = (str: string) => {
|
||||
return str.replaceAll('-', '_');
|
||||
};
|
||||
|
||||
@@ -15,18 +15,15 @@ import {
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { BaseBlockPlugin } from '@/plugins/base-block-plugin';
|
||||
import { PluginService } from '@/plugins/plugins.service';
|
||||
import { PluginSetting } from '@/plugins/types';
|
||||
|
||||
@Injectable()
|
||||
export class DummyPlugin extends BaseBlockPlugin {
|
||||
export class DummyPlugin extends BaseBlockPlugin<PluginSetting[]> {
|
||||
constructor(
|
||||
pluginService: PluginService,
|
||||
private logger: LoggerService,
|
||||
) {
|
||||
super('dummy', pluginService);
|
||||
|
||||
this.title = 'Dummy';
|
||||
|
||||
this.settings = [];
|
||||
super('dummy', [], pluginService);
|
||||
|
||||
this.template = { name: 'Dummy Plugin' };
|
||||
|
||||
Reference in New Issue
Block a user