
开篇寄语
前几天,伯衡君推荐用Scriptable撰写并运行Javascript脚本,部署在iPhone和iPad桌面上,及时获取各种资讯以提高生活品质,具体可以参看下面的前情提要。本篇文章,则是汇总一些伯衡君在使用过程中觉得十分惊艳的脚本,本篇会陆续更新的,敬请关注。
前情提要
收录列表
| 百度热搜 | 北京一周尾号限行 | 豆瓣电影一周口碑榜 |
| 订阅或日期进度条 | 汇率展示 | 微博热搜Top10 |
| 知乎热榜Top10 | Nasa美图 | 随机菜谱 |
| 随机电影台词 | 随机古诗 | 随机英文名句 |
温馨提示
文中会涉及到的透明脚本效果,请参看下方两张图,手机壁纸尽量挑一些颜色不太复杂的,这样效果会非常好,有那种浑然一体的效果:

内容详情
- 百度热搜
- 取了前10新闻,以在最小的小组件显示,可根据需求自行修改,百度热搜出现的数据有20个
const { transparent } = importModule('no-background')
const w = new ListWidget()
w.backgroundImage = await transparent(Script.name())
const req = new Request("https://www.baidu.com/");
req.method = "GET";
req.headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.55"
};
const res = await req.loadString();
// console.log(res)
const tmp = res.split(`<textarea id="hotsearch_data" style="display:none;">`)[1].split(`</textarea>`)[0]
const data = JSON.parse(tmp).hotsearch;
for(let i of data.slice(0,10)){
const stack = w.addStack()
stack.centerAlignContent()
const text = stack.addText(i.pure_title)
text.textColor = Color.white()
text.font = Font.semiboldSystemFont(9)
}
if(Device.isPad()){
w.presentExtraLarge()
}else if(Device.isPhone()){
w.presentLarge()
}
Script.setWidget(w)
Script.complete()
- 获取中国北京(或者其他地方则需要到各省的政务网站去查看了)一周尾号限行
- 这里引入了一个名为no-background的脚本,需要提前先写好放置在Scriptable中,先运行该透明背景脚本,将手机桌面截屏的图片保存好,运行脚本后将该图片设为背景,之后就可以运行实现Scriptable小组件的透明化布置了,no-background脚本请参考该网址:https://github.com/supermamon/scriptable-no-background,当然,你也可以自己设置背景
const { transparent } = importModule('no-background') //自己设置时该行去掉
const w = new ListWidget()
w.backgroundImage = await transparent(Script.name()) //await transparent(Script.name())替换为new Color("#ff0000")等
let url = "http://yw.jtgl.beijing.gov.cn/jgjxx/services/getRuleWithWeek"
let req = new Request(url)
let result = await req.loadJSON()
const stack = w.addStack();
stack.centerAlignContent();
const title = stack.addText("北京一周尾号限行")
title.textColor = Color.white()
title.font = Font.semiboldSystemFont(12)
if(result.state="success"){
//获取数据成功,进行筛选
let tmp = result.result;
for(let i = 0; i<tmp.length; i++){
const stack = w.addStack();
stack.centerAlignContent();
const days = stack.addText(tmp[i].limitedWeek)
days.textColor = Color.white()
days.font = Font.semiboldSystemFont(12)
stack.addSpacer(20)
const text = stack.addText(tmp[i].limitedNumber)
text.textColor = Color.white()
text.font = Font.semiboldSystemFont(12)
stack.addSpacer(20)
}
}else{
//获取数据失败
}
if(Device.isPad()){
w.presentExtraLarge()
}else if(Device.isPhone()){
w.presentLarge()
}
Script.setWidget(w)
Script.complete()
- 获取豆瓣电影一周口碑榜
- 这里说一下,需要先以WebView.loadURL打开豆瓣官网,切换到电脑模式后再返回就可以获取了,代码中有一段注释“//”去掉就可以了,当切换到电脑端界面时返回再注释上就可以了
const w = new ListWidget()
w.backgroundColor = new Color("#42a056")
const wv = new WebView();
const url = 'https://movie.douban.com/';
await wv.loadURL(url)
var getData = `
function getData(){
a = []
x = document.querySelectorAll(".billboard-bd .title a")
for(s of x){
a.push(s.innerHTML)
}
return a
}
getData()
`
let response = await wv.evaluateJavaScript(getData,false)
for(let r in response){
const stack = w.addStack()
stack.centerAlignContent()
const text = stack.addText(response[r])
text.textColor = Color.white()
stack.addSpacer(10)
}
if(Device.isPad()){
w.presentExtraLarge()
}else if(Device.isPhone()){
w.presentLarge()
}
// WebView.loadURL(url)
Script.setWidget(w)
Script.complete()
- 订阅或者重要日期进程条
- 设置一个重要日期或者服务的到期时间,与今天时间对比,距离截止小于60天进度条变成红色以提醒
const width=125
const h=5
const w = new ListWidget()
w.backgroundColor=new Color("#222222")
//设置截止列表
const arr = {
'Bandwagonhost': '2021-12-08',
'GoDaddy': '2022-06-15',
'Textnow': '2022-04-09',
'Name': '2029-10-11'
}
const oneDay = 24 * 60 * 60 * 1000;
for(let i of Object.keys(arr)){
const firstDate = new Date(`${arr[i]}`);
const secondDate = new Date();
const diffDays = Math.round(Math.abs((firstDate - secondDate) / oneDay));
if(diffDays<=60){
getwidget(diffDays + 30, 31, i, "#ff0000")
}else{
getwidget(diffDays + 30, 31, i, "#ffd60a")
}
}
Script.setWidget(w)
Script.complete()
w.presentSmall()
function getwidget(ends, haveGone, str, color) {
const titlew = w.addText(str)
titlew.textColor = new Color("#e587ce")
titlew.font = Font.boldSystemFont(13)
w.addSpacer(6)
const imgw = w.addImage(creatProgress(ends, haveGone, color))
imgw.imageSize=new Size(width, h)
w.addSpacer(6)
}
function creatProgress(ends, havegone, color){
const context =new DrawContext()
context.size=new Size(width, h)
context.opaque=false
context.respectScreenScale=true
context.setFillColor(new Color("#48484b"))
const path = new Path()
path.addRoundedRect(new Rect(0, 0, width, h), 3, 2)
context.addPath(path)
context.fillPath()
context.setFillColor(new Color(color))
const path1 = new Path()
path1.addRoundedRect(new Rect(0, 0, width*havegone/ends, h), 3, 2)
context.addPath(path1)
context.fillPath()
return context.getImage()
}
- 汇率展示
- 这个需要到一个网址申请api,app.exchangerate-api.com,每月有1500次请求限制而已,将代码中的url换上自己的即可
const w = new ListWidget()
w.backgroundColor = new Color("#222222")
const resp= await get({url:"https://v6.exchangerate-api.com/v6/your token/latest/USD"})
const arr = Object.keys(resp["conversion_rates"]).filter(x=>x=="USD" || x=="CNY" || x=="JPY")
for(let rate of arr){
const stack =w.addStack()
stack.centerAlignContent()
const img= await loadImage("https://www.ecb.europa.eu/shared/img/flags/"+rate+".gif")
const imgw =stack.addImage(img)
imgw.imageSize=new Size(20, 20)
stack.addSpacer(20)
const textw = stack.addText(resp["conversion_rates"][rate].toFixed(2))
textw.textColor = Color.white()
stack.addSpacer(20)
}
Script.setWidget(w)
Script.complete()
w.presentLarge()
async function get(opts) {
const request = new Request(opts.url)
request.headers = {
...opts.headers,
...this.defaultHeaders
}
var result=await request.loadJSON()
return result
}
async function loadImage(imgUrl) {
let req = new Request(imgUrl)
let image = await req.loadImage()
return image
}
- 微博热搜Top10
- 伯衡君这里设置了两种模式,一种是网页打开模式,一种是小组件模式,可以自由选择,如果选择网页打开模式,那么建议把.slice(0,10)去掉,然后将// WebView.loadHTML(title)前面的“//”去掉
const wv = new WebView()
const w = new ListWidget()
w.backgroundColor = new Color("#E6162D")
const url = "https://m.weibo.cn/api/container/getIndex?containerid=106003type%3D25%26t%3D3%26disable_hot%3D1%26filter_type%3Drealtimehot"
const req = new Request(url)
const result = await req.loadJSON()
const check = result.ok
var title = `
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body{
background-color:#E6162D;
}
.weibohot{
color:#000000;
}
ul li{
list-style:none;
}
</style>
<div calss="weibohot">
<ul>
`
if (check != 1) {
return
} else {
for (let i of result.data.cards[0].card_group.slice(0,10)) {
const stack = w.addStack()
stack.centerAlignContent()
const text = stack.addText(i.desc)
text.textColor = Color.white()
text.font = Font.semiboldSystemFont(9)
title += `<li>${i.desc}</li>`;
}
title += "</ul></div>"
}
// WebView.loadHTML(title)
if(Device.isPad()){
w.presentExtraLarge()
}else if(Device.isPhone()){
w.presentLarge()
}
Script.setWidget(w)
Script.complete()
- 实时上证和深成指数
const w = new ListWidget()
w.backgroundColor = new Color("#4169E1")
const array = ["上证指数", "深圳成指"]
let url = "https://cors.luckydesigner.workers.dev/?http://hq.sinajs.cn/list=sh000001,sz399001"
let req = new Request(url)
let result = await req.loadString()
let sz = result.split(";")
for(let i = 0; i<sz.length - 1; i++){
const stack = w.addStack()
stack.centerAlignContent()
const text = stack.addText(array[i])
text.textColor = Color.white()
stack.addSpacer(20)
const ratew = stack.addText(sz[i].split(",")[3])
ratew.textColor = Color.white()
stack.addSpacer(20)
}
if(Device.isPad()){
w.presentExtraLarge()
}else if(Device.isPhone()){
w.presentLarge()
}
Script.setWidget(w)
Script.complete()
- 知乎热榜Top10
const w = new ListWidget()
w.backgroundColor = new Color("#4169E1")
let url = "https://api.zhihu.com/topstory/hot-lists/total?limit=10&reverse_order=0"
let req = new Request(url)
let result = await req.loadJSON()
for(let i = 0; i<result.data.length; i++){
if(i<10){
const stack = w.addStack()
stack.centerAlignContent()
const text = stack.addText(`${i + 1}.${result.data[i].target.title}`)
text.textColor = Color.white()
text.font = Font.semiboldSystemFont(9)
}
}
if(Device.isPad()){
w.presentExtraLarge()
}else if(Device.isPhone()){
w.presentLarge()
}
Script.setWidget(w)
Script.complete()
- Nasa美图
- 先前往https://api.nasa.gov/申请一个key,之后替换脚本中的Api key为自己的就可以了
let apiURL = "https://api.nasa.gov/planetary/apod";
let apiKey = "Api Key";
if ( Keychain.contains('Api Key') ) {
apiKey = Keychain.get('Api Key');
}
let apodNavURL = "https://apod.nasa.gov";
function createWidget() {
let widget = new ListWidget();
widget.spacing = 0;
let now = Date.now();
let delay = 6 * 60 * 60 * 1000;
let nextUpdate = new Date(now + delay);
widget.refreshAfterDate = nextUpdate;
return widget;
}
async function loadPhoto(widget) {
let requestURI = `${apiURL}?api_key=${apiKey}`;
let request = new Request(requestURI);
let json = await request.loadJSON();
if ( json && json.url ) {
if ( json.media_type == 'image' ) {
let imgRequest = new Request(json.url);
let img = await imgRequest.loadImage();
widget.backgroundImage = img;
}
else if ( json.media_type == 'video' && json.url.match(/^https?:\/\/(www.)?youtube.com/) ) {
let ytID = json.url.match(/\/embed\/([^)]+)\?.*/, "/vi/$1/0.jpg")
let imgURL = `https://img.youtube.com/vi/${ ytID[1] }/0.jpg`;
let imgRequest = new Request(imgURL);
let img = await imgRequest.loadImage();
widget.backgroundImage = img;
let font = Font.blackRoundedSystemFont(10);
let text = widget.addText("Video");
text.rightAlignText();
text.font = font;
text.textColor = Color.white();
widget.addSpacer(null);
}
else {
let font = Font.blackRoundedSystemFont(22);
let text = widget.addText("今天没有天文图像 "+json.media_type);
text.centerAlignText();
text.font = font;
text.textColor = Color.white();
let bg = new LinearGradient();
bg.colors = [
new Color("#6666ff", 1.0),
new Color("#000033", 1.0)
];
bg.locations = [0, 1];
widget.backgroundGradient = bg;
}
widget.url = apodNavURL;
} else if ( json && json.error && json.error.message ) {
widget.addText(json.error.message);
} else {
widget.addText("Error, no message")
}
}
let widget = createWidget();
await loadPhoto(widget);
if (config.runsInWidget) {
Script.setWidget(widget);
} else {
widget.presentLarge();
}
Script.complete();
- 随机菜谱
- 随机展示一道菜,点击插件后跳转到Youtube观看制作方法
const {meals} = await new Request("https://www.themealdb.com/api/json/v1/1/random.php").loadJSON()
const {
strMeal: title,
strMealThumb: imgUrl,
strYoutube: videoUrl,
strSource: articleUrl
} = meals[0]
const w = new ListWidget()
w.url = videoUrl || articleUrl
w.backgroundColor = new Color("#222222")
w.backgroundImage = await new Request(imgUrl).loadImage()
const t = w.addText(title)
t.textColor = Color.white()
t.font = new Font("Avenir-Heavy", 28)
t.shadowRadius = 3
t.shadowColor = Color.black()
t.centerAlignText()
if(Device.isPad()){
w.presentExtraLarge()
}else if(Device.isPhone()){
w.presentLarge()
}
Script.setWidget(w)
Script.complete()
- 随机电影台词
- 随机一句经典电影台词

