Esempio n. 1
0
    def batch_update_drive_sheet(self,
                                 user_access_token,
                                 sheet_token,
                                 adds=None,
                                 copys=None,
                                 deletes=None):
        """操作子表

        :type self: OpenLark
        :param user_access_token: user_access_token
        :type user_access_token: str
        :param sheet_token: 文件的 token 列表
        :type sheet_token: str
        :param adds: 参数
        :type adds: List[BatchUpdateDriveSheetRequestAdd]
        :param copys: 参数
        :type copys: List[BatchUpdateDriveSheetRequestCopy]
        :param deletes: 参数
        :type deletes: List[BatchUpdateDriveSheetRequestDelete]
        :rtype (list[UpdateDriveSheetResponse], list[UpdateDriveSheetResponse], list[UpdateDriveSheetResponse])

        该接口用于根据 sheet_token 操作表格,如增加sheet,复制sheet、删除sheet。

        https://open.feishu.cn/document/ukTMukTMukTM/uYTMzUjL2EzM14iNxMTN
        """
        url = self._gen_request_url(
            '/open-apis/sheet/v2/spreadsheets/{}/sheets_batch_update'.format(
                sheet_token))
        requests = []
        if adds:
            for i in adds:
                requests.append(i.as_dict())
        if copys:
            for i in copys:
                requests.append(i.as_dict())
        if deletes:
            for i in deletes:
                requests.append(i.as_dict())

        res = self._post(url,
                         body={'requests': requests},
                         auth_token=user_access_token)
        adds_resp = []  # type: List[UpdateDriveSheetResponse]
        copys_resp = []  # type: List[UpdateDriveSheetResponse]
        deletes_resp = []  # type: List[UpdateDriveSheetResponse]

        for i in res.get('data', {}).get('replies', []):
            if 'addSheet' in i:
                adds_resp.append(
                    make_datatype(UpdateDriveSheetResponse,
                                  i['addSheet']['properties']))
            elif 'copySheet' in i:
                copys_resp.append(
                    make_datatype(UpdateDriveSheetResponse,
                                  i['copySheet']['properties']))
            elif 'deleteSheet' in i:
                deletes_resp.append(
                    make_datatype(UpdateDriveSheetResponse, i['deleteSheet']))

        return adds_resp, copys_resp, deletes_resp
Esempio n. 2
0
    def get_approval_definition(self, definition_code, locale='zh'):
        """查看审批定义

        :type self: OpenLark
        :param definition_code: 审批定义Code,需要有管理员权限,然后在 https://www.feishu.cn/approval/admin/approvalList 创建
        :type definition_code: str
        :param locale: zh or en
        :type locale: str
        :return: 审批定义对象 ApprovalDefinition
        :rtype: ApprovalDefinition

        根据 definition_code 获取某个审批定义的详情,用于构造创建审批实例的请求。

        https://open.feishu.cn/document/ukTMukTMukTM/uADNyUjLwQjM14CM0ITN
        """
        url = self._gen_request_url('/approval/openapi/v2/approval/get', app='approval')
        locale = {'zh': 'zh-CN', 'en': 'en-US'}.get(str(locale).lower())
        body = {'approval_code': definition_code, 'locale': locale}
        res = self._post(url, body, with_tenant_token=True)
        data = res['data']

        approval_name = data['approval_name']
        form = json.loads(data['form'])
        nodes = data['node_list']

        definition = ApprovalDefinition(
            approval_name=approval_name,
            forms=[make_datatype(ApprovalForm, i) for i in form],
            nodes=[make_datatype(ApprovalNode, i) for i in nodes],
        )
        return definition
Esempio n. 3
0
    def get_drive_file_meta(self, user_access_token, files):
        """获取各类文件的元数据

        :type self: OpenLark
        :param user_access_token: user_access_token
        :type user_access_token: str
        :param files: 文件的 token 列表
        :type files: list[DriveFileToken]
        :return: 文件元信息
        :rtype: list[DriveFileMeta]

        该接口用于根据 token 获取各类文件的元数据

        https://open.feishu.cn/document/ukTMukTMukTM/uMjN3UjLzYzN14yM2cTN
        """
        url = self._gen_request_url('/open-apis/suite/docs-api/meta')
        body = {
            'request_docs': [{
                'docs_token': i.token,
                'docs_type': i.type,
            } for i in files]
        }
        res = self._post(url, body=body, auth_token=user_access_token)
        return [
            make_datatype(DriveFileMeta, i) for i in res['data']['docs_metas']
        ]
