import telebot import requests from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton BOT_TOKEN = "7741492900:AAG6M_faLy5JG7g0t2gBFj-sXtBIaBXQZHk" ALLOWED_USERS = [165067365] CF_API_TOKEN = "VfeGnC3v5McJ3sJ6bsjjhG0IhvOgY-5-v6lpYgH3" bot = telebot.TeleBot(BOT_TOKEN) ZONE_CACHE = {} USER_STATE = {} def get_zones(): headers = {"Authorization": f"Bearer {CF_API_TOKEN}"} r = requests.get("https://api.cloudflare.com/client/v4/zones", headers=headers).json() return r.get("result", []) def get_zone_id(domain): root = ".".join(domain.split(".")[-2:]) if root in ZONE_CACHE: return ZONE_CACHE[root] for z in get_zones(): if z["name"] == root: ZONE_CACHE[root] = z["id"] return z["id"] return None def is_authorized(uid): return uid in ALLOWED_USERS def main_menu(): markup = InlineKeyboardMarkup(row_width=2) markup.add( InlineKeyboardButton('➕ 添加记录', callback_data='add'), InlineKeyboardButton('🗑 删除记录', callback_data='del'), InlineKeyboardButton('☁️ 小黄云', callback_data='proxy'), InlineKeyboardButton('📋 列出记录', callback_data='list') ) return markup def domain_menu(action): zones = get_zones() markup = InlineKeyboardMarkup(row_width=2) for z in zones[:10]: markup.add(InlineKeyboardButton(z['name'], callback_data=f"{action}:{z['name']}")) markup.add(InlineKeyboardButton('🔙 返回', callback_data='back')) return markup @bot.message_handler(commands=['help', 'start']) def start_cmd(message): if not is_authorized(message.from_user.id): return bot.send_message(message.chat.id, '🌐 CF DNS 管理', reply_markup=main_menu()) @bot.callback_query_handler(func=lambda c: True) def callback_handler(call): uid = call.from_user.id if not is_authorized(uid): return data = call.data if data == 'back': bot.edit_message_text('🌐 CF DNS 管理', call.message.chat.id, call.message.message_id, reply_markup=main_menu()) return if data in ['add', 'del', 'proxy', 'list']: bot.edit_message_text(f'选择域名:', call.message.chat.id, call.message.message_id, reply_markup=domain_menu(data)) return if data.startswith('list:'): domain = data.split(':')[1] zone_id = get_zone_id(domain) headers = {"Authorization": f"Bearer {CF_API_TOKEN}"} r = requests.get(f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records", headers=headers).json() records = r.get("result", []) if not records: txt = f'📋 {domain}\n\n无记录' else: lines = [f"{rec['name']} -> {rec['content']} ({'☁️' if rec['proxied'] else '⚪'})" for rec in records[:15]] txt = f'📋 {domain}\n\n' + '\n'.join(lines) markup = InlineKeyboardMarkup() markup.add(InlineKeyboardButton('🔙 返回', callback_data='back')) bot.edit_message_text(txt, call.message.chat.id, call.message.message_id, reply_markup=markup) return if data.startswith('add:'): domain = data.split(':')[1] USER_STATE[uid] = {'action': 'add', 'domain': domain} markup = InlineKeyboardMarkup() markup.add(InlineKeyboardButton('🔙 取消', callback_data='back')) bot.edit_message_text(f'➕ 添加记录到 {domain}\n\n请输入: 子域名 IP\n例如: www 1.2.3.4', call.message.chat.id, call.message.message_id, reply_markup=markup) return if data.startswith('del:'): domain = data.split(':')[1] zone_id = get_zone_id(domain) headers = {"Authorization": f"Bearer {CF_API_TOKEN}"} r = requests.get(f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records", headers=headers).json() records = r.get("result", []) markup = InlineKeyboardMarkup(row_width=1) for rec in records[:10]: short = rec['name'].replace(f'.{domain}', '') if rec['name'] != domain else '@' markup.add(InlineKeyboardButton(f"🗑 {short} -> {rec['content']}", callback_data=f"doit:{rec['id']}:{domain}")) markup.add(InlineKeyboardButton('🔙 返回', callback_data='back')) bot.edit_message_text(f'选择要删除的记录:', call.message.chat.id, call.message.message_id, reply_markup=markup) return if data.startswith('doit:'): parts = data.split(':') rec_id, domain = parts[1], parts[2] zone_id = get_zone_id(domain) headers = {"Authorization": f"Bearer {CF_API_TOKEN}"} requests.delete(f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{rec_id}", headers=headers) bot.answer_callback_query(call.id, '✅ 已删除') bot.edit_message_text('🌐 CF DNS 管理', call.message.chat.id, call.message.message_id, reply_markup=main_menu()) return if data.startswith('proxy:'): domain = data.split(':')[1] zone_id = get_zone_id(domain) headers = {"Authorization": f"Bearer {CF_API_TOKEN}"} r = requests.get(f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records", headers=headers).json() records = r.get("result", []) markup = InlineKeyboardMarkup(row_width=1) for rec in records[:10]: short = rec['name'].replace(f'.{domain}', '') if rec['name'] != domain else '@' icon = '☁️' if rec['proxied'] else '⚪' markup.add(InlineKeyboardButton(f"{icon} {short} -> {rec['content']}", callback_data=f"toggle:{rec['id']}:{domain}")) markup.add(InlineKeyboardButton('🔙 返回', callback_data='back')) bot.edit_message_text(f'点击切换小黄云:', call.message.chat.id, call.message.message_id, reply_markup=markup) return if data.startswith('toggle:'): parts = data.split(':') rec_id, domain = parts[1], parts[2] zone_id = get_zone_id(domain) headers = {"Authorization": f"Bearer {CF_API_TOKEN}", "Content-Type": "application/json"} r = requests.get(f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{rec_id}", headers=headers).json() rec = r.get("result", {}) new_state = not rec.get('proxied', False) requests.patch(f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{rec_id}", headers=headers, json={"proxied": new_state}) bot.answer_callback_query(call.id, f"✅ 小黄云已{'开启' if new_state else '关闭'}") r = requests.get(f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records", headers=headers).json() records = r.get("result", []) markup = InlineKeyboardMarkup(row_width=1) for rec in records[:10]: short = rec['name'].replace(f'.{domain}', '') if rec['name'] != domain else '@' icon = '☁️' if rec['proxied'] else '⚪' markup.add(InlineKeyboardButton(f"{icon} {short} -> {rec['content']}", callback_data=f"toggle:{rec['id']}:{domain}")) markup.add(InlineKeyboardButton('🔙 返回', callback_data='back')) bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=markup) return @bot.message_handler(func=lambda m: m.from_user.id in USER_STATE) def handle_add(message): uid = message.from_user.id if not is_authorized(uid): return state = USER_STATE.get(uid) if not state or state['action'] != 'add': return parts = message.text.strip().split() if len(parts) < 2: bot.reply_to(message, '格式错误,请输入: 子域名 IP') return sub, ip = parts[0], parts[1] domain = state['domain'] del USER_STATE[uid] zone_id = get_zone_id(domain) if not zone_id: bot.reply_to(message, f'找不到域名 {domain}') return name = f"{sub}.{domain}" if sub != '@' else domain headers = {"Authorization": f"Bearer {CF_API_TOKEN}", "Content-Type": "application/json"} data = {"type": "A", "name": name, "content": ip, "ttl": 1, "proxied": False} r = requests.post(f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records", headers=headers, json=data).json() if r.get("success"): bot.reply_to(message, f'✅ 添加成功: {name} -> {ip}', reply_markup=main_menu()) else: bot.reply_to(message, f'❌ 失败: {r.get("errors", [])}') if __name__ == '__main__': print('CF Bot 启动...') bot.infinity_polling()