def get_action(self, bot, update, args): if not args: if update.message: update.message.reply_text(text=f"Usage:\n{self.get_usage()}", parse_mode=ParseMode.MARKDOWN) return if RateLimit.limit_reached(update): return coins = utl.get_kw(args, "coins") try: cp = CryptoCompare(token=self._token) wallets = cp.get_wallet_info() except Exception as e: return self.handle_error(e, update) if wallets["Response"] == "Error": if update.message: update.message.reply_text( text=f"{emo.ERROR} {wallets['Message']}", parse_mode=ParseMode.MARKDOWN) return found = False # Return wallets for specified coin(s) if coins: coin_list = coins.split(",") for _, wa in wallets["Data"].items(): if all(x.strip().upper() in wa["Coins"] for x in coin_list): update.message.reply_text(text=self._get_msg(wa), parse_mode=ParseMode.MARKDOWN) found = True # Return info about specified wallet(s) else: wallet = " ".join(args) for _, wa in wallets["Data"].items(): if wallet.upper() in wa["Name"].upper(): update.message.reply_text(text=self._get_msg(wa), parse_mode=ParseMode.MARKDOWN) found = True if not found: update.message.reply_text(text=f"{emo.INFO} No wallet found", parse_mode=ParseMode.MARKDOWN)
def get_action(self, bot, update, args): kw = utl.get_kw(args) limit = kw.get("limit", 5) kw.pop("limit", None) if RateLimit.limit_reached(update): return try: events = CoinGecko().get_events(**kw) except Exception as e: return self.handle_error(e, update) for i in range(int(limit)): if len(events["data"]) <= i: break event = events["data"][i] title = event["title"] event_type = event["type"] description = event["description"] organizer = event["organizer"] from_date = event["start_date"] to_date = event["end_date"] address = event["address"].strip() city = event["city"].strip() country = event["country"].strip() website = event["website"] org = f" by {organizer}" if organizer else "" msg = f"[{title}]({website})\n" \ f"{event_type}{org}\n\n" \ f"{utl.esc_md(description)}\n\n" \ f"*Date*\nStart {from_date}\nEnd {to_date}\n\n" \ f"*Location*\n{address}\n{city}\n{country}\n\n" update.message.reply_text(text=msg, parse_mode=ParseMode.MARKDOWN)
def get_action(self, bot, update, args): # TODO: Do this in every plugin keywords = utl.get_kw(args) arg_list = utl.del_kw(args) # TODO: Do this in most other plugins if not arg_list: if not keywords.get(Keyword.INLINE): update.message.reply_text(text=f"Usage:\n{self.get_usage()}", parse_mode=ParseMode.MARKDOWN) return vs_cur = str() if "-" in arg_list[0]: pair = arg_list[0].split("-", 1) vs_cur = pair[1].upper() coin = pair[0].upper() else: coin = arg_list[0].upper() exchange = str() if len(arg_list) > 1: exchange = arg_list[1] try: response = APICache.get_cg_coins_list() except Exception as e: return self.handle_error(e, update) coin_id = str() coin_name = str() for entry in response: if entry["symbol"].upper() == coin: coin_id = entry["id"] coin_name = entry["name"] break if RateLimit.limit_reached(update): return cg = CoinGecko() msg = str() if exchange: try: result = cg.get_coin_by_id(coin_id) except Exception as e: return self.handle_error(e, update) if result: vs_list = list() if vs_cur: vs_list = vs_cur.split(",") for ticker_len in result["tickers"]: if ticker_len["market"]["name"].upper() == exchange.upper( ): if ticker_len["base"] != coin: base_coin = ticker_len["base"] else: base_coin = ticker_len["target"] if vs_list: if base_coin in vs_list: price = utl.format(ticker_len["last"], force_length=True) msg += f"`{base_coin}: {price}`\n" else: price = utl.format(ticker_len["last"], force_length=True) msg += f"`{base_coin}: {price}`\n" else: if not vs_cur: if coin == "BTC": vs_cur = "ETH,USD,EUR" elif coin == "ETH": vs_cur = "BTC,USD,EUR" else: vs_cur = "BTC,ETH,USD,EUR" try: result = cg.get_simple_price(coin_id, vs_cur) except Exception as e: return self.handle_error(e, update) if result: for symbol, price in next(iter(result.values())).items(): if symbol in utl.get_fiat_list(): if decimal.Decimal( str(price)).as_tuple().exponent > -3: price = utl.format(price, decimals=2, force_length=True) else: price = utl.format(price, force_length=True) else: price = utl.format(price, force_length=True) msg += f"`{symbol.upper()}: {price}`\n" if msg: if exchange: ticker_len = 0 for line in msg.split("\n"): length = len(line[:line.find(":")]) if ticker_len < length: ticker_len = length message = str() for line in msg.split("\n"): if line: lst = line.split(" ") index = ticker_len + 2 + len(lst[1]) - len(lst[0]) price = "{1:>{0}}".format(index, lst[1]) message += f"{lst[0]}{price}\n" msg = f"`{coin} ({coin_name}) on {exchange.capitalize()}`\n\n" + message else: msg = f"`{coin} ({coin_name})`\n\n" + msg # Add link to source of data (CoinGecko) msg += f"\n[Details on CoinGecko]({self.CG_URL}{coin_id})" else: msg = f"{emo.ERROR} Can't retrieve data for *{coin}*" if keywords.get(Keyword.INLINE): return msg self.send_msg(msg, update, keywords)
def get_action(self, bot, update, args): keywords = utl.get_kw(args) arg_list = utl.del_kw(args) if not arg_list: if not keywords.get(Keyword.INLINE): update.message.reply_text(text=f"Usage:\n{self.get_usage()}", parse_mode=ParseMode.MARKDOWN) return time_frame = 72 resolution = "HOUR" base_coin = "BTC" # Coin or pair if "-" in arg_list[0]: pair = arg_list[0].split("-", 1) base_coin = pair[1].upper() coin = pair[0].upper() else: coin = arg_list[0].upper() if coin == "BTC" and base_coin == "BTC": base_coin = "USD" if coin == base_coin: update.message.reply_text( text=f"{emo.ERROR} Can't compare *{coin}* to itself", parse_mode=ParseMode.MARKDOWN) return if RateLimit.limit_reached(update): return cmc_thread = threading.Thread(target=self._get_cmc_coin_id, args=[coin]) cmc_thread.start() # Time frame if len(arg_list) > 1: if arg_list[1].lower().endswith( "m") and arg_list[1][:-1].isnumeric(): resolution = "MINUTE" time_frame = arg_list[1][:-1] elif arg_list[1].lower().endswith( "h") and arg_list[1][:-1].isnumeric(): resolution = "HOUR" time_frame = arg_list[1][:-1] elif arg_list[1].lower().endswith( "d") and arg_list[1][:-1].isnumeric(): resolution = "DAY" time_frame = arg_list[1][:-1] else: update.message.reply_text( text=f"{emo.ERROR} Argument *{arg_list[1]}* is invalid", parse_mode=ParseMode.MARKDOWN) return try: if resolution == "MINUTE": ohlcv = CryptoCompare().get_historical_ohlcv_minute( coin, base_coin, time_frame) elif resolution == "HOUR": ohlcv = CryptoCompare().get_historical_ohlcv_hourly( coin, base_coin, time_frame) elif resolution == "DAY": ohlcv = CryptoCompare().get_historical_ohlcv_daily( coin, base_coin, time_frame) else: ohlcv = CryptoCompare().get_historical_ohlcv_hourly( coin, base_coin, time_frame) except Exception as e: return self.handle_error(e, update) if ohlcv["Response"] == "Error": if ohlcv["Message"] == "limit is larger than max value.": update.message.reply_text( text=f"{emo.ERROR} Time frame can't be larger " f"then *{con.CG_DATA_LIMIT}* data points", parse_mode=ParseMode.MARKDOWN) return elif ohlcv["Message"].startswith( "There is no data for the symbol"): ohlcv = None else: update.message.reply_text( text=f"{emo.ERROR} CoinGecko ERROR: {ohlcv['Message']}", parse_mode=ParseMode.MARKDOWN) return ohlcv = ohlcv["Data"] if ohlcv: try: o = [value["open"] for value in ohlcv] h = [value["high"] for value in ohlcv] l = [value["low"] for value in ohlcv] c = [value["close"] for value in ohlcv] t = [value["time"] for value in ohlcv] except: return self.handle_error(f"No OHLC data for {coin}", update) if not ohlcv or utl.all_same(o, h, l, c): if base_coin != "BTC" and base_coin != "USD": update.message.reply_text( text=f"{emo.ERROR} Base currency for " f"*{coin}* can only be *BTC* or *USD*", parse_mode=ParseMode.MARKDOWN) return # Time frame if len(arg_list) > 1: if resolution != "DAY": update.message.reply_text( text=f"{emo.ERROR} Timeframe for *{coin}* " f"can only be specified in days", parse_mode=ParseMode.MARKDOWN) return else: time_frame = int(time_frame) else: time_frame = 60 # Days try: cp_ohlc = APICache.get_cp_coin_list() except Exception as e: return self.handle_error(e, update) for c in cp_ohlc: if c["symbol"] == coin: # Current datetime in seconds t_now = time.time() # Convert chart time span to seconds time_frame = time_frame * 24 * 60 * 60 # Start datetime for chart in seconds t_start = t_now - int(time_frame) try: ohlcv = CoinPaprika().get_historical_ohlc( c["id"], int(t_start), end=int(t_now), quote=base_coin.lower(), limit=366) except Exception as e: return self.handle_error(e, update) cp_api = True break if not ohlcv: update.message.reply_text( text=f"{emo.ERROR} No OHLC data for *{coin}* " f"available or time frame too big", parse_mode=ParseMode.MARKDOWN) return try: o = [value["open"] for value in ohlcv] h = [value["high"] for value in ohlcv] l = [value["low"] for value in ohlcv] c = [value["close"] for value in ohlcv] t = [ time.mktime(dau.parse(value["time_close"]).timetuple()) for value in ohlcv ] except: return self.handle_error(f"No OHLC data for {coin}", update) margin_l = 140 tickformat = "0.8f" max_value = max(h) if max_value > 0.9: if max_value > 999: margin_l = 120 tickformat = "0,.0f" else: margin_l = 125 tickformat = "0.2f" try: fig = fif.create_candlestick(o, h, l, c, pd.to_datetime(t, unit='s')) except Exception as e: return self.handle_error(e, update) fig['layout']['yaxis'].update(tickformat=tickformat, tickprefix=" ", ticksuffix=f" ") fig['layout'].update(title=dict(text=coin, font=dict(size=26)), yaxis=dict(title=dict(text=base_coin, font=dict(size=18)), )) fig['layout'].update(shapes=[{ "type": "line", "xref": "paper", "yref": "y", "x0": 0, "x1": 1, "y0": c[len(c) - 1], "y1": c[len(c) - 1], "line": { "color": "rgb(50, 171, 96)", "width": 1, "dash": "dot" } }]) fig['layout'].update(paper_bgcolor='rgb(233,233,233)', plot_bgcolor='rgb(233,233,233)', autosize=False, width=800, height=600, margin=go.layout.Margin(l=margin_l, r=50, b=85, t=100, pad=4)) cmc_thread.join() fig['layout'].update(images=[ dict(source=f"{con.CMC_LOGO_URL_PARTIAL}{self.cmc_coin_id}.png", opacity=0.8, xref="paper", yref="paper", x=1.05, y=1, sizex=0.2, sizey=0.2, xanchor="right", yanchor="bottom") ]) self.send_photo( io.BufferedReader(BytesIO(pio.to_image(fig, format='jpeg'))), update, keywords)
def get_action(self, bot, update, args): keywords = utl.get_kw(args) arg_list = utl.del_kw(args) if not arg_list: if not keywords.get(Keyword.INLINE): update.message.reply_text(text=f"Usage:\n{self.get_usage()}", parse_mode=ParseMode.MARKDOWN) return if RateLimit.limit_reached(update): return coin = arg_list[0].upper() data = None cgid = None try: response = APICache.get_cg_coins_list() except Exception as e: return self.handle_error(e, update) # Get coin ID and data for entry in response: if entry["symbol"].upper() == coin: try: data = CoinGecko().get_coin_by_id(entry["id"]) except Exception as e: return self.handle_error(e, update) cgid = entry["id"] break if not data: update.message.reply_text(text=f"{emo.ERROR} No data for *{coin}*", parse_mode=ParseMode.MARKDOWN) return name = data["name"] symbol = data["symbol"].upper() rank_mc = data["market_cap_rank"] rank_cg = data["coingecko_rank"] cs = int(float(data['market_data']['circulating_supply'])) sup_c = f"{utl.format(cs)} {symbol}" if data["market_data"]["total_supply"]: ts = int(float(data["market_data"]["total_supply"])) sup_t = f"{utl.format(ts)} {symbol}" else: sup_t = "N/A" usd = data["market_data"]["current_price"]["usd"] eur = data["market_data"]["current_price"]["eur"] btc = data["market_data"]["current_price"]["btc"] eth = data["market_data"]["current_price"]["eth"] p_usd = utl.format(usd, force_length=True) p_eur = utl.format(eur, force_length=True, template=p_usd) p_btc = utl.format(btc, force_length=True, template=p_usd) p_eth = utl.format(eth, force_length=True, template=p_usd) p_usd = "{:>12}".format(p_usd) p_eur = "{:>12}".format(p_eur) p_btc = "{:>12}".format(p_btc) p_eth = "{:>12}".format(p_eth) # Do not display BTC or ETH price if coin is BTC or ETH btc_str = "" if coin == "BTC" else f"BTC {p_btc}\n" eth_str = "" if coin == "ETH" else f"ETH {p_eth}\n" v_24h = utl.format( int(float(data["market_data"]["total_volume"]["usd"]))) m_cap = utl.format(int(float( data["market_data"]["market_cap"]["usd"]))) if data["market_data"]["price_change_percentage_1h_in_currency"]: c_1h = data["market_data"][ "price_change_percentage_1h_in_currency"]["usd"] c1h = utl.format(float(c_1h), decimals=2, force_length=True) h1 = "{:>10}".format(f"{c1h}%") else: h1 = "{:>10}".format("N/A") if data["market_data"]["price_change_percentage_24h_in_currency"]: c_1d = data["market_data"][ "price_change_percentage_24h_in_currency"]["usd"] c1d = utl.format(float(c_1d), decimals=2, force_length=True) d1 = "{:>10}".format(f"{c1d}%") else: d1 = "{:>10}".format("N/A") if data["market_data"]["price_change_percentage_7d_in_currency"]: c_1w = data["market_data"][ "price_change_percentage_7d_in_currency"]["usd"] c1w = utl.format(float(c_1w), decimals=2, force_length=True) w1 = "{:>10}".format(f"{c1w}%") else: w1 = "{:>10}".format("N/A") if data["market_data"]["price_change_percentage_30d_in_currency"]: c_1m = data["market_data"][ "price_change_percentage_30d_in_currency"]["usd"] c1m = utl.format(float(c_1m), decimals=2, force_length=True) m1 = "{:>10}".format(f"{c1m}%") else: m1 = "{:>10}".format("N/A") if data["market_data"]["price_change_percentage_1y_in_currency"]: c_1y = data["market_data"][ "price_change_percentage_1y_in_currency"]["usd"] c1y = utl.format(float(c_1y), decimals=2, force_length=True) y1 = "{:>10}".format(f"{c1y}%") else: y1 = "{:>10}".format("N/A") msg = f"`" \ f"{name} ({symbol})\n\n" \ f"USD {p_usd}\n" \ f"EUR {p_eur}\n" \ f"{btc_str}" \ f"{eth_str}\n" \ f"Hour {h1}\n" \ f"Day {d1}\n" \ f"Week {w1}\n" \ f"Month {m1}\n" \ f"Year {y1}\n\n" \ f"Market Cap Rank: {rank_mc}\n" \ f"Coin Gecko Rank: {rank_cg}\n\n" \ f"Volume 24h: {v_24h} USD\n" \ f"Market Cap: {m_cap} USD\n" \ f"Circ. Supp: {sup_c}\n" \ f"Total Supp: {sup_t}\n\n" \ f"`" \ f"Stats on [CoinGecko](https://www.coingecko.com/en/coins/{cgid}) & " \ f"[Coinlib](https://coinlib.io/coin/{coin}/{coin})" if keywords.get(Keyword.INLINE): return msg self.send_msg(msg, update, keywords)
def get_action(self, bot, update, args): keywords = utl.get_kw(args) arg_list = utl.del_kw(args) if not arg_list: if not keywords.get(Keyword.INLINE): update.message.reply_text( text=f"Usage:\n{self.get_usage()}", parse_mode=ParseMode.MARKDOWN) return if RateLimit.limit_reached(update): return vs_cur = "usd" if "-" in arg_list[0]: pair = arg_list[0].split("-", 1) vs_cur = pair[1].lower() coin = pair[0].upper() else: coin = arg_list[0].upper() ath_date = str() ath_price = str() cur_price = str() ath_change = str() # Get coin ID try: response = APICache.get_cg_coins_list() except Exception as e: return self.handle_error(e, update) for entry in response: if entry["symbol"].lower() == coin.lower(): try: coin_info = CoinGecko().get_coin_by_id(entry["id"]) except Exception as e: return self.handle_error(e, update) cur_price = coin_info["market_data"]["current_price"] ath_price = coin_info["market_data"]["ath"] ath_date = coin_info["market_data"]["ath_date"] ath_change = coin_info["market_data"]["ath_change_percentage"] break msg = str() for c in vs_cur.split(","): if c in ath_price: ath_p = format(ath_price[c]) cur_p = format(cur_price[c], template=ath_p) change = format(ath_change[c], decimals=2) date_time = ath_date[c] date_ath = date_time[:10] date_list = date_ath.split("-") y = int(date_list[0]) m = int(date_list[1]) d = int(date_list[2]) ath = date(y, m, d) now = datetime.date.today() ath_p_str = f"Price ATH: {ath_p} {c.upper()}\n" cur_p_str = f"Price NOW: {cur_p.rjust(len(ath_p))} {c.upper()}\n" msg += f"`" \ f"{date_ath} ({(now - ath).days} days ago)\n" \ f"{ath_p_str}" \ f"{cur_p_str}" \ f"Change: {change}%\n\n" \ f"`" if msg: msg = f"`All-Time High for {coin}`\n\n {msg}" else: msg = f"{emo.ERROR} Can't retrieve data for *{coin}*" if keywords.get(Keyword.INLINE): return msg self.send_msg(msg, update, keywords)
def get_action(self, bot, update, args): restart = False force = False check = False if "force" in args: force = True if "check" in args: check = True if "restart" in args: restart = True if force and check: msg = f"{emo.ERROR} Combination of 'force' " \ f"and 'check' arguments not allowed" update.message.reply_text(msg) return kw = utl.get_kw(args) branch = kw.get("branch", None) release = kw.get("release", None) if branch and release: msg = f"{emo.ERROR} Combination of 'branch' " \ f"and 'release' arguments not allowed" update.message.reply_text(msg) return msg = f"{emo.WAIT} Check for update..." m = update.message.reply_text(msg) user = Cfg.get('update', 'github_user') repo = Cfg.get('update', 'github_repo') gh = GitHub(github_user=user, github_repo=repo) uid = update.message.from_user.id download_url = str() try: # Clean old update data if present shutil.rmtree(os.path.join(os.getcwd(), con.UPD_DIR)) except: pass # ---------- BRANCH ---------- if branch: try: # Get latest commit info for branch response = gh.get_latest_branch(branch) except Exception as e: return self.handle_error(e, update) cfg_hash = Cfg.get("update", "update_hash") new_hash = response["commit"]["sha"] msg = f"{emo.CHECK} Check for update..." bot.edit_message_text(msg, chat_id=uid, message_id=m.message_id) if cfg_hash == new_hash and not force: msg = f"{emo.CHECK} You are already running the latest version" update.message.reply_text(msg) return if check: msg = f"{emo.CHECK} New branch commits available!" update.message.reply_text(msg) return # Get latest version of branch as zip download_url = f"https://github.com/{user}/{repo}/archive/{branch}.zip" # ---------- RELEASE ---------- else: try: if release: # Get specific release response = gh.get_releases() else: # Get latest release response = gh.get_latest_release() except Exception as e: return self.handle_error(e, update) if release: tag = response[0]["tag_name"] release_notes = response[0]["body"] else: tag = response["tag_name"] release_notes = response["body"] try: response = gh.get_tags() except Exception as e: return self.handle_error(e, update) new_hash = str() for t in response: if t["name"] == tag: new_hash = t["commit"]["sha"] download_url = t["zipball_url"] break if not new_hash: msg = f"{emo.ERROR} Tag '{tag}' unknown" update.message.reply_text(msg) return cfg_hash = Cfg.get("update", "update_hash") msg = f"{emo.CHECK} Check for update..." bot.edit_message_text(msg, chat_id=uid, message_id=m.message_id) if cfg_hash == new_hash and not force: msg = f"{emo.CHECK} You are already running this release" update.message.reply_text(msg) return if check: msg = f"{emo.CHECK} New release *{tag}* available!\n\n" \ f"*Release Notes*\n{release_notes}" update.message.reply_text(msg, parse_mode=ParseMode.MARKDOWN) return # ---------- DOWNLOAD & UPDATE ---------- msg = f"{emo.WAIT} Downloading update..." m = update.message.reply_text(msg) try: response = requests.get(download_url) response.raise_for_status() except Exception as e: return self.handle_error(e, update) msg = f"{emo.CHECK} Downloading update..." bot.edit_message_text(msg, chat_id=uid, message_id=m.message_id) msg = f"{emo.WAIT} Updating bot..." m = update.message.reply_text(msg) zip_file = zipfile.ZipFile(io.BytesIO(response.content)) zip_file.extractall(con.UPD_DIR) done = False unzip_dir = str() for _, dirs, _ in os.walk(con.UPD_DIR): for d in dirs: unzip_dir = d done = True break if done: break self._update_bot(os.path.join(con.UPD_DIR, unzip_dir)) Cfg.set(new_hash, "update", "update_hash") msg = f"{emo.CHECK} Updating bot..." bot.edit_message_text(msg, chat_id=uid, message_id=m.message_id) if restart: msg = f"{emo.WAIT} Restarting bot..." update.message.reply_text(msg) time.sleep(0.2) os.execl(sys.executable, sys.executable, *sys.argv) else: msg = "Bot /restart needed" update.message.reply_text(msg)
def get_action(self, bot, update, args): keywords = utl.get_kw(args) arg_list = utl.del_kw(args) if arg_list: t = arg_list[0].lower() if not t == "hour" and not t == "day": if not keywords.get(Keyword.INLINE): update.message.reply_text( text= f"{emo.ERROR} First argument has to be `day` or `hour`", parse_mode=ParseMode.MARKDOWN) return if len(arg_list) > 1: entries = arg_list[1] if not entries.isnumeric(): if not keywords.get(Keyword.INLINE): update.message.reply_text( text=f"{emo.ERROR} Second argument (# of positions " f"to display) has to be a number", parse_mode=ParseMode.MARKDOWN) return if len(arg_list) > 2: entries = arg_list[2] if not entries.isnumeric(): if not keywords.get(Keyword.INLINE): update.message.reply_text( text=f"{emo.ERROR} Third argument (min. volume) " f"has to be a number", parse_mode=ParseMode.MARKDOWN) return if RateLimit.limit_reached(update): return period = CoinData.HOUR volume = None entries = 10 if arg_list: # Period if arg_list[0].lower() == "hour": period = CoinData.HOUR elif arg_list[0].lower() == "day": period = CoinData.DAY else: period = CoinData.HOUR # Entries if len(arg_list) > 1 and arg_list[1].isnumeric(): entries = int(arg_list[1]) # Volume if len(arg_list) > 2 and arg_list[2].isnumeric(): volume = int(arg_list[2]) try: best = CoinData().get_movers(CoinData.BEST, period=period, entries=entries, volume=volume) except Exception as e: return self.handle_error(e, update) if not best: if not keywords.get(Keyword.INLINE): update.message.reply_text( text=f"{emo.INFO} No matching data found", parse_mode=ParseMode.MARKDOWN) return msg = str() for coin in best: name = coin["Name"] symbol = coin["Symbol"] desc = f"{name} ({symbol})" if len(desc) > self.DESC_LEN: desc = f"{desc[:self.DESC_LEN-3]}..." if period == CoinData.HOUR: change = coin["Change_1h"] else: change = coin["Change_24h"] change = utl.format(change, decimals=2, force_length=True) change = "{1:>{0}}".format(self.DESC_LEN + 9 - len(desc), change) msg += f"`{desc}{change}%`\n" vol = str() if volume: vol = f" (vol > {utl.format(volume)})" msg = f"`Best movers 1{period.lower()[:1]}{vol}\n\n`{msg}" if keywords.get(Keyword.INLINE): return msg self.send_msg(msg, update, keywords)
def get_action(self, bot, update, args): keywords = utl.get_kw(args) arg_list = utl.del_kw(args) if not arg_list: if not keywords.get(Keyword.INLINE): update.message.reply_text(text=f"Usage:\n{self.get_usage()}", parse_mode=ParseMode.MARKDOWN) return time_frame = 3 # Days base_coin = "BTC" if "-" in arg_list[0]: pair = arg_list[0].split("-", 1) base_coin = pair[1].upper() coin = pair[0].upper() else: coin = arg_list[0].upper() if coin == "BTC" and base_coin == "BTC": base_coin = "USD" if coin == base_coin: update.message.reply_text( text=f"{emo.ERROR} Can't compare *{coin}* to itself", parse_mode=ParseMode.MARKDOWN) return if len(arg_list) > 1 and arg_list[1].isnumeric(): time_frame = arg_list[1] if RateLimit.limit_reached(update): return cg_thread = threading.Thread(target=self._get_cg_coin_id, args=[coin]) cmc_thread = threading.Thread(target=self._get_cmc_coin_id, args=[coin]) cg_thread.start() cmc_thread.start() cg_thread.join() if not self.cg_coin_id: update.message.reply_text( text=f"{emo.ERROR} Can't retrieve data for *{coin}*", parse_mode=ParseMode.MARKDOWN) return try: market = CoinGecko().get_coin_market_chart_by_id( self.cg_coin_id, base_coin.lower(), time_frame) except Exception as e: return self.handle_error(e, update) # Volume df_volume = DataFrame(market["total_volumes"], columns=["DateTime", "Volume"]) df_volume["DateTime"] = pd.to_datetime(df_volume["DateTime"], unit="ms") volume = go.Scatter(x=df_volume.get("DateTime"), y=df_volume.get("Volume"), name="Volume") # Price df_price = DataFrame(market["prices"], columns=["DateTime", "Price"]) df_price["DateTime"] = pd.to_datetime(df_price["DateTime"], unit="ms") price = go.Scatter(x=df_price.get("DateTime"), y=df_price.get("Price"), yaxis="y2", name="Price", line=dict(color=("rgb(22, 96, 167)"), width=2)) cmc_thread.join() if not self.cmc_coin_id: update.message.reply_text( text=f"{emo.ERROR} Can't retrieve data for *{coin}*", parse_mode=ParseMode.MARKDOWN) return margin_l = 140 tickformat = "0.8f" max_value = df_price["Price"].max() if max_value > 0.9: if max_value > 999: margin_l = 110 tickformat = "0,.0f" else: margin_l = 115 tickformat = "0.2f" layout = go.Layout( images=[ dict( source=f"{con.CMC_LOGO_URL_PARTIAL}{self.cmc_coin_id}.png", opacity=0.8, xref="paper", yref="paper", x=1.05, y=1, sizex=0.2, sizey=0.2, xanchor="right", yanchor="bottom") ], paper_bgcolor='rgb(233,233,233)', plot_bgcolor='rgb(233,233,233)', autosize=False, width=800, height=600, margin=go.layout.Margin(l=margin_l, r=50, b=85, t=100, pad=4), yaxis=dict(domain=[0, 0.20]), yaxis2=dict(title=dict(text=base_coin, font=dict(size=18)), domain=[0.25, 1], tickprefix=" ", ticksuffix=f" "), title=dict(text=coin, font=dict(size=26)), legend=dict(orientation="h", yanchor="top", xanchor="center", y=1.05, x=0.45), shapes=[{ "type": "line", "xref": "paper", "yref": "y2", "x0": 0, "x1": 1, "y0": market["prices"][len(market["prices"]) - 1][1], "y1": market["prices"][len(market["prices"]) - 1][1], "line": { "color": "rgb(50, 171, 96)", "width": 1, "dash": "dot" } }], ) try: fig = go.Figure(data=[price, volume], layout=layout) except Exception as e: return self.handle_error(e, update) fig["layout"]["yaxis2"].update(tickformat=tickformat) self.send_photo( io.BufferedReader(BytesIO(pio.to_image(fig, format='jpeg'))), update, keywords)