Esempio n. 4
0
    def refresh_user_session(self, refresh_token, use_auth_v1=False):
        """刷新用户扫码登录后获取的 access_token

        :type self: OpenLark
        :param refresh_token: 扫码登录后会拿到这个值
        :type refresh_token: str
        :param use_auth_v1: 使用 auth v1 api
        :type use_auth_v1: bool
        :return: OAuthCodeToSessionResp
        :rtype: OAuthCodeToSessionResp

        刷新用户 token

        特别需要注意的是:老 api 获取的 expire_in 是时间戳,新 api 获取的 expire_in 是秒数

        老 api:https://open.feishu.cn/document/ukTMukTMukTM/ukTNz4SO1MjL5UzM

        新 api:https://open.feishu.cn/document/ukTMukTMukTM/uQDO4UjL0gDO14CN4gTN
        """
        if use_auth_v1:
            url = self._gen_request_url(
                '/open-apis/authen/v1/refresh_access_token')
        else:
            url = self._gen_request_url(
                '/connect/qrconnect/oauth2/access_token/')

        body = {
            "app_access_token": self.app_access_token,
            "grant_type": "refresh_token",
            "refresh_token": refresh_token
        }
        res = self._post(url, body)

        return make_datatype(OAuthCodeToSessionResp, res)
Esempio n. 5
0
    def oauth_code_2_session(self, code, use_auth_v1=False):
        """获取登录用户身份

        :type self: OpenLark
        :param code: 扫码登录后会自动302到redirect_uri并带上此参数
        :type code: str
        :param use_auth_v1: 使用 auth v1 api
        :type use_auth_v1: bool
        :return: OAuthCodeToSessionResp
        :rtype: OAuthCodeToSessionResp

        Web 扫码后拿到的 code 换取用户信息,通过此接口获取登录用户身份。

        特别需要注意的是:老 api 获取的 expire_in 是时间戳,新 api 获取的 expire_in 是秒数

        老 api:https://open.feishu.cn/document/ukTMukTMukTM/ukTNz4SO1MjL5UzM

        新 api:https://open.feishu.cn/document/ukTMukTMukTM/uEDO4UjLxgDO14SM4gTN
        """
        if use_auth_v1:
            url = self._gen_request_url('/open-apis/authen/v1/access_token')
        else:
            url = self._gen_request_url(
                '/connect/qrconnect/oauth2/access_token/')

        body = {
            'app_access_token': self.app_access_token,
            'grant_type': 'authorization_code',
            'code': code,
        }
        res = self._post(url, body)

        return make_datatype(OAuthCodeToSessionResp, res)
Esempio n. 6
0
    def get_user(self, open_id='', user_id=''):
        """获取用户信息

        :type self: OpenLark
        :param open_id: 用户的 open_id
        :type open_id: str
        :param user_id: 用户的 user_id
        :type user_id: str
        :return: User 对象
        :rtype: User

        https://open.feishu.cn/document/ukTMukTMukTM/ukjMwUjL5IDM14SOyATN
        """
        if open_id:
            url = self._gen_request_url(
                '/open-apis/user/v3/info?open_id={}'.format(open_id))
        elif user_id:
            url = self._gen_request_url(
                '/open-apis/user/v3/info?employee_id={}'.format(user_id))
        else:
            raise LarkInvalidArguments(
                msg='[get_user] empty open_id and user_id')

        res = self._get(url, with_tenant_token=True)
        res['user_id'] = pop_or_none(res, 'employee_id')
        return make_datatype(User, res)
Esempio n. 7
0
    def get_calendar_list(self, max_results=500, page_token='', sync_token=''):
        """获取日历列表

        :type self: OpenLark
        :param max_results: 一次请求要求返回最大数,该参数不能大于 1000,默认 500
        :type max_results: int
        :param page_token: 用于标志当次请求从哪页开始,90 天有效期
        :type page_token: str
        :param sync_token: 表示从上次返回的截止页起,返回结果,90 天有效期
        :type sync_token: str
        :return: 两个分页参数,和日历对象 Calendar 的列表
        :rtype: Tuple[str, str, List[Calendar]]

        该接口用于获取应用在企业内的日历列表。

        https://open.feishu.cn/document/ukTMukTMukTM/uMTM14yMxUjLzETN
        """
        if max_results > 1000:
            max_results = 1000

        url = self._gen_request_url(
            '/open-apis/calendar/v3/calendar_list?max_results={}'.format(
                max_results))
        if page_token:
            url = '{}&page_token={}'.format(url, page_token)
        if sync_token:
            url = '{}&sync_token={}'.format(url, sync_token)

        res = self._get(url, with_tenant_token=True, success_code=200000)
        data = res.get('data', [])
        page_token = res.get('page_token', '')
        sync_token = res.get('sync_token', '')
        calendar_list = [make_datatype(Calendar, i) for i in data]
        return page_token, sync_token, calendar_list
Esempio n. 8
0
    def create_drive_file(self, user_access_token, folder_token, title, file_type):
        """创建云空间文件

        :type self: OpenLark
        :param user_access_token: user_access_token
        :type user_access_token: str
        :param folder_token: 文件夹的 token
        :type folder_token: str
        :param title: 文档标题
        :type title: str
        :param file_type: 文档类型,可选值为 doc 和 sheet
        :type file_type: DriveFileType
        :return: 文件夹元信息
        :rtype: DriveCreateFile

        该接口用于根据 folder_token 创建 Docs或 Sheets 。

        https://open.feishu.cn/document/ukTMukTMukTM/uQTNzUjL0UzM14CN1MTN
        """
        url = self._gen_request_url('/open-apis/drive/explorer/v2/file/{}'.format(folder_token))
        body = {
            'title': title,
            'type': converter_enum(file_type, ranges=[DriveFileType.doc, DriveFileType.sheet]),
        }
        res = self._post(url, body=body, auth_token=user_access_token)
        return make_datatype(DriveCreateFile, res['data'])
