# encoding:utf-8 """ wechat channel """ import io import json import os import threading import time import requests from bridge.context import * from bridge.reply import * from channel.chat_channel import ChatChannel from channel import chat_channel from channel.wechat.wechat_message import * from common.expired_dict import ExpiredDict from common.log import logger from common.singleton import singleton from common.time_check import time_checker from common.utils import convert_webp_to_png from config import conf, get_appdata_dir # from lib import itchat # from lib.itchat.content import * # import itchat # from itchat.content import * from urllib.parse import urlparse import asyncio import threading from common import kafka_helper, redis_helper from confluent_kafka import Consumer, KafkaException import json,time,re import pickle from datetime import datetime import oss2 import random # from common.kafka_client import KafkaClient # @itchat.msg_register([TEXT, VOICE, PICTURE, NOTE, ATTACHMENT, SHARING]) # def handler_single_msg(msg): # try: # cmsg = WechatMessage(msg, False) # except NotImplementedError as e: # logger.debug("[WX]single message {} skipped: {}".format(msg["MsgId"], e)) # return None # WechatChannel().handle_single(cmsg) # return None # @itchat.msg_register([TEXT, VOICE, PICTURE, NOTE, ATTACHMENT, SHARING], isGroupChat=True) # def handler_group_msg(msg): # try: # cmsg = WechatMessage(msg, True) # except NotImplementedError as e: # logger.debug("[WX]group message {} skipped: {}".format(msg["MsgId"], e)) # return None # WechatChannel().handle_group(cmsg) # return None # def _check(func): # def wrapper(self, cmsg: ChatMessage): # msgId = cmsg.msg_id # if msgId in self.receivedMsgs: # logger.info("Wechat message {} already received, ignore".format(msgId)) # return # self.receivedMsgs[msgId] = True # create_time = cmsg.create_time # 消息时间戳 # if conf().get("hot_reload") == True and int(create_time) < int(time.time()) - 60: # 跳过1分钟前的历史消息 # logger.debug("[WX]history message {} skipped".format(msgId)) # return # if cmsg.my_msg and not cmsg.is_group: # logger.debug("[WX]my message {} skipped".format(msgId)) # return # return func(self, cmsg) # return wrapper # # 可用的二维码生成接口 # # https://api.qrserver.com/v1/create-qr-code/?size=400×400&data=https://www.abc.com # # https://api.isoyu.com/qr/?m=1&e=L&p=20&url=https://www.abc.com # def qrCallback(uuid, status, qrcode): # # logger.debug("qrCallback: {} {}".format(uuid,status)) # if status == "0": # try: # from PIL import Image # img = Image.open(io.BytesIO(qrcode)) # _thread = threading.Thread(target=img.show, args=("QRCode",)) # _thread.setDaemon(True) # _thread.start() # except Exception as e: # pass # import qrcode # url = f"https://login.weixin.qq.com/l/{uuid}" # qr_api1 = "https://api.isoyu.com/qr/?m=1&e=L&p=20&url={}".format(url) # qr_api2 = "https://api.qrserver.com/v1/create-qr-code/?size=400×400&data={}".format(url) # qr_api3 = "https://api.pwmqr.com/qrcode/create/?url={}".format(url) # qr_api4 = "https://my.tv.sohu.com/user/a/wvideo/getQRCode.do?text={}".format(url) # print("You can also scan QRCode in any website below:") # print(qr_api3) # print(qr_api4) # print(qr_api2) # print(qr_api1) # _send_qr_code([qr_api3, qr_api4, qr_api2, qr_api1]) # qr = qrcode.QRCode(border=1) # qr.add_data(url) # qr.make(fit=True) # qr.print_ascii(invert=True) @singleton class WechatChannel(ChatChannel): NOT_SUPPORT_REPLYTYPE = [] def __init__(self): super().__init__() self.receivedMsgs = ExpiredDict(conf().get("expires_in_seconds", 3600)) self.auto_login_times = 0 def startup(self): try: print('启动') # itchat.instance.receivingRetryCount = 600 # 修改断线超时时间 # # login by scan QRCode # hotReload = conf().get("hot_reload", False) # status_path = os.path.join(get_appdata_dir(), "itchat","itchat.pkl") # # with open(status_path, 'rb') as file: # # data = pickle.load(file) # # logger.info(data) # itchat.auto_login( # enableCmdQR=2, # hotReload=hotReload, # statusStorageDir=status_path, # qrCallback=qrCallback, # exitCallback=self.exitCallback, # loginCallback=self.loginCallback # ) # self.user_id = itchat.instance.storageClass.userName # self.name = itchat.instance.storageClass.nickName # logger.info("Wechat login success, user_id: {}, nickname: {}".format(self.user_id, self.name)) # # 创建一个线程来运行 consume_messages # kafka_thread = threading.Thread(target=kafka_helper.kafka_client.consume_messages, args=(wx_messages_process_callback, self.name)) # kafka_thread.start() # logger.info("启动kafka") # # 好友定时同步 # agent_nickname=self.name # friend_thread =threading.Thread(target=ten_mins_change_save_friends, args=(agent_nickname,)) # friend_thread.start() # # 立刻同步 # agent_info=fetch_agent_info(agent_nickname) # agent_tel=agent_info.get("agent_tel",None) # # friends=itchat.get_contact(update=True)[1:] # friends=itchat.get_friends(update=True)[1:] # save_friends_to_redis(agent_tel,agent_nickname, friends) # logger.info("启动好友同步") # # start message listener # logger.info("启动itchat") # itchat.run() except Exception as e: logger.exception(e) # def exitCallback(self): # print('主动退出') # try: # from common.linkai_client import chat_client # if chat_client.client_id and conf().get("use_linkai"): # print('退出') # _send_logout() # time.sleep(2) # self.auto_login_times += 1 # if self.auto_login_times < 100: # chat_channel.handler_pool._shutdown = False # self.startup() # except Exception as e: # pass # def loginCallback(self): # logger.debug("Login success") # print('登录成功') # # 同步 # _send_login_success() # # handle_* 系列函数处理收到的消息后构造Context,然后传入produce函数中处理Context和发送回复 # # Context包含了消息的所有信息,包括以下属性 # # type 消息类型, 包括TEXT、VOICE、IMAGE_CREATE # # content 消息内容,如果是TEXT类型,content就是文本内容,如果是VOICE类型,content就是语音文件名,如果是IMAGE_CREATE类型,content就是图片生成命令 # # kwargs 附加参数字典,包含以下的key: # # session_id: 会话id # # isgroup: 是否是群聊 # # receiver: 需要回复的对象 # # msg: ChatMessage消息对象 # # origin_ctype: 原始消息类型,语音转文字后,私聊时如果匹配前缀失败,会根据初始消息是否是语音来放宽触发规则 # # desire_rtype: 希望回复类型,默认是文本回复,设置为ReplyType.VOICE是语音回复 # @time_checker # @_check # def handle_single(self, cmsg: ChatMessage): # # filter system message # if cmsg.other_user_id in ["weixin"]: # return # if cmsg.ctype == ContextType.VOICE: # if conf().get("speech_recognition") != True: # return # logger.debug("[WX]receive voice msg: {}".format(cmsg.content)) # elif cmsg.ctype == ContextType.IMAGE: # logger.debug("[WX]receive image msg: {}".format(cmsg.content)) # elif cmsg.ctype == ContextType.PATPAT: # logger.debug("[WX]receive patpat msg: {}".format(cmsg.content)) # elif cmsg.ctype == ContextType.TEXT: # logger.debug("[WX]receive text msg: {}, cmsg={}".format(json.dumps(cmsg._rawmsg, ensure_ascii=False), cmsg)) # # content = cmsg.content # 消息内容 # # from_user_nickname = cmsg.from_user_nickname # 发送方昵称 # # to_user_nickname = cmsg.to_user_nickname # 接收方昵称 # # wx_content_dialogue_message=[{"type": "text", "text": content}] # # message=dialogue_message(from_user_nickname,to_user_nickname,wx_content_dialogue_message) # # kafka_helper.kafka_client.produce_message(message) # # logger.info("发送对话 %s", json.dumps(message, ensure_ascii=False)) # input_content = cmsg.content # input_from_user_nickname = cmsg.from_user_nickname # input_to_user_nickname = cmsg.to_user_nickname # input_wx_content_dialogue_message=[{"type": "text", "text": input_content}] # input_message=dialogue_message(input_from_user_nickname,input_to_user_nickname,input_wx_content_dialogue_message) # kafka_helper.kafka_client.produce_message(input_message) # logger.info("发送对话 %s",input_message) # else: # logger.debug("[WX]receive msg: {}, cmsg={}".format(cmsg.content, cmsg)) # context = self._compose_context(cmsg.ctype, cmsg.content, isgroup=False, msg=cmsg) # if context: # self.produce(context) # @time_checker # @_check # def handle_group(self, cmsg: ChatMessage): # if cmsg.ctype == ContextType.VOICE: # if conf().get("group_speech_recognition") != True: # return # logger.debug("[WX]receive voice for group msg: {}".format(cmsg.content)) # elif cmsg.ctype == ContextType.IMAGE: # logger.debug("[WX]receive image for group msg: {}".format(cmsg.content)) # elif cmsg.ctype in [ContextType.JOIN_GROUP, ContextType.PATPAT, ContextType.ACCEPT_FRIEND, ContextType.EXIT_GROUP]: # logger.debug("[WX]receive note msg: {}".format(cmsg.content)) # elif cmsg.ctype == ContextType.TEXT: # # logger.debug("[WX]receive group msg: {}, cmsg={}".format(json.dumps(cmsg._rawmsg, ensure_ascii=False), cmsg)) # pass # elif cmsg.ctype == ContextType.FILE: # logger.debug(f"[WX]receive attachment msg, file_name={cmsg.content}") # else: # logger.debug("[WX]receive group msg: {}".format(cmsg.content)) # context = self._compose_context(cmsg.ctype, cmsg.content, isgroup=True, msg=cmsg, no_need_at=conf().get("no_need_at", False)) # if context: # self.produce(context) # # 统一的发送函数,每个Channel自行实现,根据reply的type字段发送不同类型的消息 # def send(self, reply: Reply, context: Context): receiver = context["receiver"] if reply.type == ReplyType.TEXT: sent_res=itchat.send(reply.content, toUserName=receiver) logger.info("[WX] sendMsg={}, receiver={} {}".format(reply, receiver,sent_res.get('BaseResponse',{}).get('RawMsg'))) msg: ChatMessage = context["msg"] is_group=msg.is_group if not is_group: # 响应用户 output_content=reply.content output_from_user_nickname=msg.to_user_nickname # 回复翻转 output_to_user_nickname=msg.from_user_nickname # 回复翻转 output_wx_content_dialogue_message=[{"type": "text", "text": output_content}] output_message=dialogue_message(output_from_user_nickname,output_to_user_nickname,output_wx_content_dialogue_message) kafka_helper.kafka_client.produce_message(output_message) logger.info("发送对话 %s", output_message) elif reply.type == ReplyType.ERROR or reply.type == ReplyType.INFO: itchat.send(reply.content, toUserName=receiver) logger.info("[WX] sendMsg={}, receiver={}".format(reply, receiver)) elif reply.type == ReplyType.VOICE: itchat.send_file(reply.content, toUserName=receiver) logger.info("[WX] sendFile={}, receiver={}".format(reply.content, receiver)) elif reply.type == ReplyType.IMAGE_URL: # 从网络下载图片 img_url = reply.content logger.debug(f"[WX] start download image, img_url={img_url}") pic_res = requests.get(img_url, stream=True) image_storage = io.BytesIO() size = 0 for block in pic_res.iter_content(1024): size += len(block) image_storage.write(block) logger.info(f"[WX] download image success, size={size}, img_url={img_url}") image_storage.seek(0) if ".webp" in img_url: try: image_storage = convert_webp_to_png(image_storage) except Exception as e: logger.error(f"Failed to convert image: {e}") return itchat.send_image(image_storage, toUserName=receiver) logger.info("[WX] sendImage url={}, receiver={}".format(img_url, receiver)) elif reply.type == ReplyType.IMAGE: # 从文件读取图片 image_storage = reply.content image_storage.seek(0) itchat.send_image(image_storage, toUserName=receiver) logger.info("[WX] sendImage, receiver={}".format(receiver)) elif reply.type == ReplyType.FILE: # 新增文件回复类型 file_storage = reply.content itchat.send_file(file_storage, toUserName=receiver) logger.info("[WX] sendFile, receiver={}".format(receiver)) # msg: ChatMessage = context["msg"] # # content=msg["content"] # is_group=msg.is_group # if not is_group: # # print(f'响应:{content}') # # 用户输入 # # input_content=msg.content # # input_from_user_nickname=msg.from_user_nickname # # input_to_user_nickname=msg.to_user_nickname # # input_wx_content_dialogue_message=[{"type": "text", "text": input_content}] # # input_message=dialogue_message(input_from_user_nickname,input_to_user_nickname,input_wx_content_dialogue_message) # # kafka_helper.kafka_client.produce_message(input_message) # # logger.info("发送对话 %s", json.dumps(input_message, separators=(',', ':'), ensure_ascii=False)) # # 响应用户 # output_content=reply.content # output_from_user_nickname=msg.to_user_nickname # 回复翻转 # output_to_user_nickname=msg.from_user_nickname # 回复翻转 # output_wx_content_dialogue_message=[{"type": "file", "text": output_content}] # output_message=dialogue_message(output_from_user_nickname,output_to_user_nickname,output_wx_content_dialogue_message) # kafka_helper.kafka_client.produce_message(output_message) # logger.info("发送对话 %s", output_message) elif reply.type == ReplyType.VIDEO: # 新增视频回复类型 video_storage = reply.content itchat.send_video(video_storage, toUserName=receiver) logger.info("[WX] sendFile, receiver={}".format(receiver)) elif reply.type == ReplyType.VIDEO_URL: # 新增视频URL回复类型 video_url = reply.content logger.debug(f"[WX] start download video, video_url={video_url}") video_res = requests.get(video_url, stream=True) video_storage = io.BytesIO() size = 0 for block in video_res.iter_content(1024): size += len(block) video_storage.write(block) logger.info(f"[WX] download video success, size={size}, video_url={video_url}") video_storage.seek(0) itchat.send_video(video_storage, toUserName=receiver) logger.info("[WX] sendVideo url={}, receiver={}".format(video_url, receiver)) # def _send_login_success(): # try: # from common.linkai_client import chat_client # if chat_client.client_id: # chat_client.send_login_success() # except Exception as e: # pass # def _send_logout(): # try: # from common.linkai_client import chat_client # if chat_client.client_id: # chat_client.send_logout() # except Exception as e: # pass # def _send_qr_code(qrcode_list: list): # try: # from common.linkai_client import chat_client # if chat_client.client_id: # chat_client.send_qrcode(qrcode_list) # except Exception as e: # pass # def clean_json_string(json_str): # # 删除所有控制字符(非打印字符),包括换行符、回车符等 # return re.sub(r'[\x00-\x1f\x7f]', '', json_str) # def save_friends_to_redis(agent_tel,agent_nickname, friends): # contact_list = [] # for friend in friends: # friend_data = { # "UserName": friend.UserName, # "NickName": friend.NickName, # "Signature": friend.Signature, # "Province": friend.Province, # "City": friend.City, # "Sex": str(friend.Sex), # 性别可转换为字符串存储 # "Alias": friend.Alias # } # contact_list.append(friend_data) # 将每个朋友的信息加入到列表中 # agent_contact_list = { # "AgentTel":agent_tel, # "agent_nick_name": agent_nickname, # "contact_list": contact_list # 将朋友列表添加到字典中 # } # # 将联系人信息保存到 Redis,使用一个合适的 key # hash_key = f"__AI_OPS_WX__:CONTACTLIST" # redis_helper.redis_helper.update_hash_field(hash_key, agent_tel, json.dumps(agent_contact_list, ensure_ascii=False)) # 设置有效期为 600 秒 # def hourly_change_save_friends(agent_nickname): # last_hour = datetime.now().hour # 获取当前小时 # while True: # current_hour = datetime.now().hour # if current_hour != last_hour: # 检测小时是否变化 # friends=itchat.get_friends(update=True)[1:] # agent_info=fetch_agent_info(agent_nickname) # agent_tel=agent_info.get("agent_tel",None) # save_friends_to_redis(agent_tel,agent_nickname, friends) # last_hour = current_hour # time.sleep(1) # 每秒检查一次 # def ten_mins_change_save_friends(agent_nickname): # last_check_minute = datetime.now().minute # 获取当前分钟 # while True: # current_minute = datetime.now().minute # if current_minute % 10 == 0 and current_minute != last_check_minute: # 检测每10分钟变化 # friends = itchat.get_friends(update=True)[1:] # agent_info = fetch_agent_info(agent_nickname) # agent_tel = agent_info.get("agent_tel", None) # save_friends_to_redis(agent_tel, agent_nickname, friends) # last_check_minute = current_minute # 更新最后检查的分钟 # time.sleep(60) # 每分钟检查一次 # def wx_messages_process_callback(user_nickname,message): # """ # 处理消费到的 Kafka 消息(基础示例) # :param message: Kafka 消费到的消息内容 # """ # # print(user_nickname) # # print(f"Processing message: {message}") # # return True # msg_content= message # cleaned_content = clean_json_string(msg_content) # content=json.loads(cleaned_content) # data = content.get("data", {}) # msg_type_data=data.get("msg_type",None) # content_data = data.get("content",{}) # agent_nickname_data=content_data.get("agent_nickname",None) # agent_tel=content_data.get("agent_tel",None) # if user_nickname == agent_nickname_data and msg_type_data=='group-sending': # friends=itchat.get_friends(update=True)[1:] # contact_list_content_data=content_data.get("contact_list",None) # # 更新好友缓存 # save_friends_to_redis(agent_tel,agent_nickname_data,friends) # # 遍历要群发的好友 # for contact in contact_list_content_data: # nickname = contact.get("nickname",None) # if(nickname not in [friend['NickName'] for friend in friends]): # logger.warning(f'微信中没有 {nickname} 的昵称,将不会发送消息') # for friend in friends: # if friend.get("NickName",None) == nickname: # wx_content_list=content_data.get("wx_content",[]) # for wx_content in wx_content_list: # # 处理文字 # if wx_content.get("type",None) == 'text': # wx_content_text=wx_content.get("text",None) # sent_res=itchat.send(wx_content_text, toUserName=friend.get("UserName",None)) # logger.info(f"{user_nickname} 向 {nickname} 发送文字【 {wx_content_text} 】 {sent_res.get('BaseResponse',{}).get('RawMsg')}") # # // 发送kafka # wx_content_dialogue_message=[{"type": "text", "text": wx_content_text}] # message=dialogue_message(agent_nickname_data,friend.get("NickName",None),wx_content_dialogue_message) # kafka_helper.kafka_client.produce_message(message) # logger.info("发送对话 %s",message) # # 等待随机时间 # time.sleep(random.uniform(5, 15)) # # 处理图片 # elif wx_content.get("type",None) == 'image_url': # print('发送图片') # image_url= wx_content.get("image_url",{}) # url=image_url.get("url",None) # # 网络图片 # logger.debug(f"[WX] start download image, img_url={url}") # pic_res = requests.get(url, stream=True) # image_storage = io.BytesIO() # size = 0 # for block in pic_res.iter_content(1024): # size += len(block) # image_storage.write(block) # logger.info(f"[WX] download image success, size={size}, img_url={url}") # image_storage.seek(0) # if ".webp" in url: # try: # image_storage = convert_webp_to_png(image_storage) # except Exception as e: # logger.error(f"Failed to convert image: {e}") # return # sent_res=itchat.send_image(image_storage, toUserName=friend.get("UserName",None)) # logger.info(f"{user_nickname} 向 {nickname} 发送图片【 {url} 】{sent_res.get('BaseResponse',{}).get('RawMsg')}") # # // 发送kafka # wx_content_dialogue_message=[{"type": "image_url", "image_url": {"url": url}}] # message=dialogue_message(agent_nickname_data,friend.get("NickName",None),wx_content_dialogue_message) # kafka_helper.kafka_client.produce_message(message) # logger.info("发送对话 %s",message) # # 等待随机时间 # time.sleep(random.uniform(5, 15)) # #处理文件 # elif wx_content.get("type",None) == 'file': # print('处理文件') # file_url= wx_content.get("file_url",{}) # url=file_url.get("url",None) # # 提取路径部分 # parsed_url = urlparse(url).path # # 获取文件名和扩展名 # filename = os.path.basename(parsed_url) # 文件名(包含扩展名) # name, ext = os.path.splitext(filename) # 分离文件名和扩展名 # if ext in ['.pdf']: # print('处理PDF文件') # tmp_file_path=save_to_local_from_url(url) # sent_res=itchat.send_file(tmp_file_path, toUserName=friend.get("UserName",None)) # logger.info(f'删除本地{ext}文件: {tmp_file_path}') # os.remove(tmp_file_path) # logger.info(f"{user_nickname} 向 {nickname} 发送 {ext} 文件【 {url} 】{sent_res.get('BaseResponse',{}).get('RawMsg')}") # # // 发送kafka # wx_content_dialogue_message=[{"type": "file", "file_url": {"url": url}}] # message=dialogue_message(agent_nickname_data,friend.get("NickName",None),wx_content_dialogue_message) # kafka_helper.kafka_client.produce_message(message) # logger.info("发送对话 %s",message) # # 等待随机时间 # time.sleep(random.uniform(5, 15)) # elif ext in ['.mp4']: # print('处理MP4文件') # tmp_file_path=save_to_local_from_url(url) # itchat.send_file(tmp_file_path, toUserName=friend.get("UserName",None)) # logger.info(f'删除本地{ext}文件: {tmp_file_path}') # os.remove(tmp_file_path) # logger.info(f"{user_nickname} 向 {nickname} 发送 {ext} 文件【 {url} 】") # # // 发送kafka # wx_content_dialogue_message=[{"type": "file", "file_url": {"url": url}}] # message=dialogue_message(agent_nickname_data,friend.get("NickName",None),wx_content_dialogue_message) # kafka_helper.kafka_client.produce_message(message) # logger.info("发送对话 %s",message) # # 等待随机时间 # time.sleep(random.uniform(5, 15)) # else: # logger.warning(f'暂不支持 {ext} 文件的处理') # return True # else: # return False # def dialogue_message(nickname_from,nickname_to,wx_content): # """ # 构造消息的 JSON 数据 # :param contents: list,包含多个消息内容,每个内容为字典,如: # [{"type": "text", "text": "AAAAAAA"}, # {"type": "image_url", "image_url": {"url": "https://AAAAA.jpg"}}, # {"type":"file","file_url":{"url":"https://AAAAA.pdf"}} # ] # :return: JSON 字符串 # """ # # 获取当前时间戳,精确到毫秒 # current_timestamp = int(time.time() * 1000) # # 获取当前时间,格式化为 "YYYY-MM-DD HH:MM:SS" # current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) # # 构造 JSON 数据 # data = { # "messageId": str(current_timestamp), # "topic": "topic.aiops.wx", # "time": current_time, # "data": { # "msg_type": "dialogue", # "content": { # "nickname_from": nickname_from, # "nickname_to": nickname_to, # "wx_content":wx_content # } # } # } # return json.dumps(data, separators=(',', ':'), ensure_ascii=False) # def fetch_agent_info(agent_nickname): # if os.environ.get('environment', 'default')=='default': # return { # "agent_nickname": agent_nickname, # "agent_tel": "19200137635" # } # aiops_api=conf().get("aiops_api") # # 定义请求URL # url = f"{aiops_api}/business/Agent/smartinfobyname" # # 定义请求头 # headers = { # "accept": "*/*", # "Content-Type": "application/json" # } # # 定义请求数据 # data = { # "smartName": agent_nickname # } # try: # # 发送POST请求 # response = requests.post(url, headers=headers, data=json.dumps(data)) # # 确认响应状态码 # if response.status_code == 200: # response_data = response.json() # if response_data.get("code") == 200: # # 提取 smartName 和 smartPhone # data = response_data.get("data", {}) # return { # "agent_nickname": data.get("smartName"), # "agent_tel": data.get("smartPhone") # } # else: # logger.error(f"API 返回错误信息: {response_data.get('msg')}") # return None # else: # logger.error(f"请求失败,状态码:{response.status_code}") # return None # except Exception as e: # logger.error(f"请求出错: {e}") # return None # def save_to_local_from_url(url): # ''' # 从url保存到本地tmp目录 # ''' # parsed_url = urlparse(url) # # 从 URL 提取文件名 # filename = os.path.basename(parsed_url.path) # # tmp_dir = os.path(__file__) # 获取系统临时目录 # # print(tmp_dir) # tmp_file_path = os.path.join(os.getcwd(),'tmp', filename) # 拼接完整路径 # # 检查是否存在同名文件 # if os.path.exists(tmp_file_path): # logger.info(f"文件已存在,将覆盖:{tmp_file_path}") # # 下载文件并保存到临时目录 # response = requests.get(url, stream=True) # with open(tmp_file_path, 'wb') as f: # for chunk in response.iter_content(chunk_size=1024): # if chunk: # 检查是否有内容 # f.write(chunk) # return tmp_file_path # def upload_oss(access_key_id, access_key_secret, endpoint, bucket_name, local_file_path, oss_file_name, expiration_days=7): # """ # 上传文件到阿里云OSS并设置生命周期规则,同时返回文件的公共访问地址。 # :param access_key_id: 阿里云AccessKey ID # :param access_key_secret: 阿里云AccessKey Secret # :param endpoint: OSS区域对应的Endpoint # :param bucket_name: OSS中的Bucket名称 # :param local_file_path: 本地文件路径 # :param oss_file_name: OSS中的文件存储路径 # :param expiration_days: 文件保存天数,默认7天后删除 # :return: 文件的公共访问地址 # """ # # 创建Bucket实例 # auth = oss2.Auth(access_key_id, access_key_secret) # bucket = oss2.Bucket(auth, endpoint, bucket_name) # ### 1. 设置生命周期规则 ### # rule_id = f'delete_after_{expiration_days}_days' # 规则ID # prefix = oss_file_name.split('/')[0] + '/' # 设置规则应用的前缀为文件所在目录 # # 定义生命周期规则 # rule = oss2.models.LifecycleRule(rule_id, prefix, status=oss2.models.LifecycleRule.ENABLED, # expiration=oss2.models.LifecycleExpiration(days=expiration_days)) # # 设置Bucket的生命周期 # lifecycle = oss2.models.BucketLifecycle([rule]) # bucket.put_bucket_lifecycle(lifecycle) # print(f"已设置生命周期规则:文件将在{expiration_days}天后自动删除") # ### 2. 上传文件到OSS ### # bucket.put_object_from_file(oss_file_name, local_file_path) # ### 3. 构建公共访问URL ### # file_url = f"http://{bucket_name}.{endpoint.replace('http://', '')}/{oss_file_name}" # print(f"文件上传成功,公共访问地址:{file_url}") # return file_url