Skip to content

Commit

Permalink
applying DI wip
Browse files Browse the repository at this point in the history
  • Loading branch information
antonkorotkov committed Jun 23, 2024
1 parent 105d40f commit 1b52488
Show file tree
Hide file tree
Showing 21 changed files with 141 additions and 91 deletions.
18 changes: 16 additions & 2 deletions app/app.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
class App {
#db;
#mongoose;
#log;
#marketsService;
#bot;

constructor(options) {
this.#mongoose = options.mongoose;
this.#db = options.db;
this.#log = options.logger.debug(this.constructor.name);
this.#marketsService = options.marketsService;
this.#bot = options.bot;

this.#log('Initializing...');
}

async start() {
this.#mongoose.connection.once('open', () => {
console.log('Connected to database!');
this.#mongoose.connection.once('open', async () => {
this.#log('Connected to database!');

await this.#marketsService.watch(changed => {
this.#log(changed.length);
});

this.#bot.start();
});

await this.#db();
Expand Down
44 changes: 44 additions & 0 deletions app/bot/TelegramBot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const { I18n } = require("@grammyjs/i18n");
const { Bot } = require("grammy");

const i18n = new I18n({
defaultLocale: "en",
directory: "app/bot/locales"
});

class TelegramBot {
#log;
bot;

constructor(options) {
this.#log = options.logger.debug(this.constructor.name);
this.bot = new Bot(options.config.telegramBotToken);

this.#log('Initializing...');
this.bot.catch(this.errorHandler);
this.bot.use(options.logger.middleware(this.constructor.name));
this.bot.use(i18n);
this.bot.command('start', async ctx => {
await ctx.reply('Test');
});
}

errorHandler({ ctx, error }) {
this.#log('Error while handling update %s:', ctx.update.update_id);

if (error instanceof GrammyError) {
this.#log('Error in request: %s', error.description);
} else if (error instanceof HttpError) {
this.#log('Could not contact Telegram:', error);
} else {
this.#log("Unknown error:", error);
}
}

start() {
this.#log('Started.');
this.bot.start();
}
}

module.exports = TelegramBot;
File renamed without changes.
File renamed without changes.
File renamed without changes.
8 changes: 7 additions & 1 deletion app/container.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
const { createContainer, asClass, asValue, asFunction } = require('awilix');

const config = require('./config');
const logger = require('./logger');
const { connectToDatabase, mongoose } = require('./db/database');
const App = require('./app');
const MarketsService = require('./market/MarketsService');
const TelegramBot = require('./bot/TelegramBot');

const container = createContainer({
strict: true
});

container.register({
config: asValue(config),
logger: asValue(logger),
mongoose: asValue(mongoose),
db: asFunction(connectToDatabase).singleton(),
app: asClass(App)
app: asClass(App),
marketsService: asClass(MarketsService).singleton(),
bot: asClass(TelegramBot).singleton()
});

module.exports = container;
6 changes: 4 additions & 2 deletions app/db/database.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const mongoose = require('mongoose');