Esempio n. 9
0
    def get_chat_list_of_bot(self, page_size=100, page_token=None):
        """获取机器人所在的群列表

        :type self: OpenLark
        :param page_size: 分页大小,最大支持 200;默认为 100
        :type page_size: int
        :param page_token: 分页标记,分页查询还有更多群时会同时返回新的 page_token, 下次遍历可采用该 page_token 获取更多
        :type page_token:str
        :return: 有更多群, 下次分页的参数,群的列表
        :rtype: Tuple[bool, str, List[Chat]]

        获取机器人所在的群列表。

        https://open.feishu.cn/document/ukTMukTMukTM/uITO5QjLykTO04iM5kDN
        """
        url = self._gen_request_url(
            '/open-apis/chat/v4/list?page_size={}'.format(page_size))
        if page_token:
            url = '{}&page_token={}'.format(url, page_token)
        res = self._get(url, with_tenant_token=True)
        data = res['data']
        has_more = data.get('has_more')  # type: bool
        page_token = data.get('page_token')  # type: str
        chats = [make_datatype(Chat, i) for i in data.get('groups', [])]
        return has_more, page_token, chats
Esempio n. 10
0
    def get_contact_scope(self):
        """获取通讯录授权范围

        :type self: OpenLark
        :return: is_visible_to_all, department_ids, users
        :rtype: (bool, list[str], list[SimpleUser])

        该接口用于获取应用被授权可访问的通讯录范围,包括可访问的部门列表及用户列表。

        授权范围为全员时,返回的部门列表为该企业所有的一级部门;
        否则返回的部门为管理员在设置授权范围时勾选的部门(不包含勾选部门的子部门)。

        https://open.feishu.cn/document/ukTMukTMukTM/ugjNz4CO2MjL4YzM

        https://bytedance.feishu.cn/docs/doccnOcR1fnxBACchoY9tlg7Amg#
        """
        url = self._gen_request_url('/open-apis/contact/v2/scope/get')
        res = self._get(url, with_tenant_token=True)
        data = res['data']
        department_ids = data.get('authed_departments', [])  # type: List[str]
        users = [
            make_datatype(SimpleUser, i) for i in data.get('authed_users', [])
        ]  # type: List[SimpleUser]
        is_visible_to_all = '0' in department_ids

        return is_visible_to_all, department_ids, users
Esempio n. 11
0
    def batch_get_room_list(self, room_ids, fields=None):
        """查询会议室详情

        :type self: OpenLark
        :param room_ids: 用于查询指定会议室的ID列表
        :type room_ids: List[str]
        :param fields: 可选字段有:"id,name,description,capacity,building_id,building_name,floor_name,is_disabled,
                       display_id",默认返回所有字段
        :return: 会议室列表
        :rtype: list[Room]

        https://open.feishu.cn/document/ukTMukTMukTM/uEDOyUjLxgjM14SM4ITN
        """
        params = {
            'room_ids': room_ids,
        }
        if fields:
            params['fields'] = ','.join(fields)

        url = self._gen_request_url(
            '/open-apis/meeting_room/room/batch_get?{}'.format(
                urlencode(params, doseq=True)))

        res = self._get(url, with_tenant_token=True)
        data = res.get('data', {})
        rooms = data.get('rooms', [])
        room_list = [make_datatype(Room, i) for i in rooms]

        return room_list
Esempio n. 12
0
    def add_drive_comment(self, user_access_token, file_token, content):
        """添加全文评论

        :type self: OpenLark
        :param user_access_token: user_access_token
        :type user_access_token: str
        :param file_token: 文件的 token
        :type file_token: str
        :param content: 评论内容
        :type content: str
        :return: 评论对象
        :rtype: DriveComment

        该接口用于根据 file_token 给文档添加全文评论

        https://open.feishu.cn/document/ukTMukTMukTM/ucDN4UjL3QDO14yN0gTN
        """
        if not content or not isinstance(content, string_types):
            raise LarkInvalidArguments(msg='content empty')

        content = content. \
            replace('<', '&lt;'). \
            replace('>', '&gt;'). \
            replace('&', '&amp;'). \
            replace('\'', '&#x27;'). \
            replace('"', '&quot;')

        url = self._gen_request_url('/open-apis/comment/add_whole')
        body = {'type': 'doc', 'token': file_token, 'content': content}
        res = self._post(url, body=body, auth_token=user_access_token)
        return make_datatype(DriveComment, res['data'])
