Example #1
0
def get_group_contact():
    """
    获取群组数据
    :return:
    """
    # 获取所有联系人的列表
    all_contact_res = proxy.get_contact()
    if all_contact_res['code'] == 200:
        contact_list = all_contact_res['data']['contact_list']
        # contact_list  [{'Uin': 0, 'UserName': '******', 'NickName': '该帐号已冻结' ......
        # if contact_list[0]['Uin'] == 0:
        #     return jsonify(ret_val.gen(ret_val.CODE_PROXY_ERR, extra_msg=contact_list[0]['NickName']))

        # 从联系人列表中提取出群列表
        group_name_list = groups_username_from_contacts(contact_list)
        # 存储群组的联系人信息
        group_contact_res = proxy.batch_get_group_contact(group_name_list)

        if group_contact_res['code'] == ret_val.CODE_SUCCESS:
            group_contact_list = group_contact_res['data'][
                'group_contact_list']

            # 对群做筛选
            utils.filter_group_contact_list(group_contact_list)

            save_group_contact_list(group_contact_list)

            return jsonify(
                ret_val.gen(ret_val.CODE_SUCCESS,
                            data=group_contact_list,
                            extra_msg='提取群组信息成功'))
        else:
            return jsonify(group_contact_res)
    else:
        return jsonify(all_contact_res)
Example #2
0
def get_msg():
    # 从代理拉取新消息
    ret = proxy.get_msg()
    if ret['code'] == ret_val.CODE_SUCCESS:
        # 消息列表
        AddMsgList = ret['data']['AddMsgList']
        group_msg_list = {
            'msg_list': [],  # 所有的消息列表
            'msg_list_detected': [],  # 检测出有问题的消息列表
        }
        if AddMsgList:
            group_msg_list = utils.produce_group_msg(AddMsgList)
            WxGroupMessage.batch_insert(group_msg_list['msg_list'])
            if group_msg_list['msg_list_detected']:
                MsgDetectResult.batch_insert(
                    group_msg_list['msg_list_detected'])

        # 联系人变动列表
        ModContactList = ret['data']['ModContactList']
        print('mod contact before : ', ModContactList)
        mod_group_list = groups_from_contacts(ModContactList)
        print('mod contact after : ', mod_group_list)
        # 因为处于已登录状态,所以更新联系人以username(@@)作为唯一ID
        update_group_contact_list(mod_group_list)

        return jsonify(
            ret_val.gen(ret_val.CODE_SUCCESS,
                        data={
                            'group_msg_list': group_msg_list,
                            'mod_group_list': mod_group_list
                        }))
    else:
        return jsonify(ret)
Example #3
0
def web_init():
    """
    初始化微信首页栏的联系人、公众号等
    ** 最重要的是** 初始化登录者的信息(昵称、性别等),初始化同步消息所用的SycnKey
    :return: 用于信息列表
    """

    # 组装请求参数及url
    url = '%s/webwxinit' % session.get(SESSION_KEY.WxLoginInfo).get("url")
    params = {
        'r': int(-time.time() / 1579),
        'pass_ticket': session.get(SESSION_KEY.WxLoginInfo).get("passticket")
    }
    data = {'BaseRequest': getBaseRequest()}
    headers = {
        'ContentType': 'application/json; charset=UTF-8',
        'User-Agent': config.USER_AGENT,
    }

    # 发起请求获取返回结果
    r = s.post(url, params=params, data=json.dumps(data), headers=headers)
    dic = json.loads(r.content.decode('utf-8', 'replace'))

    if not dic or not dic.get('User').get('Uin'):
        return ret_val.gen(
            ret_val.CODE_PROXY_ERR,
            extra_msg='Cannot get wx_init response ! 微信web_init接口未得到正确的返回结果')

    print(session)
    '''
    处理用户数据
    '''
    user_dict = {}
    emoji_formatter(dic['User'], 'NickName')  # 格式化昵称中的表情
    # 将返回的user信息与user_dict合并,userdict里面的键全小写
    for key in dic['User'].keys():
        user_dict[key.lower()] = dic['User'][key]

    user_dict['invitestartcount'] = int(dic['InviteStartCount'])
    user_dict['skey'] = dic['SKey']
    user_dict['synckeydict'] = dic['SyncKey']
    # 用于同步消息的 synckey  结果示例:1_690237487|2_690237829|3_690237697|1000_1545727682
    user_dict['synckey'] = '|'.join([
        '%s_%s' % (item['Key'], item['Val']) for item in dic['SyncKey']['List']
    ])

    return ret_val.gen(ret_val.CODE_SUCCESS, data={"user_dict": user_dict})
