Skip to content

Commit

Permalink
util/index: convert js to ts
Browse files Browse the repository at this point in the history
The languageModel & fiatModel interface created to type-check
the data and ensure that it conforms to
the expected structure and format.
This can help you avoid errors and bugs when
working with the data in your code.
This commit enables resolveJsonModule to import module
with '.json' extension.
  • Loading branch information
Mersho committed Oct 9, 2023
1 parent f07ac09 commit cf8be67
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 61 deletions.
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true
"esModuleInterop": true,
"resolveJsonModule": true
}
}
4 changes: 4 additions & 0 deletions util/fiatModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ export interface IFiat {
price?: boolean;
locale?: string;
}

export interface IFiatCurrencies {
[key: string]: IFiat;
}
140 changes: 80 additions & 60 deletions util/index.js → util/index.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,44 @@
const axios = require('axios');
import { I18nContext } from "@grammyjs/i18n";
import { ICommunity, IOrderChannel } from "../models/community";
import { IOrder } from "../models/order";
import { UserDocument } from "../models/user";
import { IFiatCurrencies, IFiat } from "./fiatModel";
import { ILanguage, ILanguages } from "./languagesModel";
import { Telegram } from "telegraf/typings/core/types/typegram";
import axios from "axios";
import fiatJson from './fiat.json';
import languagesJson from './languages.json';
import { Order, Community } from "../models";
import logger from "../logger";
const { I18n } = require('@grammyjs/i18n');
const currencies = require('./fiat.json');
const languages = require('./languages.json');
const { Order, Community } = require('../models');
const logger = require('../logger');

const languages: ILanguages = languagesJson;
const currencies: IFiatCurrencies = fiatJson;

