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)
@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 )
从字典数据创建树洞对象
@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)
@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 )
从字典数据创建回复对象
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