mirror of
https://github.com/PaiGramTeam/PaiGram.git
synced 2024-11-25 01:29:42 +00:00
✨ Add group manage service
This commit is contained in:
parent
d520a0980a
commit
ce7ab18eda
57
alembic/versions/27aaa52f9d4a_groups.py
Normal file
57
alembic/versions/27aaa52f9d4a_groups.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""groups
|
||||
|
||||
Revision ID: 27aaa52f9d4a
|
||||
Revises: c6282bc5bf67
|
||||
Create Date: 2024-01-16 13:54:37.980830
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "27aaa52f9d4a"
|
||||
down_revision = "c6282bc5bf67"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"groups",
|
||||
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("chat_id", sa.BigInteger(), nullable=True),
|
||||
sa.Column(
|
||||
"type",
|
||||
sa.Enum(
|
||||
"SENDER",
|
||||
"PRIVATE",
|
||||
"GROUP",
|
||||
"SUPERGROUP",
|
||||
"CHANNEL",
|
||||
name="chattypeenum",
|
||||
),
|
||||
nullable=True,
|
||||
),
|
||||
sa.Column("description", sa.TEXT(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("is_left", sa.Integer(), nullable=True),
|
||||
sa.Column("is_banned", sa.Integer(), nullable=True),
|
||||
sa.Column("title", sqlmodel.AutoString(), nullable=False),
|
||||
sa.Column("username", sqlmodel.AutoString(), nullable=True),
|
||||
sa.Column("big_photo_id", sqlmodel.AutoString(), nullable=True),
|
||||
sa.Column("small_photo_id", sqlmodel.AutoString(), nullable=True),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("chat_id"),
|
||||
mysql_charset="utf8mb4",
|
||||
mysql_collate="utf8mb4_general_ci",
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("groups")
|
||||
# ### end Alembic commands ###
|
4
core/handler/grouphandler.py
Normal file
4
core/handler/grouphandler.py
Normal file
@ -0,0 +1,4 @@
|
||||
from gram_core.handler.grouphandler import GroupHandler
|
||||
|
||||
|
||||
__all__ = ("GroupHandler",)
|
3
core/services/groups/__init__.py
Normal file
3
core/services/groups/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from .services import GroupService
|
||||
|
||||
__all__ = ("GroupService",)
|
6
core/services/groups/cache.py
Normal file
6
core/services/groups/cache.py
Normal file
@ -0,0 +1,6 @@
|
||||
from gram_core.services.groups.cache import GroupBanCache, GroupUpdateCache
|
||||
|
||||
__all__ = (
|
||||
"GroupBanCache",
|
||||
"GroupUpdateCache",
|
||||
)
|
7
core/services/groups/models.py
Normal file
7
core/services/groups/models.py
Normal file
@ -0,0 +1,7 @@
|
||||
from gram_core.services.groups.models import Group, GroupDataBase, ChatTypeEnum
|
||||
|
||||
__all__ = (
|
||||
"Group",
|
||||
"GroupDataBase",
|
||||
"ChatTypeEnum",
|
||||
)
|
3
core/services/groups/repositories.py
Normal file
3
core/services/groups/repositories.py
Normal file
@ -0,0 +1,3 @@
|
||||
from gram_core.services.groups.repositories import GroupRepository
|
||||
|
||||
__all__ = ("GroupRepository",)
|
3
core/services/groups/services.py
Normal file
3
core/services/groups/services.py
Normal file
@ -0,0 +1,3 @@
|
||||
from gram_core.services.groups.services import GroupService
|
||||
|
||||
__all__ = ("GroupService",)
|
@ -0,0 +1,8 @@
|
||||
from .services import UserService, UserAdminService, UserBanService
|
||||
|
||||
|
||||
__all__ = (
|
||||
"UserService",
|
||||
"UserAdminService",
|
||||
"UserBanService",
|
||||
)
|
@ -1,3 +1,6 @@
|
||||
from gram_core.services.users.cache import UserAdminCache
|
||||
from gram_core.services.users.cache import UserAdminCache, UserBanCache
|
||||
|
||||
__all__ = ("UserAdminCache",)
|
||||
__all__ = (
|
||||
"UserAdminCache",
|
||||
"UserBanCache",
|
||||
)
|
||||
|
@ -1,3 +1,7 @@
|
||||
from gram_core.services.users.services import UserService, UserAdminService
|
||||
from gram_core.services.users.services import UserService, UserAdminService, UserBanService
|
||||
|
||||
__all__ = ("UserService", "UserAdminService")
|
||||
__all__ = (
|
||||
"UserService",
|
||||
"UserAdminService",
|
||||
"UserBanService",
|
||||
)
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 6d1e8bcc497ed255e581b5e47de88e01cdef98f7
|
||||
Subproject commit af709ec5da23040949cbcbdb8e6833039c904894
|
@ -1,29 +1,40 @@
|
||||
import contextlib
|
||||
import html
|
||||
from typing import Tuple
|
||||
from typing import Tuple, Optional, TYPE_CHECKING
|
||||
|
||||
from telegram import Chat, ChatMember, ChatMemberAdministrator, ChatMemberOwner, Update
|
||||
from telegram import ChatMemberAdministrator, ChatMemberOwner, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from telegram.error import BadRequest, Forbidden
|
||||
from telegram.ext import CallbackContext, CommandHandler
|
||||
|
||||
from core.basemodel import RegionEnum
|
||||
from core.plugin import Plugin, handler
|
||||
from core.services.cookies import CookiesService
|
||||
from core.services.players import PlayersService
|
||||
from core.services.groups.services import GroupService
|
||||
from core.services.users.services import UserBanService
|
||||
from utils.log import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Chat, ChatMember, Update
|
||||
from telegram.ext import ContextTypes
|
||||
|
||||
|
||||
class GetChat(Plugin):
|
||||
def __init__(
|
||||
self,
|
||||
players_service: PlayersService,
|
||||
cookies_service: CookiesService,
|
||||
group_service: GroupService,
|
||||
user_ban_service: UserBanService,
|
||||
):
|
||||
self.cookies_service = cookies_service
|
||||
self.players_service = players_service
|
||||
self.group_service = group_service
|
||||
self.user_ban_service = user_ban_service
|
||||
|
||||
@staticmethod
|
||||
async def parse_group_chat(chat: Chat, admins: Tuple[ChatMember]) -> str:
|
||||
async def parse_group_chat(chat: "Chat", admins: Tuple["ChatMember", ...], is_banned: bool) -> str:
|
||||
text = f"群 ID:<code>{chat.id}</code>\n群名称:<code>{chat.title}</code>\n"
|
||||
text += f"黑名单:<code>{'是' if is_banned else '否'}</code>\n"
|
||||
if chat.username:
|
||||
text += f"群用户名:@{chat.username}\n"
|
||||
if chat.description:
|
||||
@ -46,12 +57,13 @@ class GetChat(Plugin):
|
||||
text += "\n"
|
||||
return text
|
||||
|
||||
async def parse_private_chat(self, chat: Chat) -> str:
|
||||
async def parse_private_chat(self, chat: "Chat", is_banned: bool) -> str:
|
||||
text = (
|
||||
f'<a href="tg://user?id={chat.id}">MENTION</a>\n'
|
||||
f"用户 ID:<code>{chat.id}</code>\n"
|
||||
f"用户名称:<code>{chat.full_name}</code>\n"
|
||||
)
|
||||
text += f"黑名单:<code>{'是' if is_banned else '否'}</code>\n"
|
||||
if chat.username:
|
||||
text += f"用户名:@{chat.username}\n"
|
||||
player_info = await self.players_service.get_player(chat.id)
|
||||
@ -68,27 +80,70 @@ class GetChat(Plugin):
|
||||
text += f"<code>{temp}</code>\n游戏 ID:<code>{player_info.player_id}</code>"
|
||||
return text
|
||||
|
||||
@handler(CommandHandler, command="get_chat", block=False, admin=True)
|
||||
async def get_chat_command(self, update: Update, context: CallbackContext):
|
||||
user = update.effective_user
|
||||
logger.info("用户 %s[%s] get_chat 命令请求", user.full_name, user.id)
|
||||
message = update.effective_message
|
||||
def get_chat_id(self, context: "ContextTypes.DEFAULT_TYPE") -> Optional[int]:
|
||||
args = self.get_args(context)
|
||||
if not args:
|
||||
if args and len(args) > 1 and args[0].isnumeric():
|
||||
return int(args[0])
|
||||
|
||||
@staticmethod
|
||||
def gen_button(chat_id: int) -> "InlineKeyboardMarkup":
|
||||
return InlineKeyboardMarkup(
|
||||
[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
"拉黑",
|
||||
callback_data=f"block|add|{chat_id}",
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
"取消拉黑",
|
||||
callback_data=f"block|del|{chat_id}",
|
||||
),
|
||||
],
|
||||
]
|
||||
)
|
||||
|
||||
@handler.command(command="get_chat", block=False, admin=True)
|
||||
async def get_chat_command(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
|
||||
user = update.effective_user
|
||||
message = update.effective_message
|
||||
chat_id = self.get_chat_id(context)
|
||||
logger.info("用户 %s[%s] get_chat 命令请求 chat_id[%s]", user.full_name, user.id, chat_id)
|
||||
if not chat_id:
|
||||
await message.reply_text("参数错误,请指定群 id !")
|
||||
return
|
||||
try:
|
||||
chat_id = int(args[0])
|
||||
except ValueError:
|
||||
await message.reply_text("参数错误,请指定群 id !")
|
||||
return
|
||||
try:
|
||||
chat = await self.get_chat(args[0])
|
||||
if chat_id < 0:
|
||||
admins = await chat.get_administrators() if chat_id < 0 else None
|
||||
text = await self.parse_group_chat(chat, admins)
|
||||
is_banned = await self.group_service.is_banned(chat_id)
|
||||
else:
|
||||
text = await self.parse_private_chat(chat)
|
||||
await message.reply_text(text, parse_mode="HTML")
|
||||
is_banned = await self.user_ban_service.is_banned(chat_id)
|
||||
try:
|
||||
chat = await self.get_chat(chat_id)
|
||||
if chat_id < 0:
|
||||
admins = await chat.get_administrators()
|
||||
text = await self.parse_group_chat(chat, admins, is_banned)
|
||||
else:
|
||||
text = await self.parse_private_chat(chat, is_banned)
|
||||
await message.reply_text(text, parse_mode="HTML", reply_markup=self.gen_button(chat_id))
|
||||
except (BadRequest, Forbidden) as exc:
|
||||
await message.reply_text(f"通过 id 获取会话信息失败,API 返回:{exc.message}")
|
||||
logger.warning("通过 id 获取会话信息失败,API 返回:%s", str(exc))
|
||||
text = f"会话 ID:<code>{chat_id}</code>\n"
|
||||
text += f"黑名单:<code>{'是' if is_banned else '否'}</code>\n"
|
||||
await message.reply_text(text, parse_mode="HTML", reply_markup=self.gen_button(chat_id))
|
||||
|
||||
@handler.command(command="leave_chat", block=False, admin=True)
|
||||
async def leave_chat(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
|
||||
user = update.effective_user
|
||||
message = update.effective_message
|
||||
chat_id = self.get_chat_id(context)
|
||||
logger.info("用户 %s[%s] leave_chat 命令请求 chat_id[%s]", user.full_name, user.id, chat_id)
|
||||
if not chat_id:
|
||||
await message.reply_text("参数错误,请指定群 id !")
|
||||
return
|
||||
try:
|
||||
with contextlib.suppress(BadRequest, Forbidden):
|
||||
chat = await context.bot.get_chat(chat_id)
|
||||
await message.reply_text(f"正在尝试退出群 {chat.title}[{chat.id}]")
|
||||
await context.bot.leave_chat(chat_id)
|
||||
except (BadRequest, Forbidden) as exc:
|
||||
await message.reply_text(f"退出 chat_id[{chat_id}] 发生错误! 错误信息为 {str(exc)}")
|
||||
return
|
||||
await message.reply_text(f"退出 chat_id[{chat_id}] 成功!")
|
||||
|
159
plugins/admin/group_manage.py
Normal file
159
plugins/admin/group_manage.py
Normal file
@ -0,0 +1,159 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram.constants import ChatType
|
||||
from telegram.error import BadRequest, Forbidden
|
||||
from telegram.ext import ChatMemberHandler
|
||||
|
||||
from core.handler.grouphandler import GroupHandler
|
||||
from core.plugin import Plugin, handler
|
||||
from core.services.groups import GroupService
|
||||
from core.services.groups.models import GroupDataBase as Group
|
||||
from core.services.users import UserBanService
|
||||
from utils.chatmember import extract_status_change
|
||||
from utils.log import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Update
|
||||
from telegram.ext import ContextTypes
|
||||
|
||||
|
||||
class GroupManage(Plugin):
|
||||
def __init__(
|
||||
self,
|
||||
group_service: GroupService,
|
||||
user_ban_service: UserBanService,
|
||||
):
|
||||
self.type_handler = None
|
||||
self.group_service = group_service
|
||||
self.user_ban_service = user_ban_service
|
||||
|
||||
async def initialize(self) -> None:
|
||||
self.type_handler = GroupHandler(self.application)
|
||||
self.application.telegram.add_handler(self.type_handler, group=-2)
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
self.application.telegram.remove_handler(self.type_handler, group=-2)
|
||||
|
||||
@handler.chat_member(chat_member_types=ChatMemberHandler.MY_CHAT_MEMBER, block=False)
|
||||
async def check_group(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
|
||||
result = extract_status_change(update.my_chat_member)
|
||||
if result is None:
|
||||
return
|
||||
was_member, is_member = result
|
||||
chat = update.effective_chat
|
||||
if chat.type not in [ChatType.GROUP, ChatType.SUPERGROUP, ChatType.CHANNEL]:
|
||||
return
|
||||
if not was_member and is_member:
|
||||
if await self.group_service.is_banned(chat.id):
|
||||
logger.info("会话 %s[%s] 在黑名单中,尝试退出", chat.title, chat.id)
|
||||
await GroupHandler.leave_chat(context.bot, chat.id)
|
||||
return
|
||||
if await self.group_service.is_need_update(chat.id):
|
||||
await GroupHandler.update_group(context.bot, self.group_service, chat)
|
||||
else:
|
||||
await self.group_service.join(chat.id)
|
||||
if was_member and not is_member:
|
||||
await self.group_service.leave(chat.id)
|
||||
|
||||
def get_chat_id(self, context: "ContextTypes.DEFAULT_TYPE") -> Optional[int]:
|
||||
args = self.get_args(context)
|
||||
if args and len(args) > 1 and args[0].isnumeric():
|
||||
return int(args[0])
|
||||
|
||||
async def add_block_group(self, chat_id: int):
|
||||
group = await self.group_service.get_group_by_id(chat_id)
|
||||
if group:
|
||||
group.is_banned = True
|
||||
await self.group_service.update_group(group)
|
||||
else:
|
||||
chat = None
|
||||
try:
|
||||
chat = await self.get_chat(chat_id)
|
||||
except (BadRequest, Forbidden) as exc:
|
||||
logger.warning("通过 id 获取会话信息失败,API 返回:%s", str(exc))
|
||||
if chat:
|
||||
group = Group.from_chat(chat)
|
||||
else:
|
||||
group = Group.from_id(chat_id)
|
||||
group.is_banned = True
|
||||
await self.group_service.update_group(group)
|
||||
|
||||
@handler.command(command="add_block", block=False, admin=True)
|
||||
@handler.callback_query(pattern=r"^block\|add\|", block=False, admin=True)
|
||||
async def add_block(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
|
||||
callback_query = update.callback_query
|
||||
user = update.effective_user
|
||||
message = update.effective_message
|
||||
chat_id = self.get_chat_id(context) if not callback_query else int(callback_query.data.split("|")[2])
|
||||
logger.info(
|
||||
"用户 %s[%s] add_block 命令请求 chat_id[%s] callback[%s]", user.full_name, user.id, chat_id, bool(callback_query)
|
||||
)
|
||||
if not chat_id:
|
||||
await message.reply_text("参数错误,请指定群 id !")
|
||||
return
|
||||
|
||||
async def reply(text: str):
|
||||
if callback_query:
|
||||
await callback_query.answer(text, show_alert=True)
|
||||
else:
|
||||
await message.reply_text(text)
|
||||
|
||||
if chat_id < 0:
|
||||
if await self.group_service.is_banned(chat_id):
|
||||
await reply("该群已在黑名单中!")
|
||||
return
|
||||
await self.add_block_group(chat_id)
|
||||
await reply("已将该群加入黑名单!")
|
||||
else:
|
||||
if await self.user_ban_service.is_banned(chat_id):
|
||||
await reply("该用户已在黑名单中!")
|
||||
return
|
||||
try:
|
||||
await self.user_ban_service.add_ban(chat_id)
|
||||
except PermissionError:
|
||||
await reply("无法操作管理员!")
|
||||
return
|
||||
await reply("已将该用户加入黑名单!")
|
||||
|
||||
@handler.command(command="del_block", block=False, admin=True)
|
||||
@handler.callback_query(pattern=r"^block\|del\|", block=False, admin=True)
|
||||
async def del_block(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
|
||||
callback_query = update.callback_query
|
||||
user = update.effective_user
|
||||
message = update.effective_message
|
||||
chat_id = self.get_chat_id(context) if not callback_query else int(callback_query.data.split("|")[2])
|
||||
logger.info(
|
||||
"用户 %s[%s] del_block 命令请求 chat_id[%s] callback[%s]", user.full_name, user.id, chat_id, bool(callback_query)
|
||||
)
|
||||
if not chat_id:
|
||||
await message.reply_text("参数错误,请指定群 id !")
|
||||
return
|
||||
|
||||
async def reply(text: str):
|
||||
if callback_query:
|
||||
await callback_query.answer(text, show_alert=True)
|
||||
else:
|
||||
await message.reply_text(text)
|
||||
|
||||
if chat_id < 0:
|
||||
if not await self.group_service.is_banned(chat_id):
|
||||
await reply("该群不在黑名单中!")
|
||||
return
|
||||
success = await self.group_service.del_ban(chat_id)
|
||||
if not success:
|
||||
await reply("该群不在黑名单中!")
|
||||
return
|
||||
await reply("已将该群移出黑名单!")
|
||||
else:
|
||||
if not await self.user_ban_service.is_banned(chat_id):
|
||||
await reply("该用户不在黑名单中!")
|
||||
return
|
||||
try:
|
||||
success = await self.user_ban_service.del_ban(chat_id)
|
||||
if not success:
|
||||
await reply("该用户不在黑名单中!")
|
||||
return
|
||||
except PermissionError:
|
||||
await reply("无法操作管理员!")
|
||||
return
|
||||
await reply("已将该用户移出黑名单!")
|
@ -85,6 +85,10 @@ class SetCommandPlugin(Plugin):
|
||||
BotCommand("update", "更新"),
|
||||
BotCommand("set_command", "重设命令"),
|
||||
BotCommand("status", "当前Bot运行状态"),
|
||||
BotCommand("leave_chat", "退出群组"),
|
||||
BotCommand("get_chat", "获取会话信息"),
|
||||
BotCommand("add_block", "添加黑名单"),
|
||||
BotCommand("del_block", "移除黑名单"),
|
||||
]
|
||||
await context.bot.set_my_commands(commands=group_command) # 留空,default 为 botCommandScopeDefault, 所有聊天可见
|
||||
await context.bot.set_my_commands(commands=user_command + group_command, scope=BotCommandScopeAllPrivateChats())
|
||||
|
@ -7,6 +7,7 @@ from core.plugin import Plugin, handler
|
||||
from core.services.cookies import CookiesService
|
||||
from core.services.players import PlayersService
|
||||
from core.services.users.services import UserAdminService
|
||||
from core.services.groups.services import GroupService
|
||||
from utils.chatmember import extract_status_change
|
||||
from utils.log import logger
|
||||
|
||||
@ -17,10 +18,12 @@ class ChatMember(Plugin):
|
||||
user_admin_service: UserAdminService = None,
|
||||
players_service: PlayersService = None,
|
||||
cookies_service: CookiesService = None,
|
||||
group_service: GroupService = None,
|
||||
):
|
||||
self.cookies_service = cookies_service
|
||||
self.players_service = players_service
|
||||
self.user_admin_service = user_admin_service
|
||||
self.group_service = group_service
|
||||
|
||||
@handler.chat_member(chat_member_types=ChatMemberHandler.MY_CHAT_MEMBER, block=False)
|
||||
async def track_chats(self, update: Update, context: CallbackContext) -> None:
|
||||
@ -48,6 +51,8 @@ class ChatMember(Plugin):
|
||||
logger.info("用户 %s[%s] 从 %s[%s] 频道移除Bot", user.full_name, user.id, chat.title, chat.id)
|
||||
|
||||
async def greet(self, user: User, chat: Chat, context: CallbackContext) -> None:
|
||||
if await self.group_service.is_banned(chat.id):
|
||||
return
|
||||
quit_status = True
|
||||
if config.join_groups == JoinGroups.NO_ALLOW:
|
||||
try:
|
||||
|
Loading…
Reference in New Issue
Block a user