Esempio n. 13
0
    def batch_get_department_detail(self, department_ids):
        """批量获取部门详情,只返回权限范围内的部门。

        :type self: OpenLark
        :param department_ids: 部门 ID 列表
        :type department_ids: list[str]
        :return: departments, errors
        :rtype: (dict[str, Department], dict[str, OpenLarkException])

        批量获取部门详情,只返回权限范围内的部门。

        https://open.feishu.cn/document/ukTMukTMukTM/uczN3QjL3czN04yN3cDN

        https://bytedance.feishu.cn/docs/doccnOcR1fnxBACchoY9tlg7Amg#
        """
        if isinstance(department_ids, str):
            department_ids = [department_ids]

        qs = '&'.join(['ids={}'.format(i) for i in department_ids])
        url = self._gen_request_url(
            '/open-apis/contact/v2/department/detail/batch_get?' + qs)
        res = self._get(url, with_tenant_token=True)
        data = res['data']

        departments = {}  # type: Dict[str, Department]
        for i in data.get('departments', []):
            dept = make_datatype(Department, i)  # type: Department
            departments[dept.id] = dept

        errors = {}  # type: Dict[str, OpenLarkException]
        for i in data.get('errors', []):
            e = gen_exception(code=i.get('code'), url='', msg=i.get('msg'))
            errors[i.get('id', '')] = e
        return departments, errors
Esempio n. 14
0
    def get_role_user_list(self, role_id, page_size=20, page_token=''):
        """获取角色成员列表

        :type self: OpenLark
        :param role_id: 角色 id
        :type role_id: str
        :param page_size: 分页大小,最大支持 200;默认为 20
        :type page_size: int
        :param page_token: 分页标记,分页查询还有更多群时会同时返回新的 page_token, 下次遍历可采用该 page_token 获取更多
        :type page_token: str
        :return: (has_more, page_token, user_list)
        :rtype: (bool, str, list[SimpleUser])

        该接口用于获取角色下的用户列表,调用该接口需要有该企业的通讯录读权限或写权限。返回结果为该应用通讯录权限范围内的角色成员列表。

        https://open.feishu.cn/document/ukTMukTMukTM/uczMwUjL3MDM14yNzATN
        """

        base = self._gen_request_url('/open-apis/contact/v2/role/members')
        url = join_url(base, [
            ('role_id', role_id),
            ('page_size', page_size),
            ('page_token', page_token),
        ])
        res = self._get(url, with_tenant_token=True)
        data = res['data']

        has_more = data.get('has_more', False)
        page_token = data.get('page_token', '')
        user_list = [
            make_datatype(SimpleUser, i) for i in data.get('user_list', [])
        ]  # type: List[SimpleUser]
        return has_more, page_token, user_list
Esempio n. 15
0
    def get_pay_order_detail(self, order_id):
        """查询订单详情

        :type self: OpenLark
        :param order_id: 获取用户购买套餐信息设置的过滤条件,normal为正常状态,refund为已退款,为空或者all表示所有,未支付的订单无法查到
        :type order_id: str

        该接口用于查询某个订单的具体信息

        https://open.feishu.cn/document/ukTMukTMukTM/uITNwUjLyUDM14iM1ATN
        """
        url = self._gen_request_url(
            '/open-apis/pay/v1/order/get?order_id={}'.format(order_id))

        res = self._get(url, with_app_token=True)

        data = res['data']

        total = data.get('total')
        has_more = data.get('has_more')
        page_token = data.get('page_token')
        orders = [
            make_datatype(PayOrder, i) for i in data.get('order_list', [])
        ]

        return has_more, page_token, total, orders
Esempio n. 16
0
    def delete_drive_file(self, user_access_token, file_token, file_type):
        """删除云空间文件

        :type self: OpenLark
        :param user_access_token: user_access_token
        :type user_access_token: str
        :param file_token: 文件的 token
        :type file_token: str
        :param file_type: 文档类型,可选值为 doc 和 sheet
        :type file_type: DriveFileType
        :return: 文件夹元信息
        :rtype: DriveDeleteFile

        本文档包含两个接口,分别用于删除 Doc 和 Sheet,对应的文档类型请调用对应的接口

        文档只能被文档所有者删除,文档被删除后将会放到回收站里

        https://open.feishu.cn/document/ukTMukTMukTM/uATM2UjLwEjN14CMxYTN
        """
        if converter_enum(file_type) == 'doc':
            url = self._gen_request_url('/open-apis/drive/explorer/v2/file/docs/{}'.format(file_token))
        elif converter_enum(file_type) == 'sheet':
            url = self._gen_request_url('/open-apis/drive/explorer/v2/file/spreadsheets/{}'.format(file_token))
        else:
            raise LarkInvalidArguments(msg='delete file type should be doc or sheet')

        res = self._delete(url, auth_token=user_access_token)
        return make_datatype(DriveDeleteFile, res['data'])