Example #4
0
def get_member_head_img():
    # 群组信息
    group_id = request.args.get('group_id')
    encry_chatroom_id = request.args.get('encry_chatroom_id')
    # 成员信息
    username = request.args.get('username')
    user_nickname = request.args.get('user_nickname')
    # TODO: 强制要求头像更新
    force = request.args.get('user_nickname')

    if not all((group_id, encry_chatroom_id, username, user_nickname)):
        return jsonify(
            ret_val.gen(
                ret_val.CODE_PARAMS_ERR,
                extra_msg=
                '需要传入 group_id, encry_chatroom_id, username, user_nickname'))

    filedir = 'IMMonitor/static/wxheadimg/'
    filename = '%s%s' % (group_id, user_nickname)
    filename = hashlib.md5(filename.encode('utf8')).hexdigest()
    file_format = '.png'
    file = '{filedir}{filename}{file_format}'.format(filedir=filedir,
                                                     filename=filename,
                                                     file_format=file_format)
    # 不存在头像才请求新的
    if not os.path.exists(file):
        bit_img = proxy.get_member_head_img(EncryChatRoomId=encry_chatroom_id,
                                            username=username)
        if len(bit_img) > 10:
            with open(file, 'wb') as f:
                f.write(bit_img)

            return jsonify(
                ret_val.gen(ret_val.CODE_SUCCESS,
                            data={'FilePath': file.replace('IMMonitor', '')}))
        else:
            return jsonify(
                ret_val.gen(ret_val.CODE_SUCCESS,
                            data={'FilePath':
                                  '/static/wxheadimg/default.png'}))

    # 存在头像了返回原来的头像
    else:
        return jsonify(
            ret_val.gen(ret_val.CODE_SUCCESS,
                        data={'FilePath': file.replace('IMMonitor', '')}))
Example #5
0
def get_msg():
    """
    拉取新消息代理
    :param self:
    :return: tuple (dic['AddMsgList'], dic['ModContactList'])
    AddMsgList: list 所有的新消息列表
    ModContactList:list 所有联系人有变动的列表
    """

    loginInfo = session.get(SESSION_KEY.WxLoginInfo)
    # 组装获取新消息的url及参数
    url = '%s/webwxsync?sid=%s&skey=%s&pass_ticket=%s' % (
        loginInfo['url'], loginInfo['sid'],
        loginInfo['skey'], loginInfo['passticket'])
    data = {
        'BaseRequest': getBaseRequest(),
        'SyncKey': loginInfo['synckeydict'],
        'rr': ~int(time.time()), }
    headers = {
        'ContentType': 'application/json; charset=UTF-8',
        'User-Agent': config.USER_AGENT}

    # 发起拉取新消息的请求
    r = s.post(url, data=json.dumps(data), headers=headers, timeout=config.TIMEOUT)

    response = json.loads(r.content.decode('utf-8', 'replace'))
    if response['BaseResponse']['Ret'] != 0:
        return ret_val.gen(ret_val.CODE_PROXY_ERR,
                           extra_msg='Error get msg ! 拉取新消息出错 !')

    # 更新session登录信息中的synckeyresponset及synckey
    session[SESSION_KEY.WxLoginInfo]['synckeydict'] = response['SyncKey']
    session[SESSION_KEY.WxLoginInfo]['synckey'] = '|'.join(['%s_%s' % (item['Key'], item['Val'])
                                          for item in response['SyncCheckKey']['List']])

    return ret_val.gen(ret_val.CODE_SUCCESS, data={
        'AddMsgList': response['AddMsgList'],
        'ModContactList': response['ModContactList']
    })
Example #6
0
def on_join(data):
    '''
    加入一个用户自己的房间,实现多用户通信
    :param data: socket传过来的值
    :return: socket emit 加入房间状态
    '''
    params_username = data.get('username')
    session_username = session.get('user_id')
    if not params_username or session_username is None or params_username != session_username:
        retmsg = ret_val.gen(ret_val.CODE_SOCKET_ERR,
                             extra_msg='Can not join Room %s ! 无法加入房间:%s' %
                             (params_username, params_username))

        socketio.emit('check_join', data=retmsg)
        return

    join_room(session_username)
    retmsg = ret_val.gen(ret_val.CODE_SUCCESS,
                         extra_msg='Join room named %s successfully ! 成功加入房间' %
                         (params_username, params_username))
    socketio.emit('check_join', data=retmsg, room=session_username)
    return
