迁移回来了
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
#!/bin/bash
|
||||
username=$(whoami)
|
||||
script_dir=$(dirname "$0")
|
||||
cd $script_dir
|
||||
echo "==Tesuto Alarm Dec 2025(By Cirrus)安装程序=="
|
||||
echo "当前用户: $username"
|
||||
echo "当前目录: $script_dir"
|
||||
echo ""
|
||||
echo "检测程序是否正在运行:"
|
||||
if launchctl list | grep -q cn.org.cirrus.tesutoalarmdec2025;then
|
||||
echo "在运行,先终止进程,再安装。"
|
||||
echo ""
|
||||
launchctl stop cn.org.cirrus.tesutoalarmdec2025
|
||||
launchctl unload /Users/$username/Library/LaunchAgents/cn.org.cirrus.tesutoalarmdec2025.plist
|
||||
echo ""
|
||||
echo "继续安装。"
|
||||
else
|
||||
echo "不在运行,继续安装。"
|
||||
fi
|
||||
echo ""
|
||||
echo "提示:程序文件默认保存目录:"
|
||||
echo "二进制文件:${script_dir}/tesuto"
|
||||
echo "STDOUT目录:${script_dir}/tesuto.log"
|
||||
echo "STDERR目录:${script_dir}/tesuto.error.log"
|
||||
echo "注意:请勿重命名、移动、修改、删除。"
|
||||
echo ""
|
||||
echo "请输入声音文件的绝对路径:(直接回车表示$script_dir/sound.mp3)"
|
||||
read sound
|
||||
if [ "$sound" == "" ];then
|
||||
sound="${script_dir}/sound.mp3"
|
||||
fi
|
||||
echo "注意:为保证程序运行正常,请保持该文件在原目录,且未重命名。"
|
||||
echo ""
|
||||
echo "请输入闹铃JSON文件的保存文件夹目录:(以“/”结尾,请勿加上alarms.json)"
|
||||
echo "(直接回车表示$script_dir/)"
|
||||
read jsonpath
|
||||
if [ "$jsonpath" == "" ];then
|
||||
jsonpath="${script_dir}/"
|
||||
fi
|
||||
cat > ${jsonpath}alarms.json << EOF
|
||||
[
|
||||
{
|
||||
"name": "Test1",
|
||||
"hour": 8,
|
||||
"minute": 45,
|
||||
"triggered": false
|
||||
},
|
||||
{
|
||||
"name": "Test2",
|
||||
"hour": 9,
|
||||
"minute": 41,
|
||||
"triggered": false
|
||||
}
|
||||
]
|
||||
EOF
|
||||
cat > ./tesuto.swift << EOF
|
||||
import UniformTypeIdentifiers
|
||||
import SwiftUI
|
||||
import Combine
|
||||
class Alarm: Codable, ObservableObject
|
||||
{
|
||||
let name: String
|
||||
let hour: Int
|
||||
let minute: Int
|
||||
var triggered: Bool
|
||||
}
|
||||
let app = NSApplication.shared
|
||||
NSApp.setActivationPolicy(.regular)
|
||||
let sound = NSSound(contentsOf: URL(fileURLWithPath: ("$sound" as NSString).expandingTildeInPath), byReference: false)
|
||||
if sound != nil
|
||||
{
|
||||
print("\(Date()): 读取声音文件成功")
|
||||
print("尝试播放:(2秒后停止)")
|
||||
fflush(stdout)
|
||||
sound?.play()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2)
|
||||
{
|
||||
sound?.stop()
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print("\(Date()): 读取声音文件失败")
|
||||
fflush(stdout)
|
||||
}
|
||||
var alarms: [Alarm] = []
|
||||
func readJSON(url: URL) -> Bool
|
||||
{
|
||||
do
|
||||
{
|
||||
let json = try Data(contentsOf: url)
|
||||
let loadedData = try JSONDecoder().decode([Alarm].self, from: json)
|
||||
alarms = loadedData
|
||||
print("\(Date()): 读取JSON成功")
|
||||
print("闹钟数据:")
|
||||
for alarm in alarms
|
||||
{
|
||||
print("\n名称:\(alarm.name)")
|
||||
print("时间:\(alarm.hour):\(alarm.minute)")
|
||||
}
|
||||
fflush(stdout)
|
||||
return true
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("\(Date()): 读取JSON失败(\(error))")
|
||||
fflush(stdout)
|
||||
return false
|
||||
}
|
||||
}
|
||||
func perform(for alarm: Alarm)
|
||||
{
|
||||
let window = NSAlert()
|
||||
window.messageText = "闹钟提醒"
|
||||
window.informativeText = "\(alarm.name) - \(alarm.hour):\(alarm.minute)"
|
||||
window.addButton(withTitle: "关闭\(alarm.name)")
|
||||
print("\(Date()): 触发\(alarm.name) - \(alarm.hour):\(alarm.minute)")
|
||||
fflush(stdout)
|
||||
sound?.play()
|
||||
Task
|
||||
{
|
||||
try await Task.sleep(nanoseconds: 1 * 60 * 1_000_000_000)
|
||||
sound?.stop()
|
||||
print("\(Date()): 超时自动关闭\(alarm.name) - \(alarm.hour):\(alarm.minute)")
|
||||
fflush(stdout)
|
||||
return
|
||||
}
|
||||
window.runModal()
|
||||
sound?.stop()
|
||||
print("\(Date()): 手动关闭\(alarm.name) - \(alarm.hour):\(alarm.minute)")
|
||||
fflush(stdout)
|
||||
}
|
||||
let timer = Timer.publish(every: 1, on: .main, in: .default).autoconnect()
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
_ = readJSON(url: URL(fileURLWithPath: ("${jsonpath}alarms.json" as NSString).expandingTildeInPath))
|
||||
timer.sink
|
||||
{
|
||||
_ in
|
||||
let now = Date()
|
||||
let calendar = Calendar.current
|
||||
let currentSecond = calendar.component(.second, from: now)
|
||||
if currentSecond < 3
|
||||
{
|
||||
let currentHour = calendar.component(.hour, from: now)
|
||||
let currentMinute = calendar.component(.minute, from: now)
|
||||
for alarm in alarms
|
||||
{
|
||||
if alarm.hour == currentHour && alarm.minute == currentMinute && !alarm.triggered
|
||||
{
|
||||
perform(for: alarm)
|
||||
alarm.triggered.toggle()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 5)
|
||||
{
|
||||
alarm.triggered.toggle()
|
||||
print("\(Date()): 清除\(alarm.name)的triggered标识")
|
||||
fflush(stdout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
RunLoop.current.run()
|
||||
EOF
|
||||
swiftc tesuto.swift
|
||||
cat > ./cn.org.cirrus.tesutoalarmdec2025.plist << EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>cn.org.cirrus.tesutoalarmdec2025</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>${script_dir}/tesuto</string>
|
||||
</array>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>${script_dir}/tesuto.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>${script_dir}/tesuto.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
cp ./cn.org.cirrus.tesutoalarmdec2025.plist /Users/$username/Library/LaunchAgents/cn.org.cirrus.tesutoalarmdec2025.plist
|
||||
launchctl load /Users/$username/Library/LaunchAgents/cn.org.cirrus.tesutoalarmdec2025.plist
|
||||
echo ""
|
||||
echo "如果听到您指定音频文件的开头2秒,则表明程序安装完成。"
|
||||
echo "详见cirrus.log。"
|
||||
echo "更改时区代码较为麻烦,故请您将就看,把每个时间的小时数+8即可。"
|
||||
Reference in New Issue
Block a user