Esempio n. 17
0
    def batch_get_building(self, building_ids, fields=None):
        """查询建筑物详情

        :type self: OpenLark
        :param building_ids: 必须参数,用于查询指定建筑物的ID列表
        :type building_ids: list[str]
        :param fields: 可选参数,用于指定返回的字段名,可选字段有:"id,name,description,floors",默认返回所有字段
        :return: 建筑物列表
        :rtype: list[Building]

        https://open.feishu.cn/document/ukTMukTMukTM/ukzNyUjL5cjM14SO3ITN
        """
        params = {
            'building_ids': building_ids,
        }
        if fields:
            params['fields'] = ','.join(fields)

        url = self._gen_request_url(
            '/open-apis/meeting_room/building/batch_get?{}'.format(
                urlencode(params, doseq=True)))

        res = self._get(url, with_tenant_token=True)
        data = res.get('data', {})
        buildings = data.get('buildings', [])
        building_list = [make_datatype(Building, i) for i in buildings]

        return building_list
Esempio n. 18
0
def _get_conf():
    with open('./tests/conf.json', 'r') as f:
        v = json.load(f)
        conf = make_datatype(TestConfig, v)  # type: TestConfig

    logging.info('[test] local json conf is %s', conf)

    conf.lark_app_id = os.getenv('APP_ID_1')
    conf.lark_app_secret = os.getenv('APP_SECRET_1')

    return conf
Esempio n. 19
0
    def get_visible_apps(self,
                         user_id=None,
                         open_id=None,
                         page_size=20,
                         page_token='',
                         lang=I18NType.zh_cn):
        """获取应用在企业内的可用范围

        :type self: OpenLark
        :param user_id: 目标用户 user_id,与 open_id 至少给其中之一,user_id 优先于 open_id
        :type user_id: str
        :param open_id: 目标用户 open_id
        :type open_id: str
        :param page_size: 本次拉取用户列表最大个数(最大值 1000 ,0 自动最大个数 )
        :type page_size: int
        :param page_token: 分页拉取用户列表起始位置标示,不填表示从头开始
        :type page_token: str
        :param lang: 优先展示的应用信息的语言版本(zh_cn:中文,en_us:英文,ja_jp:日文)
        :type lang: I18NType
        :return: 是否还有更多, page_token, page_size, 总数, 语言, 应用列表
        :rtype: (bool, str, int, int, I18NType, list[App])

        该接口用于查询应用在该企业内可以被使用的范围,只能被企业自建应用调用且需要“获取应用信息”权限。

        https://open.feishu.cn/document/ukTMukTMukTM/uIjM3UjLyIzN14iMycTN
        """
        url = self._gen_request_url(
            '/open-apis/application/v1/user/visible_apps?')
        if user_id:
            url = '{}&user_id={}'.format(url, user_id)
        elif open_id:
            url = '{}&open_id={}'.format(url, open_id)
        else:
            raise LarkInvalidArguments(msg='empty user_id and open_id')
        if page_token:
            url = '{}&page_token={}'.format(url, page_token)
        if page_size:
            url = '{}&page_size={}'.format(url, page_size)
        if lang:
            url = '{}&lang={}'.format(url, converter_enum(lang))

        res = self._get(url, with_tenant_token=True)
        data = res['data']

        apps = [make_datatype(App, i)
                for i in data.get('app_list', [])]  # type: List[App]

        has_more = bool(data.get('has_more', False))  # type: bool
        lang = I18NType(data.get('lang', 'zh_cn'))  # type: I18NType
        page_size = data.get('page_size')  # type: int
        page_token = data.get('page_token')  # type: str
        total_count = data.get('total_count')  # type: int
        return has_more, page_token, page_size, total_count, lang, apps
Esempio n. 20
0
def _make_v1_department_info(res):
    """
    :rtype: Department
    """
    department_info = res['data'].get('department_info') or {}
    open_id = department_info.get('leader_open_id')
    user_id = department_info.get('leader_employee_id')
    department_info['leader'] = {
        'open_id': open_id,
        'user_id': user_id,
    }
    return make_datatype(Department, department_info)  # type: Department
Esempio n. 21
0
    def get_users_by_mobile_email(self, emails=None, mobiles=None):
        """获取用户信息

        :type self: OpenLark
        :param emails: 邮箱列表
        :type emails: list[str]
        :param mobiles: 手机列表
        :type mobiles: list[str]
        :return: 邮箱的用户字典,邮箱不存在的列表,手机的用户字典,手机不存在的列表
        :rtype: (dict[str, list[SimpleUser]], list[str], dict[str, list[SimpleUser]], list[str])

        根据用户邮箱或手机号查询用户 open_id 和 user_id,支持批量查询。

        只能查询到应用可用性范围内的用户 ID。

        调用该接口需要具有 “获取用户 ID” 权限。

        https://open.feishu.cn/document/ukTMukTMukTM/uUzMyUjL1MjM14SNzITN
        """

        qs = '&'.join(['mobiles=' + i for i in (mobiles or [])] +
                      ['emails=' + i for i in (emails or [])])
        url = self._gen_request_url('/open-apis/user/v1/batch_get_id?' + qs)

        res = self._get(url, with_tenant_token=True)
        data = res['data']
        email_users = data.get('email_users', {})
        email_users = {
            k: [make_datatype(SimpleUser, vv) for vv in v]
            for k, v in email_users.items()
        }  # type: dict[str, List[SimpleUser]]
        emails_not_exist = data.get('emails_not_exist', [])  # type: List[str]
        mobile_users = data.get('mobile_users', {})
        mobile_users = {
            k: [make_datatype(SimpleUser, vv) for vv in v]
            for k, v in mobile_users.items()
        }  # type: dict[str, List[SimpleUser]]
        mobiles_not_exist = data.get('mobiles_not_exist',
                                     [])  # type: List[str]
        return email_users, emails_not_exist, mobile_users, mobiles_not_exist
