Skip to content

Commit

Permalink
feat: static async factory approach
Browse files Browse the repository at this point in the history
Signed-off-by: jahabeebs <47253537+jahabeebs@users.noreply.github.com>
  • Loading branch information
jahabeebs committed Oct 1, 2024
1 parent c3f555e commit e03e697
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 58 deletions.
16 changes: 15 additions & 1 deletion apps/agent/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { inspect } from "util";
import { EboActorsManager, EboProcessor } from "@ebo-agent/automated-dispute";
import { ProtocolProvider } from "@ebo-agent/automated-dispute/dist/providers/protocolProvider.js";
import { DiscordNotifier } from "@ebo-agent/automated-dispute/src/services/index.js";
import { BlockNumberService } from "@ebo-agent/blocknumber";
import { Logger } from "@ebo-agent/shared";

Expand Down Expand Up @@ -30,6 +31,8 @@ const config = {
processor: {
msBetweenChecks: 1,
},
DISCORD_BOT_TOKEN: process.env.DISCORD_BOT_TOKEN || "",
DISCORD_CHANNEL_ID: process.env.DISCORD_CHANNEL_ID || "",
};

const logger = Logger.getInstance();
Expand All @@ -49,7 +52,18 @@ const main = async (): Promise<void> => {

const actorsManager = new EboActorsManager();

const processor = new EboProcessor(protocolProvider, blockNumberService, actorsManager, logger);
const notifier = await DiscordNotifier.create({
discordBotToken: config.DISCORD_BOT_TOKEN,
discordChannelId: config.DISCORD_CHANNEL_ID,
});

const processor = new EboProcessor(
protocolProvider,
blockNumberService,
actorsManager,
logger,
notifier,
);

await processor.start(config.processor.msBetweenChecks);
};
Expand Down
3 changes: 2 additions & 1 deletion packages/automated-dispute/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"async-mutex": "0.5.0",
"discord.js": "14.16.2",
"heap-js": "2.5.0",
"viem": "2.17.11"
"viem": "2.17.11",
"zod": "3.23.8"
}
}
8 changes: 8 additions & 0 deletions packages/automated-dispute/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from "zod";

const ConfigSchema = z.object({
DISCORD_BOT_TOKEN: z.string().min(1),
DISCORD_CHANNEL_ID: z.string().min(1),
});

