2023-05-21 01:26:58 +00:00
|
|
|
|
import re
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
from functools import cached_property
|
|
|
|
|
from typing import ClassVar
|
|
|
|
|
|
|
|
|
|
import module.config.server as server
|
2023-08-10 18:43:06 +00:00
|
|
|
|
from module.exception import ScriptError
|
2023-05-21 01:26:58 +00:00
|
|
|
|
|
2023-10-07 13:52:41 +00:00
|
|
|
|
# ord('.') = 65294
|
2024-01-12 15:15:27 +00:00
|
|
|
|
REGEX_PUNCTUATION = re.compile(r'[ ,..\'"“”,。…::;;!!??·・•*※\-—-/\\\n\t()\[\]()「」『』【】《》[]]')
|
2023-05-21 01:26:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_name(n):
|
|
|
|
|
n = REGEX_PUNCTUATION.sub('', str(n)).lower()
|
2023-10-07 13:52:41 +00:00
|
|
|
|
return n.strip()
|
2023-05-21 01:26:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
class Keyword:
|
|
|
|
|
id: int
|
2023-06-12 16:43:57 +00:00
|
|
|
|
name: str
|
2023-05-21 01:26:58 +00:00
|
|
|
|
cn: str
|
|
|
|
|
en: str
|
|
|
|
|
jp: str
|
2023-05-22 12:20:31 +00:00
|
|
|
|
cht: str
|
2023-09-25 15:24:40 +00:00
|
|
|
|
es: str
|
2023-05-21 01:26:58 +00:00
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
Instance attributes and methods
|
|
|
|
|
"""
|
2023-08-10 18:43:06 +00:00
|
|
|
|
|
2023-06-19 00:39:41 +00:00
|
|
|
|
@cached_property
|
|
|
|
|
def ch(self) -> str:
|
|
|
|
|
return self.cn
|
2023-05-21 01:26:58 +00:00
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
|
def cn_parsed(self) -> str:
|
|
|
|
|
return parse_name(self.cn)
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
|
def en_parsed(self) -> str:
|
|
|
|
|
return parse_name(self.en)
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
|
def jp_parsed(self) -> str:
|
|
|
|
|
return parse_name(self.jp)
|
|
|
|
|
|
|
|
|
|
@cached_property
|
2023-05-22 12:20:31 +00:00
|
|
|
|
def cht_parsed(self) -> str:
|
|
|
|
|
return parse_name(self.cht)
|
|
|
|
|
|
2023-09-25 15:24:40 +00:00
|
|
|
|
@cached_property
|
|
|
|
|
def es_parsed(self) -> str:
|
|
|
|
|
return parse_name(self.cht)
|
|
|
|
|
|
2023-05-22 12:20:31 +00:00
|
|
|
|
def __str__(self):
|
2023-06-14 16:15:14 +00:00
|
|
|
|
return f'{self.__class__.__name__}({self.name})'
|
2023-05-22 12:20:31 +00:00
|
|
|
|
|
|
|
|
|
__repr__ = __str__
|
|
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
|
return str(self) == str(other)
|
|
|
|
|
|
|
|
|
|
def __hash__(self):
|
|
|
|
|
return hash(self.name)
|
|
|
|
|
|
|
|
|
|
def __bool__(self):
|
|
|
|
|
return True
|
2023-05-21 01:26:58 +00:00
|
|
|
|
|
2023-09-15 05:29:22 +00:00
|
|
|
|
def _keywords_to_find(self, lang: str = None, ignore_punctuation=True):
|
|
|
|
|
if lang is None:
|
|
|
|
|
lang = server.lang
|
|
|
|
|
|
|
|
|
|
if lang in server.VALID_LANG:
|
|
|
|
|
match lang:
|
2023-05-21 01:26:58 +00:00
|
|
|
|
case 'cn':
|
|
|
|
|
if ignore_punctuation:
|
|
|
|
|
return [self.cn_parsed]
|
|
|
|
|
else:
|
|
|
|
|
return [self.cn]
|
|
|
|
|
case 'en':
|
|
|
|
|
if ignore_punctuation:
|
|
|
|
|
return [self.en_parsed]
|
|
|
|
|
else:
|
|
|
|
|
return [self.en]
|
|
|
|
|
case 'jp':
|
|
|
|
|
if ignore_punctuation:
|
|
|
|
|
return [self.jp_parsed]
|
|
|
|
|
else:
|
|
|
|
|
return [self.jp]
|
2023-05-30 18:20:45 +00:00
|
|
|
|
case 'cht':
|
2023-05-21 01:26:58 +00:00
|
|
|
|
if ignore_punctuation:
|
2023-05-22 12:20:31 +00:00
|
|
|
|
return [self.cht_parsed]
|
2023-05-21 01:26:58 +00:00
|
|
|
|
else:
|
2023-05-22 12:20:31 +00:00
|
|
|
|
return [self.cht]
|
2023-09-25 15:24:40 +00:00
|
|
|
|
case 'es':
|
|
|
|
|
if ignore_punctuation:
|
|
|
|
|
return [self.es_parsed]
|
|
|
|
|
else:
|
|
|
|
|
return [self.es]
|
2023-05-21 01:26:58 +00:00
|
|
|
|
else:
|
|
|
|
|
if ignore_punctuation:
|
|
|
|
|
return [
|
|
|
|
|
self.cn_parsed,
|
|
|
|
|
self.en_parsed,
|
|
|
|
|
self.jp_parsed,
|
2023-05-22 12:20:31 +00:00
|
|
|
|
self.cht_parsed,
|
2023-09-25 15:24:40 +00:00
|
|
|
|
self.es_parsed,
|
2023-05-21 01:26:58 +00:00
|
|
|
|
]
|
|
|
|
|
else:
|
|
|
|
|
return [
|
|
|
|
|
self.cn,
|
|
|
|
|
self.en,
|
|
|
|
|
self.jp,
|
2023-05-22 12:20:31 +00:00
|
|
|
|
self.cht,
|
2023-09-25 15:24:40 +00:00
|
|
|
|
self.es,
|
2023-05-21 01:26:58 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
Class attributes and methods
|
2023-05-30 18:20:45 +00:00
|
|
|
|
|
|
|
|
|
Note that dataclasses inherited `Keyword` must override `instances` attribute,
|
|
|
|
|
or `instances` will still be a class attribute of base class.
|
|
|
|
|
```
|
|
|
|
|
@dataclass
|
|
|
|
|
class DungeonNav(Keyword):
|
|
|
|
|
instances: ClassVar = {}
|
|
|
|
|
```
|
2023-05-21 01:26:58 +00:00
|
|
|
|
"""
|
2023-06-17 16:13:21 +00:00
|
|
|
|
# Key: instance ID. Value: instance object.
|
2023-05-21 01:26:58 +00:00
|
|
|
|
instances: ClassVar = {}
|
|
|
|
|
|
|
|
|
|
def __post_init__(self):
|
|
|
|
|
self.__class__.instances[self.id] = self
|
|
|
|
|
|
2023-08-02 11:07:46 +00:00
|
|
|
|
@classmethod
|
|
|
|
|
def _compare(cls, name, keyword):
|
|
|
|
|
return name == keyword
|
|
|
|
|
|
2023-05-21 01:26:58 +00:00
|
|
|
|
@classmethod
|
2023-09-15 05:29:22 +00:00
|
|
|
|
def find(cls, name, lang: str = None, ignore_punctuation=True):
|
2023-05-21 01:26:58 +00:00
|
|
|
|
"""
|
|
|
|
|
Args:
|
|
|
|
|
name: Name in any server or instance id.
|
2023-09-15 05:29:22 +00:00
|
|
|
|
lang: Lang to find from
|
|
|
|
|
None to search the names from current server only.
|
2023-05-21 01:26:58 +00:00
|
|
|
|
ignore_punctuation: True to remove punctuations and turn into lowercase before searching.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Keyword instance.
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
ScriptError: If nothing found.
|
|
|
|
|
"""
|
|
|
|
|
# Already a keyword
|
|
|
|
|
if isinstance(name, Keyword):
|
|
|
|
|
return name
|
|
|
|
|
# Probably an ID
|
|
|
|
|
if isinstance(name, int) or (isinstance(name, str) and name.isdigit()):
|
|
|
|
|
try:
|
|
|
|
|
return cls.instances[int(name)]
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
2023-06-12 16:43:57 +00:00
|
|
|
|
# Probably a variable name
|
|
|
|
|
if isinstance(name, str) and '_' in name:
|
|
|
|
|
for instance in cls.instances.values():
|
|
|
|
|
if name == instance.name:
|
|
|
|
|
return instance
|
|
|
|
|
# Probably an in-game name
|
2023-05-22 12:20:31 +00:00
|
|
|
|
if ignore_punctuation:
|
|
|
|
|
name = parse_name(name)
|
|
|
|
|
else:
|
|
|
|
|
name = str(name)
|
2023-05-21 01:26:58 +00:00
|
|
|
|
instance: Keyword
|
|
|
|
|
for instance in cls.instances.values():
|
|
|
|
|
for keyword in instance._keywords_to_find(
|
2023-09-15 05:29:22 +00:00
|
|
|
|
lang=lang, ignore_punctuation=ignore_punctuation):
|
2023-08-02 11:07:46 +00:00
|
|
|
|
if cls._compare(name, keyword):
|
2023-05-21 01:26:58 +00:00
|
|
|
|
return instance
|
|
|
|
|
|
2023-06-12 16:43:57 +00:00
|
|
|
|
# Not found
|
2023-05-21 01:26:58 +00:00
|
|
|
|
raise ScriptError(f'Cannot find a {cls.__name__} instance that matches "{name}"')
|
2023-12-20 16:15:46 +00:00
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def find_name(cls, name):
|
|
|
|
|
"""
|
|
|
|
|
Args:
|
|
|
|
|
name: Attribute name of keyword.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Keyword instance.
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
ScriptError: If nothing found.
|
|
|
|
|
"""
|
|
|
|
|
if isinstance(name, Keyword):
|
|
|
|
|
return name
|
|
|
|
|
for instance in cls.instances.values():
|
|
|
|
|
if name == instance.name:
|
|
|
|
|
return instance
|
|
|
|
|
|
|
|
|
|
# Not found
|
|
|
|
|
raise ScriptError(f'Cannot find a {cls.__name__} instance that matches "{name}"')
|
2024-01-01 16:43:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class KeywordDigitCounter(Keyword):
|
|
|
|
|
"""
|
|
|
|
|
A fake Keyword class to filter digit counters in ocr results
|
|
|
|
|
OcrResultButton.match_keyword will be a str
|
|
|
|
|
"""
|
|
|
|
|
@classmethod
|
|
|
|
|
def find(cls, name, lang: str = None, ignore_punctuation=True):
|
|
|
|
|
from module.ocr.ocr import DigitCounter
|
|
|
|
|
if DigitCounter.is_format_matched(name):
|
|
|
|
|
return name
|
|
|
|
|
else:
|
|
|
|
|
raise ScriptError
|