在 VPS 上打造全自动、高颜值的 iOS 限免 Telegram 频道
想法💡
目的是不想闲置浪费我的服务器了,然后突然想做一个 Telegram 频道,专门推送 Apple App Store 的限时免费应用。
项目简介
本教程将帮你搭建一个专业级的 iOS 限免推送频道,实现以下功能:
✅ 准实时推送:每 2 分钟检测一次,发现限免立即通知
✅ 智能过滤:自动过滤 "降价 1 元" 等伪限免,只推真正的 0 元应用
✅ 精美排版:带封面图、交互按钮、Emoji 视觉引导
✅ 时间显示:显示推送时间和发现限免的时间
✅ 自动恢复:基于 Systemd 守护进程,崩溃自动重启
核心技术栈:Docker + RSSHub + Python + Systemd + Telegram Bot API
教程开始
第一部分:准备工作
硬件要求
服务器:任意 VPS(本教程以 OVH 为例,1 核 1G 内存即可)
系统:Debian 11/12 或 Ubuntu 20.04/22.04
权限:Root
软件准备
Telegram Bot Token:找
@BotFather申请频道 ID:创建频道,将 Bot 设为管理员,转发消息给
@getidsbot获取
第二部分:部署私有 RSSHub
为避免公共 RSS 源不稳定,我们在本地搭建 RSSHub。
1. 安装 Docker
# 更新系统
sudo apt update && sudo apt upgrade -y
# 使用官方脚本安装 Docker(最稳定的方式)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# 启动 Docker
sudo systemctl start docker
sudo systemctl enable docker重要提示:安装过程中如果弹出 "Daemons using outdated libraries" 提示框,直接按回车即可。
2. 校准服务器时区
这一步非常关键,时间不对会导致 HTTPS 证书验证失败。
# 设置为中国时区
sudo timedatctl set-timezone Asia/Shanghai
# 同步网络时间
sudo apt install ntpdate -y
sudo ntpdate pool.ntp.org
# 验证时间(确保年份、日期、时间都正确)
date3. 启动 RSSHub 容器
docker run -d \
--name rsshub \
-p 1200:1200 \
--restart=always \
diygod/rsshub:latest验证:执行
curl http://127.0.0.1:1200/appstore/xianmian,有 XML 数据返回即成功。
第三部分:部署 Python 推送脚本
1. 创建项目环境
mkdir -p /root/ios_notifier
cd /root/ios_notifier
# 安装 Python 虚拟环境
sudo apt install python3-venv python3-pip -y
# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate
# 安装依赖库
pip install requests feedparser beautifulsoup4 lxml2. 编写核心脚本
创建文件:nano /root/ios_notifier/main.py
完整代码如下(直接复制):
import feedparser
import requests
import sqlite3
import time
import re
import json
import sys
from bs4 import BeautifulSoup
from datetime import datetime
from time import mktime
# ================= 配置区域 (请根据实际情况修改) =================
# 1. 你的 Bot Token
BOT_TOKEN = '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11'
# 2. 你的频道 ID (必须带 -100)
CHANNEL_ID = '-100xxxxxxxxxx'
# 3. 频道用户名 (文案展示用)
CHANNEL_NAME = '@你的频道用户名'
# 4. 频道链接 (按钮跳转用)
CHANNEL_LINK = 'https://t.me/你的频道用户名'
# 5. RSS 源地址
RSS_URL = 'https://appadvice.com/feed'
# 6. 轮询间隔 (秒) - 改成 5 分钟,减少无意义检测
CHECK_INTERVAL = 300
# 7. 数据库路径
DB_PATH = '/root/ios_notifier/history.db'
# =============================================================
def init_db():
"""初始化数据库,用于防止重复推送"""
with sqlite3.connect(DB_PATH) as conn:
conn.execute('''CREATE TABLE IF NOT EXISTS sent_apps
(link TEXT PRIMARY KEY, title TEXT, date_added TEXT)''')
def is_sent(link):
"""检查是否已推送过"""
with sqlite3.connect(DB_PATH) as conn:
cursor = conn.execute("SELECT 1 FROM sent_apps WHERE link=?", (link,))
return cursor.fetchone() is not None
def mark_as_sent(link, title):
"""标记为已推送"""
with sqlite3.connect(DB_PATH) as conn:
conn.execute("INSERT OR IGNORE INTO sent_apps (link, title, date_added) VALUES (?, ?, ?)",
(link, title, datetime.now().isoformat()))
def extract_image(html_content):
"""从 HTML 描述中提取封面图"""
try:
soup = BeautifulSoup(html_content, 'lxml')
img = soup.find('img')
if img and img.get('src'):
return img['src']
except:
return None
return None
def clean_text(html_content):
"""清洗文本,移除 HTML 标签和原价行"""
soup = BeautifulSoup(html_content, 'lxml')
text = soup.get_text(separator='\n').strip()
lines = [line.strip() for line in text.splitlines() if line.strip() and "原价" not in line]
cleaned = '\n'.join(lines)
return cleaned[:250] + "..." if len(cleaned) > 250 else cleaned
def send_telegram(entry):
"""构造并发送精美的 Telegram 消息"""
title = entry.title
link = entry.link
description = entry.description
image_url = extract_image(description)
text_content = clean_text(description)
# === 时间信息处理 ===
now = datetime.now().strftime('%Y-%m-%d %H:%M')
try:
if hasattr(entry, 'published_parsed') and entry.published_parsed:
published_time = datetime.fromtimestamp(mktime(entry.published_parsed))
time_diff = datetime.now() - published_time
hours_ago = int(time_diff.total_seconds() / 3600)
if hours_ago < 1:
time_ago_text = "刚刚发现"
elif hours_ago < 24:
time_ago_text = f"{hours_ago}小时前发现"
else:
days_ago = int(hours_ago / 24)
time_ago_text = f"{days_ago}天前发现"
else:
time_ago_text = "最新发现"
except:
time_ago_text = "最新发现"
# === 精美排版文案 ===
caption = (
f"<b>▎{title}</b>\n\n"
f"{text_content}\n\n"
f"<b>🕐 推送时间:</b> {now}\n"
f"<b>⏱ 限免状态:</b> {time_ago_text}\n"
f"<b>⚠️ 提醒:</b> 限免随时可能结束,尽快下载\n\n"
f"<b>🔓 解锁方法:</b>\n"
f"点击下方按钮,获取即永久 (Lifetime)\n\n"
f"<b>🔔 限时活动,购买前请确认在限免期 🔔</b>\n\n"
f"<b>🉐 可以不用,但是不能没有</b>\n\n"
f"👇 👇 👇\n"
f"<b>🧾 标签:</b> #iOS #限免\n"
f"<b>📢 频道:</b> {CHANNEL_NAME}\n"
f"<b>☝️ 消息怕错过?请收藏频道并开启推送! ☝️</b>"
)
# === 交互按钮 ===
keyboard = {
"inline_keyboard": [
[{"text": "🍎 前往 App Store 下载", "url": link}],
[{"text": "📨 转发给朋友一起领", "url": f"https://t.me/share/url?url={link}&text=快来!{title} 限免了!"}]
]
}
try:
payload = {
'chat_id': CHANNEL_ID,
'caption': caption,
'parse_mode': 'HTML',
'reply_markup': json.dumps(keyboard)
}
if image_url:
url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendPhoto"
payload['photo'] = image_url
else:
url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
payload['text'] = payload.pop('caption')
requests.post(url, json=payload)
return True
except Exception as e:
print(f"[错误] 发送失败: {e}")
return False
def check_updates():
"""单次检查逻辑"""
print(f"[{datetime.now().strftime('%H:%M:%S')}] 正在检查 App Store 更新...")
try:
feed = feedparser.parse(RSS_URL)
if not feed.entries:
print("RSS 源无数据")
return
for entry in feed.entries[:15]:
# === 核心过滤逻辑(增强版)===
# 1. 标题含"降价"直接跳过
if "降价" in entry.title:
mark_as_sent(entry.link, entry.title)
continue
# 2. 过滤文章类内容(增强规则)
title_lower = entry.title.lower()
article_keywords = [
"best", "top", "great", "apps for", "apps to",
"how to", "tips", "guide", "review", "roundup",
"collection", "games of", "essential", "new year",
"help you", "keep", "continue", "jump into"
]
if any(keyword in title_lower for keyword in article_keywords):
# 静默跳过文章,不输出日志(减少刷屏)
mark_as_sent(entry.link, entry.title)
continue
# 3. 多维度判断是否真的免费
description_str = entry.description if hasattr(entry, 'description') else ""
is_free = (
"¥0.00" in description_str or
"现价: ¥0" in description_str or
"现价:¥0" in description_str or
"免费" in entry.title or
"限免" in entry.title or
("free" in title_lower and "app" in title_lower) or
"$0.00" in description_str or
"price: $0" in description_str.lower() or
"now free" in description_str.lower()
)
if not is_free:
# 非免费的也静默跳过
continue
# ===================
if not is_sent(entry.link):
if send_telegram(entry):
print(f"✅ 推送成功: {entry.title}")
mark_as_sent(entry.link, entry.title)
time.sleep(3)
except Exception as e:
print(f"❌ 异常: {e}")
if __name__ == "__main__":
init_db()
print("--- 🚀 iOS 限免监控机器人已启动 ---")
print(f"数据源: {RSS_URL}")
print(f"检测频率: 每 {CHECK_INTERVAL} 秒 ({CHECK_INTERVAL//60} 分钟)")
# === 守护进程主循环 ===
while True:
check_updates()
sys.stdout.flush()
time.sleep(CHECK_INTERVAL)保存文件:Ctrl+O → 回车 → Ctrl+X
第四部分:设置 Systemd 守护进程
将脚本注册为系统服务,实现开机自启和崩溃自愈。
1. 创建服务文件
nano /etc/systemd/system/ios_notifier.service2. 粘贴以下配置
[Unit]
Description=iOS App Free Notifier Service
After=network.target docker.service
[Service]
User=root
WorkingDirectory=/root/ios_notifier
ExecStart=/root/ios_notifier/venv/bin/python3 /root/ios_notifier/main.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target保存:Ctrl+O → 回车 → Ctrl+X
3. 启动服务
# 重载配置
sudo systemctl daemon-reload
# 开机自启
sudo systemctl enable ios_notifier
# 立即启动
sudo systemctl start ios_notifier第五部分:验证与监控
实时查看日志
journalctl -u ios_notifier -f预期输出:
[12:00:00] 正在检查 App Store 更新...
✅ 推送成功: 某某 App
[12:02:00] 正在检查 App Store 更新...查看服务状态
sudo systemctl status ios_notifier看到绿色的 ● active (running) 即为成功。
强制测试推送
如果想立刻看到推送效果(而不是等新限免),可以清空数据库强制重发:
# 停止服务
sudo systemctl stop ios_notifier
# 删除历史记录
rm /root/ios_notifier/history.db
# 重启服务
sudo systemctl start ios_notifier
# 查看日志
journalctl -u ios_notifier -f此时你的 Telegram 频道会立刻收到 5-10 条测试消息。
第六部分:故障排查指南
问题一:时间显示错误或证书报错
原因:服务器时间不对(穿越到了未来或过去)。
解决:
sudo ntpdate pool.ntp.org
date # 确认时间正确
sudo systemctl restart ios_notifier问题二:日志显示但频道没收到消息
排查:
检查 Bot Token 是否正确填写
检查 Channel ID 是否带
-100前缀确认 Bot 在频道中是管理员身份
用
curl测试 Token:
curl https://api.telegram.org/bot你的TOKEN/getMe问题三:推送全是"降价"商品
解决:代码中已加入过滤逻辑,检查代码第 104-110 行的过滤条件。
成果展示
部署完成后,你的频道将实现:
✅ 每 2 分钟自动检测,有限免立即推送
✅ 显示推送时间和发现时间,让用户了解限免新鲜度
✅ 精美排版:封面图 + Emoji + 交互按钮
✅ 智能过滤:只推真正的 0 元限免
✅ 7x24 小时运行:Systemd 保证崩溃自动重启
这套方案部署在 VPS 上,内存占用不到 200MB,是利用闲置服务器资源的最佳实践!
附录:常用管理命令
# 查看日志
journalctl -u ios_notifier -f
# 停止服务
sudo systemctl stop ios_notifier
# 启动服务
sudo systemctl start ios_notifier
# 重启服务
sudo systemctl restart ios_notifier
# 查看状态
sudo systemctl status ios_notifier
# 修改检测频率
nano /root/ios_notifier/main.py
# 修改 CHECK_INTERVAL 的值,然后重启服务📝 修改 main.py 的完整步骤
第一步:停止服务(防止修改时冲突)
sudo systemctl stop ios_notifier为什么要先停?因为文件正在被 Python 进程读取,虽然强行改也行,但停掉更安全。
第二步:编辑文件
使用 nano 编辑器(最适合新手):
nano /root/ios_notifier/main.pynano 编辑器基础操作:
第三步:常见修改项及位置
1. 修改 Bot Token 和频道 ID(第 11-21 行)
按 Ctrl + W,搜索 BOT_TOKEN,会跳到配置区:
BOT_TOKEN = '这里改成你的真实Token'
CHANNEL_ID = '-100xxxxxxxxxx' # 改成你的频道ID
CHANNEL_NAME = '@你的频道用户名' # 改成真实用户名
CHANNEL_LINK = 'https://t.me/你的频道用户名' # 改成真实链接2. 修改检测频率(第 26 行)
CHECK_INTERVAL = 120 # 改成 60 = 1分钟,改成 300 = 5分钟3. 修改推送文案(第 95-112 行)
搜索 caption =,可以修改 Emoji、标题、文案等。
第四步:保存并退出
按
Ctrl + O(字母 O,不是数字 0)屏幕底部会提示
File Name to Write: /root/ios_notifier/main.py直接按回车
按
Ctrl + X退出编辑器
第五步:重启服务(让修改生效)
sudo systemctl start ios_notifier或者用重启命令(效果一样):
sudo systemctl restart ios_notifier第六步:验证修改是否生效
立刻查看日志,确认没有报错:
journalctl -u ios_notifier -f正常输出示例:
[12:30:00] 正在检查 App Store 更新...
✅ 推送成功: 某某 App如果报错(比如 SyntaxError 或 ValueError):
说明你改错了某个地方(比如引号没配对、缩进搞乱了)。
快速修复:
# 再次停止服务
sudo systemctl stop ios_notifier
# 重新编辑
nano /root/ios_notifier/main.py
# 修正错误后再重启
sudo systemctl start ios_notifier🎯 快速操作速查表
💡 进阶技巧:在线备份
每次修改前,建议先备份一下:
# 备份原文件(带日期后缀)
cp /root/ios_notifier/main.py /root/ios_notifier/main.py.backup.$(date +%Y%m%d)
# 如果改坏了,可以恢复
# cp /root/ios_notifier/main.py.backup.20250111 /root/ios_notifier/main.py完美🎉