Example #7
0
def get_QR():
    """
    根据扫码登录uuid生成登录二维码
    :param uuid: 扫码登录uuid
    :return: BytesIO 二维码二进制io流
    """
    uuid = _get_QRuuid()
    # 如果没有请求到扫码登录的uuid,返回错误
    if not uuid:
        return ret_val.gen(ret_val.CODE_PROXY_ERR,
                           extra_msg='Cannot get QRuuid ! 无法获取二维码扫码登录uuid !')

    else:
        session[SESSION_KEY.WxLoginInfo]['uuid'] = uuid
        qrStorage = io.BytesIO()
        qrCode = QRCode('https://login.weixin.qq.com/l/' + uuid)
        qrCode.png(qrStorage, scale=10)
        return ret_val.gen(ret_val.CODE_SUCCESS,
                           data={
                               "qrcode":
                               bytes.decode(
                                   base64.b64encode(qrStorage.getvalue()))
                           })
Example #8
0
def send_raw_msg(msgType, content, toUserName):
    loginInfo = session.get(SESSION_KEY.WxLoginInfo)
    url = '%s/webwxsendmsg' % loginInfo['url']
    data = {
        'BaseRequest': getBaseRequest(),
        'Msg': {
            'Type': msgType,
            'Content': content,
            'FromUserName': loginInfo['username'],
            'ToUserName': toUserName,
            'LocalID': int(time.time() * 1e4),
            'ClientMsgId': int(time.time() * 1e4),
        },
        'Scene': 0, }
    headers = {'ContentType': 'application/json; charset=UTF-8', 'User-Agent': config.USER_AGENT}
    r = s.post(url, headers=headers,
                    data=json.dumps(data, ensure_ascii=False).encode('utf8'))

    response = json.loads(r.content.decode('utf-8', 'replace'))
    if response['BaseResponse']['Ret'] != 0:
        return ret_val.gen(ret_val.CODE_PROXY_ERR,
                           extra_msg='Error get msg ! 消息发送失败 !')
    else:
        return ret_val.gen(ret_val.CODE_SUCCESS)
Example #9
0
def send_raw_msg():
    """
    发送消息, 目前只支持文字
    TODO: 支持发送图片、语音、视频等
    :return:
    """
    content = request.args.get('content')
    to_username = request.args.get('to_username')
    if not content or not to_username:
        return jsonify(
            ret_val.gen(ret_val.CODE_PARAMS_ERR,
                        extra_msg='需要传入 content, to_username 参数'))

    ret = proxy.send_raw_msg(1, content=content, toUserName=to_username)
    return jsonify(ret)
Example #10
0
def check_login():
    """
    检测用户是否已登录
    :param self:
    :param uuid: 扫描登录的uuid
    :return:    200 确认登录
                 201 扫描成功(还未按登录按钮)
                 204 现在正有一个检查登录请求在发送,不需要重复请求
                 408 一直未扫描(或者登陆超时)
                 400/424 代理出错
    """
    '''
    -----------------------------------------------------------------------------------------------------
    | 接口地址 | https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login
    -----------------------------------------------------------------------------------------------------
    | 请求方法 | GET
    -----------------------------------------------------------------------------------------------------
    |          | tip: 1 未扫描 0 已扫描 
    | 传递参数 | uuid: xxx 
    |          |  _: 时间戳
    -----------------------------------------------------------------------------------------------------
    |  返回值  |  window.code=xxx;
    |          |  xxx:
    |          |    200 确认登录
    |          |    201 扫描成功(还未按登录按钮)
    |          |    408 一直未扫描(或者登陆超时)
    |          |    400 424 代理出错
    |          |    
    |          | 当返回200时,在返回的body中,还会返回以下数据让前端重定向到登录后的页面,
    |          | window.redirect_uri="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=xxx&uuid=xxx&lang=xxx&scan=xxx";
    |          | 这里我们使用 proccess_login_info 函数再起发起该重定向地址的请求
    -----------------------------------------------------------------------------------------------------
    '''
    uuid = session.get(SESSION_KEY.WxLoginInfo).get('uuid')

    # return ret_val.gen(ret_val.CODE_SUCCESS)
    # 构造checklogin请求url及参数
    url = '%s/cgi-bin/mmwebwx-bin/login' % config.BASE_URL
    localTime = int(time.time())
    params = 'loginicon=true&uuid=%s&tip=1&r=%s&_=%s' % (
        uuid, int(-localTime / 1579), localTime)
    headers = {'User-Agent': config.USER_AGENT}

    # 发起请求
    r = s.get(url, params=params, headers=headers)
    regx = r'window.code=(\d+)'
    data = re.search(regx, r.text)

    # TODO: 扫码的时候返回用户头像用于显示
    # r.text = window.code=201;window.userAvatar = 'data:img/jpg;base64,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';

    # 如果是确认登录
    if not data:
        return ret_val.gen(
            ret_val.CODE_PROXY_ERR,
            extra_msg=
            'Regex extract nothing in check login response data ! 正则匹配没有在检测登录接口的返回值中匹配到任何信息 !'
        )

    retcode = data.group(1)
    # 确认登录
    if retcode == '200':
        # 处理确认登录的返回结果
        if process_login_info(r.text):
            # 登录之后设置checklogin为True
            return ret_val.gen(ret_val.CODE_SUCCESS)
        else:
            return ret_val.gen(
                ret_val.CODE_PROXY_ERR,
                extra_msg='Error when process confirm login ! 处理确认登录时发送错误 !')
    else:
        return {"code": int(retcode), "status": "ok"}
Example #11
0
def get_head_img():
    username = request.args.get('username')
    group_id = request.args.get('group_id')
    uin = request.args.get('uin')
    if not username:
        return jsonify(
            ret_val.gen(ret_val.CODE_PARAMS_ERR, extra_msg='需要传入username'))

    filedir = 'IMMonitor/static/wxheadimg/'
    file_format = '.png'

    if uin:
        file = '{filedir}user_{filename}{file_format}'.format(
            filedir=filedir, filename=uin, file_format=file_format)
        # 不存在头像才请求新的
        if not os.path.exists(file):
            bit_img = proxy.get_head_img(username=username, type=0)
            if len(bit_img) > 10:
                with open(file, 'wb') as f:
                    f.write(bit_img)

                return jsonify(
                    ret_val.gen(
                        ret_val.CODE_SUCCESS,
                        data={'FilePath': file.replace('IMMonitor', '')}))
            else:
                return jsonify(
                    ret_val.gen(
                        ret_val.CODE_SUCCESS,
                        data={'FilePath': '/static/wxheadimg/default.png'}))

        # 存在头像了返回原来的头像
        else:
            return jsonify(
                ret_val.gen(ret_val.CODE_SUCCESS,
                            data={'FilePath': file.replace('IMMonitor', '')}))
    elif group_id:
        file = '{filedir}group_{filename}{file_format}'.format(
            filedir=filedir, filename=group_id, file_format=file_format)
        # 不存在头像才请求新的
        if not os.path.exists(file):
            bit_img = proxy.get_head_img(username=username, type=1)
            if len(bit_img) > 10:
                with open(file, 'wb') as f:
                    f.write(bit_img)

                return jsonify(
                    ret_val.gen(
                        ret_val.CODE_SUCCESS,
                        data={'FilePath': file.replace('IMMonitor', '')}))
            else:
                return jsonify(
                    ret_val.gen(
                        ret_val.CODE_SUCCESS,
                        data={'FilePath': '/static/wxheadimg/default.png'}))

        # 存在头像了返回原来的头像
        else:
            return jsonify(
                ret_val.gen(ret_val.CODE_SUCCESS,
                            data={'FilePath': file.replace('IMMonitor', '')}))