export const config = ConfigSchema.parse(process.env);
35 changes: 18 additions & 17 deletions packages/automated-dispute/src/services/discordNotifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,29 @@ interface DiscordNotifierConfig {
*/
export class DiscordNotifier implements NotificationService {
private client: Client;
private readyPromise: Promise<void>;
private config: DiscordNotifierConfig;

private constructor(client: Client, config: DiscordNotifierConfig) {
this.client = client;
this.config = config;
}

/**
* Creates an instance of the DiscordNotifier.
* @param {DiscordNotifierConfig} config - The configuration object for the DiscordNotifier.
* @returns {Promise<DiscordNotifier>} A promise that resolves to a DiscordNotifier instance.
*/
constructor(config: DiscordNotifierConfig) {
public static async create(config: DiscordNotifierConfig): Promise<DiscordNotifier> {
const intents = new IntentsBitField().add(
IntentsBitField.Flags.Guilds,
IntentsBitField.Flags.GuildMessages,
);
this.client = new Client({ intents });
this.config = config;
this.readyPromise = this.initialize();
}
const client = new Client({ intents });

/**
* Initializes the Discord notifier by logging in with the bot token and waiting for the "ready" event.
* @returns {Promise<void>} A promise that resolves when the Discord bot is ready.
* @throws {Error} If the initialization fails.
*/
private async initialize(): Promise<void> {
try {
await this.client.login(this.config.discordBotToken);
await client.login(config.discordBotToken);
await new Promise<void>((resolve) => {
this.client.once("ready", () => {
client.once("ready", () => {
console.log("Discord bot is ready");
resolve();
});
Expand All @@ -47,6 +43,8 @@ export class DiscordNotifier implements NotificationService {
console.error("Failed to initialize Discord notifier:", error);
throw error;
}

return new DiscordNotifier(client, config);
}

/**
Expand All @@ -55,9 +53,8 @@ export class DiscordNotifier implements NotificationService {
* @param {any} context - Additional context information.
* @returns {Promise<void>} A promise that resolves when the message is sent.
*/
async notifyError(error: Error, context: any): Promise<void> {
public async notifyError(error: Error, context: any): Promise<void> {
try {
await this.readyPromise;
const channel = await this.client.channels.fetch(this.config.discordChannelId);
if (!channel || !channel.isTextBased()) {
throw new Error("Discord channel not found or is not text-based");
Expand All @@ -78,6 +75,10 @@ export class DiscordNotifier implements NotificationService {
* @returns {string} The formatted error message.
*/
private formatErrorMessage(error: Error, context: any): string {
return `**Error:** ${error.name} - ${error.message}\n**Context:**\n\`\`\`json\n${JSON.stringify(context, null, 2)}\n\`\`\``;
return `**Error:** ${error.name} - ${error.message}\n**Context:**\n\`\`\`json\n${JSON.stringify(
context,
null,
2,
)}\n\`\`\``;
}
}
10 changes: 3 additions & 7 deletions packages/automated-dispute/src/services/eboActorsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { Mutex } from "async-mutex";
import { RequestAlreadyHandled } from "../exceptions/index.js";
import { ProtocolProvider } from "../providers/protocolProvider.js";
import { ActorRequest, RequestId } from "../types/index.js";
import { DiscordNotifier } from "./discordNotifier.js";
import { EboActor } from "./eboActor.js";
import { EboMemoryRegistry } from "./eboRegistry/eboMemoryRegistry.js";
import { NotificationService } from "./notificationService.js";

export class EboActorsManager {
private readonly requestActorMap: Map<RequestId, EboActor>;
Expand Down Expand Up @@ -39,6 +39,7 @@ export class EboActorsManager {
protocolProvider: ProtocolProvider,
blockNumberService: BlockNumberService,
logger: ILogger,
notifier: NotificationService,
): EboActor {
const requestId = actorRequest.id;

Expand All @@ -48,19 +49,14 @@ export class EboActorsManager {

const eventProcessingMutex = new Mutex();

const discordNotifier = new DiscordNotifier({
discordBotToken: process.env.DISCORD_BOT_TOKEN!,
discordChannelId: process.env.DISCORD_CHANNEL_ID!,
});

const actor = new EboActor(
actorRequest,
protocolProvider,
blockNumberService,
registry,
eventProcessingMutex,
logger,
discordNotifier,
notifier,
);

this.requestActorMap.set(requestId, actor);
Expand Down
3 changes: 3 additions & 0 deletions packages/automated-dispute/src/services/eboProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ProtocolProvider } from "../providers/protocolProvider.js";
import { alreadyDeletedActorWarning, droppingUnhandledEventsWarning } from "../templates/index.js";
import { ActorRequest, EboEvent, EboEventName, Epoch, RequestId } from "../types/index.js";
import { EboActorsManager } from "./eboActorsManager.js";
import { NotificationService } from "./notificationService.js";

const DEFAULT_MS_BETWEEN_CHECKS = 10 * 60 * 1000; // 10 minutes

Expand All @@ -22,6 +23,7 @@ export class EboProcessor {
private readonly blockNumberService: BlockNumberService,
private readonly actorsManager: EboActorsManager,
private readonly logger: ILogger,
private readonly notifier: NotificationService,
) {}

/**
Expand Down Expand Up @@ -249,6 +251,7 @@ export class EboProcessor {
this.protocolProvider,
this.blockNumberService,
this.logger,
this.notifier,
);

return actor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ describe("DiscordNotifier", () => {

let notifier: DiscordNotifier;

beforeEach(() => {
beforeEach(async () => {
vi.clearAllMocks();
notifier = new DiscordNotifier(mockConfig);
notifier = await DiscordNotifier.create(mockConfig);
});

it("initializes the Discord client and login", async () => {
Expand All @@ -55,8 +55,6 @@ describe("DiscordNotifier", () => {
intents: expect.any(IntentsBitField),
});

await notifier["readyPromise"];

const instance = ClientMock.mock.results[0].value;
expect(instance.login).toHaveBeenCalledWith("mock-token");
expect(instance.once).toHaveBeenCalledWith("ready", expect.any(Function));
Expand All @@ -66,8 +64,6 @@ describe("DiscordNotifier", () => {
const error = new Error("Test error");
const context = { key: "value" };

await notifier["readyPromise"];

await notifier.notifyError(error, context);

const ClientMock = Client as unknown as vi.Mock;
Expand Down Expand Up @@ -101,7 +97,7 @@ describe("DiscordNotifier", () => {
error.name = "TestError";
const context = { key: "value" };

const formattedMessage = notifier["formatErrorMessage"](error, context);
const formattedMessage = (notifier as any).formatErrorMessage(error, context);

expect(formattedMessage).toContain("**Error:** TestError - Test error message");
expect(formattedMessage).toContain("**Context:**");
Expand Down
57 changes: 39 additions & 18 deletions packages/automated-dispute/tests/services/eboActorsManager.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { beforeEach } from "node:test";
import { BlockNumberService } from "@ebo-agent/blocknumber";
import { Caip2ChainId } from "@ebo-agent/blocknumber/dist/types.js";
import { ILogger } from "@ebo-agent/shared";
import { describe, expect, it, vi } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";

import { RequestAlreadyHandled } from "../../src/exceptions/index.js";
import { ProtocolProvider } from "../../src/providers/index.js";
import { EboActorsManager } from "../../src/services/index.js";
import { EboActorsManager, NotificationService } from "../../src/services/index.js";
import mocks from "../mocks/index.js";
import {
DEFAULT_MOCKED_PROTOCOL_CONTRACTS,
Expand All @@ -16,17 +15,6 @@ import {

const logger: ILogger = mocks.mockLogger();

vi.mock("../../src/services/discordNotifier", () => {
return {
DiscordNotifier: vi.fn().mockImplementation(() => {
return {
initialize: vi.fn().mockResolvedValue(undefined),
notifyError: vi.fn().mockResolvedValue(undefined),
};
}),
};
});

describe("EboActorsManager", () => {
const request = DEFAULT_MOCKED_REQUEST_CREATED_DATA;
const actorRequest = {
Expand All @@ -37,8 +25,15 @@ describe("EboActorsManager", () => {

let protocolProvider: ProtocolProvider;
let blockNumberService: BlockNumberService;
let notifier: NotificationService;

beforeEach(() => {
vi.clearAllMocks();

notifier = {
notifyError: vi.fn().mockResolvedValue(undefined),
};

const protocolProviderRpcUrls = ["http://localhost:8538"];
protocolProvider = new ProtocolProvider(
protocolProviderRpcUrls,
Expand All @@ -60,6 +55,7 @@ describe("EboActorsManager", () => {
protocolProvider,
blockNumberService,
logger,
notifier,
);

expect(actor).toMatchObject({
Expand All @@ -75,7 +71,13 @@ describe("EboActorsManager", () => {

expect(actorsManager.getActor(request.id)).toBeUndefined();

actorsManager.createActor(actorRequest, protocolProvider, blockNumberService, logger);
actorsManager.createActor(
actorRequest,
protocolProvider,
blockNumberService,
logger,
notifier,
);

const actor = actorsManager.getActor(request.id);

Expand All @@ -85,14 +87,21 @@ describe("EboActorsManager", () => {
it("throws if the request has already an actor linked to it", () => {
const actorsManager = new EboActorsManager();

actorsManager.createActor(actorRequest, protocolProvider, blockNumberService, logger);
actorsManager.createActor(
actorRequest,
protocolProvider,
blockNumberService,
logger,
notifier,
);

expect(() => {
actorsManager.createActor(
actorRequest,
protocolProvider,
blockNumberService,
logger,
notifier,
);
}).toThrowError(RequestAlreadyHandled);
});
Expand All @@ -108,7 +117,13 @@ describe("EboActorsManager", () => {
it("returns the request's linked actor", () => {
const actorsManager = new EboActorsManager();

actorsManager.createActor(actorRequest, protocolProvider, blockNumberService, logger);
actorsManager.createActor(
actorRequest,
protocolProvider,
blockNumberService,
logger,
notifier,
);

const actor = actorsManager.getActor(request.id);

Expand All @@ -125,7 +140,13 @@ describe("EboActorsManager", () => {
it("deletes the actor linked to the request", () => {
const actorsManager = new EboActorsManager();

actorsManager.createActor(actorRequest, protocolProvider, blockNumberService, logger);
actorsManager.createActor(
actorRequest,
protocolProvider,
blockNumberService,
logger,
notifier,
);

expect(actorsManager.getActor(request.id)).toBeDefined();

Expand Down
Loading

0 comments on commit e03e697

Please sign in to comment.