const connectToDatabase = ({ config }) => async () => {
if (mongoose.connection.readyState !== 1)
const connectToDatabase = ({ config, logger }) => async () => {
if (mongoose.connection.readyState !== 1) {
logger.debug('DB')('Connecting...');
await mongoose.connect(config.dbConnectionString);
}
};

module.exports = {
Expand Down
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions app/logger/debug.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var debug = require('debug');

module.exports = source => debug(source);
6 changes: 6 additions & 0 deletions app/logger/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const debug = require('./debug');
const middleware = require('./middleware');

module.exports = {
debug, middleware
};
10 changes: 10 additions & 0 deletions app/logger/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var debug = require('debug');

const middleware = source => async (ctx, next) => {
debug(source)('Update Received:');
debug(source)(ctx.message ?? ctx.update);

await next();
};

module.exports = middleware;
4 changes: 1 addition & 3 deletions market/Market.js → app/market/Market.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
const round = num => {
return Math.ceil(num * 1000000) / 1000000;
}
const round = require("../utils/round");

module.exports = class Market {
/** @type {string} */
Expand Down
97 changes: 42 additions & 55 deletions market/MarketCollection.js → app/market/MarketsService.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,38 @@
const round = require("../utils/round");
const Market = require("./Market");

const round = num => {
return Math.ceil(num * 1000000) / 1000000;
}
const apiUrl = 'https://www.worldcoinindex.com/apiservice/v2getmarkets';
const fetchInterval = 60_000;

module.exports = class MarketCollection {
/** @type {Market[]} */
class MarketsService {
#log;
#apiKey;
#onChange;
#markets = [];

/** @type {MarketCollectionOptions['onMarketsChange']} */
#onMarketsChange = () => {};

#interval = 60_000;
#apiUrl = 'https://www.worldcoinindex.com/apiservice/v2getmarkets';
#apiKey = process.env.WCI_KEY;
#fiat = 'USD';

/**
* @param {MarketCollectionOptions} [options]
*/
constructor(options = {}) {
const { onMarketsChange } = options;
constructor({ logger, config }) {
this.#log = logger.debug(this.constructor.name);
this.#apiKey = config.wciApiKey;

if (onMarketsChange)
this.#onMarketsChange = onMarketsChange;
this.#log('Initializing...');
}

/**
* @returns {Promise<MarketData[]>}
*/
async #fetchMarkets() {
async watch(onChange) {
try {
const response = await fetch(`${this.#apiUrl}?key=${this.#apiKey}&fiat=${this.#fiat}`);
const json = await response.json();

if (json.Markets && Array.isArray(json.Markets) && Array.isArray(json.Markets.at(0)))
return json.Markets.at(0);
} catch (err) {
console.error('ERROR', err);
return Promise.reject(err.message);
}
}
if (onChange && !this.#onChange)
this.#onChange = onChange;

async refresh() {
try {
const changes = [];
const marketsData = await this.#fetchMarkets();

console.log('received markets data', marketsData.length);
const marketsData = await this.#fetch();

marketsData.sort((a, b) => a.Name.localeCompare(b.Name)).forEach(marketData => {
const foundIndex = this.#markets.findIndex(m => m.getName() === marketData.Name);

if (foundIndex >= 0) {
if (foundIndex !== -1) {
const oldPrice = round(this.#markets[foundIndex].getPrice());
const newPrice = round(marketData.Price);
if (oldPrice !== newPrice) {
console.log('change found', oldPrice, newPrice);
this.#log('%s price changed: %s -> %s', this.#markets[foundIndex].getCoin(), oldPrice, newPrice);
changes.push(new Market(marketData));
}

Expand All @@ -67,21 +43,34 @@ module.exports = class MarketCollection {
}
});

if (changes.length)
this.#onMarketsChange(changes);
if (changes.length && this.#onChange)
this.#onChange(changes);
} catch (err) {
console.error('ERROR', err);
this.#log('ERROR:', err);
}

setTimeout(() => {
this.refresh();
}, this.#interval);
this.watch();
}, fetchInterval);
}

async #fetch() {
try {
this.#log('Fetching markets...');
const response = await fetch(`${apiUrl}?key=${this.#apiKey}&fiat=USD`);
const json = await response.json();

if (json.Markets && Array.isArray(json.Markets) && Array.isArray(json.Markets.at(0))) {
this.#log('Markets retrieved:', json.Markets.at(0).length);
return json.Markets.at(0);
}

} catch (err) {
this.#log('ERROR:', err);
return Promise.reject(err.message);
}
}

/**
* @param {string} phrase
* @returns {Market[]}
*/
findMarkets(phrase) {
const searchPhrase = phrase.toLowerCase();

Expand All @@ -90,11 +79,9 @@ module.exports = class MarketCollection {
);
}

/**
* @param {string} coin
* @returns {Market}
*/
getMarketByCoin(coin) {
return this.#markets.find(m => m.getCoin() === coin);
}
}
}

module.exports = MarketsService;
5 changes: 5 additions & 0 deletions app/utils/round.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const round = num => {
return Math.ceil(num * 1000000) / 1000000;
}

module.exports = round;
7 changes: 0 additions & 7 deletions db/connect.js

This file was deleted.

6 changes: 0 additions & 6 deletions logger/index.js

This file was deleted.

3 changes: 0 additions & 3 deletions market/index.js

This file was deleted.

11 changes: 0 additions & 11 deletions market/type.d.ts

This file was deleted.

1 change: 1 addition & 0 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node ./index.js"
"start": "DEBUG=* node ./index.js"
},
"author": "Anton Korotkov",
"license": "ISC",
"dependencies": {
"@grammyjs/conversations": "^1.2.0",
"@grammyjs/i18n": "^1.0.2",
"awilix": "^10.0.2",
"debug": "^4.3.5",
"dotenv": "^16.4.5",
"grammy": "^1.24.1",
"moment": "^2.30.1",
Expand Down

0 comments on commit 1b52488

Please sign in to comment.