Esempio n. 22
0
    def search_drive_file(self,
                          user_access_token,
                          key,
                          owner_open_ids=None,
                          chat_open_ids=None,
                          docs_types=None,
                          count=50,
                          offset=0):
        """文档搜索

        :type self: OpenLark
        :param user_access_token: user_access_token
        :type user_access_token: str
        :param key: 搜索的关键词
        :type key: str
        :param owner_open_ids: 文档所有人
        :type owner_open_ids: list[str]
        :param chat_open_ids: 文档所在群
        :type chat_open_ids: list[str]
        :param docs_types: 文档类型,支持:"doc", "sheet", "slide", "bitable", "mindnote", "file", "wiki"
        :type docs_types: list[DriveFileType]
        :param count: 个数
        :type count: int
        :param offset: 偏移
        :type offset: int
        :return: 文件元信息
        :rtype: (bool, int, list[DriveFileMeta])

        该接口用于根据搜索条件进行文档搜索

        https://open.feishu.cn/document/ukTMukTMukTM/ugDM4UjL4ADO14COwgTN
        """
        url = self._gen_request_url('/open-apis/suite/docs-api/search/object')
        body = {
            'search_key': key,
            'count': count,
            'offset': offset,
        }
        if owner_open_ids:
            body['owner_ids'] = owner_open_ids
        if chat_open_ids:
            body['chat_ids'] = chat_open_ids
        if docs_types:
            body['docs_types'] = [converter_enum(i) for i in docs_types]
        res = self._post(url, body=body, auth_token=user_access_token)
        has_more = res['data']['has_more']
        total = res['data']['total']
        entities = [
            make_datatype(DriveFileMeta, i)
            for i in res['data']['docs_entities']
        ]
        return has_more, total, entities
Esempio n. 23
0
    def get_building_list(self,
                          page_size=10,
                          page_token='',
                          order_by='',
                          fields=None):
        """获取建筑物列表

        :type self: OpenLark
        :param page_size: 可选参数,请求期望返回的建筑物数量,不足则返回全部,该值默认为 10,最大为 100
        :type page_size: int
        :param page_token: 可选参数,用于标记当前请求的页数,将返回以当前页开始,往后 page_size 个元素
        :type page_token: str
        :param order_by: 可选参数,提供用于对名称进行升序/降序排序的方式查询,可选项有:"name-asc,name-desc",传入其他字符串不做处理,默认无序
        :type order_by: str
        :param fields: 可选参数,可选字段有:"id,name,description,floors",默认返回所有字段
        :type fields: list[str]
        :return: has_more, page_token, 建筑物列表
        :rtype: (bool, str, list[Building])

        该接口用于获取本企业下的建筑物(办公大楼)。

        https://open.feishu.cn/document/ukTMukTMukTM/ugzNyUjL4cjM14CO3ITN
        """
        if page_size > 100:
            page_size = 100
        elif page_size <= 0:
            page_size = 10

        params = {
            'page_size': page_size,
        }
        if page_token:
            params['page_token'] = page_token
        if order_by:
            params['order_by'] = order_by
        if fields:
            params['fields'] = ','.join(fields)

        url = self._gen_request_url(
            '/open-apis/meeting_room/building/list?{}'.format(
                urlencode(params)))
        res = self._get(url, with_tenant_token=True)
        data = res['data']

        has_more = data.get('has_more', False)
        page_token = data.get('page_token', '')
        building_list = [
            make_datatype(Building, i) for i in data.get('buildings', [])
        ]
        return has_more, page_token, building_list
