Initial Release of IGNCore version 2.5
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
|
||||
class AunoComment:
|
||||
def __init__(self, author, date, content):
|
||||
self.author = author
|
||||
self.date = date
|
||||
self.content = content
|
||||
@@ -0,0 +1,179 @@
|
||||
import html
|
||||
import re
|
||||
from typing import List
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Any, Int, Item
|
||||
from core.decorators import instance, command
|
||||
from core.text import Text
|
||||
from modules.standard.items.auno_comment import AunoComment
|
||||
from modules.standard.items.items_controller import ItemsController
|
||||
|
||||
|
||||
@instance()
|
||||
class AunoController:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def inject(self, registry):
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.items_controller: ItemsController = registry.get_instance("items_controller")
|
||||
|
||||
@command(command="auno", params=[Int("item_id")], access_level="member",
|
||||
description="Fetch comments for item from Auno by item id")
|
||||
def auno_comments_item_id_cmd(self, _, item_id):
|
||||
item = self.items_controller.get_by_item_id(item_id)
|
||||
if item:
|
||||
low_id = item.lowid
|
||||
high_id = item.highid
|
||||
name = item.name
|
||||
else:
|
||||
low_id = item_id
|
||||
high_id = item_id
|
||||
name = item_id
|
||||
|
||||
return self.get_combined_response(low_id, high_id, name)
|
||||
|
||||
@command(command="auno", params=[Item("item_link")], access_level="member",
|
||||
description="Fetch comments for item from Auno by item link")
|
||||
def auno_comments_item_link_cmd(self, _, item):
|
||||
return self.get_combined_response(item.low_id, item.high_id, item.name)
|
||||
|
||||
@command(command="auno", params=[Any("search")], access_level="member",
|
||||
description="Fetch comments for item from Auno by search")
|
||||
def auno_comments_cmd(self, _, search):
|
||||
items = self.items_controller.find_items(search)
|
||||
count = len(items)
|
||||
|
||||
if count > 0:
|
||||
if count > 1:
|
||||
link_txt = f"Multiple search results for \"{search}\" ({count})"
|
||||
return ChatBlob(link_txt, self.multiple_results_blob(items, search))
|
||||
else:
|
||||
return self.get_combined_response(items[0].lowid, items[0].highid, items[0].name)
|
||||
else:
|
||||
return "No items found matching <highlight>%s</highlight>." % search
|
||||
|
||||
def get_combined_response(self, low_id, high_id, name):
|
||||
combined_response = self.get_auno_response(low_id, high_id)
|
||||
|
||||
if len(combined_response) > 0:
|
||||
# high id comments
|
||||
soup = BeautifulSoup(combined_response[0].text, features="html.parser")
|
||||
comments: List[AunoComment] = self.find_comments(soup)
|
||||
|
||||
if len(combined_response) > 1:
|
||||
# low id comments
|
||||
soup = BeautifulSoup(combined_response[1].text, features="html.parser")
|
||||
comments += self.find_comments(soup)
|
||||
|
||||
# sort the comments by date
|
||||
comments.sort(key=lambda comment: comment.date)
|
||||
|
||||
if len(comments) > 0:
|
||||
return ChatBlob(f"Comments for {name} ({len(comments)})",
|
||||
self.build_comments_blob(comments, name, low_id, high_id))
|
||||
else:
|
||||
return f"No comments found for <highlight>{name}</highlight>."
|
||||
else:
|
||||
return "Error fetching comments from Auno.org."
|
||||
|
||||
def build_comments_blob(self, comments, name, low_id, high_id):
|
||||
link_auno = self.text.make_chatcmd("Auno", f"/start {self.get_auno_request_url(high_id)}")
|
||||
link_aoitems = self.text.make_chatcmd("AOItems", f"/start {self.get_aoitems_request_url(high_id)}")
|
||||
|
||||
item = self.items_controller.get_by_item_id(high_id)
|
||||
blob = ""
|
||||
if item:
|
||||
ql = item.highql
|
||||
blob += f"Item: {self.text.make_item(int(low_id), int(high_id), int(ql), name)}\n"
|
||||
blob += f"Item links: [{link_auno}] [{link_aoitems}]\n\n"
|
||||
blob += "<header2>Comments</header2>\n"
|
||||
|
||||
for comment in comments:
|
||||
blob += html.unescape(comment.content) + "\n"
|
||||
blob += f"<highlight>{comment.author}</highlight> [<grey>{comment.date}</grey>]\n"
|
||||
blob += "\n<pagebreak>"
|
||||
|
||||
return blob
|
||||
|
||||
def multiple_results_blob(self, items, search):
|
||||
max_multiple_results = 40
|
||||
|
||||
blob = f"Found <highlight>{len(items)}</highlight> items matching <highlight>\"{search}\"</highlight>\n"
|
||||
if len(items) > max_multiple_results:
|
||||
blob += "Results have been truncated to only show the first %s results...\n\n" % max_multiple_results
|
||||
items = items[:max_multiple_results]
|
||||
|
||||
for i, item in enumerate(items):
|
||||
itemref = self.text.make_item(item.lowid, item.highid, item.highql, item.name)
|
||||
comments_link = self.text.make_tellcmd("Comments", f"auno {item.highid}")
|
||||
auno_link_h = self.text.make_chatcmd("Auno", f"/start {self.get_auno_request_url(item.highid)}")
|
||||
blob += f"{i + 1}. {itemref}\n | [{comments_link}] [{auno_link_h}]"
|
||||
blob += "\n\n<pagebreak>"
|
||||
|
||||
return blob
|
||||
|
||||
def find_comments(self, soup):
|
||||
comments: List[AunoComment] = []
|
||||
|
||||
brs = soup.find_all("br")
|
||||
for br in brs:
|
||||
br.replace_with("\n")
|
||||
|
||||
trs = soup.find_all(self.tr_contains_comment)
|
||||
for tr in trs:
|
||||
author, date = tr.find("span").string.split("@")
|
||||
|
||||
comment_content = tr.find("div")
|
||||
comment = ""
|
||||
|
||||
for content in comment_content:
|
||||
if type(content) is str:
|
||||
comment += html.escape(content)
|
||||
else:
|
||||
comment += html.escape(content.string)
|
||||
|
||||
# when those halfwits think a billion linebreaks are necessary
|
||||
# and because auno's comment output is ever so lovely...
|
||||
# noinspection RegExpUnnecessaryNonCapturingGroup
|
||||
comment = re.sub(r"(\n(?:\n)*(?:\s)*)", "\n", comment)
|
||||
|
||||
comments.append(AunoComment(author.strip(), date.strip(), comment.strip()))
|
||||
return comments
|
||||
|
||||
def tr_contains_comment(self, tag):
|
||||
if tag:
|
||||
if 'id' in tag.attrs:
|
||||
p = re.compile(r"aoc\d+")
|
||||
return p.match(tag['id'])
|
||||
|
||||
def get_auno_response(self, low_id, high_id):
|
||||
auno_request_low = self.get_auno_request_url(low_id)
|
||||
auno_request_high = self.get_auno_request_url(high_id)
|
||||
|
||||
auno_response_h = requests.get(auno_request_high, timeout=5)
|
||||
auno_response_l = None
|
||||
|
||||
if low_id != high_id:
|
||||
auno_response_l = requests.get(auno_request_low, timeout=5)
|
||||
|
||||
combined_response = []
|
||||
|
||||
if auno_response_h:
|
||||
if auno_response_h.status_code == 200:
|
||||
combined_response.append(auno_response_h)
|
||||
if auno_response_l:
|
||||
if auno_response_l.status_code == 200:
|
||||
combined_response.append(auno_response_l)
|
||||
|
||||
return combined_response
|
||||
|
||||
def get_auno_request_url(self, item_id):
|
||||
return "https://auno.org/ao/db.php?id=%s" % item_id
|
||||
|
||||
def get_aoitems_request_url(self, item_id):
|
||||
return "https://aoitems.com/item/%s/" % item_id
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,232 @@
|
||||
import html
|
||||
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Int, Any, NamedParameters
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.text import Text
|
||||
|
||||
|
||||
@instance()
|
||||
class ItemsController:
|
||||
PAGE_SIZE = 30
|
||||
|
||||
def inject(self, registry):
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.command_alias_service = registry.get_instance("command_alias_service")
|
||||
|
||||
def pre_start(self):
|
||||
self.db.load_sql_file(self.module_dir + "/" + "aodb.sql", pre_optimized=True)
|
||||
self.db.create_view("aodb")
|
||||
|
||||
def start(self):
|
||||
self.command_alias_service.add_alias("item", "items")
|
||||
self.command_alias_service.add_alias("i", "items")
|
||||
|
||||
@command(command="items", params=[Int("item_id")], access_level="member",
|
||||
description="Search for an item by item id")
|
||||
def items_id_cmd(self, _, item_id):
|
||||
item = self.get_by_item_id(item_id)
|
||||
if item:
|
||||
return self.format_items_response(None, str(item_id), [item], 0, 1)
|
||||
else:
|
||||
return f"Could not find item with ID <highlight>{item_id:d}</highlight>."
|
||||
|
||||
@command(command="items",
|
||||
params=[Int("ql", is_optional=True), Any("search"), NamedParameters(["page"])],
|
||||
access_level="member",
|
||||
description="Search for an item")
|
||||
def items_search_cmd(self, _, ql, search, named_params):
|
||||
page = int(named_params.page or "1")
|
||||
|
||||
search = html.unescape(search)
|
||||
|
||||
offset = (page - 1) * self.PAGE_SIZE
|
||||
|
||||
all_items = self.find_items(search, ql)
|
||||
|
||||
return self.format_items_response(ql, search, all_items, offset, page)
|
||||
|
||||
def format_items_response(self, ql, search, all_items, offset, page):
|
||||
items = self.sort_items(search, all_items)[offset:offset + self.PAGE_SIZE]
|
||||
cnt = len(items)
|
||||
|
||||
if cnt == 0:
|
||||
if ql:
|
||||
return f"No QL <highlight>{ql:d}</highlight> items found matching <highlight>{search}</highlight>."
|
||||
else:
|
||||
return f"No items found matching <highlight>{search}</highlight>."
|
||||
elif cnt == 1:
|
||||
item = items[0]
|
||||
return self.format_single_item([item], ql)
|
||||
else:
|
||||
blob = ""
|
||||
# blob += "Version: <highlight>%s</highlight>\n" % "unknown"
|
||||
if ql:
|
||||
blob += f"Search: <highlight>QL {ql:d} {search}</highlight>\n"
|
||||
else:
|
||||
blob += f"Search: <highlight>{search}</highlight>\n"
|
||||
blob += "\n"
|
||||
|
||||
if page > 1:
|
||||
blob += " " + self.text.make_chatcmd(f"<< Page {page - 1:d}",
|
||||
self.get_chat_command(ql, search, page - 1))
|
||||
if offset + self.PAGE_SIZE < len(all_items):
|
||||
blob += " Page " + str(page)
|
||||
blob += " " + self.text.make_chatcmd(f"Page {page + 1:d} >>",
|
||||
self.get_chat_command(ql, search, page + 1))
|
||||
if self.PAGE_SIZE < len(all_items):
|
||||
blob += "\n"
|
||||
blob += "\n"
|
||||
|
||||
blob += self.format_items(items, ql)
|
||||
# noinspection LongLine
|
||||
blob += f"\nItem DB rips created using the {self.text.make_chatcmd('Budabot Items Extractor', '/start https://github.com/Budabot/ItemsExtractor')} tool."
|
||||
|
||||
return ChatBlob(
|
||||
f"Item Search Results ({offset + 1:d} - {min(offset + self.PAGE_SIZE, len(all_items)):d} "
|
||||
f"of {len(all_items):d})", blob)
|
||||
|
||||
def format_items(self, items, ql=None):
|
||||
blob = ""
|
||||
for item_group in ItemIter(items):
|
||||
blob += "<pagebreak>"
|
||||
blob += self.text.make_image(item_group[0].icon) + "\n"
|
||||
blob += self.format_single_item(item_group, ql)
|
||||
blob += "\n\n"
|
||||
|
||||
return blob
|
||||
|
||||
def format_single_item(self, item_group, ql):
|
||||
msg = ""
|
||||
msg += item_group[0].name
|
||||
|
||||
for item in reversed(item_group):
|
||||
if ql:
|
||||
if item.lowql != item.highql:
|
||||
msg += f" {self.text.make_item(item.lowid, item.highid, ql, ql)}"
|
||||
msg += f" [{self.text.make_item(item.lowid, item.highid, item.lowql, item.lowql)} - " \
|
||||
f"{self.text.make_item(item.lowid, item.highid, item.highql, item.highql)}]"
|
||||
elif item.lowql == item.highql:
|
||||
msg += f" [{self.text.make_item(item.lowid, item.highid, item.highql, item.highql)}]"
|
||||
elif item.lowql == item.highql:
|
||||
msg += f" [{self.text.make_item(item.lowid, item.highid, item.highql, item.highql)}]"
|
||||
else:
|
||||
msg += f" [{self.text.make_item(item.lowid, item.highid, item.lowql, item.lowql)} " \
|
||||
f"- {self.text.make_item(item.lowid, item.highid, item.highql, item.highql)}]"
|
||||
|
||||
return msg
|
||||
|
||||
def find_items(self, name, ql=None):
|
||||
params = [name]
|
||||
sql = "SELECT * FROM aodb WHERE name LIKE ? "
|
||||
if ql:
|
||||
sql += " AND lowql <= ? AND highql >= ?"
|
||||
params.append(ql)
|
||||
params.append(ql)
|
||||
|
||||
sql += " UNION SELECT * FROM aodb WHERE name <EXTENDED_LIKE=%d> ?" % len(params)
|
||||
params.append(name)
|
||||
|
||||
if ql:
|
||||
sql += " AND lowql <= ? AND highql >= ?"
|
||||
params.append(ql)
|
||||
params.append(ql)
|
||||
|
||||
sql += " ORDER BY name ASC, highql DESC"
|
||||
|
||||
return self.db.query(sql, params, extended_like=True)
|
||||
|
||||
def sort_items(self, search, items):
|
||||
search = search.lower()
|
||||
search_parts = search.split(" ")
|
||||
|
||||
# if item name matches search exactly (case-insensitive) then priority = 0
|
||||
# if item name contains every whole word from search (case-insensitive) then priority = 1
|
||||
# +1 priority for each whole word from search that item name does not contain
|
||||
|
||||
for row in items:
|
||||
row.priority = 0
|
||||
row_name = row.name.lower()
|
||||
if row_name != search:
|
||||
row.priority += 1
|
||||
row_parts = row_name.split(" ")
|
||||
for search_part in search_parts:
|
||||
if search_part not in row_parts:
|
||||
row.priority += 1
|
||||
|
||||
items.sort(key=lambda x: x.priority, reverse=False)
|
||||
|
||||
return items
|
||||
|
||||
def get_by_item_id(self, item_id, ql=None):
|
||||
if ql:
|
||||
return self.db.query_single("SELECT * FROM aodb "
|
||||
"WHERE (highid = ? OR lowid = ?) "
|
||||
"AND (lowql <= ? AND highql >= ?) "
|
||||
"ORDER BY highid = ? DESC "
|
||||
"LIMIT 1",
|
||||
[item_id, item_id, ql, ql, item_id])
|
||||
else:
|
||||
return self.db.query_single("SELECT * FROM aodb "
|
||||
"WHERE highid = ? OR lowid = ? "
|
||||
"ORDER BY highid = ? DESC "
|
||||
"LIMIT 1", [item_id, item_id, item_id])
|
||||
|
||||
def find_by_name(self, name, ql=None):
|
||||
if ql:
|
||||
return self.db.query_single("SELECT * FROM aodb "
|
||||
"WHERE name = ? "
|
||||
"AND lowql <= ? "
|
||||
"AND highql >= ? "
|
||||
"ORDER BY highid DESC "
|
||||
"LIMIT 1",
|
||||
[name, ql, ql])
|
||||
else:
|
||||
return self.db.query_single("SELECT * FROM aodb "
|
||||
"WHERE name = ? "
|
||||
"ORDER BY highql DESC, "
|
||||
"highid DESC "
|
||||
"LIMIT 1",
|
||||
[name])
|
||||
|
||||
def get_chat_command(self, ql, search, page):
|
||||
if ql:
|
||||
return f"/tell <myname> items {ql:d} {search} --page={page:d}"
|
||||
else:
|
||||
return f"/tell <myname> items {search} --page={page:d}"
|
||||
|
||||
|
||||
class ItemIter:
|
||||
"""Iterator that groups items with the same name and icon together."""
|
||||
|
||||
def __init__(self, items):
|
||||
self.items = items
|
||||
self.current_index = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
num_items = len(self.items)
|
||||
if self.current_index >= num_items:
|
||||
raise StopIteration
|
||||
else:
|
||||
grouped = []
|
||||
|
||||
item = self.items[self.current_index]
|
||||
self.current_index += 1
|
||||
grouped.append(item)
|
||||
current_item = item
|
||||
while self.current_index < num_items:
|
||||
item = self.items[self.current_index]
|
||||
if item.name != current_item.name \
|
||||
or item.icon != current_item.icon \
|
||||
or item.highql == current_item.highql:
|
||||
break
|
||||
current_item = item
|
||||
grouped.append(item)
|
||||
self.current_index += 1
|
||||
|
||||
return grouped
|
||||
@@ -0,0 +1,155 @@
|
||||
# noinspection LongLineForFile
|
||||
|
||||
DROP TABLE IF EXISTS skills;
|
||||
CREATE TABLE IF NOT EXISTS skills
|
||||
(
|
||||
id INT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
common_name VARCHAR(50) NOT NULL
|
||||
);
|
||||
INSERT INTO skills (id, name, common_name)
|
||||
VALUES (318, '% Add. Nano Cost', ''),
|
||||
(319, '% Add. Xp', ''),
|
||||
(102, '1h Blunt', '1 handed blunt'),
|
||||
(103, '1h Edged', '1 handed edged'),
|
||||
(107, '2h Blunt', '2 handed blunt'),
|
||||
(105, '2h Edged', '2 handed edged'),
|
||||
(277, 'Add All Def.', ''),
|
||||
(276, 'Add All Off.', ''),
|
||||
(281, 'Add. Chem. Dam.', ''),
|
||||
(311, 'Add. Cold Dam.', ''),
|
||||
(280, 'Add. Energy Dam.', ''),
|
||||
(316, 'Add. Fire Dam.', ''),
|
||||
(279, 'Add. Melee Dam.', ''),
|
||||
(315, 'Add. Nano Dam.', ''),
|
||||
(317, 'Add. Poison Dam.', ''),
|
||||
(278, 'Add. Proj. Dam.', ''),
|
||||
(282, 'Add. Rad. Dam.', ''),
|
||||
(137, 'Adventuring', ''),
|
||||
(51, 'Aggdef', ''),
|
||||
(201, 'Aggressiveness', ''),
|
||||
(17, 'Agility', ''),
|
||||
(151, 'Aimed Shot', ''),
|
||||
(116, 'Assault Rif', 'Assault Rifle'),
|
||||
(22, 'Attack rating', ''),
|
||||
(128, 'Bio Metamor', 'Biological Metamorphosis'),
|
||||
(152, 'Body Dev.', 'Body Development'),
|
||||
(111, 'Bow', ''),
|
||||
(121, 'Bow Spc Att', 'Bow Special Attack'),
|
||||
(142, 'Brawling', ''),
|
||||
(165, 'Break&Entry', 'Breaking and Entering'),
|
||||
(148, 'Burst', ''),
|
||||
(93, 'Chemical AC', ''),
|
||||
(163, 'Chemistry', ''),
|
||||
(95, 'Cold AC', ''),
|
||||
(161, 'Comp. Liter', 'Computer Literacy'),
|
||||
(164, 'Concealment', ''),
|
||||
(391, 'Critical Decrease', ''),
|
||||
(379, 'CriticalIncrease', ''),
|
||||
(35, 'Damage to Pet', ''),
|
||||
(39, 'Damage To Pet Damage Multiplier', ''),
|
||||
(383, 'Decreased Nano-Interrupt Modifier %', ''),
|
||||
(145, 'Deflect', ''),
|
||||
(144, 'Dimach', ''),
|
||||
(536, 'Direct Nano Damage Efficiency', ''),
|
||||
(96, 'Disease AC', ''),
|
||||
(154, 'Dodge-Rng', 'Dodge Range'),
|
||||
(153, 'Duck-Exp', 'Duck Explosives'),
|
||||
(126, 'Elec. Engi', 'Electrical Engineering'),
|
||||
(92, 'Energy AC', ''),
|
||||
(155, 'Evade-ClsC', 'Evade Close Combat'),
|
||||
(147, 'Fast Attack', ''),
|
||||
(97, 'Fire AC', ''),
|
||||
(123, 'First Aid', ''),
|
||||
(150, 'Fling Shot', ''),
|
||||
(45, 'Free deck slot', ''),
|
||||
(167, 'Full Auto', ''),
|
||||
(109, 'Grenade', ''),
|
||||
(689, 'Heal Reactivity', ''),
|
||||
(343, 'HealDelta', ''),
|
||||
(535, 'Healing Efficiency', ''),
|
||||
(110, 'Heavy Weapons', ''),
|
||||
(90, 'Imp/Proj AC', 'Projectile AC'),
|
||||
(19, 'Intelligence', ''),
|
||||
(100, 'Martial Arts', ''),
|
||||
(127, 'Matt.Metam', 'Matter Metamorphosis'),
|
||||
(130, 'Matter Crea', 'Matter Creation'),
|
||||
(1, 'Max Health', ''),
|
||||
(221, 'Max Nano', ''),
|
||||
(181, 'Max NCU', ''),
|
||||
(478, 'MaxReflectedChemicalDmg', ''),
|
||||
(480, 'MaxReflectedColdDmg', ''),
|
||||
(477, 'MaxReflectedEnergyDmg', ''),
|
||||
(482, 'MaxReflectedFireDmg', ''),
|
||||
(476, 'MaxReflectedMeleeDmg', ''),
|
||||
(481, 'MaxReflectedNanoDmg', ''),
|
||||
(483, 'MaxReflectedPoisonDmg', ''),
|
||||
(475, 'MaxReflectedProjectileDmg', ''),
|
||||
(479, 'MaxReflectedRadiationDmg', ''),
|
||||
(125, 'Mech. Engi', 'Mechanical Engineering'),
|
||||
(104, 'Melee Ener.', 'Melee Energy'),
|
||||
(118, 'Melee. Init.', 'Melee Initiative'),
|
||||
(91, 'Melee/ma AC', 'Melee/Martial Arts AC'),
|
||||
(114, 'MG / SMG', ''),
|
||||
(101, 'Mult. Melee', 'Multi Melee'),
|
||||
(134, 'Multi Ranged', ''),
|
||||
(132, 'Nano Pool', ''),
|
||||
(160, 'Nano Progra', 'Nano Programming'),
|
||||
(168, 'Nano Resist', ''),
|
||||
(149, 'NanoC. Init.', 'Nano Cast Initiative'),
|
||||
(364, 'NanoDelta', 'Nano Delta'),
|
||||
(136, 'Perception', ''),
|
||||
(159, 'Pharma Tech', ''),
|
||||
(120, 'Physic. Init', 'Physical Initiative'),
|
||||
(106, 'Piercing', ''),
|
||||
(112, 'Pistol', ''),
|
||||
(21, 'Psychic', ''),
|
||||
(129, 'Psycho Modi', 'Pychological Modifications'),
|
||||
(162, 'Psychology', ''),
|
||||
(157, 'Quantum FT', ''),
|
||||
(94, 'Radiation AC', ''),
|
||||
(133, 'Ranged Ener', 'Ranged Energy'),
|
||||
(119, 'Ranged. Init.', 'Ranged Initiative'),
|
||||
(381, 'RangeInc. NF', 'Ranged Increase Nano'),
|
||||
(380, 'RangeInc. Weapon', 'Ranged Increase Weapon'),
|
||||
(208, 'ReflectChemicalAC', ''),
|
||||
(217, 'ReflectColdAC', ''),
|
||||
(207, 'ReflectEnergyAC', ''),
|
||||
(219, 'ReflectFireAC', ''),
|
||||
(206, 'ReflectMeleeAC', ''),
|
||||
(218, 'ReflectNanoAC', ''),
|
||||
(225, 'ReflectPoisonAC', ''),
|
||||
(205, 'ReflectProjectileAC', ''),
|
||||
(216, 'ReflectRadiationAC', ''),
|
||||
(593, 'Regain XP Percentage', ''),
|
||||
(113, 'Rifle', ''),
|
||||
(143, 'Riposte', ''),
|
||||
(156, 'Run Speed', ''),
|
||||
(360, 'Scale', ''),
|
||||
(20, 'Sense', ''),
|
||||
(122, 'Sensory Impr', 'Sensory Improvement'),
|
||||
(108, 'Sharp Obj', 'Sharp Objects'),
|
||||
(229, 'ShieldChemicalAC', ''),
|
||||
(231, 'ShieldColdAC', ''),
|
||||
(228, 'ShieldEnergyAC', ''),
|
||||
(233, 'ShieldFireAC', ''),
|
||||
(227, 'ShieldMeleeAC', ''),
|
||||
(232, 'ShieldNanoAC', ''),
|
||||
(234, 'ShieldPoisonAC', ''),
|
||||
(226, 'ShieldProjectileAC', ''),
|
||||
(230, 'ShieldRadiationAC', ''),
|
||||
(115, 'Shotgun', ''),
|
||||
(382, 'SkillLockModifier', ''),
|
||||
(146, 'Sneak Atck', 'Sneak Attack'),
|
||||
(18, 'Stamina', ''),
|
||||
(16, 'Strength', ''),
|
||||
(138, 'Swimming', ''),
|
||||
(131, 'Time&Space', ''),
|
||||
(135, 'Trap Disarm.', ''),
|
||||
(124, 'Treatment', ''),
|
||||
(141, 'Tutoring', ''),
|
||||
(180, 'Used NCU', ''),
|
||||
(139, 'Vehicle Air', ''),
|
||||
(166, 'Vehicle Ground', ''),
|
||||
(117, 'Vehicle Water', ''),
|
||||
(158, 'Weapon Smt', 'Weapon Smithing');
|
||||
@@ -0,0 +1,144 @@
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Any, Options
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.text import Text
|
||||
|
||||
|
||||
@instance()
|
||||
class WhatBuffsController:
|
||||
def inject(self, registry):
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.command_alias_service = registry.get_instance("command_alias_service")
|
||||
|
||||
def pre_start(self):
|
||||
self.db.load_sql_file(self.module_dir + "/" + "item_buffs.sql", pre_optimized=True)
|
||||
self.db.create_view("item_buffs")
|
||||
self.db.load_sql_file(self.module_dir + "/" + "item_types.sql", pre_optimized=True)
|
||||
self.db.create_view("item_types")
|
||||
self.db.load_sql_file(self.module_dir + "/" + "skills.sql", pre_optimized=True)
|
||||
self.db.create_view("skills")
|
||||
|
||||
def start(self):
|
||||
self.command_alias_service.add_alias("buffs", "whatbuffs")
|
||||
|
||||
@command(command="whatbuffs", params=[], access_level="member",
|
||||
description="Find items or nanos that buff a skill (or ability)")
|
||||
def whatbuffs_list_cmd(self, _):
|
||||
data = self.db.query("SELECT name FROM skills ORDER BY name")
|
||||
blob = ""
|
||||
for row in data:
|
||||
blob += self.text.make_tellcmd(row.name, f"whatbuffs {row.name}") + "\n"
|
||||
|
||||
blob += self.get_footer()
|
||||
return ChatBlob("Whatbuffs Skill List", blob)
|
||||
|
||||
@command(command="whatbuffs",
|
||||
params=[
|
||||
Any("skill"),
|
||||
Options(["arms", "back", "chest", "deck", "feet", "fingers",
|
||||
"hands", "head", "hud", "legs", "nanoprogram",
|
||||
"neck", "shoulders", "unknown", "util", "weapon", "wrists", "all"])],
|
||||
access_level="member",
|
||||
description="Find items or nanos that buff a skill (or ability) for a particular item type")
|
||||
def whatbuffs_detail_cmd(self, _, skill_name, item_type):
|
||||
item_type = item_type.capitalize()
|
||||
|
||||
return self.show_search_results(item_type, skill_name)
|
||||
|
||||
@command(command="whatbuffs", params=[Any("skill")], access_level="member",
|
||||
description="Find items or nanos that buff a skill (or ability)")
|
||||
def whatbuffs_skill_cmd(self, _, skill_name):
|
||||
skills = self.search_for_skill(skill_name)
|
||||
if len(skills) == 0:
|
||||
return "Could not find skill <highlight>%s</highlight>." % skill_name
|
||||
elif len(skills) == 1:
|
||||
skill = skills.pop()
|
||||
|
||||
data = self.db.query("SELECT i.item_type, COUNT(1) AS cnt "
|
||||
"FROM aodb "
|
||||
"JOIN item_types i ON aodb.highid = i.item_id "
|
||||
"JOIN item_buffs b ON aodb.highid = b.item_id "
|
||||
"JOIN skills s ON b.attribute_id = s.id "
|
||||
"WHERE s.id = ? "
|
||||
"GROUP BY item_type "
|
||||
"HAVING cnt > 0 "
|
||||
"ORDER BY item_type", [skill.id])
|
||||
|
||||
blob = ""
|
||||
total_count = 0
|
||||
for row in data:
|
||||
total_count += row.cnt
|
||||
for row in data:
|
||||
blob += f'{self.text.zfill(row.cnt, total_count)} - ' \
|
||||
f'{self.text.make_tellcmd(row.item_type, f"whatbuffs {skill.name} {row.item_type}")}\n'
|
||||
|
||||
blob += self.get_footer()
|
||||
return ChatBlob(f"Whatbuffs {skill.name} - Choose Type", blob)
|
||||
else:
|
||||
blob = "Choose a skill:\n\n"
|
||||
for skill in skills:
|
||||
blob += self.text.make_tellcmd(skill.name, f"whatbuffs {skill.name}") + "\n"
|
||||
|
||||
blob += self.get_footer()
|
||||
return ChatBlob("Whatbuffs - Choose Skill", blob)
|
||||
|
||||
def search_for_skill(self, skill_name):
|
||||
skill_name = skill_name.lower()
|
||||
|
||||
data = self.db.query(
|
||||
"SELECT id, name FROM skills WHERE name <EXTENDED_LIKE=0> ? OR common_name <EXTENDED_LIKE=1> ?",
|
||||
[skill_name, skill_name], extended_like=True)
|
||||
|
||||
# check for exact match first, in order to disambiguate between Bow and Bot Special Attack
|
||||
for row in data:
|
||||
if row.name.lower() == skill_name:
|
||||
return [row]
|
||||
|
||||
return data
|
||||
|
||||
def show_search_results(self, item_type, skill_name):
|
||||
skills = self.search_for_skill(skill_name)
|
||||
|
||||
if len(skills) == 0:
|
||||
return "Could not find skill <highlight>%s</highlight>." % skill_name
|
||||
elif len(skills) == 1:
|
||||
skill = skills.pop()
|
||||
return self.get_search_results(item_type, skill)
|
||||
else:
|
||||
blob = ""
|
||||
for skill in skills:
|
||||
blob += self.text.make_tellcmd(skill.name, "whatbuffs %s %s" % (skill.name, item_type)) + "\n"
|
||||
|
||||
return ChatBlob("Whatbuffs - Choose Skill", blob)
|
||||
|
||||
def get_search_results(self, item_type, skill):
|
||||
data = self.db.query("SELECT aodb.*, b.amount, i.item_type "
|
||||
"FROM aodb "
|
||||
"JOIN item_types i ON aodb.highid = i.item_id "
|
||||
"JOIN item_buffs b ON aodb.highid = b.item_id "
|
||||
"JOIN skills s ON b.attribute_id = s.id "
|
||||
"WHERE i.item_type LIKE ? AND s.id = ? "
|
||||
"ORDER BY item_type, amount DESC", ["%" if item_type == "All" else item_type, skill.id])
|
||||
|
||||
if len(data) == 0:
|
||||
return f"No items found of type <highlight>{item_type}</highlight> " \
|
||||
f"that buff <highlight>{skill.name}</highlight>."
|
||||
else:
|
||||
current_item_type = ""
|
||||
blob = ""
|
||||
|
||||
for row in data:
|
||||
if current_item_type != row.item_type:
|
||||
blob += f"\n\n<header2>{row.item_type}</header2>\n"
|
||||
current_item_type = row.item_type
|
||||
blob += f"{self.text.zfill(row.amount, data[0].amount)} - " \
|
||||
f"{self.text.make_item(row.lowid, row.highid, row.highql, row.name)}\n"
|
||||
|
||||
blob += self.get_footer()
|
||||
|
||||
return ChatBlob(f"Whatbuffs - {skill.name} {item_type} ({len(data):d})", blob)
|
||||
|
||||
def get_footer(self):
|
||||
return "\nItem DB Extraction Info provided by Unk"
|
||||
Reference in New Issue
Block a user