#!/usr/bin/env python3 from __future__ import annotations import json import os from functools import partial from http import HTTPStatus from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path ROOT = Path(__file__).resolve().parents[1] PUBLIC = ROOT / 'public' DIGEST_ROOT = Path(os.environ.get('DORIS_DIGEST_DIR', '/home/fizzlepoof/.openclaw/workspace/doris-digest')) DIGEST_FEEDBACK = DIGEST_ROOT / 'data' / 'feedback.json' PAPERLESS_STATE = ROOT / 'data' / 'paperless_review_state.json' HOST = os.environ.get('HOST', '0.0.0.0') PORT = int(os.environ.get('PORT', '8787')) def load_json(path: Path, default): try: return json.loads(path.read_text()) if path.exists() else default except Exception: return default def save_json(path: Path, value) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(value, indent=2) + '\n') class Handler(SimpleHTTPRequestHandler): def __init__(self, *args, directory=None, **kwargs): super().__init__(*args, directory=str(PUBLIC), **kwargs) def do_POST(self): length = int(self.headers.get('Content-Length', '0') or '0') raw = self.rfile.read(length) if length else b'{}' try: payload = json.loads(raw.decode('utf-8') or '{}') except Exception: return self._json(HTTPStatus.BAD_REQUEST, {'ok': False, 'error': 'invalid_json'}) if self.path == '/api/topic-feedback': return self._topic_feedback(payload) if self.path == '/api/topic-feedback/reset': return self._topic_reset() if self.path == '/api/paperless-action': return self._paperless_action(payload) return self._json(HTTPStatus.NOT_FOUND, {'ok': False, 'error': 'not_found'}) def _topic_feedback(self, payload: dict): term = str(payload.get('term', '')).strip() vote = str(payload.get('vote', '')).strip().lower() if not term or vote not in {'up', 'down'}: return self._json(HTTPStatus.BAD_REQUEST, {'ok': False, 'error': 'bad_payload'}) items = load_json(DIGEST_FEEDBACK, []) items = [x for x in items if not (str(x.get('origin', '')) == 'dashboard' and str(x.get('term', '')).strip().lower() == term.lower())] items.append({ 'term': term, 'vote': vote, 'note': f"Dashboard {'hide' if vote == 'down' else 'prefer'} topic: {term}", 'origin': 'dashboard', 'source': str(payload.get('source', '')).strip(), 'domain': str(payload.get('domain', '')).strip(), }) save_json(DIGEST_FEEDBACK, items) return self._json(HTTPStatus.OK, {'ok': True, 'count': len(items)}) def _topic_reset(self): items = load_json(DIGEST_FEEDBACK, []) items = [x for x in items if str(x.get('origin', '')) != 'dashboard'] save_json(DIGEST_FEEDBACK, items) return self._json(HTTPStatus.OK, {'ok': True, 'count': len(items)}) def _paperless_action(self, payload: dict): item_id = str(payload.get('id', '')).strip() action = str(payload.get('action', '')).strip().lower() if not item_id or action not in {'needs_attention', 'good_to_go'}: return self._json(HTTPStatus.BAD_REQUEST, {'ok': False, 'error': 'bad_payload'}) state = load_json(PAPERLESS_STATE, {}) current = state.get(item_id, {}) if isinstance(state, dict) else {} current_status = str(current.get('status') or '').lower() if action == 'needs_attention': new_status = 'active' if current_status == 'needs_attention' else 'needs_attention' else: new_status = 'good_to_go' state[item_id] = {'status': new_status} save_json(PAPERLESS_STATE, state) return self._json(HTTPStatus.OK, {'ok': True, 'id': item_id, 'status': new_status}) def _json(self, status: HTTPStatus, payload: dict): body = json.dumps(payload).encode('utf-8') self.send_response(status) self.send_header('Content-Type', 'application/json; charset=utf-8') self.send_header('Content-Length', str(len(body))) self.end_headers() self.wfile.write(body) def log_message(self, fmt, *args): print('%s - - [%s] %s' % (self.address_string(), self.log_date_time_string(), fmt % args)) if __name__ == '__main__': handler = partial(Handler, directory=str(PUBLIC)) ThreadingHTTPServer((HOST, PORT), handler).serve_forever()