const isLarge = config.widgetFamily === 'large'
async function getData() {
const target = 'https://howdz.deno.dev/movieLines'
const { img1, img2, img3, img4, link, name, quotes } = await (new Request(target)).loadJSON()
const randomImgArr = [img1, img1, img2, img3, img4].filter(Boolean)
const randomImgIdx = ~~(Math.random() * randomImgArr.length)
const randomImg = randomImgArr[randomImgIdx]
const wallpaperImg = randomImg.replace('original', 'w1280')
const img = await (new Request(wallpaperImg)).loadImage()
const greyImg = await getGreyImg(img)
return { name, link, quotes, img: greyImg }
}
async function getGreyImg(img) {
let ctx = new DrawContext()
ctx.size = img.size
ctx.drawImageInRect(img, new Rect(0, 0, img.size['width'], img.size['height']))
ctx.setFillColor(new Color("#000000", 0.7))
ctx.fillRect(new Rect(0, 0, img.size['width'], img.size['height']))
let res = await ctx.getImage()
return res
}
const widget = new ListWidget()
const { name: movieName, link, quotes, img } = await getData()
widget.backgroundImage = img
widget.url = link
const content = widget.addStack()
content.setPadding(0, 0, 10, 0)
const quotesText = content.addText(quotes)
quotesText.lineLimit = isLarge ? 5: 3
quotesText.textColor = Color.white()
quotesText.font = Font.boldSystemFont(isLarge ? 24: 18)
quotesText.shadowRadius = 2
quotesText.shadowOffset = new Point(1, 1)
const footer = widget.addStack()
footer.setPadding(isLarge ? 32 : 16, 0, 0, 0)
footer.addSpacer()
const footerText = footer.addText(`——『 ${movieName} 』`)
footerText.lineLimit = 1
footerText.shadowRadius = 2
footerText.shadowOffset = new Point(1, 1)
footerText.textColor = Color.white()
footerText.rightAlignText()
if (config.runsInWidget) {
Script.setWidget(widget)
} else {
widget.presentMedium()
}
Script.complete()
- 随机古诗
- 随机生成一段古诗

