Esempio n. 1
0
 def sendText(userGroup, msg, bot: Action, model=Model.SEND_DEFAULT, atQQ=""):
     if msg not in ("", Status.FAILURE):
         if model == Model.SEND_DEFAULT:
             bot.sendGroupText(userGroup, content=str(msg))
         if model == Model.SEND_AT:
             if atQQ == "":
                 raise Exception("没有指定 at 的人!")
             at = f"[ATUSER({atQQ})]\n"
             bot.sendGroupText(userGroup, content=at + str(msg))
Esempio n. 2
0
def receive_group_msg(ctx: GroupMsg):
    global action
    if action is None:
        action = Action(ctx.CurrentQQ,
                        host=getattr(ctx, "_host"),
                        port=getattr(ctx, "_port"))

    if ctx.Content == "疫情订阅":
        cache_data.insert_group(ctx.FromGroupId)
        action.sendGroupText(ctx.FromGroupId, "ok")
    elif ctx.Content == "疫情退订":
        cache_data.delete_group(ctx.FromGroupId)
        action.sendGroupText(ctx.FromGroupId, "ok")
Esempio n. 3
0
def receive_events(ctx: EventMsg):
    revoke_data = ep.group_revoke(ctx)
    if revoke_data is None:
        return

    admin = revoke_data.AdminUserID
    group_id = revoke_data.GroupID
    user_id = revoke_data.UserID
    msg_random = revoke_data.MsgRandom
    msg_seq = revoke_data.MsgSeq

    if any([
            user_id == ctx.CurrentQQ,  # 忽略机器人自己撤回的消息
            admin != user_id,  # 忽略管理员撤回的消息
    ]):
        return

    db_name = f"group{group_id}"
    db: DB = db_map[db_name]
    db.init(db_name)

    data = db.find(msg_random=msg_random,
                   msg_seq=msg_seq,
                   user_id=user_id,
                   group_id=group_id)
    if not data:
        return
    data = data[0]
    (
        _,
        msg_type,
        _,
        _,
        _,
        user_id,
        user_name,
        group_id,
        content,
    ) = data
    action = Action(
        ctx.CurrentQQ,
        host=ctx._host,
        port=ctx._port  # type:ignore
    )
    if msg_type == MsgTypes.TextMsg:
        action.sendGroupText(group_id, f"{user_name}想撤回以下内容: \n{content}")
    elif msg_type == MsgTypes.PicMsg:
        pic_data = json.loads(content)
        action.sendGroupText(group_id, f"{user_name}想撤回以下内容: ")
        action.sendGroupPic(
            group_id, picMd5s=[pic["FileMd5"] for pic in pic_data["GroupPic"]])
Esempio n. 4
0
def receive_events(ctx: EventMsg):
    join_ctx = refine_group_join_event_msg(ctx)
    if join_ctx is not None:
        action = Action(ctx.CurrentQQ)
        action.sendGroupText(
            join_ctx.FromUin,
            '{} 已加入组织~'.format(join_ctx.UserName),
        )
        pic = draw_text('欢迎 {} 入群!'.format(join_ctx.UserName))
        pic_base64 = base64.b64encode(pic).decode()
        action.sendGroupPic(
            join_ctx.FromUin,
            picBase64Buf=pic_base64,
        )
Esempio n. 5
0
def manage_plugin(ctx: GroupMsg):
    if ctx.FromUserId != ctx.master:
        return
    action = Action(ctx.CurrentQQ)
    c = ctx.Content
    if c == "插件管理":
        action.sendGroupText(
            ctx.FromGroupId,
            (
                "py插件 => 发送启用插件列表\n"
                "已停用py插件 => 发送停用插件列表\n"
                "刷新py插件 => 刷新所有插件,包括新建文件\n"
                "重载py插件+插件名 => 重载指定插件\n"
                "停用py插件+插件名 => 停用指定插件\n"
                "启用py插件+插件名 => 启用指定插件\n"
            ),
        )
        return
    # 发送启用插件列表
    if c == "py插件":
        action.sendGroupText(ctx.FromGroupId, "\n".join(bot.plugins))
        return
    # 发送停用插件列表
    if c == "已停用py插件":
        action.sendGroupText(ctx.FromGroupId, "\n".join(bot.removed_plugins))
        return
    try:
        if c == "刷新py插件":
            bot.reload_plugins()
        # 重载指定插件 重载py插件+[插件名]
        elif c.startswith("重载py插件"):
            plugin_name = c[6:]
            bot.reload_plugin(plugin_name)
        # 停用指定插件 停用py插件+[插件名]
        elif c.startswith("停用py插件"):
            plugin_name = c[6:]
            bot.remove_plugin(plugin_name)
        # 启用指定插件 启用py插件+[插件名]
        elif c.startswith("启用py插件"):
            plugin_name = c[6:]
            bot.recover_plugin(plugin_name)
    except Exception as e:
        action.sendGroupText(ctx.FromGroupId, "操作失败: %s" % e)
Esempio n. 6
0
class iot(BaseClient):
    client_name: str = "IOTbot Client"
    client_id: str = "iot"
    client_config: Dict[str, Any]
    bot: Botoy
    action: Action
    channel: SlaveChannel
    logger: logging.Logger = logging.getLogger(__name__)

    info_list = TTLCache(maxsize=2, ttl=600)

    info_dict = TTLCache(maxsize=2, ttl=600)

    group_member_list = TTLCache(maxsize=20, ttl=3600)
    stranger_cache = TTLCache(maxsize=100, ttl=3600)

    sio: socketio.Client = None
    event: threading.Event = None

    def __init__(self, client_id: str, config: Dict[str, Any], channel):
        super().__init__(client_id, config)
        self.client_config = config[self.client_id]
        self.uin = self.client_config['qq']
        self.host = self.client_config.get('host', 'http://127.0.0.1')
        self.port = self.client_config.get('port', 8888)
        IOTConfig.configs = self.client_config
        self.bot = Botoy(qq=self.uin, host=self.host, port=self.port)
        self.action = Action(qq=self.uin, host=self.host, port=self.port)
        IOTFactory.bot = self.bot
        IOTFactory.action = self.action
        self.channel = channel
        ChatMgr.slave_channel = channel
        self.iot_msg = IOTMsgProcessor(self.uin)

        @self.bot.when_connected
        def on_ws_connected():
            self.logger.info("Connected to OPQBot!")

        @self.bot.when_disconnected
        def on_ws_disconnected():
            self.logger.info("Disconnected from OPQBot!")

        @self.bot.on_friend_msg
        def on_friend_msg(ctx: FriendMsg):
            self.logger.debug(ctx)
            if int(ctx.FromUin) == int(self.uin) and not IOTConfig.configs.get(
                    'receive_self_msg', True):
                self.logger.info(
                    "Received self message and flag set. Cancel delivering...")
                return
            remark_name = self.get_friend_remark(ctx.FromUin)
            if not remark_name:
                info = self.get_stranger_info(ctx.FromUin)
                if info:
                    remark_name = info.get('nickname', '')
                else:
                    remark_name = str(ctx.FromUin)
            if ctx.MsgType == 'TempSessionMsg':  # Temporary chat
                chat_uid = f'private_{ctx.FromUin}_{ctx.TempUin}'
            elif ctx.MsgType == 'PhoneMsg':
                chat_uid = f'phone_{ctx.FromUin}'
            else:
                chat_uid = f'friend_{ctx.FromUin}'
            chat = ChatMgr.build_efb_chat_as_private(
                EFBPrivateChat(
                    uid=chat_uid,
                    name=remark_name,
                ))
            author = chat.other

            # Splitting messages
            messages: List[Message] = []
            func = getattr(self.iot_msg, f'iot_{ctx.MsgType}_friend')
            messages.extend(func(ctx, chat))

            # Sending messages one by one
            message_id = ctx.MsgSeq
            for idx, val in enumerate(messages):
                if not isinstance(val, Message):
                    continue
                val.uid = f"friend_{ctx.FromUin}_{message_id}_{idx}"
                val.chat = chat
                val.author = author
                val.deliver_to = coordinator.master
                coordinator.send_message(val)
                if val.file:
                    val.file.close()

        @self.bot.on_group_msg
        def on_group_msg(ctx: GroupMsg):
            # OPQbot has no indicator for anonymous user, so we have to test the uin
            nickname = ctx.FromNickName
            if int(ctx.FromUserId) == int(
                    self.uin) and not IOTConfig.configs.get(
                        'receive_self_msg', True):
                self.logger.info(
                    "Received self message and flag set. Cancel delivering...")
                return
            remark_name = self.get_friend_remark(ctx.FromUserId)
            if not remark_name:
                info = self.get_stranger_info(ctx.FromUserId)
                if info:
                    remark_name = info.get('nickname', '')
            chat = ChatMgr.build_efb_chat_as_group(
                EFBGroupChat(uid=f"group_{ctx.FromGroupId}",
                             name=ctx.FromGroupName))
            author = ChatMgr.build_efb_chat_as_member(
                chat,
                EFBGroupMember(name=nickname,
                               alias=remark_name,
                               uid=str(ctx.FromUserId)))
            # Splitting messages
            messages: List[Message] = []
            func = getattr(self.iot_msg, f'iot_{ctx.MsgType}_group')
            messages.extend(func(ctx, chat))

            # Sending messages one by one
            message_id = ctx.MsgSeq
            for idx, val in enumerate(messages):
                if not isinstance(val, Message):
                    continue
                val.uid = f"group_{ctx.FromGroupId}_{message_id}_{idx}"
                val.chat = chat
                val.author = author
                val.deliver_to = coordinator.master
                coordinator.send_message(val)
                if val.file:
                    val.file.close()

        @self.bot.on_event
        def on_event(ctx: EventMsg):
            pass  # fixme

    def login(self):
        pass

    def logout(self):
        self.action.logout()

    def relogin(self):
        pass

    def send_message(self, msg: 'Message') -> 'Message':
        chat_info = msg.chat.uid.split('_')
        chat_type = chat_info[0]
        chat_uid = chat_info[1]
        if msg.edit:
            pass  # todo Revoke message & resend

        if msg.type in [MsgType.Text, MsgType.Link]:
            if isinstance(msg.target, Message):  # Reply to message
                max_length = 50
                tgt_alias = iot_at_user(msg.target.author.uid)
                tgt_text = process_quote_text(msg.target.text, max_length)
                msg.text = "%s%s\n\n%s" % (tgt_alias, tgt_text, msg.text)
            self.iot_send_text_message(chat_type, chat_uid, msg.text)
            msg.uid = str(uuid.uuid4())
            self.logger.debug('[%s] Sent as a text message. %s', msg.uid,
                              msg.text)
        elif msg.type in (MsgType.Image, MsgType.Sticker, MsgType.Animation):
            self.logger.info("[%s] Image/Sticker/Animation %s", msg.uid,
                             msg.type)
            self.iot_send_image_message(chat_type, chat_uid, msg.file,
                                        msg.text)
            msg.uid = str(uuid.uuid4())
        elif msg.type is MsgType.Voice:
            self.logger.info(f"[{msg.uid}] Voice.")
            if not VOICE_SUPPORTED:
                self.iot_send_text_message(chat_type, chat_uid, "[语音消息]")
            else:
                pydub.AudioSegment.from_file(msg.file).export(
                    msg.file,
                    format='s16le',
                    parameters=["-ac", "1", "-ar", "24000"])
                output_file = tempfile.NamedTemporaryFile()
                if not Silkv3.encode(msg.file.name, output_file.name):
                    self.iot_send_text_message(chat_type, chat_uid, "[语音消息]")
                else:
                    self.iot_send_voice_message(chat_type, chat_uid,
                                                output_file)
                if msg.text:
                    self.iot_send_text_message(chat_type, chat_uid, msg.text)
            msg.uid = str(uuid.uuid4())
        return msg

    def send_status(self, status: 'Status'):
        raise NotImplementedError

    def receive_message(self):
        # replaced by on_*
        pass

    def get_friends(self) -> List['Chat']:
        if not self.info_list.get('friend', None):
            self.update_friend_list()
        friends = []
        self.info_dict['friend'] = {}
        for friend in self.info_list.get('friend', []):
            friend_uin = friend['FriendUin']
            friend_name = friend['NickName']
            friend_remark = friend['Remark']
            new_friend = EFBPrivateChat(uid=f"friend_{friend_uin}",
                                        name=friend_name,
                                        alias=friend_remark)
            self.info_dict['friend'][friend_uin] = friend
            friends.append(ChatMgr.build_efb_chat_as_private(new_friend))
        return friends

    def get_groups(self) -> List['Chat']:
        if not self.info_list.get('group', None):
            self.update_group_list()
        groups = []
        self.info_dict['group'] = {}
        for group in self.info_list.get('group', []):
            group_name = group['GroupName']
            group_id = group['GroupId']
            new_group = EFBGroupChat(uid=f"group_{group_id}", name=group_name)
            self.info_dict['group'][group_id] = IOTGroup(group)
            groups.append(ChatMgr.build_efb_chat_as_group(new_group))
        return groups

    def get_login_info(self) -> Dict[Any, Any]:
        pass

    def get_stranger_info(self, user_id) -> Union[Dict, None]:
        if not self.stranger_cache.get(user_id, None):
            response = self.action.getUserInfo(user=user_id)
            if response.get('code', 1) != 0:  # Failed to get info
                return None
            else:
                self.stranger_cache[user_id] = response.get('data', None)
        return self.stranger_cache.get(user_id)

    def get_group_info(self,
                       group_id: int,
                       no_cache=True) -> Union[None, IOTGroup]:
        if no_cache or not self.info_dict.get('group', None):
            self.update_group_list()
        return self.info_dict['group'].get(group_id, None)

    def get_chat_picture(self, chat: 'Chat') -> BinaryIO:
        chat_type = chat.uid.split('_')
        if chat_type[0] == 'private':
            private_uin = chat_type[1].split('_')[0]
            return download_user_avatar(private_uin)
        elif chat_type[0] == 'friend':
            return download_user_avatar(chat_type[1])
        elif chat_type[0] == 'group':
            return download_group_avatar(chat_type[1])

    def get_chat(self, chat_uid: ChatID) -> 'Chat':
        chat_info = chat_uid.split('_')
        chat_type = chat_info[0]
        chat_attr = chat_info[1]
        chat = None
        if chat_type == 'friend':
            chat_uin = int(chat_attr)
            remark_name = self.get_friend_remark(chat_uin)
            chat = ChatMgr.build_efb_chat_as_private(
                EFBPrivateChat(
                    uid=chat_attr,
                    name=remark_name if remark_name else "",
                ))
        elif chat_type == 'group':
            chat_uin = int(chat_attr)
            group_info = self.get_group_info(chat_uin, no_cache=False)
            group_members = self.get_group_member_list(chat_uin,
                                                       no_cache=False)
            chat = ChatMgr.build_efb_chat_as_group(
                EFBGroupChat(uid=f"group_{chat_uin}",
                             name=group_info.get('GroupName', "")),
                group_members)
        elif chat_type == 'private':
            pass  # fixme
        elif chat_type == 'phone':
            pass  # fixme
        return chat

    def get_chats(self) -> Collection['Chat']:
        return self.get_friends() + self.get_groups()

    def get_group_member_list(self, group_id, no_cache=True):
        if no_cache \
                or not self.group_member_list.get(group_id, None):  # Key expired or not exists
            # Update group member list
            group_members = self.action.getGroupMembers(group_id)
            efb_group_members: List[EFBGroupMember] = []
            for qq_member in group_members:
                qq_member = IOTGroupMember(qq_member)
                efb_group_members.append(
                    EFBGroupMember(name=qq_member['NickName'],
                                   alias=qq_member['GroupCard'],
                                   uid=qq_member['MemberUin']))
            self.group_member_list[group_id] = efb_group_members
        return self.group_member_list[group_id]

    def poll(self):
        # threading.Thread(target=self.bot.run, daemon=True).start()
        # self.bot.run()
        self.sio = self.bot.run_no_wait()
        self.event = threading.Event()
        self.event.wait()
        self.logger.info("IOTBot quited.")

    def stop_polling(self):
        if self.sio:
            self.sio.disconnect()
        if self.bot.pool:
            self.bot.pool.shutdown(wait=False)
        if self.event:
            self.event.set()

    def update_friend_list(self):
        """
        Update friend list from OPQBot

        """
        self.info_list['friend'] = self.action.getUserList()

    def update_group_list(self):
        self.info_list['group'] = self.action.getGroupList()

    def get_friend_remark(self, uin: int) -> Union[None, str]:
        count = 0
        while count <= 1:
            if not self.info_list.get('friend', None):
                self.update_friend_list()
                self.get_friends()
                count += 1
            else:
                break
        if count > 1:  # Failure or friend not found
            raise Exception("Failed to update friend list!"
                            )  # todo Optimize error handling
        if not self.info_dict.get('friend',
                                  None) or uin not in self.info_dict['friend']:
            return None
        # When there is no mark available, the OPQBot API will fill the remark field with nickname
        # Thus no need to test whether isRemark is true or not
        return self.info_dict['friend'][uin].get('Remark', None)

    def iot_send_text_message(self, chat_type: str, chat_uin: str,
                              content: str):
        if chat_type == 'phone':  # Send text to self
            self.action.sendPhoneText(content)
        elif chat_type == 'group':
            chat_uin = int(chat_uin)
            self.action.sendGroupText(chat_uin, content)
        elif chat_type == 'friend':
            chat_uin = int(chat_uin)
            self.action.sendFriendText(chat_uin, content)
        elif chat_type == 'private':
            user_info = chat_uin.split('_')
            chat_uin = int(user_info[0])
            chat_origin = int(user_info[1])
            self.action.sendPrivateText(chat_uin, chat_origin, content)

    def iot_send_image_message(self,
                               chat_type: str,
                               chat_uin: str,
                               file: IO,
                               content: Union[str, None] = None):
        image_base64 = base64.b64encode(file.read()).decode("UTF-8")
        md5_sum = hashlib.md5(file.read()).hexdigest()
        content = content if content else ""
        if chat_type == 'private':
            user_info = chat_uin.split('_')
            chat_uin = int(user_info[0])
            chat_origin = int(user_info[1])
            self.action.sendPrivatePic(user=chat_uin,
                                       group=chat_origin,
                                       picBase64Buf=image_base64,
                                       content=content)
        elif chat_type == 'friend':
            chat_uin = int(chat_uin)
            self.action.sendFriendPic(user=chat_uin,
                                      picBase64Buf=image_base64,
                                      content=content)
        elif chat_type == 'group':
            chat_uin = int(chat_uin)
            self.action.sendGroupPic(group=chat_uin,
                                     picBase64Buf=image_base64,
                                     content=content)

    def iot_send_voice_message(self, chat_type: str, chat_uin: str, file: IO):
        voice_base64 = base64.b64encode(file.read()).decode("UTF-8")
        if chat_type == 'private':
            user_info = chat_uin.split('_')
            chat_uin = int(user_info[0])
            chat_origin = int(user_info[1])
            self.action.sendPrivateVoice(user=chat_uin,
                                         group=chat_origin,
                                         voiceBase64Buf=voice_base64)
        elif chat_type == 'friend':
            chat_uin = int(chat_uin)
            self.action.sendFriendVoice(user=chat_uin,
                                        voiceBase64Buf=voice_base64)
        elif chat_type == 'group':
            chat_uin = int(chat_uin)
            self.action.sendGroupVoice(group=chat_uin,
                                       voiceBase64Buf=voice_base64)
