Rust练手项目,写个有趣的小工具定时从一言网获取一段有趣的话并推送通知
Rust练手项目,写个有趣的小工具
- 代码
继续练习Rust, 写个小工具定时从一言网获取一段有趣的话并提示,如下
练习以下Rust点
- 并发编程 Mutex, Arc指针使用
- HTTP请求
- Windows Gui
代码
Cargo.toml
[package]
name = "funny_word"
edition = "2021"
version.workspace = true
authors.workspace = true
description.workspace = true
[dependencies]
trayicon = "0.2.0"
winapi = { version = "0.3.9", features = [
"winuser",
"windef",
"minwindef",
"shellapi",
"libloaderapi",
"commctrl",
"basetsd",
] }
win-toast-notify = "0.1.6"
windows = { version = "0.58.0", features = [
"Win32_Security",
"Win32_System_Threading",
"Win32_UI_WindowsAndMessaging",
] }
reqwest = { version = "0.11", features = ["blocking"] }
[package]
name = "funny_word"
edition = "2021"
version.workspace = true
authors.workspace = true
description.workspace = true
[dependencies]
trayicon = "0.2.0"
winapi = { version = "0.3.9", features = [
"winuser",
"windef",
"minwindef",
"shellapi",
"libloaderapi",
"commctrl",
"basetsd",
] }
win-toast-notify = "0.1.6"
windows = { version = "0.58.0", features = [
"Win32_Security",
"Win32_System_Threading",
"Win32_UI_WindowsAndMessaging",
] }
reqwest = { version = "0.11", features = ["blocking"] }
main.rs
#![windows_subsystem = "windows"]
use core::mem::MaybeUninit;
use std::{
sync::{Arc, Mutex},
thread::sleep,
time::Duration,
};
use trayicon::*;
use win_toast_notify::WinToastNotify;
use winapi::um::winuser;
fn main() {
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum Events {
RightClickTrayIcon,
LeftClickTrayIcon,
OneMinute,
FiveMinute,
FifteenMinute,
HalfHour,
OneHour,
Exit,
}
let (sender, receiver) = std::sync::mpsc::channel::<Events>();
let icon_bytes = include_bytes!("rust.ico");
let mut tray_icon = TrayIconBuilder::new()
.sender(move |e: &Events| {
let _ = sender.send(*e);
})
.icon(Icon::from_buffer(icon_bytes, None, None).unwrap())
.tooltip("💪没事喝点鸡汤吧!")
.on_right_click(Events::RightClickTrayIcon)
.on_click(Events::LeftClickTrayIcon)
.menu(
MenuBuilder::new()
.separator()
.submenu(
"提醒间隔",
MenuBuilder::new()
.checkable("1分钟", true, Events::OneMinute)
.checkable("5分钟", false, Events::FiveMinute)
.checkable("15分钟", false, Events::FifteenMinute)
.checkable("半小时", false, Events::HalfHour)
.checkable("一小时", false, Events::OneHour),
)
.separator()
.item("退出", Events::Exit),
)
.build()
.unwrap();
let _interval = Arc::new(Mutex::new(1));
let interval = _interval.clone();
std::thread::spawn(move || {
receiver.iter().for_each(|m| match m {
Events::RightClickTrayIcon => {
tray_icon.show_menu().unwrap();
}
Events::LeftClickTrayIcon => {
tray_icon.show_menu().unwrap();
}
Events::Exit => {
std::process::exit(0);
}
Events::OneMinute => {
let mut i = interval.lock().unwrap();
*i = 1;
tray_icon
.set_menu_item_checkable(Events::OneMinute, true)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::FiveMinute, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::FifteenMinute, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::HalfHour, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::OneHour, false)
.unwrap();
}
Events::FiveMinute => {
let mut i = interval.lock().unwrap();
*i = 5;
tray_icon
.set_menu_item_checkable(Events::OneMinute, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::FiveMinute, true)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::FifteenMinute, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::HalfHour, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::OneHour, false)
.unwrap();
}
Events::FifteenMinute => {
let mut i = interval.lock().unwrap();
*i = 15;
tray_icon
.set_menu_item_checkable(Events::OneMinute, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::FiveMinute, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::FifteenMinute, true)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::HalfHour, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::OneHour, false)
.unwrap();
}
Events::HalfHour => {
let mut i = interval.lock().unwrap();
*i = 30;
tray_icon
.set_menu_item_checkable(Events::OneMinute, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::FiveMinute, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::FifteenMinute, false)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::HalfHour, true)
.unwrap();
tray_icon
.set_menu_item_checkable(Events::OneHour, false)
.unwrap();
}
Events::OneHour => {
let mut i = interval.lock().unwrap();
*i = 60;
tray_icon.set_menu_item_checkable(Events::OneMinute, false);
tray_icon.set_menu_item_checkable(Events::FiveMinute, false);
tray_icon.set_menu_item_checkable(Events::FifteenMinute, false);
tray_icon.set_menu_item_checkable(Events::HalfHour, false);
tray_icon.set_menu_item_checkable(Events::OneHour, true);
}
})
});
std::thread::spawn(move || loop {
match reqwest::blocking::get("https://v1.hitokoto.cn?&encode=text") {
Ok(resp) => match resp.text() {
Ok(text) => {
WinToastNotify::new()
.set_title("来自一言网")
.set_messages(vec![&text])
.show()
.expect("发送通知失败!");
}
Err(e) => {
WinToastNotify::new()
.set_title("错误")
.set_messages(vec![&format!(
"HTTP错误 {}",
e.status().unwrap_or_default()
)])
.show()
.expect("发送通知失败!");
}
},
Err(e) => {
WinToastNotify::new()
.set_title("获取网站内容失败错误")
.set_messages(vec![&format!(
"HTTP错误 {}",
e.status().unwrap_or_default()
)])
.show()
.expect("发送通知失败!");
}
}
let mut _b = 1;
{
// 加个大括号是为了尽快释放lock, 免得睡眠中(60 * (*i))一直占用锁,点击右键菜单无反应
let interval = _interval.clone();
let i = interval.lock().unwrap();
_b = *i;
}
sleep(Duration::from_secs(60 * _b));
});
loop {
unsafe {
let mut msg = MaybeUninit::uninit();
let bret = winuser::GetMessageA(msg.as_mut_ptr(), 0 as _, 0, 0);
if bret > 0 {
winuser::TranslateMessage(msg.as_ptr());
winuser::DispatchMessageA(msg.as_ptr());
} else {
break;
}
}
}
}
rust.ico图标文件放在main.rs目录