aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig7
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules3
m---------NotEnoughUpdates-REPO0
-rw-r--r--Pipfile16
-rw-r--r--Pipfile.lock178
-rw-r--r--sbdata/__init__.py1
-rw-r--r--sbdata/__main__.py36
-rw-r--r--sbdata/repo.py61
-rw-r--r--sbdata/task.py64
-rw-r--r--sbdata/tasks.py55
-rw-r--r--sbdata/util.py13
-rw-r--r--sbdata/wiki.py15
13 files changed, 453 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..9155a4f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+root = true
+
+[*.py]
+max_line_length = 180
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fe3edea
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.mypy_cache
+__pycache__
+build/
+.idea/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..cb24a04
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "NotEnoughUpdates-REPO"]
+ path = NotEnoughUpdates-REPO
+ url = https://github.com/NotEnoughUpdates/NotEnoughUpdates-REPO/
diff --git a/NotEnoughUpdates-REPO b/NotEnoughUpdates-REPO
new file mode 160000
+Subproject d93e124a64dd4da6dcae45f8c1857b2c4ad6a1d
diff --git a/Pipfile b/Pipfile
new file mode 100644
index 0000000..39ee4b5
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,16 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+requests = "*"
+mwparserfromhell = "*"
+click = "*"
+questionary = "*"
+
+[dev-packages]
+mypy = "*"
+
+[requires]
+python_version = "3.10"
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 0000000..9480071
--- /dev/null
+++ b/Pipfile.lock
@@ -0,0 +1,178 @@
+{
+ "_meta": {
+ "hash": {
+ "sha256": "2f97b2115fdfc808a8b0d6e47b772ed9a39aa5e4a87cf2c2846103de8d070412"
+ },
+ "pipfile-spec": 6,
+ "requires": {
+ "python_version": "3.10"
+ },
+ "sources": [
+ {
+ "name": "pypi",
+ "url": "https://pypi.org/simple",
+ "verify_ssl": true
+ }
+ ]
+ },
+ "default": {
+ "certifi": {
+ "hashes": [
+ "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872",
+ "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"
+ ],
+ "version": "==2021.10.8"
+ },
+ "charset-normalizer": {
+ "hashes": [
+ "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
+ "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
+ ],
+ "markers": "python_version >= '3'",
+ "version": "==2.0.12"
+ },
+ "click": {
+ "hashes": [
+ "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1",
+ "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"
+ ],
+ "index": "pypi",
+ "version": "==8.0.4"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
+ "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
+ ],
+ "markers": "python_version >= '3'",
+ "version": "==3.3"
+ },
+ "mwparserfromhell": {
+ "hashes": [
+ "sha256:0519497b8a7472298324ef92e1e82c1ab5cab85b4d64462d7ae46c4464c8b872",
+ "sha256:0c2bbb36110410b5f6d6d8b2f35f65f8ec8f57c0477609d35bcaac4784a59e5a",
+ "sha256:18dac5162471c38e5bbf6ee3698c49d2753e8dde372864112fdaf81047ce89d3",
+ "sha256:1c908c9738c5c6bce04b825b3f95592d971ff439ace294a86fc758070afc6d0c",
+ "sha256:203ad9cd78dec7480fde45c9f49d0bc2a2eaa28fa1b585461fb9f56f6587f46c",
+ "sha256:2d6e124396ee41c35ea12017a66c560abb1f7f51bee04e631a149318adaf15e2",
+ "sha256:2d983914c19dee5c2a13298b1ccd3a1ed2b65c81d322b7e7df99cd5386a460c6",
+ "sha256:3e5f4bb96b68557acd14c4baa62cbe440b6e6d0f5263cb4860d37e1ceeada2a7",
+ "sha256:42eb94a6bad20b7f8845fd2900a45373cb4d414d5a357b27457c7c6c259115c5",
+ "sha256:511ff847cddb8e7014b6afb0af5dbdb5cf05ada67e31fc39efa34fbbdccb8e8b",
+ "sha256:54d9819c11530fc00b8a70fa3508898109b3df72336f7b8e52f8faffbe03ee88",
+ "sha256:5dfd7f57fa3d516b21790ef7f6094119082baa2e6072cef78fb9f999b77e674f",
+ "sha256:60d86c8d3501edc1331b37df72b74689ee392da077c36a8b453460b8e3714cdd",
+ "sha256:7c822985760b9e82857ecfb99dbb60ac35d0ebf7b2977a0215c7c56fe70c2b68",
+ "sha256:7f5682ab9e1a55b20e9fb669582493d196c76a512276456848153c39d726d7d2",
+ "sha256:82010e5b5da130cbcb002747f5592ffca73488e0e9cf1ebdfef6e8559c535c41",
+ "sha256:92bec9528ae34d272893ccaf2b527df85c314ff28cfbb3056340467b095d834c",
+ "sha256:a344ceabde013aa2f9b23494e73af11b99795f63a30124e955c185de2c8ae397",
+ "sha256:abae1052b9c12a8814c76dd26ec9cfdd71102e7f89c28fb58a5fba7ee55ad1bc",
+ "sha256:b7f19e7d064c467f32e0704becd81c841a807335934134d6aa859d98d01c7cf3",
+ "sha256:cd0a74474ed6e85808c874511d28a253ffd2d1e5a3abe915705a25804212ac73",
+ "sha256:d521a2e3787c83ecf607a7806ae655d32f3c3884b2dcf35a388183c6028ddce4",
+ "sha256:d63d76f576e133c14a29f1ad2f3fc2afa17b74945ebc017e8d7d3bcb59f5243c",
+ "sha256:da2568e2a492dcc913f1c026434371af7a24e05dc56450c3ab063d9e580b48f2",
+ "sha256:dd81220d66bf829664a6f911b3f58e7af3061fd7fdee68c0fc9731f5bcd7519d",
+ "sha256:e0c3d3bc409f8ac1221639ee2dab0dc830711d9a56a39014aad2824c2c98b3e2",
+ "sha256:f8c450c39ef647678831ecf9a1f8236521d369afc4ae59a9c601d07f298eda35",
+ "sha256:fc4f5718e761a3f5ad76eb9089e0792ed3a6786095abe098e37e7ac7af76afef"
+ ],
+ "index": "pypi",
+ "version": "==0.6.4"
+ },
+ "prompt-toolkit": {
+ "hashes": [
+ "sha256:30129d870dcb0b3b6a53efdc9d0a83ea96162ffd28ffe077e94215b233dc670c",
+ "sha256:9f1cd16b1e86c2968f2519d7fb31dd9d669916f515612c269d14e9ed52b51650"
+ ],
+ "markers": "python_full_version >= '3.6.2'",
+ "version": "==3.0.28"
+ },
+ "questionary": {
+ "hashes": [
+ "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90",
+ "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"
+ ],
+ "index": "pypi",
+ "version": "==1.10.0"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
+ "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
+ ],
+ "index": "pypi",
+ "version": "==2.27.1"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
+ "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'",
+ "version": "==1.26.8"
+ },
+ "wcwidth": {
+ "hashes": [
+ "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784",
+ "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"
+ ],
+ "version": "==0.2.5"
+ }
+ },
+ "develop": {
+ "mypy": {
+ "hashes": [
+ "sha256:0b52778a018559a256c819ee31b2e21e10b31ddca8705624317253d6d08dbc35",
+ "sha256:0fdc9191a49c77ab5fa0439915d405e80a1118b163ab03cd2a530f346b12566a",
+ "sha256:13677cb8b050f03b5bb2e8bf7b2668cd918b001d56c2435082bbfc9d5f730f42",
+ "sha256:1903c92ff8642d521b4627e51a67e49f5be5aedb1fb03465b3aae4c3338ec491",
+ "sha256:1f66f2309cdbb07e95e60e83fb4a8272095bd4ea6ee58bf9a70d5fb304ec3e3f",
+ "sha256:2dba92f58610d116f68ec1221fb2de2a346d081d17b24a784624389b17a4b3f9",
+ "sha256:2efd76893fb8327eca7e942e21b373e6f3c5c083ff860fb1e82ddd0462d662bd",
+ "sha256:3ac14949677ae9cb1adc498c423b194ad4d25b13322f6fe889fb72b664c79121",
+ "sha256:471af97c35a32061883b0f8a3305ac17947fd42ce962ca9e2b0639eb9141492f",
+ "sha256:51be997c1922e2b7be514a5215d1e1799a40832c0a0dee325ba8794f2c48818f",
+ "sha256:628f5513268ebbc563750af672ccba5eef7f92d2d90154233edd498dfb98ca4e",
+ "sha256:68038d514ae59d5b2f326be502a359160158d886bd153fc2489dbf7a03c44c96",
+ "sha256:6eab2bcc2b9489b7df87d7c20743b66d13254ad4d6430e1dfe1a655d51f0933d",
+ "sha256:712affcc456de637e774448c73e21c84dfa5a70bcda34e9b0be4fb898a9e8e07",
+ "sha256:71bec3d2782d0b1fecef7b1c436253544d81c1c0e9ca58190aed9befd8f081c5",
+ "sha256:83f66190e3c32603217105913fbfe0a3ef154ab6bbc7ef2c989f5b2957b55840",
+ "sha256:8aaf18d0f8bc3ffba56d32a85971dfbd371a5be5036da41ac16aefec440eff17",
+ "sha256:a0e5657ccaedeb5fdfda59918cc98fc6d8a8e83041bc0cec347a2ab6915f9998",
+ "sha256:a168da06eccf51875fdff5f305a47f021f23f300e2b89768abdac24538b1f8ec",
+ "sha256:b1a116c451b41e35afc09618f454b5c2704ba7a4e36f9ff65014fef26bb6075b",
+ "sha256:b2fa5f2d597478ccfe1f274f8da2f50ea1e63da5a7ae2342c5b3b2f3e57ec340",
+ "sha256:d9d7647505bf427bc7931e8baf6cacf9be97e78a397724511f20ddec2a850752",
+ "sha256:f8fe1bfab792e4300f80013edaf9949b34e4c056a7b2531b5ef3a0fb9d598ae2"
+ ],
+ "index": "pypi",
+ "version": "==0.940"
+ },
+ "mypy-extensions": {
+ "hashes": [
+ "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
+ "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
+ ],
+ "version": "==0.4.3"
+ },
+ "tomli": {
+ "hashes": [
+ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
+ "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==2.0.1"
+ },
+ "typing-extensions": {
+ "hashes": [
+ "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42",
+ "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==4.1.1"
+ }
+ }
+}
diff --git a/sbdata/__init__.py b/sbdata/__init__.py
new file mode 100644
index 0000000..572b454
--- /dev/null
+++ b/sbdata/__init__.py
@@ -0,0 +1 @@
+from sbdata import tasks as _
diff --git a/sbdata/__main__.py b/sbdata/__main__.py
new file mode 100644
index 0000000..596a51f
--- /dev/null
+++ b/sbdata/__main__.py
@@ -0,0 +1,36 @@
+import dataclasses
+import json
+import sys
+from typing import Any
+
+import questionary
+
+from sbdata.repo import Item
+from sbdata.task import Arguments, tasks
+
+
+class ObjectEncoder(json.JSONEncoder):
+
+ def default(self, o: Any) -> Any:
+ if isinstance(o, Item):
+ return o.internalname
+ if dataclasses.is_dataclass(o):
+ return o.__dict__
+ return super().default(o)
+
+
+def main():
+ args = Arguments(sys.argv)
+ task = args.get_value(
+ 'Task', tasks.get(args.task),
+ questionary.select('Which task do you want to execute?', choices=[
+ questionary.Choice(task.label, task) for task in tasks.values()
+ ]))
+ print("Selected task: " + task.label)
+ data = task.run(args)
+ if args.has_flag('json'):
+ print(json.dumps(data, cls=ObjectEncoder))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/sbdata/repo.py b/sbdata/repo.py
new file mode 100644
index 0000000..475f2a6
--- /dev/null
+++ b/sbdata/repo.py
@@ -0,0 +1,61 @@
+import dataclasses
+import json
+import os
+import pathlib
+import re
+import typing
+
+item_list = {}
+repo_dir = pathlib.Path(os.environ.get('REPO_DIR', 'NotEnoughUpdates-REPO'))
+
+
+@dataclasses.dataclass
+class Item:
+ displayname: str
+ itemid: str
+ internalname: str
+ lore: list[str]
+
+
+def unformat_name(name: str) -> str:
+ return re.sub('ยง.', '', name)
+
+
+def bare_name(name: str) -> str:
+ name = unformat_name(name).replace("'", '').lower()
+ if name.startswith('ultimate'):
+ name = name[8:]
+ return name.strip()
+
+
+def load_item(item: pathlib.Path):
+ with item.open('r') as fp:
+ data = json.load(fp)
+ item_list[data['internalname']] = Item(data['displayname'], data['itemid'], data['internalname'], data['lore'])
+
+
+def find_item_by_name(name: str) -> typing.Optional[Item]:
+ name = bare_name(name)
+ pot = [item for item in item_list.values()
+ if item.internalname.casefold() == name
+ or bare_name(item.displayname) in name
+ or (item.itemid == 'minecraft:enchanted_book'
+ and name in bare_name(item.lore[0]))]
+ if pot:
+ return pot[0]
+ return None
+
+
+def load_items():
+ item_dir = repo_dir / 'items'
+ for item in item_dir.iterdir():
+ if item.name.endswith('.json'):
+ load_item(item)
+
+
+def load_repo_data():
+ item_list.clear()
+ load_items()
+
+
+load_repo_data()
diff --git a/sbdata/task.py b/sbdata/task.py
new file mode 100644
index 0000000..7011ca2
--- /dev/null
+++ b/sbdata/task.py
@@ -0,0 +1,64 @@
+import dataclasses
+import os
+import sys
+import typing
+
+import questionary
+
+_T = typing.TypeVar('_T')
+
+
+class Arguments:
+
+ def __init__(self, args: list[str]):
+ self.prog = args[0]
+ self.args: typing.Dict[str, str] = {}
+ self.flags: list[str] = []
+ self.no_prompt = os.environ.get('PROMPT') == 'NO_PROMPT'
+ self.task: typing.Optional[str]
+ self.task = None
+ last_arg = None
+ for arg in args:
+ if last_arg is None:
+ if arg.startswith('--'):
+ last_arg = arg[2:]
+ elif arg.startswith('-'):
+ self.flags.append(arg[1:])
+ elif arg.startswith(':'):
+ self.task = arg[1:]
+ else:
+ print("Unknown arg: " + arg)
+ else:
+ self.args[last_arg] = arg
+ last_arg = None
+
+ def get_value(self, label: str, value: _T, question: questionary.Question) -> _T:
+ if value is None:
+ if self.no_prompt:
+ print('No argument present for ' + label)
+ sys.exit(1)
+ return question.ask()
+ return value
+
+ def has_flag(self, param: str) -> bool:
+ return param in self.flags
+
+
+@dataclasses.dataclass
+class Task:
+ label: str
+ name: str
+ run: typing.Callable
+
+
+tasks = {}
+
+TASK_TYPE = typing.Callable[[Arguments], None]
+
+
+def register_task(label: str) -> typing.Callable[[TASK_TYPE], TASK_TYPE]:
+ def d(func: TASK_TYPE) -> TASK_TYPE:
+ tasks[func.__name__] = Task(label, func.__name__, func)
+ return func
+
+ return d
diff --git a/sbdata/tasks.py b/sbdata/tasks.py
new file mode 100644
index 0000000..8ac1f8c
--- /dev/null
+++ b/sbdata/tasks.py
@@ -0,0 +1,55 @@
+import ast
+import dataclasses
+import json
+import re
+import sys
+import typing
+
+from sbdata.repo import find_item_by_name, Item
+from sbdata.task import register_task, Arguments
+from sbdata.wiki import get_wiki_sources_by_title
+
+
+@dataclasses.dataclass
+class DungeonDrop:
+ item: Item
+ floor: int
+ chest: str
+ drop_chances: dict[str, str]
+
+ def get_drop_chance(self, has_s_plus: bool, talisman_level: int, boss_luck: int):
+ drop_identifier = "S" + ('+' if has_s_plus else '') + 'ABCD'[talisman_level] + str(len([i for i in [0, 1, 3, 5, 10] if i >= boss_luck]))
+ return self.drop_chances.get(drop_identifier)
+
+
+@register_task("Fetch Dungeon Loot")
+def fetch_dungeon_loot(args: Arguments):
+ items = []
+ for floor in get_wiki_sources_by_title(*[f'Template:Catacombs Floor {f} Loot Master' for f in ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII']]).values():
+ for template in floor.filter_templates():
+ if template.name.strip() == 'Dungeon Chest Table/Row':
+ item = None
+ ifloor = None
+ chest = None
+ drop_chances = {}
+
+ for param in template.params:
+ attr_name = param.name.nodes[0].strip()
+ attr_value = param.value.nodes[0].strip()
+ if attr_name == 'item':
+ if item is None:
+ item = find_item_by_name(attr_value)
+ elif attr_name == 'customlink':
+ if item is None:
+ item = find_item_by_name(attr_value.split('#')[-1])
+ elif attr_name == 'chest':
+ chest = attr_value
+ elif attr_name == 'floor':
+ ifloor = int(attr_value)
+ elif attr_name.startswith("S"):
+ drop_chances[attr_name] = attr_value
+ if item is None or ifloor is None or chest is None:
+ print('WARNING: Missing data for item: ' + str(template))
+ else:
+ items.append(DungeonDrop(item, ifloor, chest, drop_chances))
+ return items
diff --git a/sbdata/util.py b/sbdata/util.py
new file mode 100644
index 0000000..87a3098
--- /dev/null
+++ b/sbdata/util.py
@@ -0,0 +1,13 @@
+import functools
+from typing import TypeVar, Generator, ParamSpec, Callable
+
+_Param = ParamSpec('_Param')
+_RetType = TypeVar('_RetType')
+
+
+def no_generator(func: Callable[_Param, Generator[_RetType, None, None]]) -> Callable[_Param, list[_RetType]]:
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs) -> list[_RetType]:
+ return list(func(*args, **kwargs))
+
+ return wrapper
diff --git a/sbdata/wiki.py b/sbdata/wiki.py
new file mode 100644
index 0000000..7e029a5
--- /dev/null
+++ b/sbdata/wiki.py
@@ -0,0 +1,15 @@
+import urllib.parse
+import requests
+import mwparserfromhell
+
+
+def get_wiki_sources_by_title(*page_titles: str, wiki_host: str = 'wiki.hypixel.net') -> dict[str, mwparserfromhell.wikicode.Wikicode]:
+ prepared_titles = "|".join(map(urllib.parse.quote, page_titles))
+ api_data = requests.get(f'https://{wiki_host}/api.php?action=query&prop=revisions&titles={prepared_titles}&rvprop=content&format=json&rvslots=main').json()
+ if "batchcomplete" not in api_data:
+ print(f'Batch data not present in wiki response for: {page_titles}')
+
+ return {
+ page['title']: mwparserfromhell.parse(page['revisions'][0]['slots']['main']['*'])
+ for _, page in api_data["query"]["pages"].items()
+ }