Redesign: add 1-3★ auto-reply pools, filter modes, new settings UI
- app.py: add pool keys for stars 1-3, AUTO_REPLY_STARS_KEY/FILTER_KEY, _load_enabled_stars(), _load_filter_mode(), updated _has_review_content() with filter_mode param, new /auto-reply-settings POST route, index route passes enabled_stars/filter_mode/pools 1-5 to template - templates/index.html: remove reviews list & controls section, replace pool card with full settings card (star toggles, filter mode radios, dynamic per-star pool columns), update JS for all 5 stars - templates/login.html: fix ratings feature text (1-5★), update price to 7 ₽/день with corrected description - static/styles.css: login-promo fixed 300px width, logo 140px, add settings UI styles (star-toggles, filter-options, pool-columns, star-chips 1-3) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,11 @@ AUTO_REPLY_SETTING_KEY = "auto_reply_enabled"
|
||||
AUTO_REPLY_LAST_RUN_KEY = "auto_reply_last_run_ts"
|
||||
AUTO_REPLY_POOL_5_KEY = "auto_reply_pool_5"
|
||||
AUTO_REPLY_POOL_4_KEY = "auto_reply_pool_4"
|
||||
AUTO_REPLY_POOL_3_KEY = "auto_reply_pool_3"
|
||||
AUTO_REPLY_POOL_2_KEY = "auto_reply_pool_2"
|
||||
AUTO_REPLY_POOL_1_KEY = "auto_reply_pool_1"
|
||||
AUTO_REPLY_STARS_KEY = "auto_reply_stars_json"
|
||||
AUTO_REPLY_FILTER_KEY = "auto_reply_filter"
|
||||
AUTO_REPLY_QUEUE_KEY = "auto_reply_queue_json"
|
||||
AUTO_REPLY_LAST_FETCH_KEY = "auto_reply_last_fetch_ts"
|
||||
AUTO_REPLY_INTERVAL_MINUTES = int(os.getenv("AUTO_REPLY_INTERVAL_MINUTES", "10"))
|
||||
@@ -815,8 +820,9 @@ def _save_auto_reply_queue(queue: List[dict]) -> None:
|
||||
|
||||
def _build_auto_reply_queue(reviews: List[Review]) -> List[dict]:
|
||||
queue: List[dict] = []
|
||||
filter_mode = _load_filter_mode()
|
||||
for review in reviews:
|
||||
if _has_review_content(review):
|
||||
if _has_review_content(review, filter_mode):
|
||||
logger.info("Auto-reply skip review_id=%s reason=has_review_text", review.id)
|
||||
db.add_auto_reply_log(
|
||||
review_id=review.id,
|
||||
@@ -863,9 +869,32 @@ def _load_reply_pool(star: int) -> List[str]:
|
||||
if star == 4:
|
||||
db_value = db.get_setting(AUTO_REPLY_POOL_4_KEY)
|
||||
return _parse_pool(db_value or ENV_REPLY_POOL_4, DEFAULT_REPLY_POOL_4)
|
||||
if star in (1, 2, 3):
|
||||
return _parse_pool(db.get_setting(f"auto_reply_pool_{star}") or "", [])
|
||||
return []
|
||||
|
||||
|
||||
def _load_enabled_stars() -> Set[int]:
|
||||
raw = db.get_setting(AUTO_REPLY_STARS_KEY)
|
||||
if raw:
|
||||
try:
|
||||
parsed = json.loads(raw)
|
||||
if isinstance(parsed, list):
|
||||
result = {int(s) for s in parsed if isinstance(s, (int, float, str)) and str(s).isdigit() and 1 <= int(s) <= 5}
|
||||
if result:
|
||||
return result
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return AUTO_REPLY_STARS
|
||||
|
||||
|
||||
def _load_filter_mode() -> str:
|
||||
raw = db.get_setting(AUTO_REPLY_FILTER_KEY)
|
||||
if raw in ("no_text", "empty", "all"):
|
||||
return raw
|
||||
return "no_text"
|
||||
|
||||
|
||||
def _pool_to_multiline_text(pool: List[str]) -> str:
|
||||
return "\n".join(pool)
|
||||
|
||||
@@ -877,8 +906,17 @@ def _pick_auto_reply(star: int) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
def _has_review_content(review: Review) -> bool:
|
||||
return bool((review.text or "").strip())
|
||||
def _has_review_content(review: Review, filter_mode: str = "no_text") -> bool:
|
||||
if filter_mode == "no_text":
|
||||
return bool((review.text or "").strip())
|
||||
if filter_mode == "empty":
|
||||
return bool(
|
||||
(review.text or "").strip()
|
||||
or (review.pros or "").strip()
|
||||
or (review.cons or "").strip()
|
||||
)
|
||||
# filter_mode == "all": reply to everything, treat as no content to skip
|
||||
return False
|
||||
|
||||
|
||||
def process_auto_replies() -> int:
|
||||
@@ -906,7 +944,7 @@ def process_auto_replies() -> int:
|
||||
reviews = client.fetch_reviews(
|
||||
limit=100,
|
||||
unanswered_only=True,
|
||||
allowed_ratings=AUTO_REPLY_STARS,
|
||||
allowed_ratings=_load_enabled_stars(),
|
||||
)
|
||||
except FeedbackApiError:
|
||||
db.set_setting(AUTO_REPLY_LAST_FETCH_KEY, str(time.time()))
|
||||
@@ -1265,8 +1303,13 @@ def index():
|
||||
next_auto_reply_at=next_auto_reply_at,
|
||||
next_auto_reply_in_seconds=next_auto_reply_in_seconds,
|
||||
api_cooldown_seconds_left=api_cooldown_seconds_left,
|
||||
enabled_stars=list(_load_enabled_stars()),
|
||||
filter_mode=_load_filter_mode(),
|
||||
reply_pool_5_text=_pool_to_multiline_text(_load_reply_pool(5)),
|
||||
reply_pool_4_text=_pool_to_multiline_text(_load_reply_pool(4)),
|
||||
reply_pool_3_text=_pool_to_multiline_text(_load_reply_pool(3)),
|
||||
reply_pool_2_text=_pool_to_multiline_text(_load_reply_pool(2)),
|
||||
reply_pool_1_text=_pool_to_multiline_text(_load_reply_pool(1)),
|
||||
reply_pool_5_list=_load_reply_pool(5),
|
||||
reply_pool_4_list=_load_reply_pool(4),
|
||||
auto_reply_queue=_load_auto_reply_queue(),
|
||||
@@ -1322,6 +1365,23 @@ def auto_reply_pools():
|
||||
return redirect(url_for("index", status="pools_saved", action="unanswered", stars=[5, 4]))
|
||||
|
||||
|
||||
@app.route("/auto-reply-settings", methods=["POST"])
|
||||
@login_required
|
||||
def auto_reply_settings():
|
||||
stars = [int(s) for s in request.form.getlist("stars") if s.isdigit() and 1 <= int(s) <= 5]
|
||||
filter_mode = request.form.get("filter_mode", "no_text")
|
||||
if filter_mode not in ("no_text", "empty", "all"):
|
||||
filter_mode = "no_text"
|
||||
db.set_setting(AUTO_REPLY_STARS_KEY, json.dumps(stars))
|
||||
db.set_setting(AUTO_REPLY_FILTER_KEY, filter_mode)
|
||||
# Save pools for each enabled star
|
||||
for star in range(1, 6):
|
||||
items = [i.strip() for i in request.form.getlist(f"pool_{star}_item") if i.strip()]
|
||||
if items:
|
||||
db.set_setting(f"auto_reply_pool_{star}", _pool_to_multiline_text(items))
|
||||
return redirect(url_for("index", status="pools_saved"))
|
||||
|
||||
|
||||
@app.route("/reply", methods=["POST"])
|
||||
@login_required
|
||||
def reply_all():
|
||||
|
||||
Reference in New Issue
Block a user