// ISO 4217, all ISO currency codes are 3 letters but users can trade shitcoins
const isIso4217 = code => {
const isIso4217 = (code: string): boolean => {
if (code.length < 3 || code.length > 5) {
return false;
}
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
code = code.toLowerCase().split('');
return code.every(letter => {
code = code.toLowerCase()
return code.split('').every(letter => {
if (alphabet.indexOf(letter) == -1) {
return false;
}
return true;
});
};

const getCurrency = code => {
if (!isIso4217(code)) return false;
const getCurrency = (code: string): (IFiat | null) => {
if (!isIso4217(code)) return null;
const currency = currencies[code];
if (!currency) return false;
if (!currency) return null;

return currency;
};

const plural = n => {
const plural = (n: number): string => {
if (n === 1) {
return '';
}
Expand All @@ -37,7 +47,7 @@ const plural = n => {

// This function formats a number to locale strings.
// If Iso code or locale code doesn´t exist, the function will return a number without format.
const numberFormat = (code, number) => {
const numberFormat = (code: string, number: number) => {
if (!isIso4217(code)) return false;

if (!currencies[code]) return number;
Expand All @@ -53,7 +63,7 @@ const numberFormat = (code, number) => {
// This function checks if the current buyer and seller were doing circular operations
// In order to increase their trades_completed and volume_traded.
// If we found those trades in the last 24 hours we decrease both variables to both users
const handleReputationItems = async (buyer, seller, amount) => {
const handleReputationItems = async (buyer: UserDocument, seller: UserDocument, amount: number) => {
try {
const yesterday = new Date(Date.now() - 86400000).toISOString();
const orders = await Order.find({
Expand All @@ -64,7 +74,7 @@ const handleReputationItems = async (buyer, seller, amount) => {
});
if (orders.length > 0) {
let totalAmount = 0;
orders.forEach(order => {
orders.forEach((order: IOrder) => {
totalAmount += order.amount;
});
const lastAmount = orders[orders.length - 1].amount;
Expand Down Expand Up @@ -118,9 +128,10 @@ const handleReputationItems = async (buyer, seller, amount) => {
}
};

const getBtcFiatPrice = async (fiatCode, fiatAmount) => {
const getBtcFiatPrice = async (fiatCode: string, fiatAmount: number) => {
try {
const currency = getCurrency(fiatCode);
if (currency === null) throw Error("Currency not found");
if (!currency.price) return;
// Before hit the endpoint we make sure the code have only 3 chars
const code = currency.code.substring(0, 3);
Expand All @@ -130,13 +141,13 @@ const getBtcFiatPrice = async (fiatCode, fiatAmount) => {
}
const sats = (fiatAmount / response.data.btc) * 100000000;

return parseInt(sats);
return Number(sats);
} catch (error) {
logger.error(error);
}
};

const getBtcExchangePrice = (fiatAmount, satsAmount) => {
const getBtcExchangePrice = (fiatAmount: number, satsAmount: number) => {
try {
const satsPerBtc = 1e8;
const feeRate = (satsPerBtc * fiatAmount) / satsAmount;
Expand All @@ -147,8 +158,8 @@ const getBtcExchangePrice = (fiatAmount, satsAmount) => {
}
};

const objectToArray = object => {
const array = [];
const objectToArray = (object: any): any[] => {
const array: any[] = [];

for (const i in object) array.push(object[i]);

Expand All @@ -162,7 +173,7 @@ const getCurrenciesWithPrice = () => {
return withPrice;
};

const getEmojiRate = rate => {
const getEmojiRate = (rate: number): string => {
const star = '⭐';
const roundedRate = Math.round(rate);
const output = [];
Expand All @@ -173,7 +184,7 @@ const getEmojiRate = rate => {

// Round number to exp decimal digits
// Source: https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Math/round#redondeo_decimal
const decimalRound = (value, exp) => {
const decimalRound = (value: number, exp: number): number => {
if (typeof exp === 'undefined' || +exp === 0) {
return Math.round(value);
}
Expand All @@ -184,27 +195,29 @@ const decimalRound = (value, exp) => {
return NaN;
}
// Shift
value = value.toString().split('e');
value = Math.round(+(value[0] + 'e' + (value[1] ? +value[1] - exp : -exp)));
let valueArr = value.toString().split('e');
value = Math.round(+(valueArr[0] + 'e' + (valueArr[1] ? +valueArr[1] - exp : -exp)));
// Shift back
value = value.toString().split('e');
return +(value[0] + 'e' + (value[1] ? +value[1] + exp : exp));
valueArr = value.toString().split('e');
return +(valueArr[0] + 'e' + (valueArr[1] ? +valueArr[1] + exp : exp));
};

const extractId = text => {
const extractId = (text: string): (string | null) => {
const matches = text.match(/:([a-f0-9]{24}):$/);

return matches[1];
if (matches !== null){
return matches?.[1];
}
return null;
};

// Clean strings that are going to be rendered with markdown
const sanitizeMD = text => {
const sanitizeMD = (text: any) => {
if (!text) return '';

return text.toString().replace(/(?=[|<>(){}[\]\-_!#.`=+])/g, '\\');
return String(text).replace(/(?=[|<>(){}[\]\-_!#.`=+])/g, '\\');
};

const secondsToTime = secs => {
const secondsToTime = (secs: number) => {
const hours = Math.floor(secs / (60 * 60));

const divisor = secs % (60 * 60);
Expand All @@ -216,9 +229,9 @@ const secondsToTime = secs => {
};
};

const isGroupAdmin = async (groupId, user, telegram) => {
const isGroupAdmin = async (groupId: string, user: UserDocument, telegram: Telegram) => {
try {
const member = await telegram.getChatMember(groupId, user.tg_id);
const member = await telegram.getChatMember({chat_id: groupId, user_id: Number(user.tg_id)});
if (
member &&
(member.status === 'creator' || member.status === 'administrator')
Expand All @@ -242,12 +255,12 @@ const isGroupAdmin = async (groupId, user, telegram) => {
logger.error(error);
return {
success: false,
message: error.toString(),
message: String(error),
};
}
};

const deleteOrderFromChannel = async (order, telegram) => {
const deleteOrderFromChannel = async (order: IOrder, telegram: Telegram) => {
try {
let channel = process.env.CHANNEL;
if (order.community_id) {
Expand All @@ -265,13 +278,13 @@ const deleteOrderFromChannel = async (order, telegram) => {
}
}
}
await telegram.deleteMessage(channel, order.tg_channel_message1);
await telegram.deleteMessage({chat_id: channel!, message_id: Number(order.tg_channel_message1!)});
} catch (error) {
logger.error(error);
}
};

const getOrderChannel = async order => {
const getOrderChannel = async (order: IOrder) => {
let channel = process.env.CHANNEL;
if (order.community_id) {
const community = await Community.findOne({ _id: order.community_id });
Expand All @@ -281,7 +294,7 @@ const getOrderChannel = async order => {
if (community.order_channels.length === 1) {
channel = community.order_channels[0].name;
} else {
community.order_channels.forEach(async c => {
community.order_channels.forEach(async (c: IOrderChannel) => {
if (c.type === order.type) {
channel = c.name;
}
Expand All @@ -292,10 +305,11 @@ const getOrderChannel = async order => {
return channel;
};

const getDisputeChannel = async order => {
const getDisputeChannel = async (order: IOrder) => {
let channel = process.env.DISPUTE_CHANNEL;
if (order.community_id) {
const community = await Community.findOne({ _id: order.community_id });
if (community === null) throw Error("Community was not found in DB");
channel = community.dispute_channel;
}

Expand All @@ -307,8 +321,13 @@ const getDisputeChannel = async order => {
* @param {*} user
* @returns i18n context
*/
const getUserI18nContext = async user => {
const language = user.language || 'en';
const getUserI18nContext = async (user: UserDocument) => {
let language = null;
if (!('language' in user)) {
language = 'en';
} else {
language = user.language;
}
const i18n = new I18n({
locale: language,
defaultLanguageOnMissing: true,
Expand All @@ -318,7 +337,7 @@ const getUserI18nContext = async user => {
return i18n.createContext(user.lang);
};

const getDetailedOrder = (i18n, order, buyer, seller) => {
const getDetailedOrder = (i18n: I18nContext, order: IOrder, buyer: UserDocument, seller: UserDocument) => {
try {
const buyerUsername = buyer ? sanitizeMD(buyer.username) : '';
const buyerReputation = buyer
Expand All @@ -336,7 +355,7 @@ const getDetailedOrder = (i18n, order, buyer, seller) => {
createdAt = sanitizeMD(createdAt);
takenAt = sanitizeMD(takenAt);
const status = sanitizeMD(order.status);
const fee = order.fee ? parseInt(order.fee) : '';
const fee = order.fee ? Number(order.fee) : '';
const creator =
order.creator_id === buyerId ? buyerUsername : sellerUsername;
const message = i18n.t('order_detail', {
Expand All @@ -361,7 +380,7 @@ const getDetailedOrder = (i18n, order, buyer, seller) => {
};

// We need to know if this user is a dispute solver for this community
const isDisputeSolver = (community, user) => {
const isDisputeSolver = (community: ICommunity, user: UserDocument) => {
if (!community || !user) {
return false;
}
Expand All @@ -371,55 +390,56 @@ const isDisputeSolver = (community, user) => {

// Return the fee the bot will charge to the seller
// this fee is a combination from the global bot fee and the community fee
const getFee = async (amount, communityId) => {
const maxFee = Math.round(amount * parseFloat(process.env.MAX_FEE));
const getFee = async (amount: number, communityId: string) => {
const maxFee = Math.round(amount * Number(process.env.MAX_FEE));
if (!communityId) return maxFee;

const botFee = maxFee * parseFloat(process.env.FEE_PERCENT);
const botFee = maxFee * Number(process.env.FEE_PERCENT);
let communityFee = Math.round(maxFee - botFee);
const community = await Community.findOne({ _id: communityId });
if (community === null) throw Error("Community was not found in DB");
communityFee = communityFee * (community.fee / 100);

return botFee + communityFee;
};

const itemsFromMessage = str => {
const itemsFromMessage = (str: string) => {
return str
.split(' ')
.map(e => e.trim())
.filter(e => !!e);
};

// Check if a number is int
const isInt = n => parseInt(n) === n;
const isInt = (n: number) => Number(n) === n;

// Check if a number is float
const isFloat = n => typeof n === 'number' && !isInt(n);
const isFloat = (n: number) => typeof n === 'number' && !Number.isInteger(n);

// Returns an emoji flag for a language
const getLanguageFlag = code => {
const getLanguageFlag = (code: string): ILanguage => {
return languages[code];
};

const delay = time => {
const delay = (time: number) => {
return new Promise(resolve => setTimeout(resolve, time));
};

// Returns the hold invoice expiration time in seconds,
// and the hold invoice safety window in seconds
const holdInvoiceExpirationInSecs = () => {
const expirationTimeInSecs =
parseInt(process.env.HOLD_INVOICE_CLTV_DELTA) * 10 * 60;
Number(process.env.HOLD_INVOICE_CLTV_DELTA) * 10 * 60;
const safetyWindowInSecs =
parseInt(process.env.HOLD_INVOICE_CLTV_DELTA_SAFETY_WINDOW) * 10 * 60;
Number(process.env.HOLD_INVOICE_CLTV_DELTA_SAFETY_WINDOW) * 10 * 60;
return {
expirationTimeInSecs,
safetyWindowInSecs,
};
};

// Returns the user age in days
const getUserAge = user => {
const getUserAge = (user: UserDocument) => {
const userCreationDate = new Date(user.created_at);
const today = new Date();
const ageInDays = Math.floor(
Expand All @@ -434,13 +454,13 @@ const getUserAge = user => {
* @param {*} i18n context
* @returns String with the remaining time to expiration in format '1 hours 30 minutes'
*/
const getTimeToExpirationOrder = (order, i18n) => {
const getTimeToExpirationOrder = (order: IOrder, i18n: I18nContext) => {
const initialDateObj = new Date(order.created_at);
const timeToExpire = parseInt(process.env.ORDER_PUBLISHED_EXPIRATION_WINDOW);
const timeToExpire = Number(process.env.ORDER_PUBLISHED_EXPIRATION_WINDOW);
initialDateObj.setSeconds(initialDateObj.getSeconds() + timeToExpire);

const currentDateObj = new Date();
const timeDifferenceMs = initialDateObj - currentDateObj;
const timeDifferenceMs = initialDateObj.valueOf() - currentDateObj.valueOf();
const totalSecondsRemaining = Math.floor(timeDifferenceMs / 1000);
const textHour = i18n.t('hours');
const textMin = i18n.t('minutes');
Expand All @@ -454,7 +474,7 @@ const getTimeToExpirationOrder = (order, i18n) => {
return `${hours} ${textHour} ${minutes} ${textMin}`;
};

module.exports = {
export {
isIso4217,
plural,
getCurrency,
Expand Down
Loading

0 comments on commit cf8be67

Please sign in to comment.