fix: delete extra file
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# goodBaby
|
||||
# goodBaby v2
|
||||
|
||||
摇篮系统
|
||||
|
||||
@@ -6,82 +6,10 @@
|
||||
|
||||
## 他会做什么
|
||||
|
||||
1. 接受 singnal 请求
|
||||
2. 当定时器超时(过长时间没有收到请求)时,触发 `trigger` 函数
|
||||
3. 发送消息到QQ(依赖[CatBot](https://github.com/ssdomei232/CatBot))
|
||||
4. 发送邮件
|
||||
5. 将 Github 上的遗言仓库设置为 public
|
||||
6. 发送一条 Bilibili 动态
|
||||
7. 当倒计时小于48小时时,向用钉钉机器人发消息提醒你
|
||||
船新版本正在开发...
|
||||
|
||||
## 配置
|
||||
将支持:
|
||||
|
||||
> [!TIP]
|
||||
> 更详细的配置注解请前往 [Wiki 页面](https://github.com/ssdomei232/goodBaby/wiki)
|
||||
|
||||
参照 config.example.json 修改 config.json
|
||||
|
||||
```json
|
||||
{
|
||||
"signal_secret": "xxx", // 发送 signal请求时需要的密钥
|
||||
"debug": false, // debug 模式
|
||||
"disconnect_duration": 80, // 失连时间,超出后会触发系统(小时)
|
||||
"bili_msg": "摇篮系统测试", // 哔哩哔哩动态的附加消息
|
||||
"mail_config": {
|
||||
"host": "smtp.example.com",
|
||||
"port": 465,
|
||||
"user": "xxx",
|
||||
"pass": "xxx",
|
||||
"mail_list": ["xxx@example.com", "xxx1@example.com"],// 要发送的邮箱
|
||||
"mail_title": "摇篮系统已触发", // 邮件标题
|
||||
"mail_content": "这是摇篮系统的邮件内容。" // 要附加的邮件内容
|
||||
}, // 邮件配置
|
||||
"basic": {
|
||||
"name": "xx", // 你的名字
|
||||
"qq_number": "123", // 你的QQ号
|
||||
"nickname": "xx_123", // 网名
|
||||
"age": 16, // 你的年龄
|
||||
"cause_stop": "自杀" // 你最可能的死因
|
||||
},
|
||||
"github_config": {
|
||||
"owner": "example", // github用户名
|
||||
"repos": ["example-repo1", "example-repo2"], // 要公开的 github 仓库
|
||||
"token": "ghp_xxxx" // github token
|
||||
},
|
||||
"dingtalk_bot": {
|
||||
"access_token": "xxx",
|
||||
"secret": "xxx"
|
||||
}, // 钉钉机器人(用于提醒发送signal和bilibili过期提醒)
|
||||
"onebot_config": {
|
||||
"url": "http://localhost:5700",
|
||||
"token": "xxx",
|
||||
"send_groups": [12345678, 87654321], // 要发送到的Onebot群组
|
||||
"msg": "这是OneBot的消息内容。" // Onebot消息
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 运行
|
||||
|
||||
```bash
|
||||
mkdir goodbaby
|
||||
cd goodbaby
|
||||
wget https://raw.githubusercontent.com/ssdomei232/goodBaby/refs/heads/main/docker-compose.yml
|
||||
docker compose up -d
|
||||
docker logs goodbaby ## 扫码登陆后才能使用
|
||||
```
|
||||
|
||||
在日志中找到 Bilibili 的登陆二维码或在文件管理中找到`tmp/qrcode.png`,打开Bilibili APP 扫码登陆
|
||||
|
||||
> [!NOTE]
|
||||
> 只有在登陆 Bilibili 后程序才会正常运行
|
||||
|
||||
## 使用
|
||||
|
||||
访问你开放的端口(默认为8088),进入WebUI,输入你的密钥,点击保存(密钥会存储到浏览器的localStorage中),此时你可以在WebUI查看倒计时和发送Signal
|
||||
|
||||
## Todo list
|
||||
|
||||
- [ ] 熔断机制
|
||||
- [ ] Webhook 自定义请求
|
||||
- [ ] 保存定时器状态
|
||||
* WebUI
|
||||
* 更多通知渠道
|
||||
* 更灵活的设置
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
background-color: #f8fafc;
|
||||
color: #334155;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 640px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
background: #f1f5f9;
|
||||
padding: 32px 24px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.875rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 32px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.card:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
color: #1e293b;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
font-weight: 500;
|
||||
color: #334155;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
padding: 12px 16px;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 6px;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: #94a3b8;
|
||||
box-shadow: 0 0 0 3px rgba(148, 163, 184, 0.1);
|
||||
}
|
||||
|
||||
.status-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.timer-display {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #e2e8f0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: #3b82f6;
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.btn-primary, .btn-secondary, .btn-danger {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #64748b;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #475569;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: 0.875rem;
|
||||
color: #64748b;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 24px;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
.notification {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 12px 20px;
|
||||
border-radius: 6px;
|
||||
color: white;
|
||||
font-size: 0.875rem;
|
||||
transform: translateX(200%);
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 1000;
|
||||
max-width: 300px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.notification.success {
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
.notification.error {
|
||||
background: #ef4444;
|
||||
}
|
||||
|
||||
.notification.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
class TimerController {
|
||||
constructor() {
|
||||
this.secret = localStorage.getItem('timerSecret') || '';
|
||||
this.baseURL = window.location.origin;
|
||||
this.initElements();
|
||||
this.bindEvents();
|
||||
this.updateSecretDisplay();
|
||||
|
||||
// 自动刷新状态
|
||||
this.startAutoRefresh();
|
||||
}
|
||||
|
||||
initElements() {
|
||||
this.secretInput = document.getElementById('secret');
|
||||
this.saveSecretBtn = document.getElementById('save-secret');
|
||||
this.refreshStatusBtn = document.getElementById('refresh-status');
|
||||
this.sendSignalBtn = document.getElementById('send-signal');
|
||||
this.remainingTimeEl = document.getElementById('remaining-time');
|
||||
this.progressFillEl = document.getElementById('progress-fill');
|
||||
this.notificationEl = document.getElementById('notification');
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.saveSecretBtn.addEventListener('click', () => this.saveSecret());
|
||||
this.refreshStatusBtn.addEventListener('click', () => this.fetchStatus());
|
||||
this.sendSignalBtn.addEventListener('click', () => this.sendSignal());
|
||||
|
||||
// 回车键保存密钥
|
||||
this.secretInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.saveSecret();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveSecret() {
|
||||
const secret = this.secretInput.value.trim();
|
||||
if (!secret) {
|
||||
this.showNotification('请输入密钥', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.secret = secret;
|
||||
localStorage.setItem('timerSecret', secret);
|
||||
this.updateSecretDisplay();
|
||||
this.showNotification('密钥已保存', 'success');
|
||||
this.fetchStatus(); // 保存后自动刷新状态
|
||||
}
|
||||
|
||||
updateSecretDisplay() {
|
||||
if (this.secret) {
|
||||
this.secretInput.placeholder = '密钥已保存';
|
||||
this.secretInput.value = '';
|
||||
} else {
|
||||
this.secretInput.placeholder = '输入您的访问密钥';
|
||||
}
|
||||
}
|
||||
|
||||
async fetchStatus() {
|
||||
try {
|
||||
const response = await fetch(`${this.baseURL}/timer/status`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200) {
|
||||
this.displayTime(data.remaining_time);
|
||||
} else {
|
||||
this.showNotification('获取状态失败', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching status:', error);
|
||||
this.showNotification('网络错误,请稍后重试', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
displayTime(seconds) {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const secs = Math.floor(seconds % 60);
|
||||
|
||||
this.remainingTimeEl.textContent =
|
||||
`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
||||
|
||||
// 更新进度条(假设最大时间为24小时)
|
||||
const maxTime = 24 * 3600; // 24小时
|
||||
const progress = Math.min(100, (seconds / maxTime) * 100);
|
||||
this.progressFillEl.style.width = `${100 - progress}%`;
|
||||
}
|
||||
|
||||
async sendSignal() {
|
||||
if (!this.secret) {
|
||||
this.showNotification('请先设置密钥', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.baseURL}/signal?secret=${encodeURIComponent(this.secret)}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200) {
|
||||
this.showNotification('信号发送成功!', 'success');
|
||||
setTimeout(() => this.fetchStatus(), 1000); // 1秒后刷新状态
|
||||
} else {
|
||||
this.showNotification(`操作失败: ${data.message}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error sending signal:', error);
|
||||
this.showNotification('网络错误,请稍后重试', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
showNotification(message, type) {
|
||||
this.notificationEl.textContent = message;
|
||||
this.notificationEl.className = `notification ${type} show`;
|
||||
|
||||
setTimeout(() => {
|
||||
this.notificationEl.classList.remove('show');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
startAutoRefresh() {
|
||||
// 每30秒自动刷新一次状态
|
||||
setInterval(() => {
|
||||
if (this.secret) {
|
||||
this.fetchStatus();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
// 页面加载完成后立即获取一次状态
|
||||
setTimeout(() => this.fetchStatus(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new TimerController();
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>GoodBaby</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>GoodBaby</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="card">
|
||||
<h2>访问设置</h2>
|
||||
<div class="form-group">
|
||||
<label for="secret">密钥</label>
|
||||
<input type="password" id="secret" placeholder="输入您的访问密钥">
|
||||
<button id="save-secret" class="btn-secondary">保存</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>状态信息</h2>
|
||||
<div class="status-container">
|
||||
<div class="timer-display">
|
||||
<span id="remaining-time">--:--:--</span>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div id="progress-fill" class="progress-fill"></div>
|
||||
</div>
|
||||
<button id="refresh-status" class="btn-primary">刷新状态</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>执行操作</h2>
|
||||
<div class="controls">
|
||||
<button id="send-signal" class="btn-danger">发送信号</button>
|
||||
<p class="hint">点击按钮重置定时器</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© 2026 GoodBaby</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div id="notification" class="notification"></div>
|
||||
|
||||
<script src="/static/js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user