mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-15 11:04:12 +08:00
fix(agent): grant channel owners admin access
This commit is contained in:
@@ -5,7 +5,11 @@ from urllib.parse import quote, unquote
|
||||
|
||||
from app.core.context import MediaInfo, Context
|
||||
from app.core.event import eventmanager
|
||||
from app.helper.agent import matches_channel_admin
|
||||
from app.helper.agent import (
|
||||
matches_channel_admin,
|
||||
register_channel_admin_resolver,
|
||||
resolve_config_principal_ids,
|
||||
)
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.schemas import (
|
||||
@@ -26,6 +30,12 @@ except Exception as err: # ImportError or other load issues
|
||||
logger.error(f"Discord 模块未加载,缺少依赖或初始化错误:{err}")
|
||||
|
||||
|
||||
register_channel_admin_resolver(
|
||||
MessageChannel.Discord,
|
||||
lambda config: resolve_config_principal_ids(config, "DISCORD_ADMINS"),
|
||||
)
|
||||
|
||||
|
||||
class DiscordModule(_ModuleBase, _MessageBase[Discord]):
|
||||
_IMAGE_SUFFIXES = (
|
||||
".png",
|
||||
@@ -209,7 +219,7 @@ class DiscordModule(_ModuleBase, _MessageBase[Discord]):
|
||||
userid=userid,
|
||||
username=username,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "DISCORD_ADMINS", userid
|
||||
MessageChannel.Discord, client_config.config, userid
|
||||
),
|
||||
text=f"CALLBACK:{callback_data}",
|
||||
is_callback=True,
|
||||
@@ -243,7 +253,7 @@ class DiscordModule(_ModuleBase, _MessageBase[Discord]):
|
||||
userid=userid,
|
||||
username=username,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "DISCORD_ADMINS", userid
|
||||
MessageChannel.Discord, client_config.config, userid
|
||||
),
|
||||
text=text,
|
||||
chat_id=str(chat_id) if chat_id else None,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
|
||||
from app.core.context import Context, MediaInfo
|
||||
from app.helper.agent import register_channel_admin_resolver, resolve_config_principal_ids
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.feishu.feishu import Feishu
|
||||
@@ -8,6 +9,14 @@ from app.schemas import CommingMessage, MessageChannel, MessageResponse, Notific
|
||||
from app.schemas.types import ModuleType
|
||||
|
||||
|
||||
register_channel_admin_resolver(
|
||||
MessageChannel.Feishu,
|
||||
lambda config: resolve_config_principal_ids(
|
||||
config, "FEISHU_ADMINS", "FEISHU_OPEN_ID"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class FeishuModule(_ModuleBase, _MessageBase[Feishu]):
|
||||
def init_module(self) -> None:
|
||||
super().init_service(service_name=Feishu.__name__.lower(), service_type=Feishu)
|
||||
|
||||
@@ -115,12 +115,14 @@ class Feishu:
|
||||
"""判断飞书命令或命令型按钮回调是否应因非管理员身份被拒绝。"""
|
||||
if not self._admins:
|
||||
return False
|
||||
candidates = [
|
||||
str(user_id).strip()
|
||||
for user_id in user_ids
|
||||
if user_id is not None and str(user_id).strip()
|
||||
]
|
||||
return not any(candidate in self._admins for candidate in candidates)
|
||||
return not matches_channel_admin(
|
||||
MessageChannel.Feishu,
|
||||
{
|
||||
"FEISHU_ADMINS": ",".join(self._admins),
|
||||
"FEISHU_OPEN_ID": self._default_open_id,
|
||||
},
|
||||
*user_ids,
|
||||
)
|
||||
|
||||
def _build_api_client(self) -> lark.Client:
|
||||
"""构建飞书 OpenAPI client,用于发送和编辑消息。"""
|
||||
@@ -689,8 +691,11 @@ class Feishu:
|
||||
userid=userid,
|
||||
username=username,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
{"FEISHU_ADMINS": ",".join(self._admins)},
|
||||
"FEISHU_ADMINS",
|
||||
MessageChannel.Feishu,
|
||||
{
|
||||
"FEISHU_ADMINS": ",".join(self._admins),
|
||||
"FEISHU_OPEN_ID": self._default_open_id,
|
||||
},
|
||||
open_id,
|
||||
user_id,
|
||||
),
|
||||
@@ -732,8 +737,11 @@ class Feishu:
|
||||
userid=userid,
|
||||
username=username,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
{"FEISHU_ADMINS": ",".join(self._admins)},
|
||||
"FEISHU_ADMINS",
|
||||
MessageChannel.Feishu,
|
||||
{
|
||||
"FEISHU_ADMINS": ",".join(self._admins),
|
||||
"FEISHU_OPEN_ID": self._default_open_id,
|
||||
},
|
||||
open_id,
|
||||
user_id,
|
||||
),
|
||||
|
||||
@@ -9,7 +9,11 @@ from urllib.parse import quote, unquote
|
||||
from typing import Optional, List, Tuple, Union, Any
|
||||
|
||||
from app.core.context import MediaInfo, Context
|
||||
from app.helper.agent import matches_channel_admin
|
||||
from app.helper.agent import (
|
||||
matches_channel_admin,
|
||||
register_channel_admin_resolver,
|
||||
resolve_config_principal_ids,
|
||||
)
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.qqbot.qqbot import QQBot
|
||||
@@ -18,6 +22,14 @@ from app.schemas.types import ModuleType
|
||||
from app.utils.http import RequestUtils
|
||||
|
||||
|
||||
register_channel_admin_resolver(
|
||||
MessageChannel.QQ,
|
||||
lambda config: resolve_config_principal_ids(
|
||||
config, "QQBOT_ADMINS", "QQ_OPENID"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
"""QQ Bot 通知模块"""
|
||||
|
||||
@@ -108,12 +120,11 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
admins = cls._get_admins(config)
|
||||
if not admins:
|
||||
return False
|
||||
candidates = [
|
||||
str(user_id).strip()
|
||||
for user_id in user_ids
|
||||
if user_id is not None and str(user_id).strip()
|
||||
]
|
||||
return not any(candidate in admins for candidate in candidates)
|
||||
return not matches_channel_admin(
|
||||
MessageChannel.QQ,
|
||||
config,
|
||||
*user_ids,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _send_admin_denied(
|
||||
@@ -176,7 +187,9 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
userid=user_openid,
|
||||
username=user_openid,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "QQBOT_ADMINS", user_openid
|
||||
MessageChannel.QQ,
|
||||
client_config.config,
|
||||
user_openid,
|
||||
),
|
||||
text=content,
|
||||
images=images,
|
||||
@@ -205,7 +218,9 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
userid=userid,
|
||||
username=member_openid or group_openid,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "QQBOT_ADMINS", member_openid
|
||||
MessageChannel.QQ,
|
||||
client_config.config,
|
||||
member_openid,
|
||||
),
|
||||
text=content,
|
||||
images=images,
|
||||
|
||||
@@ -6,7 +6,11 @@ from urllib.parse import quote, unquote
|
||||
|
||||
from app.core.context import MediaInfo, Context
|
||||
from app.core.event import eventmanager
|
||||
from app.helper.agent import matches_channel_admin
|
||||
from app.helper.agent import (
|
||||
matches_channel_admin,
|
||||
register_channel_admin_resolver,
|
||||
resolve_config_principal_ids,
|
||||
)
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.slack.slack import Slack
|
||||
@@ -21,6 +25,12 @@ from app.schemas.types import ChainEventType, ModuleType
|
||||
from app.utils.structures import DictUtils
|
||||
|
||||
|
||||
register_channel_admin_resolver(
|
||||
MessageChannel.Slack,
|
||||
lambda config: resolve_config_principal_ids(config, "SLACK_ADMINS"),
|
||||
)
|
||||
|
||||
|
||||
class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
PROCESSING_REACTION = "eyes"
|
||||
_AUDIO_SUFFIXES = (
|
||||
@@ -321,7 +331,7 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
userid=userid,
|
||||
username=username,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "SLACK_ADMINS", userid
|
||||
MessageChannel.Slack, client_config.config, userid
|
||||
),
|
||||
text=text,
|
||||
is_callback=True,
|
||||
@@ -378,7 +388,7 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
userid=userid,
|
||||
username=username,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "SLACK_ADMINS", userid
|
||||
MessageChannel.Slack, client_config.config, userid
|
||||
),
|
||||
text=text,
|
||||
message_id=message_id,
|
||||
|
||||
@@ -3,7 +3,11 @@ from typing import Optional, Union, List, Tuple, Any
|
||||
from urllib.parse import quote, unquote
|
||||
|
||||
from app.core.context import MediaInfo, Context
|
||||
from app.helper.agent import matches_channel_admin
|
||||
from app.helper.agent import (
|
||||
matches_channel_admin,
|
||||
register_channel_admin_resolver,
|
||||
resolve_config_principal_ids,
|
||||
)
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.synologychat.synologychat import SynologyChat
|
||||
@@ -12,6 +16,12 @@ from app.schemas.types import ModuleType
|
||||
from app.utils.http import RequestUtils
|
||||
|
||||
|
||||
register_channel_admin_resolver(
|
||||
MessageChannel.SynologyChat,
|
||||
lambda config: resolve_config_principal_ids(config, "SYNOLOGYCHAT_ADMINS"),
|
||||
)
|
||||
|
||||
|
||||
class SynologyChatModule(_ModuleBase, _MessageBase[SynologyChat]):
|
||||
_IMAGE_SUFFIXES = (
|
||||
".png",
|
||||
@@ -182,7 +192,9 @@ class SynologyChatModule(_ModuleBase, _MessageBase[SynologyChat]):
|
||||
return CommingMessage(channel=MessageChannel.SynologyChat, source=client_config.name,
|
||||
userid=user_id, username=user_name,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "SYNOLOGYCHAT_ADMINS", user_id
|
||||
MessageChannel.SynologyChat,
|
||||
client_config.config,
|
||||
user_id,
|
||||
), text=text or "",
|
||||
images=images, audio_refs=audio_refs, files=files)
|
||||
except Exception as err:
|
||||
|
||||
@@ -5,7 +5,11 @@ from typing import Dict, Optional, Union, List, Tuple, Any
|
||||
|
||||
from app.core.context import MediaInfo, Context
|
||||
from app.core.event import eventmanager
|
||||
from app.helper.agent import matches_channel_admin
|
||||
from app.helper.agent import (
|
||||
matches_channel_admin,
|
||||
register_channel_admin_resolver,
|
||||
resolve_config_principal_ids,
|
||||
)
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.telegram.telegram import Telegram
|
||||
@@ -21,6 +25,14 @@ from app.schemas.types import ModuleType, ChainEventType
|
||||
from app.utils.structures import DictUtils
|
||||
|
||||
|
||||
register_channel_admin_resolver(
|
||||
MessageChannel.Telegram,
|
||||
lambda config: resolve_config_principal_ids(
|
||||
config, "TELEGRAM_ADMINS", "TELEGRAM_CHAT_ID"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
||||
"""
|
||||
Telegram 通知模块,负责模块生命周期、消息解析和通知发送。
|
||||
@@ -112,12 +124,11 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
||||
admins = cls._get_admins(config)
|
||||
if not admins:
|
||||
return False
|
||||
candidates = [
|
||||
str(user_id).strip()
|
||||
for user_id in user_ids
|
||||
if user_id is not None and str(user_id).strip()
|
||||
]
|
||||
return not any(candidate in admins for candidate in candidates)
|
||||
return not matches_channel_admin(
|
||||
MessageChannel.Telegram,
|
||||
config,
|
||||
*user_ids,
|
||||
)
|
||||
|
||||
def message_parser(
|
||||
self, source: str, body: Any, form: Any, args: Any
|
||||
@@ -237,7 +248,9 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
||||
userid=user_id,
|
||||
username=user_name,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "TELEGRAM_ADMINS", user_id
|
||||
MessageChannel.Telegram,
|
||||
client_config.config,
|
||||
user_id,
|
||||
),
|
||||
text=callback_text,
|
||||
is_callback=True,
|
||||
@@ -316,7 +329,9 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
||||
userid=user_id,
|
||||
username=user_name,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "TELEGRAM_ADMINS", user_id
|
||||
MessageChannel.Telegram,
|
||||
client_config.config,
|
||||
user_id,
|
||||
),
|
||||
text=cleaned_text,
|
||||
message_id=message_id,
|
||||
|
||||
@@ -3,7 +3,11 @@ from urllib.parse import quote, unquote
|
||||
from typing import Optional, Union, List, Tuple, Any, Dict
|
||||
|
||||
from app.core.context import Context, MediaInfo
|
||||
from app.helper.agent import matches_channel_admin
|
||||
from app.helper.agent import (
|
||||
matches_channel_admin,
|
||||
register_channel_admin_resolver,
|
||||
resolve_config_principal_ids,
|
||||
)
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.vocechat.vocechat import VoceChat
|
||||
@@ -11,6 +15,12 @@ from app.schemas import MessageChannel, CommingMessage, Notification
|
||||
from app.schemas.types import ModuleType
|
||||
|
||||
|
||||
register_channel_admin_resolver(
|
||||
MessageChannel.VoceChat,
|
||||
lambda config: resolve_config_principal_ids(config, "VOCECHAT_ADMINS"),
|
||||
)
|
||||
|
||||
|
||||
class VoceChatModule(_ModuleBase, _MessageBase[VoceChat]):
|
||||
_IMAGE_SUFFIXES = (
|
||||
".png",
|
||||
@@ -208,7 +218,7 @@ class VoceChatModule(_ModuleBase, _MessageBase[VoceChat]):
|
||||
return CommingMessage(channel=MessageChannel.VoceChat, source=client_config.name,
|
||||
userid=userid, username=userid,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "VOCECHAT_ADMINS",
|
||||
MessageChannel.VoceChat, client_config.config,
|
||||
from_uid, actor_userid,
|
||||
), text=text or "",
|
||||
images=images, audio_refs=audio_refs, files=files)
|
||||
|
||||
@@ -7,7 +7,11 @@ from urllib.parse import quote
|
||||
|
||||
from app.core.context import Context, MediaInfo
|
||||
from app.core.event import eventmanager
|
||||
from app.helper.agent import matches_channel_admin
|
||||
from app.helper.agent import (
|
||||
matches_channel_admin,
|
||||
register_channel_admin_resolver,
|
||||
resolve_config_principal_ids,
|
||||
)
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt
|
||||
@@ -19,6 +23,17 @@ from app.utils.dom import DomUtils
|
||||
from app.utils.structures import DictUtils
|
||||
|
||||
|
||||
def _resolve_wechat_admin_ids(config: Optional[dict]) -> set[str]:
|
||||
"""解析企业微信管理员及机器人模式下的主用户 ID。"""
|
||||
config_keys = ["WECHAT_ADMINS"]
|
||||
if (config or {}).get("WECHAT_MODE", "app") == "bot":
|
||||
config_keys.append("WECHAT_BOT_CHAT_ID")
|
||||
return resolve_config_principal_ids(config, *config_keys)
|
||||
|
||||
|
||||
register_channel_admin_resolver(MessageChannel.Wechat, _resolve_wechat_admin_ids)
|
||||
|
||||
|
||||
class WechatModule(_ModuleBase, _MessageBase[WeChat]):
|
||||
|
||||
def init_module(self) -> None:
|
||||
@@ -88,7 +103,11 @@ class WechatModule(_ModuleBase, _MessageBase[WeChat]):
|
||||
admins = cls._get_admins(config)
|
||||
if not admins:
|
||||
return False
|
||||
return str(user_id or "").strip() not in admins
|
||||
return not matches_channel_admin(
|
||||
MessageChannel.Wechat,
|
||||
config,
|
||||
user_id,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _create_client(cls, conf):
|
||||
@@ -253,7 +272,9 @@ class WechatModule(_ModuleBase, _MessageBase[WeChat]):
|
||||
return CommingMessage(channel=MessageChannel.Wechat, source=client_config.name,
|
||||
userid=user_id, username=user_id,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "WECHAT_ADMINS", user_id
|
||||
MessageChannel.Wechat,
|
||||
client_config.config,
|
||||
user_id,
|
||||
), text=content or "",
|
||||
images=images, audio_refs=audio_refs, files=files)
|
||||
except Exception as err:
|
||||
@@ -325,7 +346,9 @@ class WechatModule(_ModuleBase, _MessageBase[WeChat]):
|
||||
userid=sender,
|
||||
username=sender,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "WECHAT_ADMINS", sender
|
||||
MessageChannel.Wechat,
|
||||
client_config.config,
|
||||
sender,
|
||||
),
|
||||
text=text or "",
|
||||
images=images,
|
||||
|
||||
@@ -15,8 +15,10 @@ from app.core.cache import FileCache
|
||||
from app.core.config import settings
|
||||
from app.core.context import MediaInfo, Context
|
||||
from app.core.metainfo import MetaInfo
|
||||
from app.helper.agent import matches_channel_admin
|
||||
from app.log import logger
|
||||
from app.schemas import CommingMessage
|
||||
from app.schemas.types import MessageChannel
|
||||
from app.utils.http import RequestUtils
|
||||
from app.utils.string import StringUtils
|
||||
|
||||
@@ -489,7 +491,16 @@ class WeChatBot:
|
||||
|
||||
self._remember_target(sender)
|
||||
|
||||
if text and text.startswith("/") and self._admins and sender not in self._admins:
|
||||
is_channel_admin = matches_channel_admin(
|
||||
MessageChannel.Wechat,
|
||||
{
|
||||
"WECHAT_ADMINS": ",".join(self._admins),
|
||||
"WECHAT_BOT_CHAT_ID": getattr(self, "_default_chat_id", None),
|
||||
"WECHAT_MODE": "bot",
|
||||
},
|
||||
sender,
|
||||
)
|
||||
if text and text.startswith("/") and self._admins and not is_channel_admin:
|
||||
self.send_msg(title="只有管理员才有权限执行此命令", userid=sender)
|
||||
return
|
||||
|
||||
|
||||
@@ -3,7 +3,11 @@ from typing import Any, List, Optional, Tuple, Union
|
||||
|
||||
from app.core.cache import TTLCache
|
||||
from app.core.context import Context, MediaInfo
|
||||
from app.helper.agent import matches_channel_admin
|
||||
from app.helper.agent import (
|
||||
matches_channel_admin,
|
||||
register_channel_admin_resolver,
|
||||
resolve_config_principal_ids,
|
||||
)
|
||||
from app.log import logger
|
||||
from app.modules import _MessageBase, _ModuleBase
|
||||
from app.modules.wechatclawbot.wechatclawbot import WechatClawBot
|
||||
@@ -11,6 +15,14 @@ from app.schemas import CommingMessage, Notification
|
||||
from app.schemas.types import MessageChannel, ModuleType
|
||||
|
||||
|
||||
register_channel_admin_resolver(
|
||||
MessageChannel.WechatClawBot,
|
||||
lambda config: resolve_config_principal_ids(
|
||||
config, "WECHATCLAWBOT_ADMINS", "WECHATCLAWBOT_DEFAULT_TARGET"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class WechatClawBotModule(_ModuleBase, _MessageBase[WechatClawBot]):
|
||||
def __init__(self):
|
||||
"""初始化模块级去重缓存,拦截 iLink 偶发的重复回放消息。"""
|
||||
@@ -182,7 +194,12 @@ class WechatClawBotModule(_ModuleBase, _MessageBase[WechatClawBot]):
|
||||
]
|
||||
callback_data = text[9:].strip() if text.startswith("CALLBACK:") else ""
|
||||
is_admin_command = text.startswith("/") or callback_data.startswith("/")
|
||||
if is_admin_command and admins and user_id not in admins:
|
||||
is_channel_admin = matches_channel_admin(
|
||||
MessageChannel.WechatClawBot,
|
||||
client_config.config,
|
||||
user_id,
|
||||
)
|
||||
if is_admin_command and admins and not is_channel_admin:
|
||||
client = self.get_instance(client_config.name)
|
||||
if client:
|
||||
client.send_msg(title="只有管理员才有权限执行此命令", userid=user_id)
|
||||
@@ -199,9 +216,7 @@ class WechatClawBotModule(_ModuleBase, _MessageBase[WechatClawBot]):
|
||||
source=client_config.name,
|
||||
userid=user_id,
|
||||
username=username,
|
||||
is_channel_admin=matches_channel_admin(
|
||||
client_config.config, "WECHATCLAWBOT_ADMINS", user_id
|
||||
),
|
||||
is_channel_admin=is_channel_admin,
|
||||
text=text,
|
||||
message_id=message_id,
|
||||
chat_id=str(message.get("chat_id") or "") or None,
|
||||
|
||||
Reference in New Issue
Block a user