Esempio n. 24
0
    def update_calendar_by_id(self,
                              calendar_id,
                              summary=None,
                              description=None,
                              is_private=None,
                              default_access_role=None):
        """更新日历

        :type self: OpenLark
        :param calendar_id: 日历 ID
        :type calendar_id: str
        :param summary: 日历标题,最大长度为 256
        :type summary: str
        :param description: 日历描述,最大长度为 256
        :type description: str
        :param is_private: 是否为私有日历,私有日历不可被搜索,默认为false
        :type is_private: bool
        :param default_access_role: 表示用户的默认访问权限。取值如下:
                   reader: 订阅者,可查看日程详情
                   free_busy_reader: 游客,只能看到"忙碌/空闲"
        :type default_access_role: CalendarRole
        :return: Calendar 对象
        :rtype: Calendar

        该接口用于修改指定日历的信息。

        https://open.feishu.cn/document/ukTMukTMukTM/uYTM14iNxUjL2ETN
        """
        url = self._gen_request_url(
            '/open-apis/calendar/v3/calendars/{}'.format(calendar_id))
        body = {}
        for k, v in {
                'summary':
                summary,
                'description':
                description,
                'is_private':
                is_private,
                'default_access_role':
                default_access_role.value if default_access_role else None,
        }.items():
            if v is not None:
                body[k] = v
        res = self._patch(url,
                          body,
                          with_tenant_token=True,
                          success_code=200000)
        data = res['data']
        return make_datatype(Calendar, data)
Esempio n. 25
0
    def get_room_list(self,
                      building_id,
                      page_size=100,
                      page_token='',
                      order_by='',
                      fields=None):
        """获取会议室列表

        :type self: OpenLark
        :param building_id: 被查询的建筑物ID
        :type building_id: str
        :param page_size: 请求期望返回的会议室数量,不足则返回全部,该值默认为 100,最大为 1000
        :type page_size: int
        :param page_token: 用于标记当前请求的页数,将返回以当前页开始,往后 page_size 个元素
        :type page_token: str
        :param order_by: 提供用于对名称/楼层进行升序/降序排序的方式查询,可选项有:"name-asc,name-desc,floor_name-asc,
                         floor_name-desc",传入其他字符串不做处理,默认无序
        :type order_by: str
        :param fields: 可选字段有:"id,name,description,capacity,building_id,building_name,floor_name,is_disabled,
                       display_id",默认返回所有字段
        :type fields: list[str]
        :return: has_more, page_token, 会议室列表
        :rtype: (bool, str, list[Room])

        https://open.feishu.cn/document/ukTMukTMukTM/uADOyUjLwgjM14CM4ITN
        """
        if page_size > 1000:
            page_size = 1000
        elif page_size <= 0:
            page_size = 100

        params = {'building_id': building_id, 'page_size': page_size}
        if page_token:
            params['page_token'] = page_token
        if order_by:
            params['order_by'] = order_by
        if fields:
            params['fields'] = ','.join(fields)

        url = self._gen_request_url(
            '/open-apis/meeting_room/room/list?{}'.format(urlencode(params)))
        res = self._get(url, with_tenant_token=True)
        data = res.get('data', {})
        rooms = data.get('rooms', [])

        has_more = data.get('has_more', False)
        page_token = data.get('page_token', '')
        room_list = [make_datatype(Room, i) for i in rooms]
        return has_more, page_token, room_list
Esempio n. 26
0
    def _gen_calendar_event(self, data):
        """生成 CalendarEvent

        :rtype CalendarEvent
        """
        return CalendarEvent(id=data.get('id', ''),
                             summary=data.get('summary', ''),
                             description=data.get('description', ''),
                             start=data.get('start', {}).get('time_stamp', 0),
                             end=data.get('end', {}).get('time_stamp', 0),
                             visibility=CalendarEventVisibility(
                                 data.get('visibility', 'default')),
                             attendees=[
                                 make_datatype(CalendarAttendee, i)
                                 for i in (data.get('attendees') or [])
                             ])
Esempio n. 27
0
    def batch_get_room_freebusy(self, room_ids, time_min, time_max):
        """会议室忙闲查询

        :type self: OpenLark
        :param room_ids: 用于查询指定会议室的ID列表
        :type room_ids: list[str]
        :param time_min: 查询会议室忙闲的起始时间
        :type time_min: datetime.datetime
        :param time_max: 查询会议室忙闲的结束时间
        :type time_max: datetime.datetime
        :return: 查询会议室忙闲的起始时间(与请求参数完全相同), 查询会议室忙闲的结束时间(与请求参数完全相同), Dict['会议室ID', List[忙碌时间]]
        :rtype: (datetime.datetime, datetime.datetime, dict[str, list[RoomFreeBusy]])

        https://open.feishu.cn/document/ukTMukTMukTM/uIDOyUjLygjM14iM4ITN
        """
        tz = TimeZoneChina()
        params = {
            'room_ids': room_ids,
            'time_min': datetime_format_rfc3339(time_min, tz),
            'time_max': datetime_format_rfc3339(time_max, tz)
        }

        url = self._gen_request_url(
            '/open-apis/meeting_room/freebusy/batch_get?{}'.format(
                urlencode(params, doseq=True)))

        res = self._get(url, with_tenant_token=True)
        data = res.get('data', {})
        time_min = data.get('time_min')
        time_max = data.get('time_max')
        if time_min:
            time_min = parser.parse(time_min)
        if time_max:
            time_max = parser.parse(time_max)

        return time_min, time_max, {
            k: [
                RoomFreeBusy(start_time=parser.parse(i.get('start_time')),
                             end_time=parser.parse(i.get('end_time')),
                             uid=i.get('uid'),
                             original_time=i.get('original_time'),
                             organizer_info=make_datatype(
                                 SimpleUser, i.get('organizer_info')))
                for i in v
            ]
            for k, v in data.get('free_busy', {}).items()
        }
