From 871e3bee3eb9f9b1752c592d7bf02b5816466f94 Mon Sep 17 00:00:00 2001 From: Raatty Date: Sun, 4 Apr 2021 13:23:52 +1200 Subject: [PATCH 1/8] the begining of the rewrite --- .gitignore | 5 + LICENSE | 21 + MANIFEST.in | 1 - README.md | 5 + makefile | 11 - pyproject.toml | 6 + readme.txt | 443 --------------------- runescapeapi/__init__.py | 1 - runescapeapi/runescapeapi.py | 742 ----------------------------------- setup.cfg | 25 ++ setup.py | 17 - src/runescapeapi/__init__.py | 1 + 12 files changed, 63 insertions(+), 1215 deletions(-) create mode 100644 .gitignore create mode 100644 LICENSE delete mode 100644 MANIFEST.in create mode 100644 README.md delete mode 100644 makefile create mode 100644 pyproject.toml delete mode 100644 readme.txt delete mode 100644 runescapeapi/__init__.py delete mode 100644 runescapeapi/runescapeapi.py create mode 100644 setup.cfg delete mode 100644 setup.py create mode 100644 src/runescapeapi/__init__.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e15e5a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vscode +build +dist +venv +src/runescapeapi.egg-info diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..da7141a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Raatty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 86604e4..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include *.txt \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..52502ed --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# RuneScape API + +``` +pip install runescapeapi +``` \ No newline at end of file diff --git a/makefile b/makefile deleted file mode 100644 index 6bbe663..0000000 --- a/makefile +++ /dev/null @@ -1,11 +0,0 @@ -SHELL = bash - -install: venv - source venv/bin/activate; \ - python3 setup.py install; \ - -clean: - rm -rf venv build dist runescapeapi.egg-info - -venv: - python3 -m venv venv diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b5a3c46 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 9f6fd83..0000000 --- a/readme.txt +++ /dev/null @@ -1,443 +0,0 @@ -Help on module runescapeapi: - -NAME - runescapeapi - -DESCRIPTION - A slightly memes wrapper for the runescape api, hopefully helps people - best way to contact me with any problems is on discord raatty#3522 - read the rest of the docs for module usage step one is - import runescapeapi as runescape - the code in here might not be done in the best way but everything you - need should be there - -CLASSES - builtins.object - Beasts - Clan - GrandExchange - Highscores - Player - _Wikia(builtins.object) - Rs3Wikia - osrsWikia - - class Beasts(builtins.object) - | a bunch of methods for getting stats and seaching for monsters - | - | Static methods defined here: - | - | area_names() - | returns a list of area names - | usage: - | runescape.Beasts.area_names() - | it returns the same list every time so you should only need to call this - | once in your code - | - | by_area(area:str) - | returns a list of beasts in a specific area - | see runescape.Beasts.area_names() for the list - | usage: - | runescape.Beasts.by_area('Rat pits') - | gives you a list of all the beasts in the rat pits - | - | by_category(cat) - | returns a list of monsters of a given slayer category - | input a category id as an int or a str of the category name - | searching by string will take longer tho - | see runescape.Beasts.category_names() for categorys - | - | by_id(id:int) - | returns stats and info about a beast by id - | usage: - | runescape.Beasts.by_id(47) - | returns information about level 1 rats - | to get the ids use runescape.Beasts.search(name) - | - | by_letter(letter:str) - | list of beasts starting with a letter - | usage: - | runescape.Beasts.by_letter('r') - | will give a list of all the beasts starting with 'r' - | - | by_level(lower_:int, upper_:int) - | returns a list of beasts within a given level range, takes - | two arguments both ints first argument is the lowest level - | you want to see and the second is the highest level - | you can use 1 for the first and a high number like 10000 - | for the second to get a list of all the beasts - | - | by_weakness(weakness) - | returns a list of beasts with a given weakness, input a weakness - | id or a weakness name searching by name will take longer tho - | see runescape.Beasts.weakness_names() for weaknesses - | - | category_names() - | returns a list of slayer categorys - | - | search(query:str) - | searches the beast database for a name and returns a list seperate with + - | usage: - | runescape.Beasts.search('rat+cat') - | will give you a list of all the beasts that have rat or cat in their name - | also that gives you the id - | - | weakness_names() - | returns a list of weaknesses - | - | ---------------------------------------------------------------------- - | Data descriptors defined here: - | - | __dict__ - | dictionary for instance variables (if defined) - | - | __weakref__ - | list of weak references to the object (if defined) - - class Clan(builtins.object) - | Gets a list of members in a clan - | usage: - | list(runescape.Clan('empire of elitez')) - | or - | for member in runescape.Clan('empire of elitez') - | #do stuff - | - | Methods defined here: - | - | __init__(self, clan:str) - | Initialize self. See help(type(self)) for accurate signature. - | - | __iter__(self) - | - | __len__(self) - | - | adminish(self) - | will give a list of the adminish ranks in clan - | (the ones that kind of look like little badges like admin rank) - | usage: - | list(runescape.Clan('empire of elitez').adminish()) - | or - | for member in runescape.Clan('empire of elitez').adminish(): - | #do stuff - | - | bannanas(self) - | will give a list of the banana ranks in clan - | usage: - | list(runescape.Clan('empire of elitez').bananas()) - | or - | for member in runescape.Clan('empire of elitez').bananas(): - | #do stuff - | - | keys(self) - | will give a list of the key ranks in clan - | usage: - | list(runescape.Clan('empire of elitez').keys()) - | or - | for member in runescape.Clan('empire of elitez').keys(): - | #do stuff - | - | rank(self, rank:str) - | will give a list of the members with a specific rank in a clan - | usage: - | list(runescape.Clan('empire of elitez').rank('Owner')) - | or - | for member in runescape.Clan('empire of elitez').rank('Owner'): - | #do stuff - | - | stars(self) - | will give a list of the star ranks in clan - | usage: - | list(runescape.Clan('empire of elitez').stars()) - | or - | for member in runescape.Clan('empire of elitez').stars(): - | #do stuff - | - | ---------------------------------------------------------------------- - | Data descriptors defined here: - | - | __dict__ - | dictionary for instance variables (if defined) - | - | __weakref__ - | list of weak references to the object (if defined) - | - | ---------------------------------------------------------------------- - | Data and other attributes defined here: - | - | CLAN_MEM_URL = 'http://services.runescape.com/m=clan-hiscores/members_... - | - | CLAN_MOTIF_URL = 'http://services.runescape.com/m=avatar-rs/{}/clanmot... - - class GrandExchange(builtins.object) - | provides methods for fetching information about items in the grandexchange - | - | Static methods defined here: - | - | cat_count(id:int) - | tells you how many items there are for each letter in a category - | give the id from 0 to 37 check runescape.GrandExchange.CATEGORYS for ids - | usage: - | runescape.cat_count(12) - | will display the letter counts of the food and drink category - | yes thats where you can find cheese! - | - | graph(id:int) - | gets info on the prices over time - | usage: - | runescape.GrandExchange.graph(1985) - | that ^ will give you data plot points to create a graph - | of cheese over time - | - | item(id:int) - | gets information on a single item usage - | usage: - | runescape.GrandExchange.item(1985) - | that ^ will give you information about Cheese - | - | iter_category(category:int, page_sleep:int=0) - | iterates through an entire category in put an int from 0 to 37 - | if you start getting errors use the second argument that is how many seconds - | to sleep between pages default is 0 whitch will be fine for short bursts - | anouther way to use this is: - | ge = [c for i in range(0,38) for c in runescape.GrandExchange.iter_category(i, 5)] - | to get the full grand exchange in one list, will take some time tho - | so its best to only be getting one category at a time - | regurlar usage: - | for i in runescape.GrandExchange.iter_category(12, 1): - | #do stuff - | - | iter_letter(letter:str, category:int, page_sleep:int=0) - | iterates through a letter (a-z or #(for number)) in a category - | see runescape.GrandExchange.CATEGORYS for categorys - | usage: - | for item in runescape.GrandExchange.iter_leter('c', 12, 1): - | #do stuff (find cheese maybe?) - | that loop will go through all the items in the food and drink - | category that start with the letter 'c' will also sleep for a second - | each grand exchange page to be nice to the runescape servers and to reduce - | errors - | - | ---------------------------------------------------------------------- - | Data descriptors defined here: - | - | __dict__ - | dictionary for instance variables (if defined) - | - | __weakref__ - | list of weak references to the object (if defined) - | - | ---------------------------------------------------------------------- - | Data and other attributes defined here: - | - | CATEGORYS = {'ammo': 1, 'arrows': 2, 'bolts': 3, 'construction materia... - | - | LETTER_URL = 'http://services.runescape.com/m=itemdb_rs/api/catalogue/... - - class Highscores(builtins.object) - | fetches highscores and gives em back as a list of dicts - | 6 different types hiscore, hiscore_ironman, hiscore_hardcore_ironman - | 'hiscore_oldschool', 'hiscore_oldschool_ironman', - | 'hiscore_oldschool_ultimate - | this class takes two arguments rsn and type 'hiscore' is the default - | usage: - | person = runescape.Highscores('raatty', 'hiscore') - | or person = runescape.Highscores('raatty') would give same effect - | person.skills will give a list of skills in game order - | person.total will give you the total - | person.rsn in case you forget who you looked up - | - | Methods defined here: - | - | __init__(self, rsn:str, type_:str=None) - | Initialize self. See help(type(self)) for accurate signature. - | - | skill(self, name) - | - | ---------------------------------------------------------------------- - | Data descriptors defined here: - | - | __dict__ - | dictionary for instance variables (if defined) - | - | __weakref__ - | list of weak references to the object (if defined) - | - | ---------------------------------------------------------------------- - | Data and other attributes defined here: - | - | HIGHSCORES_URL = 'http://services.runescape.com/m={}/index_lite.ws?pla... - | - | SKILL_NAMES = {'Agility': 5, 'Attack': 1, 'Constitution': 2, 'Construc... - - class Player(builtins.object) - | will look up lots about a player - | the required argument is the rsn as a string but - | there is also a second argument auto_fetch which - | is of type bool, delfault is False but if it is - | True this class will auto maticly visit all the different - | links to get information but if its left blank or set - | to False the data will just be fetched as needed - | LookupError will be raised if the rsn does not exist - | usage: - | me = runescape.Player('raatty', True) - | - | Methods defined here: - | - | __init__(self, rsn:str, auto_fetch:bool=False) - | Initialize self. See help(type(self)) for accurate signature. - | - | alog(self) - | returns a list of all the activitys on a persons alog or - | an empty list for private people - | usage: - | me.alog() - | - | clan(self) - | returns the clan name if they are in one - | usage: - | me.clan() - | - | combat(self) - | returns the combat level either strait from - | runemetics or calculated from highscores for - | private people - | usage: - | me.combat() - | - | forum_pic(self) - | returns a url of the forum picture - | usage: - | me.forum_pic() - | - | overall_total(self) - | returns total level, total xp and rank - | usage: - | me.overall_total() - | - | quest(self, name) - | searches throught the quest list then returns the quest - | that matches that name - | usage: - | me.quest('rat catchers') - | - | quest_list(self) - | returns a list of all the quests and the lookuped players - | progree in them - | usage: - | me.quest_list() - | - | quest_summary(self) - | returns the counts of the started finished and not started quests - | or None for thease three values if they are private - | usage: - | me.quest_summary() - | - | rsn(self) - | will return the rsn of the looked up player - | correctly capitalised if their runemetics wasnt private - | usage: - | me.rsn() - | - | stats(self) - | returns a list of a persons levels - | usage: - | me.stats() - | - | title(self) - | returns the title and weather its a suffix - | usage: - | me.title() - | - | ---------------------------------------------------------------------- - | Data descriptors defined here: - | - | __dict__ - | dictionary for instance variables (if defined) - | - | __weakref__ - | list of weak references to the object (if defined) - | - | ---------------------------------------------------------------------- - | Data and other attributes defined here: - | - | CLAN_AND_TITLE_URL = 'http://services.runescape.com/m=website-data/pla... - | - | FORUM_PIC_URL = 'http://services.runescape.com/m=avatar-rs/{}/chat.png... - | - | RUNEMETRICS_BASE_URL = 'https://apps.runescape.com/runemetrics/' - | - | RUNE_METRICS_QUESTS_URL = 'https://apps.runescape.com/runemetrics/ques... - | - | RUNE_METRICS_URL = 'https://apps.runescape.com/runemetrics/profile/pro... - - class Rs3Wikia(_Wikia) - | methords for exploring the rs3 wikia - | - | Method resolution order: - | Rs3Wikia - | _Wikia - | builtins.object - | - | Methods defined here: - | - | __init__(self) - | Initialize self. See help(type(self)) for accurate signature. - | - | ---------------------------------------------------------------------- - | Methods inherited from _Wikia: - | - | isearch(self, searchTerm:str) - | returns a generator that iterates though the search results pages - | - | page(self, pageName:str) - | returns an instance of a wikia page - | - | search(self, searchTerm:str) - | just returns a list of search results from the wikia - | - | ---------------------------------------------------------------------- - | Data descriptors inherited from _Wikia: - | - | __dict__ - | dictionary for instance variables (if defined) - | - | __weakref__ - | list of weak references to the object (if defined) - - class osrsWikia(_Wikia) - | methods for exploring the osrs wikia - | - | Method resolution order: - | osrsWikia - | _Wikia - | builtins.object - | - | Methods defined here: - | - | __init__(self) - | Initialize self. See help(type(self)) for accurate signature. - | - | ---------------------------------------------------------------------- - | Methods inherited from _Wikia: - | - | isearch(self, searchTerm:str) - | returns a generator that iterates though the search results pages - | - | page(self, pageName:str) - | returns an instance of a wikia page - | - | search(self, searchTerm:str) - | just returns a list of search results from the wikia - | - | ---------------------------------------------------------------------- - | Data descriptors inherited from _Wikia: - | - | __dict__ - | dictionary for instance variables (if defined) - | - | __weakref__ - | list of weak references to the object (if defined) - -DATA - BASE_URL = 'http://services.runescape.com/' - diff --git a/runescapeapi/__init__.py b/runescapeapi/__init__.py deleted file mode 100644 index 433563a..0000000 --- a/runescapeapi/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .runescapeapi import * diff --git a/runescapeapi/runescapeapi.py b/runescapeapi/runescapeapi.py deleted file mode 100644 index 08f6520..0000000 --- a/runescapeapi/runescapeapi.py +++ /dev/null @@ -1,742 +0,0 @@ -""" -A slightly memes wrapper for the runescape api, hopefully helps people -best way to contact me with any problems is on discord raatty#3522 -read the rest of the docs for module usage step one is - import runescapeapi as runescape -the code in here might not be done in the best way but everything you -need should be there -""" -import requests -import math -import json -import time - -BASE_URL = "http://services.runescape.com/" - - -class Highscores: - """ - fetches highscores and gives em back as a list of dicts - 6 different types hiscore, hiscore_ironman, hiscore_hardcore_ironman - 'hiscore_oldschool', 'hiscore_oldschool_ironman', - 'hiscore_oldschool_ultimate - this class takes two arguments rsn and type 'hiscore' is the default - usage: - person = runescape.Highscores('raatty', 'hiscore') - or person = runescape.Highscores('raatty') would give same effect - person.skills will give a list of skills in game order - person.total will give you the total - person.rsn in case you forget who you looked up - """ - - HIGHSCORES_URL = BASE_URL + "m={}/index_lite.ws?player={}" - SKILL_NAMES = { - "Total": 0, - "Attack": 1, - "Defence": 7, - "Strength": 4, - "Constitution": 2, - "Ranged": 10, - "Prayer": 13, - "Magic": 16, - "Cooking": 12, - "Woodcutting": 18, - "Fletching": 17, - "Fishing": 9, - "Firemaking": 15, - "Crafting": 14, - "Smithing": 6, - "Mining": 3, - "Herblore": 8, - "Agility": 5, - "Thieving": 11, - "Slayer": 20, - "Farming": 21, - "Runecrafting": 19, - "Hunter": 23, - "Construction": 22, - "Summoning": 24, - "Dungeoneering": 25, - "Divination": 26, - "Invention": 27, - "Archaeology": 28, - } - - def _fetch(self, rsn, type, skill_count: int): - url = self.HIGHSCORES_URL.format(type, rsn.replace(" ", "+")) - fetched_scores = requests.get(url).text - levels = [] - for row in fetched_scores.split("\n"): - for col in row.split(" "): - if len(levels) <= skill_count: - levels.append(col) - for index, lvl in enumerate(levels): - lvl_split = lvl.split(",") - yield { - "level": int(lvl_split[1]), - "xp": int(lvl_split[2]), - "rank": int(lvl_split[0]), - "id": index, - "name": list(self.SKILL_NAMES.keys())[index], - } - - def _calculate_combat(self): - if self.skill_count == 27: - melee = self.skill("Attack") + self.skill("Strength") - magic = 2 * self.skill("Magic") - range = 2 * self.skill("Ranged") - m = max(melee, magic, range) - one = 1.3 * m - two = self.skill("Defence") + self.skill("Constitution") - three = self.skill("Prayer") // 2 - four = self.skill("Summoning") // 2 - return int(sum([one, two, three, four]) // 4) - elif self.skill_count == 23: - first = 0.325 * max( - self.skill("Attack") + self.skill("Strength"), - 1.5 * self.skill("Magic"), - 1.5 * self.skill("Ranged"), - ) - second = self.skill("Defence") / 4 - third = self.skill("Constitution") / 4 - fourth = (self.skill("Prayer") // 2) / 4 - return int(sum([first, second, third, fourth])) - - def skill(self, name): - for s in self.skills: - if name == s["name"]: - return s["level"] - - def __init__(self, rsn: str, type_: str = None): - self.rsn = rsn - if type_ is None: - self.type = "hiscore" - self.skill_count = 27 - else: - if type_ in ["hiscore", "hiscore_ironman", "hiscore_hardcore_ironman"]: - self.type = type_ - self.skill_count = 27 - elif type_ in [ - "hiscore_oldschool", - "hiscore_oldschool_ironman", - "hiscore_oldschool_ultimate", - ]: - self.type = type_ - self.skill_count = 23 - else: - raise AttributeError - self.skills = list(self._fetch(self.rsn, self.type, self.skill_count)) - self.total = self.skills[0] - self.skills = sorted(self.skills[1:], key=lambda x: self.SKILL_NAMES[x["name"]]) - self.combat_level = self._calculate_combat() - - -class Player: - """ - will look up lots about a player - the required argument is the rsn as a string but - there is also a second argument auto_fetch which - is of type bool, delfault is False but if it is - True this class will auto maticly visit all the different - links to get information but if its left blank or set - to False the data will just be fetched as needed - LookupError will be raised if the rsn does not exist - usage: - me = runescape.Player('raatty', True) - """ - - RUNEMETRICS_BASE_URL = "https://apps.runescape.com/runemetrics/" - RUNE_METRICS_URL = RUNEMETRICS_BASE_URL + "profile/profile?user={}&activities=20" - RUNE_METRICS_QUESTS_URL = RUNEMETRICS_BASE_URL + "quests?user={}" - FORUM_PIC_URL = BASE_URL + "m=avatar-rs/{}/chat.png" - CLAN_AND_TITLE_URL = ( - BASE_URL - + "m=website-data/playerDetails.ws?names=%5B%22{}%22%5D&callback=jQuery000000000000000_0000000000&_=0" - ) - - def _fetch_runemetrics(self): - metrics = requests.get(self.RUNE_METRICS_URL.format(self._rsn)).json() - try: - error = metrics["error"] - if error == "NO_PROFILE": - raise LookupError - elif error == "PROFILE_PRIVATE": - self.profile["rsn"] = self._rsn.replace("%20", " ") - stats = Highscores(self.profile["rsn"]) - self.profile["overall_total"] = stats.total - self.profile["combat"] = {"combatlevel": stats.combat_level} - self.profile["quest_summary"] = { - "started": None, - "complete": None, - "notstarted": None, - } - self.profile["alog"] = [] - self.profile["stats"] = stats.skills - except KeyError: - self.profile["rsn"] = metrics["name"] - self.profile["overall_total"] = { - "xp": metrics["totalxp"], - "level": metrics["totalskill"], - "rank": metrics["rank"], - } - self.profile["combat"] = {"combatlevel": metrics["combatlevel"]} - self.profile["quest_summary"] = { - "started": metrics["questsstarted"], - "complete": metrics["questscomplete"], - "notstarted": metrics["questsnotstarted"], - } - self.profile["alog"] = metrics["activities"] - self.profile["stats"] = sorted( - [ - dict( - i, **{"name": list(Highscores.SKILL_NAMES.keys())[i["id"] + 1]} - ) - for i in metrics["skillvalues"] - ], - key=lambda x: Highscores.SKILL_NAMES[x["name"]], - ) - - def _fetch_quests(self): - self.profile["quest_list"] = requests.get( - self.RUNE_METRICS_QUESTS_URL.format(self._rsn) - ).json()["quests"] - - def _fetch_clan_and_title(self): - c_and_t = requests.get(self.CLAN_AND_TITLE_URL.format(self._rsn)).content - c_and_t = json.loads(c_and_t[34:-4]) - try: - self.profile["clan"] = c_and_t["clan"] - except KeyError: - self.profile["clan"] = "" - try: - self.profile["title"] = { - "isSuffix": c_and_t["isSuffix"], - "title": c_and_t["title"], - } - except KeyError: - self.profile["title"] = {"isSuffix": False, "title": ""} - - def __init__(self, rsn: str, auto_fetch: bool = False): - self.profile = { - "rsn": None, - "overall_total": None, - "combat": None, - "quest_summary": None, - "alog": None, - "stats": None, - "quest_list": None, - "clan": None, - "title": None, - } - self._rsn = rsn.replace(" ", "%20") - if auto_fetch: - self._fetch_runemetrics() - self._fetch_quests() - self._fetch_clan_and_title() - - def rsn(self): - """ - will return the rsn of the looked up player - correctly capitalised if their runemetics wasnt private - usage: - me.rsn() - """ - if self.profile["rsn"] is None: - self._fetch_runemetrics() - return self.profile["rsn"] - - def overall_total(self): - """ - returns total level, total xp and rank - usage: - me.overall_total() - """ - if self.profile["overall_total"] is None: - self._fetch_runemetrics() - return self.profile["overall_total"] - - def combat(self): - """ - returns the combat level either strait from - runemetics or calculated from highscores for - private people - usage: - me.combat() - """ - if self.profile["combat"] is None: - self._fetch_runemetrics() - return self.profile["combat"] - - def quest_summary(self): - """ - returns the counts of the started finished and not started quests - or None for thease three values if they are private - usage: - me.quest_summary() - """ - if self.profile["quest_summary"] is None: - self._fetch_runemetrics() - return self.profile["quest_summary"] - - def alog(self): - """ - returns a list of all the activitys on a persons alog or - an empty list for private people - usage: - me.alog() - """ - if self.profile["alog"] is None: - self._fetch_runemetrics() - return self.profile["alog"] - - def stats(self): - """ - returns a list of a persons levels - usage: - me.stats() - """ - if self.profile["stats"] is None: - self._fetch_runemetrics() - return self.profile["stats"] - - def quest_list(self): - """ - returns a list of all the quests and the lookuped players - progree in them - usage: - me.quest_list() - """ - if self.profile["quest_list"] is None: - self._fetch_quests() - return self.profile["quest_list"] - - def quest(self, name): - """ - searches throught the quest list then returns the quest - that matches that name - usage: - me.quest('rat catchers') - """ - for q in self.quest_list(): - if q["title"].lower() == name.lower(): - return q - - def clan(self): - """ - returns the clan name if they are in one - usage: - me.clan() - """ - if self.profile["clan"] is None: - self._fetch_clan_and_title() - return self.profile["clan"] - - def title(self): - """ - returns the title and weather its a suffix - usage: - me.title() - """ - if self.profile["title"] is None: - self._fetch_clan_and_title() - return self.profile["title"] - - def forum_pic(self): - """ - returns a url of the forum picture - usage: - me.forum_pic() - """ - return self.FORUM_PIC_URL.format(self._rsn) - - -class Clan: - """ - Gets a list of members in a clan - usage: - list(runescape.Clan('empire of elitez')) - or - for member in runescape.Clan('empire of elitez') - #do stuff - """ - - CLAN_MEM_URL = BASE_URL + "m=clan-hiscores/members_lite.ws?clanName={}" - CLAN_MOTIF_URL = BASE_URL + "m=avatar-rs/{}/clanmotif.png" - - def __init__(self, clan: str): - self.name = clan - self.members = ( - requests.get(self.CLAN_MEM_URL.format(clan.replace(" ", "+"))) - .content.replace(b"\xa0", b" ") - .decode("ascii") - .split("\n")[1:-1] - ) - self.motif = self.CLAN_MOTIF_URL.format(clan.replace(" ", "%20")) - - def __iter__(self): - for member in self.members: - member = member.split(",") - yield { - "rsn": member[0], - "rank": member[1], - "clanxp": member[2], - "kills": member[3], - } - - def __len__(self): - return len(self.members) - - def keys(self): - """ - will give a list of the key ranks in clan - usage: - list(runescape.Clan('empire of elitez').keys()) - or - for member in runescape.Clan('empire of elitez').keys(): - #do stuff - """ - for i in self.members: - keyRanks = ["Owner", "Deputy Owner", "Overseer"] - i = i.split(",") - if i[1] in keyRanks: - yield {"rsn": i[0], "rank": i[1], "clanxp": i[2], "kills": i[3]} - - def adminish(self): - """ - will give a list of the adminish ranks in clan - (the ones that kind of look like little badges like admin rank) - usage: - list(runescape.Clan('empire of elitez').adminish()) - or - for member in runescape.Clan('empire of elitez').adminish(): - #do stuff - """ - for i in self.members: - adminishRanks = ["Coordinator", "Organiser", "Admin"] - i = i.split(",") - if i[1] in adminishRanks: - yield {"rsn": i[0], "rank": i[1], "clanxp": i[2], "kills": i[3]} - - def stars(self): - """ - will give a list of the star ranks in clan - usage: - list(runescape.Clan('empire of elitez').stars()) - or - for member in runescape.Clan('empire of elitez').stars(): - #do stuff - """ - for i in self.members: - starRanks = ["General", "Captain", "Lieutenant"] - i = i.split(",") - if i[1] in starRanks: - yield {"rsn": i[0], "rank": i[1], "clanxp": i[2], "kills": i[3]} - - def bannanas(self): - """ - will give a list of the banana ranks in clan - usage: - list(runescape.Clan('empire of elitez').bananas()) - or - for member in runescape.Clan('empire of elitez').bananas(): - #do stuff - """ - for i in self.members: - bananaRanks = ["Sergeant", "Corporal", "Recruit"] - i = i.split(",") - if i[1] in bananaRanks: - yield {"rsn": i[0], "rank": i[1], "clanxp": i[2], "kills": i[3]} - - def rank(self, rank: str): - """ - will give a list of the members with a specific rank in a clan - usage: - list(runescape.Clan('empire of elitez').rank('Owner')) - or - for member in runescape.Clan('empire of elitez').rank('Owner'): - #do stuff - """ - for i in self.members: - i = i.split(",") - if i[1] == rank: - yield {"rsn": i[0], "rank": i[1], "clanxp": i[2], "kills": i[3]} - - -class Beasts: - """ - a bunch of methods for getting stats and seaching for monsters - """ - - @staticmethod - def by_id(id: int): - """ - returns stats and info about a beast by id - usage: - runescape.Beasts.by_id(47) - returns information about level 1 rats - to get the ids use runescape.Beasts.search(name) - """ - BEAST_ID_URL = BASE_URL + "m=itemdb_rs/bestiary/beastData.json?beastid={}" - return requests.get(BEAST_ID_URL.format(id)).json() - - @staticmethod - def search(query: str): - """ - searches the beast database for a name and returns a list seperate with + - usage: - runescape.Beasts.search('rat+cat') - will give you a list of all the beasts that have rat or cat in their name - also that gives you the id - """ - BEAST_SEARCH_URL = BASE_URL + "m=itemdb_rs/bestiary/beastSearch.json?term={}" - return requests.get(BEAST_SEARCH_URL.format(query)).json() - - @staticmethod - def by_letter(letter: str): - """ - list of beasts starting with a letter - usage: - runescape.Beasts.by_letter('r') - will give a list of all the beasts starting with 'r' - """ - BEAST_LETTER_URL = ( - BASE_URL + "m=itemdb_rs/bestiary/bestiaryNames.json?letter={}" - ) - return requests.get(BEAST_LETTER_URL.format(letter.upper())).json() - - @staticmethod - def area_names(): - """ - returns a list of area names - usage: - runescape.Beasts.area_names() - it returns the same list every time so you should only need to call this - once in your code - """ - AREA_URL = BASE_URL + "m=itemdb_rs/bestiary/areaNames.json" - return requests.get(AREA_URL).json() - - @staticmethod - def by_area(area: str): - """ - returns a list of beasts in a specific area - see runescape.Beasts.area_names() for the list - usage: - runescape.Beasts.by_area('Rat pits') - gives you a list of all the beasts in the rat pits - """ - BY_AREA_URL = BASE_URL + "m=itemdb_rs/bestiary/areaBeasts.json?identifier={}" - return requests.get(BY_AREA_URL.format(area.replace(" ", "+"))).json() - - @staticmethod - def category_names(): - """ - returns a list of slayer categorys - """ - CATEGORY_URL = BASE_URL + "m=itemdb_rs/bestiary/slayerCatNames.json" - return requests.get(CATEGORY_URL).json() - - @staticmethod - def by_category(cat): - """ - returns a list of monsters of a given slayer category - input a category id as an int or a str of the category name - searching by string will take longer tho - see runescape.Beasts.category_names() for categorys - """ - BY_CATEGORY_URL = ( - BASE_URL + "m=itemdb_rs/bestiary/slayerBeasts.json?identifier={}" - ) - if type(cat) == int: - return requests.get(BY_CATEGORY_URL.format(cat)).json() - elif type(cat) == str: - cat_list = Beasts.category_names() - cat = cat_list[cat] - return requests.get(BY_CATEGORY_URL.format(cat)).json() - - @staticmethod - def weakness_names(): - """ - returns a list of weaknesses - """ - WEAKNESS_URL = BASE_URL + "m=itemdb_rs/bestiary/weaknessNames.json" - return requests.get(WEAKNESS_URL).json() - - @staticmethod - def by_weakness(weakness): - """ - returns a list of beasts with a given weakness, input a weakness - id or a weakness name searching by name will take longer tho - see runescape.Beasts.weakness_names() for weaknesses - """ - BY_WEAKNESS_URL = ( - BASE_URL + "m=itemdb_rs/bestiary/weaknessBeasts.json?identifier={}" - ) - if type(weakness) == int: - return requests.get(BY_WEAKNESS_URL.format(weakness)).json() - elif type(weakness) == str: - weakness_list = Beasts.weakness_names() - weakness = weakness_list[weakness] - return requests.get(BY_WEAKNESS_URL.format(weakness)).json() - - @staticmethod - def by_level(lower_: int, upper_: int): - """ - returns a list of beasts within a given level range, takes - two arguments both ints first argument is the lowest level - you want to see and the second is the highest level - you can use 1 for the first and a high number like 10000 - for the second to get a list of all the beasts - """ - BY_LEVEL_URL = ( - BASE_URL + "m=itemdb_rs/bestiary/levelGroup.json?identifier={}-{}" - ) - return requests.get(BY_LEVEL_URL.format(lower_, upper_)).json() - - -class GrandExchange: - """ - provides methods for fetching information about items in the grandexchange - """ - MODE = "itemdb_rs" - LETTER_URL = ( - BASE_URL + "m={}/api/catalogue/items.json?category={}&alpha={}&page={}" - ) - CATEGORYS = { - "miscellaneous": 0, - "ammo": 1, - "arrows": 2, - "bolts": 3, - "construction materials": 4, - "construction projects": 5, - "cooking ingredients": 6, - "costumes": 7, - "crafting materials": 8, - "familiars": 9, - "farming produce": 10, - "fletching materials": 11, - "food and drink": 12, - "herblore materials": 13, - "hunting equipment": 14, - "hunting produce": 15, - "jewellery": 16, - "mage armour": 17, - "mage weapons": 18, - "melee armour - low level": 19, - "melee armour mid level": 20, - "melee armour high level": 21, - "melee weapons - low level": 22, - "melee weapons - mid level": 23, - "melee weapons - high level": 24, - "mining and smithing": 25, - "potions": 26, - "prayer armour": 27, - "prayer materials": 28, - "range armour": 29, - "range weapons": 30, - "runecrafting": 31, - "runes spells and teleports": 32, - "seeds": 33, - "summoning scrolls": 34, - "tools and containers": 35, - "woodcutting product": 36, - "pocket items": 37, - } - - @classmethod - def item(cls, id: int): - """ - gets information on a single item usage - usage: - runescape.GrandExchange.item(1985) - that ^ will give you information about Cheese - """ - ITEM_URL = BASE_URL + "m={}/api/catalogue/detail.json?item={}" - return requests.get(ITEM_URL.format(cls.MODE, id)).json() - - @classmethod - def graph(cls, id: int): - """ - gets info on the prices over time - usage: - runescape.GrandExchange.graph(1985) - that ^ will give you data plot points to create a graph - of cheese over time - """ - GRAPH_URL = BASE_URL + "m={}/api/graph/{}.json" - return requests.get(GRAPH_URL.format(cls.MODE, id)).json() - - @classmethod - def cat_count(cls, id: int): - """ - tells you how many items there are for each letter in a category - give the id from 0 to 37 check runescape.GrandExchange.CATEGORYS for ids - usage: - runescape.GrandExchange.cat_count(12) - will display the letter counts of the food and drink category - yes thats where you can find cheese! - """ - CAT_COUNT_URL = BASE_URL + "m={}/api/catalogue/category.json?category={}" - return requests.get(CAT_COUNT_URL.format(cls.MODE, id)).json()["alpha"] - - @classmethod - def iter_letter(cls, letter: str, category: int, page_sleep: int = 0): - """ - iterates through a letter (a-z or #(for number)) in a category - see runescape.GrandExchange.CATEGORYS for categorys - usage: - for item in runescape.GrandExchange.iter_leter('c', 12, 1): - #do stuff (find cheese maybe?) - that loop will go through all the items in the food and drink - category that start with the letter 'c' will also sleep for a second - each grand exchange page to be nice to the runescape servers and to reduce - errors - """ - for l in GrandExchange.cat_count(category): - if letter == l["letter"]: - letter_count = l["items"] - break - page_count = math.ceil(letter_count / 12) - for page in range(1, page_count + 1): - time.sleep(page_sleep) - p = requests.get( - GrandExchange.LETTER_URL.format( - cls.MODE, category, letter.replace("#", "%23"), page - ) - ).json() - for item in p["items"]: - yield item - - @classmethod - def iter_category(cls, category: int, page_sleep: int = 0): - """ - iterates through an entire category in put an int from 0 to 37 - if you start getting errors use the second argument that is how many seconds - to sleep between pages default is 0 whitch will be fine for short bursts - anouther way to use this is: - ge = [c for i in range(0,38) for c in runescape.GrandExchange.iter_category(i, 5)] - to get the full grand exchange in one list, will take some time tho - so its best to only be getting one category at a time - regurlar usage: - for i in runescape.GrandExchange.iter_category(12, 1): - #do stuff - """ - cat_counts = GrandExchange.cat_count(category) - for letter in cat_counts: - letter_count = letter["items"] - for page in range(1, math.ceil(letter_count / 12) + 1): - time.sleep(page_sleep) - p = requests.get( - GrandExchange.LETTER_URL.format( - cls.MODE, category, letter["letter"].replace("#", "%23"), page - ) - ) - for item in p.json()["items"]: - yield item - - -class OSRSGrandExchange(GrandExchange): - """ - provides methods for fetching information about items in the old school grandexchange - """ - MODE = "itemdb_oldschool" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..7250370 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,25 @@ +[metadata] +name = runescapeapi +version = 3 +author = Raatty +author_email = me@raatty.club +license = MIT +description = grandexchange, highscores, beasts, runemetrics +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/Raatty/runescapeapi +project_urls = + Bug Tracker = https://github.com/Raatty/runescapeapi/issues +classifiers = + Programming Language :: Python :: 3 + License :: OSI Approved :: MIT License + Operating System :: OS Independent + +[options] +package_dir = + = src +packages = find: +python_requires = >=3.6 + +[options.packages.find] +where = src \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 6e32b34..0000000 --- a/setup.py +++ /dev/null @@ -1,17 +0,0 @@ -from setuptools import setup, find_packages - -with open('readme.txt') as f: - ld = f.read() -setup( - name='runescapeapi', - version='2', - description='grandexchange, highscores, beasts, runemetrics', - long_description=ld, - long_description_content_type="text/plain", - author='raatty', - py_modules=["runesapeapi"], - install_requires=['requests'], - python_requires='>=3', - license='MIT', - packages=find_packages() -) diff --git a/src/runescapeapi/__init__.py b/src/runescapeapi/__init__.py new file mode 100644 index 0000000..8cde782 --- /dev/null +++ b/src/runescapeapi/__init__.py @@ -0,0 +1 @@ +print("hello world") From fd1b2edf5686148045e0bf06830ea09047ef683b Mon Sep 17 00:00:00 2001 From: Raatty Date: Sat, 24 Apr 2021 18:48:54 +1200 Subject: [PATCH 2/8] runemetrics part complete maybe --- .gitignore | 3 +- runescapeapi/__init__.py | 1 + runescapeapi/runemetrics/__init__.py | 39 +++++++++++ runescapeapi/runemetrics/monthly_xp.py | 53 ++++++++++++++ runescapeapi/runemetrics/profile.py | 95 ++++++++++++++++++++++++++ runescapeapi/runemetrics/quest_list.py | 40 +++++++++++ setup.cfg | 8 +-- src/runescapeapi/__init__.py | 1 - 8 files changed, 232 insertions(+), 8 deletions(-) create mode 100644 runescapeapi/__init__.py create mode 100644 runescapeapi/runemetrics/__init__.py create mode 100644 runescapeapi/runemetrics/monthly_xp.py create mode 100644 runescapeapi/runemetrics/profile.py create mode 100644 runescapeapi/runemetrics/quest_list.py delete mode 100644 src/runescapeapi/__init__.py diff --git a/.gitignore b/.gitignore index e15e5a4..e10309b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ build dist venv -src/runescapeapi.egg-info +runescapeapi.egg-info +*.pyc diff --git a/runescapeapi/__init__.py b/runescapeapi/__init__.py new file mode 100644 index 0000000..b5b5bac --- /dev/null +++ b/runescapeapi/__init__.py @@ -0,0 +1 @@ +from .runemetrics import * diff --git a/runescapeapi/runemetrics/__init__.py b/runescapeapi/runemetrics/__init__.py new file mode 100644 index 0000000..2018f57 --- /dev/null +++ b/runescapeapi/runemetrics/__init__.py @@ -0,0 +1,39 @@ +import json +from dataclasses import dataclass +from datetime import datetime +from typing import List + +import requests + + +RUNEMETRICS_BASE = "https://apps.runescape.com/runemetrics/" +SKILL_NAMES = [ + "Attack", + "Defence", + "Strength", + "Constitution", + "Ranged", + "Prayer", + "Magic", + "Cooking", + "Woodcutting", + "Fletching", + "Fishing", + "Firemaking", + "Crafting", + "Smithing", + "Mining", + "Herblore", + "Agility", + "Thieving", + "Slayer", + "Farming", + "Runecrafting", + "Hunter", + "Construction", + "Summoning", + "Dungeoneering", + "Divination", + "Invention", + "Archaeology", +] diff --git a/runescapeapi/runemetrics/monthly_xp.py b/runescapeapi/runemetrics/monthly_xp.py new file mode 100644 index 0000000..16ac92d --- /dev/null +++ b/runescapeapi/runemetrics/monthly_xp.py @@ -0,0 +1,53 @@ +import json +from dataclasses import dataclass +from datetime import datetime +from typing import List + +import requests +from runescapeapi.runemetrics import RUNEMETRICS_BASE, SKILL_NAMES + + +@dataclass +class MonthData: + xp_gain: int + timestamp: datetime + rank: int + + +@dataclass +class MonthlyXpGain: + skill_id: int + total_xp: int + average_xp_gain: int + total_gain: int + month_data: List[MonthData] + + +def get_monthly_xp_gain_url(user_name: str, skill: str) -> str: + return ( + f"{RUNEMETRICS_BASE}xp-monthly?searchName={user_name}" + f"&skillid={SKILL_NAMES.index(skill)}" + ) + + +def monthly_xp_gain_from_responce(raw_response: str) -> MonthlyXpGain: + j = json.loads(raw_response)["monthlyXpGain"][0] + return MonthlyXpGain( + skill_id=j["skillId"], + total_xp=j["totalXp"], + average_xp_gain=j["averageXpGain"], + total_gain=j["totalGain"], + month_data=[ + MonthData( + xp_gain=month["xpGain"], + timestamp=datetime.fromtimestamp(month["timestamp"] // 1000), + rank=month["rank"], + ) + for month in j["monthData"] + ], + ) + + +def try_fetch_monthly_xp_gain(user_name: str, skill: str) -> MonthlyXpGain: + r = requests.get(get_monthly_xp_gain_url(user_name, skill)).text + return monthly_xp_gain_from_responce(r) diff --git a/runescapeapi/runemetrics/profile.py b/runescapeapi/runemetrics/profile.py new file mode 100644 index 0000000..7be9fc5 --- /dev/null +++ b/runescapeapi/runemetrics/profile.py @@ -0,0 +1,95 @@ +import json +from dataclasses import dataclass +from datetime import datetime +from typing import List + +import requests +from runescapeapi.runemetrics import RUNEMETRICS_BASE, SKILL_NAMES + + +@dataclass +class Activity: + date: datetime + details: str + text: str + + +@dataclass +class Skill: + name: str + level: int + xp: int + rank: int + id: int + + +@dataclass +class QuestsSummary: + started: int + complete: int + not_started: int + + +@dataclass +class Profile: + name: str + rank: int + total_skill: int + total_xp: int + combat_level: int + magic: int + melee: int + ranged: int + quests_summary: QuestsSummary + activities: List[Activity] + skill_values: List[Skill] + logged_in: bool + + +def get_profile_url(user_name: str) -> str: + return ( + f"{RUNEMETRICS_BASE}profile/profile?user={user_name}&activities=20" + ) + + +def profile_from_responce(raw_response: str) -> Profile: + j = json.loads(raw_response) + return Profile( + name=j["name"], + rank=int(j["rank"].replace(",", "")), + total_skill=j["totalskill"], + total_xp=j["totalxp"], + combat_level=j["combatlevel"], + magic=j["magic"], + melee=j["melee"], + ranged=j["ranged"], + quests_summary=QuestsSummary( + started=j["questsstarted"], + complete=j["questscomplete"], + not_started=j["questsnotstarted"], + ), + activities=[ + Activity( + date=datetime.strptime(act["date"], "%d-%b-%Y %H:%M"), + details=act["details"], + text=act["text"], + ) + for act in j["activities"] + ], + skill_values=[ + Skill( + name=SKILL_NAMES[skill["id"]], + level=skill["level"], + xp=skill["xp"] / 10, + rank=skill["rank"], + id=skill["id"], + ) + for skill in j["skillvalues"] + ], + logged_in=j["loggedIn"], + ) + + +def try_fetch_rune_metrics_profile(user_name: str) -> Profile: + r = requests.get(get_profile_url(user_name)).text + return profile_from_responce(r) diff --git a/runescapeapi/runemetrics/quest_list.py b/runescapeapi/runemetrics/quest_list.py new file mode 100644 index 0000000..68329a1 --- /dev/null +++ b/runescapeapi/runemetrics/quest_list.py @@ -0,0 +1,40 @@ +import json +from dataclasses import dataclass +from typing import List + +import requests +from runescapeapi.runemetrics import RUNEMETRICS_BASE + + +@dataclass +class Quest: + title: str + status: str + difficulty: int + members: bool + quest_points: int + user_elegible: bool + + +def get_quests_url(user_name: str) -> str: + return f"{RUNEMETRICS_BASE}quests?user={user_name}" + + +def quests_from_response(raw_response: str) -> List[Quest]: + j = json.loads(raw_response) + return [ + Quest( + title=q["title"], + status=q["status"], + difficulty=q["difficulty"], + members=q["members"], + quest_points=q["questPoints"], + user_elegible=q["userEligible"], + ) + for q in j["quests"] + ] + + +def try_fetch_quests_list(user_name: str) -> List[Quest]: + r = requests.get(get_quests_url(user_name)).text + return quests_from_response(r) diff --git a/setup.cfg b/setup.cfg index 7250370..1a3341a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,12 +14,8 @@ classifiers = Programming Language :: Python :: 3 License :: OSI Approved :: MIT License Operating System :: OS Independent +requires = + requests [options] -package_dir = - = src packages = find: -python_requires = >=3.6 - -[options.packages.find] -where = src \ No newline at end of file diff --git a/src/runescapeapi/__init__.py b/src/runescapeapi/__init__.py deleted file mode 100644 index 8cde782..0000000 --- a/src/runescapeapi/__init__.py +++ /dev/null @@ -1 +0,0 @@ -print("hello world") From 62710861f1993fbc2594e411c82bfa0280a515f5 Mon Sep 17 00:00:00 2001 From: Raatty Date: Sat, 5 Jun 2021 19:08:34 +1200 Subject: [PATCH 3/8] some static methods and classmethods to improve api --- runescapeapi/runemetrics/monthly_xp.py | 60 +++++++++-------- runescapeapi/runemetrics/profile.py | 92 +++++++++++++------------- runescapeapi/runemetrics/quest_list.py | 46 ++++++------- 3 files changed, 100 insertions(+), 98 deletions(-) diff --git a/runescapeapi/runemetrics/monthly_xp.py b/runescapeapi/runemetrics/monthly_xp.py index 16ac92d..62add6d 100644 --- a/runescapeapi/runemetrics/monthly_xp.py +++ b/runescapeapi/runemetrics/monthly_xp.py @@ -22,32 +22,34 @@ class MonthlyXpGain: total_gain: int month_data: List[MonthData] - -def get_monthly_xp_gain_url(user_name: str, skill: str) -> str: - return ( - f"{RUNEMETRICS_BASE}xp-monthly?searchName={user_name}" - f"&skillid={SKILL_NAMES.index(skill)}" - ) - - -def monthly_xp_gain_from_responce(raw_response: str) -> MonthlyXpGain: - j = json.loads(raw_response)["monthlyXpGain"][0] - return MonthlyXpGain( - skill_id=j["skillId"], - total_xp=j["totalXp"], - average_xp_gain=j["averageXpGain"], - total_gain=j["totalGain"], - month_data=[ - MonthData( - xp_gain=month["xpGain"], - timestamp=datetime.fromtimestamp(month["timestamp"] // 1000), - rank=month["rank"], - ) - for month in j["monthData"] - ], - ) - - -def try_fetch_monthly_xp_gain(user_name: str, skill: str) -> MonthlyXpGain: - r = requests.get(get_monthly_xp_gain_url(user_name, skill)).text - return monthly_xp_gain_from_responce(r) + @staticmethod + def url(user_name: str, skill: str) -> str: + return ( + f"{RUNEMETRICS_BASE}xp-monthly?searchName={user_name}" + f"&skillid={SKILL_NAMES.index(skill)}" + ) + + @staticmethod + def from_responce(raw_response: str) -> "MonthlyXpGain": + j = json.loads(raw_response)["monthlyXpGain"][0] + return MonthlyXpGain( + skill_id=j["skillId"], + total_xp=j["totalXp"], + average_xp_gain=j["averageXpGain"], + total_gain=j["totalGain"], + month_data=[ + MonthData( + xp_gain=month["xpGain"], + timestamp=datetime.fromtimestamp( + month["timestamp"] // 1000 + ), + rank=month["rank"], + ) + for month in j["monthData"] + ], + ) + + @classmethod + def try_fetch(cls, user_name: str, skill: str) -> "MonthlyXpGain": + r = requests.get(cls.url(user_name, skill)).text + return cls.from_responce(r) diff --git a/runescapeapi/runemetrics/profile.py b/runescapeapi/runemetrics/profile.py index 7be9fc5..1bb1cd8 100644 --- a/runescapeapi/runemetrics/profile.py +++ b/runescapeapi/runemetrics/profile.py @@ -45,51 +45,51 @@ class Profile: skill_values: List[Skill] logged_in: bool + @staticmethod + def url(user_name: str) -> str: + return ( + f"{RUNEMETRICS_BASE}profile/profile?user={user_name}&activities=20" + ) -def get_profile_url(user_name: str) -> str: - return ( - f"{RUNEMETRICS_BASE}profile/profile?user={user_name}&activities=20" - ) + @staticmethod + def from_responce(raw_response: str) -> "Profile": + j = json.loads(raw_response) + return Profile( + name=j["name"], + rank=int(j["rank"].replace(",", "")), + total_skill=j["totalskill"], + total_xp=j["totalxp"], + combat_level=j["combatlevel"], + magic=j["magic"], + melee=j["melee"], + ranged=j["ranged"], + quests_summary=QuestsSummary( + started=j["questsstarted"], + complete=j["questscomplete"], + not_started=j["questsnotstarted"], + ), + activities=[ + Activity( + date=datetime.strptime(act["date"], "%d-%b-%Y %H:%M"), + details=act["details"], + text=act["text"], + ) + for act in j["activities"] + ], + skill_values=[ + Skill( + name=SKILL_NAMES[skill["id"]], + level=skill["level"], + xp=skill["xp"] / 10, + rank=skill["rank"], + id=skill["id"], + ) + for skill in j["skillvalues"] + ], + logged_in=j["loggedIn"], + ) - -def profile_from_responce(raw_response: str) -> Profile: - j = json.loads(raw_response) - return Profile( - name=j["name"], - rank=int(j["rank"].replace(",", "")), - total_skill=j["totalskill"], - total_xp=j["totalxp"], - combat_level=j["combatlevel"], - magic=j["magic"], - melee=j["melee"], - ranged=j["ranged"], - quests_summary=QuestsSummary( - started=j["questsstarted"], - complete=j["questscomplete"], - not_started=j["questsnotstarted"], - ), - activities=[ - Activity( - date=datetime.strptime(act["date"], "%d-%b-%Y %H:%M"), - details=act["details"], - text=act["text"], - ) - for act in j["activities"] - ], - skill_values=[ - Skill( - name=SKILL_NAMES[skill["id"]], - level=skill["level"], - xp=skill["xp"] / 10, - rank=skill["rank"], - id=skill["id"], - ) - for skill in j["skillvalues"] - ], - logged_in=j["loggedIn"], - ) - - -def try_fetch_rune_metrics_profile(user_name: str) -> Profile: - r = requests.get(get_profile_url(user_name)).text - return profile_from_responce(r) + @classmethod + def try_fetch(cls, user_name: str) -> "Profile": + r = requests.get(cls.url(user_name)).text + return cls.from_responce(r) diff --git a/runescapeapi/runemetrics/quest_list.py b/runescapeapi/runemetrics/quest_list.py index 68329a1..6027d1d 100644 --- a/runescapeapi/runemetrics/quest_list.py +++ b/runescapeapi/runemetrics/quest_list.py @@ -15,26 +15,26 @@ class Quest: quest_points: int user_elegible: bool - -def get_quests_url(user_name: str) -> str: - return f"{RUNEMETRICS_BASE}quests?user={user_name}" - - -def quests_from_response(raw_response: str) -> List[Quest]: - j = json.loads(raw_response) - return [ - Quest( - title=q["title"], - status=q["status"], - difficulty=q["difficulty"], - members=q["members"], - quest_points=q["questPoints"], - user_elegible=q["userEligible"], - ) - for q in j["quests"] - ] - - -def try_fetch_quests_list(user_name: str) -> List[Quest]: - r = requests.get(get_quests_url(user_name)).text - return quests_from_response(r) + @staticmethod + def url(user_name: str) -> str: + return f"{RUNEMETRICS_BASE}quests?user={user_name}" + + @staticmethod + def from_response(raw_response: str) -> List["Quest"]: + j = json.loads(raw_response) + return [ + Quest( + title=q["title"], + status=q["status"], + difficulty=q["difficulty"], + members=q["members"], + quest_points=q["questPoints"], + user_elegible=q["userEligible"], + ) + for q in j["quests"] + ] + + @classmethod + def try_fetch(cls, user_name: str) -> List["Quest"]: + r = requests.get(cls.url(user_name)).text + return cls.from_response(r) From a30819311ec031a543f6611c3407a8f8b6dc9f26 Mon Sep 17 00:00:00 2001 From: Raatty Date: Sat, 5 Jun 2021 21:11:02 +1200 Subject: [PATCH 4/8] docs ci test --- .github/workflows/docs.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..961904c --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,30 @@ +name: Deploy docs +on: + push: + branches: + - dev + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Setup Python + uses: actions/setup-python@v2.2.2 + - name: Build pages + run: | + pip install pdoc3 + pdoc --html runescapeapi + - name: Publish pages + working-directory: ./html/runescapeapi + run: | + git init . + git checkout -B gh-pages + git config user.name "actions" + git config user.email "actions@github.com" + git add . + git commit -am "Deployed docs" + git remote add origin https://raatty:${{secrets.GITHUB_TOKEN}}@github.com/Raatty/runescapeapi + git push origin gh-pages --force \ No newline at end of file From 5717170e65b1eafafe69992dccfe40c0f9ee8ee9 Mon Sep 17 00:00:00 2001 From: Raatty Date: Sat, 5 Jun 2021 21:13:55 +1200 Subject: [PATCH 5/8] add missing dep to docs ci --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 961904c..07dbe53 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-python@v2.2.2 - name: Build pages run: | - pip install pdoc3 + pip install pdoc3 requests pdoc --html runescapeapi - name: Publish pages working-directory: ./html/runescapeapi From 942876170d36fd374ec6d0a5a6fd18e683752383 Mon Sep 17 00:00:00 2001 From: Raatty Date: Sat, 5 Jun 2021 21:36:36 +1200 Subject: [PATCH 6/8] more generic remote link so it should still work on forks --- .github/workflows/docs.yml | 4 ++-- .gitignore | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 07dbe53..002e766 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -25,6 +25,6 @@ jobs: git config user.name "actions" git config user.email "actions@github.com" git add . - git commit -am "Deployed docs" - git remote add origin https://raatty:${{secrets.GITHUB_TOKEN}}@github.com/Raatty/runescapeapi + git commit -am "Deployed docs for commit ${{github.sha}}" + git remote add origin https://${{github.repository_owner}}:${{secrets.GITHUB_TOKEN}}@github.com/${{github.repository}} git push origin gh-pages --force \ No newline at end of file diff --git a/.gitignore b/.gitignore index e10309b..ecd555c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ dist venv runescapeapi.egg-info *.pyc +html \ No newline at end of file From b341490ea4b34b0e530a41bfdd2bf21159047a4f Mon Sep 17 00:00:00 2001 From: Raatty Date: Sun, 6 Jun 2021 19:42:51 +1200 Subject: [PATCH 7/8] added clan member listing --- runescapeapi/__init__.py | 2 +- runescapeapi/clan.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 runescapeapi/clan.py diff --git a/runescapeapi/__init__.py b/runescapeapi/__init__.py index b5b5bac..819940d 100644 --- a/runescapeapi/__init__.py +++ b/runescapeapi/__init__.py @@ -1 +1 @@ -from .runemetrics import * +BASE_URL: str = "http://services.runescape.com/" diff --git a/runescapeapi/clan.py b/runescapeapi/clan.py new file mode 100644 index 0000000..7840516 --- /dev/null +++ b/runescapeapi/clan.py @@ -0,0 +1,37 @@ +from dataclasses import dataclass +from typing import List + +import requests + +from . import BASE_URL + + +@dataclass +class ClanMember: + rsn: str + rank: str + clan_xp: int + kills: int + + @staticmethod + def url(clan_name: str) -> str: + url = f"{BASE_URL}m=clan-hiscores/members_lite.ws?clanName={clan_name}" + return url.replace(" ", "%20") + + @staticmethod + def from_response(raw_responce: str) -> List["ClanMember"]: + members = [] + for member in raw_responce.replace("\xa0", " ").splitlines()[1:]: + print(member) + rsn, rank, clan_xp, kills = member.split(",") + members.append( + ClanMember( + rsn=rsn, rank=rank, clan_xp=int(clan_xp), kills=int(kills) + ) + ) + return members + + @classmethod + def try_fetch(cls, clan_name: str) -> List["ClanMember"]: + r = requests.get(cls.url(clan_name)).text + return cls.from_response(r) From 74eea060736a1f39afb93049a17735f39327c72d Mon Sep 17 00:00:00 2001 From: Raatty Date: Sun, 13 Feb 2022 13:04:28 +1300 Subject: [PATCH 8/8] the beginings of beasts part --- runescapeapi/__init__.py | 2 +- runescapeapi/beasts.py | 91 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 runescapeapi/beasts.py diff --git a/runescapeapi/__init__.py b/runescapeapi/__init__.py index 819940d..e776397 100644 --- a/runescapeapi/__init__.py +++ b/runescapeapi/__init__.py @@ -1 +1 @@ -BASE_URL: str = "http://services.runescape.com/" +BASE_URL: str = "https://services.runescape.com/" diff --git a/runescapeapi/beasts.py b/runescapeapi/beasts.py new file mode 100644 index 0000000..d08ca1c --- /dev/null +++ b/runescapeapi/beasts.py @@ -0,0 +1,91 @@ +import json +from dataclasses import dataclass +from typing import Dict, List + +import requests + +from runescapeapi import BASE_URL + +AREA_NAMES_URL: str = f"{BASE_URL}m=itemdb_rs/bestiary/areaNames.json" +""" +returns data like `List[str]` of area names compatable with `AreaBeasts` +""" +SLAYER_CAT_NAMES: str = f"{BASE_URL}m=itemdb_rs/bestiary/slayerCatNames.json" +""" +`Dict[str, int]` +""" +WEAKNESS_NAMES: str = f"{BASE_URL}m=itemdb_rs/bestiary/weaknessNames.json" +""" +`Dict[str, int]` +""" + + +@dataclass +class BeastData: + name: str + beast_id: int + members: bool + weakness: str + level: int + life_points: int + defence: int + attack: int + magic: int + ranged: int + xp: float + slayer_level: int + slayer_cat: str + size: int + attackable: bool + aggressive: bool + poisonous: bool + description: str + areas: List[str] + animations: Dict[str, int] + + @staticmethod + def url(beast_id: int) -> str: + return ( + f"{BASE_URL}m=itemdb_rs/bestiary/beastData.json?beastid={beast_id}" + ) + + @staticmethod + def from_response(raw_responce: str) -> "BeastData": + print(raw_responce) + j: dict[str:str] = json.loads(raw_responce) + j.setdefault("slayerlevel", None) + j.setdefault("slayercat", None) + return BeastData( + name=j["name"], + beast_id=j["id"], + members=j["members"], + weakness=j["weakness"], + level=j["level"], + life_points=j["lifepoints"], + defence=j["defence"], + attack=j["attack"], + magic=j["magic"], + ranged=j["ranged"], + xp=float(j["xp"]), + slayer_level=j["slayerlevel"], + slayer_cat=j["slayercat"], + size=j["size"], + attackable=j["attackable"], + aggressive=j["aggressive"], + poisonous=j["poisonous"], + description=j["description"], + areas=j["areas"], + animations=j["animations"], + ) + + @classmethod + def try_fetch(cls, beast_id: int) -> "BeastData": + url = cls.url(beast_id) + print(url) + r = requests.get(url).text + print(r) + return cls.from_response(r) + + +print() +print(BeastData.try_fetch(89)) # UNICORN?