nonebot-plugin-bililive 2.0.4__tar.gz → 2.0.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/PKG-INFO +1 -1
  2. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/database/db.py +49 -0
  3. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/pusher/dynamic_pusher.py +7 -10
  4. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/utils/mobile.js +3 -3
  5. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/version.py +1 -1
  6. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/pyproject.toml +1 -1
  7. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/tests/test_maintenance.py +19 -0
  8. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/LICENSE +0 -0
  9. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/README.md +0 -0
  10. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/__init__.py +0 -0
  11. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/__main__.py +0 -0
  12. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/cli/__init__.py +0 -0
  13. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/cli/bot.py +0 -0
  14. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/cli/utils.py +0 -0
  15. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/compat.py +0 -0
  16. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/config.py +0 -0
  17. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/database/__init__.py +0 -0
  18. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/database/models.py +0 -0
  19. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/libs/__init__.py +0 -0
  20. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/libs/dynamic/__init__.py +0 -0
  21. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/libs/dynamic/card.py +0 -0
  22. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/libs/dynamic/desc.py +0 -0
  23. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/libs/dynamic/display.py +0 -0
  24. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/libs/dynamic/user_profile.py +0 -0
  25. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/libs/dynamic/web.py +0 -0
  26. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/__init__.py +0 -0
  27. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/at/__init__.py +0 -0
  28. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/at/at_off.py +0 -0
  29. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/at/at_on.py +0 -0
  30. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/auto_agree.py +0 -0
  31. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/auto_delete.py +0 -0
  32. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/dynamic/__init__.py +0 -0
  33. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/dynamic/dynamic_off.py +0 -0
  34. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/dynamic/dynamic_on.py +0 -0
  35. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/help.py +0 -0
  36. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/live/__init__.py +0 -0
  37. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/live/live_now.py +0 -0
  38. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/live/live_off.py +0 -0
  39. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/live/live_on.py +0 -0
  40. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/permission/__init__.py +0 -0
  41. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/permission/permission_off.py +0 -0
  42. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/permission/permission_on.py +0 -0
  43. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/pusher/__init__.py +0 -0
  44. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/pusher/live_pusher.py +0 -0
  45. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/sub/__init__.py +0 -0
  46. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/sub/add_sub.py +0 -0
  47. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/sub/delete_sub.py +0 -0
  48. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/plugins/sub/sub_list.py +0 -0
  49. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/utils/__init__.py +0 -0
  50. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/utils/browser.py +0 -0
  51. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/utils/captcha_solver.py +0 -0
  52. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/bililive/utils/fonts_provider.py +0 -0
  53. {nonebot_plugin_bililive-2.0.4 → nonebot_plugin_bililive-2.0.5}/nonebot_plugin_bililive/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-bililive
3
- Version: 2.0.4
3
+ Version: 2.0.5
4
4
  Summary: Push bilibili dynamics and live notifications to QQ with NoneBot2.
5
5
  Keywords: nonebot,nonebot2,nonebot-plugin,qqbot,bilibili
6
6
  Author-Email: SK-415 <2967923486@qq.com>
@@ -21,6 +21,10 @@ class DB:
21
21
 
22
22
  _ready = False
23
23
 
24
+ @classmethod
25
+ def get_dynamic_offset_path(cls) -> Path:
26
+ return Path(get_path("dynamic_offset.json"))
27
+
24
28
  @classmethod
25
29
  async def init(cls):
26
30
  """初始化数据库"""
@@ -46,11 +50,14 @@ class DB:
46
50
  await Tortoise.generate_schemas()
47
51
  await cls.migrate()
48
52
  await cls.update_uid_list()
53
+ await cls.load_dynamic_offsets()
54
+ await cls.save_dynamic_offsets()
49
55
  cls._ready = True
50
56
 
51
57
  @classmethod
52
58
  async def close(cls):
53
59
  cls._ready = False
60
+ await cls.save_dynamic_offsets()
54
61
  await connections.close_all()
55
62
 
56
63
  @classmethod
