Skip to content

Commit

Permalink
Add some caching stuff, better example of statefulCommands, need to a…
Browse files Browse the repository at this point in the history
…dd more docs, also fixed tests
  • Loading branch information
Awkewainze committed Oct 17, 2020
1 parent 0c04e02 commit 916f66b
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export * from "./disconnect";
export * from "./goldwatch";
export * from "./inspect";
export * from "./multipartCommandExample";
export * from "./simpleMultipartExample";
export * from "./statefulCommand";
export * from "./sus";
export * from "./villager";
// Temp comment.
92 changes: 92 additions & 0 deletions src/commands/simpleMultipartExample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Message } from "discord.js";
import { inject, injectable } from "tsyringe";
import {
CachingService,
GuildAndMemberScopedIndex,
GuildMemberAndChannelScopedIndex,
Index,
PersistentCachingService
} from "../services";
import { StatefulCommand } from "./statefulCommand";

interface State {
step: Step;
firstName: string;
}

interface PersistentState {
nameSet: boolean;
name: string;
}

@injectable()
export class SimpleMultipartExample extends StatefulCommand<State, PersistentState> {
constructor(
@inject("CachingService") cachingService: CachingService,
@inject("PersistentCachingService") persistentCachingService: PersistentCachingService,
@inject(GuildAndMemberScopedIndex) private readonly guildAndMemberScopedIndex: Index,
@inject(GuildMemberAndChannelScopedIndex) private readonly guildMemberAndChannelScopedIndex: Index
) {
super(
cachingService,
persistentCachingService,
{ step: Step.None, firstName: "" },
{ nameSet: false, name: "" }
);
this.guildAndMemberScopedIndex = this.guildAndMemberScopedIndex.addScope("SimpleMultipartExample");
this.guildMemberAndChannelScopedIndex = this.guildMemberAndChannelScopedIndex.addScope(
"SimpleMultipartExample"
);
}
async check(message: Message): Promise<boolean> {
return (
/^(sayName|setName)$/i.test(message.content) ||
(await this.getState(this.guildMemberAndChannelScopedIndex)).step !== Step.None
);
}
async execute(message: Message): Promise<void> {
const currentState = await this.getState(this.guildMemberAndChannelScopedIndex);
if (currentState.step === Step.SetFirstName) {
await this.setState(this.guildMemberAndChannelScopedIndex, {
firstName: message.content,
step: Step.SetLastName
});
await message.channel.send("Enter last name");
return;
}
if (currentState.step === Step.SetLastName) {
await this.setState(this.guildMemberAndChannelScopedIndex, {
step: Step.None
});
const state = await this.getState(this.guildMemberAndChannelScopedIndex);
await this.setPersistentState(this.guildAndMemberScopedIndex, {
name: `${state.firstName} ${message.content}`,
nameSet: true
});
await message.channel.send("Type `sayName`");
return;
}

if (/^sayName$/i.test(message.content)) {
const currentPersistentState = await this.getPersistentState(this.guildAndMemberScopedIndex);
if (currentPersistentState.nameSet) {
await message.channel.send(currentPersistentState.name);
}
return;
}

if (/^setName$/i.test(message.content)) {
await this.setPersistentState(this.guildAndMemberScopedIndex, { nameSet: false });
await this.setState(this.guildMemberAndChannelScopedIndex, {
step: Step.SetFirstName
});
await message.channel.send("Enter first name");
}
}
}

enum Step {
None,
SetFirstName,
SetLastName
}
41 changes: 41 additions & 0 deletions src/commands/statefulCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CachingService, Index, PersistentCachingService } from "../services";
import { Command } from "./command";

export abstract class StatefulCommand<State, PersistentState> extends Command {
constructor(
private readonly cachingService: CachingService,
private readonly persistentCachingService: PersistentCachingService,
private readonly defaultState: State,
private readonly defaultPersistentState: PersistentState
) {
super();
}
async getState(index: Index): Promise<State> {
return await this.cachingService.getOrAdd(index.getKey(Keys.State), this.defaultState);
}
async setState(index: Index, newStateChanges: Partial<State>): Promise<void> {
const currentState = await this.cachingService.getOrAdd<State>(index.getKey(Keys.State), this.defaultState);
const newState = Object.assign(currentState, newStateChanges);
await this.cachingService.set(index.getKey(Keys.State), newState);
}

async getPersistentState(index: Index): Promise<PersistentState> {
return await this.persistentCachingService.getOrAdd<PersistentState>(
index.getKey(Keys.State),
this.defaultPersistentState
);
}
async setPersistentState(index: Index, newStateChanges: Partial<PersistentState>): Promise<void> {
const currentState = await this.persistentCachingService.getOrAdd<PersistentState>(
index.getKey(Keys.State),
this.defaultPersistentState
);
const newState = Object.assign(currentState, newStateChanges);
await this.persistentCachingService.set(index.getKey(Keys.State), newState);
}
}

/** @ignore */
class Keys {
static State = "State";
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ function main() {
container
.createChildContainer()
.register("GuildId", { useValue: message.guild.id })
.register("GuildMemberId", { useValue: message.member.id })
.register("MessageChannelId", { useValue: message.channel.id })
.resolve(CommandService)
.execute(message);
}
Expand Down
4 changes: 3 additions & 1 deletion src/injects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
GoldWatchCommand,
InspectCommand,
MultipartExampleCommand,
SimpleMultipartExample,
SusCommand,
VillagerCommand
} from "./commands";
Expand All @@ -22,7 +23,8 @@ container.register("Command", { useClass: GoldWatchCommand });
container.register("Command", { useClass: InspectCommand });
container.register("Command", { useClass: SusCommand });
container.register("Command", { useClass: VillagerCommand });
container.register("Command", { useClass: MultipartExampleCommand });

// Disabled Commands
container.register("xCommand", { useClass: DebugCommand });
container.register("xCommand", { useClass: MultipartExampleCommand });
container.register("Command", { useClass: SimpleMultipartExample });
18 changes: 18 additions & 0 deletions src/services/cachingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ export class GuildScopedIndex extends Index {
}
}

@scoped(Lifecycle.ResolutionScoped)
export class GuildAndMemberScopedIndex extends Index {
constructor(@inject("GuildId") guildId: string, @inject("GuildMemberId") memberId: string) {
super([guildId, memberId]);
}
}

@scoped(Lifecycle.ResolutionScoped)
export class GuildMemberAndChannelScopedIndex extends Index {
constructor(
@inject("GuildId") guildId: string,
@inject("GuildMemberId") memberId: string,
@inject("MessageChannelId") channelId: string
) {
super([guildId, memberId, channelId]);
}
}

export abstract class CachingService {
get type(): "Persistent" | "Nonpersistent" {
return "Nonpersistent";
Expand Down
2 changes: 1 addition & 1 deletion tests/services/commandService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class MockCommand implements Command {
private readonly priorityCon: number,
private readonly exclusiveCon: boolean
) { }
check(message: Message): boolean {
async check(message: Message): Promise<boolean> {
return this.shouldCheck;
}
async execute(message: Message): Promise<void> {
Expand Down

0 comments on commit 916f66b

Please sign in to comment.