Example #12
0
def sync_check():
    """
    检查是否有新消息代理
    :return:
    """

    '''
    -----------------------------------------------------------------------------------------------------
    | 接口地址 | https://webpush2.weixin.qq.com/cgi-bin/mmwebwx-bin/synccheck
    -----------------------------------------------------------------------------------------------------
    | 请求方法 | GET
    -----------------------------------------------------------------------------------------------------
    |          |  r=时间戳(ms)
    |          |  skey=xxx
    |          |  sid=xxx
    | 传递参数 |  uin=xxx
    |          |  deviceid=xxx
    |          |  synckey=1_654585659%7C2_654585745%7C3_654585673%7C1000_1467162721_=1467184052133
    -----------------------------------------------------------------------------------------------------
    |  返回值  |  {window.synccheck={retcode:"xxx",selector:"xxx"}
    -----------------------------------------------------------------------------------------------------
    |          |  retcode:
    |          |    0 正常
    |          |    1100 失败/退出微信
    | 返回参数 |  selector:
    |          |    0 正常,无新消息
    |          |    2 新的消息
    |          |    4 朋友圈有动态
    |          |    6 有消息返回结果
    |          |    7 进入/离开聊天界面
    -----------------------------------------------------------------------------------------------------

    web微信主要的过程就是轮询+获取消息的循环。轮询的接口为synccheck,获取消息接口为webwxsync。
    首先向synccheck接口发起GET请求,如果暂时没有新消息的话,保持住连接不返回直至超时。

    超时后会返回一个类似这样的数据包: {window.synccheck={retcode:"xxx",selector:"xxx"}
    其中RETCODE为返回状态,非0代表有错误;
    SELECTOR代表消息,0为无消息,非0值则有消息。

    因此,对于超时的情况,selector是为0的。
    如果在GET请求后发现有新消息,那么服务器会立刻返回一个同样格式的数据包,RETCODE为0,SELECTOR不为0。
    此时,就需要调用webwxsync接口,用POST方式去获取新消息。
    POST请求的返回除了有消息以外,header里还有Set cookie指令(不过好像每次的cookie都一样而且过期时间有一天之多),
    另外response body里还有一个重要的东西,就是syncKey和syncCheckKey。
    这里就是我前文中提到的过时的情况之一,网上绝大多数资料都是只有一个syncKey,实际返回的却有一个syncKey和一个syncCheckKey。
    从名字就能看出来,前者用于synccheck接口,后者用于webwxsync接口。
    由于syncKey每次都更新,所以如果某一次webwxsync接口的响应出了意外,后面的程序是没法进行下去的(本地key已经过期了)。

    参考文档:
    1、 https://blog.csdn.net/wonxxx/article/details/51787041
    2、https://www.cnblogs.com/shawnye/p/6376400.html
    '''

    loginInfo = session.get('WxLoginInfo')
    # 组装请求url及参数
    url = '%s/synccheck' % loginInfo.get('syncUrl', loginInfo['url'])
    params = {
        'r': int(time.time() * 1000),
        'skey': loginInfo['skey'],
        'sid': loginInfo['sid'],
        'uin': loginInfo['uin'],
        'deviceid': loginInfo['deviceid'],
        'synckey': loginInfo['synckey'],
        '_': loginInfo['logintime'], }
    headers = {'User-Agent': config.USER_AGENT}
    loginInfo['logintime'] += 1
    # 同步更新session中的logintime
    session[SESSION_KEY.WxLoginInfo]['logintime'] = loginInfo['logintime']
    try:
        r = s.get(url, params=params, headers=headers, timeout=config.TIMEOUT)
    except requests.exceptions.ConnectionError as e:
        try:
            if not isinstance(e.args[0].args[1], BadStatusLine):
                raise
            # will return a package with status '0 -'
            # and value like:
            # 6f:00:8a:9c:09:74:e4:d8:e0:14:bf:96:3a:56:a0:64:1b:a4:25:5d:12:f4:31:a5:30:f1:c6:48:5f:c3:75:6a:99:93
            # seems like status of typing, but before I make further achievement code will remain like this
            return '2'
        except:
            raise
    # 如果有连接为404等异常,使用Request.raise_for_status 抛出异常
    r.raise_for_status()

    # 提取返回参数
    regx = r'window.synccheck={retcode:"(\d+)",selector:"(\d+)"}'
    pm = re.search(regx, r.text)

    # 筛选消息
    # 如果返回中的 retcode参数 == 0,返回 select的值
    # 其他即判断为出错返回None
    if pm is None or pm.group(1) != '0':
        return ret_val.gen(ret_val.CODE_PROXY_ERR,
                           extra_msg='Weixin proxy sync_check get wrong response '
                                     '微信sync_check检查消息接口返回值不正确,失败或退出微信')

    return ret_val.gen(ret_val.CODE_SUCCESS, data={
            "message_status": pm.group(2)
        })