Skip to content

Instantly share code, notes, and snippets.

@lan2720
Last active July 9, 2024 07:19
Show Gist options
  • Save lan2720/cfd63569d4ea23179cbdf8153291325a to your computer and use it in GitHub Desktop.
Save lan2720/cfd63569d4ea23179cbdf8153291325a to your computer and use it in GitHub Desktop.
油猴脚本 - 美签最新slot推送到飞书消息
// ==UserScript==
// @name Push Feishu Message
// @namespace http://tampermonkey.net/
// @version 2024-07-07
// @description try to take over the world!
// @author jarvixwang
// @match *://portal.ustraveldocs.com/applicanthome*
// @match *://portal.ustraveldocs.com/appointmentcancellation*
// @icon https://www.google.com/s2/favicons?sz=64&domain=ustraveldocs.com
// @require https://code.jquery.com/jquery-3.7.1.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js
// @resource IMPORTED_CSS https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css
// @grant GM_notification
// @grant unsafeWindow
// @grant GM_getResourceText
// @grant GM_addStyle
// ==/UserScript==
// 美签预约网站的账号和密码
const account_info = {
username: 'xxx',
password: 'yyy',
};
// 飞书推送地址
const WEBHOOK_URL = "https://open.feishu.cn/open-apis/bot/v2/hook/<YOUR_OWN_ID>";
const webhookNotify=(title, rows)=>{
let data = {"msg_type": "interactive",
"card": {
"header": {
"title": {
"content": title,
"tag": "plain_text",
}
},
"elements": rows
}
}
fetch(WEBHOOK_URL, {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
})
.then(response => {
// 处理响应
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
console.log('响应json: ', response.json()); // 将响应体转换为JSON
})
.then(responseData => {
// 处理转换后的JSON数据
console.log('成功发送消息,响应数据:', responseData);
})
.catch(error => {
// 处理错误
console.error('发送消息时发生错误:', error);
});
}
const elemIds = {section: 'Unauthorized:SiteTemplate:siteLogin',
username: 'Unauthorized:SiteTemplate:siteLogin:loginComponent:loginForm:username',
password: 'Unauthorized:SiteTemplate:siteLogin:loginComponent:loginForm:password',
login_button: 'Unauthorized:SiteTemplate:siteLogin:loginComponent:loginForm:loginButton',
error_try_again_button: 'neterrorTryAgainButton',
}
const loginForMe=()=>{
// 填写信息
document.getElementById(elemIds.username).value = account_info.username;
document.getElementById(elemIds.password).value = account_info.password;
// 协议同意
const checkbox = document.evaluate("//input[@type='checkbox']",
document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
checkbox.click();
// 点击登录按钮
const button = document.getElementById(elemIds.login_button);
button.click();
console.log("login successfully!");
}
const checkUnauthorized=()=>{
const element = document.getElementById(elemIds.section);
if (element) {
console.log("检测到未授权, 需要登录");
loginForMe();
} else {
console.log("无需重新登录");
}
}
const checkNetError=()=>{
const button = document.getElementById(elemIds.error_try_again_button);
if (button) {
console.log("找到了网络错误重试按钮");
button.click();
console.log("网络错误重试按钮被点击!");
} else {
console.log("没有网络错误出现");
}
}
const isChallengePopup=()=>{
//var iframe = document.getElementById('cf-chk-widget-fgqjj');
var div = document.querySelector('.cf-turnstile-wrapper');
if (div) {
// 确保iframe已经加载完成,然后访问其内容
setTimeout(function() {
// 获取iframe的文档对象
var currentDocument = div.contentDocument || div.contentWindow.document;
// 使用querySelector查找第一个checkbox
var checkbox = currentDocument.querySelector('input[type="checkbox"]');
checkbox.click();
console.log("cloudfare challenge出现checkbox已点击");
}, 1000);
} else {
console.log("没有人工验证出现");
}
}
const selectEnglishLanguage=()=>{
console.log("检查英语作为页面显示语言");
const selectors = document.getElementsByTagName('select');
let languageSelector = null;
for (let i = 0; i < selectors.length; i++) {
if (selectors[i].hasAttribute('onchange') && selectors[i].getAttribute('onchange').toString() === 'changeLanguage()') {
console.log("找到了语言选择下拉框");
languageSelector = selectors[i];
break;
}
}
//console.log(languageSelector);
if (languageSelector && languageSelector.value !== 'English') {
console.log("当前语言非英语,设置语言为英语");
languageSelector.value = 'English'; // 语言不是英语时设置语言为英语
languageSelector.dispatchEvent(new Event('change'));
} else {
console.log("当前语言是英语,无需操作");
}
}
const parseDateFromLabel=(inputStr)=>{
const regex = /(Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\s(January|February|March|April|May|June|July|August|September|October|November|December)\s\d{1,2},\s\d{4}/;
const match = inputStr.match(regex);
let resultDate = "";
if (match) {
resultDate = match[0];
}
return resultDate
}
const parseRowValue=(tr)=>{
const tds = tr.querySelectorAll('td');
const nextTd = tds[1];
return nextTd.textContent.trim();
}
const formatDateTime=(date, returnOnlyDate = true)=>{
const padZero = (num) => String(num).padStart(2, '0');
let dateStr = (
padZero(date.getFullYear()) + '-' +
padZero(date.getMonth() + 1) + '-' +
padZero(date.getDate())
);
if (!returnOnlyDate) {
dateStr += (' ' +
padZero(date.getHours()) + ':' +
padZero(date.getMinutes()) + ':' +
padZero(date.getSeconds())
);
}
return dateStr;
}
const textFormatted=(text, color="default", isBold=false)=>{
const fontBold = isBold ? "**" : "";
let formatText = text;
if (isBold) {
formatText = fontBold + text + fontBold;
}
if (color != "default") {
formatText = "<font color='" + color + "'>" + formatText + "</font>";
}
return formatText;
}
const getAppointmentDate=(givenAppointDate = null)=>{
console.log("开始获取预约日期")
let consulate_name = "";
let visa_class = "";
let appointmentDateStr = "";
let availableDateStr = "";
const tbody = document.evaluate('//*[@id="j_id0:SiteTemplate:j_id120"]/table/tbody', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if (tbody === null) {
console.log("当前未检测到tbody, 退出, 等待下次检测");
return;
}
const trs = tbody.querySelectorAll('tr');
for (let i = 0; i < trs.length; i++) {
var trText = trs[i].textContent.toString().trim();
if (trText.includes('U.S. Consulate General')) {
consulate_name = parseRowValue(trs[i]);
console.log("当前大使馆: ", consulate_name);
} else if (trText.includes('Visa Class')) {
visa_class = parseRowValue(trs[i]);
console.log("当前签证类型: ", visa_class);
} else if (trText.includes('Appointment Date')) {
appointmentDateStr = parseRowValue(trs[i]);
console.log("找到当前已预约日期: ", appointmentDateStr);
} else if (trText.includes('First available appointment slots')) {
const dateLabel = trText;
availableDateStr = parseDateFromLabel(dateLabel);
console.log("找到最新可预约日期: ", availableDateStr);
break;
}
}
console.log("已获取完预约日期");
let appointmentDateObj = new Date("2024-01-01");
if (givenAppointDate) {
appointmentDateObj = new Date(givenAppointDate);
} else {
appointmentDateObj = new Date(Date.parse(appointmentDateStr));
}
const availableDateObj = new Date(Date.parse(availableDateStr));
const hasEarlierSlot = availableDateObj < appointmentDateObj;
console.log("是否有更早的slot: " + hasEarlierSlot);
const title = hasEarlierSlot ? "有了!!!!!!!!!" : "还没有更早日期";
let rows = [
"**运行时间:** " + formatDateTime(new Date(), false),
"**大使馆:** " + consulate_name,
"**签证类型:** " + visa_class,
"**是否有更早的slot:** " + textFormatted(hasEarlierSlot, "red", hasEarlierSlot),
];
if (hasEarlierSlot) {
rows.push("**最新可预约日期:** " + textFormatted(formatDateTime(availableDateObj), "red", true));
rows.push("**已预约日期:** " + formatDateTime(appointmentDateObj));
// 如果有更早的slot则艾特所有人
rows.push("<at id=all></at>");
} else {
rows.push("**已预约日期:** " + formatDateTime(appointmentDateObj));
rows.push("**最新可预约日期:** " + textFormatted(formatDateTime(availableDateObj), "default", true));
}
const content = rows.join("\n");
console.log("发送消息:\n", content);
rows = [{"tag": "div",
"text": {"content": content,
"tag": "lark_md"}}]
return {"flag": hasEarlierSlot,
"title": title,
"rows": rows};
}
const clickFromHome=()=>{
const links = document.getElementsByTagName('a');
for (let i = 0; i < links.length; i++) {
if (links[i].textContent === 'Cancel Appointment' && links[i].hasAttribute('onclick')) {
console.log("找到了cancel Appointment的按钮");
links[i].click();
break;
}
}
}
const refreshDate=()=>{
console.log("开始刷新, " + new Date());
clickFromHome();
console.log("刷新完毕");
}
(function() {
'use strict';
const refreshMinutesGap = 3;
// 可以手动指定当前日期, 也可以为null则根据账号实际预约日期
const givenAppointDate = "2024-08-30";
setTimeout(function() {
// 检测是否有网络连接错误
checkNetError();
setTimeout(function() {
// 检测是否有人工验证
isChallengePopup();
setTimeout(function() {
// 检测是否需要重新登录
checkUnauthorized();
setTimeout(function() {
// 检测语言
selectEnglishLanguage(); // Select English language
// 获取最新日期
setTimeout(function() {
const result = getAppointmentDate(givenAppointDate); // Get appointment date
if (result) {
console.log("已从页面获取日期结果,即将发送飞书消息推送");
webhookNotify(result.title, result.rows);
console.log("等待", refreshMinutesGap, "分钟后,页面将刷新进行下次检测");
}
// 刷新
setTimeout(refreshDate, refreshMinutesGap * 60 * 1000);// Refresh the page after waiting for several minutes
}, 5 * 1000); // Wait for 5 seconds after changing page language, then get data from page
}, 2 * 1000);
}, 2*1000);
}, 2*1000);
}, 6 * 1000); // Wait for next event loop iteration
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment