Update features.py
This commit is contained in:
parent
80a5dc5c15
commit
6b5cd404df
230
features.py
230
features.py
@ -33,6 +33,7 @@ TEAM_MEMBER_ROLES = {"owner", "admin", "member"}
|
|||||||
PROFILE_VISIBILITIES = {"public", "members", "private"}
|
PROFILE_VISIBILITIES = {"public", "members", "private"}
|
||||||
LAST_ACTIVE_VISIBILITIES = {"everyone", "members", "nobody"}
|
LAST_ACTIVE_VISIBILITIES = {"everyone", "members", "nobody"}
|
||||||
PRESENCE_MODES = {"online", "silent", "focus", "dnd"}
|
PRESENCE_MODES = {"online", "silent", "focus", "dnd"}
|
||||||
|
DM_LAYOUT_MODES = {"cards", "compact"}
|
||||||
REACTION_EMOJI_RE = re.compile(r"^.{1,16}$")
|
REACTION_EMOJI_RE = re.compile(r"^.{1,16}$")
|
||||||
HEX_COLOR_RE = re.compile(r"^#[0-9a-fA-F]{6}$")
|
HEX_COLOR_RE = re.compile(r"^#[0-9a-fA-F]{6}$")
|
||||||
PAGE_LINK_RE = re.compile(r"\]\((?:/teams/[^/]+/pages/)?([a-z0-9-]{1,48})\)", re.IGNORECASE)
|
PAGE_LINK_RE = re.compile(r"\]\((?:/teams/[^/]+/pages/)?([a-z0-9-]{1,48})\)", re.IGNORECASE)
|
||||||
@ -86,6 +87,33 @@ def extract_page_reference_slugs(markdown_text: str) -> list[str]:
|
|||||||
return slugs
|
return slugs
|
||||||
|
|
||||||
|
|
||||||
|
def detect_website_brand(website: str) -> dict:
|
||||||
|
value = str(website or "").strip().lower()
|
||||||
|
if not value:
|
||||||
|
return {"host": "", "brand": "web", "label": "website"}
|
||||||
|
|
||||||
|
host = value
|
||||||
|
host = host.replace("https://", "").replace("http://", "")
|
||||||
|
host = host.split("/", 1)[0]
|
||||||
|
host = host.removeprefix("www.")
|
||||||
|
|
||||||
|
mappings = [
|
||||||
|
("github.com", "github", "GitHub"),
|
||||||
|
("youtube.com", "youtube", "YouTube"),
|
||||||
|
("youtu.be", "youtube", "YouTube"),
|
||||||
|
("spotify.com", "spotify", "Spotify"),
|
||||||
|
("soundcloud.com", "soundcloud", "SoundCloud"),
|
||||||
|
("twitter.com", "x", "X"),
|
||||||
|
("x.com", "x", "X"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for suffix, brand, label in mappings:
|
||||||
|
if host.endswith(suffix):
|
||||||
|
return {"host": host, "brand": brand, "label": label}
|
||||||
|
|
||||||
|
return {"host": host, "brand": "web", "label": "website"}
|
||||||
|
|
||||||
|
|
||||||
def attachment_kind_from_name_and_mime(
|
def attachment_kind_from_name_and_mime(
|
||||||
original_name: Optional[str],
|
original_name: Optional[str],
|
||||||
mime_type: Optional[str],
|
mime_type: Optional[str],
|
||||||
@ -104,6 +132,18 @@ def attachment_kind_from_name_and_mime(
|
|||||||
|
|
||||||
def load_page_children(get_db, page_id: int) -> list[dict]:
|
def load_page_children(get_db, page_id: int) -> list[dict]:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
|
has_parent_page = bool(
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
SELECT 1
|
||||||
|
FROM pragma_table_info('pages')
|
||||||
|
WHERE name = 'parent_page_id'
|
||||||
|
"""
|
||||||
|
).fetchone()
|
||||||
|
)
|
||||||
|
if not has_parent_page:
|
||||||
|
return []
|
||||||
|
|
||||||
rows = db.execute(
|
rows = db.execute(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
@ -146,6 +186,19 @@ def load_page_backlinks(get_db, page_id: int, page_slug: str, team_id: int) -> l
|
|||||||
|
|
||||||
def load_page_comments(get_db, page_id: int) -> list[dict]:
|
def load_page_comments(get_db, page_id: int) -> list[dict]:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
|
table_exists = bool(
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
SELECT 1
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type = 'table'
|
||||||
|
AND name = 'page_comments'
|
||||||
|
"""
|
||||||
|
).fetchone()
|
||||||
|
)
|
||||||
|
if not table_exists:
|
||||||
|
return []
|
||||||
|
|
||||||
rows = db.execute(
|
rows = db.execute(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
@ -187,6 +240,19 @@ def load_page_comments(get_db, page_id: int) -> list[dict]:
|
|||||||
|
|
||||||
def next_page_version_number(get_db, page_id: int) -> int:
|
def next_page_version_number(get_db, page_id: int) -> int:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
|
table_exists = bool(
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
SELECT 1
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type = 'table'
|
||||||
|
AND name = 'page_versions'
|
||||||
|
"""
|
||||||
|
).fetchone()
|
||||||
|
)
|
||||||
|
if not table_exists:
|
||||||
|
return 1
|
||||||
|
|
||||||
row = db.execute(
|
row = db.execute(
|
||||||
"""
|
"""
|
||||||
SELECT COALESCE(MAX(version_number), 0) AS value
|
SELECT COALESCE(MAX(version_number), 0) AS value
|
||||||
@ -210,6 +276,20 @@ def snapshot_page_version(
|
|||||||
created_by_user_id: int,
|
created_by_user_id: int,
|
||||||
created_at: str,
|
created_at: str,
|
||||||
) -> int:
|
) -> int:
|
||||||
|
with get_db() as db:
|
||||||
|
table_exists = bool(
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
SELECT 1
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type = 'table'
|
||||||
|
AND name = 'page_versions'
|
||||||
|
"""
|
||||||
|
).fetchone()
|
||||||
|
)
|
||||||
|
if not table_exists:
|
||||||
|
return 0
|
||||||
|
|
||||||
version_number = next_page_version_number(get_db, page_id)
|
version_number = next_page_version_number(get_db, page_id)
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
db.execute("BEGIN IMMEDIATE")
|
db.execute("BEGIN IMMEDIATE")
|
||||||
@ -247,6 +327,19 @@ def snapshot_page_version(
|
|||||||
|
|
||||||
def load_page_versions(get_db, page_id: int) -> list[dict]:
|
def load_page_versions(get_db, page_id: int) -> list[dict]:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
|
table_exists = bool(
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
SELECT 1
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type = 'table'
|
||||||
|
AND name = 'page_versions'
|
||||||
|
"""
|
||||||
|
).fetchone()
|
||||||
|
)
|
||||||
|
if not table_exists:
|
||||||
|
return []
|
||||||
|
|
||||||
rows = db.execute(
|
rows = db.execute(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
@ -274,6 +367,19 @@ def load_page_versions(get_db, page_id: int) -> list[dict]:
|
|||||||
|
|
||||||
def load_page_version_detail(get_db, version_id: int):
|
def load_page_version_detail(get_db, version_id: int):
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
|
table_exists = bool(
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
SELECT 1
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type = 'table'
|
||||||
|
AND name = 'page_versions'
|
||||||
|
"""
|
||||||
|
).fetchone()
|
||||||
|
)
|
||||||
|
if not table_exists:
|
||||||
|
return None
|
||||||
|
|
||||||
row = db.execute(
|
row = db.execute(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
@ -330,6 +436,9 @@ def page_payload(app_module, page, user: Optional[dict]) -> dict:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
rendered_html = render_markdown_to_safe_html(page["body"] or "")
|
rendered_html = render_markdown_to_safe_html(page["body"] or "")
|
||||||
|
parent_page_id = page["parent_page_id"] if "parent_page_id" in page.keys() else None
|
||||||
|
parent_page_slug = page["parent_page_slug"] if "parent_page_slug" in page.keys() else None
|
||||||
|
parent_page_title = page["parent_page_title"] if "parent_page_title" in page.keys() else None
|
||||||
return {
|
return {
|
||||||
"id": page["id"],
|
"id": page["id"],
|
||||||
"team_id": page["team_id"],
|
"team_id": page["team_id"],
|
||||||
@ -338,9 +447,9 @@ def page_payload(app_module, page, user: Optional[dict]) -> dict:
|
|||||||
"channel_id": page["channel_id"],
|
"channel_id": page["channel_id"],
|
||||||
"channel_slug": page["channel_slug"],
|
"channel_slug": page["channel_slug"],
|
||||||
"channel_name": page["channel_name"],
|
"channel_name": page["channel_name"],
|
||||||
"parent_page_id": page["parent_page_id"] if "parent_page_id" in page.keys() else None,
|
"parent_page_id": parent_page_id,
|
||||||
"parent_page_slug": page["parent_page_slug"] if "parent_page_slug" in page.keys() else None,
|
"parent_page_slug": parent_page_slug,
|
||||||
"parent_page_title": page["parent_page_title"] if "parent_page_title" in page.keys() else None,
|
"parent_page_title": parent_page_title,
|
||||||
"slug": page["slug"],
|
"slug": page["slug"],
|
||||||
"title": page["title"],
|
"title": page["title"],
|
||||||
"body": page["body"],
|
"body": page["body"],
|
||||||
@ -740,6 +849,30 @@ def richer_message_payload(
|
|||||||
|
|
||||||
seen_by_user = False
|
seen_by_user = False
|
||||||
is_bookmarked = False
|
is_bookmarked = False
|
||||||
|
is_pinned = False
|
||||||
|
pin_row = None
|
||||||
|
|
||||||
|
bookmark_table_exists = bool(
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
SELECT 1
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type = 'table'
|
||||||
|
AND name = 'message_bookmarks'
|
||||||
|
"""
|
||||||
|
).fetchone()
|
||||||
|
)
|
||||||
|
pin_table_exists = bool(
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
SELECT 1
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type = 'table'
|
||||||
|
AND name = 'message_pins'
|
||||||
|
"""
|
||||||
|
).fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
if current_user_id:
|
if current_user_id:
|
||||||
seen_by_user = bool(
|
seen_by_user = bool(
|
||||||
db.execute(
|
db.execute(
|
||||||
@ -752,26 +885,29 @@ def richer_message_payload(
|
|||||||
(row["id"], current_user_id),
|
(row["id"], current_user_id),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
)
|
)
|
||||||
is_bookmarked = bool(
|
if bookmark_table_exists:
|
||||||
db.execute(
|
is_bookmarked = bool(
|
||||||
"""
|
db.execute(
|
||||||
SELECT 1
|
"""
|
||||||
FROM message_bookmarks
|
SELECT 1
|
||||||
WHERE message_id = ?
|
FROM message_bookmarks
|
||||||
AND user_id = ?
|
WHERE message_id = ?
|
||||||
""",
|
AND user_id = ?
|
||||||
(row["id"], current_user_id),
|
""",
|
||||||
).fetchone()
|
(row["id"], current_user_id),
|
||||||
)
|
).fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
pin_row = db.execute(
|
if pin_table_exists:
|
||||||
"""
|
pin_row = db.execute(
|
||||||
SELECT pinned_at, pinned_by_user_id
|
"""
|
||||||
FROM message_pins
|
SELECT pinned_at, pinned_by_user_id
|
||||||
WHERE message_id = ?
|
FROM message_pins
|
||||||
""",
|
WHERE message_id = ?
|
||||||
(row["id"],),
|
""",
|
||||||
).fetchone()
|
(row["id"],),
|
||||||
|
).fetchone()
|
||||||
|
is_pinned = bool(pin_row)
|
||||||
|
|
||||||
primary_attachment = attachment_payload_legacy_from_message_select_row(row)
|
primary_attachment = attachment_payload_legacy_from_message_select_row(row)
|
||||||
attachments = attachment_list_for_message(get_db, row["id"])
|
attachments = attachment_list_for_message(get_db, row["id"])
|
||||||
@ -803,7 +939,7 @@ def richer_message_payload(
|
|||||||
"seen_by_current_user": seen_by_user,
|
"seen_by_current_user": seen_by_user,
|
||||||
"reactions": reaction_summary_for_message(get_db, row["id"], user),
|
"reactions": reaction_summary_for_message(get_db, row["id"], user),
|
||||||
"is_dm": bool(("direct_conversation_id" in row.keys()) and row["direct_conversation_id"]),
|
"is_dm": bool(("direct_conversation_id" in row.keys()) and row["direct_conversation_id"]),
|
||||||
"is_pinned": bool(pin_row),
|
"is_pinned": is_pinned,
|
||||||
"pinned_at": pin_row["pinned_at"] if pin_row else None,
|
"pinned_at": pin_row["pinned_at"] if pin_row else None,
|
||||||
"pinned_by_user_id": pin_row["pinned_by_user_id"] if pin_row else None,
|
"pinned_by_user_id": pin_row["pinned_by_user_id"] if pin_row else None,
|
||||||
"is_bookmarked": is_bookmarked,
|
"is_bookmarked": is_bookmarked,
|
||||||
@ -856,15 +992,23 @@ def profile_payload(
|
|||||||
|
|
||||||
profile_visibility = settings_row["profile_visibility"] if settings_row else "public"
|
profile_visibility = settings_row["profile_visibility"] if settings_row else "public"
|
||||||
if profile_visibility not in PROFILE_VISIBILITIES:
|
if profile_visibility not in PROFILE_VISIBILITIES:
|
||||||
profile_visibility = "public"
|
profile_visibility = "public"
|
||||||
|
|
||||||
last_active_visibility = settings_row["last_active_visibility"] if settings_row else "everyone"
|
last_active_visibility = settings_row["last_active_visibility"] if settings_row else "everyone"
|
||||||
if last_active_visibility not in LAST_ACTIVE_VISIBILITIES:
|
if last_active_visibility not in LAST_ACTIVE_VISIBILITIES:
|
||||||
last_active_visibility = "everyone"
|
last_active_visibility = "everyone"
|
||||||
|
|
||||||
presence_mode = settings_row["presence_mode"] if settings_row else "online"
|
presence_mode = settings_row["presence_mode"] if settings_row else "online"
|
||||||
if presence_mode not in PRESENCE_MODES:
|
if presence_mode not in PRESENCE_MODES:
|
||||||
presence_mode = "online"
|
presence_mode = "online"
|
||||||
|
|
||||||
|
dm_layout_mode = settings_row["dm_layout_mode"] if settings_row else "cards"
|
||||||
|
if dm_layout_mode not in DM_LAYOUT_MODES:
|
||||||
|
dm_layout_mode = "cards"
|
||||||
|
|
||||||
|
show_presence_badge = bool(
|
||||||
|
settings_row["show_presence_badge"] if settings_row and "show_presence_badge" in settings_row.keys() else 1
|
||||||
|
)
|
||||||
|
|
||||||
last_active_at = user_row["last_login_at"] if "last_login_at" in user_row.keys() else None
|
last_active_at = user_row["last_login_at"] if "last_login_at" in user_row.keys() else None
|
||||||
|
|
||||||
@ -876,6 +1020,9 @@ def profile_payload(
|
|||||||
elif last_active_visibility == "nobody":
|
elif last_active_visibility == "nobody":
|
||||||
can_show_last_active = bool(viewer and viewer["id"] == user_row["id"])
|
can_show_last_active = bool(viewer and viewer["id"] == user_row["id"])
|
||||||
|
|
||||||
|
website = profile_row["website"] or ""
|
||||||
|
website_meta = detect_website_brand(website) if website else {"host": "", "brand": "web", "label": "website"}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": user_row["id"],
|
"id": user_row["id"],
|
||||||
"username": user_row["username"],
|
"username": user_row["username"],
|
||||||
@ -889,19 +1036,22 @@ def profile_payload(
|
|||||||
"bio": bio,
|
"bio": bio,
|
||||||
"bio_mode": bio_mode,
|
"bio_mode": bio_mode,
|
||||||
"bio_html": safe_profile_bio_html(html_module, bio, bio_mode),
|
"bio_html": safe_profile_bio_html(html_module, bio, bio_mode),
|
||||||
"website": profile_row["website"] or "",
|
"website": website,
|
||||||
|
"website_meta": website_meta,
|
||||||
"location": profile_row["location"] or "",
|
"location": profile_row["location"] or "",
|
||||||
"status_message": profile_row["status_message"] or "",
|
"status_message": profile_row["status_message"] if "status_message" in profile_row.keys() else "",
|
||||||
"banner_attachment_id": profile_row["banner_attachment_id"],
|
"banner_attachment_id": profile_row["banner_attachment_id"] if "banner_attachment_id" in profile_row.keys() else None,
|
||||||
"banner_image_url": f"/api/attachments/{profile_row['banner_attachment_id']}/download" if profile_row["banner_attachment_id"] else "",
|
"banner_image_url": f"/api/attachments/{profile_row['banner_attachment_id']}/download" if ("banner_attachment_id" in profile_row.keys() and profile_row["banner_attachment_id"]) else "",
|
||||||
"updated_at": profile_row["updated_at"],
|
"updated_at": profile_row["updated_at"],
|
||||||
"is_self": is_self,
|
"is_self": is_self,
|
||||||
"name_color": explicit_name_color,
|
"name_color": explicit_name_color,
|
||||||
"resolved_name_color": resolved,
|
"resolved_name_color": resolved,
|
||||||
"profile_background_color": settings_row["profile_background_color"] if settings_row else None,
|
"profile_background_color": settings_row["profile_background_color"] if settings_row and "profile_background_color" in settings_row.keys() else None,
|
||||||
"profile_banner_color": settings_row["profile_banner_color"] if settings_row else None,
|
"profile_banner_color": settings_row["profile_banner_color"] if settings_row and "profile_banner_color" in settings_row.keys() else None,
|
||||||
"profile_visibility": profile_visibility,
|
"profile_visibility": profile_visibility,
|
||||||
"last_active_visibility": last_active_visibility,
|
"last_active_visibility": last_active_visibility,
|
||||||
|
"show_presence_badge": show_presence_badge,
|
||||||
|
"dm_layout_mode": dm_layout_mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -977,6 +1127,7 @@ def list_dms_for_user(get_db, user_id: int) -> list[dict]:
|
|||||||
"last_message_text": row["last_message_text"],
|
"last_message_text": row["last_message_text"],
|
||||||
"last_message_created_at": row["last_message_created_at"],
|
"last_message_created_at": row["last_message_created_at"],
|
||||||
"last_message_id": row["last_message_id"],
|
"last_message_id": row["last_message_id"],
|
||||||
|
"unread_count": 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return items
|
return items
|
||||||
@ -1338,8 +1489,21 @@ def call_payload(get_db, call_id: int) -> Optional[dict]:
|
|||||||
|
|
||||||
|
|
||||||
def dispatch_scheduled_messages(get_db, now_iso_func, app_logger) -> None:
|
def dispatch_scheduled_messages(get_db, now_iso_func, app_logger) -> None:
|
||||||
now_value = now_iso_func()
|
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
|
table_exists = bool(
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
SELECT 1
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type = 'table'
|
||||||
|
AND name = 'scheduled_messages'
|
||||||
|
"""
|
||||||
|
).fetchone()
|
||||||
|
)
|
||||||
|
if not table_exists:
|
||||||
|
return
|
||||||
|
|
||||||
|
now_value = now_iso_func()
|
||||||
rows = db.execute(
|
rows = db.execute(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user