Files
jianxin 1a46343419
continuous-integration/drone/push Build is failing
first commit
2025-02-18 10:40:39 +08:00

359 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import requests
import http
import json
from typing import Dict, List, Optional
import hashlib
import base64
import hmac
import time
import lark_oapi as lark
from lark_oapi.api.bitable.v1 import *
# 配置参数
CONFIG = {
"BASE_URL": "https://boss-api.shadow-rpa.net/boss/api/v1",
"TOKEN": "234e69f9-939d-44e0-8b8f-1560ba77e375",
"newDutyUuid": "a16edf00-c99f-44ae-bbb9-56d3e3629386",
"BITTABLE_APP_ID": "cli_a5110cd1da70900b",
"BITTABLE_APP_SECRET": "XwTUlUq7dGK1XexUS7ljShJJ7t1MIwXF",
"BITTABLE_APP_TOKEN": "KppebaHOsaHGMxslbLRc82amn5f",
"BITTABLE_APP_TABLE_ID": "tblH8KwM2grwj9DI",
}
def gen_sign(timestamp, secret):
# 拼接timestamp和secret
string_to_sign = "{}\n{}".format(timestamp, secret)
hmac_code = hmac.new(
string_to_sign.encode("utf-8"), digestmod=hashlib.sha256
).digest()
# 对结果进行base64处理
sign = base64.b64encode(hmac_code).decode("utf-8")
return sign
def get_headers(token: Optional[str] = None) -> Dict:
"""
生成请求头
Args:
token: 认证token,如果为None则使用默认配置
Returns:
包含认证信息的请求头字典
"""
return {
"accept": "application/json",
"authorization": f"Bearer {token or CONFIG['TOKEN']}",
"content-type": "application/json;charset=UTF-8",
"origin": "https://boss.shadow-rpa.net",
"referer": "https://boss.shadow-rpa.net/",
}
def api_request(method: str, endpoint: str, **kwargs) -> Dict:
"""
统一的API请求处理
Args:
method: 请求方法(GET/POST)
endpoint: API端点
**kwargs: 请求参数
Returns:
API响应数据
"""
url = f"{CONFIG['BASE_URL']}/{endpoint}"
response = requests.request(method, url, **kwargs)
response.raise_for_status()
return response.json()
def get_all_work_orders(page_size: int = 20, status: str = "new") -> List:
"""
获取所有工单列表
Args:
page_size: 每页数量
status: 工单状态
Returns:
工单列表
"""
all_orders = []
page = 1
while True:
params = {"page": page, "size": page_size, "status": status}
res = api_request(
"GET", "wo/order/woList", headers=get_headers(), params=params
)
data = res.get("data", {})
print(data)
pages = res.get("page", {}).get("pages", 0)
if not data:
break
all_orders.extend(data)
if page >= pages:
break
page += 1
# print(all_orders)
return all_orders
def process_work_order(order: Dict) -> Dict:
"""
处理单个工单信息,获取提交者信息
Args:
order: 工单数据
Returns:
处理后的工单信息
"""
result = {
"uuid": order["uuid"],
"title": order.get("title", "").strip(),
"content": order.get("content", "").strip(),
"submitter": order.get("submitterId"),
"enterprise": order.get("enterpriseName"),
"phone": None,
}
# 获取提交者信息
if result["submitter"]:
user_info = query_user_info(result["submitter"])
user_data = user_info.get("data", {}).get("dataSource", {}).get("data", [])
# 查找匹配的用户数据
matched_user = next(
(x for x in user_data if x.get("userName") == result["submitter"]), None
)
# 解密电话号码
if matched_user and matched_user.get("phone__l"):
encrypt_phone = matched_user["phone__l"].get("encrypte")
if encrypt_phone:
decrypted = decrypt_sensitive_data(encrypt_phone)
result["phone"] = decrypted.get("data", {}).get("decryptValue")
return result
def claim_work_order(work_order_uuid):
url = "https://boss-api.shadow-rpa.net/boss/api/v1/wo/order/claim"
headers = get_headers()
data = {"workOrderUuid": work_order_uuid}
response = requests.post(url, headers=headers, json=data)
return response.json()
def transfer_work_order(work_order_uuid, new_duty_uuid, remark=""):
url = "https://boss-api.shadow-rpa.net/boss/api/v1/wo/order/transfer"
headers = get_headers()
data = {
"workOrderUuid": work_order_uuid,
"remark": remark,
"newDutyUuid": new_duty_uuid,
}
response = requests.post(url, headers=headers, json=data)
return response.json()
def query_user_info(
fieldValues, table_code="userInfoList", page=1, size=20, filters=None
):
if filters is None:
filters = [
{"fieldName": "userName", "fieldValues": fieldValues, "filterType": "WD"}
]
url = "https://boss-api.shadow-rpa.net/boss/api/v1/sys/universal/component/table"
headers = get_headers()
data = {
"source": "",
"tableCode": table_code,
"pageQuery": {"page": page, "size": size, "filters": filters, "orders": []},
}
response = requests.post(url, headers=headers, json=data)
return response.json()
def decrypt_sensitive_data(
encrypt_value, code="userInfoList", code_type="table", field_name="phone"
):
url = (
f"https://boss-api.shadow-rpa.net/boss/api/v1/sys/operateLog/sensitiveDataQuery"
)
headers = get_headers()
params = {
"encryptValue": encrypt_value,
"code": code,
"codeType": code_type,
"fieldName": field_name,
}
response = requests.get(url, headers=headers, params=params)
return response.json()
def feishu_notify(text):
webhook_url = "/open-apis/bot/v2/hook/d6551d8c-d792-46e4-929d-ad0d1b031027"
keys = "lPQwjE2TqMD0Q4q0zDlUY"
conn = http.client.HTTPSConnection("open.feishu.cn")
payload = json.dumps({"msg_type": "text", "content": {"text": text}})
headers = {"Content-Type": "application/json"}
conn.request("POST", webhook_url, payload, headers)
res = conn.getresponse()
data = res.read()
def add_bittable_record(data):
# 创建client
client = (
lark.Client.builder()
.app_id(CONFIG["BITTABLE_APP_ID"])
.app_secret(CONFIG["BITTABLE_APP_SECRET"])
.log_level(lark.LogLevel.DEBUG)
.build()
)
# 构造请求对象
request: CreateAppTableRecordRequest = (
CreateAppTableRecordRequest.builder()
.app_token(CONFIG["BITTABLE_APP_TOKEN"])
.table_id(CONFIG["BITTABLE_APP_TABLE_ID"])
.request_body(AppTableRecord.builder().fields(data).build())
.build()
)
# 发起请求
response: CreateAppTableRecordResponse = client.bitable.v1.app_table_record.create(
request
)
# 处理失败返回
if not response.success():
lark.logger.error(
f"client.bitable.v1.app_table_record.create failed, code: {response.code}, msg: {response.msg}, log_id: {response.get_log_id()}, resp: \n{json.dumps(json.loads(response.raw.content), indent=4, ensure_ascii=False)}"
)
return
# 处理业务结果
lark.logger.info(lark.JSON.marshal(response.data, indent=4))
def feishu_notify_to_xbot_content_group(text):
webhook_url = "/open-apis/bot/v2/hook/b2db78ac-799a-4251-8341-18d49091fa62"
Keys = "T8phm8fcfZ04bvOHaI0kzd"
timestamp = str(int(time.time()))[:10]
# print(timestamp)
sign = gen_sign(timestamp, Keys)
conn = http.client.HTTPSConnection("open.feishu.cn")
payload = json.dumps(
{
"msg_type": "text",
"content": {"text": text},
"sign": sign,
"timestamp": timestamp,
}
)
headers = {"Content-Type": "application/json"}
conn.request("POST", webhook_url, payload, headers)
res = conn.getresponse()
data = res.read()
print(data)
def main():
"""
主程序入口
"""
messages = []
message_without_phone = []
try:
# 获取所有工单
work_orders = get_all_work_orders()
if not work_orders:
print("未找到工单")
# 处理每个工单
print(f"共找到 {len(work_orders)} 个工单")
for order in work_orders:
processed_order = process_work_order(order)
# 打印工单信息
print(f"\n{'='*40}")
print(f"工单ID: {processed_order['uuid']}")
print(f"标题: {processed_order['title']}")
print(f"内容: {processed_order['content']}")
print(f"提交者: {processed_order['submitter']}")
print(f"电话: {processed_order['phone'] or '未找到'}")
print(f"企业: {processed_order['enterprise'] or '未找到'}")
print(
f"邀约: 我是影刀的小伙伴, 想跟你沟通反馈的需求: {processed_order['content']}"
)
print(f"{'='*40}\n")
# 剑心通知
messages.append(
# f"工单ID: {processed_order['uuid']}\n"
f"提交者: {processed_order['submitter']}\n"
f"标题: {processed_order['title']}\n"
f"电话: {processed_order['phone']}\n"
f"企业: {processed_order['enterprise'] or '未找到'}\n"
f"邀约: 我是影刀的小伙伴, 想跟你沟通反馈的需求: {processed_order['content']}\n\n"
)
# 群组通知
message_without_phone.append(
f"工单ID: {processed_order['uuid']}\n"
f"提交者: {processed_order['submitter']}\n"
f"标题: {processed_order['title']}\n"
f"企业: {processed_order['enterprise'] or '未找到'}\n"
)
# 表格同步
data = {
"企业": processed_order["enterprise"] or "社区版",
"手机号": processed_order["phone"],
"提交人": processed_order["submitter"],
"需求ID": processed_order["uuid"],
"需求内容": processed_order["content"],
"需求标题": processed_order["title"],
}
add_bittable_record(data)
transfer_work_order(
processed_order["uuid"],
CONFIG.get("newDutyUuid"),
)
# break
# messages = "\n".join(messages)
# if messages.strip():
# print("发送飞书通知")
# feishu_notify(text=messages)
# message_without_phone = "\n".join(message_without_phone)
# if message_without_phone.strip():
# print("发送飞书通知到内容组")
# feishu_notify_to_xbot_content_group(text=message_without_phone)
except requests.RequestException as e:
print(f"请求发生错误: {e}")
except Exception as e:
print(f"程序发生错误: {e}")
if __name__ == "__main__":
main()