Esempio n. 28
0
    def batch_get_department_detail_user(self, user_ids=None, open_ids=None):
        """批量获取用户详细信息

        :type self: OpenLark
        :param user_ids: 用户 UserID 列表
        :type user_ids: list[str]
        :param open_ids: 用户 OpenID 列表
        :type open_ids: list[str]
        :return: has_more, page_token, departments
        :rtype: (Dict[str, DepartmentUser], Dict[str, OpenLarkException])

        批量获取用户信息详情,需具有用户所在部门或者用户的通讯录权限。

        https://open.feishu.cn/document/ukTMukTMukTM/ugjNz4CO2MjL4YzM

        https://bytedance.feishu.cn/docs/doccnOcR1fnxBACchoY9tlg7Amg#
        """
        if user_ids and open_ids:
            raise LarkInvalidArguments(msg='only need user_ids or open_ids')
        elif not user_ids and not open_ids:
            raise LarkInvalidArguments(msg='need user_ids or open_ids')

        qs = ''
        user_key = ''
        if user_ids:
            qs = '&'.join(['user_ids={}'.format(i) for i in user_ids])
            user_key = 'user_id'
        elif open_ids:
            qs = '&'.join(['open_ids={}'.format(i) for i in open_ids])
            user_key = 'open_id'

        url = self._gen_request_url('/open-apis/contact/v2/user/batch_get')
        url = url + '?' + qs
        res = self._get(url, with_tenant_token=True)
        data = res['data']

        users = {}  # type: Dict[str, DepartmentUser]
        for i in data.get('users', []):
            user = make_datatype(DepartmentUser, i)
            users[getattr(user, user_key)] = user

        errors = {}  # type: Dict[str, OpenLarkException]
        for i in data.get('errors', []):
            e = gen_exception(code=i.get('code'), url='', msg=i.get('msg'))
            errors[i.get('id', '')] = e
        return users, errors
Esempio n. 29
0
    def oauth_get_user(self, user_access_token):
        """获取用户信息

        :type self: OpenLark
        :param user_access_token:

        此接口仅用于获取登录用户的信息。 调用此接口需要在 Header 中带上 user_access_token。

        https://open.feishu.cn/document/ukTMukTMukTM/uIDO4UjLygDO14iM4gTN
        """
        url = self._gen_request_url('/open-apis/authen/v1/user_info')
        res = self._get(url, auth_token=user_access_token)
        data = res['data']

        data['avatar'] = data.get('avatar') or data.get('avatar_url')
        data['user_id'] = data.get('user_id') or data.get('employee_id')
        return make_datatype(User, data)
Esempio n. 30
0
    def get_installed_apps(self,
                           page_size=20,
                           page_token='',
                           lang=I18NType.zh_cn,
                           status=-1):
        """获取企业安装的应用

        :type self: OpenLark
        :param page_size: 本次拉取用户列表最大个数(最大值 1000 ,0 自动最大个数 )
        :type page_size: int
        :param page_token: 分页拉取用户列表起始位置标示,不填表示从头开始
        :type page_token: str
        :param lang: 优先展示的应用信息的语言版本(zh_cn:中文,en_us:英文,ja_jp:日文)
        :type lang: I18NType
        :param status: 要返回的应用的状态,0:停用;1:启用;-1:全部
        :type status: int
        :return: 是否还有更多, page_token, page_size, 总数, 语言, 应用列表
        :rtype: (bool, str, int, int, I18NType, list[App])

        该接口用于查询企业安装的应用列表,只能被企业自建应用调用且需要“获取应用信息”权限。

        https://open.feishu.cn/document/ukTMukTMukTM/uYDN3UjL2QzN14iN0cTN
        """
        url = self._gen_request_url('/open-apis/application/v3/app/list?')
        if page_token:
            url = '{}&page_token={}'.format(url, page_token)
        if page_size:
            url = '{}&page_size={}'.format(url, page_size)
        if lang:
            url = '{}&lang={}'.format(url, converter_enum(lang))
        if status in [0, 1, -1]:
            url = '{}&status={}'.format(url, status)

        res = self._get(url, with_tenant_token=True)
        data = res['data']

        apps = [make_datatype(App, i)
                for i in data.get('app_list', [])]  # type: List[App]

        has_more = bool(data.get('has_more', False))  # type: bool
        lang = I18NType(data.get('lang', 'zh_cn'))  # type: I18NType
        page_size = data.get('page_size')  # type: int
        page_token = data.get('page_token')  # type: str
        total_count = data.get('total_count')  # type: int
        return has_more, page_token, page_size, total_count, lang, apps