Skip to content

Commit

Permalink
add interceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
fjodor-rybakov committed Sep 27, 2020
1 parent dfaf2c5 commit ac6c134
Show file tree
Hide file tree
Showing 16 changed files with 140 additions and 23 deletions.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Configuration
commandPrefix: '!',
allowGuilds: ['Some guild id'], // Optional
denyGuilds: ['Some guild id'] // Optional
// and other discord options
})],
providers: [BotGateway]
})
Expand All @@ -59,6 +60,7 @@ Or async
commandPrefix: '!',
allowGuilds: ['Some guild id'], // Optional
denyGuilds: ['Some guild id'] // Optional
// and other discord options
})
})],
providers: [BotGateway]
Expand Down Expand Up @@ -190,6 +192,36 @@ export interface OnDecoratorOptions {
}
```

### Decorator @UseInterceptor (Test feature)

You must implement `DiscordInterceptor` interface
```typescript
/*bot.interceptor.ts*/

import { DiscordInterceptor } from 'discord-nestjs';
import { ClientEvents } from 'discord.js';

export class BotInterceptor implements DiscordInterceptor {
intercept(event: keyof ClientEvents, context: any): any {
return 'Some custom value';
}
}
```

```typescript
/*bot.gateway.ts*/
import { On, UseInterceptors } from 'discord-nestjs';

