Skip to content

Commit

Permalink
Merge pull request #6 from QuantGeekDev/bugfix/dist
Browse files Browse the repository at this point in the history
Bugfix/dist
  • Loading branch information
QuantGeekDev authored Jan 4, 2024
2 parents cedae6e + 743d6ea commit 59f5456
Show file tree
Hide file tree
Showing 21 changed files with 192 additions and 148 deletions.
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": false,
"semi": true,
"singleQuote": true,
"useTabs": true,
"jsxSingleQuote": true,
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

Intenteded to display real estate properties concisely via a Telegram Bot


[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=QuantGeekDev_PropertyGram-telegram-bot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=QuantGeekDev_PropertyGram-telegram-bot)


## Features

- Built with TypeScript for static typing and enhanced code quality.
Expand All @@ -16,8 +18,9 @@ Intenteded to display real estate properties concisely via a Telegram Bot

After cloning the repository, you can use the following npm scripts:

- `compile`: Compiles the TypeScript files to JavaScript, placing the compiled files in the `build` directory.
- `postcompile`: Copies the locales from the `src` directory to the `build` directory.

- `compile`: Compiles the TypeScript files to JavaScript, placing the compiled files in the `dist` directory.
- `postcompile`: Copies the locales from the `src` directory to the `dist` directory.
- `bg:start`: Prepares the environment and starts the bot in the background using pm2.
- `bg:restart`: Restarts the bot service.
- `bg:logs`: Fetches logs for the bot, defaulting to the last 1000 lines.
Expand Down
32 changes: 32 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"main": "dist/index.js",
"scripts": {
"compile": "tsc",
"postcompile": "cp -r src/locales build",
"bg:start": "npm run prepare && pm2 start build/index.js --name telegram-bot",
"postcompile": "cp -r src/locales dist",
"bg:start": "npm run prepare && pm2 start dist/index.js --name propertygram-bot",
"bg:restart": "npm run prepare && pm2 restart telegram-bot",
"bg:logs": "pm2 logs telegram-bot --lines 1000 -f",
"start": "npm run compile && node .",
Expand All @@ -24,13 +24,15 @@
"license": "GPL-3.0-or-later",
"dependencies": {
"@grammyjs/i18n": "^0.5.1",
"debug": "^4.3.4",
"dotenv": "^16.0.1",
"grammy": "^1.10.1",
"mongodb": "^4.9.1"
},
"devDependencies": {
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.3",
"@types/debug": "^4.1.12",
"@types/node": "^18.7.4",
"@types/pg": "^8.6.5",
"@typescript-eslint/parser": "^6.7.3",
Expand Down
35 changes: 19 additions & 16 deletions src/config/app.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
// TODO: Use path aliases
import { loadEnv } from '../helpers/load-env.js'
import { validateEnv } from '../helpers/validate-env.js'
import { startBot } from './bot.js'
import { connectToDb } from './database.js'
import { loadEnv } from '../helpers/load-env.js';
import { validateEnv } from '../helpers/validate-env.js';
import { startBot } from './bot.js';
import { connectToDb } from './database.js';
import debugCreator from 'debug';

const debug = debugCreator('app:');

export async function startApp() {
debug('Starting app...');
try {
loadEnv()
validateEnv(['TOKEN', 'DB_CONNECTION_STRING'])
loadEnv();
validateEnv(['TELEGRAM_TOKEN', 'DB_CONNECTION_STRING']);
} catch (error) {
console.error('Error occurred while loading environment:', error)
process.exit(1)
console.error('Error occurred while loading environment:', error);
process.exit(1);
}

let database
let database;
try {
database = await connectToDb()
database = await connectToDb();
} catch (error) {
console.error('Error occurred while connecting to the database:', error)
process.exit(2)
console.error('Error occurred while connecting to the database:', error);
process.exit(2);
}

try {
await startBot(database)
await startBot(database);
} catch (error) {
console.error('Error occurred while starting the bot:', error)
process.exit(3)
console.error('Error occurred while starting the bot:', error);
process.exit(3);
}
}
66 changes: 33 additions & 33 deletions src/config/bot.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import type { I18n } from '@grammyjs/i18n/dist/source/i18n.js'
import { Bot as TelegramBot, session } from 'grammy'
import type { I18n } from '@grammyjs/i18n/dist/source/i18n.js';
import { Bot as TelegramBot, session } from 'grammy';

import { resolvePath } from '../helpers/resolve-path.js'
import { createReplyWithTextFunc } from '../services/context.js'
import type { CustomContext } from '../types/context.js'
import type { Chat, Database } from '../types/database.js'
import { initLocaleEngine } from './locale-engine.js'
import { startController } from '../controllers/start.js'
import { stopController } from '../controllers/stop.js'
import type { Bot } from '../types/telegram.js'
import { buildName, getOrCreatePlayer } from '../services/user.js'
import { getOrCreateChat } from '../services/chat.js'
import { resolvePath } from '../helpers/resolve-path.js';
import { createReplyWithTextFunc } from '../services/context.js';
import type { CustomContext } from '../types/context.js';
import type { Chat, Database } from '../types/database.js';
import { initLocaleEngine } from './locale-engine.js';
import { startController } from '../controllers/start.js';
import { stopController } from '../controllers/stop.js';
import type { Bot } from '../types/telegram.js';
import { buildName, getOrCreatePlayer } from '../services/user.js';
import { getOrCreateChat } from '../services/chat.js';

function extendContext(bot: Bot, database: Database) {
bot.use(async (ctx, next) => {
if (!ctx.chat || !ctx.from) {
return
return;
}

ctx.text = createReplyWithTextFunc(ctx)
ctx.db = database
ctx.text = createReplyWithTextFunc(ctx);
ctx.db = database;

let chat: Chat | null = null
let chat: Chat | null = null;
if (ctx.chat.type !== 'private') {
chat = await getOrCreateChat({
db: database,
chatId: ctx.chat.id,
title: ctx.chat.title
})
});
}

ctx.entities = {
Expand All @@ -37,33 +37,33 @@ function extendContext(bot: Bot, database: Database) {
name: buildName(ctx.from.first_name, ctx.from.last_name)
}),
chat
}
};

await next()
})
await next();
});
}

