def __init__(self, bot): with open('config.json') as config: self.config_data = json.load(config) self.bot = bot self.started = False self.market_list = None self.market_stats = None self.acronym_list = None self.top_five = [] self.top_five_gains = [] self.top_five_losses = [] self.coin_market = CoinMarket(self.config_data["cmc_api_key"]) self.server_data = self._check_server_file() self.cmc = CoinMarketFunctionality(bot, self.coin_market, self.server_data) self.alert = AlertFunctionality(bot, self.coin_market, self.config_data["alert_capacity"], self.server_data) self.subscriber = SubscriberFunctionality(bot, self.coin_market, self.config_data["subscriber_capacity"], self.server_data) # self.cal = CalFunctionality(bot, # self.config_data, # self.server_data) self.misc = MiscFunctionality(bot, self.server_data) self._save_server_file(self.server_data, backup=True) self.bot.loop.create_task(self._continuous_updates())
def __init__(self, bot): with open('config.json') as config: self.config_data = json.load(config) self.bot = bot self.coin_market = CoinMarket() self.live_on = False self.crypto_acronyms = None if self.config_data['load_acronyms']: print("Loading cryptocurrency acronyms..") self.acronym_list = self._load_acronyms()
def __init__(self, bot): self.supported_operators = ["<", ">", "<=", ">="] self.subscriber_data = self._check_subscriber_file() self.alert_data = self._check_alert_file() self.bot = bot self.market_list = None self.market_stats = None self.coin_market = CoinMarket() self.live_on = False asyncio. async (self._update_game_status_()) asyncio. async (self._continuous_updates())
def __init__(self, bot): with open('config.json') as config: self.config_data = json.load(config) self.bot = bot self.started = False self.market_list = None self.market_stats = None self.acronym_list = None self.coin_market = CoinMarket() self.cmc = CoinMarketFunctionality(bot, self.coin_market) self.alert = AlertFunctionality(bot, self.coin_market, self.config_data["alert_capacity"]) self.subscriber = SubscriberFunctionality( bot, self.coin_market, self.config_data["subscriber_capacity"]) self.bot.loop.create_task(self._continuous_updates())
class CoreFunctionality: """Handles Core functionality""" def __init__(self, bot): with open('config.json') as config: self.config_data = json.load(config) self.bot = bot self.started = False self.market_list = None self.market_stats = None self.acronym_list = None self.coin_market = CoinMarket() self.cmc = CoinMarketFunctionality(bot, self.coin_market) self.alert = AlertFunctionality(bot, self.coin_market, self.config_data["alert_capacity"]) self.subscriber = SubscriberFunctionality( bot, self.coin_market, self.config_data["subscriber_capacity"]) self.bot.loop.create_task(self._continuous_updates()) async def _update_data(self, minute=0): try: await self._update_market() self._load_acronyms() self.cmc.update(self.market_list, self.acronym_list, self.market_stats) self.alert.update(self.market_list, self.acronym_list) self.subscriber.update(self.market_list, self.acronym_list) await self.update_game_status() await self.alert.alert_user() if self.started: await self.subscriber.display_live_data(minute) except Exception as e: print("Failed to update data. See error.log.") logger.error("Exception: {}".format(str(e))) async def update_game_status(self): """ Updates the game status of the bot """ try: game_status = discord.Game(name="$updates to see log") await self.bot.change_presence(game=game_status) except Exception as e: print("Failed to update game status. See error.log.") logger.error("Exception: {}".format(str(e))) async def _continuous_updates(self): await self._update_data() self.started = True print('CoinMarketDiscordBot is online.') logger.info('Bot is online.') while True: time = datetime.datetime.now() if time.minute % 5 == 0: await self._update_data(time.minute) await asyncio.sleep(60) else: await asyncio.sleep(20) async def _update_market(self): """ Loads all the cryptocurrencies that exist in the market @return - list of crypto-currencies """ try: retry_count = 0 market_stats = self.coin_market.fetch_coinmarket_stats() currency_data = self.coin_market.fetch_currency_data(load_all=True) while market_stats is None or currency_data is None: if retry_count >= 10: msg = ("Max retry attempts reached. Please make " "sure you're able to access coinmarketcap " "through their website, check if the coinmarketapi " "is down, and check if " "anything is blocking you from requesting " "data.") raise CoreFunctionalityException(msg) logger.warning("Retrying to get data..") if market_stats is None: market_stats = self.coin_market.fetch_coinmarket_stats() if currency_data is None: currency_data = self.coin_market.fetch_currency_data( load_all=True) retry_count += 1 await asyncio.sleep(5) market_dict = {} for currency in currency_data: market_dict[currency['id']] = currency self.market_stats = market_stats self.market_list = market_dict except CoreFunctionalityException as e: logger.error(str(e)) except Exception as e: print("Failed to update market. See error.log.") logger.error("Exception: {}".format(str(e))) def _load_acronyms(self): """ Loads all acronyms of existing crypto-coins out there @return - list of crypto-acronyms """ try: if self.market_list is None: raise Exception("Market list was not loaded.") acronym_list = {} duplicate_list = {} for currency, data in self.market_list.items(): if data['symbol'] in acronym_list: if data['symbol'] not in duplicate_list: duplicate_list[data['symbol']] = 1 duplicate_list[data['symbol']] += 1 if data['symbol'] not in acronym_list[data['symbol']]: acronym_list[data['symbol'] + '1'] = acronym_list[data['symbol']] acronym_list[data['symbol']] = ( "Duplicate acronyms " "found. Possible " "searches are:\n" "{}1 ({})\n".format(data['symbol'], acronym_list[data['symbol']])) dupe_key = data['symbol'] + str( duplicate_list[data['symbol']]) acronym_list[dupe_key] = currency acronym_list[data['symbol']] = ( acronym_list[data['symbol']] + "{} ({})\n".format(dupe_key, currency)) else: acronym_list[data['symbol']] = currency self.acronym_list = acronym_list except Exception as e: print("Failed to load cryptocurrency acronyms. See error.log.") logger.error("Exception: {}".format(str(e)))
class CoreFunctionality: """Handles Core functionality""" def __init__(self, bot): with open('config.json') as config: self.config_data = json.load(config) self.bot = bot self.started = False self.market_list = None self.market_stats = None self.acronym_list = None self.coin_market = CoinMarket() self.server_data = self._check_server_file() self.cmc = CoinMarketFunctionality(bot, self.coin_market, self.server_data) self.alert = AlertFunctionality(bot, self.coin_market, self.config_data["alert_capacity"], self.server_data) self.subscriber = SubscriberFunctionality( bot, self.coin_market, self.config_data["subscriber_capacity"], self.server_data) self.misc = MiscFunctionality(bot, self.server_data) self._save_server_file(self.server_data, backup=True) self.bot.loop.create_task(self._continuous_updates()) def _check_server_file(self): """ Checks to see if there's a valid server_settings.json file """ try: with open('server_settings.json') as settings: return json.load(settings) except FileNotFoundError: self._save_server_file() return json.loads('{}') except Exception as e: print("Unable to load server file. See error.log.") logger.error("Exception: {}".format(str(e))) def _save_server_file(self, server_data={}, backup=False): """ Saves server_settings.json file """ if backup: server_settings_filename = "server_settings_backup.json" else: server_settings_filename = "server_settings.json" with open(server_settings_filename, 'w') as outfile: json.dump(server_data, outfile, indent=4) def _update_server_data(self): try: self.cmc.update(server_data=self.server_data) self.alert.update(server_data=self.server_data) self.subscriber.update(server_data=self.server_data) self.misc.update(server_data=self.server_data) except Exception as e: print("Failed to update server data. See error.log.") logger.error("Exception: {}".format(str(e))) async def _update_data(self, minute=0): try: await self._update_market() self._load_acronyms() self.cmc.update(self.market_list, self.acronym_list, self.market_stats) self.alert.update(self.market_list, self.acronym_list) self.subscriber.update(self.market_list, self.acronym_list) await self._update_game_status() await self.alert.alert_user() if self.started: await self.subscriber.display_live_data(minute) except Exception as e: print("Failed to update data. See error.log.") logger.error("Exception: {}".format(str(e))) async def _update_game_status(self): """ Updates the game status of the bot """ try: game_status = discord.Game(name="$updates to see log") await self.bot.change_presence(game=game_status) except Exception as e: print("Failed to update game status. See error.log.") logger.error("Exception: {}".format(str(e))) async def _continuous_updates(self): await self._update_data() self.started = True print('CoinMarketDiscordBot is online.') logger.info('Bot is online.') while True: time = datetime.datetime.now() if time.minute % 5 == 0: await self._update_data(time.minute) await asyncio.sleep(60) else: await asyncio.sleep(20) async def _update_market(self): """ Loads all the cryptocurrencies that exist in the market @return - list of crypto-currencies """ try: retry_count = 0 market_stats = self.coin_market.fetch_coinmarket_stats() currency_data = self.coin_market.fetch_currency_data(load_all=True) while market_stats is None or currency_data is None: if retry_count >= 10: msg = ("Max retry attempts reached. Please make " "sure you're able to access coinmarketcap " "through their website, check if the coinmarketapi " "is down, and check if " "anything is blocking you from requesting " "data.") raise CoreFunctionalityException(msg) logger.warning("Retrying to get data..") if market_stats is None: market_stats = self.coin_market.fetch_coinmarket_stats() if currency_data is None: currency_data = self.coin_market.fetch_currency_data( load_all=True) retry_count += 1 await asyncio.sleep(5) market_dict = {} for currency in currency_data: market_dict[currency['id']] = currency self.market_stats = market_stats self.market_list = market_dict except CoreFunctionalityException as e: logger.error(str(e)) except Exception as e: print("Failed to update market. See error.log.") logger.error("Exception: {}".format(str(e))) def _load_acronyms(self): """ Loads all acronyms of existing crypto-coins out there @return - list of crypto-acronyms """ try: if self.market_list is None: raise Exception("Market list was not loaded.") acronym_list = {} duplicate_list = {} for currency, data in self.market_list.items(): if data['symbol'] in acronym_list: if data['symbol'] not in duplicate_list: duplicate_list[data['symbol']] = 1 duplicate_list[data['symbol']] += 1 if data['symbol'] not in acronym_list[data['symbol']]: acronym_list[data['symbol'] + '1'] = acronym_list[data['symbol']] acronym_list[data['symbol']] = ( "Duplicate acronyms " "found. Possible " "searches are:\n" "{}1 ({})\n".format(data['symbol'], acronym_list[data['symbol']])) dupe_key = data['symbol'] + str( duplicate_list[data['symbol']]) acronym_list[dupe_key] = currency acronym_list[data['symbol']] = ( acronym_list[data['symbol']] + "{} ({})\n".format(dupe_key, currency)) else: acronym_list[data['symbol']] = currency self.acronym_list = acronym_list except Exception as e: print("Failed to load cryptocurrency acronyms. See error.log.") logger.error("Exception: {}".format(str(e))) async def _say_msg(self, msg=None, channel=None, emb=None): """ Bot will say msg if given correct permissions @param msg - msg to say @param channel - channel to send msg to @param emb - embedded msg to say """ try: if channel: if emb: await self.bot.send_message(channel, embed=emb) else: await self.bot.send_message(channel, msg) else: if emb: await self.bot.say(embed=emb) else: await self.bot.say(msg) except: pass async def display_server_settings(self, ctx): """ Displays server settings of cmds the admins have enabled """ try: try: ctx.message.channel.server except: await self._say_msg("Not a valid server to retrieve settings.") return msg = '' server_id = ctx.message.server.id if server_id not in self.server_data: await self._say_msg("No settings to display.") return elif len(self.server_data[server_id]) == 0: await self._say_msg("No settings to display.") return for setting in self.server_data[server_id]: setting_line = "{}\n".format(setting) msg += setting_line em = discord.Embed(title="Server Settings", description=msg, colour=0xFFFFFF) await self._say_msg(emb=em) except Exception as e: print("Failed to display server settings. See error.log.") logger.error("Exception: {}".format(str(e))) async def toggle_commands(self, ctx, mode): """ Toggles the command mode on/off """ try: try: user_roles = ctx.message.author.roles except: await self._say_msg("Command must be used in a server.") return if CMB_ADMIN not in [role.name for role in user_roles]: await self._say_msg("Admin role '{}' is required for " "this command.".format(CMB_ADMIN)) return channel = ctx.message.channel.id try: server = self.bot.get_channel( channel).server # validate channel except: await self._say_msg("Not a valid server to toggle mode.") return if server.id not in self.server_data: self.server_data[server.id] = [mode] await self._say_msg("Server set '{}'.".format(mode)) elif mode in self.server_data[server.id]: self.server_data[server.id].remove(mode) await self._say_msg("'{}' has been taken off.".format(mode)) elif mode not in self.server_data[server.id]: self.server_data[server.id].append(mode) await self._say_msg("Server set '{}'.".format(mode)) self._save_server_file(self.server_data) self._update_server_data() except Exception as e: print("Failed to toggle {}. See error.log.".format(mode)) logger.error("Exception: {}".format(str(e)))
class CoinMarketFunctionality: """ Handles CMC command functionality """ def __init__(self, bot): with open('config.json') as config: self.config_data = json.load(config) self.bot = bot self.coin_market = CoinMarket() self.live_on = False self.crypto_acronyms = None if self.config_data['load_acronyms']: print("Loading cryptocurrency acronyms..") self.acronym_list = self._load_acronyms() def _load_acronyms(self): """ Loads all acronyms of existing crypto-coins out there @return - list of acronyms """ try: acronym_list, duplicate_count = self.coin_market.load_all_acronyms( ) print("Acronyms have successfully loaded.") logger.info("Acronyms have successfully loaded.") return acronym_list except CoinMarketException as e: print("Failed to load cryptocurrency acronyms. See error.log.") logger.error("CoinMarketException: {}".format(str(e))) return None async def display_search(self, currency, fiat): """ Embeds search results and displays it in chat. @param currency - cryptocurrency to search for @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: if ',' in currency: if ' ' in currency: await self.bot.say( "Don't include spaces in multi-coin search.") return currency_list = currency.split(',') data = await self.coin_market.get_multiple_currency( self.acronym_list, currency_list, fiat) em = discord.Embed(title="Search results", description=data, colour=0xFFD700) else: data, isPositivePercent = await self.coin_market.get_currency( self.acronym_list, currency, fiat) if isPositivePercent: em = discord.Embed(title="Search results", description=data, colour=0x009993) else: em = discord.Embed(title="Search results", description=data, colour=0xD14836) await self.bot.say(embed=em) except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) await self.bot.say(e) except FiatException as e: error_msg = (str(e) + "\nIf you're doing multiple searches, please " "make sure there's no spaces after the comma.") logger.error("FiatException: {}".format(str(e))) await self.bot.say(error_msg) except CoinMarketException as e: print("An error has occured. See error.log.") logger.error("CoinMarketException: {}".format(str(e))) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def calculate_coin_to_fiat(self, currency, currency_amt, fiat): """ Calculates coin to fiat rate and displays it @param currency - cryptocurrency that was bought @param currency_amt - amount of currency coins @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: ucase_fiat = self.coin_market.fiat_check(fiat) if currency.upper() in self.acronym_list: currency = self.acronym_list[currency.upper()] data = self.coin_market.fetch_currency_data(currency, ucase_fiat)[0] current_cost = float(data['price_{}'.format(fiat.lower())]) fiat_cost = self.coin_market.format_price( currency_amt * current_cost, fiat) currency = currency.title() result = "**{} {}** is worth **{}**".format( currency_amt, data['symbol'], str(fiat_cost)) em = discord.Embed(title="{}({}) to {}".format( currency, data['symbol'], fiat.upper()), description=result, colour=0xFFD700) await self.bot.say(embed=em) except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except Exception as e: await self.bot.say( "Command failed. Make sure the arguments are valid.") print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def calculate_fiat_to_coin(self, currency, price, fiat): """ Calculates coin to fiat rate and displays it @param currency - cryptocurrency that was bought @param currency_amt - amount of currency coins @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: ucase_fiat = self.coin_market.fiat_check(fiat) if currency.upper() in self.acronym_list: currency = self.acronym_list[currency.upper()] data = self.coin_market.fetch_currency_data(currency, ucase_fiat)[0] current_cost = float(data['price_{}'.format(fiat.lower())]) amt_of_coins = "{:.8f}".format(price / current_cost) amt_of_coins = amt_of_coins.rstrip('0') price = self.coin_market.format_price(price, fiat) currency = currency.title() result = "**{}** is worth **{} {}**".format( price, amt_of_coins, currency) em = discord.Embed(title="{} to {}({})".format( fiat.upper(), currency, data['symbol']), description=result, colour=0xFFD700) await self.bot.say(embed=em) except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except Exception as e: await self.bot.say( "Command failed. Make sure the arguments are valid.") print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def calculate_profit(self, currency, currency_amt, cost, fiat): """ Performs profit calculation operation and displays it @param currency - cryptocurrency that was bought @param currency_amt - amount of currency coins @param cost - the price of the cryptocurrency bought at the time @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: ucase_fiat = self.coin_market.fiat_check(fiat) if currency.upper() in self.acronym_list: currency = self.acronym_list[currency.upper()] data = self.coin_market.fetch_currency_data(currency, ucase_fiat)[0] current_cost = float(data['price_{}'.format(fiat.lower())]) initial_investment = float(currency_amt) * float(cost) profit = float((float(currency_amt) * current_cost) - initial_investment) overall_investment = float(initial_investment + profit) currency = currency.title() formatted_initial_investment = self.coin_market.format_price( initial_investment, fiat) formatted_profit = self.coin_market.format_price(profit, fiat).replace( '$-', '-$') formatted_overall_investment = self.coin_market.format_price( overall_investment, fiat) msg = ( "Initial Investment: **{}** (**{}** coin(s), costs **{}** each)\n" "Profit: **{}**\n" "Total investment worth: **{}**".format( formatted_initial_investment, currency_amt, cost, formatted_profit, formatted_overall_investment)) color = 0xD14836 if profit < 0 else 0x009993 em = discord.Embed(title="Profit calculated ({})".format(currency), description=msg, colour=color) await self.bot.say(embed=em) except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except Exception as e: await self.bot.say( "Command failed. Make sure the arguments are valid.") print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def display_stats(self, fiat): """ Obtains the market stats to display @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: data = await self.coin_market.get_stats(fiat) em = discord.Embed(title="Market Stats", description=data, colour=0x008000) await self.bot.say(embed=em) except MarketStatsException as e: logger.error("MarketStatsException: {}".format(str(e))) await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) await self.bot.say(e) except CoinMarketException as e: print("An error has occured. See error.log.") logger.error("CoinMarketException: {}".format(str(e))) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def display_live_data(self, fiat): """ Obtains and displays live updates of coin stats in n-second intervals. An example for this command would be: "$live" @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: currency_list = self.config_data['live_check_currency'] live_channel = self.config_data['live_channel'] timer = self.config_data['live_update_interval'] if not self.live_on: self.live_on = True while True: try: await self.bot.purge_from( self.bot.get_channel(live_channel), limit=100) except: pass data = await self.coin_market.get_multiple_currency( self.acronym_list, currency_list, fiat) em = discord.Embed(title="Live Currency Update", description=data, colour=0xFFD700) await self.bot.send_message( self.bot.get_channel(live_channel), embed=em) await asyncio.sleep(float(timer)) else: await self.bot.say("Live updates are already on.") except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except CoinMarketException as e: print("An error has occured. See error.log.") logger.error("CoinMarketException: {}".format(str(e))) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e)))
class CommandFunctionality: """Handles CMC command functionality""" def __init__(self, bot): self.supported_operators = ["<", ">", "<=", ">="] self.subscriber_data = self._check_subscriber_file() self.alert_data = self._check_alert_file() self.bot = bot self.market_list = None self.market_stats = None self.coin_market = CoinMarket() self.live_on = False asyncio. async (self._update_game_status_()) asyncio. async (self._continuous_updates()) def _check_subscriber_file(self): """ Checks to see if there's a valid subscribers.json file """ try: with open('subscribers.json') as subscribers: return json.load(subscribers) except FileNotFoundError: with open('subscribers.json', 'w') as outfile: json.dump({}, outfile, indent=4) return json.loads('{}') except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) def _check_alert_file(self): """ Checks to see if there's a valid alerts.json file """ try: with open('alerts.json') as alerts: return json.load(alerts) except FileNotFoundError: with open('alerts.json', 'w') as outfile: json.dump({}, outfile, indent=4) return json.loads('{}') except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) @asyncio.coroutine def _update_game_status_(self): """ Updates the game status of the bot """ num_channels = len(self.subscriber_data) game_status = discord.Game(name="with {} subscriber(s)" "".format(num_channels)) yield from self.bot.change_presence(game=game_status) @asyncio.coroutine def _update_data(self): self._update_market() self.acronym_list = self._load_acronyms() yield from self._display_live_data() yield from self._alert_user_() @asyncio.coroutine def _continuous_updates(self): yield from self._update_data() while True: time = datetime.datetime.now() if time.minute % 5 == 0: yield from self._update_data() yield from asyncio.sleep(60) else: yield from asyncio.sleep(20) def _update_market(self): """ Loads all the cryptocurrencies that exist in the market @return - list of crypto-currencies """ try: self.market_stats = self.coin_market.fetch_coinmarket_stats() currency_data = self.coin_market.fetch_currency_data(load_all=True) market_dict = {} for currency in currency_data: market_dict[currency['id']] = currency self.market_list = market_dict except CurrencyException as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) def _load_acronyms(self): """ Loads all acronyms of existing crypto-coins out there @return - list of crypto-acronyms """ try: if self.market_list is None: raise Exception("Market list was not loaded.") acronym_list = {} duplicate_count = 0 for currency, data in self.market_list.items(): if data['symbol'] in acronym_list: duplicate_count += 1 if data['symbol'] not in acronym_list[data['symbol']]: acronym_list[data['symbol'] + str(1)] = acronym_list[data['symbol']] acronym_list[data['symbol']] = ( "Duplicate acronyms " "found. Possible " "searches are:\n" "{}1 ({})\n".format(data['symbol'], acronym_list[data['symbol']])) dupe_acronym = re.search('\\d+', acronym_list[data['symbol']]) dupe_num = str( int(dupe_acronym.group(len(dupe_acronym.group()) - 1)) + 1) dupe_key = data['symbol'] + dupe_num acronym_list[dupe_key] = currency acronym_list[data['symbol']] = ( acronym_list[data['symbol']] + "{} ({})".format(dupe_key, currency)) else: acronym_list[data['symbol']] = currency return acronym_list except Exception as e: print("Failed to load cryptocurrency acronyms. See error.log.") logger.error("Exception: {}".format(str(e))) async def display_search(self, currency, fiat): """ Embeds search results and displays it in chat. @param currency - cryptocurrency to search for @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: msg_count = 0 if ',' in currency: if ' ' in currency: await self.bot.say( "Don't include spaces in multi-coin search.") return currency_list = currency.split(',') data = self.coin_market.get_current_multiple_currency( self.market_list, self.acronym_list, currency_list, fiat.upper()) for msg in data: if msg_count == 0: em = discord.Embed(title="Search results", description=msg, colour=0xFFD700) msg_count += 1 else: em = discord.Embed(description=msg, colour=0xFFD700) await self.bot.say(embed=em) else: data, isPositivePercent = self.coin_market.get_current_currency( self.market_list, self.acronym_list, currency, fiat.upper()) if isPositivePercent: em = discord.Embed(title="Search results", description=data, colour=0x009993) else: em = discord.Embed(title="Search results", description=data, colour=0xD14836) await self.bot.say(embed=em) except Forbidden: pass except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) await self.bot.say(e) except FiatException as e: error_msg = (str(e) + "\nIf you're doing multiple searches, please " "make sure there's no spaces after the comma.") logger.error("FiatException: {}".format(str(e))) await self.bot.say(error_msg) except CoinMarketException as e: print("An error has occured. See error.log.") logger.error("CoinMarketException: {}".format(str(e))) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def display_stats(self, fiat): """ Obtains the market stats to display @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: data = self.coin_market.get_current_stats(self.market_stats, fiat) em = discord.Embed(title="Market Stats", description=data, colour=0x008000) await self.bot.say(embed=em) except Forbidden: pass except MarketStatsException as e: logger.error("MarketStatsException: {}".format(str(e))) await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) await self.bot.say(e) except CoinMarketException as e: print("An error has occured. See error.log.") logger.error("CoinMarketException: {}".format(str(e))) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def _display_live_data(self): """ Obtains and displays live updates of coin stats in n-second intervals. """ try: # remove_channels = [] subscriber_list = self.subscriber_data msg_count = 0 for channel in subscriber_list: if self.bot.get_channel( channel) in self.bot.get_all_channels(): channel_settings = subscriber_list[channel] if channel_settings["currencies"]: if channel_settings["purge"]: try: await self.bot.purge_from( self.bot.get_channel(channel), limit=100) except: pass data = self.coin_market.get_current_multiple_currency( self.market_list, self.acronym_list, channel_settings["currencies"], channel_settings["fiat"]) for msg in data: if msg_count == 0: em = discord.Embed( title="Live Currency Update", description=msg, colour=0xFFD700) msg_count += 1 else: em = discord.Embed(description=msg, colour=0xFFD700) try: await self.bot.send_message( self.bot.get_channel(channel), embed=em) except: pass msg_count = 0 # else: # remove_channels.append(channel) # if remove_channels: # for channel in remove_channels: # if channel in subscriber_list: # subscriber_list.pop(channel) # with open('subscribers.json', 'w') as outfile: # json.dump(self.subscriber_data, # outfile, # indent=4) # num_channels = len(subscriber_list) # game_status = discord.Game(name="with {} subscriber(s)".format(num_channels)) # await self.bot.change_presence(game=game_status) except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except CoinMarketException as e: print("An error has occured. See error.log.") logger.error("CoinMarketException: {}".format(str(e))) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def calculate_coin_to_coin(self, currency1, currency2, currency_amt): """ Calculates cryptocoin to another cryptocoin and displays it @param currency1 - currency to convert from @param currency2 - currency to convert to @param currency_amt - amount of currency coins """ try: acronym1 = '' acronym2 = '' if currency1.upper() in self.acronym_list: acronym1 = currency1.upper() currency1 = self.acronym_list[currency1.upper()] else: acronym1 = self.market_list[currency1]["symbol"] if currency2.upper() in self.acronym_list: acronym2 = currency2.upper() currency2 = self.acronym_list[currency2.upper()] else: acronym2 = self.market_list[currency2]["symbol"] price_btc1 = float(self.market_list[currency1]['price_btc']) price_btc2 = float(self.market_list[currency2]['price_btc']) btc_amt = float("{:.8f}".format(currency_amt * price_btc1)) converted_amt = "{:.8f}".format(btc_amt / price_btc2).rstrip('0') currency_amt = "{:.8f}".format(currency_amt).rstrip('0') if currency_amt.endswith('.'): currency_amt = currency_amt.replace('.', '') result = "**{} {}** converts to **{} {}**".format( currency_amt, currency1.title(), converted_amt, currency2.title()) em = discord.Embed(title="{}({}) to {}({})".format( currency1.title(), acronym1, currency2.title(), acronym2), description=result, colour=0xFFD700) await self.bot.say(embed=em) except Forbidden: pass except Exception as e: await self.bot.say( "Command failed. Make sure the arguments are valid.") print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def calculate_coin_to_fiat(self, currency, currency_amt, fiat): """ Calculates coin to fiat rate and displays it @param currency - cryptocurrency that was bought @param currency_amt - amount of currency coins @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: ucase_fiat = self.coin_market.fiat_check(fiat) if currency.upper() in self.acronym_list: currency = self.acronym_list[currency.upper()] data = self.market_list[currency] current_cost = float(data['price_usd']) fiat_cost = self.coin_market.format_price( currency_amt * current_cost, ucase_fiat) currency = currency.title() result = "**{} {}** is worth **{}**".format( currency_amt, data['symbol'], str(fiat_cost)) em = discord.Embed(title="{}({}) to {}".format( currency, data['symbol'], ucase_fiat), description=result, colour=0xFFD700) await self.bot.say(embed=em) except Forbidden: pass except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except Exception as e: await self.bot.say( "Command failed. Make sure the arguments are valid.") print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def calculate_fiat_to_coin(self, currency, price, fiat): """ Calculates coin to fiat rate and displays it @param currency - cryptocurrency that was bought @param currency_amt - amount of currency coins @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: ucase_fiat = self.coin_market.fiat_check(fiat) if currency.upper() in self.acronym_list: currency = self.acronym_list[currency.upper()] data = self.market_list[currency] current_cost = float(data['price_usd']) amt_of_coins = "{:.8f}".format(price / current_cost) amt_of_coins = amt_of_coins.rstrip('0') price = self.coin_market.format_price(price, ucase_fiat) currency = currency.title() result = "**{}** is worth **{} {}**".format( price, amt_of_coins, currency) em = discord.Embed(title="{} to {}({})".format( ucase_fiat, currency, data['symbol']), description=result, colour=0xFFD700) await self.bot.say(embed=em) except Forbidden: pass except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except Exception as e: await self.bot.say( "Command failed. Make sure the arguments are valid.") print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def calculate_profit(self, currency, currency_amt, cost, fiat): """ Performs profit calculation operation and displays it @param currency - cryptocurrency that was bought @param currency_amt - amount of currency coins @param cost - the price of the cryptocurrency bought at the time @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: ucase_fiat = self.coin_market.fiat_check(fiat) if currency.upper() in self.acronym_list: currency = self.acronym_list[currency.upper()] data = self.market_list[currency] current_cost = float(data['price_usd']) initial_investment = float(currency_amt) * float(cost) profit = float((float(currency_amt) * current_cost) - initial_investment) overall_investment = float(initial_investment + profit) currency = currency.title() formatted_initial_investment = self.coin_market.format_price( initial_investment, ucase_fiat) formatted_profit = self.coin_market.format_price( profit, ucase_fiat) formatted_overall_investment = self.coin_market.format_price( overall_investment, ucase_fiat) msg = ( "Initial Investment: **{}** (**{}** coin(s), costs **{}** each)\n" "Profit: **{}**\n" "Total investment worth: **{}**".format( formatted_initial_investment, currency_amt, cost, formatted_profit, formatted_overall_investment)) color = 0xD14836 if profit < 0 else 0x009993 em = discord.Embed(title="Profit calculated ({})".format(currency), description=msg, colour=color) await self.bot.say(embed=em) except Forbidden: pass except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except Exception as e: await self.bot.say( "Command failed. Make sure the arguments are valid.") print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) def _save_subscriber_file(self): """ Saves subscribers.json file """ with open('subscribers.json', 'w') as outfile: json.dump(self.subscriber_data, outfile, indent=4) async def add_subscriber(self, ctx, fiat): """ Adds channel to the live update subscriber list in subscribers.json @param ctx - context of the command sent @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: ucase_fiat = self.coin_market.fiat_check(fiat) channel = str(ctx.message.channel.id) subscriber_list = self.subscriber_data try: self.bot.get_channel(channel).server # validate channel except: await self.bot.say( "Failed to add channel as a subscriber. " " Please make sure this channel is within a " "valid server.") return if channel not in subscriber_list: subscriber_list[channel] = {} channel_settings = subscriber_list[channel] channel_settings["purge"] = False channel_settings["fiat"] = ucase_fiat channel_settings["currencies"] = [] self._save_subscriber_file() await self._update_game_status_() await self.bot.say("Channel has succcesfully subscribed. Now " "add some currencies with `$addc` to begin " "receiving updates.") else: await self.bot.say("Channel is already subscribed.") except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def remove_subscriber(self, ctx): """ Removes channel from the subscriber list in subscribers.json @param ctx - context of the command sent """ try: channel = ctx.message.channel.id subscriber_list = self.subscriber_data if channel in subscriber_list: subscriber_list.pop(channel) self._save_subscriber_file() await self._update_game_status_() await self.bot.say("Channel has unsubscribed.") else: await self.bot.say("Channel was never subscribed.") except Forbidden: pass except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def toggle_purge(self, ctx): """ Turns purge mode on/off for the channel """ try: channel = ctx.message.channel.id subscriber_list = self.subscriber_data self.bot.get_channel(channel).server # validate channel if channel not in subscriber_list: await self.bot.say("Channel was never subscribed.") return channel_settings = subscriber_list[channel] channel_settings["purge"] = not channel_settings["purge"] self._save_subscriber_file() if channel_settings["purge"]: await self.bot.say( "Purge mode on. Bot will now purge messages upon" " live updates. Please make sure your bot has " "the right permissions to remove messages.") else: await self.bot.say("Purge mode off.") except Exception as e: await self.bot.say( "Failed to set purge mode. Please make sure this" " channel is within a valid server.") async def get_sub_currencies(self, ctx): """ Displays the currencies the channel in context is subbed too @param ctx - context of the command sent """ try: channel = str(ctx.message.channel.id) subscriber_list = self.subscriber_data if channel in subscriber_list: channel_settings = subscriber_list[channel] currency_list = channel_settings["currencies"] if len(currency_list) != 0: msg = "Currently this channel displays the following:\n" for currency in currency_list: msg += "__**{}**__\n".format(currency.title()) else: msg = "Channel does not have any currencies to display." await self.bot.say(msg) else: msg = "Channel was never subscribed." await self.bot.say(msg) except Forbidden: pass except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def add_currency(self, ctx, currency): """ Adds a cryptocurrency to the subscriber settings @param ctx - context of the command sent @param currency - the cryptocurrency to add """ try: if currency.upper() in self.acronym_list: currency = self.acronym_list[currency.upper()] if "Duplicate" in currency: await self.bot.say(currency) return if currency not in self.market_list: raise CurrencyException( "Currency is invalid: ``{}``".format(currency)) channel = ctx.message.channel.id subscriber_list = self.subscriber_data if channel in subscriber_list: channel_settings = subscriber_list[channel] if currency in channel_settings["currencies"]: await self.bot.say("``{}`` is already added.".format( currency.title())) return channel_settings["currencies"].append(currency) self._save_subscriber_file() await self.bot.say("``{}`` was successfully added.".format( currency.title())) else: await self.bot.say("The channel needs to be subscribed first.") except Forbidden: pass except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) await self.bot.say(e) except CoinMarketException as e: print("An error has occured. See error.log.") logger.error("CoinMarketException: {}".format(str(e))) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def remove_currency(self, ctx, currency): """ Removes a cryptocurrency from the subscriber settings @param ctx - context of the command sent @param currency - the cryptocurrency to remove """ try: if currency.upper() in self.acronym_list: currency = self.acronym_list[currency.upper()] if "Duplicate" in currency: await self.bot.say(currency) return channel = ctx.message.channel.id subscriber_list = self.subscriber_data if channel in subscriber_list: channel_settings = subscriber_list[channel] if currency in channel_settings["currencies"]: channel_settings["currencies"].remove(currency) self._save_subscriber_file() await self.bot.say( "``{}`` was successfully removed.".format( currency.title())) return else: await self.bot.say( "``{}`` was never added or is invalid.".format( currency.title())) else: await self.bot.say("The channel needs to be subscribed first.") except Forbidden: pass except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) await self.bot.say(e) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) def _translate_operation_(self, operator): """ Translates the supported operations for alerts into english @param operator - operator condition to notify the channel """ if operator in self.supported_operators: if operator == "<": operator_translation = "less than" elif operator == "<=": operator_translation = "less than or equal to" elif operator == ">": operator_translation = "greater than" elif operator == ">=": operator_translation = "greater than or equal to" return operator_translation else: raise Exception("Unable to translate operation.") def _check_alert_(self, currency, operator, price, fiat): """ Checks if the alert condition isn't true @param currency - cryptocurrency to set an alert of @param operator - operator condition to notify the channel @param price - price for condition to compare @param fiat - desired fiat currency (i.e. 'EUR', 'USD') @return - True if condition doesn't exist, False if it does """ market_price = float(self.market_list[currency]["price_usd"]) market_price = float( self.coin_market.format_price(market_price, fiat, False)) if operator in self.supported_operators: if operator == "<": if market_price < float(price): return False elif operator == "<=": if market_price <= float(price): return False elif operator == ">": if market_price > float(price): return False elif operator == ">=": if market_price >= float(price): return False return True else: raise Exception async def add_alert(self, ctx, currency, operator, price, fiat): """ Adds an alert to alerts.json @param currency - cryptocurrency to set an alert of @param operator - operator condition to notify the channel @param price - price for condition to compare @param fiat - desired fiat currency (i.e. 'EUR', 'USD') """ try: i = 1 alert_num = None ucase_fiat = self.coin_market.fiat_check(fiat) if currency.upper() in self.acronym_list: currency = self.acronym_list[currency.upper()] if "Duplicate" in currency: await self.bot.say(currency) return if currency not in self.market_list: raise CurrencyException( "Currency is invalid: ``{}``".format(currency)) try: if not self._check_alert_(currency, operator, price, ucase_fiat): await self.bot.say("Failed to create alert. Current price " "of **{}** already meets the condition." "".format(currency.title())) return except Exception: await self.bot.say("Invalid operator: **{}**".format(operator)) return user_id = str(ctx.message.author.id) if user_id not in self.alert_data: self.alert_data[user_id] = {} while i <= len(self.alert_data[user_id]) + 1: if str(i) not in self.alert_data[user_id]: alert_num = str(i) i += 1 if alert_num is None: raise Exception("Something went wrong with adding alert.") alert_cap = int(self.subscriber_data["alert_capacity"]) if int(alert_num) > alert_cap: await self.bot.say( "Unable to add alert, user alert capacity of" " **{}** has been reached.".format(alert_cap)) return alert_list = self.alert_data[user_id] alert_list[alert_num] = {} channel_alert = alert_list[alert_num] channel_alert["currency"] = currency if operator in self.supported_operators: channel_alert["operation"] = operator else: await self.bot.say( "Invalid operator: {}. Your choices are **<*" "*, **<=**, **>**, or **>=**" "".format(operator)) return channel_alert["price"] = ("{:.6f}".format(price)).rstrip('0') if channel_alert["price"].endswith('.'): channel_alert["price"] = channel_alert["price"].replace( '.', '') channel_alert["fiat"] = ucase_fiat with open('alerts.json', 'w') as outfile: json.dump(self.alert_data, outfile, indent=4) await self.bot.say("Alert has been set.") except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) await self.bot.say(e) except FiatException as e: logger.error("FiatException: {}".format(str(e))) self.live_on = False await self.bot.say(e) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) def _save_alert_file_(self): """ Saves alerts.json file """ with open('alerts.json', 'w') as outfile: json.dump(self.alert_data, outfile, indent=4) async def remove_alert(self, ctx, alert_num): """ Removes an alert from the user's list of alerts @param ctx - context of the command sent @param alert_num - number of the specific alert to remove """ try: user_id = str(ctx.message.author.id) user_list = self.alert_data alert_list = user_list[user_id] if alert_num in alert_list: removed_alert = alert_num alert_setting = alert_list[alert_num] alert_currency = alert_setting["currency"] alert_operation = self._translate_operation_( alert_setting["operation"]) alert_price = alert_setting["price"] alert_fiat = alert_setting["fiat"] alert_list.pop(str(alert_num)) self._save_alert_file_() await self.bot.say( "Alert **{}** where **{}** is **{}** **{}** " "in **{}** was successfully " "removed.".format(removed_alert, alert_currency, alert_operation, alert_price, alert_fiat)) else: await self.bot.say("The number you've entered does not exist " "in the alert list. Use `$geta` to receive " "a list of ongoing alerts.") except Forbidden: pass except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) await self.bot.say(e) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def get_alert_list(self, ctx): """ Gets the list of alerts and displays them @param ctx - context of the command sent """ try: user_id = ctx.message.author.id user_list = self.alert_data msg = {} result_msg = "" if user_id in user_list: alert_list = user_list[user_id] if len(alert_list) != 0: msg[0] = "The following alerts have been set:\n" for alert in alert_list: currency = alert_list[alert]["currency"].title() operation = self._translate_operation_( alert_list[alert]["operation"]) msg[int(alert)] = ( "[**{}**] Alert when **{}** is **{}** **{}** " "in **{}**\n".format(alert, currency, operation, alert_list[alert]["price"], alert_list[alert]["fiat"])) for line in sorted(msg): result_msg += msg[line] else: result_msg = "Channel does not have any alerts to display." else: result_msg = "User never created any alerts." await self.bot.say(result_msg) except Forbidden: pass except CurrencyException as e: logger.error("CurrencyException: {}".format(str(e))) await self.bot.say(e) except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e))) async def _alert_user_(self): """ Checks and displays alerts that have met the condition of the cryptocurrency price """ try: raised_alerts = defaultdict(list) for user in self.alert_data: alert_list = self.alert_data[str(user)] for alert in alert_list: alert_currency = alert_list[alert]["currency"] operator_symbol = alert_list[alert]["operation"] alert_operator = self._translate_operation_( operator_symbol) alert_price = alert_list[alert]["price"] alert_fiat = alert_list[alert]["fiat"] if not self._check_alert_(alert_currency, operator_symbol, alert_price, alert_fiat): raised_alerts[user].append(alert) user_obj = await self.bot.get_user_info(user) await self.bot.send_message( user_obj, "Alert **{}**! " "**{}** is **{}** **{}** " "in **{}**" "".format(alert, alert_currency.title(), alert_operator, alert_price, alert_fiat)) if raised_alerts: for user in raised_alerts: for alert_num in raised_alerts[user]: self.alert_data[user].pop(str(alert_num)) self._save_alert_file_() except Exception as e: print("An error has occured. See error.log.") logger.error("Exception: {}".format(str(e)))