├── .vscode ├── c_cpp_properties.json ├── launch.json └── settings.json ├── LLM_python └── LLM.py ├── README.md ├── UI ├── __pycache__ │ ├── audit_log.cpython-39.pyc │ └── calendar.cpython-311.pyc ├── audit_log.py ├── audit_log.txt ├── main.py └── schedules │ ├── 2024_10_8.txt │ ├── 2024_5_16.txt │ ├── 2024_9_24.txt │ ├── 2024_9_25.txt │ └── 2025_4_10.txt ├── assets └── usage.png ├── audit_log.txt ├── kernel └── save_to_file.cpp ├── requirements.txt ├── schedules ├── 2024_12_31.txt └── 2025_4_8.txt └── 文档资料 ├── pc端日程表生成器.pptx └── 思路.docx /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "windows-gcc-x64", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "compilerPath": "D:/mingw64/bin/gcc.exe", 9 | "cStandard": "${default}", 10 | "cppStandard": "${default}", 11 | "intelliSenseMode": "windows-gcc-x64", 12 | "compilerArgs": [ 13 | "" 14 | ] 15 | } 16 | ], 17 | "version": 4 18 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "C/C++ Runner: Debug Session", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "args": [], 9 | "stopAtEntry": false, 10 | "externalConsole": true, 11 | "cwd": "d:/Desktop/Convenient-calendar-automatic-generator/kernel", 12 | "program": "d:/Desktop/Convenient-calendar-automatic-generator/kernel/build/Debug/outDebug", 13 | "MIMode": "gdb", 14 | "miDebuggerPath": "gdb", 15 | "setupCommands": [ 16 | { 17 | "description": "Enable pretty-printing for gdb", 18 | "text": "-enable-pretty-printing", 19 | "ignoreFailures": true 20 | } 21 | ] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp_Runner.cCompilerPath": "gcc", 3 | "C_Cpp_Runner.cppCompilerPath": "g++", 4 | "C_Cpp_Runner.debuggerPath": "gdb", 5 | "C_Cpp_Runner.cStandard": "", 6 | "C_Cpp_Runner.cppStandard": "", 7 | "C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvarsall.bat", 8 | "C_Cpp_Runner.useMsvc": false, 9 | "C_Cpp_Runner.warnings": [ 10 | "-Wall", 11 | "-Wextra", 12 | "-Wpedantic", 13 | "-Wshadow", 14 | "-Wformat=2", 15 | "-Wcast-align", 16 | "-Wconversion", 17 | "-Wsign-conversion", 18 | "-Wnull-dereference" 19 | ], 20 | "C_Cpp_Runner.msvcWarnings": [ 21 | "/W4", 22 | "/permissive-", 23 | "/w14242", 24 | "/w14287", 25 | "/w14296", 26 | "/w14311", 27 | "/w14826", 28 | "/w44062", 29 | "/w44242", 30 | "/w14905", 31 | "/w14906", 32 | "/w14263", 33 | "/w44265", 34 | "/w14928" 35 | ], 36 | "C_Cpp_Runner.enableWarnings": true, 37 | "C_Cpp_Runner.warningsAsError": false, 38 | "C_Cpp_Runner.compilerArgs": [], 39 | "C_Cpp_Runner.linkerArgs": [], 40 | "C_Cpp_Runner.includePaths": [], 41 | "C_Cpp_Runner.includeSearch": [ 42 | "*", 43 | "**/*" 44 | ], 45 | "C_Cpp_Runner.excludeSearch": [ 46 | "**/build", 47 | "**/build/**", 48 | "**/.*", 49 | "**/.*/**", 50 | "**/.vscode", 51 | "**/.vscode/**" 52 | ], 53 | "C_Cpp_Runner.useAddressSanitizer": false, 54 | "C_Cpp_Runner.useUndefinedSanitizer": false, 55 | "C_Cpp_Runner.useLeakSanitizer": false, 56 | "C_Cpp_Runner.showCompilationTime": false, 57 | "C_Cpp_Runner.useLinkTimeOptimization": false, 58 | "C_Cpp_Runner.msvcSecureNoWarnings": false 59 | } -------------------------------------------------------------------------------- /LLM_python/LLM.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | 4 | # 定义API的URL 5 | API_URL = "http://222.20.126.129:11434/api/generate" 6 | 7 | # 模拟的函数来生成请求数据 8 | def http_post_generate(post_data, context): 9 | headers = { 10 | "Content-Type": "application/json; charset=UTF-8", 11 | "flag": "libcurl", 12 | "Accept-Encoding": "gzip, deflate, br" 13 | } 14 | 15 | # 发起POST请求 16 | response = requests.post(API_URL, headers=headers, data=post_data) 17 | 18 | # 如果请求成功,处理返回的数据 19 | if response.status_code == 200: 20 | print(f"code: {response.status_code}") 21 | response_json = response.json() 22 | 23 | # 打印调试信息 24 | print(f"AI response: {response_json['response']}") 25 | 26 | # 更新上下文 27 | context[:] = response_json["context"] # 更新context 28 | return response_json['response'] 29 | else: 30 | print(f"Error: {response.status_code}") 31 | print(response.text) 32 | 33 | 34 | # 转换为UTF-8的函数 35 | def to_utf8(input_str): 36 | return input_str.encode('utf-8').decode('utf-8') 37 | 38 | 39 | # 主函数 40 | def main(): 41 | # print("Put 1 to exit") 42 | 43 | # 输入提示词和问题 44 | hint_for_judge_str="\n上述的信息能够转化为日程信息吗,如果能回复\"Yes\",否则回复\"No\"。" 45 | # hint_for_judge_str="打印上述的信息" 46 | # hint_str = "接下来我将给你发送日志信息,首先请找出具体时间、地点和重要内容,且重要内容要比较详细,回复的具体格式如下“时间:时间信息\n地点:地点信息\n重要内容:内容信息\nEND。如果是线上的将地点替换为会议号或其它相关链接且链接的含义或者内容要写明。如果没有指出地址、时间可以空着。" 47 | hint_str = "分析发给你的信息,回复的具体格式:\"具体时间:具体时间如几点几分、早上还是晚上\n地点:地点信息\n内容(若无可省略):内容信息\nEND\"。如果是线上的将地点替换为会议号或其它相关链接且链接的含义或者内容要写明。\n" 48 | hint_of_data="分析发给你的信息,识别其中的年月日若无年或月可省略,回复格式为:\"xx年xx月xx日,比如2021年3月4日\";若无年月日则识别例如\"今天\"\"明天\"\"后天\"的字并直接回复" 49 | propt_str = input("请输入您的问题:") # 获取用户输入的问题 50 | 51 | # 定义上下文 52 | context = [] 53 | 54 | # # 使用用户输入的问题进行后续处理 55 | # post_body = { 56 | # "model": "llama3.1", # 选择的大模型 57 | # "prompt": propt_str, # 转换输入问题为UTF-8 58 | # "stream": False, # 以非流式(不必在意的参数) 59 | # "context": context # 用于保存对话的上下文 60 | # } 61 | 62 | # # 将请求体转为JSON格式 63 | # post_data = json.dumps(post_body, ensure_ascii=False).encode('utf-8') 64 | 65 | # # 分析日志信息API 66 | # http_post_generate(post_data, context) 67 | 68 | hint_for_judge_str=propt_str+hint_for_judge_str 69 | # print(hint_for_judge_str) 70 | # 准备POST请求体的内容 71 | post_body = { 72 | "model": "llama3.1", # 选择的大模型 73 | "prompt": hint_for_judge_str, # 转换提示词为UTF-8 74 | "stream": False, # 以非流式(不必在意的参数) 75 | "context": context # 用于保存对话的上下文 76 | } 77 | 78 | # 将请求体转为JSON格式 79 | post_data = json.dumps(post_body, ensure_ascii=False).encode('utf-8') 80 | 81 | # 调用API 82 | judge=http_post_generate(post_data, context) 83 | 84 | 85 | 86 | 87 | 88 | if judge[:3]=="Yes": 89 | # 准备POST请求体的内容 90 | # hint_str+=propt_str 91 | post_body = { 92 | "model": "llama3.1", # 选择的大模型 93 | "prompt": hint_str, # 转换提示词为UTF-8 94 | "stream": False, # 以非流式(不必在意的参数) 95 | "context": context # 用于保存对话的上下文 96 | } 97 | 98 | # 将请求体转为JSON格式 99 | post_data = json.dumps(post_body, ensure_ascii=False).encode('utf-8') 100 | 101 | # 调用API 102 | http_post_generate(post_data, context) 103 | 104 | # 获取日期信息,为了保存日程 105 | post_body = { 106 | "model": "llama3.1", # 选择的大模型 107 | "prompt": hint_of_data, # 转换提示词为UTF-8 108 | "stream": False, # 以非流式(不必在意的参数) 109 | "context": context # 用于保存对话的上下文 110 | } 111 | 112 | # 将请求体转为JSON格式 113 | post_data = json.dumps(post_body, ensure_ascii=False).encode('utf-8') 114 | 115 | # 调用API 116 | http_post_generate(post_data, context) 117 | else: 118 | print("不是日志信息") 119 | 120 | if __name__ == "__main__": 121 | main() 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ctrl-c_calendar 2 | 3 | 4 | **ctrl-c_calendar** 是一个基于LLM的便捷生成日程工具,特点是简洁易用、一目了然,同时AI赋能。 5 | 6 | ## 环境配置 7 | 8 | 首先克隆本仓库。 9 | 10 | ```bash 11 | git clone https://github.com/gitveg/ctrl-c_calendar.git 12 | ``` 13 | 14 | 然后安装依赖 15 | ```bash 16 | pip install -r requirements.txt 17 | ``` 18 | 19 | 启动程序 20 | ```bash 21 | cd UI 22 | python main.py 23 | ``` 24 | 25 | ## 使用方法 26 | 27 | 复制一段带有日程信息的文字,然后点击"粘贴复制内容给AI分析",AI会自动分析判断是否生成日程表。 28 | 29 | ![usage](https://github.com/gitveg/ctrl-c_calendar/blob/main/assets/usage.png) 30 | -------------------------------------------------------------------------------- /UI/__pycache__/audit_log.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitveg/ctrl-c_calendar/39a907a07854d80c76397665dc337ec6b43d3747/UI/__pycache__/audit_log.cpython-39.pyc -------------------------------------------------------------------------------- /UI/__pycache__/calendar.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitveg/ctrl-c_calendar/39a907a07854d80c76397665dc337ec6b43d3747/UI/__pycache__/calendar.cpython-311.pyc -------------------------------------------------------------------------------- /UI/audit_log.py: -------------------------------------------------------------------------------- 1 | # audit_log.py 2 | import logging 3 | from datetime import datetime 4 | 5 | print("audit_log.py 开始执行...") 6 | 7 | # 设置日志记录器 8 | try: 9 | logging.basicConfig(filename="audit_log.txt", level=logging.INFO, 10 | format='%(asctime)s - %(levelname)s - %(message)s') 11 | print("日志配置成功") 12 | except Exception as e: 13 | print(f"日志配置失败: {str(e)}") 14 | raise 15 | 16 | def log_operation(operation_name, status, details=None): 17 | """记录操作到审计日志""" 18 | try: 19 | message = f"Operation: {operation_name} - Status: {status}" 20 | if details: 21 | message += f" - Details: {details}" 22 | logging.info(message) 23 | print(f"操作已记录: {message}") 24 | except Exception as e: 25 | print(f"记录操作失败: {str(e)}") -------------------------------------------------------------------------------- /UI/audit_log.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitveg/ctrl-c_calendar/39a907a07854d80c76397665dc337ec6b43d3747/UI/audit_log.txt -------------------------------------------------------------------------------- /UI/main.py: -------------------------------------------------------------------------------- 1 | print("程序开始执行...") 2 | 3 | print("正在导入 tkinter...") 4 | import tkinter as tk 5 | print("tkinter 导入成功") 6 | 7 | print("正在导入 ttk...") 8 | from tkinter import ttk 9 | print("ttk 导入成功") 10 | 11 | print("正在导入 datetime...") 12 | from datetime import datetime, timedelta 13 | print("datetime 导入成功") 14 | 15 | print("正在导入 calendar...") 16 | import calendar 17 | print("calendar 导入成功") 18 | 19 | print("正在导入 os...") 20 | import os 21 | print("os 导入成功") 22 | 23 | print("正在导入 messagebox...") 24 | from tkinter import messagebox 25 | print("messagebox 导入成功") 26 | 27 | print("正在导入 json...") 28 | import json 29 | print("json 导入成功") 30 | 31 | print("正在导入 requests...") 32 | import requests 33 | print("requests 导入成功") 34 | 35 | print("正在导入 re...") 36 | import re 37 | print("re 导入成功") 38 | 39 | print("正在导入 audit_log...") 40 | import audit_log 41 | print("audit_log 导入成功") 42 | 43 | print("正在导入 hashlib...") 44 | import hashlib 45 | print("hashlib 导入成功") 46 | 47 | print("正在导入 logging...") 48 | import logging 49 | print("logging 导入成功") 50 | 51 | print("正在导入 socket...") 52 | import socket 53 | print("socket 导入成功") 54 | 55 | print("正在导入 ssl...") 56 | import ssl 57 | print("ssl 导入成功") 58 | 59 | print("正在导入 uuid...") 60 | import uuid 61 | print("uuid 导入成功") 62 | 63 | print("正在导入 time...") 64 | import time 65 | print("time 导入成功") 66 | 67 | print("正在导入 threading...") 68 | import threading 69 | print("threading 导入成功") 70 | 71 | print("正在导入 base64...") 72 | import base64 73 | print("base64 导入成功") 74 | 75 | print("正在导入 wraps...") 76 | from functools import wraps 77 | print("wraps 导入成功") 78 | 79 | print("所有模块导入完成") 80 | 81 | # 设置日志记录 82 | print("正在配置日志...") 83 | logging.basicConfig( 84 | level=logging.INFO, 85 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 86 | filename='calendar_app.log', 87 | filemode='a' 88 | ) 89 | logger = logging.getLogger('calendar_app') 90 | print("日志配置完成") 91 | 92 | # 全局变量 93 | year = datetime.now().year 94 | month = datetime.now().month 95 | selected_day = datetime.now().day 96 | schedule_dir = "schedules" 97 | 98 | # 自定义颜色 99 | COLORS = { 100 | 'primary': '#4A90E2', 101 | 'secondary': '#50E3C2', 102 | 'success': '#7ED321', 103 | 'danger': '#FF4D4F', 104 | 'warning': '#F5A623', 105 | 'background': '#F8F9FA', 106 | 'card': '#FFFFFF', 107 | 'text': '#2C3E50', 108 | 'text_light': '#7F8C8D', 109 | 'border': '#E0E0E0', 110 | 'hover': '#F0F7FF', 111 | 'today': '#4A90E2', 112 | 'schedule': '#7ED321', 113 | 'weekend': '#FF4D4F' 114 | } 115 | 116 | # 自定义字体 117 | title_font = ("Microsoft YaHei UI", 20, "bold") 118 | bold_font = ("Microsoft YaHei UI", 14, "bold") 119 | normal_font = ("Microsoft YaHei UI", 12) 120 | 121 | # 安全配置 122 | class SecurityConfig: 123 | MAX_LOGIN_ATTEMPTS = 3 124 | SESSION_TIMEOUT = 1800 # 30分钟 125 | MAX_FILE_SIZE = 1024 * 1024 # 1MB 126 | ALLOWED_FILE_TYPES = ['.txt'] 127 | MAX_CONTENT_LENGTH = 10000 # 字符数 128 | API_TIMEOUT = 15 # API请求超时时间(秒) 129 | 130 | # 用户会话管理 131 | class UserSession: 132 | def __init__(self): 133 | self.last_activity = datetime.now() 134 | self.login_attempts = 0 135 | self.is_locked = False 136 | 137 | def update_activity(self): 138 | self.last_activity = datetime.now() 139 | 140 | def check_expiry(self): 141 | return (datetime.now() - self.last_activity).total_seconds() > SecurityConfig.SESSION_TIMEOUT 142 | 143 | def increment_login_attempts(self): 144 | self.login_attempts += 1 145 | if self.login_attempts >= SecurityConfig.MAX_LOGIN_ATTEMPTS: 146 | self.is_locked = True 147 | 148 | def reset(self): 149 | self.last_activity = datetime.now() 150 | self.login_attempts = 0 151 | self.is_locked = False 152 | 153 | # 创建用户会话实例 154 | user_session = UserSession() 155 | 156 | # 安全装饰器 157 | def audit_operation(operation_name): 158 | def decorator(func): 159 | @wraps(func) 160 | def wrapper(*args, **kwargs): 161 | logger.info(f"Performing {operation_name}") 162 | try: 163 | result = func(*args, **kwargs) 164 | logger.info(f"Successfully completed {operation_name}") 165 | audit_log.log_operation(operation_name, "SUCCESS") 166 | return result 167 | except Exception as e: 168 | logger.error(f"Error in {operation_name}: {str(e)}") 169 | audit_log.log_operation(operation_name, "FAILURE", str(e)) 170 | raise 171 | return wrapper 172 | return decorator 173 | 174 | def check_session_expiry(func): 175 | @wraps(func) 176 | def wrapper(*args, **kwargs): 177 | if user_session.check_expiry(): 178 | logger.warning("Session expired") 179 | messagebox.showwarning("安全警告", "会话已过期,请重新登录") 180 | return None 181 | user_session.update_activity() 182 | return func(*args, **kwargs) 183 | return wrapper 184 | 185 | # API相关函数 186 | @audit_operation("API请求") 187 | def secure_api_request(url, headers, data, timeout=SecurityConfig.API_TIMEOUT): 188 | try: 189 | # 创建SSL上下文 190 | context = ssl.create_default_context() 191 | context.check_hostname = False 192 | context.verify_mode = ssl.CERT_NONE 193 | 194 | # 执行请求 195 | response = requests.post( 196 | url, 197 | headers=headers, 198 | json=data, 199 | timeout=timeout, 200 | verify=False 201 | ) 202 | response.raise_for_status() 203 | 204 | return response.json() 205 | except requests.exceptions.RequestException as e: 206 | logger.error(f"API请求错误: {str(e)}") 207 | raise 208 | 209 | def http_post_generate(post_data, context): 210 | try: 211 | print("准备发送HTTP POST请求...") 212 | # 处理不同类型的数据 213 | if context == "分析日程": 214 | url = "http://222.20.126.129:11434/api/generate" 215 | headers = { 216 | "Content-Type": "application/json" 217 | } 218 | data = { 219 | "model": "llama2", 220 | "prompt": post_data, 221 | "stream": False 222 | } 223 | 224 | else: 225 | logger.error("未知的请求上下文") 226 | raise ValueError("未知的请求上下文") 227 | 228 | print("发送请求...") 229 | print(f"URL: {url}") 230 | print(f"数据: {data}") 231 | 232 | response = secure_api_request(url, headers, data) 233 | print("请求成功") 234 | return response 235 | except Exception as e: 236 | logger.error(f"HTTP POST请求失败: {str(e)}") 237 | print(f"请求失败: {str(e)}") 238 | raise 239 | 240 | # 日历功能函数 241 | @check_session_expiry 242 | def LLM_func(propt_str): 243 | # 输入提示词和问题 244 | try: 245 | print("分析文本是否包含日程信息...") 246 | full_prompt = f""" 247 | 请分析以下文本是否包含日程信息。 248 | 如果包含日程信息,请提取出日期、时间、地点、事项等信息,并以JSON格式返回。 249 | 如果文本不包含日程信息,请返回"false"。 250 | 251 | 文本内容:{propt_str} 252 | """ 253 | response = http_post_generate(full_prompt, "分析日程") 254 | print("获取响应结果") 255 | print(response) 256 | 257 | if isinstance(response, dict) and 'response' in response: 258 | result = response['response'].strip() 259 | print(f"收到结果: {result}") 260 | 261 | # 检查是否为false 262 | if result.lower() == 'false': 263 | return False, None, None 264 | 265 | # 尝试解析JSON 266 | try: 267 | # 提取JSON部分 268 | json_match = re.search(r'({.+})', result, re.DOTALL) 269 | if json_match: 270 | json_str = json_match.group(1) 271 | data = json.loads(json_str) 272 | 273 | # 提取信息 274 | content = "" 275 | if 'date' in data: 276 | date = data['date'] 277 | else: 278 | date = None 279 | 280 | if 'time' in data: 281 | content += f"时间: {data['time']}\n" 282 | if 'location' in data: 283 | content += f"地点: {data['location']}\n" 284 | if 'event' in data: 285 | content += f"事项: {data['event']}\n" 286 | if 'description' in data: 287 | content += f"描述: {data['description']}\n" 288 | if 'content' in data: 289 | content += f"{data['content']}\n" 290 | 291 | return True, content, date 292 | else: 293 | # 如果无法提取JSON,将整个结果作为内容返回 294 | return True, result, None 295 | except json.JSONDecodeError as e: 296 | logger.error(f"解析JSON错误: {str(e)}") 297 | print(f"解析JSON错误: {str(e)}") 298 | # 如果无法解析为JSON,将整个结果作为内容返回 299 | if result.lower() != 'false': 300 | return True, result, None 301 | return False, None, None 302 | 303 | return False, None, None 304 | except Exception as e: 305 | logger.error(f"LLM功能错误: {str(e)}") 306 | print(f"LLM功能错误: {str(e)}") 307 | messagebox.showerror("错误", f"分析失败: {str(e)}") 308 | return False, None, None 309 | 310 | def parse_date(message): 311 | # 获取今天的日期 312 | today = datetime.now() 313 | current_year = today.year 314 | current_month = today.month 315 | current_day = today.day 316 | 317 | # 尝试提取年月日信息 318 | year_pattern = r'(\d{4})年' 319 | month_pattern = r'(\d{1,2})月' 320 | day_pattern = r'(\d{1,2})日' 321 | date_pattern = r'(\d{4})[/-](\d{1,2})[/-](\d{1,2})' 322 | 323 | # 提取年月日 324 | year_match = re.search(year_pattern, message) 325 | month_match = re.search(month_pattern, message) 326 | day_match = re.search(day_pattern, message) 327 | date_match = re.search(date_pattern, message) 328 | 329 | year = None 330 | month = None 331 | day = None 332 | 333 | # 优先使用完整日期格式 334 | if date_match: 335 | year = int(date_match.group(1)) 336 | month = int(date_match.group(2)) 337 | day = int(date_match.group(3)) 338 | else: 339 | # 单独提取年月日 340 | if year_match: 341 | year = int(year_match.group(1)) 342 | else: 343 | year = current_year 344 | 345 | if month_match: 346 | month = int(month_match.group(1)) 347 | else: 348 | month = current_month 349 | 350 | if day_match: 351 | day = int(day_match.group(1)) 352 | else: 353 | day = current_day 354 | 355 | # 确保日期有效 356 | try: 357 | if month < 1 or month > 12: 358 | month = current_month 359 | 360 | max_days = calendar.monthrange(year, month)[1] 361 | if day < 1 or day > max_days: 362 | day = min(current_day, max_days) 363 | 364 | return year, month, day 365 | except Exception as e: 366 | logger.error(f"解析日期错误: {str(e)}") 367 | return current_year, current_month, current_day 368 | 369 | @audit_operation("AI分析日程保存") 370 | @check_session_expiry 371 | def save_LLM_schedule(y, m, d, message): 372 | try: 373 | # 创建日程目录 374 | os.makedirs(schedule_dir, exist_ok=True) 375 | 376 | # 构建文件名 377 | filename = f"{schedule_dir}/{y}_{m}_{d}.txt" 378 | 379 | # 安全检查 380 | if len(message) > SecurityConfig.MAX_CONTENT_LENGTH: 381 | logger.warning(f"内容超出最大长度限制: {len(message)} > {SecurityConfig.MAX_CONTENT_LENGTH}") 382 | messagebox.showwarning("安全警告", "内容过长,已被截断") 383 | message = message[:SecurityConfig.MAX_CONTENT_LENGTH] 384 | 385 | # 检测潜在恶意内容 386 | if re.search(r'= 5: # 周末 628 | bg_color = COLORS['background'] 629 | fg_color = COLORS['weekend'] 630 | else: 631 | bg_color = COLORS['background'] 632 | fg_color = COLORS['text'] 633 | 634 | lbl = tk.Button(cal_frame, text=day, padx=15, pady=10, width=4, 635 | command=lambda d=day: select_day(d), 636 | font=normal_font, bg=bg_color, fg=fg_color, 637 | activebackground=COLORS['hover'], relief='flat', 638 | cursor='hand2') 639 | lbl.grid(row=r + 2, column=c, padx=2, pady=2, sticky='ew') 640 | 641 | for i in range(7): 642 | cal_frame.grid_columnconfigure(i, weight=1) 643 | 644 | # 创建主窗口 645 | print("正在创建主窗口...") 646 | try: 647 | root = tk.Tk() 648 | print("Tk()创建成功") 649 | root.title("智能日历助手") 650 | root.geometry("1200x800") # 增大窗口尺寸 651 | root.configure(bg=COLORS['background']) # 使用更柔和的背景色 652 | root.resizable(True, True) # 允许调整窗口大小 653 | print("设置窗口大小成功") 654 | except Exception as e: 655 | print(f"创建主窗口时出错: {str(e)}") 656 | raise 657 | 658 | # 设置应用图标 659 | try: 660 | root.iconbitmap("calendar_icon.ico") 661 | except Exception as e: 662 | print(f"无法加载应用图标: {str(e)}") 663 | logger.warning("无法加载应用图标") 664 | 665 | # 获取当前日期 666 | now = datetime.now() 667 | now_str = now.strftime("%Y-%m-%d %H:%M") 668 | year = now.year 669 | month = now.month 670 | weekday = now.weekday() 671 | days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"] 672 | weekday_name = days[weekday] 673 | selected_day = now.day 674 | 675 | # 创建主框架 676 | main_frame = tk.Frame(root, bg=COLORS['background']) 677 | main_frame.pack(fill=tk.BOTH, expand=True, padx=40, pady=40) 678 | 679 | # 创建顶部标题栏 680 | header_frame = tk.Frame(main_frame, bg=COLORS['card'], bd=0, relief='flat') 681 | header_frame.pack(fill=tk.X, pady=(0, 20)) 682 | 683 | app_title = tk.Label(header_frame, text="智能日历助手", font=title_font, 684 | bg=COLORS['card'], fg=COLORS['text']) 685 | app_title.pack(side=tk.LEFT, padx=20, pady=20) 686 | 687 | current_date_label = tk.Label(header_frame, 688 | text=f"今天是{year}年{month}月{selected_day}日 {weekday_name}", 689 | font=bold_font, bg=COLORS['card'], fg=COLORS['text_light']) 690 | current_date_label.pack(side=tk.RIGHT, padx=20, pady=20) 691 | 692 | # 创建内容区域 693 | content_frame = tk.Frame(main_frame, bg=COLORS['background']) 694 | content_frame.pack(fill=tk.BOTH, expand=True) 695 | 696 | # 创建左侧日历区域 697 | left_frame = tk.Frame(content_frame, bg=COLORS['card'], bd=0, relief='flat') 698 | left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 20)) 699 | 700 | # 创建日历控制按钮框架 701 | control_frame = tk.Frame(left_frame, bg=COLORS['card']) 702 | control_frame.pack(fill=tk.X, pady=20, padx=20) 703 | 704 | # 年份控制按钮 705 | year_btn_frame = tk.Frame(control_frame, bg=COLORS['card']) 706 | year_btn_frame.pack(side=tk.TOP, fill=tk.X, pady=5) 707 | 708 | prev_year_btn = tk.Button(year_btn_frame, text="◀ 上一年", command=prev_year, 709 | font=normal_font, bg=COLORS['primary'], fg='white', 710 | activebackground=COLORS['primary'], relief='flat', 711 | padx=20, pady=8, cursor='hand2') 712 | prev_year_btn.pack(side=tk.LEFT, padx=5) 713 | 714 | year_label = tk.Label(year_btn_frame, text=f"{year}年", font=bold_font, 715 | bg=COLORS['card'], fg=COLORS['text']) 716 | year_label.pack(side=tk.LEFT, expand=True) 717 | 718 | next_year_btn = tk.Button(year_btn_frame, text="下一年 ▶", command=next_year, 719 | font=normal_font, bg=COLORS['primary'], fg='white', 720 | activebackground=COLORS['primary'], relief='flat', 721 | padx=20, pady=8, cursor='hand2') 722 | next_year_btn.pack(side=tk.RIGHT, padx=5) 723 | 724 | # 月份控制按钮 725 | month_btn_frame = tk.Frame(control_frame, bg=COLORS['card']) 726 | month_btn_frame.pack(side=tk.TOP, fill=tk.X, pady=5) 727 | 728 | prev_month_btn = tk.Button(month_btn_frame, text="◀ 上月", command=prev_month, 729 | font=normal_font, bg=COLORS['primary'], fg='white', 730 | activebackground=COLORS['primary'], relief='flat', 731 | padx=20, pady=8, cursor='hand2') 732 | prev_month_btn.pack(side=tk.LEFT, padx=5) 733 | 734 | month_label = tk.Label(month_btn_frame, text=f"{month}月", font=bold_font, 735 | bg=COLORS['card'], fg=COLORS['text']) 736 | month_label.pack(side=tk.LEFT, expand=True) 737 | 738 | next_month_btn = tk.Button(month_btn_frame, text="下月 ▶", command=next_month, 739 | font=normal_font, bg=COLORS['primary'], fg='white', 740 | activebackground=COLORS['primary'], relief='flat', 741 | padx=20, pady=8, cursor='hand2') 742 | next_month_btn.pack(side=tk.RIGHT, padx=5) 743 | 744 | # 日历框架 745 | cal_frame = tk.Frame(left_frame, bg=COLORS['card']) 746 | cal_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20) 747 | 748 | # 创建右侧日程区域 749 | right_frame = tk.Frame(content_frame, bg=COLORS['card'], padx=20, pady=20) 750 | right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) 751 | 752 | # 创建标签页 753 | notebook = ttk.Notebook(right_frame) 754 | notebook.pack(fill=tk.BOTH, expand=True) 755 | 756 | # 日程编辑标签页 757 | edit_frame = tk.Frame(notebook, bg=COLORS['card']) 758 | notebook.add(edit_frame, text="日程编辑") 759 | 760 | # 创建标题标签 761 | schedule_title = tk.Label(edit_frame, text="日程内容", font=bold_font, 762 | bg=COLORS['card'], fg=COLORS['text']) 763 | schedule_title.pack(padx=20, pady=10) 764 | 765 | # 创建文本区域 - 减小高度 766 | text_area = tk.Text(edit_frame, wrap=tk.WORD, height=8, font=normal_font, 767 | bg='white', fg=COLORS['text'], relief='flat', 768 | padx=10, pady=10) 769 | text_area.pack(fill=tk.BOTH, expand=True, padx=20, pady=10) 770 | 771 | # 创建按钮框架 772 | button_frame = tk.Frame(edit_frame, bg=COLORS['card']) 773 | button_frame.pack(fill=tk.X, padx=20, pady=10) 774 | 775 | # 保存按钮 - 增加高度和可见性 776 | save_btn = tk.Button(button_frame, text="保存日程", 777 | command=lambda: save_schedule(text_area.get(1.0, tk.END)), 778 | font=bold_font, bg=COLORS['success'], fg='white', 779 | activebackground=COLORS['success'], relief='raised', 780 | width=15, height=2, padx=20, pady=10, cursor='hand2') 781 | save_btn.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10) 782 | 783 | # AI格式化按钮 - 增加高度和可见性 784 | format_btn = tk.Button(button_frame, text="AI格式化", 785 | command=lambda: format_schedule(text_area.get(1.0, tk.END)), 786 | font=bold_font, bg=COLORS['secondary'], fg='white', 787 | activebackground=COLORS['secondary'], relief='raised', 788 | width=15, height=2, padx=20, pady=10, cursor='hand2') 789 | format_btn.pack(side=tk.RIGHT, fill=tk.X, expand=True, padx=10) 790 | 791 | # AI识别标签页 792 | ai_frame = tk.Frame(notebook, bg=COLORS['card']) 793 | notebook.add(ai_frame, text="AI智能识别") 794 | 795 | # 创建AI识别按钮 - 增加高度和可见性 796 | ai_btn = tk.Button(ai_frame, text="识别剪贴板内容", 797 | command=paste_content_to_calendar, 798 | font=bold_font, bg=COLORS['primary'], fg='white', 799 | activebackground=COLORS['primary'], relief='raised', 800 | width=20, height=2, padx=20, pady=10, cursor='hand2') 801 | ai_btn.pack(padx=20, pady=20) 802 | 803 | # 创建预览区域 - 减小高度 804 | preview_label = tk.Label(ai_frame, text="识别结果预览", font=bold_font, 805 | bg=COLORS['card'], fg=COLORS['text']) 806 | preview_label.pack(padx=20, pady=10) 807 | 808 | preview_text = tk.Text(ai_frame, wrap=tk.WORD, height=8, font=normal_font, 809 | bg='white', fg=COLORS['text'], relief='flat', 810 | padx=10, pady=10) 811 | preview_text.pack(fill=tk.BOTH, expand=True, padx=20, pady=10) 812 | 813 | # 创建保存识别结果按钮 - 增加高度和可见性 814 | save_ai_btn = tk.Button(ai_frame, text="保存识别结果", 815 | command=save_ai_result, 816 | font=bold_font, bg=COLORS['success'], fg='white', 817 | activebackground=COLORS['success'], relief='raised', 818 | width=20, height=2, padx=20, pady=10, cursor='hand2') 819 | save_ai_btn.pack(padx=20, pady=20) 820 | 821 | # 初始化日历显示 822 | update_calendar(year, month) 823 | 824 | # 启动主循环 825 | root.mainloop() 826 | -------------------------------------------------------------------------------- /UI/schedules/2024_10_8.txt: -------------------------------------------------------------------------------- 1 | 2024年10月8日的日程: 2 | 具体时间:10月8日晚上21:00-22:30 3 | 地点:腾讯会议,会议链接:https://meeting.tencent.com/dm/2huGlkjUkwOc#腾讯会议:426-863-142 4 | 内容:就业推荐表、三方签约/解约等就业基本流程,就业形势、学校就业整体情况、选调相关内容等 -------------------------------------------------------------------------------- /UI/schedules/2024_5_16.txt: -------------------------------------------------------------------------------- 1 | 2025年4月16日的日程: -------------------------------------------------------------------------------- /UI/schedules/2024_9_24.txt: -------------------------------------------------------------------------------- 1 | 2024年9月24日的日程: 2 | 今天要好好干活,然后要努力了! -------------------------------------------------------------------------------- /UI/schedules/2024_9_25.txt: -------------------------------------------------------------------------------- 1 | 2024年9月25日的日程: 2 | 今天要唱K了! -------------------------------------------------------------------------------- /UI/schedules/2025_4_10.txt: -------------------------------------------------------------------------------- 1 | 2025年4月10日的日程: 2 | 12 3 | -------------------------------------------------------------------------------- /assets/usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitveg/ctrl-c_calendar/39a907a07854d80c76397665dc337ec6b43d3747/assets/usage.png -------------------------------------------------------------------------------- /audit_log.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitveg/ctrl-c_calendar/39a907a07854d80c76397665dc337ec6b43d3747/audit_log.txt -------------------------------------------------------------------------------- /kernel/save_to_file.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | 11 | typedef struct schedule{ 12 | long long id; 13 | string name; 14 | string place; 15 | time_t start_time; 16 | time_t terminal_time; 17 | string remark; 18 | struct schedule *next; 19 | } Schedule; 20 | 21 | /* 22 | ���ܣ����ճ������е�ijһ����洢���ļ��� 23 | ������ 24 | sche����Ҫ�洢���ճ̽ṹ���ַ 25 | ����ֵ�� 26 | �����ɹ���1 27 | ����ʧ�ܣ�0 28 | */ 29 | 30 | bool save_to_file(Schedule* sche) { 31 | FILE* file; 32 | char filename[100]; 33 | 34 | time_t t = time(NULL); 35 | int offset = 0; 36 | 37 | t*= 100; 38 | 39 | sprintf(filename, "./schedule/%lld.sche", t + offset); 40 | 41 | while (access(filename, F_OK) == 0) { 42 | ++offset; 43 | sprintf(filename, "./schedule/%lld.sche", t + offset); 44 | } 45 | 46 | if (offset >= 100) { 47 | printf("Too much schedule\n"); 48 | return 0; 49 | } 50 | 51 | if ((file = fopen(filename, "w")) == NULL) { 52 | printf("Cannot open file\n"); 53 | return 0; 54 | } 55 | 56 | fprintf(file, "%s\r\n%s\r\n%lld\r\n%lld\r\n%s\r\n", 57 | sche->name.c_str(), 58 | sche->place.c_str(), 59 | (long long)sche->start_time, 60 | (long long)sche->terminal_time, 61 | sche->remark.c_str() 62 | ); 63 | 64 | fclose(file); 65 | return 1; 66 | } 67 | 68 | 69 | /* 70 | ���ܣ���./schedule�ļ����µ��ճ��ļ�ȫ����ȡ�������У�����һ���ճ������������id�����ļ��� 71 | ������ 72 | sche��ָ���ճ���������ʼָ���ָ�� 73 | ����ֵ�� 74 | �����ɹ���1 75 | ����ʧ�ܣ�0 76 | */ 77 | 78 | bool read_from_file(Schedule** sche) { 79 | DIR* dir; 80 | struct dirent* entry; 81 | FILE* file; 82 | char filename[256]; 83 | 84 | if ((dir = opendir("./schedule")) == NULL) { 85 | printf("Cannot open ./schedule directory\n"); 86 | return 0; 87 | } 88 | 89 | *sche = NULL; 90 | Schedule* last = NULL; 91 | 92 | while ((entry = readdir(dir)) != NULL) { 93 | 94 | // �ж��ļ���׺���Ƿ�Ϊ.sche 95 | const char *dot = strrchr(entry->d_name, '.'); 96 | if (!dot || dot == entry->d_name) { 97 | continue; 98 | } 99 | else { 100 | if (strcmp(dot, ".sche")!=0) { 101 | continue; 102 | } 103 | } 104 | 105 | //�ж��ļ����Ƿ���Ϲ淶 106 | if (dot - entry->d_name != 12) { 107 | // λ��������12 108 | continue; 109 | } 110 | 111 | for (char *index = entry->d_name; index < dot ; ++index) { 112 | if(*index < '0' || *index > '9') { 113 | // �������� 114 | continue; 115 | } 116 | } 117 | 118 | snprintf(filename, sizeof(filename), "./schedule/%s", entry->d_name); 119 | 120 | if ((file = fopen(filename, "r")) == NULL) { 121 | printf("Cannot open file: %s\n", filename); 122 | continue; 123 | } 124 | 125 | Schedule* newSchedule = new Schedule; 126 | char name[256], place[256], remark[256], id[15]; 127 | long long start_time, terminal_time; 128 | 129 | // ��ȡ�ļ�����ת��Ϊid 130 | strncpy(id, entry->d_name, 12); 131 | id[12] = '\0'; 132 | newSchedule->id = strtoll(id, NULL, 10); 133 | 134 | if (fscanf(file, "%256s %256s %lld %lld %256s", 135 | name, place, &start_time, &terminal_time, remark) == 5) { 136 | 137 | newSchedule->name = name; 138 | newSchedule->place = place; 139 | newSchedule->start_time = start_time; 140 | newSchedule->terminal_time = terminal_time; 141 | newSchedule->remark = remark; 142 | newSchedule->next = NULL; 143 | 144 | if (*sche == NULL) { 145 | *sche = newSchedule; 146 | } 147 | else { 148 | last->next = newSchedule; 149 | } 150 | last = newSchedule; 151 | } 152 | else { 153 | delete newSchedule; 154 | printf("Error reading data from file: %s\n", filename); 155 | } 156 | 157 | fclose(file); 158 | } 159 | 160 | closedir(dir); 161 | return (*sche != NULL); 162 | } 163 | 164 | 165 | /* 166 | ���ܣ����ճ������е�ijһ������޸Ĵ洢���ļ��� 167 | ������ 168 | sche����Ҫ�洢���޸Ĺ����ճ̽ṹ���ַ 169 | ����ֵ�� 170 | �����ɹ���1 171 | ����ʧ�ܣ�0 172 | */ 173 | 174 | bool change_to_file(Schedule* sche) { 175 | FILE* file; 176 | char filename[100]; 177 | 178 | sprintf(filename, "./schedule/%lld.sche", sche->id); 179 | 180 | if ((file = fopen(filename, "w")) == NULL) { 181 | printf("Cannot open file\n"); 182 | return 0; 183 | } 184 | 185 | fprintf(file, "%s\r\n%s\r\n%lld\r\n%lld\r\n%s\r\n", 186 | sche->name.c_str(), 187 | sche->place.c_str(), 188 | (long long)sche->start_time, 189 | (long long)sche->terminal_time, 190 | sche->remark.c_str() 191 | ); 192 | 193 | fclose(file); 194 | return 1; 195 | } 196 | 197 | 198 | /* 199 | ���ܣ����ճ������е�ijһ������޸Ĵ洢���ļ��� 200 | ������ 201 | sche����Ҫ�洢���޸Ĺ����ճ̽ṹ���ַ 202 | ����ֵ�� 203 | �����ɹ���1 204 | ����ʧ�ܣ�0 205 | */ 206 | 207 | bool delete_file(Schedule* sche) { 208 | char filename[100]; 209 | sprintf(filename, "./schedule/%lld.sche", sche->id); 210 | return remove(filename); 211 | } 212 | 213 | 214 | 215 | int main() { 216 | 217 | Schedule* newSchedule = new Schedule; 218 | newSchedule->id = 1; 219 | newSchedule->name = "����"; 220 | newSchedule->place = "ͼ���"; 221 | newSchedule->start_time = time(NULL); 222 | newSchedule->terminal_time = newSchedule->start_time + 3600; 223 | newSchedule->remark = "���DZ�ע"; 224 | newSchedule->next = NULL; 225 | 226 | 227 | if (save_to_file(newSchedule)) { 228 | cout << "Schedule saved successfully." << endl; 229 | } 230 | else { 231 | cout << "Failed to save schedule." << endl; 232 | } 233 | 234 | 235 | Schedule* scheduleList = NULL; 236 | if (read_from_file(&scheduleList)) { 237 | cout << "Schedules loaded from file:" << endl; 238 | Schedule* current = scheduleList; 239 | while (current != NULL) { 240 | cout << "ID: " << current->id << endl; 241 | cout << "Name: " << current->name << endl; 242 | cout << "Place: " << current->place << endl; 243 | cout << "Start Time: " << ctime(¤t->start_time); 244 | cout << "End Time: " << ctime(¤t->terminal_time); 245 | cout << "Remark: " << current->remark << endl; 246 | current = current->next; 247 | } 248 | } 249 | else { 250 | cout << "Failed to load schedules from file." << endl; 251 | } 252 | 253 | return 0; 254 | } 255 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.31.0 2 | python-dateutil>=2.8.2 3 | -------------------------------------------------------------------------------- /schedules/2024_12_31.txt: -------------------------------------------------------------------------------- 1 | 2024年12月31日的日程: 2 | 具体时间:19:00 3 | 地点:公司开会地点(未知) 4 | 内容:会议内容未详 -------------------------------------------------------------------------------- /schedules/2025_4_8.txt: -------------------------------------------------------------------------------- 1 | 2025年4月8日的日程: 2 | 具体时间: 3 | 地点:https://meeting.tencent.com/dm/jkXva8F4dewN #腾讯会议:225-958-125 4 | 内容:邝嘉诺预定的会议 -------------------------------------------------------------------------------- /文档资料/pc端日程表生成器.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitveg/ctrl-c_calendar/39a907a07854d80c76397665dc337ec6b43d3747/文档资料/pc端日程表生成器.pptx -------------------------------------------------------------------------------- /文档资料/思路.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitveg/ctrl-c_calendar/39a907a07854d80c76397665dc337ec6b43d3747/文档资料/思路.docx --------------------------------------------------------------------------------