@Injectable()
export class BotGateway {
@UseInterceptors(BotInterceptor)
@On({event: 'message'})
async onSomeEvent(context: string): Promise<void> {
// to do something
}
}
```

### Decorator @Middleware (Test feature)

You must implement `DiscordMiddleware` interface
Expand Down
1 change: 1 addition & 0 deletions lib/constant/discord.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export const ON_DECORATOR = "ON_DECORATOR";
export const ONCE_DECORATOR = "ONCE_DECORATOR";

export const MIDDLEWARE_DECORATOR = "MIDDLEWARE_DECORATOR";
export const USE_INTERCEPTORS_DECORATOR = "USE_INTERCEPTORS_DECORATOR";
3 changes: 2 additions & 1 deletion lib/decorator/interface/middleware-options.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ClientEvents } from 'discord.js';
import { InjectableOptions } from '@nestjs/common/decorators/core/injectable.decorator';

/**
* Middleware options
*/
export interface MiddlewareOptions {
export interface MiddlewareOptions extends InjectableOptions {
/**
* Take events
*/
Expand Down
2 changes: 1 addition & 1 deletion lib/decorator/middleware.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const Middleware = (options: MiddlewareOptions = {}): ClassDecorator => {
return <TFunction extends Function>(
target: TFunction
): TFunction | void => {
applyDecorators(Injectable);
applyDecorators(Injectable(options));
Reflect.defineMetadata(MIDDLEWARE_DECORATOR, options, target.prototype);
return target;
};
Expand Down
16 changes: 16 additions & 0 deletions lib/decorator/use-interceptor.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { USE_INTERCEPTORS_DECORATOR } from '../constant/discord.constant';
import { DiscordInterceptor } from '..';

/**
* UseInterceptor decorator
*/
export const UseInterceptors = (...interceptors: (DiscordInterceptor | Function)[]): MethodDecorator => {
return (
target: Record<string, any>,
propertyKey: string | symbol,
descriptor: PropertyDescriptor,
): PropertyDescriptor => {
Reflect.defineMetadata(USE_INTERCEPTORS_DECORATOR, interceptors, target, propertyKey);
return descriptor;
};
};
7 changes: 5 additions & 2 deletions lib/discord.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { DynamicModule, Module, Provider } from '@nestjs/common';
import { DiscoveryModule } from '@nestjs/core';
import { DiscordModuleAsyncOptions } from './interface/discord-module-async-options';
import { DiscordOptionsFactory } from './interface/discord-options-factory';
import { DiscordService } from './discord.service';
import { DiscordService } from './service/discord.service';
import { DiscordModuleOption } from './interface/discord-module-option';
import { DISCORD_MODULE_OPTIONS } from './constant/discord.constant';
import { DiscordClient } from './discord-client';
import { DiscordMiddlewareService } from './discord-middleware.service';
import { DiscordMiddlewareService } from './service/discord-middleware.service';
import { OnResolver } from './resolver/on.resolver';
import { OnCommandResolver } from './resolver/on-command.resolver';
import { OnceResolver } from './resolver/once.resolver';
import { DiscordInterceptorService } from './service/discord-interceptor.service';

@Module({
imports: [DiscoveryModule]
Expand All @@ -20,6 +21,7 @@ export class DiscordModule {
module: DiscordModule,
providers: [
DiscordMiddlewareService,
DiscordInterceptorService,
OnResolver,
OnCommandResolver,
OnceResolver,
Expand All @@ -45,6 +47,7 @@ export class DiscordModule {
imports: options.imports || [],
providers: [
DiscordMiddlewareService,
DiscordInterceptorService,
OnResolver,
OnCommandResolver,
OnceResolver,
Expand Down
2 changes: 2 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
export * from './discord.module';
export * from './interface/discord-module-option';
export * from './interface/discord-middleware';
export * from './interface/discord-interceptor';
export * from './decorator/on.decorator';
export * from './decorator/once.decorator';
export * from './decorator/on-command.decorator';
export * from './decorator/middleware.decorator';
export * from './decorator/use-interceptor.decorator';
export * from './decorator/interface/on-command-decorator-options';
export * from './decorator/interface/on-decorator-options';
export * from './decorator/interface/middleware-options';
Expand Down
11 changes: 11 additions & 0 deletions lib/interface/discord-interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ClientEvents } from "discord.js";

/**
* Base interceptor interface
*/
export interface DiscordInterceptor<T = any> {
intercept(
event: keyof ClientEvents,
context: T
): any | Promise<any>;
}
15 changes: 12 additions & 3 deletions lib/resolver/on-command.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { DiscordResolve } from '../interface/discord-resolve';
import { Message } from 'discord.js';
import { ON_MESSAGE_DECORATOR } from '../constant/discord.constant';
import { ON_MESSAGE_DECORATOR, USE_INTERCEPTORS_DECORATOR } from '../constant/discord.constant';
import { DiscordClient, OnCommandDecoratorOptions } from '..';
import { DiscordResolveOptions } from '../interface/discord-resolve-options';
import { DiscordMiddlewareService } from '../discord-middleware.service';
import { DiscordMiddlewareService } from '../service/discord-middleware.service';
import { Injectable } from '@nestjs/common';
import { DiscordInterceptor } from '..';
import { DiscordInterceptorService } from '../service/discord-interceptor.service';

@Injectable()
export class OnCommandResolver implements DiscordResolve {
constructor(private readonly discordMiddlewareService: DiscordMiddlewareService) {
constructor(
private readonly discordMiddlewareService: DiscordMiddlewareService,
private readonly discordInterceptorService: DiscordInterceptorService
) {
}

resolve(options: DiscordResolveOptions): void {
const { discordClient, instance, methodName, middlewareList } = options;
const metadata: OnCommandDecoratorOptions = Reflect.getMetadata(ON_MESSAGE_DECORATOR, instance, methodName);
const interceptors: (DiscordInterceptor | Function)[] = Reflect.getMetadata(USE_INTERCEPTORS_DECORATOR, instance, methodName);
if (metadata) {
const {
name,
Expand Down Expand Up @@ -54,6 +60,9 @@ export class OnCommandResolver implements DiscordResolve {
}
message.content = message.content.trim();
await this.discordMiddlewareService.applyMiddleware(middlewareList, 'message', [message]);
if (interceptors && interceptors.length !== 0) {
message = await this.discordInterceptorService.applyInterceptors(interceptors, 'message', message);
}
instance[methodName](message);
}
});
Expand Down
15 changes: 12 additions & 3 deletions lib/resolver/on.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { DiscordResolve } from '../interface/discord-resolve';
import { ClientEvents } from 'discord.js';
import { ON_DECORATOR } from '../constant/discord.constant';
import { ON_DECORATOR, USE_INTERCEPTORS_DECORATOR } from '../constant/discord.constant';
import { DiscordClient, OnDecoratorOptions } from '..';
import { DiscordResolveOptions } from '../interface/discord-resolve-options';
import { DiscordMiddlewareService } from '../discord-middleware.service';
import { DiscordMiddlewareService } from '../service/discord-middleware.service';
import { Injectable } from '@nestjs/common';
import { DiscordInterceptor } from '..';
import { DiscordInterceptorService } from '../service/discord-interceptor.service';

@Injectable()
export class OnResolver implements DiscordResolve {
constructor(private readonly discordMiddlewareService: DiscordMiddlewareService) {
constructor(
private readonly discordMiddlewareService: DiscordMiddlewareService,
private readonly discordInterceptorService: DiscordInterceptorService
) {
}

resolve(options: DiscordResolveOptions): void {
const { discordClient, instance, methodName, middlewareList } = options;
const metadata: OnDecoratorOptions = Reflect.getMetadata(ON_DECORATOR, instance, methodName);
const interceptors: (DiscordInterceptor | Function)[] = Reflect.getMetadata(USE_INTERCEPTORS_DECORATOR, instance, methodName);
if (metadata) {
discordClient.on(metadata.event, async (...data: ClientEvents[keyof ClientEvents]) => {
if (!this.isAllowGuild(discordClient, data)) {
Expand All @@ -23,6 +29,9 @@ export class OnResolver implements DiscordResolve {
return;
}
await this.discordMiddlewareService.applyMiddleware(middlewareList, metadata.event, data);
if (interceptors && interceptors.length !== 0) {
data = await this.discordInterceptorService.applyInterceptors(interceptors, metadata.event, data);
}
instance[methodName](...data);
});
}
Expand Down
16 changes: 12 additions & 4 deletions lib/resolver/once.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { DiscordResolve } from '../interface/discord-resolve';
import { ClientEvents } from 'discord.js';
import { DiscordClient, OnDecoratorOptions } from '..';
import { ONCE_DECORATOR } from '../constant/discord.constant';
import { ONCE_DECORATOR, USE_INTERCEPTORS_DECORATOR } from '../constant/discord.constant';
import { DiscordResolveOptions } from '../interface/discord-resolve-options';
import { DiscordMiddlewareService } from '../discord-middleware.service';
import { DiscordMiddlewareService } from '../service/discord-middleware.service';
import { Injectable } from '@nestjs/common';
import { DiscordInterceptor } from '..';
import { DiscordInterceptorService } from '../service/discord-interceptor.service';

@Injectable()
export class OnceResolver implements DiscordResolve {
constructor(private readonly discordMiddlewareService: DiscordMiddlewareService) {
constructor(
private readonly discordMiddlewareService: DiscordMiddlewareService,
private readonly discordInterceptorService: DiscordInterceptorService
) {
}

resolve(options: DiscordResolveOptions): void {
const {discordClient, instance, methodName, middlewareList} = options;
const metadata: OnDecoratorOptions = Reflect.getMetadata(ONCE_DECORATOR, instance, methodName);
const interceptors: (DiscordInterceptor | Function)[] = Reflect.getMetadata(USE_INTERCEPTORS_DECORATOR, instance, methodName);
if (metadata) {
discordClient.once(metadata.event, async (...data: ClientEvents[keyof ClientEvents]) => {
if (!this.isAllowGuild(discordClient, data)) {
Expand All @@ -23,12 +29,14 @@ export class OnceResolver implements DiscordResolve {
return;
}
await this.discordMiddlewareService.applyMiddleware(middlewareList, metadata.event, data);
if (interceptors && interceptors.length !== 0) {
data = await this.discordInterceptorService.applyInterceptors(interceptors, metadata.event, data);
}
instance[methodName](...data);
});
}
}


private isAllowGuild(discordClient: DiscordClient, data: any[] = []): boolean {
const guild = data.find((item) => !!item && !!item.guild);
const guildId = !!guild && guild.guild.id;
Expand Down
22 changes: 22 additions & 0 deletions lib/service/discord-interceptor.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { DiscordInterceptor } from '..';
import { ClientEvents, Message } from 'discord.js';
import { Injectable } from '@nestjs/common';

@Injectable()
export class DiscordInterceptorService {
async applyInterceptors(
interceptors: (DiscordInterceptor | Function)[],
event: keyof ClientEvents,
context: any
): Promise<any> {
return interceptors.reduce(async (prev: Promise<Message>, curr: DiscordInterceptor | Function) => {
let interceptorInstance: DiscordInterceptor;
if (typeof curr === 'function') {
// @ts-ignore
interceptorInstance = new curr();
}
const prevData = await prev;
return interceptorInstance.intercept(event, prevData);
}, context);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { DiscordMiddlewareInstance } from './interface/discord-middleware-instance';
import { DiscordMiddlewareInstance } from '../interface/discord-middleware-instance';
import { ClientEvents } from "discord.js";
import { Injectable } from '@nestjs/common';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import { MIDDLEWARE_DECORATOR } from './constant/discord.constant';
import { MIDDLEWARE_DECORATOR } from '../constant/discord.constant';

@Injectable()
export class DiscordMiddlewareService {
Expand Down
12 changes: 6 additions & 6 deletions lib/discord.service.ts → lib/service/discord.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Injectable, OnApplicationBootstrap } from '@nestjs/common';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
import { DiscordResolve } from './interface/discord-resolve';
import { OnCommandResolver } from './resolver/on-command.resolver';
import { OnResolver } from './resolver/on.resolver';
import { DiscordClient } from './discord-client';
import { OnceResolver } from './resolver/once.resolver';
import { DiscordMiddlewareInstance } from './interface/discord-middleware-instance';
import { DiscordResolve } from '../interface/discord-resolve';
import { OnCommandResolver } from '../resolver/on-command.resolver';
import { OnResolver } from '../resolver/on.resolver';
import { DiscordClient } from '../discord-client';
import { OnceResolver } from '../resolver/once.resolver';
import { DiscordMiddlewareInstance } from '../interface/discord-middleware-instance';
import { DiscordMiddlewareService } from './discord-middleware.service';

@Injectable()
Expand Down
3 changes: 3 additions & 0 deletions lib/utils/is-class-decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const isClassDecorator = (target: any): boolean => {
return typeof target === 'function';
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "discord-nestjs",
"version": "0.2.3",
"version": "0.2.4",
"description": "NestJS package for discord.js",
"author": "fjodor-rybakov",
"license": "MIT",
Expand Down

0 comments on commit ac6c134

Please sign in to comment.