mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-06 09:28:34 +00:00
feat(mattermost): separate group policy for threads vs channels
This commit is contained in:
parent
be5af019b9
commit
cd4c1d0f6e
@ -47,6 +47,7 @@ class MattermostConfig(Base):
|
|||||||
allow_from_match_mode: str = "id"
|
allow_from_match_mode: str = "id"
|
||||||
allow_from: list[str] = Field(default_factory=list)
|
allow_from: list[str] = Field(default_factory=list)
|
||||||
group_policy: str = "mention"
|
group_policy: str = "mention"
|
||||||
|
group_policy_in_thread: str = "open"
|
||||||
group_allow_from: list[str] = Field(default_factory=list)
|
group_allow_from: list[str] = Field(default_factory=list)
|
||||||
reply_in_thread: bool = True
|
reply_in_thread: bool = True
|
||||||
include_thread_context: bool = True
|
include_thread_context: bool = True
|
||||||
@ -244,8 +245,10 @@ class MattermostChannel(BaseChannel):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_dm and not self._should_respond_in_channel(message_text, channel_id):
|
if not is_dm:
|
||||||
return
|
in_thread = bool(root_id)
|
||||||
|
if not self._should_respond_in_channel(message_text, channel_id, in_thread=in_thread):
|
||||||
|
return
|
||||||
|
|
||||||
message_text = self._strip_bot_mention(message_text)
|
message_text = self._strip_bot_mention(message_text)
|
||||||
|
|
||||||
@ -360,12 +363,18 @@ class MattermostChannel(BaseChannel):
|
|||||||
return chat_id in self.config.group_allow_from
|
return chat_id in self.config.group_allow_from
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _should_respond_in_channel(self, text: str, chat_id: str) -> bool:
|
def _should_respond_in_channel(
|
||||||
if self.config.group_policy == "open":
|
self, text: str, chat_id: str, *, in_thread: bool = False,
|
||||||
|
) -> bool:
|
||||||
|
policy = (
|
||||||
|
self.config.group_policy_in_thread if in_thread
|
||||||
|
else self.config.group_policy
|
||||||
|
)
|
||||||
|
if policy == "open":
|
||||||
return True
|
return True
|
||||||
if self.config.group_policy == "mention":
|
if policy == "mention":
|
||||||
return self._is_mentioned(text)
|
return self._is_mentioned(text)
|
||||||
if self.config.group_policy == "allowlist":
|
if policy == "allowlist":
|
||||||
return chat_id in self.config.group_allow_from
|
return chat_id in self.config.group_allow_from
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@ -375,6 +375,53 @@ async def test_group_policy_allowlist():
|
|||||||
assert channel._should_respond_in_channel("msg", "c2") is False
|
assert channel._should_respond_in_channel("msg", "c2") is False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_group_policy_in_thread_default_open():
|
||||||
|
"""Thread uses open policy by default, so no mention needed in threads."""
|
||||||
|
channel, fake = _make_channel({"groupPolicy": "mention"})
|
||||||
|
channel._self_username = "nanobot"
|
||||||
|
# In a main channel (not thread), mention is required
|
||||||
|
assert channel._should_respond_in_channel("hello", "c1", in_thread=False) is False
|
||||||
|
assert channel._should_respond_in_channel("@nanobot hello", "c1", in_thread=False) is True
|
||||||
|
# In a thread, no mention needed (default open policy)
|
||||||
|
assert channel._should_respond_in_channel("hello", "c1", in_thread=True) is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_group_policy_in_thread_mention():
|
||||||
|
"""Thread can also use mention policy when configured."""
|
||||||
|
channel, fake = _make_channel({
|
||||||
|
"groupPolicy": "mention",
|
||||||
|
"groupPolicyInThread": "mention",
|
||||||
|
})
|
||||||
|
channel._self_username = "nanobot"
|
||||||
|
# In a thread with mention policy, mention is required
|
||||||
|
assert channel._should_respond_in_channel("hello", "c1", in_thread=True) is False
|
||||||
|
assert channel._should_respond_in_channel("@nanobot hello", "c1", in_thread=True) is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_group_policy_in_thread_open():
|
||||||
|
"""Thread uses open policy when explicitly configured."""
|
||||||
|
channel, fake = _make_channel({
|
||||||
|
"groupPolicy": "mention",
|
||||||
|
"groupPolicyInThread": "open",
|
||||||
|
})
|
||||||
|
assert channel._should_respond_in_channel("hello", "c1", in_thread=True) is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_group_policy_in_thread_allowlist():
|
||||||
|
"""Thread uses allowlist policy when configured."""
|
||||||
|
channel, fake = _make_channel({
|
||||||
|
"groupPolicy": "mention",
|
||||||
|
"groupPolicyInThread": "allowlist",
|
||||||
|
"groupAllowFrom": ["c1"],
|
||||||
|
})
|
||||||
|
assert channel._should_respond_in_channel("msg", "c1", in_thread=True) is True
|
||||||
|
assert channel._should_respond_in_channel("msg", "c2", in_thread=True) is False
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Match mode: id / username / email
|
# Match mode: id / username / email
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export default {
|
|||||||
{ key: "channels.mattermost.token" },
|
{ key: "channels.mattermost.token" },
|
||||||
{ key: "channels.mattermost.teamId" },
|
{ key: "channels.mattermost.teamId" },
|
||||||
{ key: "channels.mattermost.groupPolicy" },
|
{ key: "channels.mattermost.groupPolicy" },
|
||||||
|
{ key: "channels.mattermost.groupPolicyInThread" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -27,13 +27,21 @@
|
|||||||
"placeholder": "Optional team ID"
|
"placeholder": "Optional team ID"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "Group behavior",
|
"label": "Channel behavior",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "Mention only",
|
"mention": "Mention only",
|
||||||
"open": "All messages",
|
"open": "All messages",
|
||||||
"allowlist": "Allowlist"
|
"allowlist": "Allowlist"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "Thread behavior",
|
||||||
|
"choices": {
|
||||||
|
"mention": "Mention only",
|
||||||
|
"open": "All messages (no mention needed)",
|
||||||
|
"allowlist": "Allowlist"
|
||||||
|
}
|
||||||
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
"label": "Allowed users",
|
"label": "Allowed users",
|
||||||
"placeholder": "User IDs, comma separated"
|
"placeholder": "User IDs, comma separated"
|
||||||
|
|||||||
@ -27,13 +27,21 @@
|
|||||||
"placeholder": "ID de equipo opcional"
|
"placeholder": "ID de equipo opcional"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "Comportamiento en grupos",
|
"label": "Comportamiento en canales",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "Solo menciones",
|
"mention": "Solo menciones",
|
||||||
"open": "Todos los mensajes",
|
"open": "Todos los mensajes",
|
||||||
"allowlist": "Lista permitida"
|
"allowlist": "Lista permitida"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "Comportamiento en hilos",
|
||||||
|
"choices": {
|
||||||
|
"mention": "Solo menciones",
|
||||||
|
"open": "Todos los mensajes (sin mención)",
|
||||||
|
"allowlist": "Lista permitida"
|
||||||
|
}
|
||||||
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
"label": "Usuarios permitidos",
|
"label": "Usuarios permitidos",
|
||||||
"placeholder": "ID de usuario separados por comas"
|
"placeholder": "ID de usuario separados por comas"
|
||||||
|
|||||||
@ -27,11 +27,19 @@
|
|||||||
"placeholder": "ID d’équipe facultatif"
|
"placeholder": "ID d’équipe facultatif"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "Comportement en groupe",
|
"label": "Comportement en canal",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "Mentions uniquement",
|
"mention": "Mentions uniquement",
|
||||||
"open": "Tous les messages",
|
"open": "Tous les messages",
|
||||||
"allowlist": "Liste d’autorisation"
|
"allowlist": "Liste d'autorisation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "Comportement en fil",
|
||||||
|
"choices": {
|
||||||
|
"mention": "Mentions uniquement",
|
||||||
|
"open": "Tous les messages (sans mention)",
|
||||||
|
"allowlist": "Liste d'autorisation"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
|
|||||||
@ -27,13 +27,21 @@
|
|||||||
"placeholder": "ID tim opsional"
|
"placeholder": "ID tim opsional"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "Perilaku grup",
|
"label": "Perilaku kanal",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "Hanya sebutan",
|
"mention": "Hanya sebutan",
|
||||||
"open": "Semua pesan",
|
"open": "Semua pesan",
|
||||||
"allowlist": "Daftar izin"
|
"allowlist": "Daftar izin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "Perilaku thread",
|
||||||
|
"choices": {
|
||||||
|
"mention": "Hanya sebutan",
|
||||||
|
"open": "Semua pesan (tanpa sebutan)",
|
||||||
|
"allowlist": "Daftar izin"
|
||||||
|
}
|
||||||
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
"label": "Pengguna yang diizinkan",
|
"label": "Pengguna yang diizinkan",
|
||||||
"placeholder": "ID pengguna, dipisahkan koma"
|
"placeholder": "ID pengguna, dipisahkan koma"
|
||||||
|
|||||||
@ -27,13 +27,21 @@
|
|||||||
"placeholder": "任意のチーム ID"
|
"placeholder": "任意のチーム ID"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "グループでの動作",
|
"label": "チャンネルでの動作",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "メンションのみ",
|
"mention": "メンションのみ",
|
||||||
"open": "すべてのメッセージ",
|
"open": "すべてのメッセージ",
|
||||||
"allowlist": "許可リスト"
|
"allowlist": "許可リスト"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "スレッドでの動作",
|
||||||
|
"choices": {
|
||||||
|
"mention": "メンションのみ",
|
||||||
|
"open": "すべてのメッセージ (メンション不要)",
|
||||||
|
"allowlist": "許可リスト"
|
||||||
|
}
|
||||||
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
"label": "許可するユーザー",
|
"label": "許可するユーザー",
|
||||||
"placeholder": "ユーザー ID(カンマ区切り)"
|
"placeholder": "ユーザー ID(カンマ区切り)"
|
||||||
|
|||||||
@ -27,13 +27,21 @@
|
|||||||
"placeholder": "선택적 팀 ID"
|
"placeholder": "선택적 팀 ID"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "그룹 동작",
|
"label": "채널 동작",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "멘션만",
|
"mention": "멘션만",
|
||||||
"open": "모든 메시지",
|
"open": "모든 메시지",
|
||||||
"allowlist": "허용 목록"
|
"allowlist": "허용 목록"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "스레드 동작",
|
||||||
|
"choices": {
|
||||||
|
"mention": "멘션만",
|
||||||
|
"open": "모든 메시지 (언급 불필요)",
|
||||||
|
"allowlist": "허용 목록"
|
||||||
|
}
|
||||||
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
"label": "허용된 사용자",
|
"label": "허용된 사용자",
|
||||||
"placeholder": "사용자 ID, 쉼표로 구분"
|
"placeholder": "사용자 ID, 쉼표로 구분"
|
||||||
|
|||||||
@ -27,13 +27,21 @@
|
|||||||
"placeholder": "ID de equipe opcional"
|
"placeholder": "ID de equipe opcional"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "Comportamento em grupos",
|
"label": "Comportamento em canais",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "Somente menções",
|
"mention": "Somente menções",
|
||||||
"open": "Todas as mensagens",
|
"open": "Todas as mensagens",
|
||||||
"allowlist": "Lista de permissão"
|
"allowlist": "Lista de permissão"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "Comportamento em threads",
|
||||||
|
"choices": {
|
||||||
|
"mention": "Somente menções",
|
||||||
|
"open": "Todas as mensagens (sem menção)",
|
||||||
|
"allowlist": "Lista de permissão"
|
||||||
|
}
|
||||||
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
"label": "Usuários permitidos",
|
"label": "Usuários permitidos",
|
||||||
"placeholder": "IDs de usuário separados por vírgulas"
|
"placeholder": "IDs de usuário separados por vírgulas"
|
||||||
|
|||||||
@ -27,13 +27,21 @@
|
|||||||
"placeholder": "ID nhóm tùy chọn"
|
"placeholder": "ID nhóm tùy chọn"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "Hành vi trong nhóm",
|
"label": "Hành vi trong kênh",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "Chỉ khi được nhắc",
|
"mention": "Chỉ khi được nhắc",
|
||||||
"open": "Mọi tin nhắn",
|
"open": "Mọi tin nhắn",
|
||||||
"allowlist": "Danh sách cho phép"
|
"allowlist": "Danh sách cho phép"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "Hành vi trong thread",
|
||||||
|
"choices": {
|
||||||
|
"mention": "Chỉ khi được nhắc",
|
||||||
|
"open": "Mọi tin nhắn (không cần nhắc)",
|
||||||
|
"allowlist": "Danh sách cho phép"
|
||||||
|
}
|
||||||
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
"label": "Người dùng được phép",
|
"label": "Người dùng được phép",
|
||||||
"placeholder": "ID người dùng, phân tách bằng dấu phẩy"
|
"placeholder": "ID người dùng, phân tách bằng dấu phẩy"
|
||||||
|
|||||||
@ -27,13 +27,21 @@
|
|||||||
"placeholder": "可选的团队 ID"
|
"placeholder": "可选的团队 ID"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "群组行为",
|
"label": "频道行为",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "仅提及时",
|
"mention": "仅提及时",
|
||||||
"open": "所有消息",
|
"open": "所有消息",
|
||||||
"allowlist": "白名单"
|
"allowlist": "白名单"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "线程行为",
|
||||||
|
"choices": {
|
||||||
|
"mention": "仅提及时",
|
||||||
|
"open": "所有消息(无需提及)",
|
||||||
|
"allowlist": "白名单"
|
||||||
|
}
|
||||||
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
"label": "允许的用户",
|
"label": "允许的用户",
|
||||||
"placeholder": "用户 ID,用逗号分隔"
|
"placeholder": "用户 ID,用逗号分隔"
|
||||||
|
|||||||
@ -27,13 +27,21 @@
|
|||||||
"placeholder": "可選的團隊 ID"
|
"placeholder": "可選的團隊 ID"
|
||||||
},
|
},
|
||||||
"groupPolicy": {
|
"groupPolicy": {
|
||||||
"label": "群組行為",
|
"label": "頻道行為",
|
||||||
"choices": {
|
"choices": {
|
||||||
"mention": "僅提及時",
|
"mention": "僅提及時",
|
||||||
"open": "所有訊息",
|
"open": "所有訊息",
|
||||||
"allowlist": "允許清單"
|
"allowlist": "允許清單"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groupPolicyInThread": {
|
||||||
|
"label": "線程行為",
|
||||||
|
"choices": {
|
||||||
|
"mention": "僅提及時",
|
||||||
|
"open": "所有訊息(無需提及)",
|
||||||
|
"allowlist": "允許清單"
|
||||||
|
}
|
||||||
|
},
|
||||||
"allowFrom": {
|
"allowFrom": {
|
||||||
"label": "允許的使用者",
|
"label": "允許的使用者",
|
||||||
"placeholder": "使用者 ID,以逗號分隔"
|
"placeholder": "使用者 ID,以逗號分隔"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user