const fontSizeMap = {
small: 16,
medium: 24,
large: 32
}
const fontSize = fontSizeMap[config.widgetFamily || 'medium']
const loadImage = async (url) => (await new Request(url)).loadImage()
const getGreyImg = async (img, light = 0.4) => {
const ctx = new DrawContext()
ctx.size = img.size
ctx.drawImageInRect(img, new Rect(0, 0, img.size['width'], img.size['height']))
ctx.setFillColor(new Color("#000000", light))
ctx.fillRect(new Rect(0, 0, img.size['width'], img.size['height']))
return await ctx.getImage()
}
const loadRandomBg = async () => {
const randomImgURL = 'https://howdz.deno.dev/unsplash/random?w=768&keyword=Nature'
const randomImg = await loadImage(randomImgURL)
const greyImg = await getGreyImg(randomImg)
return greyImg
}
const loadVerse = async () => (await new Request('https://v1.jinrishici.com/all.json')).loadJSON()
const { content: randomVerse, origin: title }= await loadVerse()
const widget = new ListWidget()
const randomImg = await loadRandomBg()
widget.setPadding(0, fontSize / 2, 0, fontSize / 2)
widget.backgroundImage = randomImg
widget.url = 'https://hanyu.baidu.com/s?wd=' + encodeURIComponent(title)+ '&from=poem'
const textEl = widget.addText(randomVerse)
textEl.textColor = Color.white()
textEl.font = Font.boldSystemFont(fontSize)
textEl.shadowRadius = 2
textEl.shadowOffset = new Point(1, 1)
textEl.centerAlignText()
if (config.widget !== 'small') {
const footer = widget.addStack()
footer.setPadding(fontSize, 0, 0, 0)
footer.addSpacer()
const footerText = footer.addText(`——『 ${title} 』`)
footer.font = Font.boldSystemFont(16)
footerText.lineLimit = 1
footerText.shadowRadius = 2
footerText.shadowOffset = new Point(1, 1)
footerText.textColor = Color.white()
footerText.rightAlignText()
}
if (config.runsInWidget) {
Script.setWidget(widget)
} else {
widget.presentMedium()
}
Script.complete()
- 随机英文名居
- 随机生成一段英文

const fontSizeMap = {
small: 18,
medium: 24,
large: 32
}
const fontSize = fontSizeMap[config.widgetFamily || 'medium']
const loadImage = async (url) => (await new Request(url)).loadImage()
const getGreyImg = async (img, light = 0.4) => {
const ctx = new DrawContext()
ctx.size = img.size
ctx.drawImageInRect(img, new Rect(0, 0, img.size['width'], img.size['height']))
ctx.setFillColor(new Color("#000000", light))
ctx.fillRect(new Rect(0, 0, img.size['width'], img.size['height']))
return await ctx.getImage()
}
const loadRandomBg = async () => {
const randomImgURL = 'https://howdz.deno.dev/unsplash/random?w=768&keyword=Nature'
const randomImg = await loadImage(randomImgURL)
const greyImg = await getGreyImg(randomImg)
return greyImg
}
const loadSentence = async () => (await new Request('https://favqs.com/api/qotd')).loadJSON()
const { quote } = await loadSentence()
const { body: text, author } = quote
const widget = new ListWidget()
const randomImg = await loadRandomBg()
widget.setPadding(0, fontSize / 2, 0, fontSize / 2)
widget.backgroundImage = randomImg
const textEl = widget.addText(text.replaceAll('\\r', '').replaceAll('<br>', ''))
textEl.textColor = Color.white()
textEl.font = Font.boldSystemFont(fontSize)
textEl.shadowRadius = 2
textEl.shadowOffset = new Point(1, 1)
textEl.centerAlignText()
if (config.widgetFamily !== 'small') {
const footer = widget.addStack()
footer.setPadding(fontSize, 0, 0, 0)
footer.addSpacer()
const footerText = footer.addText(`—— ${author}`)
footer.font = Font.boldSystemFont(16)
footerText.lineLimit = 1
footerText.shadowRadius = 2
footerText.shadowOffset = new Point(1, 1)
footerText.textColor = Color.white()
footerText.rightAlignText()
}
if (config.runsInWidget) {
Script.setWidget(widget)
} else {
widget.presentMedium()
}
Script.complete()
- 我的微信
- 微信扫一扫加好友
-
- 我的微信公众号
- 扫描关注公众号
-