Esempio n. 7
0
def receive_group_msg(ctx: GroupMsg):
    global action  # pylint: disable=W0603
    if action is None:
        # pylint: disable=W0212
        action = Action(ctx.CurrentQQ, host=ctx._host, port=ctx._port)

    if ctx.FromUserId == ctx.CurrentQQ:
        return

    # 退订UP
    if ctx.Content.startswith("哔哩视频退订"):
        try:
            mid = re.findall(r"(\d+)", ctx.Content)[0]
        except Exception:
            msg = "UID应为数字"
        else:
            db = DB()
            if db.unsubscribe_up(ctx.FromGroupId, mid):
                upinfo = API.get_up_info_by_mid(mid)
                if upinfo is not None:
                    msg = "成功退订UP主:{}".format(upinfo.name)
                else:
                    msg = "成功退订UP主:{}".format(mid)
            else:
                msg = "本群未订阅该UP主"
        action.sendGroupText(ctx.FromGroupId, msg)
    # 查看订阅UP列表
    elif ctx.Content == "哔哩视频列表":
        db = DB()
        mids = db.get_ups_by_gid(ctx.FromGroupId)
        if mids:
            ups = []
            for mid in mids:
                upinfo = API.get_up_info_by_mid(mid)
                if upinfo is not None:
                    ups.append("{}({})".format(upinfo.mid, upinfo.name))
                else:
                    ups.append(str(mid))
            msg = "本群已订阅UP主:\n" + "\n".join(ups)
        else:
            msg = "本群还没有订阅过一个UP主"
        action.sendGroupText(ctx.FromGroupId, msg)

    # 退订番剧
    elif ctx.Content.startswith("哔哩番剧退订"):
        try:
            mid = re.findall(r"(\d+)", ctx.Content)[0]
        except Exception:
            msg = "番剧ID应为数字"
        else:
            db = DB()
            if db.unsubscribe_bangumi(ctx.FromGroupId, mid):
                # 通过最新集数中的api获取番剧基本信息勉勉强强满足需求
                bangumi = API.get_latest_ep_by_media_id(mid)
                if bangumi is not None:
                    msg = "成功退订番剧:{}".format(bangumi.long_title)
                else:
                    msg = "成功退订番剧:{}".format(mid)
            else:
                msg = "本群未订阅该UP主"
        action.sendGroupText(ctx.FromGroupId, msg)
    # 查看订阅番剧列表
    elif ctx.Content == "哔哩番剧列表":
        db = DB()
        mids = db.get_bangumi_by_gid(ctx.FromGroupId)
        if mids:
            msgs = []
            for mid in mids:
                bangumi = API.get_latest_ep_by_media_id(mid)
                if bangumi is not None:
                    msgs.append("{}({})".format(mid, bangumi.long_title))
                else:
                    msgs.append(str(mid))
            msg = "本群已订阅番剧:\n" + "\n".join(msgs)
        else:
            msg = "本群还没有订阅过一部番剧"
        action.sendGroupText(ctx.FromGroupId, msg)

    # 其他操作逻辑转到session操作
    else:
        bilibili_handler.message_receiver(ctx)