feat: target channel context history, custom website extraction needs, and categorized main menu

This commit is contained in:
mamad
2026-08-28 22:14:49 +03:30
parent 8ccecfe699
commit 9551ccce71
8 changed files with 350 additions and 73 deletions
+2 -1
View File
@@ -170,8 +170,9 @@ ALTER TABLE targets ADD COLUMN IF NOT EXISTS sleep_end_hour INT DEFAULT 0;
ALTER TABLE targets ADD COLUMN IF NOT EXISTS is_sleep_enabled BOOLEAN DEFAULT FALSE;
ALTER TABLE targets ADD COLUMN IF NOT EXISTS auto_source_ids BIGINT[] DEFAULT '{}';
ALTER TABLE targets ADD COLUMN IF NOT EXISTS language VARCHAR(32) DEFAULT 'fa';
ALTER TABLE targets ADD COLUMN IF NOT EXISTS custom_prompt TEXT DEFAULT '';
ALTER TABLE targets ADD COLUMN IF NOT EXISTS dispatch_order VARCHAR(32) DEFAULT 'order';
ALTER TABLE targets ADD COLUMN IF NOT EXISTS context_message_count INT DEFAULT 0;
ALTER TABLE source_websites ADD COLUMN IF NOT EXISTS custom_instructions TEXT DEFAULT '';
ALTER TABLE sources ADD COLUMN IF NOT EXISTS context_message_count INT DEFAULT 0;
ALTER TABLE posts ADD COLUMN IF NOT EXISTS source_created_at TIMESTAMPTZ;
CREATE INDEX IF NOT EXISTS idx_posts_source_created ON posts(source_channel_id, source_created_at DESC);
+2
View File
@@ -31,6 +31,7 @@ class SourceWebsite:
last_reanalyzed_at: Optional[str] = None
last_fetched_at: Optional[str] = None
api_config: Dict[str, Any] = field(default_factory=dict)
custom_instructions: str = ""
last_error: Optional[str] = None
last_error_at: Optional[str] = None
is_active: bool = True
@@ -54,6 +55,7 @@ class TargetChannel:
auto_source_ids: List[int] = field(default_factory=list)
language: str = "fa"
dispatch_order: str = "order" # "order" (FIFO) or "random"
context_message_count: int = 0
last_post_time: Optional[str] = None
is_active: bool = True
created_at: Optional[str] = None
+45 -4
View File
@@ -92,22 +92,24 @@ class Repository:
category_id: Optional[int] = None,
check_interval_min: int = 30,
auto_reanalyze_hours: int = 24,
api_config: Optional[Dict[str, Any]] = None
api_config: Optional[Dict[str, Any]] = None,
custom_instructions: str = ""
) -> int:
pool = await self._get_pool()
async with pool.acquire() as conn:
cfg_json = json.dumps(api_config or {})
row = await conn.fetchrow(
"""
INSERT INTO source_websites (name, url, category_id, check_interval_min, auto_reanalyze_hours, api_config)
VALUES ($1, $2, $3, $4, $5, $6::jsonb)
INSERT INTO source_websites (name, url, category_id, check_interval_min, auto_reanalyze_hours, api_config, custom_instructions)
VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7)
ON CONFLICT(url) DO UPDATE SET
name = EXCLUDED.name,
category_id = COALESCE(EXCLUDED.category_id, source_websites.category_id),
custom_instructions = COALESCE(NULLIF(EXCLUDED.custom_instructions, ''), source_websites.custom_instructions),
is_active = TRUE
RETURNING id;
""",
name, url, category_id, check_interval_min, auto_reanalyze_hours, cfg_json
name, url, category_id, check_interval_min, auto_reanalyze_hours, cfg_json, custom_instructions
)
return row["id"]
@@ -190,11 +192,50 @@ class Repository:
async with pool.acquire() as conn:
await conn.execute("UPDATE source_websites SET auto_reanalyze_hours = $1 WHERE id = $2;", max(0, hours), site_id)
async def update_source_website_custom_instructions(self, site_id: int, instructions: str) -> None:
pool = await self._get_pool()
async with pool.acquire() as conn:
await conn.execute("UPDATE source_websites SET custom_instructions = $1 WHERE id = $2;", instructions.strip(), site_id)
async def delete_source_website(self, site_id: int) -> None:
pool = await self._get_pool()
async with pool.acquire() as conn:
await conn.execute("UPDATE source_websites SET is_active = FALSE WHERE id = $1;", site_id)
async def update_target_context_count(self, target_id: int, count: int) -> None:
pool = await self._get_pool()
async with pool.acquire() as conn:
await conn.execute("UPDATE targets SET context_message_count = $1 WHERE id = $2;", max(0, count), target_id)
async def get_recent_target_posts(
self,
target_id: int,
limit: int = 10,
exclude_post_id: Optional[int] = None
) -> List[Post]:
if limit <= 0:
return []
pool = await self._get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch(
"""
SELECT * FROM posts
WHERE (
status = 'published'
OR target_channel_id = $1
OR published_to::text LIKE '%"target_id": ' || $1 || '%'
)
AND ($2::BIGINT IS NULL OR id != $2)
AND is_deleted = FALSE
ORDER BY COALESCE(published_at, created_at) DESC, id DESC
LIMIT $3;
""",
target_id, exclude_post_id, limit
)
posts = [_parse_post_row(r) for r in rows]
posts.reverse()
return posts
async def delete_target(self, target_id: int) -> None:
pool = await self._get_pool()
async with pool.acquire() as conn: