Last active
November 4, 2024 11:04
-
-
Save dioncodes/cd4554d8593814a94925735cbcdea0c8 to your computer and use it in GitHub Desktop.
Scriptable iOS Server Status Widget
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const initialData = { | |
servers: [ | |
{ | |
url: 'https://1.example.com/', | |
title: 'Server 1', | |
online: null, | |
}, | |
{ | |
url: 'https://2.example.com/', | |
title: 'Server 2', | |
online: null, | |
}, | |
{ | |
url: 'https://3.example.com/', | |
title: 'Server 3', | |
online: null, | |
}, | |
], | |
lastUpdate: null | |
} | |
// Refresh Interval in seconds | |
const refreshInterval = 300 | |
const widget = await createWidget() | |
if (!config.runsInWidget) { | |
await widget.presentSmall() | |
} | |
Script.setWidget(widget) | |
Script.complete() | |
async function createWidget(items) { | |
const data = await refresh() | |
const list = new ListWidget() | |
// uncomment the lines below if you want to show the header (not working with more than ~5 servers) | |
// const header = list.addText("Server Status") | |
// header.font = Font.mediumSystemFont(13) | |
// list.addSpacer() | |
data.servers.forEach((server) => { | |
const label = list.addText((server.online ? '🟢' : (server.online === false ? '🔴' : '❔')) + ' ' + server.title) | |
label.font = Font.boldSystemFont(12) | |
label.textColor = Color.gray() | |
list.refreshAfterDate = new Date(Date.now() + refreshInterval) | |
list.addSpacer(3) | |
}) | |
if (data.lastUpdate) { | |
list.addSpacer() | |
const lastRefreshLabel = list.addText('Last refresh: ' + data.lastUpdate) | |
lastRefreshLabel.font = Font.mediumSystemFont(8) | |
} | |
return list | |
} | |
async function refresh() { | |
let data = initialData | |
for (let server of data.servers) { | |
try { | |
let response = await new Request(server.url).loadString() | |
server.online = response && response.length > 0 | |
} catch (e) { | |
server.online = false | |
} | |
} | |
let now = new Date() | |
let hours = now.getHours() | |
let mins = now.getMinutes() | |
data.lastUpdate = (hours > 9 ? hours : '0' + hours) + ':' + (mins > 9 ? mins : '0' + mins) | |
return data | |
} |
Incorporating the advantages of the aforementioned modifications, this version also utilizes Promise.all for concurrent network requests to enhance the efficiency of checking server statuses. Additionally, it implements a retry mechanism among other optimization features. Moreover, this widget can display a list of up to 6-7 servers while showing a title.
// Variables used by Scriptable.
// icon-color: purple; icon-glyph: globe;
const initialData = {
servers: [
{ url: 'https://server1.example.com/', title: 'Server 1' },
{ url: 'https://server2.example.com/', title: 'Server 2' },
{ url: 'https://server3.example.com/', title: 'Server 3' },
{ url: 'https://server4.example.com/', title: 'Server 4' },
{ url: 'https://server5.example.com/', title: 'Server 5' },
{ url: 'https://server6.example.com/', title: 'Server 6' },
// 可以继续添加更多服务器...
],
lastUpdate: null
};
const refreshInterval = 161; // Refresh Interval in seconds
async function createWidget() {
const data = await refreshStatus();
const list = new ListWidget();
setGradientBackground(list);
addHeader(list, "CY Server&API Stat");
data.servers.forEach(server => {
addServerStatus(list, server);
list.addSpacer(3);
});
addFooter(list, data.lastUpdate);
list.refreshAfterDate = new Date(Date.now() + refreshInterval * 1000);
return list;
}
async function refreshStatus() {
let data = { ...initialData, servers: await checkServers(initialData.servers) };
data.lastUpdate = new Date().toLocaleTimeString();
return data;
}
async function checkServers(servers) {
const checks = servers.map(server => (
fetchServerStatus(server)
.then(online => ({ ...server, online }))
.catch(() => ({ ...server, online: false }))
));
return Promise.all(checks);
}
async function fetchServerStatus(server, retries = 3, delay = 1000) {
async function attemptFetch(remainingRetries) {
try {
const request = new Request(server.url);
await request.loadString(); // 尝试加载数据
return true; // 加载成功,返回在线状态
} catch (e) {
if (remainingRetries <= 0) throw e; // 重试次数用尽,抛出异常
await new Promise(resolve => setTimeout(resolve, delay)); // 等待一段时间再重试
return attemptFetch(remainingRetries - 1); // 递归调用,减少剩余重试次数
}
}
try {
return await attemptFetch(retries); // 初始调用,尝试获取状态
} catch (e) {
return false; // 所有尝试失败,返回离线状态
}
}
function setGradientBackground(list) {
const gradient = new LinearGradient();
gradient.locations = [0, 1];
gradient.colors = [
new Color('#2D1925'),
new Color('#402938')
];
list.backgroundGradient = gradient;
}
function addHeader(list, text) {
const header = list.addText(text);
header.font = Font.mediumSystemFont(13);
header.textColor = Color.white();
list.addSpacer(5);
}
function addServerStatus(list, server) {
let symbol = '❔'; // 默认状态未知
if (server.online === true) {
symbol = '🟢'; // 在线
} else if (server.online === false) {
symbol = '🔴'; // 离线
}
const label = list.addText(`${symbol} ${server.title}`);
label.font = Font.boldSystemFont(12);
label.textColor = Color.white();
}
function addFooter(list, lastUpdate) {
list.addSpacer();
const footer = list.addText(`Last refresh: ${lastUpdate}`);
footer.font = Font.mediumSystemFont(8);
footer.textColor = Color.white();
}
const widget = await createWidget();
if (!config.runsInWidget) {
await widget.presentSmall();
}
Script.setWidget(widget);
Script.complete();
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Sergey20482048 I used the above script with IP addresses by just using http://192.168.1.1/ in the server line , but my servers have a web page that you land on when you navigate to that IP. I'm not sure if yours are running a web server..
Does anyone know how to make this script use two columns so I can squeeze in two columns of 4 servers each?