Skip to content

Commit bcc6c77

Browse files
committed
fix: Refactor index file
1 parent ef2ab02 commit bcc6c77

2 files changed

Lines changed: 114 additions & 47 deletions

File tree

packages/server/src/env.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const EnvSchema = z.object({
1010
DISPATCH_CRON: z.string().default("* * * * * *"),
1111
});
1212

13-
export function getEnv() {
13+
export type Env = z.infer<typeof EnvSchema>;
14+
15+
export function getEnv(): Env {
1416
return EnvSchema.parse(process.env);
1517
}

packages/server/src/index.ts

Lines changed: 111 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,67 +10,132 @@ import { UserService } from "./services/user";
1010
import { WalletService } from "./services/wallet";
1111
import { DispatchJob } from "./jobs/dispatch";
1212
import { MessageService } from "./services/message";
13-
import { getEnv } from "./env";
13+
import { Env, getEnv } from "./env";
1414
import { SampleJob } from "./jobs/sample";
1515
import { CleanupJob } from "./jobs/cleanup";
1616

17-
const env = getEnv();
18-
const prisma = new PrismaClient({ datasourceUrl: env.DATABASE_URL });
1917
const logger = pino();
20-
const bot = new Bot<Context & SessionFlavor<unknown>>(env.BOT_TOKEN);
2118

22-
/* Services */
19+
/**
20+
* Main function to instantiate and run all components of the application
21+
*/
22+
async function main() {
23+
const env = getEnv();
24+
const prisma = await getPrismaClient(env);
25+
const services = getServices(prisma, env);
26+
const bot = getBot(env, services);
27+
const jobs = getJobs(env, services, bot);
2328

24-
const user = new UserService(prisma);
25-
const wallet = new WalletService(prisma);
26-
const message = new MessageService(prisma, env.MAX_ATTEMPTS);
29+
run(bot);
2730

28-
/* Handlers */
31+
jobs.forEach((job) => job.start());
32+
}
2933

30-
const start = getStartHandler(logger.child({ handler: "start" }), message, user, wallet);
31-
const add = getAddHandler(logger.child({ handler: "add" }), message, user, wallet);
32-
const list = getListHandler(logger.child({ handler: "list" }), message, user);
33-
const remove = getRemoveHandler(logger.child({ handler: "remove" }), message, user);
34+
/**
35+
* Instantiate and configure the Prisma Client for database interactions
36+
*/
37+
async function getPrismaClient(env: Env): Promise<PrismaClient> {
38+
const prisma = new PrismaClient({ datasourceUrl: env.DATABASE_URL });
3439

35-
/* Telegram Bot */
40+
// SQlite Database optimizations.
41+
// Remove or change for other databases
42+
await prisma.$queryRawUnsafe(`PRAGMA journal_mode=WAL;`);
43+
await prisma.$queryRawUnsafe(`PRAGMA synchronous=NORMAL;`);
3644

37-
const getSessionKey = (ctx: Context) => ctx.chat?.id.toString();
38-
bot.use(sequentialize(getSessionKey));
39-
bot.use(session({ getSessionKey }));
45+
return prisma;
46+
}
4047

41-
bot.command("start", start);
42-
bot.command("add", add);
43-
bot.command("list", list);
44-
bot.command("remove", remove);
48+
/**
49+
* Instantiate all services used across the application
50+
*/
51+
function getServices(prisma: PrismaClient, env: Env) {
52+
// Service responsible for managing messages
53+
const message = new MessageService(prisma, env.MAX_ATTEMPTS);
4554

46-
bot.catch((err) =>
47-
logger.error({ err, userId: err.ctx.from?.id, message: err.ctx.message?.text }, "Failure"),
48-
);
55+
// Service responsible for managing users
56+
const user = new UserService(prisma);
4957

50-
run(bot);
58+
// Service responsible for managing wallets
59+
const wallet = new WalletService(prisma);
5160

52-
logger.info("Started");
61+
return {
62+
message,
63+
user,
64+
wallet,
65+
};
66+
}
5367

54-
/* Jobs */
68+
/**
69+
* Instantiate and configure the Telegram Bot with all commands and middleware
70+
*/
71+
function getBot(env: Env, services: ReturnType<typeof getServices>) {
72+
const bot = new Bot<Context & SessionFlavor<unknown>>(env.BOT_TOKEN);
5573

56-
new DispatchJob(
57-
logger.child({ job: "dispatch" }),
58-
message,
59-
env.DISPATCH_CRON,
60-
bot,
61-
env.MESSAGES_PER_DISPATCH,
62-
).start();
74+
// Middleware that ensures order of message processing per user
75+
const getSessionKey = (ctx: Context) => ctx.chat?.id.toString();
76+
bot.use(sequentialize(getSessionKey));
77+
bot.use(session({ getSessionKey }));
6378

64-
new CleanupJob(
65-
logger.child({ job: "cleanup" }),
66-
message,
67-
env.CLEANUP_CRON,
68-
env.CLEANUP_CUTOFF,
69-
).start();
79+
const { message, user, wallet } = services;
7080

71-
new SampleJob(
72-
logger.child({ job: "sample" }),
73-
message,
74-
"*/10 * * * * *", // Every 10 seconds
75-
wallet,
76-
).start();
81+
// Command used to onboard a new user and optionally subscribe a wallet
82+
bot.command("start", getStartHandler(logger.child({ command: "start" }), message, user, wallet));
83+
84+
// Command used to subscribe a new wallet
85+
bot.command("add", getAddHandler(logger.child({ command: "add" }), message, user, wallet));
86+
87+
// Command used to list all subscribed wallets
88+
bot.command("list", getListHandler(logger.child({ command: "list" }), message, user));
89+
90+
// Command used to unsubscribe a wallet
91+
bot.command("remove", getRemoveHandler(logger.child({ command: "remove" }), message, user));
92+
93+
// Global error handler for the bot
94+
bot.catch((err) =>
95+
logger.error({ err, userId: err.ctx.from?.id, message: err.ctx.message?.text }, "Failure"),
96+
);
97+
98+
return bot;
99+
}
100+
101+
/**
102+
* Instantiate all jobs for scheduled background processing
103+
*/
104+
function getJobs(
105+
env: Env,
106+
services: ReturnType<typeof getServices>,
107+
bot: ReturnType<typeof getBot>,
108+
) {
109+
// Job that dispatches queued messages to users
110+
const dispatchJob = new DispatchJob(
111+
logger.child({ job: "dispatch" }),
112+
services.message,
113+
env.DISPATCH_CRON,
114+
bot,
115+
env.MESSAGES_PER_DISPATCH,
116+
);
117+
118+
// Job that deletes old messages from the database
119+
const cleanupJob = new CleanupJob(
120+
logger.child({ job: "cleanup" }),
121+
services.message,
122+
env.CLEANUP_CRON,
123+
env.CLEANUP_CUTOFF,
124+
);
125+
126+
// Sample job for demo purposes
127+
// Can be removed or modified as needed
128+
const sampleJob = new SampleJob(
129+
logger.child({ job: "sample" }),
130+
services.message,
131+
"*/10 * * * * *", // Every 10 seconds
132+
services.wallet,
133+
);
134+
135+
return [dispatchJob, cleanupJob, sampleJob];
136+
}
137+
138+
main().catch((err) => {
139+
logger.error({ err }, "Main function failed");
140+
process.exit(1);
141+
});

0 commit comments

Comments
 (0)