Alialdlfy1 commited on
Commit
3f2c57d
·
verified ·
1 Parent(s): 5317bfe

Delete modules/blogger/database.py

Browse files
Files changed (1) hide show
  1. modules/blogger/database.py +0 -377
modules/blogger/database.py DELETED
@@ -1,377 +0,0 @@
1
- import json
2
- import os
3
- import time
4
- import threading
5
- import logging
6
-
7
- logger = logging.getLogger(__name__)
8
-
9
- _DEFAULT_DB_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "data")
10
- _HF_DATA = "/data" if os.path.isdir("/data") and "SPACE_ID" in os.environ else None
11
- DB_FILE = os.environ.get("BLOGGER_DB_PATH", os.path.join(_HF_DATA or _DEFAULT_DB_DIR, "blogger_data.json"))
12
- lock = threading.Lock()
13
-
14
- DEFAULT_BLOGGER_DATA = {
15
- "config": {
16
- "blog_id": "",
17
- "client_id": "",
18
- "client_secret": "",
19
- "refresh_token": "",
20
- "publish_as_draft": False,
21
- "enabled": False,
22
- "default_jobs_image": "",
23
- },
24
- "ai_keys": {},
25
- "channels": {},
26
- "articles": {},
27
- "published_ids": [],
28
- "sections": {},
29
- "logs": [],
30
- "stats": {
31
- "total_published": 0,
32
- "total_failed": 0,
33
- "daily": {},
34
- "monthly": {},
35
- },
36
- }
37
-
38
-
39
- class BloggerDatabase:
40
- def __init__(self):
41
- self._cache = None
42
-
43
- def _ensure_file(self):
44
- os.makedirs(os.path.dirname(DB_FILE), exist_ok=True)
45
- if not os.path.exists(DB_FILE):
46
- old_path = os.path.join(_DEFAULT_DB_DIR, "blogger_data.json")
47
- if _HF_DATA and os.path.isfile(old_path):
48
- import shutil
49
- shutil.copy2(old_path, DB_FILE)
50
- logger.info(f"Migrated DB from {old_path} to {DB_FILE}")
51
- else:
52
- with open(DB_FILE, "w", encoding="utf-8") as f:
53
- json.dump(DEFAULT_BLOGGER_DATA, f, ensure_ascii=False, indent=2)
54
-
55
- def _read(self):
56
- self._ensure_file()
57
- try:
58
- with open(DB_FILE, "r", encoding="utf-8") as f:
59
- data = json.load(f)
60
- except Exception:
61
- data = {}
62
- for key, value in DEFAULT_BLOGGER_DATA.items():
63
- if key not in data:
64
- data[key] = value.copy() if isinstance(value, (list, dict)) else value
65
- if not isinstance(data.get("config"), dict):
66
- data["config"] = {}
67
- for k, v in DEFAULT_BLOGGER_DATA["config"].items():
68
- data["config"].setdefault(k, v)
69
- return data
70
-
71
- def _write(self, data):
72
- self._ensure_file()
73
- tmp = DB_FILE + ".tmp"
74
- with open(tmp, "w", encoding="utf-8") as f:
75
- json.dump(data, f, ensure_ascii=False, indent=2)
76
- f.flush()
77
- try:
78
- os.fsync(f.fileno())
79
- except Exception:
80
- pass
81
- os.replace(tmp, DB_FILE)
82
- self._cache = None
83
-
84
- def get_config(self):
85
- with lock:
86
- return dict(self._read().get("config", {}))
87
-
88
- def update_config(self, key, value):
89
- with lock:
90
- data = self._read()
91
- data["config"][key] = value
92
- self._write(data)
93
-
94
- def get_channel(self, channel_id):
95
- with lock:
96
- data = self._read()
97
- return data.get("channels", {}).get(str(channel_id))
98
-
99
- def get_all_channels(self):
100
- with lock:
101
- data = self._read()
102
- return list(data.get("channels", {}).values())
103
-
104
- def save_channel(self, channel_id, channel_data):
105
- with lock:
106
- data = self._read()
107
- data["channels"][str(channel_id)] = channel_data
108
- self._write(data)
109
-
110
- def delete_channel(self, channel_id):
111
- with lock:
112
- data = self._read()
113
- data["channels"].pop(str(channel_id), None)
114
- self._write(data)
115
-
116
- def save_article(self, article_id, article_data):
117
- with lock:
118
- data = self._read()
119
- data["articles"][str(article_id)] = article_data
120
- self._write(data)
121
-
122
- def get_article(self, article_id):
123
- with lock:
124
- data = self._read()
125
- return data.get("articles", {}).get(str(article_id))
126
-
127
- def delete_article(self, article_id):
128
- with lock:
129
- data = self._read()
130
- data["articles"].pop(str(article_id), None)
131
- self._write(data)
132
-
133
- def get_all_articles(self):
134
- with lock:
135
- data = self._read()
136
- return list(data.get("articles", {}).values())
137
-
138
- def is_published(self, fingerprint):
139
- with lock:
140
- data = self._read()
141
- return fingerprint in data.get("published_ids", [])
142
-
143
- def mark_published(self, fingerprint):
144
- with lock:
145
- data = self._read()
146
- if fingerprint not in data["published_ids"]:
147
- data["published_ids"].append(fingerprint)
148
- data["stats"]["total_published"] += 1
149
- self._write(data)
150
-
151
- def mark_failed(self):
152
- with lock:
153
- data = self._read()
154
- data["stats"]["total_failed"] += 1
155
- self._write(data)
156
-
157
- def add_log(self, entry):
158
- with lock:
159
- data = self._read()
160
- data["logs"].append(entry)
161
- if len(data["logs"]) > 1000:
162
- data["logs"] = data["logs"][-1000:]
163
- self._write(data)
164
-
165
- def get_logs(self, limit=50):
166
- with lock:
167
- data = self._read()
168
- return list(data["logs"])[-limit:]
169
-
170
- def get_stats(self):
171
- with lock:
172
- data = self._read()
173
- return dict(data.get("stats", {}))
174
-
175
- def get_articles_by_status(self, status):
176
- with lock:
177
- data = self._read()
178
- return [a for a in data.get("articles", {}).values() if a.get("status") == status]
179
-
180
- def update_article_status(self, article_id, status, extra=None):
181
- with lock:
182
- data = self._read()
183
- if str(article_id) in data["articles"]:
184
- data["articles"][str(article_id)]["status"] = status
185
- if extra:
186
- data["articles"][str(article_id)].update(extra)
187
- self._write(data)
188
-
189
- def get_all_sections(self):
190
- with lock:
191
- data = self._read()
192
- return dict(data.get("sections", {}))
193
-
194
- def add_section(self, section_id, section_data):
195
- with lock:
196
- data = self._read()
197
- data["sections"][str(section_id)] = section_data
198
- self._write(data)
199
-
200
- def delete_section(self, section_id):
201
- with lock:
202
- data = self._read()
203
- data["sections"].pop(str(section_id), None)
204
- self._write(data)
205
-
206
- def get_all_ai_keys(self):
207
- with lock:
208
- data = self._read()
209
- return dict(data.get("ai_keys", {}))
210
-
211
- def add_ai_key(self, key_id, key_data):
212
- with lock:
213
- data = self._read()
214
- data["ai_keys"][str(key_id)] = {
215
- "name": key_data.get("name", str(key_id)),
216
- "key": key_data.get("key", ""),
217
- "enabled": key_data.get("enabled", True),
218
- "usage_count": 0,
219
- "error_count": 0,
220
- "last_used": 0,
221
- "added_at": int(time.time()),
222
- }
223
- self._write(data)
224
-
225
- def delete_ai_key(self, key_id):
226
- with lock:
227
- data = self._read()
228
- data["ai_keys"].pop(str(key_id), None)
229
- self._write(data)
230
-
231
- def set_ai_key_enabled(self, key_id, enabled):
232
- with lock:
233
- data = self._read()
234
- if str(key_id) in data["ai_keys"]:
235
- data["ai_keys"][str(key_id)]["enabled"] = enabled
236
- self._write(data)
237
-
238
- def increment_daily_count(self, section):
239
- with lock:
240
- data = self._read()
241
- today = time.strftime("%Y-%m-%d")
242
- if "daily" not in data["stats"]:
243
- data["stats"]["daily"] = {}
244
- if today not in data["stats"]["daily"]:
245
- data["stats"]["daily"][today] = {}
246
- data["stats"]["daily"][today][section] = data["stats"]["daily"][today].get(section, 0) + 1
247
- self._write(data)
248
-
249
- def increment_ai_key_usage(self, key_id):
250
- with lock:
251
- data = self._read()
252
- if str(key_id) in data["ai_keys"]:
253
- data["ai_keys"][str(key_id)]["usage_count"] = data["ai_keys"][str(key_id)].get("usage_count", 0) + 1
254
- data["ai_keys"][str(key_id)]["last_used"] = int(time.time())
255
- self._write(data)
256
-
257
- def increment_ai_key_error(self, key_id):
258
- with lock:
259
- data = self._read()
260
- if str(key_id) in data["ai_keys"]:
261
- data["ai_keys"][str(key_id)]["error_count"] = data["ai_keys"][str(key_id)].get("error_count", 0) + 1
262
- self._write(data)
263
-
264
- SCHEDULE_KEY = "_schedule"
265
-
266
- def get_schedule_state(self) -> dict:
267
- with lock:
268
- data = self._read()
269
- return data.get("stats", {}).get(self.SCHEDULE_KEY, {"day": "", "last_slot": -1})
270
-
271
- def save_schedule_state(self, state: dict):
272
- with lock:
273
- data = self._read()
274
- if "stats" not in data:
275
- data["stats"] = {}
276
- data["stats"][self.SCHEDULE_KEY] = state
277
- self._write(data)
278
-
279
- GEMINI_PENDING_KEY = "_gemini_pending"
280
-
281
- def get_gemini_pending_queue(self) -> list:
282
- with lock:
283
- data = self._read()
284
- return data.get(self.GEMINI_PENDING_KEY, [])
285
-
286
- def save_gemini_pending_queue(self, queue: list):
287
- with lock:
288
- data = self._read()
289
- data[self.GEMINI_PENDING_KEY] = queue
290
- self._write(data)
291
-
292
- def add_to_gemini_pending(self, raw_text: str, source_url: str = "", media: list = None,
293
- fingerprint: str = "", channel_id: str = "", section: str = ""):
294
- with lock:
295
- data = self._read()
296
- pending = data.get(self.GEMINI_PENDING_KEY, [])
297
- # Avoid duplicate entries for the same fingerprint (e.g. re-enqueued after a failed attempt)
298
- if fingerprint and any(p.get("fingerprint") == fingerprint for p in pending):
299
- return
300
- pending.append({
301
- "raw_text": raw_text,
302
- "source_url": source_url,
303
- "media": media or [],
304
- "fingerprint": fingerprint,
305
- "channel_id": channel_id,
306
- "section": section,
307
- "attempts": 0,
308
- "added_at": int(time.time()),
309
- })
310
- data[self.GEMINI_PENDING_KEY] = pending
311
- self._write(data)
312
-
313
- def remove_from_gemini_pending(self, index: int = 0):
314
- with lock:
315
- data = self._read()
316
- pending = data.get(self.GEMINI_PENDING_KEY, [])
317
- if 0 <= index < len(pending):
318
- pending.pop(index)
319
- data[self.GEMINI_PENDING_KEY] = pending
320
- self._write(data)
321
-
322
- def remove_pending_by_fingerprint(self, fingerprint: str):
323
- """Remove a pending item by fingerprint (safe against list reordering/races)."""
324
- with lock:
325
- data = self._read()
326
- pending = data.get(self.GEMINI_PENDING_KEY, [])
327
- new_pending = [p for p in pending if p.get("fingerprint") != fingerprint]
328
- if len(new_pending) != len(pending):
329
- data[self.GEMINI_PENDING_KEY] = new_pending
330
- self._write(data)
331
-
332
- def increment_pending_attempts(self, fingerprint: str) -> int:
333
- """Increment and return the attempt counter for a pending item. Returns -1 if not found."""
334
- with lock:
335
- data = self._read()
336
- pending = data.get(self.GEMINI_PENDING_KEY, [])
337
- for p in pending:
338
- if p.get("fingerprint") == fingerprint:
339
- p["attempts"] = p.get("attempts", 0) + 1
340
- data[self.GEMINI_PENDING_KEY] = pending
341
- self._write(data)
342
- return p["attempts"]
343
- return -1
344
-
345
- MAX_BACKOFF_SECONDS = 3600
346
-
347
- def set_pending_retry_after(self, fingerprint: str, attempts: int):
348
- """Apply exponential backoff to a pending item so the scheduler does not hot-loop
349
- a repeatedly failing article. Returns the seconds until the next retry."""
350
- seconds = min(self.MAX_BACKOFF_SECONDS, 60 * (2 ** max(0, attempts - 1)))
351
- with lock:
352
- data = self._read()
353
- pending = data.get(self.GEMINI_PENDING_KEY, [])
354
- for p in pending:
355
- if p.get("fingerprint") == fingerprint:
356
- p["retry_after"] = int(time.time()) + seconds
357
- data[self.GEMINI_PENDING_KEY] = pending
358
- self._write(data)
359
- return seconds
360
- return seconds
361
-
362
- GEMINI_STATE_KEY = "_gemini"
363
-
364
- def get_gemini_state(self) -> dict:
365
- with lock:
366
- data = self._read()
367
- if "stats" not in data:
368
- data["stats"] = {}
369
- return data["stats"].get(self.GEMINI_STATE_KEY, {"global_cooldown_until": 0.0})
370
-
371
- def save_gemini_state(self, state: dict):
372
- with lock:
373
- data = self._read()
374
- if "stats" not in data:
375
- data["stats"] = {}
376
- data["stats"][self.GEMINI_STATE_KEY] = state
377
- self._write(data)