function setupMiddlewares(bot: Bot, localeEngine: I18n) {
bot.use(session())
bot.use(localeEngine.middleware())
bot.catch(console.error)
bot.use(session());
bot.use(localeEngine.middleware());
bot.catch(console.error);
}

function setupControllers(bot: Bot) {
bot.use(startController)
bot.use(stopController)
bot.use(startController);
bot.use(stopController);
}

export async function startBot(database: Database) {
const localesPath = resolvePath(import.meta.url, '../locales')
const i18n = initLocaleEngine(localesPath)
const bot = new TelegramBot<CustomContext>(process.env.TOKEN)
extendContext(bot, database)
setupMiddlewares(bot, i18n)
setupControllers(bot)
const localesPath = resolvePath(import.meta.url, '../locales');
const i18n = initLocaleEngine(localesPath);
const bot = new TelegramBot<CustomContext>(process.env.TELEGRAM_TOKEN);
extendContext(bot, database);
setupMiddlewares(bot, i18n);
setupControllers(bot);

// NOTE: Resolves only when bot is stopped
// so give it a second to start instead of `await`
bot.start()
return new Promise(resolve => setTimeout(resolve, 1_000))
bot.start();
return new Promise(resolve => setTimeout(resolve, 1_000));
}
18 changes: 9 additions & 9 deletions src/config/database.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { MongoClient } from 'mongodb'
import type { Chat, Database, User } from '../types/database.js'
import { MongoClient } from 'mongodb';
import type { Chat, Database, User } from '../types/database.js';

export async function connectToDb() {
const client = new MongoClient(process.env.DB_CONNECTION_STRING)
await client.connect()
const mongoDb = client.db()
const user = mongoDb.collection<User>('user')
const chat = mongoDb.collection<Chat>('chat')
const database: Database = { user, chat }
return database
const client = new MongoClient(process.env.DB_CONNECTION_STRING);
await client.connect();
const mongoDb = client.db();
const user = mongoDb.collection<User>('user');
const chat = mongoDb.collection<Chat>('chat');
const database: Database = { user, chat };
return database;
}
6 changes: 3 additions & 3 deletions src/config/locale-engine.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { I18n } from '@grammyjs/i18n'
import { I18n } from '@grammyjs/i18n';

export function initLocaleEngine(path: string, defaultLanguage = 'en') {
const i18n = new I18n({
directory: path,
defaultLanguage: defaultLanguage,
defaultLanguageOnMissing: true,
useSession: true
})
return i18n
});
return i18n;
}
10 changes: 5 additions & 5 deletions src/controllers/start.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Composer } from 'grammy'
import type { CustomContext } from '../types/context.js'
import { Composer } from 'grammy';
import type { CustomContext } from '../types/context.js';

export const startController = new Composer<CustomContext>()
export const startController = new Composer<CustomContext>();
startController.command('start', async ctx => {
await ctx.text('start', {
name: ctx.entities.user.name,
chatName: ctx.entities.chat?.title ?? 'PM'
})
})
});
});
10 changes: 5 additions & 5 deletions src/controllers/stop.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Composer } from 'grammy'
import type { CustomContext } from '../types/context.js'
import { Composer } from 'grammy';
import type { CustomContext } from '../types/context.js';

export const stopController = new Composer<CustomContext>()
export const stopController = new Composer<CustomContext>();
stopController.command('stop', async ctx => {
await ctx.text('stop', {
name: ctx.entities.user.name,
chatName: ctx.entities.chat?.title ?? 'PM'
})
})
});
});
4 changes: 2 additions & 2 deletions src/environment.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export declare global {
namespace NodeJS {
interface ProcessEnv {
TOKEN: string
DB_CONNECTION_STRING: string
TELEGRAM_TOKEN: string;
DB_CONNECTION_STRING: string;
}
}
}
10 changes: 5 additions & 5 deletions src/helpers/load-env.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import dotenv from 'dotenv'
import { resolvePath } from './resolve-path.js'
import dotenv from 'dotenv';
import { resolvePath } from './resolve-path.js';

export function loadEnv(configPath = '.env') {
const fullPath = resolvePath(import.meta.url, configPath)
export function loadEnv(configPath = '../../.env') {
const fullPath = resolvePath(import.meta.url, configPath);
dotenv.config({
path: fullPath
})
});
}
10 changes: 5 additions & 5 deletions src/helpers/resolve-path.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import pathModule from 'path'
import { fileURLToPath } from 'url'
import pathModule from 'path';
import { fileURLToPath } from 'url';

export function resolvePath(localPath: string, targetPath: string) {
const __filename = fileURLToPath(localPath)
const __dirname = pathModule.dirname(__filename)
return pathModule.join(__dirname, targetPath)
const __filename = fileURLToPath(localPath);
const __dirname = pathModule.dirname(__filename);
return pathModule.join(__dirname, targetPath);
}
Loading

0 comments on commit 59f5456

Please sign in to comment.