diff --git a/config/message.js b/config/message.js index 0a18305..8618c97 100644 --- a/config/message.js +++ b/config/message.js @@ -1,5 +1,5 @@ module.exports = { - TELEGRAM_START: 'Hello! I am {{botName}} 🤖\n\nCommand:\n/price <symbol> <interval>\n/chart <symbol> <interval> <studies>\n/example\n\nFollow me on Twitter:\n@CryptoOpnBot\n\nWebSite:\ncrypto.opnbot.com\n\nGitHub:\nsource code\n', + TELEGRAM_START: 'Hello! I am {{botName}} 🤖\n\nCommand:\n/price <symbol> <interval>\n/chart <symbol> <interval> <studies>\n/fear_greed_index\n/example\n\nFollow me on Twitter:\n@CryptoOpnBot\n\nWebSite:\ncrypto.opnbot.com\n\nGitHub:\nsource code\n', TELEGRAM_EXAMPLE: 'Example:\n\n/price\n/price tsla\n/price goog\n/price btcusdt\n/price btcusdt 1M\n/price bitfinex:ethusd\n/price bitfinex:ethusd all\n/price <symbol> <interval>\n\nSupport Intervals: 1d,1M,3M,1Y,5Y,all\n\n/chart\n/chart aapl\n/chart msft\n/chart btcusdt\n/chart btcusdt 15m\n/chart btcusdt 4h RSI;MA:50;MA:200\n/chart coinbase:ethusd 30m\n/chart coinbase:ethusd 1h MACD\n/chart <symbol> <interval> <studies>\n\nSupport Intervals: 1m,3m,5m,15m,30m,45m,1h,2h,3h,4h,1d,1w\n', TOO_MANY_REQUEST: 'Too many request. Please try again later.', INVALID_REQUEST: 'Invalid request. Try /example.', diff --git a/src/telegram.js b/src/telegram.js index 9a47548..347bfad 100644 --- a/src/telegram.js +++ b/src/telegram.js @@ -9,6 +9,7 @@ const CB_ACTION_PRICE_SYMBOL = 'price-symbol' const CB_ACTION_CHART_SYMBOL = 'chart-symbol' const CB_ACTION_CHART_INTERVAL = 'chart-interval' const CB_ACTION_CHART_STUDIES = 'chart-studies' +const CB_ACTION_FG_INDEX = 'fear-greed-index' module.exports = (log, argv, version, setting) => { const TelegramBot = require('@lib/telegram') @@ -16,8 +17,6 @@ module.exports = (log, argv, version, setting) => { const chatLimit = new NodeCache({ stdTTL: 60 }) // rate limit ttl per minute const teleBot = new TelegramBot(argv.telegramToken, { polling: true }) - const BOT_NAME = process.env.BOT_NAME || setting.BOT_NAME - log.verbose('\nRunning Telegram Server ✓') axios.defaults.headers.common['user-agent'] = `cryptoOpnBot/${version}` @@ -35,6 +34,10 @@ module.exports = (log, argv, version, setting) => { command: '/chart', description: 'Advanced Chart.', }, + { + command: '/fear_greed_index', + description: 'Crypto Market Sentiment Index', + }, { command: '/example', description: 'Command examples.', @@ -56,7 +59,7 @@ module.exports = (log, argv, version, setting) => { if (text === '/start') { return teleBot.sendMessage( chat.id, - MESSAGE.TELEGRAM_START.replace('{{botName}}', BOT_NAME), + MESSAGE.TELEGRAM_START.replace('{{botName}}', setting.BOT_NAME), { parse_mode: 'HTML', } @@ -69,7 +72,6 @@ module.exports = (log, argv, version, setting) => { .then((res) => teleBot.sendPhoto(chat.id, res.data, { caption: getPriceCaption(eSymbol), - parse_mode: 'HTML', reply_markup: { inline_keyboard: getChunkInputObjs( CB_ACTION_PRICE_SYMBOL, @@ -88,7 +90,6 @@ module.exports = (log, argv, version, setting) => { .then((res) => teleBot.sendPhoto(chat.id, res.data, { caption: getChartCaption(eSymbol), - parse_mode: 'HTML', reply_markup: { inline_keyboard: getChunkInputObjs( CB_ACTION_CHART_SYMBOL, @@ -109,7 +110,6 @@ module.exports = (log, argv, version, setting) => { .then((res) => teleBot.sendPhoto(chat.id, res.data, { caption: getPriceCaption(eSymbol, interval), - parse_mode: 'HTML', }) ) } else if (text?.startsWith('/chart')) { @@ -123,7 +123,18 @@ module.exports = (log, argv, version, setting) => { .then((res) => teleBot.sendPhoto(chat.id, res.data, { caption: getChartCaption(eSymbol, interval, splitStudies), - parse_mode: 'HTML', + }) + ) + } else if (text?.startsWith('/fear_greed_index')) { + return axios + .get(setting.CRYPTO_FEAR_GREED_INDEX_URL, { + responseType: 'arraybuffer', + }) + .then((res) => + teleBot.sendPhoto(chat.id, res.data, { + reply_markup: { + inline_keyboard: getInputReload(CB_ACTION_FG_INDEX), + }, }) ) } else if (text?.startsWith('/example')) { @@ -202,6 +213,13 @@ module.exports = (log, argv, version, setting) => { ) ) } + } else if (cbKey === CB_ACTION_FG_INDEX) { + return reqReloadEditMsgPhoto( + CB_ACTION_FG_INDEX, + setting.CRYPTO_FEAR_GREED_INDEX_URL, + chat.id, + message_id + ) } else { throw Error('Unable to get symbol from callback query.') } @@ -300,8 +318,7 @@ module.exports = (log, argv, version, setting) => { * @returns {String} price image caption */ function getPriceCaption(eSymbol, interval = null) { - const url = getPriceImgApiUrl(eSymbol, interval) // external to use default size - return `${eSymbol.toUpperCase()} ${interval || setting.DEFAULT_PRICE_INTERVAL}` // prettier-ignore + return `${eSymbol.toUpperCase()} ${interval || setting.DEFAULT_PRICE_INTERVAL}` } /** @@ -311,11 +328,11 @@ module.exports = (log, argv, version, setting) => { * @returns {String} chart image caption */ function getChartCaption(eSymbol, interval = null, studies = null) { - const url = getChartImgApiUrl(eSymbol, interval, studies) // external to use default size const dInterval = interval || setting.DEFAULT_CHART_INTERVAL const dStudies = studies || setting.DEFAULT_CHART_STUDIES const studyIds = lodash.uniq(dStudies.map((dStudy) => dStudy.split(':')[0])) - return `${eSymbol.toUpperCase()} ${dInterval} ${studyIds.toString()}` + + return `${eSymbol.toUpperCase()} ${dInterval} ${studyIds.toString()}` } /** @@ -338,7 +355,6 @@ module.exports = (log, argv, version, setting) => { .then((res) => { const opt = getEditMsgPhotoOpt(chatId, msgId, res.data, { caption: getPriceCaption(...inputs), - parse_mode: 'HTML', }) if (inputKeys) { opt.qs.reply_markup = { @@ -349,6 +365,28 @@ module.exports = (log, argv, version, setting) => { }) } + /** + * @param {String} cbKey + * @param {String} url + * @param {Integer} chatId + * @param {Integer} msgId + * @returns {Promise} + */ + function reqReloadEditMsgPhoto(cbKey, url, chatId, msgId) { + return axios + .get(url, { + responseType: 'arraybuffer', + }) + .then((res) => { + const opt = getEditMsgPhotoOpt(chatId, msgId, res.data) + + opt.qs.reply_markup = { + inline_keyboard: getInputReload(cbKey), + } + return teleBot._request('editMessageMedia', opt) + }) + } + /** * @param {Integer} chatId * @param {Integer} msgId @@ -366,7 +404,6 @@ module.exports = (log, argv, version, setting) => { .then((res) => { const opt = getEditMsgPhotoOpt(chatId, msgId, res.data, { caption: getChartCaption(...inputs), - parse_mode: 'HTML', }) if (inputKeys) { opt.qs.reply_markup = { @@ -427,6 +464,21 @@ module.exports = (log, argv, version, setting) => { ] } + /** + * @param {String} cbKey + * @returns {Array} + */ + function getInputReload(cbKey) { + return [ + [ + { + text: '↻ ' + new Date().toISOString(), // must be a unique message (400 Bad Request: message is not modified) + callback_data: cbKey, + }, + ], + ] + } + /** * generate inline keyboard inputs *