1. 统一调整多个文件的导入排序,将TYPE_CHECKING相关导入放在正确位置 2. 修复rate_limiter中当限制数<=0时直接返回false的逻辑 3. 为message_cache新增更新消息内容的方法 4. 重构extract_graph_content支持日记类型内容提取 5. 调整@提及匹配的正则表达式,避免误匹配 6. 完善invite_manager,添加客户端和凭据支持并实现自动接受群邀请逻辑 7. 调整adapter.py中的导入顺序和初始化逻辑 8. 修复monitor中的编辑事件处理,改为异步处理并实现消息更新缓存 9. 调整datetime导入顺序,统一使用UTC在前的格式
121 lines
4.3 KiB
Python
121 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
if TYPE_CHECKING:
|
|
from .client import UrbitClient
|
|
|
|
|
|
class InviteManager:
|
|
def __init__(
|
|
self,
|
|
auto_accept_groups: bool = False,
|
|
group_invite_allowlist: list[str] | None = None,
|
|
auto_accept_dm: bool = False,
|
|
dm_allowlist: list[str] | None = None,
|
|
client: UrbitClient | None = None,
|
|
ship_name: str = "",
|
|
ship_code: str = "",
|
|
):
|
|
self.auto_accept_groups = auto_accept_groups
|
|
self.group_invite_allowlist = [s.lstrip("~").lower() for s in (group_invite_allowlist or [])]
|
|
self.auto_accept_dm = auto_accept_dm
|
|
self.dm_allowlist = [s.lstrip("~").lower() for s in (dm_allowlist or [])]
|
|
self._pending_invites: dict[str, dict[str, Any]] = {}
|
|
self._client = client
|
|
self._ship_name = ship_name
|
|
self._ship_code = ship_code
|
|
|
|
def should_auto_accept_group_invite(self, inviter_ship: str) -> bool:
|
|
if not self.auto_accept_groups:
|
|
return False
|
|
ship = inviter_ship.lstrip("~").lower()
|
|
if not self.group_invite_allowlist:
|
|
return False
|
|
return ship in self.group_invite_allowlist
|
|
|
|
def should_auto_accept_dm(self, inviter_ship: str) -> bool:
|
|
if not self.auto_accept_dm:
|
|
return False
|
|
ship = inviter_ship.lstrip("~").lower()
|
|
if not self.dm_allowlist:
|
|
return False
|
|
return ship in self.dm_allowlist
|
|
|
|
async def handle_foreign_update(self, foreign_update: dict[str, Any]) -> dict[str, Any] | None:
|
|
group = foreign_update.get("group", "")
|
|
ship = foreign_update.get("ship", "").lstrip("~")
|
|
join = foreign_update.get("join", False)
|
|
|
|
if not group or not ship:
|
|
return None
|
|
|
|
invite_id = f"{group}:{ship}"
|
|
|
|
if join:
|
|
self._pending_invites[invite_id] = foreign_update
|
|
|
|
if self.should_auto_accept_group_invite(ship):
|
|
logger.info(f"[Urbit] Auto-accepting group invite from ~{ship} for {group}")
|
|
await self._accept_invite(group, ship)
|
|
self._pending_invites.pop(invite_id, None)
|
|
return {"action": "auto_accepted", "group": group, "ship": ship}
|
|
|
|
logger.info(f"[Urbit] Group invite pending: {group} from ~{ship}")
|
|
return {"action": "pending", "group": group, "ship": ship, "invite_id": invite_id}
|
|
|
|
if not join and invite_id in self._pending_invites:
|
|
self._pending_invites.pop(invite_id, None)
|
|
logger.info(f"[Urbit] Ship ~{ship} left {group}, invite resolved")
|
|
|
|
return {"action": "left", "group": group, "ship": ship}
|
|
|
|
async def _accept_invite(self, group: str, inviter: str) -> None:
|
|
if not self._client:
|
|
logger.warning(f"[Urbit] Cannot accept group invite {group} from ~{inviter}: client not available")
|
|
return
|
|
|
|
logger.info(f"[Urbit] Accepting group invite: {group} from ~{inviter}")
|
|
|
|
try:
|
|
payload = {
|
|
"action": "poke",
|
|
"ship": self._ship_name,
|
|
"app": "groups",
|
|
"mark": "groups-action",
|
|
"json": {
|
|
"join": {
|
|
"group": group,
|
|
"ship": f"~{inviter.lstrip('~')}",
|
|
}
|
|
},
|
|
}
|
|
|
|
r = await self._client.put(
|
|
"/~/channel/groups-0",
|
|
json=payload,
|
|
timeout=10.0,
|
|
)
|
|
|
|
if r.status_code == 401 and self._ship_code:
|
|
from .auth import refresh_session_if_needed
|
|
|
|
await refresh_session_if_needed(self._client, self._ship_code)
|
|
payload["ship"] = self._client.ship_name
|
|
r = await self._client.put(
|
|
"/~/channel/groups-0",
|
|
json=payload,
|
|
timeout=10.0,
|
|
)
|
|
|
|
r.raise_for_status()
|
|
logger.info(f"[Urbit] Successfully accepted group invite: {group}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"[Urbit] Failed to accept group invite {group} from ~{inviter}: {e}")
|
|
|
|
def get_pending_invites(self) -> list[dict[str, Any]]:
|
|
return list(self._pending_invites.values())
|