@@ -68,6 +75,43 @@ class DB:
68
75
 
69
76
  return cls._ready
70
77
 
78
+ @classmethod
79
+ async def load_dynamic_offsets(cls):
80
+ path = cls.get_dynamic_offset_path()
81
+ if not path.exists():
82
+ return
83
+ try:
84
+ raw_offsets = json.loads(path.read_text(encoding="utf-8"))
85
+ except Exception:
86
+ logger.warning("动态偏移量缓存读取失败,将使用当前内存状态继续运行")
87
+ return
88
+
89
+ for uid_str, value in raw_offsets.items():
90
+ try:
91
+ uid = int(uid_str)
92
+ dynamic_id = int(value)
93
+ except (TypeError, ValueError):
94
+ continue
95
+ if uid in dynamic_offset:
96
+ dynamic_offset[uid] = dynamic_id
97
+
98
+ @classmethod
99
+ async def save_dynamic_offsets(cls):
100
+ path = cls.get_dynamic_offset_path()
101
+ path.parent.mkdir(parents=True, exist_ok=True)
102
+ serialized_offsets = {
103
+ str(uid): dynamic_offset[uid] for uid in sorted(dynamic_offset)
104
+ }
105
+ path.write_text(
106
+ json.dumps(serialized_offsets, ensure_ascii=False, sort_keys=True),
107
+ encoding="utf-8",
108
+ )
109
+
110
+ @classmethod
111
+ async def set_dynamic_offset(cls, uid: int, value: int):
112
+ dynamic_offset[int(uid)] = int(value)
113
+ await cls.save_dynamic_offsets()
114
+
71
115
  @classmethod
72
116
  async def get_user(cls, **kwargs):
73
117
  """获取 UP 主信息"""
@@ -277,12 +321,17 @@ class DB:
277
321
  )
278
322
 
279
323
  # 清除没有订阅的 offset
324
+ changed = False
280
325
  dynamic_offset_keys = set(dynamic_offset)
281
326
  dynamic_uids = set(uid_list["dynamic"]["list"])
282
327
  for uid in dynamic_offset_keys - dynamic_uids:
283
328
  del dynamic_offset[uid]
329
+ changed = True
284
330
  for uid in dynamic_uids - dynamic_offset_keys:
285
331
  dynamic_offset[uid] = -1
332
+ changed = True
333
+ if changed:
334
+ await cls.save_dynamic_offsets()
286
335
 
287
336
  async def backup(self):
288
337
  """备份数据库"""
@@ -202,20 +202,17 @@ async def dy_sched():
202
202
 
203
203
  if not dynamics: # 没发过动态
204
204
  if uid in offset and offset[uid] == -1: # 不记录会导致第一次发动态不推送
205
- offset[uid] = 0
205
+ await db.set_dynamic_offset(uid, 0)
206
206
  return
207
207
  name = get_dynamic_author_name(dynamics[0], use_web_fallback)
208
208
 
209
209
  if uid not in offset: # 已删除
210
210
  return
211
211
  elif offset[uid] == -1: # 第一次爬取
212
- if len(dynamics) == 1: # 只有一条动态
213
- offset[uid] = get_dynamic_id(dynamics[0], use_web_fallback)
214
- else: # 第一个可能是置顶动态,但置顶也可能是最新一条,所以取前两条的最大值
215
- offset[uid] = max(
216
- get_dynamic_id(dynamics[0], use_web_fallback),
217
- get_dynamic_id(dynamics[1], use_web_fallback),
218
- )
212
+ await db.set_dynamic_offset(
213
+ uid,
214
+ max(get_dynamic_id(item, use_web_fallback) for item in dynamics),
215
+ )
219
216
  return
220
217
 
221
218
  dynamic = None
@@ -234,7 +231,7 @@ async def dy_sched():
234
231
  return
235
232
  elif should_skip_dynamic(dynamic_type, use_web_fallback):
236
233
  logger.debug(f"无需推送的动态 {dynamic_type},已跳过:{url}")
237
- offset[uid] = dynamic_id
234
+ await db.set_dynamic_offset(uid, dynamic_id)
238
235
  return
239
236
  message = (
240
237
  f"{name} {get_dynamic_type_message(dynamic_type, use_web_fallback)}:\n"
@@ -253,7 +250,7 @@ async def dy_sched():
253
250
  at=bool(sets.at) and plugin_config.bililive_dynamic_at,
254
251
  )
255
252
 
256
- offset[uid] = dynamic_id
253
+ await db.set_dynamic_offset(uid, dynamic_id)
257
254
 
258
255
  if dynamic:
259
256
  await db.update_user(uid, name)
@@ -183,13 +183,13 @@ function setFont(font = "", fontSource = "local") {
183
183
  // 将字体样式设置到 div#app 上
184
184
  const appDom = document.querySelector("#app");
185
185
  const emojiFont = emojiFontList.join(",");
186
+ const fallbackFonts = needLoadFontList.map(fontObject => fontObject.fontFamily).join(",");
186
187
  if (appDom) {
187
188
  // 动态加字体, 并给与默认值 sans-serif
188
189
  if (fontSource === "system") {
189
- appDom.style.fontFamily = font + "," + emojiFont + ",sans-serif";
190
+ appDom.style.fontFamily = [font, fallbackFonts, emojiFont, "sans-serif"].filter(Boolean).join(",");
190
191
  } else {
191
- const needLoadFont = needLoadFontList.reduce((defaultString, fontObject) => defaultString + fontObject.fontFamily + ",", "");
192
- appDom.style.fontFamily = needLoadFont + emojiFont + ",sans-serif";
192
+ appDom.style.fontFamily = [fallbackFonts, emojiFont, "sans-serif"].filter(Boolean).join(",");
193
193
  };
194
194
  appDom.style.overflowWrap = "break-word";
195
195
  }
@@ -1,4 +1,4 @@
1
1
  from packaging.version import Version
2
2
 
3
- __version__ = "2.0.4"
3
+ __version__ = "2.0.5"
4
4
  VERSION = Version(__version__)
@@ -38,7 +38,7 @@ dependencies = [
38
38
  "msvc-runtime>=14.34.31931; sys_platform == \"win32\"",
39
39
  ]
40
40
  dynamic = []
41
- version = "2.0.4"
41
+ version = "2.0.5"
42
42
 
43
43
  [project.license]
44
44
  text = "AGPL-3.0-or-later"
@@ -1,6 +1,8 @@
1
1
  import sys
2
+ import tempfile
2
3
  import unittest
3
4
  from importlib import import_module
5
+ from pathlib import Path
4
6
  from types import ModuleType, SimpleNamespace
5
7
  from unittest.mock import AsyncMock, patch
6
8
 
@@ -157,6 +159,23 @@ class DBPermissionTests(unittest.IsolatedAsyncioTestCase):
157
159
 
158
160
  self.assertFalse(ready)
159
161
 
162
+ async def test_dynamic_offsets_are_persisted_and_restored(self):
163
+ with tempfile.TemporaryDirectory() as tmpdir:
164
+ offset_path = Path(tmpdir) / "dynamic_offset.json"
165
+ db_module.dynamic_offset.clear()
166
+ db_module.dynamic_offset[123] = 456
167
+
168
+ with patch.object(db_module, "get_path", return_value=str(offset_path)):
169
+ await DB.save_dynamic_offsets()
170
+
171
+ db_module.dynamic_offset.clear()
172
+ db_module.dynamic_offset[123] = -1
173
+ db_module.dynamic_offset[789] = -1
174
+ await DB.load_dynamic_offsets()
175
+
176
+ self.assertEqual(db_module.dynamic_offset[123], 456)
177
+ self.assertEqual(db_module.dynamic_offset[789], -1)
178
+
160
179
  async def test_set_permission_creates_group_when_missing(self):
161
180
  with (
162
181
  patch.object(DB, "get_group", new=AsyncMock(return_value=None)),