mirror of
https://github.com/hexastack/hexabot
synced 2025-05-02 12:02:34 +00:00
Merge pull request #433 from Hexastack/fix/channel-data-inference-type-issue
fix: channels data type inference
This commit is contained in:
commit
1f61e43f58
@ -11,6 +11,7 @@ import {
|
|||||||
AttachmentForeignKey,
|
AttachmentForeignKey,
|
||||||
AttachmentPayload,
|
AttachmentPayload,
|
||||||
} from '@/chat/schemas/types/attachment';
|
} from '@/chat/schemas/types/attachment';
|
||||||
|
import { SubscriberChannelData } from '@/chat/schemas/types/channel';
|
||||||
import {
|
import {
|
||||||
IncomingMessageType,
|
IncomingMessageType,
|
||||||
StdEventType,
|
StdEventType,
|
||||||
@ -19,16 +20,22 @@ import {
|
|||||||
import { Payload } from '@/chat/schemas/types/quick-reply';
|
import { Payload } from '@/chat/schemas/types/quick-reply';
|
||||||
import { NLU } from '@/helper/types';
|
import { NLU } from '@/helper/types';
|
||||||
|
|
||||||
import ChannelHandler from './Handler';
|
import ChannelHandler, { ChannelNameOf } from './Handler';
|
||||||
|
|
||||||
export interface ChannelEvent {}
|
export interface ChannelEvent {}
|
||||||
|
|
||||||
// eslint-disable-next-line prettier/prettier
|
// eslint-disable-next-line prettier/prettier
|
||||||
export default abstract class EventWrapper<A, E, C extends ChannelHandler = ChannelHandler> {
|
export default abstract class EventWrapper<
|
||||||
|
A,
|
||||||
|
E,
|
||||||
|
C extends ChannelHandler = ChannelHandler,
|
||||||
|
> {
|
||||||
_adapter: A = {} as A;
|
_adapter: A = {} as A;
|
||||||
|
|
||||||
_handler: C;
|
_handler: C;
|
||||||
|
|
||||||
|
channelAttrs: SubscriberChannelDict[ChannelNameOf<C>];
|
||||||
|
|
||||||
_profile!: Subscriber;
|
_profile!: Subscriber;
|
||||||
|
|
||||||
_nlp!: NLU.ParseEntities;
|
_nlp!: NLU.ParseEntities;
|
||||||
@ -39,14 +46,18 @@ export default abstract class EventWrapper<A, E, C extends ChannelHandler = Chan
|
|||||||
*
|
*
|
||||||
* Any method declared in this class should be extended and overridden in any given channel's
|
* Any method declared in this class should be extended and overridden in any given channel's
|
||||||
* event wrapper if needed.
|
* event wrapper if needed.
|
||||||
* @param handler - The channel's handler
|
* @param handler - The channel's handler
|
||||||
* @param event - The message event received
|
* @param event - The message event received
|
||||||
* @param channelData - Channel's specific data
|
* @param channelAttrs - Channel's specific data
|
||||||
*/
|
*/
|
||||||
constructor(handler: C, event: E, channelData: any = {}) {
|
constructor(
|
||||||
|
handler: C,
|
||||||
|
event: E,
|
||||||
|
channelAttrs: SubscriberChannelDict[ChannelNameOf<C>] = {},
|
||||||
|
) {
|
||||||
this._handler = handler;
|
this._handler = handler;
|
||||||
this._init(event);
|
this._init(event);
|
||||||
this.set('channelData', channelData);
|
this.channelAttrs = channelAttrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
@ -95,8 +106,11 @@ export default abstract class EventWrapper<A, E, C extends ChannelHandler = Chan
|
|||||||
*
|
*
|
||||||
* @returns Returns any channel related data.
|
* @returns Returns any channel related data.
|
||||||
*/
|
*/
|
||||||
getChannelData(): any {
|
getChannelData(): SubscriberChannelData<ChannelNameOf<C>> {
|
||||||
return this.get('channelData', {});
|
return {
|
||||||
|
name: this._handler.getName(),
|
||||||
|
...this.channelAttrs,
|
||||||
|
} as SubscriberChannelData<ChannelNameOf<C>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -282,15 +296,6 @@ export class GenericEventWrapper extends EventWrapper<
|
|||||||
this._adapter.raw = event;
|
this._adapter.raw = event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns channel related data
|
|
||||||
*
|
|
||||||
* @returns An object representing the channel specific data
|
|
||||||
*/
|
|
||||||
getChannelData(): any {
|
|
||||||
return this.get('channelData', {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the message id
|
* Returns the message id
|
||||||
*
|
*
|
||||||
|
@ -28,6 +28,8 @@ import { ChannelName, ChannelSetting } from '../types';
|
|||||||
|
|
||||||
import EventWrapper from './EventWrapper';
|
import EventWrapper from './EventWrapper';
|
||||||
|
|
||||||
|
export type ChannelNameOf<C> = C extends ChannelHandler<infer N> ? N : never;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default abstract class ChannelHandler<
|
export default abstract class ChannelHandler<
|
||||||
N extends ChannelName = ChannelName,
|
N extends ChannelName = ChannelName,
|
||||||
@ -48,10 +50,14 @@ export default abstract class ChannelHandler<
|
|||||||
this.settings = require(path.join(this.getPath(), 'settings')).default;
|
this.settings = require(path.join(this.getPath(), 'settings')).default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name as N;
|
||||||
|
}
|
||||||
|
|
||||||
async onModuleInit() {
|
async onModuleInit() {
|
||||||
await super.onModuleInit();
|
await super.onModuleInit();
|
||||||
this.channelService.setChannel(
|
this.channelService.setChannel(
|
||||||
this.getName() as ChannelName,
|
this.getName(),
|
||||||
this as unknown as ChannelHandler<N>,
|
this as unknown as ChannelHandler<N>,
|
||||||
);
|
);
|
||||||
this.setup();
|
this.setup();
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
MessagePopulate,
|
MessagePopulate,
|
||||||
MessageStub,
|
MessageStub,
|
||||||
} from '../schemas/message.schema';
|
} from '../schemas/message.schema';
|
||||||
|
import { Subscriber } from '../schemas/subscriber.schema';
|
||||||
import {
|
import {
|
||||||
AnyMessage,
|
AnyMessage,
|
||||||
OutgoingMessage,
|
OutgoingMessage,
|
||||||
@ -137,7 +138,9 @@ export class MessageController extends BaseController<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.channelService.findChannel(subscriber?.channel.name)) {
|
const channelData = Subscriber.getChannelData(subscriber);
|
||||||
|
|
||||||
|
if (!this.channelService.findChannel(channelData.name)) {
|
||||||
throw new BadRequestException(`Subscriber channel not found`);
|
throw new BadRequestException(`Subscriber channel not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +149,7 @@ export class MessageController extends BaseController<
|
|||||||
message: messageDto.message as StdOutgoingTextMessage,
|
message: messageDto.message as StdOutgoingTextMessage,
|
||||||
};
|
};
|
||||||
const channelHandler = this.channelService.getChannelHandler(
|
const channelHandler = this.channelService.getChannelHandler(
|
||||||
subscriber.channel.name,
|
channelData.name,
|
||||||
);
|
);
|
||||||
const event = new GenericEventWrapper(channelHandler, {
|
const event = new GenericEventWrapper(channelHandler, {
|
||||||
senderId: subscriber.foreign_id,
|
senderId: subscriber.foreign_id,
|
||||||
|
@ -9,16 +9,17 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
|
IsDate,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsString,
|
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsDate,
|
IsString,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|
||||||
|
import { ChannelName } from '@/channel/types';
|
||||||
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
|
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
|
||||||
|
|
||||||
import { ChannelData } from '../schemas/types/channel';
|
import { SubscriberChannelData } from '../schemas/types/channel';
|
||||||
import { IsChannelData } from '../validation-rules/is-channel-data';
|
import { IsChannelData } from '../validation-rules/is-channel-data';
|
||||||
|
|
||||||
export class SubscriberCreateDto {
|
export class SubscriberCreateDto {
|
||||||
@ -85,7 +86,7 @@ export class SubscriberCreateDto {
|
|||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsDate()
|
@IsDate()
|
||||||
assignedAt: Date | null;
|
assignedAt?: Date | null;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: 'Subscriber last visit',
|
description: 'Subscriber last visit',
|
||||||
@ -93,7 +94,7 @@ export class SubscriberCreateDto {
|
|||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsDate()
|
@IsDate()
|
||||||
lastvisit: Date;
|
lastvisit?: Date;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: 'Subscriber retained from',
|
description: 'Subscriber retained from',
|
||||||
@ -101,7 +102,7 @@ export class SubscriberCreateDto {
|
|||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsDate()
|
@IsDate()
|
||||||
retainedFrom: Date;
|
retainedFrom?: Date;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Subscriber channel',
|
description: 'Subscriber channel',
|
||||||
@ -109,7 +110,7 @@ export class SubscriberCreateDto {
|
|||||||
})
|
})
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsChannelData()
|
@IsChannelData()
|
||||||
channel: ChannelData;
|
channel: SubscriberChannelData<ChannelName>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SubscriberUpdateDto extends PartialType(SubscriberCreateDto) {}
|
export class SubscriberUpdateDto extends PartialType(SubscriberCreateDto) {}
|
||||||
|
14
api/src/chat/index.d.ts
vendored
Normal file
14
api/src/chat/index.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* 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 { ChannelName } from '@/channel/types';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface SubscriberChannelDict
|
||||||
|
extends Record<ChannelName | string, Record<string, any>> {}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import { Transform, Type } from 'class-transformer';
|
|||||||
import { Schema as MongooseSchema } from 'mongoose';
|
import { Schema as MongooseSchema } from 'mongoose';
|
||||||
|
|
||||||
import { Attachment } from '@/attachment/schemas/attachment.schema';
|
import { Attachment } from '@/attachment/schemas/attachment.schema';
|
||||||
|
import { ChannelName } from '@/channel/types';
|
||||||
import { User } from '@/user/schemas/user.schema';
|
import { User } from '@/user/schemas/user.schema';
|
||||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||||
@ -20,7 +21,7 @@ import {
|
|||||||
} from '@/utils/types/filter.types';
|
} from '@/utils/types/filter.types';
|
||||||
|
|
||||||
import { Label } from './label.schema';
|
import { Label } from './label.schema';
|
||||||
import { ChannelData } from './types/channel';
|
import { SubscriberChannelData } from './types/channel';
|
||||||
import { SubscriberContext } from './types/subscriberContext';
|
import { SubscriberContext } from './types/subscriberContext';
|
||||||
|
|
||||||
@Schema({ timestamps: true })
|
@Schema({ timestamps: true })
|
||||||
@ -102,7 +103,7 @@ export class SubscriberStub extends BaseSchema {
|
|||||||
@Prop({
|
@Prop({
|
||||||
type: Object,
|
type: Object,
|
||||||
})
|
})
|
||||||
channel: ChannelData;
|
channel: SubscriberChannelData;
|
||||||
|
|
||||||
@Prop({
|
@Prop({
|
||||||
type: MongooseSchema.Types.ObjectId,
|
type: MongooseSchema.Types.ObjectId,
|
||||||
@ -116,6 +117,13 @@ export class SubscriberStub extends BaseSchema {
|
|||||||
default: { vars: {} },
|
default: { vars: {} },
|
||||||
})
|
})
|
||||||
context?: SubscriberContext;
|
context?: SubscriberContext;
|
||||||
|
|
||||||
|
static getChannelData<
|
||||||
|
C extends ChannelName,
|
||||||
|
S extends SubscriberStub = Subscriber,
|
||||||
|
>(subscriber: S) {
|
||||||
|
return subscriber.channel as SubscriberChannelData<C>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema({ timestamps: true })
|
@Schema({ timestamps: true })
|
||||||
|
@ -8,10 +8,14 @@
|
|||||||
|
|
||||||
import { ChannelName } from '@/channel/types';
|
import { ChannelName } from '@/channel/types';
|
||||||
|
|
||||||
interface BaseChannelData {
|
export type SubscriberChannelData<
|
||||||
name: ChannelName; // channel name
|
C extends ChannelName = null,
|
||||||
isSocket?: boolean;
|
K extends keyof SubscriberChannelDict[C] = keyof SubscriberChannelDict[C],
|
||||||
type?: any; //TODO: type has to be checked
|
> = C extends null
|
||||||
}
|
? { name: ChannelName }
|
||||||
|
: {
|
||||||
export type ChannelData = BaseChannelData;
|
name: C;
|
||||||
|
} & {
|
||||||
|
// Channel's specific attributes
|
||||||
|
[P in keyof SubscriberChannelDict[C]]: SubscriberChannelDict[C][K];
|
||||||
|
};
|
||||||
|
@ -236,7 +236,11 @@ describe('BlockService', () => {
|
|||||||
text: 'Hello',
|
text: 'Hello',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{},
|
{
|
||||||
|
isSocket: true,
|
||||||
|
ipAddress: '1.1.1.1',
|
||||||
|
agent: 'Chromium',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const webEventGetStarted = new WebEventWrapper(
|
const webEventGetStarted = new WebEventWrapper(
|
||||||
handlerMock,
|
handlerMock,
|
||||||
@ -247,7 +251,11 @@ describe('BlockService', () => {
|
|||||||
payload: 'GET_STARTED',
|
payload: 'GET_STARTED',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{},
|
{
|
||||||
|
isSocket: true,
|
||||||
|
ipAddress: '1.1.1.1',
|
||||||
|
agent: 'Chromium',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
it('should return undefined when no blocks are provided', async () => {
|
it('should return undefined when no blocks are provided', async () => {
|
||||||
|
@ -186,6 +186,7 @@ describe('BlockService', () => {
|
|||||||
const event = new WebEventWrapper(handler, webEventText, {
|
const event = new WebEventWrapper(handler, webEventText, {
|
||||||
isSocket: false,
|
isSocket: false,
|
||||||
ipAddress: '1.1.1.1',
|
ipAddress: '1.1.1.1',
|
||||||
|
agent: 'Chromium',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [block] = await blockService.findAndPopulate({ patterns: ['Hi'] });
|
const [block] = await blockService.findAndPopulate({ patterns: ['Hi'] });
|
||||||
@ -254,6 +255,7 @@ describe('BlockService', () => {
|
|||||||
const event = new WebEventWrapper(handler, webEventText, {
|
const event = new WebEventWrapper(handler, webEventText, {
|
||||||
isSocket: false,
|
isSocket: false,
|
||||||
ipAddress: '1.1.1.1',
|
ipAddress: '1.1.1.1',
|
||||||
|
agent: 'Chromium',
|
||||||
});
|
});
|
||||||
const webSubscriber = await subscriberService.findOne({
|
const webSubscriber = await subscriberService.findOne({
|
||||||
foreign_id: 'foreign-id-web-1',
|
foreign_id: 'foreign-id-web-1',
|
||||||
@ -307,6 +309,7 @@ describe('BlockService', () => {
|
|||||||
const event = new WebEventWrapper(handler, webEventText, {
|
const event = new WebEventWrapper(handler, webEventText, {
|
||||||
isSocket: false,
|
isSocket: false,
|
||||||
ipAddress: '1.1.1.1',
|
ipAddress: '1.1.1.1',
|
||||||
|
agent: 'Chromium',
|
||||||
});
|
});
|
||||||
const webSubscriber = await subscriberService.findOne({
|
const webSubscriber = await subscriberService.findOne({
|
||||||
foreign_id: 'foreign-id-web-2',
|
foreign_id: 'foreign-id-web-2',
|
||||||
|
@ -244,10 +244,7 @@ export class ChatService {
|
|||||||
if (!subscriber) {
|
if (!subscriber) {
|
||||||
const subscriberData = await handler.getUserData(event);
|
const subscriberData = await handler.getUserData(event);
|
||||||
this.eventEmitter.emit('hook:stats:entry', 'new_users', 'New users');
|
this.eventEmitter.emit('hook:stats:entry', 'new_users', 'New users');
|
||||||
subscriberData.channel = {
|
subscriberData.channel = event.getChannelData();
|
||||||
...event.getChannelData(),
|
|
||||||
name: handler.getName(),
|
|
||||||
};
|
|
||||||
subscriber = await this.subscriberService.create(subscriberData);
|
subscriber = await this.subscriberService.create(subscriberData);
|
||||||
} else {
|
} else {
|
||||||
// Already existing user profile
|
// Already existing user profile
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
|
||||||
import EventWrapper from '@/channel/lib/EventWrapper';
|
import EventWrapper from '@/channel/lib/EventWrapper';
|
||||||
import { ChannelName } from '@/channel/types';
|
|
||||||
import { LoggerService } from '@/logger/logger.service';
|
import { LoggerService } from '@/logger/logger.service';
|
||||||
import { BaseService } from '@/utils/generics/base-service';
|
import { BaseService } from '@/utils/generics/base-service';
|
||||||
|
|
||||||
@ -70,7 +69,7 @@ export class ConversationService extends BaseService<
|
|||||||
const msgType = event.getMessageType();
|
const msgType = event.getMessageType();
|
||||||
const profile = event.getSender();
|
const profile = event.getSender();
|
||||||
// Capture channel specific context data
|
// Capture channel specific context data
|
||||||
convo.context.channel = event.getHandler().getName() as ChannelName;
|
convo.context.channel = event.getHandler().getName();
|
||||||
convo.context.text = event.getText();
|
convo.context.text = event.getText();
|
||||||
convo.context.payload = event.getPayload();
|
convo.context.payload = event.getPayload();
|
||||||
convo.context.nlp = event.getNLP();
|
convo.context.nlp = event.getNLP();
|
||||||
|
@ -12,6 +12,13 @@ import CONSOLE_CHANNEL_SETTINGS, {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Settings extends SettingTree<typeof CONSOLE_CHANNEL_SETTINGS> {}
|
interface Settings extends SettingTree<typeof CONSOLE_CHANNEL_SETTINGS> {}
|
||||||
|
interface SubscriberChannelDict {
|
||||||
|
[CONSOLE_CHANNEL_NAME]: {
|
||||||
|
isSocket: boolean;
|
||||||
|
ipAddress: string;
|
||||||
|
agent: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@nestjs/event-emitter' {
|
declare module '@nestjs/event-emitter' {
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
Subproject commit cf7004ef6adac1b5e033d06987f493a9b00e01d2
|
@ -37,6 +37,7 @@ import { SocketEventDispatcherService } from '@/websocket/services/socket-event-
|
|||||||
import { WebsocketGateway } from '@/websocket/websocket.gateway';
|
import { WebsocketGateway } from '@/websocket/websocket.gateway';
|
||||||
|
|
||||||
import WebChannelHandler from '../index.channel';
|
import WebChannelHandler from '../index.channel';
|
||||||
|
import { WEB_CHANNEL_NAME } from '../settings';
|
||||||
import WebEventWrapper from '../wrapper';
|
import WebEventWrapper from '../wrapper';
|
||||||
|
|
||||||
import { webEvents } from './events.mock';
|
import { webEvents } from './events.mock';
|
||||||
@ -119,7 +120,10 @@ describe(`Web event wrapper`, () => {
|
|||||||
e,
|
e,
|
||||||
expected.channelData,
|
expected.channelData,
|
||||||
);
|
);
|
||||||
expect(event.getChannelData()).toEqual(expected.channelData);
|
expect(event.getChannelData()).toEqual({
|
||||||
|
...expected.channelData,
|
||||||
|
name: WEB_CHANNEL_NAME,
|
||||||
|
});
|
||||||
expect(event.getId()).toEqual(expected.id);
|
expect(event.getId()).toEqual(expected.id);
|
||||||
expect(event.getEventType()).toEqual(expected.eventType);
|
expect(event.getEventType()).toEqual(expected.eventType);
|
||||||
expect(event.getMessageType()).toEqual(expected.messageType);
|
expect(event.getMessageType()).toEqual(expected.messageType);
|
||||||
|
@ -59,7 +59,7 @@ import { SocketRequest } from '@/websocket/utils/socket-request';
|
|||||||
import { SocketResponse } from '@/websocket/utils/socket-response';
|
import { SocketResponse } from '@/websocket/utils/socket-response';
|
||||||
import { WebsocketGateway } from '@/websocket/websocket.gateway';
|
import { WebsocketGateway } from '@/websocket/websocket.gateway';
|
||||||
|
|
||||||
import { WEB_CHANNEL_NAMESPACE } from './settings';
|
import { WEB_CHANNEL_NAME, WEB_CHANNEL_NAMESPACE } from './settings';
|
||||||
import { Web } from './types';
|
import { Web } from './types';
|
||||||
import WebEventWrapper from './wrapper';
|
import WebEventWrapper from './wrapper';
|
||||||
|
|
||||||
@ -433,7 +433,6 @@ export default abstract class BaseWebChannelHandler<
|
|||||||
return subscriber;
|
return subscriber;
|
||||||
}
|
}
|
||||||
|
|
||||||
const channelData = this.getChannelData(req);
|
|
||||||
const newProfile: SubscriberCreateDto = {
|
const newProfile: SubscriberCreateDto = {
|
||||||
foreign_id: this.generateId(),
|
foreign_id: this.generateId(),
|
||||||
first_name: data.first_name ? data.first_name.toString() : 'Anon.',
|
first_name: data.first_name ? data.first_name.toString() : 'Anon.',
|
||||||
@ -443,8 +442,8 @@ export default abstract class BaseWebChannelHandler<
|
|||||||
lastvisit: new Date(),
|
lastvisit: new Date(),
|
||||||
retainedFrom: new Date(),
|
retainedFrom: new Date(),
|
||||||
channel: {
|
channel: {
|
||||||
...channelData,
|
name: this.getName(),
|
||||||
name: this.getName() as ChannelName,
|
...this.getChannelAttributes(req),
|
||||||
},
|
},
|
||||||
language: '',
|
language: '',
|
||||||
locale: '',
|
locale: '',
|
||||||
@ -736,13 +735,15 @@ export default abstract class BaseWebChannelHandler<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle channel event (probably a message)
|
* Return subscriber channel specific attributes
|
||||||
*
|
*
|
||||||
* @param req
|
* @param req
|
||||||
*
|
*
|
||||||
* @returns The channel's data
|
* @returns The subscriber channel's attributes
|
||||||
*/
|
*/
|
||||||
protected getChannelData(req: Request | SocketRequest): Web.ChannelData {
|
getChannelAttributes(
|
||||||
|
req: Request | SocketRequest,
|
||||||
|
): SubscriberChannelDict[typeof WEB_CHANNEL_NAME] {
|
||||||
return {
|
return {
|
||||||
isSocket: 'isSocket' in req && !!req.isSocket,
|
isSocket: 'isSocket' in req && !!req.isSocket,
|
||||||
ipAddress: this.getIpAddress(req),
|
ipAddress: this.getIpAddress(req),
|
||||||
@ -780,11 +781,11 @@ export default abstract class BaseWebChannelHandler<
|
|||||||
if (upload) {
|
if (upload) {
|
||||||
data.data = upload;
|
data.data = upload;
|
||||||
}
|
}
|
||||||
const channelData = this.getChannelData(req);
|
const channelAttrs = this.getChannelAttributes(req);
|
||||||
const event: WebEventWrapper = new WebEventWrapper(
|
const event: WebEventWrapper = new WebEventWrapper(
|
||||||
this,
|
this,
|
||||||
data,
|
data,
|
||||||
channelData,
|
channelAttrs,
|
||||||
);
|
);
|
||||||
if (event.getEventType() === 'message') {
|
if (event.getEventType() === 'message') {
|
||||||
// Handler sync message sent by chabbot
|
// Handler sync message sent by chabbot
|
||||||
@ -1185,7 +1186,9 @@ export default abstract class BaseWebChannelHandler<
|
|||||||
type: StdEventType,
|
type: StdEventType,
|
||||||
content: any,
|
content: any,
|
||||||
): void {
|
): void {
|
||||||
if (subscriber.channel.isSocket) {
|
const channelData =
|
||||||
|
Subscriber.getChannelData<typeof WEB_CHANNEL_NAME>(subscriber);
|
||||||
|
if (channelData.isSocket) {
|
||||||
this.websocketGateway.broadcast(subscriber, type, content);
|
this.websocketGateway.broadcast(subscriber, type, content);
|
||||||
} else {
|
} else {
|
||||||
// Do nothing, messages will be retrieved via polling
|
// Do nothing, messages will be retrieved via polling
|
||||||
@ -1278,6 +1281,17 @@ export default abstract class BaseWebChannelHandler<
|
|||||||
* @returns The web's response, otherwise an error
|
* @returns The web's response, otherwise an error
|
||||||
*/
|
*/
|
||||||
async getUserData(event: WebEventWrapper): Promise<SubscriberCreateDto> {
|
async getUserData(event: WebEventWrapper): Promise<SubscriberCreateDto> {
|
||||||
return event.getSender() as SubscriberCreateDto;
|
const sender = event.getSender();
|
||||||
|
const {
|
||||||
|
id: _id,
|
||||||
|
createdAt: _createdAt,
|
||||||
|
updatedAt: _updatedAt,
|
||||||
|
...rest
|
||||||
|
} = sender;
|
||||||
|
const subscriber: SubscriberCreateDto = {
|
||||||
|
...rest,
|
||||||
|
channel: Subscriber.getChannelData(sender),
|
||||||
|
};
|
||||||
|
return subscriber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
api/src/extensions/channels/web/index.d.ts
vendored
9
api/src/extensions/channels/web/index.d.ts
vendored
@ -7,11 +7,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import DEFAULT_WEB_CHANNEL_SETTINGS, {
|
import DEFAULT_WEB_CHANNEL_SETTINGS, {
|
||||||
|
WEB_CHANNEL_NAME,
|
||||||
WEB_CHANNEL_NAMESPACE,
|
WEB_CHANNEL_NAMESPACE,
|
||||||
} from './settings';
|
} from './settings';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Settings extends SettingTree<typeof DEFAULT_WEB_CHANNEL_SETTINGS> {}
|
interface Settings extends SettingTree<typeof DEFAULT_WEB_CHANNEL_SETTINGS> {}
|
||||||
|
|
||||||
|
interface SubscriberChannelDict {
|
||||||
|
[WEB_CHANNEL_NAME]: {
|
||||||
|
isSocket: boolean;
|
||||||
|
ipAddress: string;
|
||||||
|
agent: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@nestjs/event-emitter' {
|
declare module '@nestjs/event-emitter' {
|
||||||
|
@ -30,12 +30,6 @@ export namespace Web {
|
|||||||
|
|
||||||
export type Settings = Record<SettingLabel, any>;
|
export type Settings = Record<SettingLabel, any>;
|
||||||
|
|
||||||
export type ChannelData = {
|
|
||||||
isSocket: boolean;
|
|
||||||
ipAddress: string;
|
|
||||||
agent: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RequestSession = {
|
export type RequestSession = {
|
||||||
web?: {
|
web?: {
|
||||||
profile: SubscriberFull;
|
profile: SubscriberFull;
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
import { Payload } from '@/chat/schemas/types/quick-reply';
|
import { Payload } from '@/chat/schemas/types/quick-reply';
|
||||||
|
|
||||||
import BaseWebChannelHandler from './base-web-channel';
|
import BaseWebChannelHandler from './base-web-channel';
|
||||||
|
import { WEB_CHANNEL_NAME } from './settings';
|
||||||
import { Web } from './types';
|
import { Web } from './types';
|
||||||
|
|
||||||
type WebEventAdapter =
|
type WebEventAdapter =
|
||||||
@ -76,10 +77,14 @@ export default class WebEventWrapper<
|
|||||||
*
|
*
|
||||||
* @param handler - The channel's handler
|
* @param handler - The channel's handler
|
||||||
* @param event - The message event received
|
* @param event - The message event received
|
||||||
* @param channelData - Channel's specific extra data {isSocket, ipAddress}
|
* @param channelAttrs - Channel's specific extra attributes {isSocket, ipAddress}
|
||||||
*/
|
*/
|
||||||
constructor(handler: T, event: Web.Event, channelData: any) {
|
constructor(
|
||||||
super(handler, event, channelData);
|
handler: T,
|
||||||
|
event: Web.Event,
|
||||||
|
channelAttrs: SubscriberChannelDict[typeof WEB_CHANNEL_NAME],
|
||||||
|
) {
|
||||||
|
super(handler, event, channelAttrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,20 +134,6 @@ export default class WebEventWrapper<
|
|||||||
this._adapter.raw = event;
|
this._adapter.raw = event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns channel related data
|
|
||||||
*
|
|
||||||
* @returns Channel's data
|
|
||||||
*/
|
|
||||||
getChannelData(): any {
|
|
||||||
return this.get('channelData', {
|
|
||||||
isSocket: true,
|
|
||||||
ipAddress: '0.0.0.0',
|
|
||||||
agent:
|
|
||||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the message id
|
* Returns the message id
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user