treehole.models

树洞相关数据模型

  1"""
  2树洞相关数据模型
  3"""
  4
  5from dataclasses import dataclass
  6from typing import Any, Dict, Tuple, Optional, Union
  7
  8__all__ = ("Hole", "Comment", "UserName")
  9
 10
 11@dataclass(init=True, repr=True, order=False, unsafe_hash=True, frozen=False)
 12class Label:
 13    """
 14    树洞标签数据模型
 15    """
 16
 17    id: Optional[int] = None
 18    """标签 ID"""
 19    tag_name: Optional[str] = None
 20    """标签名称"""
 21    created_at: Optional[int] = None
 22    """创建时间"""
 23    updated_at: Optional[int] = None
 24    """更新时间"""
 25
 26    @classmethod
 27    def from_data(cls, data: Dict[str, Any]):
 28        """
 29        从字典数据创建标签对象
 30        """
 31        return cls(
 32            id=data.get("id"),
 33            tag_name=data.get("tag_name"),
 34            created_at=data.get("created_at"),
 35            updated_at=data.get("updated_at"),
 36        )
 37
 38
 39@dataclass(init=True, repr=False, order=False, unsafe_hash=True, frozen=False)
 40class Hole:
 41    """
 42    树洞基本数据模型
 43    """
 44
 45    pid: Optional[int] = None
 46    """树洞 ID"""
 47    timestamp: Optional[int] = None
 48    """树洞创建时间戳"""
 49    type: Optional[str] = None
 50    """树洞类型(目前已知仅有:`text` 和 `image` 两种类型)"""
 51    text: Optional[str] = None
 52    """树洞文本内容"""
 53    image_size: Optional[Tuple[int, int]] = None
 54    """树洞图片大小(仅当 `type` 为 `image` 时非零)"""
 55    extra: Optional[int] = None
 56    """树洞额外信息(暂不确定其具体含义,可能为带图的洞额外计数)"""
 57
 58    tag: Optional[str] = None
 59    """树洞标签"""
 60    label: Optional[int] = None
 61    """树洞标签分类"""
 62    label_info: Optional[Label] = None
 63    """树洞标签信息"""
 64
 65    reply: Optional[int] = None
 66    """树洞回复数"""
 67    likenum: Optional[int] = None
 68    """树洞关注数"""
 69    anonymous: Optional[int] = None
 70    """树洞是否匿名(暂不确定)"""
 71    status: Optional[int] = None
 72    """树洞状态(暂不确定)"""
 73    is_top: Optional[int] = None
 74    """树洞是否置顶(暂不确定)"""
 75    is_comment: Optional[int] = None
 76    """树洞是否可评论(暂不确定)"""
 77    is_follow: Optional[int] = None
 78    """树洞是否已关注(暂不确定)"""
 79    is_protect: Optional[int] = None
 80    """树洞是否被保护(暂不确定)"""
 81
 82    @classmethod
 83    def from_data(cls, data: Dict[str, Any]):
 84        """
 85        从字典数据创建树洞对象
 86        """
 87        return cls(
 88            pid=data.get("pid"),
 89            timestamp=data.get("timestamp"),
 90            type=data.get("type"),
 91            text=data.get("text"),
 92            image_size=tuple(data.get("image_size", (0, 0))),
 93            extra=data.get("extra"),
 94            tag=data.get("tag"),
 95            label=data.get("label"),
 96            label_info=Label.from_data(data.get("label_info", {}))
 97            if data.get("label_info")
 98            else None,
 99            reply=data.get("reply"),
100            likenum=data.get("likenum"),
101            anonymous=data.get("anonymous"),
102            status=data.get("status"),
103            is_top=data.get("is_top"),
104            is_comment=data.get("is_comment"),
105            is_follow=data.get("is_follow"),
106            is_protect=data.get("is_protect"),
107        )
108
109    def __repr__(self):
110        return str(self.data)
111
112    @property
113    def data(self):
114        """
115        树洞数据转字典
116        """
117        return self.__dict__
118
119
120@dataclass(init=True, repr=False, order=False, unsafe_hash=True, frozen=False)
121class Comment:
122    """
123    树洞回复数据模型
124    """
125
126    cid: Optional[int] = None
127    """回复 ID"""
128    pid: Optional[int] = None
129    """树洞 ID"""
130    timestamp: Optional[int] = None
131    """回复时间戳"""
132
133    name: Optional[str] = None
134    """回复者昵称"""
135    islz: Optional[int] = None
136    """是否为洞主回复(0 为否,1 为是)"""
137    text: Optional[str] = None
138    """回复文本内容"""
139
140    tag: Optional[str] = None
141    """回复标签"""
142    # TODO: Figure out the meaning of following field
143    anonymous: Optional[int] = None
144    """是否为匿名回复(暂不确定)"""
145    hidden: Optional[int] = None
146    """是否为隐藏回复(暂不确定)"""
147
148    @classmethod
149    def from_data(cls, data: Dict[str, Any]):
150        """
151        从字典数据创建回复对象
152        """
153        return cls(
154            cid=data.get("cid"),
155            pid=data.get("pid"),
156            text=data.get("text"),
157            timestamp=data.get("timestamp"),
158            tag=data.get("tag"),
159            islz=data.get("islz"),
160            name=data.get("name"),
161            anonymous=data.get("anonymous"),
162        )
163
164    def __repr__(self):
165        return str(self.data)
166
167    @property
168    def data(self):
169        """
170        回复数据转字典
171        """
172        return self.__dict__
173
174
175class UserNameMeta(type):
176    prefixes = [
177        "",
178        "Angry",
179        "Baby",
180        "Crazy",
181        "Diligent",
182        "Excited",
183        "Fat",
184        "Greedy",
185        "Hungry",
186        "Interesting",
187        "Jolly",
188        "Kind",
189        "Little",
190        "Magic",
191        "Naïve",
192        "Old",
193        "Powerful",
194        "Quiet",
195        "Rich",
196        "Superman",
197        "THU",
198        "Undefined",
199        "Valuable",
200        "Wifeless",
201        "Xiangbuchulai",
202        "Young",
203        "Zombie",
204    ]
205    suffixes = [
206        "Alice",
207        "Bob",
208        "Carol",
209        "Dave",
210        "Eve",
211        "Francis",
212        "Grace",
213        "Hans",
214        "Isabella",
215        "Jason",
216        "Kate",
217        "Louis",
218        "Margaret",
219        "Nathan",
220        "Olivia",
221        "Paul",
222        "Queen",
223        "Richard",
224        "Susan",
225        "Thomas",
226        "Uma",
227        "Vivian",
228        "Winnie",
229        "Xander",
230        "Yasmine",
231        "Zach",
232    ]
233    overflow = "You Win"
234
235    def __contains__(cls, item: str) -> bool:
236        assert isinstance(item, str)
237        name_lst = item.split()
238        assert 1 <= len(name_lst) <= 3
239        if len(name_lst) == 1:
240            return name_lst[0].capitalize() in cls.suffixes
241        elif len(name_lst) == 2:
242            return (
243                name_lst[0].capitalize() in cls.prefixes
244                and name_lst[1].capitalize() in cls.suffixes
245            )
246        else:
247            return [
248                name_lst[0].capitalize(),
249                name_lst[1].capitalize(),
250            ] == cls.overflow.split() and name_lst[2].isdigit()
251
252    def __getitem__(cls, x: Union[int, str]) -> Union[str, int]:
253        if isinstance(x, int):
254            if x < len(cls.prefixes) * len(cls.suffixes):
255                if x < len(cls.suffixes):
256                    return (
257                        cls.prefixes[x // len(cls.suffixes)]
258                        + cls.suffixes[x % len(cls.suffixes)]
259                    )
260                else:
261                    return (
262                        cls.prefixes[x // len(cls.suffixes)]
263                        + " "
264                        + cls.suffixes[x % len(cls.suffixes)]
265                    )
266            else:
267                return cls.overflow + " " + str(x)
268        elif isinstance(x, str):
269            if not x in cls:
270                raise ValueError(f"Invalid user name: {x}")
271            name_lst = x.split()
272            if len(name_lst) == 1:
273                return cls.suffixes.index(name_lst[0].capitalize())
274            elif len(name_lst) == 2:
275                return cls.prefixes.index(name_lst[0].capitalize()) * len(
276                    cls.suffixes
277                ) + cls.suffixes.index(name_lst[1].capitalize())
278            else:
279                return int(name_lst[2])
280
281
282class UserName(metaclass=UserNameMeta):
283    """
284    用户昵称名数据模型
285
286    判断是否为合法昵称(大小写不敏感):
287
288    ```python
289    "Angry alice" in UserName # True
290    "Angrya lice" in UserName # False
291    ```
292
293    编号与昵称互转(大小写不敏感,下标从 0 开始):
294
295    ```python
296    UserName[48]     # "Angry Winnie"
297    UserName["You Win 1234"]  # 1234
298    ```
299    """
@dataclass(init=True, repr=False, order=False, unsafe_hash=True, frozen=False)
class Hole:
 40@dataclass(init=True, repr=False, order=False, unsafe_hash=True, frozen=False)
 41class Hole:
 42    """
 43    树洞基本数据模型
 44    """
 45
 46    pid: Optional[int] = None
 47    """树洞 ID"""
 48    timestamp: Optional[int] = None
 49    """树洞创建时间戳"""
 50    type: Optional[str] = None
 51    """树洞类型(目前已知仅有:`text` 和 `image` 两种类型)"""
 52    text: Optional[str] = None
 53    """树洞文本内容"""
 54    image_size: Optional[Tuple[int, int]] = None
 55    """树洞图片大小(仅当 `type` 为 `image` 时非零)"""
 56    extra: Optional[int] = None
 57    """树洞额外信息(暂不确定其具体含义,可能为带图的洞额外计数)"""
 58
 59    tag: Optional[str] = None
 60    """树洞标签"""
 61    label: Optional[int] = None
 62    """树洞标签分类"""
 63    label_info: Optional[Label] = None
 64    """树洞标签信息"""
 65
 66    reply: Optional[int] = None
 67    """树洞回复数"""
 68    likenum: Optional[int] = None
 69    """树洞关注数"""
 70    anonymous: Optional[int] = None
 71    """树洞是否匿名(暂不确定)"""
 72    status: Optional[int] = None
 73    """树洞状态(暂不确定)"""
 74    is_top: Optional[int] = None
 75    """树洞是否置顶(暂不确定)"""
 76    is_comment: Optional[int] = None
 77    """树洞是否可评论(暂不确定)"""
 78    is_follow: Optional[int] = None
 79    """树洞是否已关注(暂不确定)"""
 80    is_protect: Optional[int] = None
 81    """树洞是否被保护(暂不确定)"""
 82
 83    @classmethod
 84    def from_data(cls, data: Dict[str, Any]):
 85        """
 86        从字典数据创建树洞对象
 87        """
 88        return cls(
 89            pid=data.get("pid"),
 90            timestamp=data.get("timestamp"),
 91            type=data.get("type"),
 92            text=data.get("text"),
 93            image_size=tuple(data.get("image_size", (0, 0))),
 94            extra=data.get("extra"),
 95            tag=data.get("tag"),
 96            label=data.get("label"),
 97            label_info=Label.from_data(data.get("label_info", {}))
 98            if data.get("label_info")
 99            else None,
100            reply=data.get("reply"),
101            likenum=data.get("likenum"),
102            anonymous=data.get("anonymous"),
103            status=data.get("status"),
104            is_top=data.get("is_top"),
105            is_comment=data.get("is_comment"),
106            is_follow=data.get("is_follow"),
107            is_protect=data.get("is_protect"),
108        )
109
110    def __repr__(self):
111        return str(self.data)
112
113    @property
114    def data(self):
115        """
116        树洞数据转字典
117        """
118        return self.__dict__

树洞基本数据模型

Hole( pid: Optional[int] = None, timestamp: Optional[int] = None, type: Optional[str] = None, text: Optional[str] = None, image_size: Optional[Tuple[int, int]] = None, extra: Optional[int] = None, tag: Optional[str] = None, label: Optional[int] = None, label_info: Optional[treehole.models.Label] = None, reply: Optional[int] = None, likenum: Optional[int] = None, anonymous: Optional[int] = None, status: Optional[int] = None, is_top: Optional[int] = None, is_comment: Optional[int] = None, is_follow: Optional[int] = None, is_protect: Optional[int] = None)
pid: Optional[int] = None

树洞 ID

timestamp: Optional[int] = None

树洞创建时间戳

type: Optional[str] = None

树洞类型(目前已知仅有:textimage 两种类型)

text: Optional[str] = None

树洞文本内容

image_size: Optional[Tuple[int, int]] = None

树洞图片大小(仅当 typeimage 时非零)

extra: Optional[int] = None

树洞额外信息(暂不确定其具体含义,可能为带图的洞额外计数)

tag: Optional[str] = None

树洞标签

label: Optional[int] = None

树洞标签分类

label_info: Optional[treehole.models.Label] = None

树洞标签信息

reply: Optional[int] = None

树洞回复数

likenum: Optional[int] = None

树洞关注数

anonymous: Optional[int] = None

树洞是否匿名(暂不确定)

status: Optional[int] = None

树洞状态(暂不确定)

is_top: Optional[int] = None

树洞是否置顶(暂不确定)

is_comment: Optional[int] = None

树洞是否可评论(暂不确定)

is_follow: Optional[int] = None

树洞是否已关注(暂不确定)

is_protect: Optional[int] = None

树洞是否被保护(暂不确定)

@classmethod
def from_data(cls, data: Dict[str, Any]):
 83    @classmethod
 84    def from_data(cls, data: Dict[str, Any]):
 85        """
 86        从字典数据创建树洞对象
 87        """
 88        return cls(
 89            pid=data.get("pid"),
 90            timestamp=data.get("timestamp"),
 91            type=data.get("type"),
 92            text=data.get("text"),
 93            image_size=tuple(data.get("image_size", (0, 0))),
 94            extra=data.get("extra"),
 95            tag=data.get("tag"),
 96            label=data.get("label"),
 97            label_info=Label.from_data(data.get("label_info", {}))
 98            if data.get("label_info")
 99            else None,
100            reply=data.get("reply"),
101            likenum=data.get("likenum"),
102            anonymous=data.get("anonymous"),
103            status=data.get("status"),
104            is_top=data.get("is_top"),
105            is_comment=data.get("is_comment"),
106            is_follow=data.get("is_follow"),
107            is_protect=data.get("is_protect"),
108        )

从字典数据创建树洞对象

data

树洞数据转字典

@dataclass(init=True, repr=False, order=False, unsafe_hash=True, frozen=False)
class Comment:
121@dataclass(init=True, repr=False, order=False, unsafe_hash=True, frozen=False)
122class Comment:
123    """
124    树洞回复数据模型
125    """
126
127    cid: Optional[int] = None
128    """回复 ID"""
129    pid: Optional[int] = None
130    """树洞 ID"""
131    timestamp: Optional[int] = None
132    """回复时间戳"""
133
134    name: Optional[str] = None
135    """回复者昵称"""
136    islz: Optional[int] = None
137    """是否为洞主回复(0 为否,1 为是)"""
138    text: Optional[str] = None
139    """回复文本内容"""
140
141    tag: Optional[str] = None
142    """回复标签"""
143    # TODO: Figure out the meaning of following field
144    anonymous: Optional[int] = None
145    """是否为匿名回复(暂不确定)"""
146    hidden: Optional[int] = None
147    """是否为隐藏回复(暂不确定)"""
148
149    @classmethod
150    def from_data(cls, data: Dict[str, Any]):
151        """
152        从字典数据创建回复对象
153        """
154        return cls(
155            cid=data.get("cid"),
156            pid=data.get("pid"),
157            text=data.get("text"),
158            timestamp=data.get("timestamp"),
159            tag=data.get("tag"),
160            islz=data.get("islz"),
161            name=data.get("name"),
162            anonymous=data.get("anonymous"),
163        )
164
165    def __repr__(self):
166        return str(self.data)
167
168    @property
169    def data(self):
170        """
171        回复数据转字典
172        """
173        return self.__dict__

树洞回复数据模型

Comment( cid: Optional[int] = None, pid: Optional[int] = None, timestamp: Optional[int] = None, name: Optional[str] = None, islz: Optional[int] = None, text: Optional[str] = None, tag: Optional[str] = None, anonymous: Optional[int] = None, hidden: Optional[int] = None)
cid: Optional[int] = None

回复 ID

pid: Optional[int] = None

树洞 ID

timestamp: Optional[int] = None

回复时间戳

name: Optional[str] = None

回复者昵称

islz: Optional[int] = None

是否为洞主回复(0 为否,1 为是)

text: Optional[str] = None

回复文本内容

tag: Optional[str] = None

回复标签

anonymous: Optional[int] = None

是否为匿名回复(暂不确定)

hidden: Optional[int] = None

是否为隐藏回复(暂不确定)

@classmethod
def from_data(cls, data: Dict[str, Any]):
149    @classmethod
150    def from_data(cls, data: Dict[str, Any]):
151        """
152        从字典数据创建回复对象
153        """
154        return cls(
155            cid=data.get("cid"),
156            pid=data.get("pid"),
157            text=data.get("text"),
158            timestamp=data.get("timestamp"),
159            tag=data.get("tag"),
160            islz=data.get("islz"),
161            name=data.get("name"),
162            anonymous=data.get("anonymous"),
163        )

从字典数据创建回复对象

data

回复数据转字典

class UserName:
283class UserName(metaclass=UserNameMeta):
284    """
285    用户昵称名数据模型
286
287    判断是否为合法昵称(大小写不敏感):
288
289    ```python
290    "Angry alice" in UserName # True
291    "Angrya lice" in UserName # False
292    ```
293
294    编号与昵称互转(大小写不敏感,下标从 0 开始):
295
296    ```python
297    UserName[48]     # "Angry Winnie"
298    UserName["You Win 1234"]  # 1234
299    ```
300    """

用户昵称名数据模型

判断是否为合法昵称(大小写不敏感):

"Angry alice" in UserName # True
"Angrya lice" in UserName # False

编号与昵称互转(大小写不敏感,下标从 0 开始):

UserName[48]     # "Angry Winnie"
UserName["You Win 1234"]  # 1234
UserName()