def _get(self, url, data=None): """封装底层get请求""" try: response = None for x in range(3): try: response = requests.get(url, params=data, headers=self.headers, timeout=5) except Exception as e: time.sleep(1) if x == 2: raise e else: break logger.info( 'Feishu get response. url={},data={},response={}'.format( url, data, response.text)) return json.loads(response.text) except requests.exceptions.Timeout: logger.error("Feishu get timeout! url={0} data={1}".format( url, data)) raise FeishuException('飞书接口get请求超时,请重试') except Exception as e: logger.error( "Feishu get msg fail! url={0} data={1} error by {2}".format( url, data, e)) raise FeishuException(e)
def __check_approval_status(self, result, instance_code, oper): """ 检查审批状态,如果状态与操作不一致,则提示用户 :param result:飞书的返回状态是65001,内部错误,所以判断一下是不是飞书状态跟操作不一致 :param instance_code:审批任务id :param oper:执行的操作 :return: """ if result['code'] == 65001: try: approval_info = self.get_approval_info(instance_code) if approval_info['status'] == oper: return True elif approval_info['status'] == 'APPROVED': raise FeishuException('飞书审批已经通过,无法进行审批{}'.format( OPER_DICT[oper])) elif approval_info['status'] == 'REJECTED': raise FeishuException('飞书审批已经拒绝,无法进行审批{}'.format( OPER_DICT[oper])) elif approval_info['status'] == 'CANCELED': raise FeishuException('飞书审批已经撤回,无法进行审批{}'.format( OPER_DICT[oper])) elif approval_info['status'] == 'DELETED': raise FeishuException('飞书审批已经删除,无法进行审批{}'.format( OPER_DICT[oper])) else: return False except Exception as ex: logger.error( "Feishu check status approval fail! result={0},instance_code={1},oper{2},error by {3}" .format(result, instance_code, oper, ex)) raise FeishuException(ex)
def _post(self, url, data): """封装底层post请求""" data_to_send = json.dumps(data).encode("utf-8") try: response = None for x in range(3): try: response = requests.post(url, data=data_to_send, headers=self.headers, timeout=5) except Exception as e: time.sleep(1) if x == 2: raise e else: break logger.info( 'Feishu post response. url={},data={},response={}'.format( url, data, response.text)) return json.loads(response.text) except requests.exceptions.Timeout: logger.error("Feishu post timeout! url={0} data={1}".format( url, data)) raise FeishuException('飞书接口post请求超时,请重试') except Exception as e: logger.error( "Feishu post msg fail! url={0} data={1} error by {2}".format( url, data, e)) raise FeishuException(e)
def approval_revoke(self, approval_code, instance_code, apply_user_id): """ 审批撤回 :param approval_code: 审批流程唯一标识 :param instance_code: 审批任务id :param apply_user_id: 申请人id :return: """ try: result = self._post( self.__host_url + '/approval/openapi/v2/instance/cancel', { 'approval_code': approval_code, 'instance_code': instance_code, 'user_id': apply_user_id }) if result['code'] != 0: if self.__check_approval_status(result, instance_code, 'CANCELED'): return 'repeat' else: raise FeishuException('飞书撤回审批失败,错误信息:{},请联系管理员处理'.format( result['msg'])) else: return 'success' except Exception as ex: logger.error( "Feishu approval revoke fail! instance_code={0},error by {1}". format(instance_code, ex)) raise FeishuException(ex)
def get_user_info(self, user_open_id): """ 获取用户的个人信息,只能通过open_id获取 (https://open.feishu.cn/document/ukTMukTMukTM/uIzNz4iM3MjLyczM) :param user_open_id: 用户在飞书的唯一标识,可以通过方法self.get_user_id_info获取用户的open_id标识 :return: "user_infos":[ { "name":"zhang san", "name_py":"zhang san", "en_name":"John", "employee_id":"a0615a67", "employee_no":"235634", "open_id":"ou_e03053f0541cecc3269d7a9dc34a0b21", "union_id":"on_7dba11ff38a2119f89349876b12af65c", ....... } """ try: user_info = self._get( self.__opes_url + '/open-apis/contact/v1/user/batch_get', {'open_ids': user_open_id}) if user_info['code'] == 0: return user_info['data']['user_infos'][0] else: raise FeishuException('获取该用户飞书个人信息失败,请联系管理员处理') except Exception as ex: logger.error( "Feishu get user info fail! user_open_id={0} error by {1}". format(user_open_id, ex)) raise FeishuException(ex)
def get_user_id_info(self, user_code, email_type='@company.com'): """ 通过邮箱获取用户的飞书唯一标识 ,(https://open.feishu.cn/document/ukTMukTMukTM/uUzMyUjL1MjM14SNzITN) :param user_code: company id :param email_type: 邮箱类型,主要是有些用户不是绑定的company.com :return: { "open_id": "ou_979112345678741d29069abcdef089d4", "user_id": "a7eb3abe" }, """ try: email_code = user_code + email_type result = self._get( self.__opes_url + '/open-apis/user/v1/batch_get_id', {'emails': email_code}) if 'email_users' in result['data'].keys(): user_info = result['data']['email_users'][email_code][0] return user_info else: raise FeishuException('飞书账号未与邮箱绑定,请联系飞书管理员绑定邮箱') except Exception as ex: logger.error( "Feishu get user id info fail! user_code={0} error by {1}". format(user_code, ex)) raise FeishuException(ex)
def send_user_msg(self, user_code, content, type='text'): """ 给用户发送消息 :param user_code: company id ,前提是飞书必须跟邮箱绑定在一起,是否绑定可以去查看飞书的个人信息简介 :param content: 发送消息内容 :param type: 发送消息的类型,text,是纯文本消息(https://open.feishu.cn/document/ukTMukTMukTM/uUjNz4SN2MjL1YzM), card,是卡片消息。(https://open.feishu.cn/document/ukTMukTMukTM/uYTNwUjL2UDM14iN1ATN) :return: """ try: result = {'code': -1} if type == 'text': result = self._send_msg( data={ 'email': user_code + "@company.com", 'msg_type': 'text', "content": { "text": content } }) elif type == 'card': result = self._send_msg( data={ 'email': user_code + "@company.com", 'msg_type': 'interactive', 'card': content }) if result['code'] != 0: logger.error("Send user msg fail! result={0}".format(result)) raise FeishuException(result) except Exception as e: raise FeishuException(e)
def approval_create(self, approval_code, apply_user_id, data, approval_user_id=None, approval_node_id=None): """ 临时发布申请创建审批 :param approval_code: 审批流程唯一标识 :param apply_user_id: 申请人 :param data: 表单数据 :param approval_user_id:审批人,不传默认领导审批 :param approval_node_id:审批节点id,不传默认领导审批 :return: """ try: print(data) approval_data = { "approval_code": approval_code, "user_id": apply_user_id, "form": json.dumps(data), } if approval_user_id: approval_data['node_approver_user_id_list'] = { approval_node_id: [approval_user_id], "manager_node_id": [approval_user_id] } print(approval_data) result = self._post( self.__host_url + '/approval/openapi/v2/instance/create', approval_data) if result['code'] != 0: raise FeishuException('飞书创建审批失败,错误信息:{},请联系管理员处理'.format( result['msg'])) else: return result['data'] except Exception as ex: logger.error( "Feishu approval create fail! approval_code={0},apply_user_id={1},data={2},approval_user_id={3} error by {4}" .format(approval_code, apply_user_id, data, approval_user_id, ex)) raise FeishuException(ex)
def get_approval_info(self, instance_code): """ 获取审批实例详情 :param instance_code:审批任务id :return: """ try: result = self._post( self.__host_url + '/approval/openapi/v2/instance/get', {'instance_code': instance_code}) if result['code'] == 0: return result['data'] else: raise FeishuException('飞书获取审批实例详情失败,错误信息是:{0},请联系管理员处理'.format( result['msg'])) except Exception as ex: logger.error( "Feishu approval revoke fail! instance_code={0},error by {1}". format(instance_code, ex)) raise FeishuException(ex)
def get_department_info(self, open_department_id): try: department = self._get( self.__opes_url + '/open-apis/contact/v1/department/info/get', {'open_department_id': open_department_id}) return department except Exception as ex: logger.error( "Feishu get department info fail! open_department_id={0} error by {1}" .format(open_department_id, ex)) raise FeishuException(ex)
def send_user_msg_many(self, open_ids, content, type='text'): """ 给多个用户发送消息,一次性只能发送200个人,所以下面做了循环发送 :param open_ids: 用户的飞书唯一标识列表,[,,,,] :param content: 发送消息内容 :param type: 发送消息的类型,text,是纯文本消息(https://open.feishu.cn/document/ukTMukTMukTM/uUjNz4SN2MjL1YzM), card,是卡片消息。(https://open.feishu.cn/document/ukTMukTMukTM/uYTNwUjL2UDM14iN1ATN) :return: """ try: for i in range(0, len(open_ids), 199): result = {'code': -1} if type == 'text': result = self._post(self.__opes_url + '/open-apis/message/v4/batch_send/', data={ 'open_ids': open_ids[i:i + 199], 'msg_type': 'text', "content": { "text": content } }) elif type == 'card': result = self._post(self.__opes_url + '/open-apis/message/v4/batch_send/', data={ 'open_ids': open_ids[i:i + 199], 'msg_type': 'interactive', 'card': content }) if result['code'] != 0: logger.error( "Send user msg many fail! result={0}".format(result)) raise FeishuException('发送成功{}条,发送失败{}条,错误信息:{}'.format( i + 199, len(open_ids) - i - 199, str(result))) except Exception as e: raise FeishuException(e)
def _get_tenant_access_token(self): """认证接口""" try: response = requests.post( self.__opes_url + '/open-apis/auth/v3/app_access_token/internal/', data={ 'app_id': self.__app_id, 'app_secret': self.__app_secret }) app_access_token = json.loads(response.text)['app_access_token'] return app_access_token except Exception as e: logger.error("Feishu get tenant_access_token fail!") raise FeishuException(e)