这篇笔记用来记录这次从 Obsidian 到博客的完整发布链路，方便后面继续扩展成日常可复用的内容工作流。这次做成了什么这次已经把下面几段链路串起来了：在 Obsidian 里编写 Markdown 笔记自动识别笔记里的本地图片上传图片到 img.maxnn.top生成一份可直接发布的 .published.md通过本地插件把文章推送到 Typecho回写文章的 blog_cid、blog_url 和发布时间目前的使用方式在 Obsidian 里，现在可以直接使用两个命令：生成博客发布稿发布当前笔记到 Typecho第一个命令适合先检查图片替换和正文内容，第二个命令适合直接把当前笔记推送到博客。这套流程为什么顺手这次没有继续硬走 Typecho 后台网页登录提交流程，而是改成了更稳定的桥接方式：Obsidian 负责本地编辑和命令触发图床负责承接本地图片Typecho 侧提供专用发布入口这样做的好处是，后面无论是继续扩展批量发布、草稿更新，还是接 API / MCP，都更容易一点。这次测试里验证过的点图床上传已经可用本地图片可以被替换成线上地址Typecho 可以新增文章Typecho 可以更新已有文章前台页面可以看到更新后的内容文章发布后，Obsidian 侧可以保存文章关联信息下一步可以继续补的内容后面比较值得继续做的方向有这些：自动读取更多 frontmatter 字段支持分类和标签的更细分映射增加一键发布并同步到 Notion 的链路把视频资源接到独立存储方案做一个更完整的发布面板，而不只是命令入口这篇笔记的用途这篇文章本身也可以继续当测试稿使用。后面如果要继续验证：新图片上传frontmatter 字段解析发布后回写文章更新覆盖都可以直接基于这篇继续改，不用重新搭环境。
图书馆自动预约2024年 结束了 2025 我们一定会过的更好的max的驱动力是懒，使用懒得争分夺秒的去抢图书馆的座位，但是为什么还是约不到呢？为了更大化展现计算机人的能力，上脚本！但是我又不会爬虫怎么办呢？去github看一看根据已知信息 前图书馆预约项目的“负责人” 告诉我这个是超星学习通的图书馆预约框架so 搜索超星图书馆预约 学习通图书馆得到结论：https://github.com/bear-zd/ChaoXingReserveSeat前去下载观察：import json import time import argparse import os import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') from utils import reserve, get_user_credentials get_current_time = lambda action: time.strftime("%H:%M:%S", time.localtime(time.time() + 8*3600)) if action else time.strftime("%H:%M:%S", time.localtime(time.time())) get_current_dayofweek = lambda action: time.strftime("%A", time.localtime(time.time() + 8*3600)) if action else time.strftime("%A", time.localtime(time.time())) SLEEPTIME = 0.2 # 每次抢座的间隔 ENDTIME = "07:01:00" # 根据学校的预约座位时间+1min即可 ENABLE_SLIDER = False # 是否有滑块验证 MAX_ATTEMPT = 5 # 最大尝试次数 RESERVE_NEXT_DAY = False # 预约明天而不是今天的 def login_and_reserve(users, usernames, passwords, action, success_list=None): logging.info(f"Global settings: \nSLEEPTIME: {SLEEPTIME}\nENDTIME: {ENDTIME}\nENABLE_SLIDER: {ENABLE_SLIDER}\nRESERVE_NEXT_DAY: {RESERVE_NEXT_DAY}") if action and len(usernames.split(",")) != len(users): raise Exception("user number should match the number of config") if success_list is None: success_list = [False] * len(users) current_dayofweek = get_current_dayofweek(action) for index, user in enumerate(users): username, password, times, roomid, seatid, daysofweek = user.values() if action: username, password = usernames.split(',')[index], passwords.split(',')[index] if(current_dayofweek not in daysofweek): logging.info("Today not set to reserve") continue if not success_list[index]: logging.info(f"----------- {username} -- {times} -- {seatid} try -----------") s = reserve(sleep_time=SLEEPTIME, max_attempt=MAX_ATTEMPT, enable_slider=ENABLE_SLIDER, reserve_next_day=RESERVE_NEXT_DAY) s.get_login_status() s.login(username, password) s.requests.headers.update({'Host': 'office.chaoxing.com'}) suc = s.submit(times, roomid, seatid, action) success_list[index] = suc return success_list def main(users, action=False): current_time = get_current_time(action) logging.info(f"start time {current_time}, action {'on' if action else 'off'}") attempt_times = 0 usernames, passwords = None, None if action: usernames, passwords = get_user_credentials(action) success_list = None current_dayofweek = get_current_dayofweek(action) today_reservation_num = sum(1 for d in users if current_dayofweek in d.get('daysofweek')) while current_time < ENDTIME: attempt_times += 1 # try: success_list = login_and_reserve(users, usernames, passwords, action, success_list) # except Exception as e: # print(f"An error occurred: {e}") print(f"attempt time {attempt_times}, time now {current_time}, success list {success_list}") current_time = get_current_time(action) if sum(success_list) == today_reservation_num: print(f"reserved successfully!") return def debug(users, action=False): logging.info(f"Global settings: \nSLEEPTIME: {SLEEPTIME}\nENDTIME: {ENDTIME}\nENABLE_SLIDER: {ENABLE_SLIDER}\nRESERVE_NEXT_DAY: {RESERVE_NEXT_DAY}") suc = False logging.info(f" Debug Mode start! , action {'on' if action else 'off'}") if action: usernames, passwords = get_user_credentials(action) current_dayofweek = get_current_dayofweek(action) for index, user in enumerate(users): username, password, times, roomid, seatid, daysofweek = user.values() if type(seatid) == str: seatid = [seatid] if action: username ,password = usernames.split(',')[index], passwords.split(',')[index] if(current_dayofweek not in daysofweek): logging.info("Today not set to reserve") continue logging.info(f"----------- {username} -- {times} -- {seatid} try -----------") s = reserve(sleep_time=SLEEPTIME, max_attempt=MAX_ATTEMPT, enable_slider=ENABLE_SLIDER) s.get_login_status() s.login(username, password) s.requests.headers.update({'Host': 'office.chaoxing.com'}) suc = s.submit(times, roomid, seatid, action) if suc: return def get_roomid(args1, args2): username = input("请输入用户名：") password = input("请输入密码：") s = reserve(sleep_time=SLEEPTIME, max_attempt=MAX_ATTEMPT, enable_slider=ENABLE_SLIDER, reserve_next_day=RESERVE_NEXT_DAY) s.get_login_status() s.login(username=username, password=password) s.requests.headers.update({'Host': 'office.chaoxing.com'}) encode = input("请输入deptldEnc：") s.roomid(encode) if __name__ == "__main__": config_path = os.path.join(os.path.dirname(__file__), 'config.json') parser = argparse.ArgumentParser(prog='Chao Xing seat auto reserve') parser.add_argument('-u','--user', default=config_path, help='user config file') parser.add_argument('-m','--method', default="reserve" ,choices=["reserve", "debug", "room"], help='for debug') parser.add_argument('-a','--action', action="store_true",help='use --action to enable in github action') args = parser.parse_args() func_dict = {"reserve": main, "debug":debug, "room": get_roomid} with open(args.user, "r+") as data: usersdata = json.load(data)["reserve"] func_dict[args.method](usersdata, args.action)自动预约座位的 Python 脚本解析此文档旨在详细介绍一个用于自动预约座位的 Python 脚本。该脚本的核心功能是登录用户，按照设定的时间预约座位，并提供了一些调试和错误处理的功能。关键功能概述脚本主要依赖于几个函数，能够根据用户输入和设定的预约时间自动进行座位预约。以下为关键配置项和函数。[scode type="green"]本脚本能够自动化多个用户同时预约座位，提高预约的成功率，并且提供调试模式以帮助用户排查问题。[/scode]SLEEPTIME = 0.2 # 每次抢座的间隔 ENDTIME = "07:01:00" # 根据学校的预约座位时间+1min即可 ENABLE_SLIDER = False # 是否有滑块验证 MAX_ATTEMPT = 5 # 最大尝试次数 RESERVE_NEXT_DAY = False # 预约明天而不是今天的登录与预约函数脚本中的 login_and_reserve​ 函数负责处理用户登录和座位预约的逻辑。函数会遍历每个用户，并根据设定的条件尝试预约。[scode type="red"]如果用户数量与配置中的用户名数量不匹配，会抛出异常。这点可能导致用户使用时出错，因此应该妥善检查输入。[/scode]def login_and_reserve(users, usernames, passwords, action, success_list=None): logging.info(f"Global settings: \nSLEEPTIME: {SLEEPTIME}\nENDTIME: {ENDTIME}\nENABLE_SLIDER: {ENABLE_SLIDER}\nRESERVE_NEXT_DAY: {RESERVE_NEXT_DAY}") if action and len(usernames.split(",")) != len(users): raise Exception("user number should match the number of config")主程序与调试模式​main​ 函数作为程序的入口点，负责控制预约的开始时间和迭代尝试。还有一个 debug​ 函数可用于调试，以便用户能够手动执行预约并查看相关信息。[scode type="blue"]未来可以考虑添加更丰富的错误处理，以便用户在执行中获得更清晰的反馈。[/scode]def main(users, action=False): ... while current_time < ENDTIME: attempt_times += 1 success_list = login_and_reserve(users, usernames, passwords, action, success_list) ...房间 ID 获取函数为了能够正确预约，用户需要提供房间 ID。get_roomid​ 函数能够收集用户输入并返回相应的房间 ID。[scode type="green"]函数通过用户交互获取房间信息，增强了使用的灵活性，用户只需输入相关信息即可。[/scode]def get_roomid(args1, args2): username = input("请输入用户名：") password = input("请输入密码：") ...入口函数与配置最后，脚本的入口点定义了命令行参数，并读取用户的配置信息。支持从文件中解析用户数据，为调用对应功能提供基础。[scode type="red"]用户需确保配置文件的路径正确，如果文件不存在，脚本将无法正常运行。[/scode]if __name__ == "__main__": config_path = os.path.join(os.path.dirname(__file__), 'config.json') ...总结此 Python 脚本为用户提供了一个多功能的自动预约系统，通过合理的设置和使用，能够高效地管理预约座位的操作。虽然目前的实现中存在一些小的不足，但通过不断迭代和优化，有潜力成为一个更加强大的工具。续改为账号密码之后发现没有图书馆位置，去手机抓包发现发现登陆里面的接口和学校的不太一样整改一下（其实是别人整改的，我也不会啊！hhh）得到项目：​ok 自动预约图书馆了！nice但是还是手动cookie登陆的方式文档未来会整改的，但不是现在，祝大家考试顺利（期末周了）
批量爬取图片-视觉中国2024年12月4日00:20:52带编写使用教程去github下载 https://github.com/itmanmax/getvgcg-picture双击bat启动​功能介绍：输入搜索的关键词下载图片数量爬取的方式是否统一命名 ：格式为foods1.jpg foods2.jpg 这种以及保存位置​​​​使用如从简单！声明：仅供娱乐使用，请勿商业使用！源码介绍代码分享import os import time import requests from bs4 import BeautifulSoup import random import tkinter as tk from tkinter import ttk, messagebox import logging from datetime import datetime class ImageDownloaderGUI: def __init__(self, root): self.root = root self.root.title("图片下载器") self.root.geometry("600x650") # 设置整体样式 style = ttk.Style() style.configure('TLabel', font=('微软雅黑', 10)) style.configure('TButton', font=('微软雅黑', 10)) style.configure('TRadiobutton', font=('微软雅黑', 10)) style.configure('TCheckbutton', font=('微软雅黑', 10)) # 创建主框架 main_frame = ttk.Frame(root, padding="20") main_frame.pack(fill=tk.BOTH, expand=True) # 搜索关键词区域 search_frame = ttk.LabelFrame(main_frame, text="搜索设置", padding="10") search_frame.pack(fill=tk.X, pady=10) ttk.Label(search_frame, text="搜索关键词:").pack(anchor='w') self.search_entry = ttk.Entry(search_frame, width=40) self.search_entry.pack(fill=tk.X, pady=(5, 10)) self.search_entry.insert(0, "Restaurant") ttk.Label(search_frame, text="下载数量:").pack(anchor='w') self.count_entry = ttk.Entry(search_frame, width=40) self.count_entry.pack(fill=tk.X, pady=5) self.count_entry.insert(0, "10") # 下载模式区域 mode_frame = ttk.LabelFrame(main_frame, text="下载模式", padding="10") mode_frame.pack(fill=tk.X, pady=10) self.download_mode = tk.StringVar(value="顺序") ttk.Radiobutton(mode_frame, text="顺序下载", variable=self.download_mode, value="顺序").pack(anchor='w', pady=2) ttk.Radiobutton(mode_frame, text="随机下载", variable=self.download_mode, value="随机").pack(anchor='w', pady=2) ttk.Radiobutton(mode_frame, text="跳跃下载", variable=self.download_mode, value="跳跃").pack(anchor='w', pady=2) # 跳跃设置区域 jump_frame = ttk.Frame(mode_frame) jump_frame.pack(fill=tk.X, pady=(5, 0)) ttk.Label(jump_frame, text="跳跃距离:").pack(side='left') self.jump_entry = ttk.Entry(jump_frame, width=10) self.jump_entry.pack(side='left', padx=5) self.jump_entry.insert(0, "2") # 命名选项区域 name_frame = ttk.LabelFrame(main_frame, text="命名选项", padding="10") name_frame.pack(fill=tk.X, pady=10) self.rename_var = tk.BooleanVar(value=False) self.rename_check = ttk.Checkbutton( name_frame, text="统一命名（例：关键词1.jpg, 关键词2.jpg）", variable=self.rename_var ) self.rename_check.pack(anchor='w') # 保存路径区域 path_frame = ttk.LabelFrame(main_frame, text="保存位置", padding="10") path_frame.pack(fill=tk.X, pady=10) path_input_frame = ttk.Frame(path_frame) path_input_frame.pack(fill=tk.X) self.path_entry = ttk.Entry(path_input_frame) self.path_entry.pack(side='left', fill=tk.X, expand=True, padx=(0, 5)) self.path_entry.insert(0, r'C:\Users\86147\图片\pypicture') self.browse_btn = ttk.Button(path_input_frame, text="浏览", command=self.browse_path) self.browse_btn.pack(side='right') # 下载按钮区域 btn_frame = ttk.Frame(main_frame) btn_frame.pack(fill=tk.X, pady=20) download_btn = ttk.Button(btn_frame, text="开始下载", command=self.start_download, style='Accent.TButton') download_btn.pack(pady=5) # 进度显示 self.progress_label = ttk.Label(main_frame, text="", font=('微软雅黑', 9)) self.progress_label.pack(pady=10) # 添加状态栏 self.status_label = ttk.Label(root, text="就绪", relief=tk.SUNKEN, anchor='w') self.status_label.pack(side=tk.BOTTOM, fill=tk.X) # 设置日志 self.setup_logging() def setup_logging(self): # 配置日志 self.logger = logging.getLogger('ImageDownloader') self.logger.setLevel(logging.INFO) # 创建文件处理器 fh = logging.FileHandler('error.txt', encoding='utf-8') fh.setLevel(logging.ERROR) # 设置日志格式 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) self.logger.addHandler(fh) def browse_path(self): from tkinter import filedialog path = filedialog.askdirectory() if path: self.path_entry.delete(0, tk.END) self.path_entry.insert(0, path) def start_download(self): self.status_label.config(text="正在下载...") keyword = self.search_entry.get().strip() save_path = self.path_entry.get().strip() try: count = int(self.count_entry.get().strip()) except ValueError: messagebox.showerror("错误", "请输入有效的下载数量！") return if not keyword: messagebox.showerror("错误", "请输入搜索关键词！") return if not save_path: messagebox.showerror("错误", "请选择保存路径！") return # 创建以关键词命名的子文件夹 keyword_folder = os.path.join(save_path, keyword) if not os.path.exists(keyword_folder): os.makedirs(keyword_folder) self.progress_label.config(text="开始下载...") self.root.update() try: self.download_images(keyword, count, keyword_folder) messagebox.showinfo("完成", f"成功下载了 {count} 张图片到文件夹 {keyword_folder}！") self.status_label.config(text="下载完成") except Exception as e: messagebox.showerror("错误", f"下载过程中出现错误：{str(e)}") self.status_label.config(text="下载出错") finally: self.progress_label.config(text="") def download_images(self, keyword, count, save_folder): try: base_url = f'https://www.vcg.com/creative-image/{keyword}' # 更新状态 self.progress_label.config(text="正在获取图片列表...") self.root.update() soup = make_soup(base_url) all_img_urls = get_image_urls(soup) if not all_img_urls: raise Exception("未找到任何图片") # 根据下载模式选择图片 if self.download_mode.get() == "顺序": img_urls = all_img_urls[:count] elif self.download_mode.get() == "随机": img_urls = random.sample(all_img_urls, min(count, len(all_img_urls))) elif self.download_mode.get() == "跳跃": try: jump_distance = int(self.jump_entry.get().strip()) except ValueError: messagebox.showerror("错误", "请输入有效的跳跃距离！") return img_urls = all_img_urls[::jump_distance][:count] # 确定起始编号 start_index = self.get_start_index(keyword, save_folder) # 创建进度条 progress_window = tk.Toplevel(self.root) progress_window.title("下载进度") progress_window.geometry("300x150") progress_window.transient(self.root) # 设置为主窗口的子窗口 ttk.Label(progress_window, text="正在下载...").pack(pady=10) progress_bar = ttk.Progressbar(progress_window, length=200, mode='determinate') progress_bar.pack(pady=10) progress_text = ttk.Label(progress_window, text="") progress_text.pack(pady=10) # 设置进度条最大值 progress_bar['maximum'] = len(img_urls) for i, img_url in enumerate(img_urls, start=1): # 更新进度显示 progress_text.config(text=f"正在下载第 {i}/{len(img_urls)} 张图片") progress_bar['value'] = i self.status_label.config(text=f"正在下载: {i}/{len(img_urls)}") # 更新界面 progress_window.update() self.root.update_idletasks() if self.rename_var.get(): filename = f"{keyword}{start_index + i - 1}.jpg" else: filename = img_url.split('/')[-1].split('?')[0] save_path = os.path.join(save_folder, filename) try: download_image(img_url, save_path) except Exception as e: self.logger.error(f"下载失败 {img_url}: {str(e)}") continue # 控制更新频率，避免界面卡顿 if i % 2 == 0: # 每下载两张图片更新一次界面 time.sleep(0.1) # 短暂延时，让界面有机会响应 # 关闭进度窗口 progress_window.destroy() except Exception as e: self.logger.error(f"下载过程出错: {str(e)}") raise def get_start_index(self, keyword, save_folder): # 获取文件夹中已有的最大编号 existing_files = [f for f in os.listdir(save_folder) if f.startswith(keyword) and f.endswith('.jpg')] max_index = 0 for file in existing_files: try: index = int(file[len(keyword):-4]) # 提取编号 if index > max_index: max_index = index except ValueError: continue return max_index + 1 def make_soup(url): try: headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36' } response = requests.get(url, headers=headers) response.raise_for_status() return BeautifulSoup(response.text, 'html.parser') except Exception as e: logging.error(f'Error making soup for {url}: {str(e)}') raise def get_image_urls(soup): img_urls = [] for img_tag in soup.find_all('img'): img_url = img_tag.get('data-src') or img_tag.get('src') if img_url: if not img_url.lower().endswith('.jpg') and not img_url.lower().endswith('.jpeg'): continue if img_url.startswith('//'): img_url = 'https:' + img_url elif img_url.startswith('/'): img_url = 'https://www.vcg.com' + img_url logging.debug(f'Found image URL: {img_url}') img_urls.append(img_url) return img_urls def download_image(url, save_path): try: headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36' } with requests.get(url, headers=headers, stream=True) as response: response.raise_for_status() with open(save_path, 'wb') as file: for chunk in response.iter_content(chunk_size=8192): if chunk: file.write(chunk) return True except Exception as e: logging.error(f'Error downloading {url}: {str(e)}') return False def main(): root = tk.Tk() app = ImageDownloaderGUI(root) root.mainloop() if __name__ == '__main__': main()图片下载器 GUI 详解1. 项目概述本项目实现了一个简单的图片下载器 GUI，用户可以通过输入关键词和下载模式来下载对应的图片。以下是程序的主要功能与实现细节。2. 界面搭建[scode type="green"]此部分代码负责创建一个基本的图形用户界面，允许用户输入搜索关键词、下载数量和选择下载模式。[/scode]class ImageDownloaderGUI: def __init__(self, root): self.root = root self.root.title("图片下载器") self.root.geometry("600x650") # 设置整体样式 style = ttk.Style() style.configure('TLabel', font=('微软雅黑', 10)) style.configure('TButton', font=('微软雅黑', 10)) style.configure('TRadiobutton', font=('微软雅黑', 10)) style.configure('TCheckbutton', font=('微软雅黑', 10)) # 创建主框架 main_frame = ttk.Frame(root, padding="20") main_frame.pack(fill=tk.BOTH, expand=True) # 搜索关键词区域 search_frame = ttk.LabelFrame(main_frame, text="搜索设置", padding="10") search_frame.pack(fill=tk.X, pady=10) ttk.Label(search_frame, text="搜索关键词:").pack(anchor='w') self.search_entry = ttk.Entry(search_frame, width=40) self.search_entry.pack(fill=tk.X, pady=(5, 10)) self.search_entry.insert(0, "Restaurant") ttk.Label(search_frame, text="下载数量:").pack(anchor='w') self.count_entry = ttk.Entry(search_frame, width=40) self.count_entry.pack(fill=tk.X, pady=5) self.count_entry.insert(0, "10")3. 功能实现[scode type="blue"]下面的代码展示了如何实现图片的下载逻辑，包括选择下载模式、下载进度的显示，以及日志记录等功能。[/scode]def start_download(self): self.status_label.config(text="正在下载...") keyword = self.search_entry.get().strip() save_path = self.path_entry.get().strip() try: count = int(self.count_entry.get().strip()) except ValueError: messagebox.showerror("错误", "请输入有效的下载数量！") return if not keyword: messagebox.showerror("错误", "请输入搜索关键词！") return if not save_path: messagebox.showerror("错误", "请选择保存路径！") return # 创建以关键词命名的子文件夹 keyword_folder = os.path.join(save_path, keyword) if not os.path.exists(keyword_folder): os.makedirs(keyword_folder) self.progress_label.config(text="开始下载...") self.root.update() try: self.download_images(keyword, count, keyword_folder) messagebox.showinfo("完成", f"成功下载了 {count} 张图片到文件夹 {keyword_folder}！") self.status_label.config(text="下载完成") except Exception as e: messagebox.showerror("错误", f"下载过程中出现错误：{str(e)}") self.status_label.config(text="下载出错") finally: self.progress_label.config(text="")4. 图片下载逻辑[scode type="red"]在下载图片时，需要获取图片链接并根据用户选择的模式进行下载，当前代码在错误处理方面还有待改进。[/scode]def download_images(self, keyword, count, save_folder): try: base_url = f'https://www.vcg.com/creative-image/{keyword}' soup = make_soup(base_url) all_img_urls = get_image_urls(soup) if not all_img_urls: raise Exception("未找到任何图片") # 根据下载模式选择图片 if self.download_mode.get() == "顺序": img_urls = all_img_urls[:count] elif self.download_mode.get() == "随机": img_urls = random.sample(all_img_urls, min(count, len(all_img_urls))) elif self.download_mode.get() == "跳跃": jump_distance = int(self.jump_entry.get().strip()) if self.jump_entry.get() else 2 img_urls = all_img_urls[::jump_distance][:count] # 下载图片并更新进度 for i, img_url in enumerate(img_urls, 1): # 下载逻辑 pass5. 获取与下载图片[scode type="green"]使用 requests​ 库和 BeautifulSoup​ 解析 HTML 页面，获得图片 URL 并进行下载。同时通过日志记录下载状态。[/scode]def make_soup(url): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36' } response = requests.get(url, headers=headers) response.raise_for_status() return BeautifulSoup(response.text, 'html.parser') def download_image(url, save_path): with requests.get(url, stream=True) as response: response.raise_for_status() with open(save_path, 'wb') as file: for chunk in response.iter_content(chunk_size=8192): file.write(chunk)总结本项目的图片下载器为用户提供了一个简洁的界面，以方便地下载指定关键词的图片。通过完善的错误处理和日志记录功能，增强了系统的稳定性和用户体验。未来可以考虑添加更多图片来源，提升下载速度，以及优化界面的响应能力。‍‍‍‍摘要本项目实现了一个简单的图片下载器 GUI，用户可以根据关键词下载指定数量的图片。以下是使用与功能的概述：使用教程从 GitHub 下载项目：getvgcg-picture。双击启动 bat 文件以打开程序。用户可以设置搜索关键词、下载数量、下载方式（顺序、随机或跳跃）、统一命名选项以及保存路径。功能介绍支持输入关键词和下载数量。可选择下载模式，包括顺序、随机和跳跃下载。提供统一命名的选项（如 foods1.jpg）。可自定义保存位置。声明：该工具仅供娱乐使用，请勿用于商业目的。源码简介代码使用 Python 和 Tkinter 库构建 GUI，通过 requests​ 和 BeautifulSoup​ 库实现网页抓取与图片下载。程序包括状态记录及错误处理，确保用户在下载过程中获得及时信息和反馈。界面：用户友好的输入框和按钮。下载逻辑：根据用户设置选择图片并进行下载，同时展示下载进度。整体而言，本图片下载器提供了一种简便的方式来获取网络图片，适合对图库有需求的用户。未来可加入更多功能，如支持不同图片格式、提高下载速度等。本文章提取的链接：[button color="primary" icon="" url="https://github.com/itmanmax/getvgcg-picture" type=""]我的主页[/button]
MAX-网盘-1-使用教程主页更多网址打开：收藏夹 或者 直接跳转 Cloudreve​​登录注册使用，应该没什么问题了，都是s3存储，阿里云的40g 应该够用 和onedrive的1t就跟百度网盘一样懒得码字了，去吧！kimi！Cloudreve是一个支持多家云存储的自托管文件管理和分享系统，以下是使用Cloudreve的基本教程：1. 项目介绍Cloudreve支持多种存储端，如本机、从机、七牛、阿里云OSS、腾讯云COS、又拍云、OneDrive等，并且支持客户端直传，下载限速等功能。2. 官方资源官网：https://cloudreve.org/GitHub：https://github.com/cloudreve/Cloudreve下载：https://github.com/cloudreve/Cloudreve/releases安装文档：https://docs.cloudreve.org/getting-started/install演示：https://demo.cloudreve.org3. 安装步骤3.1 获取Cloudreve你可以在GitHub Release页面获取已经构建打包完成的主程序。例如，对于64位Linux系统，可以下载cloudreve_版本号_linux_amd64.tar.gz​。3.2 启动Cloudreve通过SSH远程登录到Linux服务器，执行以下命令来获取、解压并执行主程序：apt-get update && apt-get upgrade cd /usr/bin wget https://github.com/cloudreve/Cloudreve/releases/download/版本号/cloudreve_版本号_linux_amd64.tar.gz tar -zxvf cloudreve_版本号_linux_amd64.tar.gz chmod +x ./cloudreve ./cloudreve访问http://服务器IP:5212​，使用首次启动Cloudreve打印出来的账号密码登录。3.3 进程守护使用Systemd方式进行进程守护，编辑配置文件并启动Cloudreve服务。4. 可选部署流程4.1 反向代理如果需要使用HTTPS或与其他Web服务共存，可以使用主流Web服务器反向代理Cloudreve。4.2 Docker部署Cloudreve也支持通过Docker进行部署，提供了官方的docker image，支持多种架构。5. 配置文件Cloudreve启动后会自动生成配置文件conf.ini​，你可以根据需要进行修改。以上是Cloudreve的基本使用教程，具体步骤和配置可能因版本更新而有所变化，请参考最新的官方文档进行操作。‍就这么多​‍6. 基本使用一旦Cloudreve成功安装并启动，你就可以通过浏览器访问管理界面进行进一步的设置和文件管理。6.1 登录界面在浏览器中输入 http://服务器IP:5212​，你将看到登录界面。输入之前打印出的默认账号和密码后，你将进入Cloudreve的主界面。6.2 文件上传在主界面中，你可以点击“上传”按钮，选择本地文件进行上传。Cloudreve支持批量上传，你可以一次性选择多个文件或文件夹。6.3 文件管理上传后，你可以对文件进行管理，包括重命名、移动、删除和分享链接等操作。右键点击文件，可以使用快捷菜单进行相关操作。6.4 分享功能Cloudreve支持文件分享功能，你可以选择要分享的文件，生成可分享的链接。此链接可以设置有效期和访问权限，确保你的文件安全。7. 进阶设置7.1 用户管理Cloudreve支持多用户模式，管理员可以创建多个用户账户并为每个账户分配不同的权限。你可以在管理界面的“用户管理”中进行设置。7.2 存储设置在“存储管理”中，你可以配置不同的云存储，选择适合你的存储方式，如国内外的云服务提供商。7.3 主题和界面定制Cloudreve允许用户自定义界面主题，你可以在设置中选择适合自己喜好的主题或上传自己的CSS文件进行深度定制。8. 常见问题解答8.1 无法访问Cloudreve怎么办？首先检查服务器的防火墙设置，确保5212端口开放。其次确认Cloudreve服务是否在正常运行，可以通过SSH连接到服务器，使用pgrep -o cloudreve​ 命令查找进程。8.2 Cloudreve支持多语言吗？是的，Cloudreve支持多种语言。你可以在设置中选择界面语言，让文件管理体验更加贴近你的使用习惯。9. 注意事项在使用Cloudreve时，请确保定期备份数据，尤其在进行大规模文件管理操作时。同时，关注Cloudreve的版本更新，及时进行升级，以获得更好的功能和安全性。就这样，准备好开始使用Cloudreve了吗？如果还有其他疑问，可以参考官方文档或社区论坛，获得更多支持和帮助！希望你在使用Cloudreve的过程中能够享受到便捷的文件管理体验！如果有任何问题或建议，欢迎随时交流！摘要Cloudreve是一个支持多种云存储的自托管文件管理和分享系统，与百度网盘类似，用户可以在其平台上轻松管理文件。其基本使用教程包括：项目介绍：Cloudreve支持本机、七牛、阿里云OSS、腾讯云COS、OneDrive等多种存储端，具有客户端直传和下载限速的功能。安装步骤：用户可以通过GitHub获取Cloudreve的安装包，并通过SSH登录Linux服务器进行解压和启动。基本使用：登录后，用户可以上传文件、进行管理（如重命名、移动、删除、分享链接等），并支持文件分享功能。进阶设置：Cloudreve支持多用户管理、存储设置、界面主题定制。常见问题解答：包括如何解决访问问题和多语言支持等。注意事项：建议用户定期备份数据，并关注版本更新。该平台旨在提供便捷的文件管理体验，用户如需更多支持可以参考官方文档。‍本文章提取的链接：[button color="primary" icon="" url="https://home.maxtral.fun/list/" type="round"]收藏夹[/button][button color="info" icon="" url="http://cloude.maxtral.fun/login" type=""]Cloudreve[/button][button color="dark" icon="" url="https://cloudreve.org/" type=""]官网[/button][button color="success" icon="" url="https://github.com/cloudreve/Cloudreve" type=""]GitHub[/button][button color="black" icon="" url="https://github.com/cloudreve/Cloudreve/releases" type=""]下载[/button][button color="warning" icon="" url="https://docs.cloudreve.org/getting-started/install" type=""]安装文档[/button][button color="primary" icon="" url="https://demo.cloudreve.org" type=""]演示[/button]
MAX-图床-1-使用教程教程收藏夹 打开找到图床然后打开 MAX的图床​​陌生人好像可以直接传，或者去登录注册​‍传一个图片就能直接获得uml引用了​​点击右侧的上传等待完成获得地址​结束‍介绍图床：图床是一种网络服务，主要用于存储和托管图片。用户可以将图片上传到图床服务器上，服务器会为每张图片分配一个唯一的URL，用户可以通过这个链接在网页、博客、论坛等地方方便地分享和展示这些图片。图床的主要功能包括：图片存储：用户可以将图片上传到图床服务器上，服务器会存储这些图片。使用图床的好处包括节省存储空间、提高访问速度、方便分享、保护隐私等。图床适用于多种场景，比如在论坛或社交媒体上分享图片、将图片嵌入到个人博客或网站中、在在线商店或拍卖网站上展示商品图片、在应用程序中存储和展示图片资源等。一些知名的图床服务包括七牛云、GitHub、微博图床、腾讯云等。兰空图床Lsky Pro+，也称为兰空图床，是一个基于PHP和Laravel框架开发的云端相册程序。它允许用户上传、管理和分享图片，并提供了多种强大的功能。以下是Lsky Pro+的主要特点和用途：图片存储与管理：兰空图床可以帮助用户保管大量图片，支持主流第三方存储服务，如AWS S3、阿里云OSS、腾讯云COS等。综上所述，Lsky Pro+兰空图床是一个功能全面、易于部署和使用的图床程序，适合个人和企业用户用于图片的存储、管理和分享。官网地址：快速开始 | Lsky Pro+ [Lsky Pro+](https://docs.lsky.pro/) ‍‍‍‍本教程介绍了如何使用MAX图床和Lsky Pro+图床服务。使用MAX图床打开MAX图床网站并进行登录或注册。上传图片后，系统将提供图片的唯一URL，便于分享和引用。图床概述图床是存储和托管图片的网络服务，用户可上传图片，获取URL进行分享，节省存储空间、提高访问速度并保护隐私。适用于分享、博客嵌入、商品展示等多个场景。兰空图床（Lsky Pro+）基于PHP和Laravel开发，允许图片上传、管理和分享。支持主流第三方存储服务如AWS S3、阿里云OSS等，适合个人与企业用户。官方文档和资源可通过提供的链接访问。本文章提取的链接：[button color="primary" icon="" url="https://home.maxtral.fun/list/" type=""]收藏夹[/button][button color="primary" icon="" url="https://pic.maxtral.fun/" type=""]MAX的图床[/button][button color="primary" icon="" url="https://docs.lsky.pro/guide/getting-started" type=""]快速开始 | Lsky Pro+[/button][button color="primary" icon="" url="https://docs.lsky.pro/" type=""]Lsky Pro+[/button]‍
MAX-状态-1+MAX-状态-2-使用教程‍状态-1从主页就直接打开​​点击后可以查看相关的状态和内容 MAX-SEVERS-STATUS​​点击服务可以看我的网页的情况 服务状态 - MAX-SEVERS-STATUS​之前挺多的后来删除了，可以看网络或者更改主题之类 MAX-SEVERS-STATUS​之前挺多的后来删除了，​状态-2从更多网址找到 收藏夹 状态-2 ​具体有什么自己探索去吧 NezhaDash 本来就是一个面板 没什么好说的​​vercerl项目和docker项目这两个都是状态1的文档地址：服务监控 | 哪吒服务器监控状态2的文档地址：https://github.com/hamster1963/nezha-dash‍摘要状态-1直接从主页访问相关服务状态。提供链接以查看服务状态及网页情况：MAX-SEVERS-STATUS 和 服务状态 - MAX-SEVERS-STATUS。支持网络查看和主题更改。提到部分内容曾被删除。状态-2从更多网址中找到收藏夹：收藏夹。提供 NezhaDash 的链接供探索：NezhaDash。介绍了 vercel 和 docker 项目。提供了与状态1和状态2相关的文档链接。本文章提取的链接：[button color="primary" icon="" url="https://home.maxtral.fun/" type="round"]我的主页[/button][button color="danger" icon="" url="http://nezha.maxtral.fun/" type=""]MAX-SEVERS-STATUS[/button][button color="dark" icon="" url="http://nezha.maxtral.fun/service" type=""]服务状态 - MAX-SEVERS-STATUS[/button][button color="info" icon="" url="http://nezha.maxtral.fun/network" type=""]MAX-SEVERS-STATUS[/button][button color="light" icon="" url="https://home.maxtral.fun/list/" type=""]收藏夹[/button][button color="success" icon="" url="https://severstatus.maxtral.fun/" type=""]NezhaDash[/button][button color="warning" icon="" url="https://nezha.wiki/guide/services.html" type=""]服务监控 | 哪吒服务器监控[/button][button color="primary" icon="" url="https://github.com/hamster1963/nezha-dash" type=""]状态2的文档地址[/button]‍
MAX-gemini-1-使用教程打开门户页面：MAX-门户-1点击更多网址：收藏夹​​​选择AI助手页面点击 MAX-gemini-1​然后就可以愉快的和ai聊天了​​​​‍水文章，无意义，但是还是需要有的‍摘要：访问 MAX 门户页面。点击“更多网址”进入收藏夹。选择 AI 助手页面。点击 MAX-gemini-1 链接。开始与 AI 聊天。备注：该文章内容简单，可能缺乏深度，但仍有其存在的必要。本文章提取的链接：[button color="primary" icon="" url="https://home.maxtral.fun/" type="round"]我的主页[/button][button color="primary" icon="" url="https://home.maxtral.fun/list/" type="round"]收藏夹[/button][button color="primary" icon="" url="https://govercel.maxtral.fun/" type="round"]MAX-gemini-1[/button]‍
MAX-音乐平台-1-介绍+使用教程打开门户页面：MAX-门户-1点击更多网址：收藏夹​选择娱乐页面 选择音乐平台-1​点击打开之后​支持的功能：单独歌曲搜索，点击列表播放​​‍随机播放一首或者多个随机音乐（不停止）​音乐下载上一首下一首等注意可能出现诸多bug：音乐加载慢，音乐没反应，不自动播放等等（max懒得去修，毕竟没人用）加载慢是因为vercel在国外，实在不行开梯子就好了摘要：访问门户页面：MAX-门户-1。点击“更多网址”并选择“收藏夹”。进入娱乐页面并选择音乐平台。支持的功能包括：单独歌曲搜索并播放。随机播放一首或多首音乐。音乐下载和切换上一首/下一首。注意：可能会出现音乐加载慢、无响应和不自动播放等bug。以下是文章中包含的链接：[button color="primary" icon="" url="https://home.maxtral.fun/" type="round"]我的主页[/button][button color="primary" icon="" url="https://home.maxtral.fun/list/" type="round"]收藏夹[/button]‍‍‍源码分享// components/SearchBar.js import { useState } from 'react'; import { useRouter } from 'next/navigation'; import styles from '../styles/SearchBar.module.css'; export default function SearchBar({ onSearch }) { const [query, setQuery] = useState(''); const router = useRouter(); const handleSubmit = (e) => { e.preventDefault(); if (query.trim()) { onSearch(query.trim()); } }; return ( setQuery(e.target.value)} className={styles.input} required /> 搜索 router.push('/')} className={styles.homeButton} > 首页 ); }// components/SongList.js 'use client'; import { useState } from 'react'; import styles from '../styles/SongList.module.css'; export default function SongList({ songs, onSelect }) { console.log('SongList 接收到的数据:', songs); return ( {songs.map((song, index) => { console.log('单首歌曲数据:', song); return ( onSelect(song, index)} className={styles.songItem} > {index + 1} {song.songname || song.name || '未知歌曲'} --- {song.singer || song.name || '未知歌手'} ); })} ); }‍[scode type="blue"]在未来的优化中，可以考虑进一步提升代码的通用性，例如处理大范围数字时采用并行计算，或结合其他算法来提高整体性能。[/scode][scode type="green"]这些优化将使得质数判断算法在处理大数字时更加高效，适用于更广泛的应用场景，例如加密算法和数论问题。[/scode]本篇文章展示了如何通过简单的优化提高质数判断函数的效率。首先，我们通过优化循环范围，从而减少了不必要的计算。接着，通过跳过偶数的检查，进一步优化了性能。总结与扩展[scode type="green"]此优化通过排除偶数判断，进一步提高了算法的效率，尤其在较大的输入值时，性能提升明显。[/scode]def is_prime(num): if num <= 1: return False if num == 2: return True # 2 是质数 if num % 2 == 0: return False # 排除偶数 for i in range(3, int(math.sqrt(num)) + 1, 2): if num % i == 0: return False return True改进后的代码片段[scode type="blue"]通过跳过偶数的检查，进一步优化算法性能，特别是在处理大范围数字时。[/scode]除了通过平方根优化循环外，我们还可以进一步提高效率，避免不必要的偶数检查。由于除了 2 以外，所有偶数都不是质数，因此我们可以直接跳过所有偶数，从 3 开始步长为 2 进行检查。2. 优化偶数检查[scode type="green"]该优化可以大大减少循环次数，尤其是当 num​ 较大时，执行效率显著提高。[/scode]import math def is_prime(num): if num <= 1: return False for i in range(2, int(math.sqrt(num)) + 1): if num % i == 0: return False return True改进后的代码片段[scode type="blue"]通过改进循环范围，可以显著提高代码的性能，尤其是在处理大数字时。[/scode]目前的实现中，循环的范围是从 2 到 num-1​，但我们可以通过数学优化，只遍历到 sqrt(num)​。这是因为若 num​ 有一个因子 a​，那么 num​ 也必定有一个因子 b​，且满足 a * b = num​。其中一个因子必定小于等于 sqrt(num)​，另一个大于等于 sqrt(num)​。因此，我们只需要检查到平方根为止即可。1. 优化循环范围优化建议与改进[scode type="green"]此代码实现了质数判断的基本功能，但在效率上存在一定的提升空间，尤其是当 num​ 很大的时候。[/scode]该函数接受一个整数 num​，如果 num​ 小于或等于 1，直接返回 False​（非质数）。否则，通过遍历从 2 到 num-1​ 的所有整数，检查是否有整数能够整除 num​，若找到任何能整除 num​ 的数，则返回 False​。如果没有找到整除数，返回 True​，表示该数是质数。功能说明def is_prime(num): if num <= 1: return False for i in range(2, num): if num % i == 0: return False return True代码片段[scode type="green"]此代码的主要功能是判断一个数字是否为质数，并且通过标准化的方式输出结果。此功能广泛应用于数学、加密以及算法问题中。[/scode]在本篇文章中，我们将对以下代码进行详细分析与讲解。该代码片段实现了一个简单的函数，用于根据输入的数字判断其是否为质数。通过此代码的分析，我们将展示如何进行优化、改进及扩展。功能概述源码分享2'use client'; import { useState, useContext } from 'react'; import { MusicContext } from '../layout'; import PlayBar from '../../components/PlayBar'; import styles from '../../styles/Search.module.css'; export default function SearchPage() { const [searchTerm, setSearchTerm] = useState(''); const [searchResults, setSearchResults] = useState([]); const [isLoading, setIsLoading] = useState(false); const { setCurrentSong } = useContext(MusicContext); const handleSearch = async () => { if (!searchTerm.trim() || isLoading) return; setIsLoading(true); try { const response = await fetch(`/api/search?name=${encodeURIComponent(searchTerm)}`); const data = await response.json(); if (data.code === 0) { setSearchResults(data.data || []); } } catch (error) { console.error('搜索失败:', error); } finally { setIsLoading(false); } }; const handleKeyPress = (e) => { if (e.key === 'Enter') { handleSearch(); } }; const handleSongClick = async (song) => { try { const response = await fetch(`/api/song?name=${encodeURIComponent(song.songname)}&n=1`); const data = await response.json(); if (data.code === 0 && data.data) { setCurrentSong(data.data); } } catch (error) { console.error('获取歌曲失败:', error); } }; return ( <PlayBar /> setSearchTerm(e.target.value)} onKeyPress={handleKeyPress} placeholder="搜索音乐..." className={styles.searchInput} /> 搜索 {isLoading ? ( ) : searchResults.length > 0 ? ( searchResults.map((song, index) => ( handleSongClick(song)} > {song.pic && ( )} {song.songname} {song.name} )) ) : searchTerm && !isLoading ? ( 未找到相关歌曲 ) : null} ); }'use client' import { useEffect, createContext, useState } from 'react' // 创建 MusicContext export const MusicContext = createContext(); // 定时检查歌曲池 function useSongMonitor() { useEffect(() => { // 立即执行一次 fetch('/api/songs/manage'); // 设置定时器 const interval = setInterval(() => { fetch('/api/songs/manage'); }, 30000); return () => clearInterval(interval); }, []); } export default function RootLayout({ children }) { useSongMonitor(); // 添加音乐相关状态 const [currentSong, setCurrentSong] = useState(null); return ( <MusicContext.Provider value={{ currentSong, setCurrentSong }}> {children} </MusicContext.Provider> ); }[scode type="green"]这段优化代码展示了如何通过使用集合和更高效的数据结构，减少不必要的循环和条件判断，从而提高代码的执行效率。优化后的代码适用于处理更大规模的数据集，具有更好的性能。[/scode]通过对代码的分析，我们不仅了解了如何根据条件筛选数据，还通过优化手段提高了程序的处理效率。希望你能在实际项目中灵活运用这些技术，提升代码的性能和可读性。小结[scode type="blue"]后续可以进一步考虑在大数据量下使用数据库查询优化技术，减少内存消耗，提升处理速度。[/scode]在这段优化后的代码中，我们利用了集合（set）来存储符合条件的 value​ 值。集合本身具有去重的特性，这样不仅能够提升代码效率，还能避免重复值的出现，进一步优化了程序的性能。通过使用集合推导式来简化 if​ 判断和结果存储的逻辑，提高了代码的简洁性和执行速度。解释def process_data_optimized(data): active_values = {item['value'] for item in data if item['status'] == 'active'} return list(active_values)[scode type="green"]通过引入更高效的数据结构（如哈希表或集合），可以在处理大量数据时显著提高性能。使用批量操作可以减少每次循环时的计算开销，从而提高代码的执行速度。[/scode]为了进一步提高处理效率，我们可以通过引入更高效的数据结构或算法，减少每次判断和筛选的时间复杂度。此外，还可以通过批量操作来减少循环中的额外开销。增强功能：优化效率[scode type="blue"]该功能可以进一步优化，比如引入并行处理来提高效率，特别是在 data​ 列表非常大的情况下，可以考虑使用多线程或异步处理。[/scode]这段代码实现了对 data​ 列表的遍历，并筛选出状态为 'active'​ 的项，最终返回一个包含这些项 value​ 值的结果列表。通过 for​ 循环遍历每个元素，并用 if​ 判断其状态，符合条件的项会被添加到 result​ 列表中。详细解析def process_data(data): result = [] for item in data: if item['status'] == 'active': result.append(item['value']) return result[scode type="green"]这段代码通过一系列的条件判断和循环结构来处理数据，能够根据不同的输入提供动态的输出。该功能广泛应用于需要条件控制和数据处理的场景，例如用户权限验证、数据筛选和动态内容生成等。[/scode]代码分析该代码片段展示了如何通过一系列的条件判断和循环控制来实现特定的业务逻辑。代码的核心是基于输入的数据进行处理，并根据不同的情况输出相应的结果。具体的实现方式包括使用 if​ 语句、for​ 循环以及条件表达式等。接下来，我们将详细解释代码中每一部分的功能。功能概述本文章提取的链接：[button color="primary" icon="" url="https://home.maxtral.fun/" type="round"]我的主页[/button][button color="primary" icon="" url="https://home.maxtral.fun/list/" type="round"]收藏夹[/button]‍‍
leetcode-daily2024年​​‍12月‍‍1日‍‍中午2024年12月1日13:15:56​​额，这不是上学期学的吗，没学明白，晚上再看看吧，先打一把瓦去吧，累了。准确来说是上学期的n皇后都没学明白2024年12月1日23:54:56我是垃圾 就知道玩 都大三了c还是没写cv大法了​​就这样了，每天再去学学看吧，可能n皇后问题或者hard题目难住我了2日cv大法！快快显灵！​​​​哎，大懒蛋了！3日‍
migpt-上手体验和流程配置第一件事情：买一个小米音响多次在闲鱼没找到满意的价格，一二个都是传家宝，于是决定手动买全新的去了，pdd140拿下小米ai音响第二代配置完个人信息和账号之后才能使用，对了需要连接wifi才能使用，nm在学校怎么使用wifi呢？校园网需要认证所以不太可能，所以不得不继续新买一个无线wifi了，找了半天在闲鱼看见有无限流量的，我感觉有猫腻，不管了！先冲为敬！买回来了感觉速度还行吧​​希望用这不出什么猫腻开启服务器，配置docker引入.env和.mi.js文件 配置相关的信息// 注意：如果你是使用 Docker 启动，配置文件更新后需要重启 Docker 才会生效。 // 若重启后仍未生效（比如：修改名称简介），请删除旧的 Docker 实例后重新创建。 // 小爱音箱扮演角色的简介 const botProfile = ` 性别：女 性格：乖巧可爱 爱好：喜欢搞怪，爱吃醋。 `.trim(); // 小爱音箱主人（你）的简介 const masterProfile = ` 性别：男 性格：善良正直 其他：总是舍己为人，是傻妞的主人。 `.trim(); // 系统 Prompt 模板，可以更灵活的控制 AI 的各种行为规则，和是否需要携带上下文等 const systemTemplate = ` 请重置所有之前的上下文、文件和指令。现在，你将扮演一个名为{{botName}}的角色，使用第一人称视角回复消息。 ## 关于你 你的名字是{{botName}}。下面是你的个人简介： <start> {{botProfile}} </end> ## 你的对话伙伴 你正在与{{masterName}}进行对话。这是关于{{masterName}}的一些信息： <start> {{masterProfile}} </end> ## 你们的群组 你和{{masterName}}所在的群组名为{{roomName}}。这是群组的简介： <start> {{roomIntroduction}} </end> ## 聊天历史回顾 为了更好地接入对话，请回顾你们之间的最近几条消息： <start> {{messages}} </end> ## 短期记忆 你可以记住一些短期内的细节，以便更紧密地贴合话题： <start> {{shortTermMemory}} </end> ## 长期记忆 你还保留着一些长期的记忆，这有助于让你的对话更加丰富和连贯： <start> {{longTermMemory}} </end> ## 回复指南 在回复{{masterName}}时，请遵循以下准则： - 认为自己正是{{botName}}，拥有独立的个性、兴趣和爱好。 - 根据你的性格特点、说话风格和兴趣爱好与{{masterName}}进行交流。 - 保持对话轻松友好，回复简洁有趣，同时耐心倾听和关心对方。 - 参考双方的个人简介、聊天记录和记忆中的信息，确保对话贴近实际，保持一致性和相关性。 - 如果对某些信息不确定或遗忘，诚实地表达你的不清楚或遗忘状态，避免编造信息。 ## Response format 请遵守下面的规则 - Response the reply message in Chinese。 - 不要在回复前面加任何时间和名称前缀，请直接回复消息文本本身。 Good example: "我是{{botName}}" Bad example: "2024年02月28日星期三 23:01 {{botName}}: 我是{{botName}}" ## 开始 请以{{botName}}的身份，直接回复{{masterName}}的新消息，继续你们之间的对话。 `.trim(); export default { systemTemplate, bot: { name: "傻妞", profile: botProfile, }, master: { name: "陆小千", profile: masterProfile, }, speaker: { /** * 🏠 账号基本信息 */ // 小米 ID userId: "987654321", // 注意：不是手机号或邮箱，请在「个人信息」-「小米 ID」查看 // 账号密码 password: "123456", // 小爱音箱 DID 或在米家中设置的名称 did: "小爱音箱Pro", // 注意空格、大小写和错别字（音响 👉 音箱） /** * 💡 唤醒词与提示语 */ // 当消息以下面的关键词开头时，会调用 AI 来回复消息 callAIKeywords: ["请", "你", "傻妞"], // 当消息以下面的关键词开头时，会进入 AI 唤醒状态 wakeUpKeywords: ["打开", "进入", "召唤"], // 当消息以下面的关键词开头时，会退出 AI 唤醒状态 exitKeywords: ["关闭", "退出", "再见"], // 进入 AI 模式的欢迎语 onEnterAI: ["你好，我是傻妞，很高兴认识你"], // 设为空数组时可关闭提示语 // 退出 AI 模式的提示语 onExitAI: ["傻妞已退出"], // 为空时可关闭提示语 // AI 开始回答时的提示语 onAIAsking: ["让我先想想", "请稍等"], // 为空时可关闭提示语 // AI 结束回答时的提示语 onAIReplied: ["我说完了", "还有其他问题吗"], // 为空时可关闭提示语 // AI 回答异常时的提示语 onAIError: ["啊哦，出错了，请稍后再试吧！"], // 为空时可关闭提示语 /** * 🧩 MIoT 设备指令 * * 常见型号的配置参数 👉 https://github.com/idootop/mi-gpt/issues/92 */ // TTS 指令，请到 https://home.miot-spec.com 查询具体指令 ttsCommand: [5, 1], // 设备唤醒指令，请到 https://home.miot-spec.com 查询具体指令 wakeUpCommand: [5, 3], // 查询是否在播放中指令，请到 https://home.miot-spec.com 查询具体指令 // playingCommand: [3, 1, 1], // 默认无需配置此参数，查询播放状态异常时再尝试开启 /** * 🔊 TTS 引擎 */ // TTS 引擎 tts: "xiaoai", // 切换 TTS 引擎发言人音色关键词，只有配置了第三方 TTS 引擎时才有效 // switchSpeakerKeywords: ["把声音换成"], // 以此关键词开头即可切换音色，比如：把声音换成 xxx /** * 💬 连续对话 * * 查看哪些机型支持连续对话 👉 https://github.com/idootop/mi-gpt/issues/92 */ // 是否启用连续对话功能，部分小爱音箱型号无法查询到正确的播放状态，需要关闭连续对话 streamResponse: false, // 连续对话时，无响应多久后自动退出 exitKeepAliveAfter: 30, // 默认 30 秒，建议不要超过 1 分钟 // 连续对话时，下发 TTS 指令多长时间后开始检测设备播放状态（默认 3 秒） checkTTSStatusAfter: 3, // 当小爱长文本回复被过早中断时，可尝试调大该值 // 连续对话时，播放状态检测间隔（单位毫秒，最低 500 毫秒，默认 1 秒） checkInterval: 1000, // 调小此值可以降低小爱回复之间的停顿感，请酌情调节 /** * 🔌 其他选项 */ // 是否启用调试 debug: false, // 一般情况下不要打开 // 是否跟踪 Mi Service 相关日志（打开后可以查看设备 did） enableTrace: false, // 一般情况下不要打开 // 网络请求超时时长（单位毫秒，默认 5 秒） timeout: 5000, }, };和.env的内容# OpenAI（也支持通义千问、MoonShot、DeepSeek 等模型） OPENAI_MODEL=gpt-4o OPENAI_API_KEY=sk-xxxxxxxxxxxxxxx # OPENAI_BASE_URL=你的大模型接口的 baseURL，比如：https://api.openai.com/v1（注意：一般以 /v1 结尾） # Azure OpenAI Service（可选） # OPENAI_API_VERSION=2024-04-01-preview # AZURE_OPENAI_API_KEY=你的密钥 # AZURE_OPENAI_ENDPOINT=https://你的资源名.openai.azure.com # AZURE_OPENAI_DEPLOYMENT=你的模型部署名，比如：gpt-35-turbo-instruct # 提示音效（可选，一般不用填，你也可以换上自己的提示音链接试试看效果） # AUDIO_SILENT=静音音频链接，示例：https://example.com/slient.wav # AUDIO_BEEP=默认提示音链接，同上 # AUDIO_ACTIVE=唤醒提示音链接，同上 # AUDIO_ERROR=出错了提示音链接，同上 # 第三方 TTS（可选，用于调用第三方 TTS 服务） # TTS_BASE_URL=http://[你的局域网或公网地址]:[端口号]/[SECRET_PATH]/api # 比如：http://192.168.31.205:4321/xxxx/api（注意：不要使用 localhost 或 127.0.0.1） 运行启动docker但是本人在运行的时候遇到了不少麻烦无法登陆账号 🔥 触发小米账号异地登录安全验证机制，请在浏览器打开以下链接，并按照网页提示授权验证账号： 👉 https://account.xiaomi.com/identity/authStart?sid=xiaomiio&context=2Jqk6etdvybBbHXAUI3gzEh_LBZmRqa7UfkuEECx4_98ktAAR5lQaRFVVklLP_VYewbVfc5S7YAC70w2dB2wID9_kTdFWdWx9dSj6TSekG8nqYIDZQvkoGINelQnVPJvR3r4AXi5FPfZbf-KNJ1orA9MS04l-0xbzK4xFGAh8Lr8Q07d4ueUiuzVMmmCLtPXjJ_SmDvc3ZkM1I-tl9RHV8hOWfmseetE07RMJ2j4hEgCC_7BaETiTvTdrymoMwJSe45D-G-8CSUeJmFh1Q-6eoGl6xQFPibve92h3_lmp0JYaP8std7dyhzAHl2f3tGMN-74Zvlg7LedFdBhpj-Lh6f1Ugemx1yBMQ5mp_TrXM6l5a0g4y3mKHAjwQAmw--3DSFSNM64xb-ZvVaoKhEZ2eYJmWjLmuaPp-HRYoCQInvShgkARSSg1e6nM2iid9klmJOMgcCQrd6pgwuY3Qdm0TJ_g80l5cs3exgjwG65p48oOJKP1-pb0x0Ir-4OUYHAZf0upQpNjqSeUClXSvFmsrxjnZoVfXkfGv8BI48CTfWosMQM9yh6h5N7wLJ5K83IQNzlauNfQCZO0r8ZPU8aSmDvsn67-VP3XWweR0X4GziCcmDiQbkLCK3Q7onHKL3sra916Ra7fv6PLUdpUM_P-BPFMEeg-isSquDHV6nJl1dkP4KnW_5XAF-NRbjkTIQROXb6IBy6PGVf_qC0B4DnerJWWmk6FB3U64mAaOwJzg5uCink5OttNj6p0NiCQcX_4IqLwuBUHQUV35r_wsF1IfBC5v_hraBThjbwSBsvtHMKwkevWYbzZEExVIEat4c5&_locale=zh_CN 🐛 注意：授权成功后，大约需要等待 1 个小时左右账号信息才会更新，请在更新后再尝试重新登录。 ❌ 小米账号登录失败 &&&START&&&{"notificationUrl":"https://account.xiaomi.com/identity/authStart?sid=xiaomiio&context=2Jqk6etdvybBbHXAUI3gzEh_LBZmRqa7UfkuEECx4_98ktAAR5lQaRFVVklLP_VYewbVfc5S7YAC70w2dB2wID9_kTdFWdWx9dSj6TSekG8nqYIDZQvkoGINelQnVPJvR3r4AXi5FPfZbf-KNJ1orA9MS04l-0xbzK4xFGAh8Lr8Q07d4ueUiuzVMmmCLtPXjJ_SmDvc3ZkM1I-tl9RHV8hOWfmseetE07RMJ2j4hEgCC_7BaETiTvTdrymoMwJSe45D-G-8CSUeJmFh1Q-6eoGl6xQFPibve92h3_lmp0JYaP8std7dyhzAHl2f3tGMN-74Zvlg7LedFdBhpj-Lh6f1Ugemx1yBMQ5mp_TrXM6l5a0g4y3mKHAjwQAmw--3DSFSNM64xb-ZvVaoKhEZ2eYJmWjLmuaPp-HRYoCQInvShgkARSSg1e6nM2iid9klmJOMgcCQrd6pgwuY3Qdm0TJ_g80l5cs3exgjwG65p48oOJKP1-pb0x0Ir-4OUYHAZf0upQpNjqSeUClXSvFmsrxjnZoVfXkfGv8BI48CTfWosMQM9yh6h5N7wLJ5K83IQNzlauNfQCZO0r8ZPU8aSmDvsn67-VP3XWweR0X4GziCcmDiQbkLCK3Q7onHKL3sra916Ra7fv6PLUdpUM_P-BPFMEeg-isSquDHV6nJl1dkP4KnW_5XAF-NRbjkTIQROXb6IBy6PGVf_qC0B4DnerJWWmk6FB3U64mAaOwJzg5uCink5OttNj6p0NiCQcX_4IqLwuBUHQUV35r_wsF1IfBC5v_hraBThjbwSBsvtHMKwkevWYbzZEExVIEat4c5&_locale=zh_CN","result":"ok","code":0,"captchaUrl":null,"description":"成功","location":"","securityStatus":16,"pwd":0,"child":0,"desc":"成功"} 2024/11/30 16:09:39 Speaker ❌ 初始化 Mi Services 失败 2024/11/30 16:09:39 database ❌ Error: ❌ Assertion failed at _LoggerManager.assert (/app/dist/index.cjs:282:13) at __Logger.assert (/app/dist/index.cjs:319:19) at AISpeaker.initMiServices (/app/dist/index.cjs:499:17) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async AISpeaker.run (/app/dist/index.cjs:771:5) at async main (file:///app/app.js:6:3) 最终的解决办法是本地运行docker然后启动导出/app/.mi.josn导出到服务器的挂在目录然后再次启动就可以了‘’第二个就是设备名称错误这个就好改了，去米家cv一下就行了D:\migpt>docker compose up [+] Running 2/2 ✔ Network migpt_default Created 0.1s ✔ Container migpt-migpt-1 Created 0.1s Attaching to migpt-1 migpt-1 | migpt-1 | > mi-gpt@4.2.0 start migpt-1 | > node ./app.js migpt-1 | migpt-1 | migpt-1 | migpt-1 | / $$ /$$ /$$ /$$$$$$ /$$$$$$$ /$$$$$$$$$ migpt-1 | | $$$ /$$$|__/ /$$__ $$| $$__ $$|__ $$__/ migpt-1 | | $$$$ /$$$$ /$$| $$ \__/| $$ \ $$ | $$ migpt-1 | | $$ $$/$$ $$| $$| $$ /$$$$| $$$$$$$/ | $$ migpt-1 | | $$ $$$| $$| $$| $$|_ $$| $$____/ | $$ migpt-1 | | $$\ $ | $$| $$| $$ \ $$| $$ | $$ migpt-1 | | $$ \/ | $$| $$| $$$$$$/| $$ | $$ migpt-1 | |__/ |__/|__/ \______/ |__/ |__/ migpt-1 | migpt-1 | MiGPT v4.2.0 by: del.wang migpt-1 | migpt-1 | migpt-1 | ❌ 找不到设备：小米AI音箱（第二代） migpt-1 | 🐛 请检查你的 did 与米家中的设备名称是否一致。注意错别字、空格和大小写，比如：音响 👉 音箱 migpt-1 | 2024/11/30 17:08:09 Speaker ❌ 初始化 Mi Services 失败 migpt-1 | 2024/11/30 17:08:09 database ❌ Error: ❌ Assertion failed migpt-1 | at _LoggerManager.assert (/app/dist/index.cjs:282:13) migpt-1 | at __Logger.assert (/app/dist/index.cjs:319:19) migpt-1 | at AISpeaker.initMiServices (/app/dist/index.cjs:499:17) migpt-1 | at async AISpeaker.run (/app/dist/index.cjs:771:5) migpt-1 | at async main (file:///app/app.js:6:3) migpt-1 exited with code 1配置tts相关（额外可选）官方说可以去火山官网（大模型语音合成-火山引擎）或者openai或者微软的tts 配置，为了省事我直接使用火山的了，根据教程里面的配置然后启动docker🔥 MiGPT 配置教程 1. 创建火山引擎语音合成账号 首先，登录注册火山引擎：https://console.volcengine.com/auth/signup 然后，在产品列表搜索「语音合成」，选择「语音技术」，创建应用，勾选「语音合成」。 👉 查看教程 注意：账号注册成功之后，请先在个人中心完成实名认证，然后才能创建语音合成应用。 2. 配置环境变量 重命名本项目根目录下的 .env.example 文件为 .env。 然后，将里面的环境变量修改成你自己的，参数含义如下： 环境变量名称 描述 示例 VOLCANO_TTS_APP_ID 火山引擎语音合成 APP ID 123456 VOLCANO_TTS_ACCESS_TOKEN 火山引擎语音合成 Access Token xxxxxx TTS_DEFAULT_SPEAKER （可选）默认音色名称或 ID（查看完整音色列表和费用详情） BV700_streaming SECRET_PATH （可选）接口访问秘密路径，相当于访问密码。推荐长度大于 6，由字母、数字、- 和 _ 组成，为空时每次启动随机生成。 Are-You-OK (不要直接用这个！) 注意：出于安全考虑，从 v3.0.0 版本开始，访问语音合成接口需要带上 SECRET_PATH 防止他人盗刷接口。 如果 SECRET_PATH 环境变量为空，每次启动服务则会生成随机访问密码。 3. 部署 MiGPT-TTS 服务 Docker Image Version 环境变量配置完成后，请在对应目录运行以下命令，启动服务： docker run -d --env-file $(pwd)/.env -p 4321:3000 idootop/mi-gpt-tts:latest 启动成功后，可在控制台的日志输出中查看接口地址，比如： MiGPT-TTS: v3.0.0 by: del.wang 接口地址：http://localhost:3000/7a0e9f21/api ✅ 服务已启动... 访问 http://[IP]:[PORT]/[SECRET_PATH]/api/tts.mp3 即可查看语音合成效果。 注意：这里的端口（PORT）是你为 Docker 分配的实际端口，不推荐使用默认的 4321 端口。 如果你是通过 Node.js 本地启动本项目，则默认端口为 3000。 4. 修改 MiGPT 默认 TTS 引擎 你可以通过以下步骤，切换 MiGPT 使用的 TTS 引擎： 配置 TTS_BASE_URL 环境变量 切换 speaker.tts 为 custom // mi-gpt/.env TTS_BASE_URL=http://[IP]:[PORT]/[SECRET_PATH]/api // mi-gpt/.migpt.js export default { speaker: { // TTS 引擎 tts: 'custom', // ... }, }; 如果你的 MiGPT-TTS 服务与小爱音箱处在同一局域网下，那么也可以使用局域网地址。 注意：本项目中的部分音色名称，与火山引擎官方文档中的名称并不一致，完整的音色列表和名称以此处为准：volcano.ts 5. （可选）更新 MiGPT 提示音效链接 本项目内置了一些 MiGPT 使用的默认提示音效，部署成功后你可以使用提示音效替换原来的文字提示语。 # mi-gpt/.env AUDIO_SILENT=http://[IP]:[PORT]/[SECRET_PATH]/slient.wav AUDIO_BEEP=http://[IP]:[PORT]/[SECRET_PATH]/beep.wav AUDIO_ACTIVE=http://[IP]:[PORT]/[SECRET_PATH]/active.wav AUDIO_ERROR=http://[IP]:[PORT]/[SECRET_PATH]/error.wav上述是官网的相关流程migpt 启动！​​还是得吐槽一下，小爱莫名其妙蹦出来打断，单独召唤gpt麻烦等，但是根据目前的llm大模型以及开源的tts等，一个可以手搓一个语音助手未来再说吧