DisturbYouself

电子产品阻断项目总结

熙熙攘攘,皆为机往。

在这个电子产品充斥着生活的时代,在这个算法发展只为吞噬人们最后的一点时光的世界,或许总应该有着某些不通过自己双手来阻止自己冲动的客观

于是,想法油然而生。

我能否通过某些自动化程序,在夜深之时,在沉醉之际,在迷离之刻通过自己清醒时设定的“mean words”将自己拽出?——换句话说,让清醒的自己带走“喝醉的自己”,而非苛求一个“醉汉”保持不现实的理智。同时赋予“清醒的程序”以“未知的神圣”——通过黑箱来保持自己的敬畏(或者说破解黑箱的精力远远大于放下手中的“屠刀”

——简单的footer似乎可以轻松解决。

基于MacOS+Python的阻断脚本

零、前置

1.Python 模块导入与使用

  • 标准库无需安装;第三方库需 pip install

  • Python 内置标准库

    子模块1.1 datetime 时间处理库

    功能语法代码示例作用
    获取当前时间datetime.datetime.now()datetime.datetime.now().time()获取当前时间对象(精确到时分秒)
    创建固定时间datetime.time(时,分)datetime.time(23, 0)生成纯时间(无日期),用于时间比较
    提取时间部分.time()now().time()从完整日期时间中,只保留时分秒

    子模块1.2 random 随机数库

    功能语法代码示例作用
    随机选取列表元素random.choice(列表)random.choice(MESSAGE_LIST)从列表中随机返回一个元素

    子模块1.3 time 库

    核心功能:time.sleep(秒) 程序暂停。

2.异常处理

防止程序因报错崩溃,网络请求必用

语法类型标准语法代码示例作用
基础异常捕获try:
正常代码
except Exception as e:
报错处理
try: response=requests.post(...)
except Exception as e:
尝试执行代码,报错则执行except块
主动抛出异常response.raise_for_status()代码中网络请求状态码非200时,主动触发异常
异常信息打印print(f"失败:{e}")代码中打印具体报错原因,方便调试

3.数据结构

子模块5.1 列表(List)
特性语法代码示例说明
定义变量名 = [元素1, 元素2, ...]MESSAGE_LIST = ["无语1", "无语2", ...]有序、可修改的容器
取值列表[索引] / random.choice(列表)random.choice(MESSAGE_LIST)按位置取元素/随机取元素
子模块5.2 字典(Dict)
特性语法代码示例说明
定义变量名 = {键:值, 键:值}payload = {"msgtype": "text", ...}键值对结构,对应JSON格式
取值字典[键]payload["text"]通过键获取对应值

4.第三方库 requests(网络请求)

用于发送 HTTP 请求(bot消息推送)

功能语法代码示例说明
POST 请求requests.post(地址, json=数据, headers=头, timeout=超时)requests.post(DINGTALK_WEBHOOK_URL, ...)向服务器发送数据(bot接口)
请求头headers = {"Content-Type": "application/json"}代码中告诉服务器:发送的是JSON格式
超时设置timeout=10代码中10秒无响应则断开,避免卡死

一、macOS之launchd定时任务配置语法

mac支持的语法:** launchd(macOS 官方定时任务工具)的 plist 配置文件语法(XML格式),这是 macOS 独有的,替代 Linux 的 cron。

说明

  1. 配置文件:xxx.plist(XML格式),存放路径 ~/Library/LaunchAgents/
  2. 作用:让 Mac 自动、定时运行你的 Python 脚本

模块1:plist 核心配置语法

配置键(Key)值类型代码示例必填/可选核心作用
Label字符串com.user.dingtalkreminder必填任务唯一标识(不能重复)
ProgramArguments数组<array><string>/usr/bin/python3</string>...</array>必填要执行的命令(Python路径+脚本路径)
StartInterval整数<integer>300</integer>可选循环间隔:300秒=5分钟
RunAtLoad布尔值<true/>可选开机/唤醒电脑后立即执行一次
StandardOutPath字符串/tmp/dingtalk_reminder.log可选正常运行日志路径
StandardErrorPath字符串/tmp/dingtalk_reminder_err.log可选报错日志路径

模块2:plist 基础结构

所有 launchd 配置都必须遵循这个 XML 结构,不可修改根节点

1
2
3
4
5
6
7
<?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>
<!-- 所有配置写在这里 -->
</dict>
</plist>

模块3:launchd 终端命令

命令作用代码示例
launchctl load 路径加载并启动定时任务launchctl load ~/Library/LaunchAgents/com.user.dingtalkreminder.plist
launchctl unload 路径停止并卸载任务同上
launchctl start 标识手动立即运行一次任务launchctl start com.user.dingtalkreminder

四、整体框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
flowchart TD
subgraph Python 脚本
A1[模块导入]
A2[函数封装]
A3[异常处理]
A4[主程序入口]
end

subgraph macOS 定时任务
B1[plist 配置]
B2[加载任务 launchctl load]
B3[自动循环执行]
end

B1 --> B2 --> B3
B3 --> A1
A1 --> A2 --> A3 --> A4

五、代码实现

1.导入模块

1
2
3
import requests # 第三方网络请求库,用于向服务器发消息
import datetime
import random

2.全局配置

1
2
3
# Webhook地址
DINGTALK_WEBHOOK_URL = "xxx"
MESSAGE_LIST = ["..."]

3.工具函数封装

3.1判断时间
1
2
3
4
5
6
7
8
9
10
def is_in_time_range():
"""判断当前时间是否在允许的时间段内"""
now = datetime.datetime.now().time()
# 时间段1
if now >= datetime.time(2, 0) or now < datetime.time(9, 0):
return True
# 时间段2
if datetime.time(12, 30) <= now <= datetime.time(13, 30):
return True
return False

now = datetime.datetime.now().time():获取电脑当前的时分秒(不含日期)

第一个是库,第二个是类,第三个获取完整的函数,第四个是提取时分秒的函数

3.2发送消息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def send_dingtalk_message():
"""发送消息(随机选择语块)"""
if not DINGTALK_WEBHOOK_URL:
print("未配置Webhook")
return

# 随机选择一条消息
random_msg = random.choice(MESSAGE_LIST)

# 消息格式
payload = {
"msgtype": "text",
"text": {
"content": random_msg
}
}

headers = {'Content-Type': 'application/json'}
try:
response = requests.post(DINGTALK_WEBHOOK_URL, json=payload, headers=headers, timeout=10)
response.raise_for_status()
print(f"✅ 消息发送成功:{random_msg}")
except Exception as e:
print(f"❌ 消息发送失败:{e}")
  1. payload:字典结构,bot接收处的固定格式

  2. headers:告诉服务器发送的是JSON格式数据

  3. try-except异常处理核心

    • try:尝试发送网络请求,10秒超时

    • response.raise_for_status():请求失败主动报错

      查看服务器返回的状态码

      • 200 = 成功 ✅
      • 403/404/500 = 失败 ❌

      如果状态码是失败:立刻主动抛出异常

    • except:捕获所有错误,打印失败原因,避免程序崩溃

4.主程序入口

1
2
3
4
5
if __name__ == "__main__":
if is_in_time_range():
send_dingtalk_message()
else:
print("当前不在允许的时间段内,不发送消息")

基于安卓系统+autoX.js的阻断脚本

零、代码框架

  1. 初始化层:Auto.js权限申请 + 全局常量/数组定义
  2. 工具函数层:时间范围判断、钉钉消息发送(封装复用逻辑)
  3. 事件监听层:监听安卓系统解锁广播(核心触发条件)
  4. 定时任务层:循环检测屏幕状态 + 定时发消息
  5. 辅助层:日志输出、网络请求、异常处理

一、适配安卓Auto.js-API

区别于普通JS的,专门用于安卓设备自动化、系统交互、权限管理,代码中用到的所有Auto.js API整理如下:

1. 权限管理API

语法/API功能描述代码中示例拓展用法
auto.waitFor()阻塞等待无障碍服务权限授权成功(自动化核心权限,必须先申请)auto.waitFor();1. auto.checkService():检查权限是否开启
2. auto.openSetting():跳转到权限设置页面
3. 无权限时代码无法运行设备操作

2. 系统广播/事件监听API

语法/API功能描述代码中示例完整拓展用法
events.broadcast.on(广播名, 回调函数)监听安卓系统全局广播(系统级事件,如解锁、锁屏、开机)events.broadcast.on("android.intent.action.USER_PRESENT", ()=>{})1. 常用系统广播:
ACTION_SCREEN_ON:屏幕亮起
ACTION_SCREEN_OFF:屏幕关闭
ACTION_BOOT_COMPLETED:开机完成
2. events.broadcast.off():取消监听
log(内容)Auto.js内置日志输出(控制台打印信息,调试专用)log("脚本已启动");支持字符串、变量、对象,替代JS的console.log()

3. 设备状态检测API

语法/API功能描述代码中示例拓展用法
device.isScreenOn()判断手机屏幕是否处于亮屏状态if (device.isScreenOn())1. device.getBrightness():获取屏幕亮度
2. device.setBrightness(值):设置亮度
3. device.getBattery():获取电量

4. 网络请求API

语法/API功能描述代码中示例拓展用法
http.postJson(URL, 请求体)发送POST JSON格式请求(调用Webhook专用)http.postJson(DINGTALK_WEBHOOK, msg)1. http.get(URL):GET请求
2. http.post(URL, 参数):表单POST
3. res.body.string():获取响应结果

二、JavaScript 语法

1. 变量/常量声明(数据存储基础)

语法作用代码中示例完整规则
const声明常量(值不可修改,推荐固定值使用)const DINGTALK_WEBHOOK = "xxx"1. 必须初始化赋值
2. 赋值后不能重新赋值
3. 块级作用域
let声明变量(值可修改,推荐动态数据使用)let intervalId = null1. 可先声明后赋值
2. 可重新赋值
3. 块级作用域
❌ 禁止用var(老旧语法,有变量提升问题)

2. 数据类型与数组

(1)基本数据类型
类型示例代码中使用场景
字符串(String)"解锁提醒"
数字(Number)23/60000
空值(Null)null初始化定时器ID(无任务时为空)
布尔(Boolean)true/false
(2)引用类型:数组
数组操作语法代码中示例完整拓展用法
定义数组const 数组名 = [元素1, 元素2...]const RANDOM_MESSAGES = ["⚠️...", "📱..."]1. 空数组:let arr = []
2. 多维数组:let arr = [[1,2],[3,4]]
取数组元素数组名[索引](索引从0开始)RANDOM_MESSAGES[0]支持负数索引(Auto.js/新版JS):arr[-1]取最后一个
数组长度数组名.lengthRANDOM_MESSAGES.length动态获取元素个数,用于随机取值
3. 函数定义
函数类型语法格式代码中示例完整规则
具名函数(声明式)function 函数名(参数) { 执行逻辑 }function isInTimeRange() {}1. 可提前调用(函数提升)
2. 适合封装工具函数
匿名回调函数(参数) => { 执行逻辑 }(箭头函数)events.broadcast.on(..., (intent) => {})1. 简化函数写法
2. 无自己的this,适合事件回调
函数调用函数名(参数)isInTimeRange()/sendDingtalkMsg(new Date())无参数可省略括号

4. 流程控制

(1)条件判断:if-else
语法代码中示例完整拓展
单分支if(条件) {}if (intervalId) {}
双分支if(条件) {} else {}if (isInTimeRange()) {} else {}
多分支if(条件1) {} else if(条件2) {} else {}时间判断中多条件组合
(2)逻辑运算符
运算符含义代码中示例
``
===严格相等(值+类型都相等)hour === 8(推荐用,杜绝隐式转换)
&&且(同时满足)hour ===8 && minute <=45

5. Date 日期对象

方法/属性功能代码中示例常用API
new Date()创建当前时间对象const now = new Date()基础时间对象
getHours()获取小时(0-23)now.getHours()时间判断核心
getMinutes()获取分钟(0-59)now.getMinutes()时间判断核心
toLocaleString()转为本地可读时间字符串unlockTime.toLocaleString()格式化时间:2026/3/27 15:30:00
拓展获取秒/年/月/日getSeconds()/getFullYear()完整时间处理

6. 定时器(循环/延迟执行)

API功能代码中示例完整规则
setInterval(回调, 毫秒)循环执行任务(每隔N毫秒执行一次)setInterval(()=>{}, 60000)1. 返回唯一ID(用于关闭)
2. 60000毫秒=1分钟
clearInterval(定时器ID)停止循环定时器clearInterval(intervalId)必须传入ID,否则无法停止
拓展setTimeout(回调, 毫秒)延迟一次执行仅执行一次,对应clearTimeout

7. 字符串操作(文本处理)

方法功能代码中示例完整拓展
字符串.replace(旧值, 新值)替换文本内容randomMsg.replace("{time}", timeStr)1. 支持正则替换:str.replace(/a/g, "b")
2. 替换所有匹配项
模板字符串`内容${变量}内容``${DINGTALK_KEYWORD}:${finalMsg}`简化字符串拼接,替代+

8. Math 数学对象

方法功能代码中示例常用拓展
Math.floor(数值)向下取整Math.floor(Math.random() * 长度)取整数索引
Math.random()生成0-1随机小数搭配取整实现随机取数组元素1. Math.ceil():向上取整
2. Math.round():四舍五入

9. 异常处理(代码容错)

语法功能代码中示例完整规则
try{...} catch(e){...}捕获代码错误,避免脚本崩溃消息发送网络请求包裹1. try:放可能报错的代码
2. catch(e):捕获错误信息
3. 可选finally:无论对错都执行

三、代码实现

整体功能

手机解锁监控脚本:监听安卓手机解锁事件 → 仅在夜间/凌晨指定时间段生效 → 解锁后每隔1分钟发送钉钉提醒 → 锁屏后自动停止提醒 → 重复解锁会清理旧任务,避免重复通知。


权限初始化(脚本运行第一步)

1
2
// 确保自动化权限就绪
auto.waitFor();
  1. 作用:Auto.js 核心权限申请,阻塞等待用户开启「无障碍服务」
  2. 必要性:安卓系统限制,所有自动化操作(监听系统事件、设备状态)必须开启此权限
  3. 执行时机:脚本启动第一时间执行,没权限脚本会一直等待,不会往下运行

全局常量定义

1
2
3
4
5
6
7
8
9
10
11
12
// 机器人Webhook地址
const DINGTALK_WEBHOOK = "xxxx";
const DINGTALK_KEYWORD = "解锁封印";
const RANDOM_MESSAGES = [
// 可以是一些mean words that hurt your heart
// 此处选择随机性语段,避免单调()
];
const SEND_INTERVAL = 60000; // 发送间隔:60秒

//全局变量
let loopTimer = null; // 存储循环定时器ID
const ctx = (typeof context !== 'undefined') ? context : app.context;

loopTimer = null

  1. let 声明变量:值会动态修改,用于存储定时器的唯一ID
  2. 初始值 null:代表当前没有运行任何定时任务
  3. 后续用来开启/关闭/清理定时器,防止多个任务重复运行

ctx 上下文兼容(关键!脚本不报错的核心)不过暂时也无法理解

  • Auto.js 不同版本环境不同,此行代码自动兼容上下文
  • 上下文是安卓系统调用广播、组件的必备参数,缺失会直接崩溃

工具函数

时间范围判断
1
2
3
4
5
6
7
8
// 时间范围判断:23:30 ~ 次日 08:45
function isInTimeRange() {
const now = new Date();
const currentMin = now.getHours() * 60 + now.getMinutes();
const startMin = xx * 60+yy; // xx:yy 转为总分钟数
const endMin = x * 60 + y; // x:y 转为总分钟数
return currentMin >= startMin || currentMin <= endMin;
}
  1. 作用:判断**当前时间是否在监控时间段内
  2. 内部
    • new Date():获取当前系统时间
    • getHours():获取当前小时(0-23)
    • getMinutes():获取当前分钟(0-59)
  3. 监控时间段(脚本生效时间)
    • xx:yy ~ 次日x:y
  4. 每次手机解锁时调用且只在夜间/凌晨监控,白天不打扰
停止循环任务(封装复用)
1
2
3
4
5
6
7
8
// 停止循环发送
function stopNotificationLoop() {
if (loopTimer) {
clearInterval(loopTimer);
loopTimer = null;
log("🛑 已停止循环发送任务");
}
}
发送消息函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 执行发送逻辑
function doSend() {
if (!isInTimeRange()) return; // 不在时间范围,直接退出

const now = new Date();
// 格式化时间:HH:MM(分钟补0,例如 9:5 → 09:05)
const timeStr = now.getHours() + ":" + now.getMinutes().toString().padStart(2, '0');
// 随机取一条消息
const rawMsg = RANDOM_MESSAGES[Math.floor(Math.random() * RANDOM_MESSAGES.length)];
// 拼接最终消息
const content = `${DINGTALK_KEYWORD}${rawMsg.replace("{time}", timeStr)}`;


// 🌟 关键:子线程发送网络请求(不阻塞主线程,不卡顿)
threads.start(function() {
try {
let res = http.postJson(DINGTALK_WEBHOOK, {
"msgtype": "text",
"text": { "content": content }
});
log("📤 发送结果:" + res.body.string());
} catch (e) {
log("❌ 发送失败:" + e.message);
}
});
}
  1. threads.start() (重要)
    • 网络请求放在子线程执行
    • 子线程完全不影响脚本运行,稳定发送
  2. try/catch:捕获网络错误,脚本不会崩溃

核心监听(安卓原生广播)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 自定义广播接收器
const myReceiver = new JavaAdapter(android.content.BroadcastReceiver, {
onReceive: function(context, intent) {
let action = intent.getAction(); // 获取系统广播动作

// 情况1:用户解锁手机(进入桌面)
if (action == android.content.Intent.ACTION_USER_PRESENT) {
log("🔓 检测到解锁,检查时间段...");
stopNotificationLoop(); // 先清理旧定时器,防止重复

if (isInTimeRange()) {
log("✅ 在监控时间内,开启循环提醒");
doSend(); // 解锁后立即发第一条
loopTimer = setInterval(doSend, SEND_INTERVAL); // 循环发送
}
}

// 情况2:屏幕关闭(锁屏)
else if (action == android.content.Intent.ACTION_SCREEN_OFF) {
log("🌚 屏幕已关闭,终止所有提醒");
stopNotificationLoop(); // 立即停止所有任务
}
}
});
  1. JavaAdapter:Auto.js调用安卓原生代码的桥梁
  2. BroadcastReceiver:安卓系统广播接收器,监听系统级事件
  3. 监听两个关键动作:
    • ACTION_USER_PRESENT用户解锁进入桌面
    • ACTION_SCREEN_OFF屏幕关闭(锁屏)
  4. 解锁逻辑:
    • 先清理旧任务 → 判断时间 → 立即发一条 → 开启循环发送
  5. 锁屏逻辑:
    • 直接停止所有任务,省电

注册广播

1
2
3
4
5
6
const myFilter = new android.content.IntentFilter();
myFilter.addAction(android.content.Intent.ACTION_USER_PRESENT);
myFilter.addAction(android.content.Intent.ACTION_SCREEN_OFF);
ctx.registerReceiver(myReceiver, myFilter);

log("🚀 监控脚本已全量启动!");
  • 据完整资料,安卓系统规定:广播必须先注册,才能接收事件
  • IntentFilter:过滤器,只监听我们需要的两个事件
  • registerReceiver:注册接收器,脚本开始监听解锁/锁屏

退出清理(防止内存泄漏)

1
2
3
4
5
// 退出清理
events.on("exit", () => {
stopNotificationLoop(); // 停止定时器
ctx.unregisterReceiver(myReceiver); // 注销广播
});

停止定时器 + 注销广播 →防止占用手机资源


脚本保活

1
2
// 保活机制:空定时器,让脚本持续运行
setInterval(() => {}, 60000);
  • Auto.js脚本默认无任务时会自动退出
  • 这个空定时器每60秒执行一次空任务,让脚本永久后台运行**,不退出

完整执行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
flowchart TD
A[启动脚本] --> B[申请无障碍服务权限]
B --> C[加载全局配置/初始化变量]
C --> D[注册安卓系统广播]
D --> E[持续监听 解锁+锁屏 事件]

E -->|手机解锁触发| F[判断时间是否在规定时间范围内]
F -->|是| G[立即发送钉钉消息]
G --> H[开启60秒循环发送任务]
F -->|否| I[不执行任何操作]

E -->|手机锁屏触发| J[立即停止所有发送任务]
H --> J
I --> E

K[手动退出脚本] --> L[自动清理定时器+注销广播]
D --> M[脚本保活,持续后台运行]

小慨

于是乎,你就可以在夜深人静、伤害自己身心、纸醉金迷之时,欢快地享受体验你自己造出的“魔鬼”的抨击了~