// ==UserScript==
// @name 求职魔法师 - 求职神器|智能填充工作信息|免去输入烦恼
// @namespace http://t.net/1
// @version 0.91
// @description 点击输入框时请求ChatGPT,并显示智能建议供选择,点击一键填充输入框
// @author jiguang
// @match *://*/*
// @grant GM_xmlhttpRequest
// @license MIT
// ==/UserScript==
(function () {
"use strict";
// OpenAI API 的密钥
const API_KEY = "sk-XXXXXXX"; // 请替换为你的 OpenAI API 密钥
// 向 OpenAI API 发送请求
const fetchSuggestions = async (inputHTML, isTextarea) => {
// 获取 body 的 innerText
let text = document.body.innerText;
// 截取前 1000 个字符或全部内容(如果少于 1000 个字符)
let first1000Chars = text.length > 1000 ? text.slice(0, 1000) : text;
const response = await fetch(
"https://api.openai.com/v1/chat/completions",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_KEY}`,
},
body: JSON.stringify({
model: "gpt-4o-mini",
messages: [
{
role: "user",
content: `我的名字:张三
手机号:13111112222
身份证号:111211199012011111
生日:1991年1月1日
地址:北京市
邮箱(密码可以是邮箱):1@foxmail.com
求职意向:Web 开发
教育背景:
- 2016.9-2020.6 清华大学软件工程专业,专业排名年级前 10%
- 主修课程:操作系统、计算机组成原理、计算机网络、数据结构与算法、C 语言程序设计、Java 语言程序设计、JavaWeb 开发与框架开发、Python 程序开发、嵌入式开发、Linux 操作系统、软件工程、软件测试、项目管理等
工作经历:
- 2022.02-2022.05 XXX有限公司 Python 后端工程师
- XXXXX
- 2022.06-2023.12 XXX公司 Java后端工程师
- XXXXX
- 2024.2 至今 XXXX公司 技术总监
- 负责技术管理工作
技能荣誉:
- 校级奖学金 2019-6
自我评价:
我具备较强的问题解决能力。在面对挑战时,我能够冷静分析情况,快速找出解决方案并付诸实施。这使我在项目进程中能够保持高效运作,并不断推动工作向前发展。在专业技能方面,我不断学习和提升自己,通过参加培训和阅读相关书籍,积累了丰富的知识和经验。我对新事物保持开放的态度,乐于尝试新的工作方法,力求在工作中寻求创新和突破。
。`,
},
{
role: "user",
content: `网页信息: ${first1000Chars}`,
//content: `网页信息: ${document.body.innerHTML}`,
},
{
role: "user",
content: `要填充的元素: ${inputHTML}`,
},
{
role: "user",
content: `请基于以上HTML内容和我的个人信息,推测3-6条我要需要填充进去的内容${
isTextarea ? "(每条不少于200字)" : ""
},一行一个,请你只回复推测结果的内容,不要有其他内容,也不要有序号`,
},
],
max_tokens: isTextarea ? 3000 : 300,
}),
}
);
if (!response.ok) {
throw new Error(`Error: ${response.statusText}`);
}
const data = await response.json();
return data.choices[0]?.message?.content?.trim()?.split("\n") || [];
};
// 创建下拉框
const createDropdown = () => {
const dropdown = document.createElement("div");
dropdown.style.position = "absolute";
dropdown.style.display = "none";
dropdown.style.backgroundColor = "white";
dropdown.style.border = "1px solid #ccc";
dropdown.style.zIndex = "1000";
dropdown.style.maxHeight = "150px";
dropdown.style.overflowY = "auto";
dropdown.style.boxShadow = "0px 0px 5px rgba(0, 0, 0, 0.3)";
return dropdown;
};
// 创建加载动画
const createLoadingSpinner = () => {
const spinner = document.createElement("div");
spinner.className = "loading-spinner";
spinner.style.position = "absolute";
spinner.style.border = "4px solid #f3f3f3";
spinner.style.borderTop = "4px solid #3498db";
spinner.style.borderRadius = "50%";
spinner.style.width = "20px";
spinner.style.height = "20px";
spinner.style.animation = "spin 1s linear infinite";
spinner.style.zIndex = "1001"; // 确保加载动画在最上层
return spinner;
};
// 在输入框上点击时显示下拉列表
const attachDropdownToInput = (input) => {
let dropdown = createDropdown(); // 创建下拉框
let spinner = createLoadingSpinner(); // 创建加载动画
document.body.appendChild(dropdown); // 将下拉框添加到DOM
document.body.appendChild(spinner); // 将加载动画添加到DOM
let isDropdownVisible = false; // 下拉框是否可见的标记
input.addEventListener("focus", async (event) => {
const rect = event.target.getBoundingClientRect();
dropdown.style.left = `${rect.left + window.scrollX}px`;
dropdown.style.top = `${rect.bottom + window.scrollY}px`;
spinner.style.left = `${rect.left + window.scrollX}px`; // 移动到左上角
spinner.style.top = `${rect.top + window.scrollY - 30}px`; // 移动到左上角
spinner.style.display = "block"; // 显示加载动画
// 请求 OpenAI API 获取建议
try {
const isTextarea = input.tagName.toLowerCase() === "textarea";
const suggestions = await fetchSuggestions(input.outerHTML, isTextarea);
dropdown.innerHTML = ""; // 清空之前的选项
suggestions.forEach((optionValue) => {
const option = document.createElement("div");
option.textContent = optionValue;
option.style.padding = "5px 10px";
option.style.cursor = "pointer";
option.addEventListener("click", () => {
input.value = ""; // 先清空输入框
const inputEvent = new Event("input", { bubbles: true }); // 创建输入事件
for (let char of optionValue.replace(/\s+/g, "")) {
input.value += char; // 模拟逐字符输入
input.dispatchEvent(inputEvent); // 触发输入事件
}
dropdown.style.display = "none"; // 选择后隐藏下拉列表
isDropdownVisible = false; // 更新标记
});
option.addEventListener("mouseenter", () => {
option.style.backgroundColor = "#f0f0f0"; // 高亮显示
});
option.addEventListener("mouseleave", () => {
option.style.backgroundColor = "white"; // 恢复颜色
});
dropdown.appendChild(option);
});
dropdown.style.display = "block"; // 显示下拉列表
isDropdownVisible = true; // 更新标记
} catch (error) {
console.error("Error fetching suggestions:", error);
dropdown.innerHTML = '<div style="padding:10px;">无法获取建议</div>';
dropdown.style.display = "block"; // 显示错误信息
isDropdownVisible = true; // 更新标记
} finally {
spinner.style.display = "none"; // 隐藏加载动画
}
});
input.addEventListener("blur", () => {
setTimeout(() => {
if (!isDropdownVisible) {
dropdown.style.display = "none"; // 隐藏下拉框
}
}, 150); // 延迟以容许选择下拉框选项
});
// 在下拉框聚焦时,用户交互时更改标记
dropdown.addEventListener("mouseenter", () => {
isDropdownVisible = true; // 进入下拉框时标记为可见
});
dropdown.addEventListener("mouseleave", () => {
isDropdownVisible = false; // 离开下拉框时标记为不可见
dropdown.style.display = "none"; // 隐藏下拉框
});
};
// 查找所有的输入框和文本区域并嵌入下拉列表
const updateInputs = () => {
const inputs = document.querySelectorAll(
"input:not([updated]), textarea:not([updated])"
);
inputs.forEach((input) => {
if (
input.tagName.toLowerCase() === "textarea" ||
["text", "password", "email", "search", "url", "tel"].includes(
input.type
)
) {
attachDropdownToInput(input);
input.setAttribute("updated", "true");
}
});
};
// 每隔3秒执行一次
setInterval(updateInputs, 3000);
// 添加 CSS 动画
const style = document.createElement("style");
style.innerHTML = `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-spinner {
display: none;
}
`;
document.head.appendChild(style);
})();