Skip to content

Instantly share code, notes, and snippets.

@dioncodes
Last active November 4, 2024 11:04
Show Gist options
  • Save dioncodes/cd4554d8593814a94925735cbcdea0c8 to your computer and use it in GitHub Desktop.
Save dioncodes/cd4554d8593814a94925735cbcdea0c8 to your computer and use it in GitHub Desktop.
Scriptable iOS Server Status Widget
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
}
@tommyd75
Copy link

@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?

@ChenYuZe519
Copy link

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