def test_client_raw_body_only(self): client = EsiClient(raw_body_only=True) self.assertEqual(client.raw_body_only, True) with httmock.HTTMock(public_incursion): incursions = client.request(self.app.op['get_incursions']()) self.assertIsNone(incursions.data) self.assertTrue(len(incursions.raw) > 0) incursions = client.request(self.app.op['get_incursions'](), raw_body_only=False) self.assertIsNotNone(incursions.data)
def characterid_from_name(char_name: str) -> Tuple[Optional[int], Optional[str]]: """ @return: charid, name """ api = get_api() security = Security( api, ) client = EsiClient(security, timeout=10) search_answer = client.request(api.op['get_search'](search=char_name, categories=['character'], strict=True)) # this character name doesn't exist if not ('character' in search_answer.data): return None, None char_id: int = int(search_answer.data['character'][0]) char_answer = client.request(api.op['get_characters_character_id'](character_id=char_id)) char_name: str = char_answer.data['name'] return char_id, char_name
def add_system_info(system_id: int, esi_client: EsiClient) -> Optional[Tuple[int, EsiClient]]: try: api: App = get_api() system_request = api.op['get_universe_systems_system_id'](system_id=system_id) system_resp = esi_client.request(system_request) if system_resp.status != 200: logger.error(f'Could not get systen info for id={system_id} status={system_resp.status}') return system = SolarSystem() system.solarSystemID = system_resp.data['system_id'] system.solarSystemName = system_resp.data['name'] db.session.merge(system) db.session.commit() return None except Exception as e: return system_id, esi_client
def add_constellation_info(const_id: int, esi_client: EsiClient) -> Optional[Tuple[int, EsiClient]]: try: api: App = get_api() const_request = api.op['get_universe_constellations_constellation_id'](constellation_id=const_id) const_resp = esi_client.request(const_request) if const_resp.status != 200: logger.error(f'Could not get constellation info for id={const_id} status={const_resp.status}') return const = Constellation() const.constellationID = const_resp.data['constellation_id'] const.constellationName = const_resp.data['name'] db.session.merge(const) db.session.commit() return None except Exception as e: return const_id, esi_client
class Market(Cog): """ Market related commands. """ def __init__(self, bot): self.bot = bot self.esi = EsiClient( retry_requests=True, headers={ 'User-Agent': f'application: MercuryBot contact: {self.bot.config["bot"]["user_agent"]}' }) def type_from_name(self, name: str) -> Optional[dict]: """ Returns a type ID from ESI for a given name. Return value of None indicates an invalid type name. :param name: :return: """ post_op = self.bot.esi_app.op['post_universe_ids'](names=[name]) response = self.esi.request(post_op) if 'inventory_types' not in response.data: return None return response.data['inventory_types'][0] @commands.command(aliases=['pc']) async def price_check(self, ctx, item_name): """ Check the price of a given item. Returns Jita price data. """ type_data = self.type_from_name(item_name) market_data = await get_market_data(type_data['id']) return await ctx.send( embed=await build_embed(type_data, market_data['resp']))
class TestEsiPy(unittest.TestCase): CALLBACK_URI = "https://foo.bar/baz/callback" LOGIN_EVE = "https://login.eveonline.com" OAUTH_VERIFY = "%s/oauth/verify" % LOGIN_EVE OAUTH_TOKEN = "%s/oauth/token" % LOGIN_EVE CLIENT_ID = 'foo' SECRET_KEY = 'bar' BASIC_TOKEN = six.u('Zm9vOmJhcg==') SECURITY_NAME = 'evesso' RSC_SSO_ENDPOINTS = "test/resources/oauth-authorization-server.json" RSC_JWKS = "test/resources/jwks.json" @mock.patch('six.moves.urllib.request.urlopen') def setUp(self, urlopen_mock): # I hate those mock... thx urlopen instead of requests... urlopen_mock.return_value = open('test/resources/swagger.json') warnings.simplefilter('ignore') self.app = App.create('https://esi.evetech.net/latest/swagger.json') with open(TestEsiPy.RSC_SSO_ENDPOINTS, 'r') as sso_endpoints: with open(TestEsiPy.RSC_JWKS, "r") as jwks: self.security = EsiSecurity( app=self.app, redirect_uri=TestEsiPy.CALLBACK_URI, client_id=TestEsiPy.CLIENT_ID, secret_key=TestEsiPy.SECRET_KEY, sso_endpoints=json.load(sso_endpoints), jwks_key=json.load(jwks)) self.cache = DictCache() self.client = EsiClient(self.security, cache=self.cache) self.client_no_auth = EsiClient(cache=self.cache, retry_requests=True) def tearDown(self): """ clear the cache so we don't have residual data """ self.cache._dict = {} def test_esipy_client_no_args(self): client_no_args = EsiClient() self.assertIsNone(client_no_args.security) self.assertTrue(isinstance(client_no_args.cache, DictCache)) self.assertEqual(client_no_args._session.headers['User-Agent'], 'EsiPy/Client - https://github.com/Kyria/EsiPy') self.assertEqual(client_no_args.raw_body_only, False) def test_esipy_client_with_headers(self): client_with_headers = EsiClient(headers={'User-Agent': 'foobar'}) self.assertEqual(client_with_headers._session.headers['User-Agent'], 'foobar') def test_esipy_client_with_adapter(self): transport_adapter = HTTPAdapter() client_with_adapters = EsiClient(transport_adapter=transport_adapter) self.assertEqual(client_with_adapters._session.get_adapter('http://'), transport_adapter) self.assertEqual(client_with_adapters._session.get_adapter('https://'), transport_adapter) def test_esipy_client_without_cache(self): client_without_cache = EsiClient(cache=None) self.assertTrue(isinstance(client_without_cache.cache, DummyCache)) def test_esipy_client_with_cache(self): cache = DictCache() client_with_cache = EsiClient(cache=cache) self.assertTrue(isinstance(client_with_cache.cache, BaseCache)) self.assertEqual(client_with_cache.cache, cache) def test_esipy_client_wrong_cache(self): with self.assertRaises(ValueError): EsiClient(cache=DictCache) def test_esipy_request_public(self): with httmock.HTTMock(public_incursion): incursions = self.client_no_auth.request( self.app.op['get_incursions']()) self.assertEqual(incursions.data[0].type, 'Incursion') self.assertEqual(incursions.data[0].faction_id, 500019) def test_esipy_request_authed(self): with httmock.HTTMock(*_all_auth_mock_): self.security.auth('let it bee') char_location = self.client.request( self.app.op['get_characters_character_id_location']( character_id=123456789)) self.assertEqual(char_location.data.station_id, 60004756) # force expire self.security.token_expiry = 0 char_location_with_refresh = self.client.request( self.app.op['get_characters_character_id_location']( character_id=123456789)) self.assertEqual(char_location_with_refresh.data.station_id, 60004756) def test_client_cache_request(self): @httmock.all_requests def fail_if_request(url, request): self.fail('Cached data is not supposed to do requests') incursion_operation = self.app.op['get_incursions'] with httmock.HTTMock(public_incursion_no_expires): incursions = self.client_no_auth.request(incursion_operation()) self.assertEqual(incursions.data[0].state, 'mobilizing') with httmock.HTTMock(public_incursion_no_expires_second): incursions = self.client_no_auth.request(incursion_operation()) self.assertEqual(incursions.data[0].state, 'established') with httmock.HTTMock(public_incursion): incursions = self.client_no_auth.request(incursion_operation()) self.assertEqual(incursions.data[0].state, 'mobilizing') with httmock.HTTMock(fail_if_request): incursions = self.client_no_auth.request(incursion_operation()) self.assertEqual(incursions.data[0].state, 'mobilizing') def test_client_warning_header(self): # deprecated warning warnings.simplefilter('error') with httmock.HTTMock(public_incursion_warning): incursion_operation = self.app.op['get_incursions'] with self.assertRaises(UserWarning): self.client_no_auth.request(incursion_operation()) with self.assertRaises(UserWarning): self.client_no_auth.head(incursion_operation()) def test_client_raw_body_only(self): client = EsiClient(raw_body_only=True) self.assertEqual(client.raw_body_only, True) with httmock.HTTMock(public_incursion): incursions = client.request(self.app.op['get_incursions']()) self.assertIsNone(incursions.data) self.assertTrue(len(incursions.raw) > 0) incursions = client.request(self.app.op['get_incursions'](), raw_body_only=False) self.assertIsNotNone(incursions.data) def test_esipy_reuse_operation(self): operation = self.app.op['get_incursions']() with httmock.HTTMock(public_incursion): incursions = self.client_no_auth.request(operation) self.assertEqual(incursions.data[0].faction_id, 500019) # this shouldn't create any errors incursions = self.client_no_auth.request(operation) self.assertEqual(incursions.data[0].faction_id, 500019) def test_esipy_multi_request(self): operation = self.app.op['get_incursions']() with httmock.HTTMock(public_incursion): count = 0 for req, incursions in self.client_no_auth.multi_request( [operation, operation, operation], threads=2): self.assertEqual(incursions.data[0].faction_id, 500019) count += 1 # Check we made 3 requests self.assertEqual(count, 3) def test_esipy_backoff(self): operation = self.app.op['get_incursions']() start_calls = time.time() with httmock.HTTMock(public_incursion_server_error): incursions = self.client_no_auth.request(operation) self.assertEqual(incursions.data.error, 'broke') end_calls = time.time() # Check we retried 5 times self.assertEqual(incursions.data.count, 5) # Check that backoff slept for a sum > 2 seconds self.assertTrue(end_calls - start_calls > 2) def test_esipy_timeout(self): def send_function(*args, **kwargs): """ manually create a ConnectionError to test the retry and be sure no exception is thrown """ send_function.count += 1 raise ConnectionError send_function.count = 0 self.client_no_auth._session.send = mock.MagicMock( side_effect=send_function) operation = self.app.op['get_incursions']() with httmock.HTTMock(public_incursion): incursions = self.client_no_auth.request(operation) # there shouldn't be any exceptions self.assertEqual(incursions.status, 500) self.assertEqual(send_function.count, 5) def test_esipy_raise_on_error(self): operation = self.app.op['get_incursions']() with httmock.HTTMock(public_incursion_server_error): # try with retries with self.assertRaises(APIException): self.client_no_auth.request(operation, raise_on_error=True) # try without retries with self.assertRaises(APIException): self.client.request(operation, raise_on_error=True) # try with head with self.assertRaises(APIException): self.client_no_auth.head(operation, raise_on_error=True) def test_esipy_expired_response(self): operation = self.app.op['get_incursions'] with httmock.HTTMock(public_incursion_expired): warnings.filterwarnings('error', '.*returned expired result') with self.assertRaises(UserWarning): self.client_no_auth.request(operation()) warnings.resetwarnings() warnings.simplefilter('ignore') incursions = self.client_no_auth.request(operation()) self.assertEquals(incursions.status, 200) def test_esipy_uncached_method(self): operation = self.app.op['post_universe_ids'](names=['Foo']) self.assertEqual(self.cache._dict, {}) with httmock.HTTMock(post_universe_id): res = self.client.request(operation) self.assertEqual(res.data.characters[0].id, 123456789) self.assertEqual(self.cache._dict, {}) def test_esipy_head_request(self): operation = self.app.op['get_incursions']() with httmock.HTTMock(public_incursion): res = self.client.head(operation) self.assertIsNone(res.data) self.assertIn('Expires', res.header) def test_esipy_expired_header_etag(self): @httmock.all_requests def check_etag(url, request): self.assertEqual(request.headers.get('If-None-Match'), '"esipy_test_etag_status"') return httmock.response(headers={ 'Etag': '"esipy_test_etag_status"', 'expires': make_expire_time_str(), 'date': make_expire_time_str() }, status_code=304) operation = self.app.op['get_status']() with httmock.HTTMock(eve_status): self.assertEqual(self.cache._dict, {}) res = self.client.request(operation) self.assertNotEqual(self.cache._dict, {}) self.assertEqual(res.data.server_version, "1313143") time.sleep(2) with httmock.HTTMock(check_etag): res = self.client.request(operation) self.assertEqual(res.data.server_version, "1313143") def test_esipy_expired_header_noetag(self): def check_etag(url, request): self.assertNotIn('If-None-Match', request.headers) return httmock.response(status_code=200, content={ "players": 29597, "server_version": "1313143", "start_time": "2018-05-20T11:04:30Z" }) operation = self.app.op['get_status']() with httmock.HTTMock(eve_status_noetag): res = self.client.request(operation) self.assertEqual(res.data.server_version, "1313143") time.sleep(2) with httmock.HTTMock(check_etag): res = self.client.request(operation) self.assertEqual(res.data.server_version, "1313143") def test_esipy_non_json_response(self): operation = self.app.op['get_status']() with httmock.HTTMock(non_json_error): try: self.client.request(operation) except APIException as exc: self.assertEqual(exc.status_code, 502) self.assertEqual( exc.response, six.b('<html><body>Some HTML Errors</body></html>')) try: self.client_no_auth.request(operation) except APIException as exc: self.assertEqual(exc.status_code, 502) self.assertEqual( exc.response, six.b('<html><body>Some HTML Errors</body></html>'))
class ZkillCommands(Cog): def __init__(self, bot): self.bot = bot self.esi = EsiClient( retry_requests=True, headers={ 'User-Agent': f'applucation: MercuryBot contact: {self.bot.config["bot"]["user_agent"]}' }) async def _get_character_id_from_esi(self, name: str) -> int: """ Returns the character ID from ESI. Returns -1 when no ID is returned for a name. :param name: :return: """ id_op = self.bot.esi_app.op["get_search"](categories="character", search=name, strict=True) id_response = self.esi.request(id_op) if "character" not in id_response.data: return -1 return int(id_response.data['character'][0]) async def _get_character_name_from_id(self, character_id: int) -> Optional[str]: """ Returns the name of a character for a given character_id. Returns None if the ID is not valid. :param character_id: :return: """ name_op = self.bot.esi_app.op["get_characters_character_id"]( character_id=character_id) try: name_response = self.esi.request(name_op) except APIException: logger.error( f'Error getting name for character with ID {character_id} from ESI.' ) logger.error(traceback.print_exc()) return None return name_response.data['name'] @commands.command(aliases=['t']) async def threat(self, ctx, *, name: str): """ Returns info on a character from the zKill stats API. """ # Get the character_id from ESI char_id = await self._get_character_id_from_esi(name) if char_id == -1: return await ctx.send( f"Character `{name}` not found. Please check the spelling and try again." ) # Get the real name from ESI (ensure it is spelled correctly... i.e. dont trust user input) char_name = await self._get_character_name_from_id(char_id) if char_name is None: return await ctx.send( "Something went wrong, please try again later.") # Get Zkill Stats stats = await get_char_stats_from_zkill(char_id) if stats is None: return await ctx.send( "The provided character has no killboard stats.") embed = await build_embed(stats, name, char_id) return await ctx.send(embed=embed)
from esipy import App app = App.create( url="https://esi.tech.ccp.is/latest/swagger.json?datasource=tranquility") from esipy import EsiClient client = EsiClient( retry_requests=True, # set to retry on http 5xx error (default False) headers={'User-Agent': 'Gc4be9375d12d45288bced3dd57e9aae1'}, raw_body_only= True, # default False, set to True to never parse response and only return raw JSON string content. ) route_find = app.op['get_route_origin_destination'](origin=30002187, destination=30000142, flag='shortest') response = client.request(route_find) print(response.header)
class NewsWatch(Cog, command_attrs=dict(hidden=True)): def __init__(self, bot): self.bot = bot self.esi = EsiClient( retry_requests=True, headers={ 'User-Agent': f'application: MercuryBot contact: {self.bot.config["bot"]["user_agent"]}' }, raw_body_only=False, ) self.channels = None self.news_task.start() def cog_unload(self): self.news_task.cancel() async def load_channels(self): """ Loads self.channels :return: """ news = await NewsChannel.filter(news=True) devblogs = await NewsChannel.filter(devblogs=True) patchnotes = await NewsChannel.filter(patchnotes=True) self.channels = { 'news': news, 'devblogs': devblogs, 'patchnotes': patchnotes } async def _get_character_id_from_name(self, character_name: str) -> int: """ Returns the character_id for a given character name :param character_name: :return: """ id_op = self.bot.esi_app.op["get_search"](categories=['character'], search=character_name, strict=True) id_resp = self.esi.request(id_op) if 'character' not in id_resp.data: return -1 return id_resp.data['character'][0] @commands.command( aliases=['nc', 'newschan', 'nchan', 'news_chan', 'n_chan']) @checks.is_admin() async def news_channel(self, ctx, action): """ Sets or unsets the server's news feed channel. Valid Actions: - set (aliases: add) - unset (aliases: delete, remove) - edit The set and edit actions must be run from the intended channel, however, the unset action can be run from any channel. """ action = action.lower() if action in ('set', 'add'): # Check if the news channel is already set for this guild. if await NewsChannel.filter(pk=ctx.guild.id).exists(): return await ctx.send( f"The news channel for this server is already set! To change it" f"please run the `edit` action from the intended channel. To unset the news " f"channel run the `unset` action from any channel.") # Set the channel channel = await NewsChannel(guild_id=ctx.guild.id, channel_id=ctx.channel.id) await channel.save() return await ctx.send( f"{ctx.channel.mention} has been set as the news channel for `{ctx.guild.name}`" ) elif action in ('unset', 'delete', 'remove'): # Make sure the channel exists if not await NewsChannel.filter(pk=ctx.guild.id).exists(): return await ctx.send( f"The news channel for this server is not yet set! To set it," f"run the `set` action from the intended channel.") # Unset the channel channel = await NewsChannel.filter(pk=ctx.guild.id).first() await channel.delete() return await ctx.send( f"The news channel for `{ctx.guild.id}` has been unset.") elif action == 'edit': # Make sure the channel exists if not await NewsChannel.filter(pk=ctx.guild.id).exists(): return await ctx.send( f"The news channel for this server is not yet set! To set it," f"run the `set` action from the intended channel.") # Get the current channel. channel = await NewsChannel.filter(pk=ctx.guild.id).first() channel.channel_id = ctx.channel.id await channel.save() return await ctx.send( f"The news channel for this server has been updated. " f"New channel is {ctx.channel.mention}") else: return await ctx.send( f"{action} is not a valid action for this command. To see valid actions run" f"the help command. (`/help news_channel`)") @commands.command( aliases=['news_track', 'nt', 'track_news', 'track_type', 'tt']) @checks.is_admin() async def news_type(self, ctx, action, *, news_type: str): """ Sets the news channel for the current guild to track the specified type. Valid Actions: add, remove (aliases: delete) Valid Types are: news, devblogs (including plural variations), patchnotes (including plural variations), all """ action = action.lower() # Check that the channel is set if not await NewsChannel.filter(pk=ctx.guild.id).exists(): return await ctx.send( f"The news channel for this server is not yet set. To set it please run the " f"`news_channel` command from the intended channel.") channel = await NewsChannel.filter(pk=ctx.guild.id).first() bad_type = False if action == 'add': if news_type == 'news': channel.news = True elif news_type in ("patchnotes", "patch notes", "patch_notes", "patch-notes"): channel.patchnotes = True elif news_type in ('devblogs', 'dev blogs', 'dev-blogs', 'dev_blogs'): channel.devblogs = True elif news_type == 'all': channel.news = True channel.patchnotes = True channel.devblogs = True else: bad_type = True if not bad_type: await channel.save() elif action in ('remove', 'delete'): if news_type == 'news': channel.news = False elif news_type in ("patchnotes", "patch notes", "patch_notes", "patch-notes"): channel.patchnotes = False elif news_type in ('devblogs', 'dev blogs', 'dev-blogs', 'dev_blogs'): channel.devblogs = False elif news_type == 'all': channel.news = False channel.patchnotes = False channel.devblogs = False else: bad_type = True if not bad_type: await channel.save() else: return await ctx.send( f"{action} is not a valid action for this command. To see valid actions run " f"the help command. (`/help news_type`)") if bad_type: return await ctx.send( f"{news_type} is not a valid news type. To see valid news types run " f"the help command. (`/help news_type`)") await self.load_channels() return await ctx.send( f'The news channel for `{ctx.guild.name}` is now tracking `{news_type}` news articles.' ) @tasks.loop(seconds=300.0) async def news_task(self): dev_blog_url = "https://www.eveonline.com/rss/json/dev-blogs" news_url = "https://www.eveonline.com/rss/json/news" patch_url = "https://www.eveonline.com/rss/json/patch-notes" articles_to_save = [] try: async with aiohttp.ClientSession() as session: devs = await get_json(session, dev_blog_url) news = await get_json(session, news_url) patches = await get_json(session, patch_url) resps = (devs['resp'], news['resp'], patches['resp']) # Get list of posted articles article_ids = await PostedArticles.all().values_list('article_id', flat=True) for resp in resps: for article in resp: if article['id'] not in article_ids: articles_to_save.append( await PostedArticles(article_id=article['id'])) await self.post(article, article['category'].replace('-', '')) else: continue except Exception as e: logger.error(f"Error occurred in the news task! Error: {e}") logger.error(traceback.print_exc()) finally: await PostedArticles.bulk_create(articles_to_save) @news_task.before_loop async def before_news_task(self): # Wait for the bot to be ready await self.bot.wait_until_ready() # Load channels and articles await self.load_channels() @news_task.after_loop async def on_news_task_cancel(self): pass async def post(self, article, category): """ Processes and posts the article to the channels defined in self.channels. :param article: :param category: :return: """ title = article["title"].replace('"', '""') image = og(article['link'], ['og:image']).image author_id = await self._get_character_id_from_name(article['author']) if author_id == -1: author_id = 3019582 author_img = f'https://imageserver.eveonline.com/Character/{author_id}_128.jpg' desc = article['description'] if len(desc) > 1000: desc = article['description'][:1000].replace('__*', '***').replace('*__', '***')\ .replace('__', '**').replace('###', '').replace('##', '')+'...' time = datetime.strptime(article['publishingDate'].strip('Z'), '%Y-%m-%dT%H:%M:%S') embed = discord.Embed(title=title, timestamp=time, description=desc) embed.set_author(name=f'EVE Online {article["category"].title()}', icon_url='https://www.ccpgames.com/img/ccp_logo.png') embed.add_field(name="Link", value=f'{article["link"]}') embed.set_image(url=image) embed.set_footer(text=f'{article["author"]}', icon_url=author_img) for channel in self.channels[category]: channel = self.bot.get_channel(channel.channel_id) await channel.send(embed=embed)
class myApp: pp = pprint.PrettyPrinter(indent=2) app = None client = None def main(self): print "setting up the app..." self.app = App.create( url= "https://esi.tech.ccp.is/latest/swagger.json?datasource=tranquility" ) print "done. \n Setting up the client." self.client = EsiClient( retry_requests= True, # set to retry on http 5xx error (default False) header={'User-Agent': 'Jimmy - api test app: [email protected]'}, raw_body_only= True, # default False, set to True to never parse response and only return raw JSON string content. ) print "done, after this it's all me and my calls." chars = ["alderith", "gruxella", "lord grapefruit", "druzidelcastro"] try: # self.get_id_for_users(chars) # self.get_group_item_ids() self.get_orders_for_region(10000002) except Exception as err: self.pp.pprint(err) # self.get_user_info() # try: # self.original_example() # except Exception as err: # print "Error:: " # print err # e = sys.exc_info()[0] # self.pp.pprint(e) # self.original_example() # print "hello world" def get_orders_for_region(self, reg_id): count = 0 query = self.app.op['get_markets_region_id_orders']( order_type="all", region_id=reg_id, ) response = self.client.request(query) # self.pp.pprint(response.raw) print "Attempting to parse json" data = json.loads(response.raw) print "done parsing json" print len(data) # self.pp.pprint(data) # return queries = [] print "this dataset expires:" print response.header['Expires'] print "we need this many pages:" size = response.header['X-Pages'][0] print size if (size > 1): for page in range(1, size + 1): # for page in range(1,3): queries.append(self.app.op['get_markets_region_id_orders']( order_type="all", region_id=reg_id, page=page, )) print "doing long request to CCP..." # results = self.client.multi_request(queries,None,None,size) results = self.client.multi_request(queries) print "done with long request" for result in results: # self.pp.pprint(result[1].header) # self.pp.pprint(result[1].data) data = json.loads(result[1].raw) count += len(data) print "Running count = %d" % (count) print "is buy order? %r" % (data[0]['is_buy_order']) print "remaining volume: %d" % (data[0]['volume_remain']) # self.pp.pprint(results) print "Total orders = %d" % (count) return results def get_market_groups(self): query = self.app.op['get_markets_groups']() response = self.client.request(query) # self.pp.pprint(response.data) self.pp.pprint(response.header) print len(response.data) return response def get_group_item_ids(self): marketgroup_response = self.get_market_groups() groups = [] count = 0 for group in marketgroup_response.data: count += 1 print group query = self.app.op['get_markets_groups_market_group_id']( market_group_id=group) response = self.client.request(query) groups.append(response.data) if count == 25: break # self.pp.pprint(response.data) self.pp.pprint(groups) def get_id_for_users(self, *user_array): users = [] query = self.app.op['post_universe_ids'](names=user_array[0]) response = self.client.request(query) for char in response.data['characters']: print "%s id = %d" % (char['name'], char['id']) def get_user_info(self): query = self.app.op['post_universe_ids']( names=["Gruxella", "Alderith"]) # query = self.app.op['get_characters_character_id']( # character_id=712133937 # ) # query = self.app.op['get_characters_names']( # character_ids=[712133937] # ) response = self.client.request(query) print response.data def original_example(self): market_order_operation = self.app.op['get_markets_region_id_orders']( region_id=10000002, type_id=34, order_type='all', ) # do the request print "did I get here? 2" response = self.client.request(market_order_operation) # use it: response.data contains the parsed result of the request. print response.data[0].price # to get the headers objects, you can get the header attribute print response.header
class TheraWatch(Cog, command_attrs=dict(hidden=True)): """ Watch the EVE-Scout API for new Thera wormhole connections. """ def __init__(self, bot): self.bot = bot self.TYPE_MODELS = { 'region': TheraEveRegion, 'system': TheraEveSystem, 'constellation': TheraEveConstellation } self.last_thera = None self.channels = None self.esi = EsiClient( retry_requests=True, headers={ 'User-Agent': f'application: MercuryBot contact: {self.bot.config["bot"]["user_agent"]}' }, raw_body_only=False) self.thera.start() def cog_unload(self): self.thera.cancel() async def load_channels(self): """ Updates self.channels. :return: """ systems = await TheraEveSystem.all() constellations = await TheraEveConstellation.all() regions = await TheraEveRegion.all() self.channels = { 'systems': {x.system_id: await x.channels.all() for x in systems}, 'constellations': { x.constellation_id: await x.channels.all() for x in constellations }, 'regions': {x.region_id: await x.channels.all() for x in regions} } async def update_channels(self, location_type: str, location): """ Updates self.channels :param location: A location object :param location_type: string :return: """ plural = f'{location_type}s' self.channels[plural][location.pk] = await location.channels.all() async def location_from_id(self, location_type: str, location_id: int): """ Checks if the ID provided is valid for the type provided. :param location_type: string :param location_id: integer :return: Thera location model. """ RANGES = { 'region': [10000000, 13000000], 'constellation': [20000000, 23000000], 'system': [30000000, 33000000] } if not RANGES[location_type][0] <= location_id <= RANGES[ location_type][1] and location_id is not 0: return -1 if await self.TYPE_MODELS[location_type].filter(pk=location_id ).exists(): return await self.TYPE_MODELS[location_type].filter(pk=location_id ).first() if location_id is not 0: post_op = self.bot.esi_app.op['post_universe_names']( ids=[location_id]) response = self.esi.request(post_op) if location_type not in response.data[0]['category']: logger.debug(response.data) return -1 model_kwargs = { f'{location_type}_id': response.data[0]['id'], 'name': response.data[0]['name'] } location = self.TYPE_MODELS[location_type](**model_kwargs) await location.save() else: # If we want to add all regions take care of this special case. location = self.TYPE_MODELS[location_type](name="All Regions", region_id=0) await location.save() return location async def location_from_name(self, location_type: str, location_name: str): """ Returns the location object for a location using its name. :param location_type: string :param location_name: string :return: Thera location model. """ plural = f'{location_type}s' # Check the DB for the item if await self.TYPE_MODELS[location_type].filter(name=location_name ).exists(): return await self.TYPE_MODELS[location_type].filter( name=location_name).first() if location_name.lower() != "all regions": # Get the system ID from ESI. post_op = self.bot.esi_app.op['post_universe_ids']( names=[location_name]) response = self.esi.request(post_op) if plural not in response.data: return -1 model_kwargs = { f'{location_type}_id': response.data[plural][0]['id'], 'name': response.data[plural][0]['name'] } location = self.TYPE_MODELS[location_type](**model_kwargs) await location.save() else: # If we want to add all regions take care of this special case. location = self.TYPE_MODELS[location_type](name="All Regions", region_id=0) await location.save() return location async def load_last(self): """ Loads the last thera id seen. """ last_thera = await LastThera.first() if last_thera is not None: self.last_thera = last_thera.last_thera @commands.command(aliases=['set_tc', 'tc']) @checks.is_admin() async def set_thera_channel(self, ctx): """ Set the channel to post new thera connections into. """ channels = await TheraChannel.filter(pk=ctx.guild.id) if len(channels) is not 0: return await ctx.send( f'Thera channel already set for `{ctx.guild.name}`. ' f'To change it, please first unset the thera channel using ' f'the `unset_thera_channel` command.') # Set the Thera Channel guild_id = ctx.guild.id channel_id = ctx.channel.id channel = TheraChannel(guild_id=guild_id, channel_id=channel_id) await channel.save() return await ctx.send( f'{ctx.channel.mention} has been set as the ' f'Thera notifications channel for `{ctx.guild.name}`') @commands.command(aliases=['utc', 'delete_tc']) @checks.is_admin() async def unset_thera_channel(self, ctx): """ This command unsets the channel used for thera connection posts. Note: This command can be run from any channel. """ channels = await TheraChannel.filter(pk=ctx.guild.id) if len(channels) is 0: return await ctx.send( f'Thera channel not set for `{ctx.guild.name}`. ' f'Use the `set_thera_channel` command from ' f'the target channel to set it.') await channels[0].delete() # Update self.channels await self.load_channels() # Reloading channels is easier on delete. return await ctx.send(f'Unset thera channel for `{ctx.guild.name}`') @commands.command(aliases=['ts', 'tsys']) @checks.is_admin() async def thera_system(self, ctx, action, *, system): """ Add or remove watchlisted system. Both names and IDs are accepted. Valid Actions: add, remove """ # Get the TheraChannel channel = await TheraChannel.filter(pk=ctx.guild.id).first() if channel is None: return await ctx.send( f'A thera notifications channel must be set up before you can manage watchlisted ' f'locations. To set a thera notifications channel run the ' f'`set_thera_channel` from the target channel.') # First check if we have a name or id name = not system.isnumeric() # Get / Validate the system ID system_obj = None if name: # Get the system ID from ESI. system_obj = await self.location_from_name('system', system) if system_obj == -1: return await ctx.send( f'The system name provided does not appear to be valid. Please ' f'check the spelling and try again or try adding the system by ID.' ) else: system = int(system) system_obj = await self.location_from_id('system', system) if system_obj == -1: return await ctx.send( f'The system ID provided is not valid. Please check the ID and try again ' f'or try adding the system by name.') if action.lower() == 'add': # Make sure we dont have the same system on the list twice. if system_obj in await channel.systems.all(): return await ctx.send( f'System `{system}` already on watchlist.') # Add the system to the list and save the model. await channel.systems.add(system_obj) elif action.lower() == 'remove': # Make sure the system is on the list. if system_obj not in await channel.systems.all(): return await ctx.send( f'System `{system}` is not on the watchlist.') await channel.systems.remove(system_obj) else: return await ctx.send( f'`{action}` is an invalid action. Valid actions are `add` or `remove`.' ) actioned = f'{action}ed' await self.update_channels('system', system_obj) return await ctx.send(f'Watchlist System `{system}` {actioned}.') @commands.command(aliases=['tcon']) @checks.is_admin() async def thera_constellation(self, ctx, action, *, constellation): """ Add or remove a watchlisted constellation. Both names and IDs accepted. Valid Actions: add, remove """ # Get the TheraChannel channel = await TheraChannel.filter(pk=ctx.guild.id).first() if channel is None: return await ctx.send( f'A thera notifications channel must be set up before you can manage watchlisted ' f'locations. To set a thera notifications channel run the ' f'`set_thera_channel` from the target channel.') name = not constellation.isnumeric() constellation_obj = None # Get / Validate constellation id if name: # Get the constellation ID from ESI constellation_obj = await self.location_from_name( 'constellation', constellation) if constellation_obj == -1: return await ctx.send( f'The constellation name provided does not appear to be valid. ' f'Please check the spelling and try again or try adding using its ID.' ) else: constellation = int(constellation) constellation_obj = await self.location_from_id( 'constellation', constellation) if constellation_obj == -1: return await ctx.send( f'The constellation ID provided is not valid. Please check the ID and ' f'try again or try adding using its name.') if action.lower() == 'add': if constellation_obj in await channel.constellations.all(): return await ctx.send( f'Constellation `{constellation}` is already on watchlist.' ) await channel.constellations.add(constellation_obj) elif action.lower() == 'remove': if constellation_obj not in await channel.constellations.all(): return await ctx.send( f'Constellation `{constellation}` is not on the watchlist.' ) await channel.constellations.remove(constellation_obj) else: return await ctx.send( f'`{action}` is an invalid action. Valid actions are `add` and `remove`' ) await self.update_channels('constellation', constellation_obj) return await ctx.send( f'Watchlist Constellation `{constellation}` {action}ed') @commands.command(aliases=['tr', 'treg']) @checks.is_admin() async def thera_region(self, ctx, action, *, region): """ Add or remove a watchlisted region. Both names and IDs accepted. Valid Actions: add, remove """ # Get the TheraChannel channel = await TheraChannel.filter(pk=ctx.guild.id).first() if channel is None: return await ctx.send( f'A thera notifications channel must be set up before you can manage watchlisted ' f'locations. To set a thera notifications channel run the ' f'`set_thera_channel` from the target channel.') name = not region.isnumeric() region_obj = None # Get / Validate region id if name: # Get the region ID from ESI region_obj = await self.location_from_name('region', region) if region_obj == -1: return await ctx.send( f'The region name provided does not appear to be valid. Please check ' f'the spelling and try again or try adding it by ID.') else: region = int(region) region_obj = await self.location_from_id('region', region) if region_obj == -1: return await ctx.send( f'The region ID provided is not valid. Please check the ID and try again ' f'or try adding it by name.') if action.lower() == 'add': if region_obj in await channel.regions.all(): return await ctx.send(f'`{region}` already on the watchlist.') await channel.regions.add(region_obj) elif action.lower() == 'remove': if region_obj not in await channel.regions.all(): return await ctx.send(f'`{region}` is not on the watchlist.') await channel.regions.remove(region_obj) else: return await ctx.send( f'`{action}` is an invalid action. Valid actions are `add` and `remove`.' ) await self.update_channels('region', region_obj) return await ctx.send(f'Watchlist Region `{region}` {action}ed.') @tasks.loop(seconds=60.0) async def thera(self): url = 'https://www.eve-scout.com/api/wormholes' try: async with aiohttp.ClientSession() as session: resp = await get(session, url) hole = list(resp['resp'])[0] hole_id = hole['id'] source = hole['sourceSolarSystem'] if self.last_thera <= hole_id: pass # Do nothing elif source['name'] == "Thera": self.last_thera = hole_id # Ensure we keep track of the last thera id destination = hole['destinationSolarSystem'] if destination['id'] in self.channels['systems']: await self.process_hole(hole) elif destination['constellationID'] in self.channels[ 'constellations']: await self.process_hole(hole) elif destination['regionId'] in self.channels[ 'regions'] or 0 in self.channels['regions']: await self.process_hole(hole) except Exception as e: logger.warning("Exception occurred in thera loop.") logger.warning(traceback.format_exc()) @thera.before_loop async def before_thera(self): # Wait until the bot is ready await self.bot.wait_until_ready() # Load channels and the last thera id. await self.load_channels() await self.load_last() @thera.after_loop async def on_thera_cancel(self): if self.thera.is_being_cancelled(): if self.last_thera is not None: # Save last_thera. last_thera_obj = await LastThera.first() if last_thera_obj is None: new_obj = LastThera(last_thera=self.last_thera) await new_obj.save() else: last_thera_obj.last_thera = self.last_thera await last_thera_obj.save() async def process_hole(self, hole): """ Build the embed for a given hole, and build a dict of which channels to send it to. :param hole: :return: """ # Pull info from hole dict d_system = hole['destinationSolarSystem'] hole_type = hole['destinationWormholeType']['name'] if hole_type == 'K162': hole_type = hole['sourceWormholeType']['name'] system = d_system['name'] region = d_system['region']['name'] c_id = d_system['constellationID'] try: c_name = self.esi.request( self.bot.esi_app. op['get_universe_constellations_constellation_id']( constellation_id=c_id)).data['name'] except: print("oops") in_sig = hole['wormholeDestinationSignatureId'] out_sig = hole['signatureId'] # Build Discord Embed embed = discord.Embed(title="Thera Alert", color=discord.Color.blurple()) embed.set_author( name='EVE-Scout', icon_url= 'http://games.chruker.dk/eve_online/graphics/ids/128/20956.jpg') embed.set_thumbnail( url='https://www.eve-scout.com/images/eve-scout-logo.png') embed.set_footer(text=f"ID: {hole['id']}") embed.add_field(name='Region', value=region, inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field embed.add_field(name='System (Constellation)', value=f'{system} ({c_name})', inline=True) embed.add_field(name='Signature (In - Out)', value=f'`{in_sig}` - `{out_sig}`', inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field embed.add_field(name='Type', value=hole_type, inline=True) # Build channels for this hole send_channels = { 'system': list(), 'constellation': list(), 'region': list() } if d_system['id'] in self.channels['systems']: send_channels['system'] += self.channels['systems'][d_system['id']] elif d_system['constellationID'] in self.channels['constellations']: send_channels['constellation'] += self.channels['constellation'][ d_system['constellationId']] elif d_system['regionId'] in self.channels[ 'regions'] or 0 in self.channels['regions']: if 0 in self.channels[ 'regions']: # Send to anyone that specified all regions send_channels['region'] += self.channels['regions'][0] if d_system['regionId'] in self.channels[ 'regions']: # Send to anyone that specified *this* region. send_channels['region'] += self.channels['regions'][ d_system['regionId']] return await self.send_thera(embed, send_channels) async def send_thera(self, embed: discord.Embed, channels: dict): mentions = { 'system': "@everyone", 'constellation': "@here", 'region': "" } for k, v in channels.items(): for c in v: # Get channel channel = self.bot.get_channel(c.channel_id) await channel.send(content=mentions[k], embed=embed)
class TestEsiPy(unittest.TestCase): CALLBACK_URI = "https://foo.bar/baz/callback" LOGIN_EVE = "https://login.eveonline.com" OAUTH_VERIFY = "%s/oauth/verify" % LOGIN_EVE OAUTH_TOKEN = "%s/oauth/token" % LOGIN_EVE CLIENT_ID = 'foo' SECRET_KEY = 'bar' BASIC_TOKEN = six.u('Zm9vOmJhcg==') SECURITY_NAME = 'evesso' @mock.patch('six.moves.urllib.request.urlopen') def setUp(self, urlopen_mock): # I hate those mock... thx urlopen instead of requests... urlopen_mock.return_value = open('test/resources/swagger.json') self.app = App.create('https://esi.tech.ccp.is/latest/swagger.json') self.security = EsiSecurity( app=self.app, redirect_uri=TestEsiPy.CALLBACK_URI, client_id=TestEsiPy.CLIENT_ID, secret_key=TestEsiPy.SECRET_KEY, ) self.cache = DictCache() self.client = EsiClient(self.security, cache=self.cache) self.client_no_auth = EsiClient(cache=self.cache, retry_requests=True) def tearDown(self): """ clear the cache so we don't have residual data """ self.cache._dict = {} def test_esipy_client_no_args(self): client_no_args = EsiClient() self.assertIsNone(client_no_args.security) self.assertTrue(isinstance(client_no_args.cache, DictCache)) self.assertEqual(client_no_args._session.headers['User-Agent'], 'EsiPy/Client - https://github.com/Kyria/EsiPy') self.assertEqual(client_no_args.raw_body_only, False) def test_esipy_client_with_headers(self): client_with_headers = EsiClient(headers={'User-Agent': 'foobar'}) self.assertEqual(client_with_headers._session.headers['User-Agent'], 'foobar') def test_esipy_client_with_adapter(self): transport_adapter = HTTPAdapter() client_with_adapters = EsiClient(transport_adapter=transport_adapter) self.assertEqual(client_with_adapters._session.get_adapter('http://'), transport_adapter) self.assertEqual(client_with_adapters._session.get_adapter('https://'), transport_adapter) def test_esipy_client_without_cache(self): client_without_cache = EsiClient(cache=None) self.assertTrue(isinstance(client_without_cache.cache, DummyCache)) def test_esipy_client_with_cache(self): cache = DictCache() client_with_cache = EsiClient(cache=cache) self.assertTrue(isinstance(client_with_cache.cache, BaseCache)) self.assertEqual(client_with_cache.cache, cache) def test_esipy_client_wrong_cache(self): with self.assertRaises(ValueError): EsiClient(cache=DictCache) def test_esipy_request_public(self): with httmock.HTTMock(public_incursion): incursions = self.client_no_auth.request( self.app.op['get_incursions']()) self.assertEqual(incursions.data[0].type, 'Incursion') self.assertEqual(incursions.data[0].faction_id, 500019) def test_esipy_request_authed(self): with httmock.HTTMock(*_all_auth_mock_): self.security.auth('let it bee') char_location = self.client.request( self.app.op['get_characters_character_id_location']( character_id=123456789)) self.assertEqual(char_location.data.station_id, 60004756) # force expire self.security.token_expiry = 0 char_location_with_refresh = self.client.request( self.app.op['get_characters_character_id_location']( character_id=123456789)) self.assertEqual(char_location_with_refresh.data.station_id, 60004756) def test_client_cache_request(self): @httmock.all_requests def fail_if_request(url, request): self.fail('Cached data is not supposed to do requests') incursion_operation = self.app.op['get_incursions'] with httmock.HTTMock(public_incursion_no_expires): incursions = self.client_no_auth.request(incursion_operation()) self.assertEqual(incursions.data[0].state, 'mobilizing') with httmock.HTTMock(public_incursion_no_expires_second): incursions = self.client_no_auth.request(incursion_operation()) self.assertEqual(incursions.data[0].state, 'established') with httmock.HTTMock(public_incursion): incursions = self.client_no_auth.request(incursion_operation()) self.assertEqual(incursions.data[0].state, 'mobilizing') with httmock.HTTMock(fail_if_request): incursions = self.client_no_auth.request(incursion_operation()) self.assertEqual(incursions.data[0].state, 'mobilizing') def test_client_warning_header(self): with httmock.HTTMock(public_incursion_warning): warnings.simplefilter('error') incursion_operation = self.app.op['get_incursions'] with self.assertRaises(UserWarning): self.client_no_auth.request(incursion_operation()) def test_client_raw_body_only(self): client = EsiClient(raw_body_only=True) self.assertEqual(client.raw_body_only, True) with httmock.HTTMock(public_incursion): incursions = client.request(self.app.op['get_incursions']()) self.assertIsNone(incursions.data) self.assertTrue(len(incursions.raw) > 0) incursions = client.request(self.app.op['get_incursions'](), raw_body_only=False) self.assertIsNotNone(incursions.data) def test_esipy_reuse_operation(self): operation = self.app.op['get_incursions']() with httmock.HTTMock(public_incursion): incursions = self.client_no_auth.request(operation) self.assertEqual(incursions.data[0].faction_id, 500019) # this shouldn't create any errors incursions = self.client_no_auth.request(operation) self.assertEqual(incursions.data[0].faction_id, 500019) def test_esipy_multi_request(self): operation = self.app.op['get_incursions']() with httmock.HTTMock(public_incursion): count = 0 for req, incursions in self.client_no_auth.multi_request( [operation, operation, operation], threads=2): self.assertEqual(incursions.data[0].faction_id, 500019) count += 1 # Check we made 3 requests self.assertEqual(count, 3) def test_esipy_backoff(self): operation = self.app.op['get_incursions']() start_calls = time.time() with httmock.HTTMock(public_incursion_server_error): incursions = self.client_no_auth.request(operation) self.assertEqual(incursions.data.error, 'broke') end_calls = time.time() # Check we retried 5 times self.assertEqual(incursions.data.count, 5) # Check that backoff slept for a sum > 2 seconds self.assertTrue(end_calls - start_calls > 2) def test_esipy_timeout(self): def send_function(*args, **kwargs): """ manually create a ConnectionError to test the retry and be sure no exception is thrown """ send_function.count += 1 raise ConnectionError send_function.count = 0 self.client_no_auth._session.send = mock.MagicMock( side_effect=send_function) operation = self.app.op['get_incursions']() with httmock.HTTMock(public_incursion): incursions = self.client_no_auth.request(operation) # there shouldn't be any exceptions self.assertEqual(incursions.status, 500) self.assertEqual(send_function.count, 5)
class ESI: def __init__(self): self.db = Database() self.config = Config() self.scopes = self.config.getConfig()["settings"]["esiScopes"] self.esi_app = App.create( url=self.config.getConfig()["settings"]["esiURL"], ) self.security = EsiSecurity( app=self.esi_app, redirect_uri=self.config.getConfig()["settings"]["esiCallback"], client_id=self.config.getConfig()["settings"]["esiClientID"], secret_key=self.config.getConfig()["settings"]["esiSecretKey"], headers={ 'User-Agent': self.config.getConfig()["settings"]["esiCustomHeader"] }) self.client = EsiClient( security=self.security, retry_requests=True, headers={ 'User-Agent': self.config.getConfig()["settings"]["esiCustomHeader"] }) def getAuthURI(self): return self.security.get_auth_uri(scopes=self.scopes) def getToken(self, code): return self.security.auth(code) def getESIChar(self, token): self.security.update_token(token) try: self.security.refresh() except APIException as e: if str(e) == "HTTP Error 400: invalid_token": session.pop('token', None) session.pop('char', None) return redirect(url_for('page_routes.logout')) return self.security.verify() def isVerified(self, token): try: self.security.update_token(token) except: return False try: self.security.refresh() character = self.security.verify() except: return False session["char"] = character return True def getESIInfo(self, endpoint, obj): info = self.esi_app.op[endpoint](**obj) res = self.client.request(info) result = res.data try: if "response" in result: result = result["response"] except: pass return result def getESIInfoMP(self, endpoint, obj): info = self.esi_app.op[endpoint](**obj) res = self.client.head(info) if res.status == 200: number_of_pages = res.header["X-Pages"][0] ops = [] for page in range(1, number_of_pages + 1): obj["page"] = page ops.append(self.esi_app.op[endpoint](**obj)) results = self.client.multi_request(ops) return results return {} def subToken(self, refresh_token): self.security.update_token({ 'access_token': '', 'expires_in': -1, 'refresh_token': refresh_token }) def getForceRefresh(self): return self.security.refresh()
# with url = the swagger spec URL, leave strict to default app = App.create( url="https://esi.tech.ccp.is/latest/swagger.json?datasource=tranquility") # basic client, for public endpoints only client = EsiClient( retry_requests=True, # set to retry on http 5xx error (default False) header={ 'User-Agent': 'Something CCP can use to contact you and that define your app' }, raw_body_only= False, # default False, set to True to never parse response and only return raw JSON string content. ) # generate the operation tuple # the parameters given are the actual parameters the endpoint requires market_order_operation = app.op['get_markets_region_id_orders']( region_id=10000002, type_id=34, order_type='all', ) # do the request response = client.request(market_order_operation) # use it: response.data contains the parsed result of the request. print response.data[0].price # to get the headers objects, you can get the header attribute print response.header
verify=esi_security.verify() #Get access token and time token from esipy code after successful refresh (expiry method is custom changed) accesscode = esi_security._EsiSecurity__get_token_auth_header() accesscode = accesscode['Authorization'][8:] timestp = esi_security.is_token_expired2() #custom changed #The following is processing and your required data inquiry from ESI network myid = verify['CharacterID'] myname = verify['CharacterName'] wallet = esi_app.op['get_characters_character_id_wallets']( character_id =myid ) walletdata = esi_client.request(wallet) #Present output print "%s's ISK:" % (myname) print (intWithCommas(int(float(walletdata.data[0]['balance'])/100))) #Optional, use dictionary for idcheck = "type_id in question" #print result[str(idcheck)][0]
from esipy import App from esipy import EsiClient import json app = App.create( url="https://esi.tech.ccp.is/latest/swagger.json?datasource=tranquility") client = EsiClient( retry_requests=True, header={'User-Agent': 'DumpSkills'}, raw_body_only=False, ) fields = app.op['get_universe_categories_category_id'](category_id=16, ) response = client.request(fields) groups = response.data['groups'] gral = {} for group in groups: g = app.op['get_universe_groups_group_id'](group_id=group) response = client.request(g) group_name = response.data['name'] skills = response.data['types'] group_dict = {} for skill in skills: s = app.op['get_universe_types_type_id'](type_id=skill) response = client.request(s) group_dict[skill] = response.data['name'] gral[group_name] = [{'Category ID': group}, group_dict]
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'} print("Downloading json files of killmails") for date in cfg.dates: url = "https://zkillboard.com/api/history/" + date + ".json" print("Downloaded killmail list on " + date) response = requests.get(url, headers=headers) kill_dict = json.loads(response.content) print("Parsing kills on " + date) count = 0 for key in kill_dict: count = count + 1 if count % 100 == 0: print(count) killmail_fetch_operation = app.op['get_killmails_killmail_id_killmail_hash']( killmail_id=key, killmail_hash=kill_dict[key] ) response = client.request(killmail_fetch_operation) kill_as_dict = json.loads(response.raw) timestamp = kill_as_dict["killmail_time"] timestamp = datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%SZ') timestamp = timestamp.replace(tzinfo=utc) insert_query = ( "INSERT INTO killmails (killmail_id, killmail_hash, date, data) VALUES (%s, %s, %s, %s) ON CONFLICT DO NOTHING;" ) cursor.execute(insert_query, (key, kill_dict[key], timestamp, json.dumps(kill_as_dict)))
# and the client object, replace the header user agent value with something reliable ! client = EsiClient(retry_requests=True, header={'User-Agent': '*****@*****.**'}, security=security) # to update the security object, security.update_token({ 'access_token': '', # leave this empty 'expires_in': -1, # seconds until expiry, so we force refresh anyway 'refresh_token': 'yUI96Q6kOxvQ6ulwDnQ7VLD1I-4LH-Jz8DOY9Di4NhA1' }) tokens = security.refresh() # print tokens op = app.op['get_markets_structures_structure_id'](structure_id=1022734985679, page='-1') structure = client.request(op) y = 0 for x in structure.data: print str(structure.data[y].type_id) + ', ' + str( structure.data[y].price) + ', ' + str(structure.data[y].is_buy_order) y += 1
class EsiCommands(Cog): def __init__(self, bot): self.bot = bot self.esi = EsiClient( retry_requests=True, headers={ 'User-Agent': f'application: MercuryBot contact: {self.bot.config["bot"]["user_agent"]}' }) def _get_esi_id(self, search: str, category: str, strict: bool) -> Union[list, int]: """ Returns the ID for the specified query from ESI. :param search: string representing the search query :param category: the category to search against :param strict: :return: int ID if strict is true, otherwise list of IDs; -1 if not found """ search_op = self.bot.esi_app.op['get_search']( categories=[category], search=search, strict=strict, ) search_response = self.esi.request(search_op) if category not in search_response.data: return -1 if strict: return search_response.data[category][0] else: return search_response.data[category] def _get_char_from_esi(self, char_id: int) -> Optional[dict]: """ Gets public data for the specified character_id. :param char_id: :return: """ char_op = self.bot.esi_app.op['get_characters_character_id']( character_id=char_id, ) try: char_response = self.esi.request(char_op) except APIException as e: logger.error( f"Error getting char with id {char_id} from ESI! Error: {e}") logger.error(traceback.print_exc()) return None return char_response.data def _get_alliance_from_esi(self, ally_id: int) -> Optional[dict]: """ Gets public data for the specified alliance_id :param ally_id: :return: """ ally_op = self.bot.esi_app.op['get_alliances_alliance_id']( alliance_id=ally_id, ) try: ally_response = self.esi.request(ally_op) except APIException as e: logger.error( f"Error getting alliance with id {ally_id} from ESI! Error: {e}" ) logger.error(traceback.print_exc()) return None return ally_response.data def _get_corporation_from_esi(self, corp_id: int) -> Optional[dict]: """ Gets public data for the specified corporation_id :param corp_id: :return: """ corp_op = self.bot.esi_app.op['get_corporations_corporation_id']( corporation_id=corp_id, ) try: corp_response = self.esi.request(corp_op) except APIException as e: logger.error( f"Error getting corporation with id {corp_id} from ESI! Error: {e}" ) logger.error(traceback.print_exc()) return None return corp_response.data def _get_system_from_esi(self, system_id: int) -> Optional[dict]: """ Gets static system data from esi for the specified system_id :param system_id: :return: """ sys_op = self.bot.esi_app.op['get_universe_systems_system_id']( system_id=system_id, ) try: system_response = self.esi.request(sys_op) except APIException as e: logger.error( f"Error getting system with id {system_id} from ESI! Error: {e}" ) logger.error(traceback.print_exc()) return None return system_response.data def _get_region_from_esi(self, region_id: int) -> Optional[dict]: """ Gets static region data from esi for the specified region_id. :param region_id: :return: """ reg_op = self.bot.esi_app.op['get_universe_regions_region_id']( region_id=region_id) try: region_response = self.esi.request(reg_op) except APIException as e: logger.error( f"Error getting region with id {region_id} from ESI! Error: {e}" ) logger.error(traceback.print_exc()) return None return region_response.data def _get_constellation_from_esi(self, constellation_id: int) -> Optional[dict]: """ Gets static constellation data from ESI for the specified constellation_id. :param constellation_id: :return: """ const_op = self.bot.esi_app.op[ 'get_universe_constellations_constellation_id']( constellation_id=constellation_id) try: constellation_response = self.esi.request(const_op) except APIException as e: logger.error( f"Error getting constellation with id {constellation_id} from ESI! Error: {e}" ) logger.error(traceback.print_exc()) return None return constellation_response.data def _get_star_from_esi(self, star_id: int) -> Optional[dict]: """ Gets the static star data from ESI for the given star_id. :param star_id: :return: """ star_op = self.bot.esi_app.op['get_universe_stars_star_id']( star_id=star_id) try: star_response = self.esi.request(star_op) except APIException as e: logger.error( f"Error getting star with id {star_id} from ESI! Error: {a}") logger.error(traceback.print_exc()) return None return star_response.data def _get_system_stats(self, system_id: int) -> Optional[dict]: """ Returns the following data for a given system: - Jumps - Kills - Sovereignty :param system_id: :return: """ # Get System Stats jump_op = self.bot.esi_app.op['get_universe_system_jumps']() kills_op = self.bot.esi_app.op['get_universe_system_kills']() sov_op = self.bot.esi_app.op['get_sovereignty_map']() try: jump_response = self.esi.request(jump_op) kills_response = self.esi.request(kills_op) sov_response = self.esi.request(sov_op) except APIException as e: logger.error(f"Error getting system stats from ESI! Error: {e}") logger.error(traceback.print_exc()) return None ret = dict() ret['sov'] = None for sys in jump_response.data: if sys['system_id'] == system_id: ret['ship_jumps'] = sys['ship_jumps'] break for sys in kills_response.data: if sys['system_id'] == system_id: ret['kills'] = sys break for sys in sov_response.data: if sys['system_id'] == system_id: if len(sys) == 1: break ret['sov'] = sys break return ret @commands.command(aliases=('char', 'ch')) async def character(self, ctx, *, character_name: str): """ Returns public data about the named character. """ # Get ID from ESI char_id = self._get_esi_id(character_name, "character", True) if char_id == -1: return await ctx.send( "Character not found. Please check your spelling and try again." ) # Get Public data char = self._get_char_from_esi(char_id) if char is None: return await ctx.send( "Something went wrong, please try again later.") corp = self._get_corporation_from_esi(char['corporation_id']) if corp is None: return await ctx.send( "Something went wrong, please try again later.") urln = quote_plus(char['name']) urls = { 'zkb': f'https://zkillboard.com/character/{char_id}/', 'who': f'https://evewho.com/pilot/{urln}' } dob = char['birthday'].v age = datetime.now(timezone.utc) - dob embed = discord.Embed(title=f'{char["name"]} Character Info') embed.set_author(name=self.bot.user.name, icon_url=self.bot.user.avatar_url_as(format='png')) embed.set_thumbnail( url=f'https://imageserver.eveonline.com/Character/{char_id}_128.jpg' ) # Fields embed.add_field(name='Corporation', value=f'{corp["name"]} [{corp["ticker"]}]', inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field if corp['alliance_id'] is not None: ally = self._get_alliance_from_esi(corp['alliance_id']) if ally is None: return await ctx.send( "Something went wrong, please try again later.") embed.add_field(name='Alliance', value=f'{ally["name"]} [{ally["ticker"]}]') embed.add_field(name='Birthday', value=dob.strftime("%a %d %b, %Y"), inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field embed.add_field(name='Age', value=strftdelta(age), inline=True) embed.add_field(name='Additional Information', value=f'{urls["zkb"]}\n{urls["who"]}', inline=False) return await ctx.send(embed=embed) @commands.command(aliases=('corp', 'co')) async def corporation(self, ctx, *, corporation: str): """ Returns public data about the specified corporation. """ # Get ID corp_id = self._get_esi_id(corporation, "corporation", True) if corp_id == -1: return await ctx.send( "Corporation not found. Please check your spelling and try again." ) # Get Corp data corp = self._get_corporation_from_esi(corp_id) if corp is None: return await ctx.send( "Something went wrong, please try again later.") ceo = self._get_char_from_esi(corp['ceo_id']) if ceo is None: return await ctx.send( "Something went wrong, please try again later.") urls = { 'zkb': f'https://zkillboard.com/corporation/{corp_id}/', 'dotlan': f'https://evemaps.dotlan.net/corp/{corp_id}' } embed = discord.Embed(title=f'{corp["name"]} Corp Info', color=discord.Color.green()) embed.set_author(name=self.bot.user.name, icon_url=self.bot.user.avatar_url_as(format='png')) embed.set_thumbnail( url= f'https://imageserver.eveonline.com/Corporation/{corp_id}_128.png') # Fields embed.add_field(name='Ticker', value=f'[{corp["ticker"]}]', inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field embed.add_field(name='Member Count', value=f'{corp["member_count"]}', inline=True) embed.add_field(name='CEO', value=f'{ceo["name"]}', inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field if corp['date_founded'] is not None: embed.add_field( name='Founded', value=corp["date_founded"].v.strftime("%a %d %b, %Y"), inline=True) if corp['alliance_id'] is not None: ally = self._get_alliance_from_esi(corp['alliance_id']) if ally is None: return ctx.send( "Something went wrong, please try again later.") embed.add_field(name='Alliance', value=f'{ally["name"]} [{ally["ticker"]}]', inline=False) embed.add_field(name='Additional Information', value=f'{urls["zkb"]}\n{urls["dotlan"]}', inline=False) return await ctx.send(embed=embed) @commands.command(aliases=('ally', )) async def alliance(self, ctx, *, alliance: str): """ Returns public data about the specified alliance. """ ally_id = self._get_esi_id(alliance, "alliance", True) if ally_id == -1: return await ctx.send( "Alliance not found. Please check your spelling and try again." ) ally = self._get_alliance_from_esi(ally_id) if ally is None: return await ctx.send( "Something went wrong, please try again later.") found_corp = self._get_corporation_from_esi( ally['creator_corporation_id']) if found_corp is None: return await ctx.send( "Something went wrong, please try again later.") founder = self._get_char_from_esi(ally['creator_id']) if founder is None: return await ctx.send( "Something went wrong, please try again later.") exec_corp = None if 'executor_corporation_id' in ally: exec_corp = self._get_corporation_from_esi( ally['executor_corporation_id']) if exec_corp is None: return await ctx.send( "Something went wrong, please try again later.") urls = { 'zkb': f'https://zkillboard.com/alliance/{ally_id}/', 'dotlan': f'https://evemaps.dotlan.net/alliance/{ally_id}' } title = f'{ally["name"]} Alliance Info' if exec_corp is None: title = title + ' (Closed)' embed = discord.Embed(title=title, color=discord.Color.blue()) embed.set_author(name=self.bot.user.name, icon_url=self.bot.user.avatar_url_as(format='png')) embed.set_thumbnail( url=f'https://imageserver.eveonline.com/Alliance/{ally_id}_128.png' ) # Fields embed.add_field(name='Ticker', value=f'[{ally["ticker"]}]', inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field if exec_corp is not None: embed.add_field( name='Executor Corp', value=f'{exec_corp["name"]} [{exec_corp["ticker"]}]', inline=True) embed.add_field(name='Founder', value=f'{founder["name"]}', inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field embed.add_field(name='Founding Corp', value=f'{found_corp["name"]} [{found_corp["ticker"]}]', inline=True) embed.add_field(name='Founding Date', value=ally["date_founded"].v.strftime("%a %d %b, %Y"), inline=True) embed.add_field(name='Additional Information', value=f'{urls["zkb"]}\n{urls["dotlan"]}', inline=False) return await ctx.send(embed=embed) @commands.command() async def status(self, ctx): """ Returns the current status of Tranquility """ # Get ESI status status_op = self.bot.esi_app.op['get_status']() try: status_response = self.esi.request(status_op) status_response = status_response.data except APIException as e: embed = discord.Embed(title="Tranquility Status", color=discord.Color.red()) embed.set_author( name=self.bot.user.name, icon_url=self.bot.user.avatar_url_as(format='png')) embed.set_thumbnail( url= f'https://en.wikipedia.org/wiki/CCP_Games#/media/File:CCP_Games_Logo.svg' ) embed.add_field(name="Status", value="*Offline*", inline=True) return await ctx.send(embed=embed) embed = discord.Embed(title="Tranquility Status", color=discord.Color.green()) embed.set_author(name=self.bot.user.name, icon_url=self.bot.user.avatar_url_as(format='png')) embed.set_thumbnail( url=f'https://e.dotlan.net/images/Alliance/434243723_128.png') status = "Online" if "vip" in status_response: if status_response["vip"]: status = f"*{status} (VIP)*" embed.add_field(name="Status", value=status, inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field embed.add_field(name="Player Count", value='{:,}'.format(status_response['players']), inline=True) embed.add_field(name="Server Version", value=status_response['server_version'], inline=True) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field embed.add_field(name="Uptime", value=strftdelta( datetime.now(timezone.utc) - status_response['start_time'].v), inline=True) return await ctx.send(embed=embed) @commands.command(aliases=('sys', )) async def system(self, ctx, *, system_name: str): """ Returns data about the specified system. """ if re.match(r'[Jj]([0-9]{6})', system_name) or system_name == "Thera": return await ctx.send( 'System Information not available for wormhole systems.') # Get System ID sys_id = self._get_esi_id(system_name, 'solar_system', True) if sys_id == -1: return await ctx.send( "System not found. Please check your spelling and try again.") # Get System Data system = self._get_system_from_esi(sys_id) if system is None: return await ctx.send( "Something went wrong, please try again later.") if 'planets' in system: planets = len(system['planets']) moons = 0 for planet in system['planets']: if 'moons' in planet: moons += len(planet['moons']) else: planets = 0 moons = 0 star = self._get_star_from_esi(system['star_id']) constellation = self._get_constellation_from_esi( system['constellation_id']) if constellation is None: return await ctx.send( "Something went wrong, please try again later.") region = self._get_region_from_esi(constellation['region_id']) if region is None: return await ctx.send( "Something went wrong, please try again later.") stats = self._get_system_stats(sys_id) thumb_url = f'https://images.evetech.net/types/{star["type_id"]}/icon' if stats['sov'] is not None: if 'faction_id' in stats['sov']: thumb_url = f'https://images.evetech.net/corporations/{stats["sov"]["faction_id"]}/logo' elif 'alliance_id' in stats['sov']: thumb_url = f'https://images.evetech.net/alliances/{stats["sov"]["alliance_id"]}/logo' elif 'corporation_id' in stats['sov']: thumb_url = f'https://images.evetech.net/corporations/{stats["sov"]["corporation_id"]}/logo' dotlan = f'http://evemaps.dotlan.net/system/{sys_id}/' zkill = f'https://zkillboard.com/system/{sys_id}/' embed = discord.Embed(title=f'{system["name"]} System Information') embed.set_author( name='CCP Games', icon_url='https://e.dotlan.net/images/Alliance/434243723_128.png') embed.set_thumbnail(url=thumb_url) # Fields embed.add_field( name='Sec Status / Class', value= f'{"{:.2f}".format(system["security_status"])} / {system["security_class"]}' ) embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field embed.add_field(name='Region (Constellation)', value=f'{region["name"]} ({constellation["name"]})') embed.add_field(name='Planets / Moons', value=f'{planets} / {moons}') embed.add_field(name='\u200B', value='\u200B', inline=True) # Empty Field if 'stargates' in system: embed.add_field(name='Stargates', value=str(len(system['stargates']))) embed.add_field(name='Stats (Last Hour)', value=f'**Jumps:** {stats["ship_jumps"]} \n' f'**Ship Kills**: {stats["kills"]["ship_kills"]} \n' f'**NPC Kills:** {stats["kills"]["npc_kills"]}\n' f'**Pod Kills:** {stats["kills"]["pod_kills"]}', inline=False) embed.add_field(name='Additional Info', value=f'{dotlan} \n{zkill}') return await ctx.send(embed=embed)
}) token = security.refresh() token #%% security.update_token({ 'access_token': '', 'expires_in': -1, 'refresh_token': 'Pc4C1iK6p0evpU3dBSkSwg==' }) token = security.refresh() #%% operation = app_latest.op['get_status']() client.request(operation).data #%% server_status = KIN3_Esi.check_server_status(esi_objects) 'players' in server_status #%% operation = app_latest.op['get_characters_character_id_fittings']( character_id='97199391') client.request(operation).data #%% operation = app_v2.op['get_characters_character_id_fleet']( character_id='97199391') fleet_id = client.request(operation).data['fleet_id'] fleet_id
'''This was the first attempt using esipy to get kill data from CCP. Quickly transistioned away from using esipy due to its slownes''' from esipy import App # App.create(url, strict=True) # with url = the swagger spec URL, leave strict to default app = App.create( url="https://esi.tech.ccp.is/latest/swagger.json?datasource=tranquility") #appS = App.create(url="https://esi.tech.ccp.is/latest/swagger.json?datasource=tranquility") from esipy import EsiClient # basic client, for public endpoints only client = EsiClient( retry_requests=True, # set to retry on http 5xx error (default False) header={ 'User-Agent': 'Something CCP can use to contact you and that define your app' }, raw_body_only= False, # default False, set to True to never parse response and only return raw JSON string content. ) # generate the operation tuple # the parameters given are the actual parameters the endpoint requires kills = app.op['get_universe_system_kills']() response = client.request(kills) print(response.data)
class ESI: def __init__(self, client_id, secret_key, refresh_token, cache_path, prefix='esipy'): self.client_id = client_id self.secret_key = secret_key self.refresh_token = refresh_token self.cache_path = cache_path self.prefix = prefix self._start() def _start(self): self.cache = FileCache(path=self.cache_path) self.esi_app = EsiApp(cache=self.cache, cache_prefix=self.prefix) self.app = self.esi_app.get_latest_swagger self.security = EsiSecurity( app=self.app, redirect_uri='http://localhost/oauth-callback', # This doesnt matter headers={ 'User-Agent': 'Discord bot by Prozn: https://github.com/prozn/dankcord' }, client_id=self.client_id, secret_key=self.secret_key, ) self.esi = EsiClient( retry_requests= False, # set to retry on http 5xx error (default False) headers={ 'User-Agent': 'Discord bot by Prozn: https://github.com/prozn/dankcord' }, security=self.security) self.security.update_token({ 'access_token': '', # leave this empty 'expires_in': -1, # seconds until expiry, so we force refresh anyway 'refresh_token': self.refresh_token }) self.security.refresh() @esiexcept def character_info(self, character_id): op = self.app.op['get_characters_character_id']( character_id=character_id) character = self.esi.request(op, raise_on_error=True) return character def character_name(self, character_id): character = self.character_info(character_id) if not character: return False return character.data.name @esiexcept def id_name(self, id): op = self.app.op['post_universe_names'](ids=[id]) names = self.esi.request(op, raise_on_error=True) return names.data[0].name @esiexcept def corp_contracts(self, corporation_id, raw=False): op = self.app.op['get_corporations_corporation_id_contracts']( corporation_id=corporation_id) contracts = self.esi.request(op, raise_on_error=True, raw_body_only=raw) if raw: return contracts.raw else: return contracts.data @esiexcept def get_system_name(self, system_id): op = self.app.op['get_universe_systems_system_id'](system_id=system_id) system = self.esi.request(op, raise_on_error=True) return system.data.name @esiexcept def location_details(self, location_id): if location_id > 1000000000000: # it is a citadel location_type = 'citadel' op = self.app.op['get_universe_structures_structure_id']( structure_id=location_id) else: # it is a station location_type = 'station' op = self.app.op['get_universe_stations_station_id']( station_id=location_id) location = self.esi.request(op, raise_on_error=True) if location_type == 'citadel': system = location.data.solar_system_id else: system = location.data.system_id details = { 'location_id': location_id, 'location_type': location_type, 'system_id': system, 'name': location.data.name } return details @esiexcept def personal_contracts(self): raise NotImplementedError
header={'User-Agent': '*****@*****.**'}, raw_body_only=False, ) # 'skill_points_required' : { # 1 : 250 * rank, # 2 : 1415 if rank == 1 else int(rank * 1414.3 + 0.5), # 3 : 8000 * rank, # # (2.5 * level - 2.5) # 4 : int(math.ceil((2.5 * 4 - 2.5)**2 * 250 * rank)), # 5 : 256000 * rank, # }, # Get market prices into a dict market_operation = app.op['markets_prices']() market_response = client.request(market_operation) market_info = market_response.data market_prices = { price.get('type_id'): price.get("average_price") for price in market_info } # Get Alpha skills into a dict alpha_skills = {} with open(alpha_skill_list, 'r') as fin: next(fin) reader = csv.reader(fin, delimiter='\t') for group, skill, cap, points in reader: alpha_skills[skill] = cap # Get skill groups
# YOUR_CODE is the code you got from Step 3. (do not forget quotes around it) tokens = security.auth(authcode) print(tokens) # use the verify endpoint to know who we are api_info = security.verify() print(api_info) # api_info contains data like this # { # "Scopes": "esi-wallet.read_character_wallet.v1", # "ExpiresOn": "2017-07-14T21:09:20", # "TokenType": "Character", # "CharacterName": "SOME Char", # "IntellectualProperty": "EVE", # "CharacterOwnerHash": "4raef4rea8aferfa+E=", # "CharacterID": 123456789 # } # now get the wallet data op = app.op['get_characters_character_id_wallet']( character_id=api_info['CharacterID'] ) wallet = client.request(op) # and to see the data behind, let's print it print(wallet.data) with open('tokens.txt', 'w') as outfile: json.dump(tokens, outfile)
class LinkListener(Cog): def __init__(self, bot): self.bot = bot self.esi = EsiClient( retry_requests=True, headers={ 'User-Agent': f'application: MercuryBot contact: {self.bot.config["bot"]["user_agent"]}' }) async def _get_killmail(self, kill_id: int) -> dict: """ Get killmail from zkill and ESI. :param kill_id: :return: """ # Get zkill data kill_api_url = f'https://zkillboard.com/api/killID/{kill_id}/' async with aiohttp.ClientSession() as session: zkill_km = await get_json(session, kill_api_url) zkill_km = zkill_km['resp'][0] # Get KM data via ESI km_op = self.bot.esi_app.op['get_killmails_killmail_id_killmail_hash']( killmail_id=kill_id, killmail_hash=zkill_km['zkb']['hash'], ) km_response = self.esi.request(km_op) km = km_response.data km['zkb'] = zkill_km['zkb'] return km async def _get_character_name_from_id(self, character_id: int) -> Optional[str]: """ Returns the name of a character for a given character_id. Returns None if the ID is not valid. :param character_id: :return: """ name_op = self.bot.esi_app.op["get_characters_character_id"]( character_id=character_id) try: name_response = self.esi.request(name_op) except APIException: logger.error( f'Error getting name for character with ID {character_id} from ESI.' ) logger.error(traceback.print_exc()) return None return name_response.data['name'] async def type_from_id(self, type_id: int) -> Optional[dict]: """ Returns a type ID from ESI for a given name. Return value of None indicates an invalid type name. :param type_id: :return: """ post_op = self.bot.esi_app.op['get_universe_types_type_id']( type_id=type_id) try: response = self.esi.request(post_op) except APIException: logger.error(f"Issue getting Type with ID {type_id} from ESI!") logger.error(traceback.print_exc()) return None return response.data @Cog.listener() async def on_message(self, message: discord.Message): if message.author == self.bot.user: return # Check for links re_match = re.match( r'(.*)(http[s]?://([A-Za-z]*).[a-zA-z]*(/[a-zA-z]*/?)([0-9]*)[a-zA-Z/]?)', message.content) """ Match Groups: Group 1 (match[1]): anything preceding the link. Group 2 (match[2]): The link in its entirety Group 3 (match[3]): The domain of the URL. This is how we determine if/how to process it. Group 4 (match[4]): Only used for zkill at the moment, to determine if the URL is a kill or not. Group 5 (match[5]): The ID we will need for processing. We know that all the services we want to process use only numeric IDs, so this is fine. (Though probably not the best if we wanted to add dscan.me support or something. """ if re_match: if re_match[3] == 'zkillboard': if re_match[4] == '/kill/': km = await self._get_killmail(re_match[5]) data = extract_mail_data(self.bot.esi_app, self.esi, km) embed = await build_kill_embed(data) return await message.reply(embed=embed) elif re_match[4] == '/character/': char_id = re_match[5] stats = await get_char_stats_from_zkill(char_id) if stats is None: return char_name = await self._get_character_name_from_id(char_id) embed = await build_threat_embed(stats, char_name, char_id) return await message.reply(embed=embed) else: return elif re_match[3] == 'evemarketer': type_id = re_match[5] type_data = await self.type_from_id(type_id) # Rename the type_id key for compatability with embed builder. type_data['id'] = type_data.pop('type_id') market_data = await get_market_data(type_id) embed = await build_market_embed(type_data, market_data['resp']) return await message.reply(embed=embed) else: return