diff --git a/src/main/java/org/bot/MyBot.java b/src/main/java/org/bot/MyBot.java index db49543..8f9c089 100644 --- a/src/main/java/org/bot/MyBot.java +++ b/src/main/java/org/bot/MyBot.java @@ -4,6 +4,7 @@ import org.bot.commands.*; import org.bot.commands.base.AuthorizedCommandDecorator; import org.bot.commands.base.CommandHandler; +import org.bot.commands.base.CommandProcessor; import org.bot.operations.KucoinOperations; import org.bot.operations.OperationsDispatcher; import org.bot.utils.EnvVars; @@ -35,7 +36,7 @@ private MyBot() { commandHandler.register(new AuthorizedCommandDecorator(new BalanceCommand())); commandHandler.register(new AuthorizedCommandDecorator(new TradeCommand())); - // CommandProcessor.getInstance().registerPortfolioCommands(); + CommandProcessor.getInstance().registerPortfolioCommands(); OperationsDispatcher.getInstance().register(new KucoinOperations()); } diff --git a/src/main/java/org/bot/commands/PortfolioCommand.java b/src/main/java/org/bot/commands/PortfolioCommand.java index fbb2308..77fe4ba 100644 --- a/src/main/java/org/bot/commands/PortfolioCommand.java +++ b/src/main/java/org/bot/commands/PortfolioCommand.java @@ -5,7 +5,8 @@ import org.bot.commands.base.Command; import org.bot.models.Portfolio; import org.bot.models.PortfolioLink; -import org.bot.utils.CoingeckoFacade; +import org.bot.providers.CoinMarketCapProvider; +import org.bot.providers.DataProvider; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; @@ -40,8 +41,8 @@ public String getDescription() { private String buildPortfolioResponse() { try { - CoingeckoFacade coingeckoFacade = CoingeckoFacade.getInstance(); - Portfolio portfolio = coingeckoFacade.getCoingeckoPortfolio(getPortfolioLink().getLink()); + DataProvider provider = CoinMarketCapProvider.getInstance(); + Portfolio portfolio = provider.getPortfolio(getPortfolioLink().getLink()); portfolio.sort(); return portfolio.toString(); } catch (Exception e) { diff --git a/src/main/java/org/bot/commands/TrendingCommand.java b/src/main/java/org/bot/commands/TrendingCommand.java index edaccb6..74b931e 100644 --- a/src/main/java/org/bot/commands/TrendingCommand.java +++ b/src/main/java/org/bot/commands/TrendingCommand.java @@ -1,11 +1,16 @@ package org.bot.commands; +import lombok.extern.slf4j.Slf4j; import org.bot.commands.base.Command; import org.bot.models.Trending; -import org.bot.utils.CoingeckoFacade; +import org.bot.providers.CoingeckoProvider; +import org.bot.providers.DataProvider; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; +import java.util.Arrays; + +@Slf4j public class TrendingCommand implements Command { public TrendingCommand() { @@ -26,12 +31,17 @@ public String getName() { @Override public String getDescription() { - return "show trending coins on Coingecko"; + return "show trending coins"; } private String buildTrendingResponse() { - CoingeckoFacade coingeckoFacade = CoingeckoFacade.getInstance(); - Trending trending = coingeckoFacade.getTrendingCoins(); - return trending.toString(); + try { + DataProvider provider = CoingeckoProvider.getInstance(); + Trending trending = provider.getTrendingCoins(); + return trending.toString(); + } catch (Exception e) { + log.error(Arrays.toString(e.getStackTrace())); + return e.getMessage(); + } } } diff --git a/src/main/java/org/bot/models/factory/CoinFactory.java b/src/main/java/org/bot/models/factory/CoinFactory.java deleted file mode 100644 index a6e10a7..0000000 --- a/src/main/java/org/bot/models/factory/CoinFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.bot.models.factory; - -import lombok.extern.slf4j.Slf4j; -import org.bot.models.Coin; - -@Slf4j -public class CoinFactory { - - private CoinFactory() { - - } - - public static Coin fromRawCoin(String raw) { - Coin coin = new Coin(); - try { - coin.setCoinName(raw.split(" \\(")[0]); - coin.setTicker(raw.split("\\(")[1].split("\\)")[0]); - coin.setLink("https://www.coingecko.com" + raw.split("\"width: 115px;\" href=\"")[1].split("\"")[0]); - coin.setPrice(Double.parseDouble(raw.split("")[2].split("<")[0]); - } catch (Exception e) { - log.warn("some info for token ignored: " + e.getMessage()); - } - return coin; - } -} diff --git a/src/main/java/org/bot/providers/CoinMarketCapProvider.java b/src/main/java/org/bot/providers/CoinMarketCapProvider.java new file mode 100644 index 0000000..5d771d3 --- /dev/null +++ b/src/main/java/org/bot/providers/CoinMarketCapProvider.java @@ -0,0 +1,97 @@ +package org.bot.providers; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.http.client.HttpResponseException; +import org.bot.models.Coin; +import org.bot.models.Portfolio; +import org.bot.models.Trending; +import org.bot.utils.exceptions.CoingeckoException; +import org.bot.utils.exceptions.NotImplementedException; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +@Slf4j +public class CoinMarketCapProvider implements DataProvider { + private static CoinMarketCapProvider instance; + + private CoinMarketCapProvider() { + // Private constructor to prevent instantiation + } + + public static CoinMarketCapProvider getInstance() { + if (instance == null) { + instance = new CoinMarketCapProvider(); + } + return instance; + } + + @Override + public Portfolio getPortfolio(String url) { + Map coins = new LinkedHashMap<>(); + Response response = null; + OkHttpClient client = new OkHttpClient(); + try { + Request request = this.buildGetRequest(url); + response = client.newCall(request).execute(); + if (!response.isSuccessful()) + throw new HttpResponseException(response.code(), response.toString()); + assert response.body() != null; + String responseBody = response.body().string(); + String[] coinsRaw = responseBody.split("class=\"sc-4984dd93-0 kKpPOn\">"); + for (int i = 1; i < coinsRaw.length; i = i + 1) { + Coin coin = this.fromRawCoin(coinsRaw[i]); + if (coin.getTicker() != null) coins.put(coin.getTicker().toUpperCase(), coin); + } + } catch (Exception e) { + log.error(Arrays.toString(e.getStackTrace())); + log.error(e.toString()); + throw new CoingeckoException(); + } finally { + if (response != null) { + response.close(); + } + } + return new Portfolio(coins); + } + + @Override + public Trending getTrendingCoins() { + throw new NotImplementedException(); + } + + @Override + public Coin fromRawCoin(String raw) { + Coin coin = new Coin(); + try { + coin.setCoinName(raw.split("<")[0]); + } catch (Exception e) { + log.warn("name for token ignored: " + e); + } + try { + coin.setTicker(raw.split("click=\"true\">")[1].split("<")[0]); + } catch (Exception e) { + log.warn("ticker for token ignored: " + e); + } + try { + coin.setLink("https://coinmarketcap.com" + raw.split("\\$")[1].split("<")[0].replace(",", ""))); + } catch (Exception e) { + log.warn("price for token ignored: " + e); + } + try { + coin.setChange24(raw.split("display:inline-block\">")[2].split("<")[0]); + } catch (Exception e) { + log.warn("change24 for token ignored: " + e); + } + return coin; + } +} diff --git a/src/main/java/org/bot/utils/CoingeckoFacade.java b/src/main/java/org/bot/providers/CoingeckoProvider.java similarity index 61% rename from src/main/java/org/bot/utils/CoingeckoFacade.java rename to src/main/java/org/bot/providers/CoingeckoProvider.java index f727fe9..9436c50 100644 --- a/src/main/java/org/bot/utils/CoingeckoFacade.java +++ b/src/main/java/org/bot/providers/CoingeckoProvider.java @@ -1,4 +1,4 @@ -package org.bot.utils; +package org.bot.providers; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; @@ -9,33 +9,32 @@ import org.bot.models.Coin; import org.bot.models.Portfolio; import org.bot.models.Trending; -import org.bot.models.factory.CoinFactory; import org.bot.utils.exceptions.CoingeckoException; import java.util.*; @Slf4j -public class CoingeckoFacade { +public class CoingeckoProvider implements DataProvider { - private static CoingeckoFacade instance; + private static CoingeckoProvider instance; - private CoingeckoFacade() { + private CoingeckoProvider() { // Private constructor to prevent instantiation } - public static CoingeckoFacade getInstance() { + public static CoingeckoProvider getInstance() { if (instance == null) { - instance = new CoingeckoFacade(); + instance = new CoingeckoProvider(); } return instance; } - public Portfolio getCoingeckoPortfolio(String url) { + public Portfolio getPortfolio(String url) { Map coins = new LinkedHashMap<>(); Response response = null; OkHttpClient client = new OkHttpClient(); try { - Request request = Helpers.buildRequestCoingecko(url); + Request request = this.buildGetRequest(url); response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new HttpResponseException(response.code(), response.toString()); @@ -43,7 +42,7 @@ public Portfolio getCoingeckoPortfolio(String url) { String responseBody = response.body().string(); String[] coinsRaw = responseBody.split("\"");")[2].split("<")[0]); + } catch (Exception e) { + log.warn("some info for token ignored: " + e.getMessage()); + } + return coin; + } } diff --git a/src/main/java/org/bot/providers/DataProvider.java b/src/main/java/org/bot/providers/DataProvider.java new file mode 100644 index 0000000..2e991d6 --- /dev/null +++ b/src/main/java/org/bot/providers/DataProvider.java @@ -0,0 +1,21 @@ +package org.bot.providers; + +import okhttp3.Request; +import org.bot.models.Coin; +import org.bot.models.Portfolio; +import org.bot.models.Trending; + +public interface DataProvider { + Portfolio getPortfolio(String url); + + Trending getTrendingCoins(); + + default Request buildGetRequest(String url) { + return new Request.Builder() + .url(url) + .get() + .build(); + } + + Coin fromRawCoin(String raw); +} diff --git a/src/main/java/org/bot/tasks/NotifyPercentageChangeTask.java b/src/main/java/org/bot/tasks/NotifyPercentageChangeTask.java index 987093f..b7e1572 100644 --- a/src/main/java/org/bot/tasks/NotifyPercentageChangeTask.java +++ b/src/main/java/org/bot/tasks/NotifyPercentageChangeTask.java @@ -11,7 +11,7 @@ import org.bot.repositories.CoinNotifyRepository; import org.bot.repositories.PortfolioLinkRepository; import org.bot.tasks.base.Task; -import org.bot.utils.CoingeckoFacade; +import org.bot.providers.CoingeckoProvider; import org.bot.utils.formatters.ToBoldDecorator; import java.util.List; @@ -31,9 +31,9 @@ public void run() { if (coinsToCheck.isEmpty()) return; List portfolios = CacheFlyWeight.getInstance(PortfolioLink.class, PortfolioLinkRepository.getInstance()).findAll(); - Portfolio portfolio = null; + Portfolio portfolio; try { - portfolio = CoingeckoFacade.getInstance().getCoingeckoPortfolio(portfolios.get(0).getLink()); + portfolio = CoingeckoProvider.getInstance().getPortfolio(portfolios.get(0).getLink()); } catch (Exception e) { throw new RuntimeException(e); } @@ -56,7 +56,7 @@ public void run() { } i++; } - }catch (Exception e) { + } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/org/bot/utils/Helpers.java b/src/main/java/org/bot/utils/Helpers.java index 956075c..87aed8d 100644 --- a/src/main/java/org/bot/utils/Helpers.java +++ b/src/main/java/org/bot/utils/Helpers.java @@ -1,7 +1,5 @@ package org.bot.utils; -import okhttp3.Request; - import java.util.Map; public class Helpers { @@ -11,15 +9,4 @@ private Helpers() { public static void addToMapOrSum(Map map, String key, Double value) { map.compute(key, (k, v) -> (v == null) ? value : v + value); } - - public static Request buildRequestCoingecko(String url) { - return new Request.Builder() - .url(url) - .header("Upgrade-Insecure-Requests", "1") - .header("Sec-Fetch-Dest", "document") - .header("Sec-Fetch-Mode", "navigate") - .header("Sec-Fetch-Site", "cross-site") - .get() - .build(); - } } diff --git a/src/main/java/org/bot/visitor/ValidatingCommandVisitor.java b/src/main/java/org/bot/visitor/ValidatingCommandVisitor.java index 20e5856..9325598 100644 --- a/src/main/java/org/bot/visitor/ValidatingCommandVisitor.java +++ b/src/main/java/org/bot/visitor/ValidatingCommandVisitor.java @@ -20,8 +20,8 @@ public void visitSavePortofolioLinkCommand() { if (parts.length != 3) throw new InvalidCommandException(); String link = parts[2]; - if (!link.contains("coingecko.com") || !link.contains("portfolios/public/")) - throw new InvalidCommandException("Coingecko link not valid"); + if (!link.contains("coinmarketcap.com/watchlist")) + throw new InvalidCommandException("Portfolio link not valid"); } @Override