diff --git a/TeiBa/README.md b/TeiBa/README.md new file mode 100644 index 0000000..fa2f611 --- /dev/null +++ b/TeiBa/README.md @@ -0,0 +1,152 @@ +# 百度贴吧自动签到脚本 + +## 项目简介 + +这是一个用于百度贴吧自动签到的青龙面板脚本,支持多账号管理、自动获取关注的贴吧列表并执行签到操作,同时提供签到结果通知功能。脚本通过读取青龙环境变量或配置文件中的Cookie信息,实现无人值守的贴吧签到任务。 + +## 功能特点 + +- 自动获取所有关注的百度贴吧并执行签到 +- 支持多账号同时管理和签到 +- 智能处理已签到、被屏蔽的贴吧 +- 详细的签到结果统计和日志输出 +- 支持青龙面板通知功能,实时推送签到结果 +- 自动处理网络请求异常,包含重试机制 + +## 前提条件 + +1. 已部署青龙面板环境(v2或v2.1+) +2. 拥有有效的百度账号Cookie(包含BDUSS字段) +3. 青龙面板已安装通知模块(可选,用于推送签到结果) + +## 配置步骤 + +### 1. 获取百度贴吧Cookie + +#### 获取BDUSS的方法: + +1. 使用浏览器登录百度账号 +2. 访问百度贴吧任意页面(如 https://tieba.baidu.com) +3. 按F12打开开发者工具,切换到"应用程序"(Application)选项卡 +4. 在左侧菜单中选择"Cookie",找到`BDUSS`字段 +5. 复制`BDUSS`的值,格式类似:`xxx&yyy=zzz; ...` + +#### 完整Cookie获取方法: + +1. 登录百度贴吧后,右键网页空白处选择"检查" +2. 在开发者工具中切换到"网络"(Network)选项卡 +3. 刷新页面,找到任意请求(如`tbs`或`like`) +4. 在请求详情中找到"请求标头"(Request Headers) +5. 复制整个`Cookie`字段的值 + +### 2. 配置青龙环境变量 + +在青龙面板中添加环境变量: + +1. 进入青龙面板 -> 环境变量 -> 新增 +2. 变量名:`TIEBA` +3. 变量值:粘贴获取的Cookie(多账号用换行分隔) + +**多账号示例:** +``` +BDUSS=xxx1; STOKEN=yyy1; ... +BDUSS=xxx2; STOKEN=yyy2; ... +``` + +### 3. 配置文件方式(可选) + +如果不想使用环境变量,也可以在青龙中创建配置文件: + +1. 在青龙脚本目录下创建`config.json`文件 +2. 内容格式: +```json +{ + "TIEBA": [ + {"cookie": "BDUSS=xxx1; STOKEN=yyy1; ..."}, + {"cookie": "BDUSS=xxx2; STOKEN=yyy2; ..."} + ] +} +``` +3. 脚本会优先读取环境变量,若不存在则尝试读取配置文件 + +## 使用方法 + +1. 将脚本文件上传到青龙面板的`scripts`目录 +2. 在青龙面板中手动运行脚本,或设置定时任务 +3. 查看脚本运行日志或接收通知获取签到结果 + +## 定时任务设置 + +在青龙面板中设置定时任务: + +1. 进入青龙面板 -> 定时任务 -> 新增 +2. 任务名称:自定义(如"百度贴吧签到") +3. 命令:`python3 /ql/scripts/tieba_sign.py`(根据实际路径调整) +4. Cron表达式:`0 0 1 * * ?`(每天凌晨1点执行,可自定义) + +**常用Cron示例:** +- 每天0点执行:`0 0 * * * ?` +- 每周一0点执行:`0 0 * * 1 ?` +- 每月1-5号0点执行:`0 0 1-5 * * ?` + +## 通知功能配置 + +脚本支持青龙面板的通知功能,需确保青龙已安装`notify.py`通知模块: + +1. 确保青龙面板中存在`notify.py`文件 +2. 脚本会自动调用`notify.send`方法发送通知 +3. 签到完成后会汇总所有账号结果并发送通知 + +## 脚本日志说明 + +脚本运行时会输出详细日志: + +- 成功签到:显示贴吧名称和签到排名 +- 已签到:提示今日已完成签到 +- 被屏蔽:显示贴吧已被屏蔽 +- 错误信息:包含具体错误码和原因 +- 账号间延迟:处理多账号时的等待时间 +- 汇总统计:显示各账号签到结果和总体统计 + +## 常见问题 + +### 1. 签到失败,提示"登录失败,Cookie异常" + +- 原因:Cookie已过期或缺少必要字段 +- 解决方法:重新获取最新Cookie,确保包含`BDUSS`字段 + +### 2. 无法获取贴吧列表 + +- 原因:网络请求异常或账号权限问题 +- 解决方法:检查网络连接,尝试更换Cookie + +### 3. 提示"今日已签到" + +- 原因:该贴吧当天已完成签到 +- 解决方法:无需处理,脚本会自动跳过已签到的贴吧 + +### 4. 通知发送失败 + +- 原因:青龙未安装通知模块或模块路径错误 +- 解决方法:安装`notify.py`通知模块,或检查模块路径 + +### 5. 多账号处理时速度过慢 + +- 原因:脚本为避免频繁请求设置了延迟 +- 解决方法:如需加快速度,可调整`sign_forums`方法中的延迟参数 + +## 脚本更新 + +1. 如需更新脚本,直接替换青龙面板中的脚本文件 +2. 建议定期更新以适应百度贴吧接口变化 +3. 若签到失败率突然升高,可能是接口变动,需检查脚本是否需要更新 + +## 注意事项 + +1. 请勿频繁使用同一Cookie大量签到,避免账号异常 +2. Cookie有效期通常为7-15天,建议定期更新 +3. 脚本仅供个人使用,请勿用于商业用途 +4. 百度可能随时变更接口,脚本可能需要不定期维护 +5. 多账号处理时,账号间延迟可避免触发风控机制 + +通过以上步骤配置后,脚本将自动完成百度贴吧的签到任务,并通过通知推送结果,实现无人值守的贴吧签到管理。 \ No newline at end of file diff --git a/TeiBa/tb_signin.py b/TeiBa/tb_signin.py new file mode 100644 index 0000000..96b99a7 --- /dev/null +++ b/TeiBa/tb_signin.py @@ -0,0 +1,332 @@ +""" +任务名称 +name: 百度贴吧签到 +定时规则 +cron: 0 0 1 * * ? +""" +import hashlib +import json +import os +import random +import time +from typing import Optional, Union + +import requests + +class Tieba(object): + name = "百度贴吧" + + def __init__(self, check_item: dict): + self.TBS_URL = "http://tieba.baidu.com/dc/common/tbs" + self.LIKE_URL = "http://c.tieba.baidu.com/c/f/forum/like" + self.SIGN_URL = "http://c.tieba.baidu.com/c/c/forum/sign" + self.LOGIN_INFO_URL = "https://zhidao.baidu.com/api/loginInfo" + self.SIGN_KEY = "tiebaclient!!!" + + self.HEADERS = { + "Host": "tieba.baidu.com", + "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36", + "Connection": "keep-alive", + "Accept-Encoding": "gzip, deflate", + "Cache-Control": "no-cache", + } + + self.SIGN_DATA = { + "_client_type": "2", + "_client_version": "9.7.8.0", + "_phone_imei": "000000000000000", + "model": "MI+5", + "net_type": "1", + } + + self.session = requests.Session() + self.session.headers.update(self.HEADERS) + + cookie = check_item.get("cookie") + if not cookie: + raise ValueError("必须提供 BDUSS 或完整 Cookie") + + cookie_dict = { + item.split("=")[0]: item.split("=")[1] + for item in cookie.split("; ") + if "=" in item + } + requests.utils.add_dict_to_cookiejar(self.session.cookies, cookie_dict) + self.bduss = cookie_dict.get("BDUSS", "") + if not self.bduss: + raise ValueError("Cookie 中未找到 BDUSS") + + def request( + self, url: str, method: str = "get", data: Optional[dict] = None, retry: int = 3 + ) -> dict: + for i in range(retry): + try: + if method.lower() == "get": + response = self.session.get(url, timeout=10) + else: + response = self.session.post(url, data=data, timeout=10) + + response.raise_for_status() + if not response.text.strip(): + raise ValueError("空响应内容") + + return response.json() + + except Exception as e: + if i == retry - 1: + raise Exception(f"请求失败: {str(e)}") + + wait_time = 1.5 * (2**i) + random.uniform(0, 1) + time.sleep(wait_time) + + raise Exception(f"请求失败,已达最大重试次数 {retry}") + + def encode_data(self, data: dict) -> dict: + s = "" + for key in sorted(data.keys()): + s += f"{key}={data[key]}" + sign = hashlib.md5((s + self.SIGN_KEY).encode("utf-8")).hexdigest().upper() + data.update({"sign": sign}) + return data + + def get_user_info(self) -> tuple[Union[str, bool], str]: + try: + result = self.request(self.TBS_URL) + if result.get("is_login", 0) == 0: + return False, "登录失败,Cookie 异常" + tbs = result.get("tbs", "") + try: + user_info = self.request(self.LOGIN_INFO_URL) + user_name = user_info.get("userName", "未知用户") + except Exception: + user_name = "未知用户" + return tbs, user_name + except Exception as e: + return False, f"登录验证异常: {e}" + + def get_favorite(self) -> list[dict]: + forums = [] + page_no = 1 + + while True: + data = { + "BDUSS": self.bduss, + "_client_type": "2", + "_client_id": "wappc_1534235498291_488", + "_client_version": "9.7.8.0", + "_phone_imei": "000000000000000", + "from": "1008621y", + "page_no": str(page_no), + "page_size": "200", + "model": "MI+5", + "net_type": "1", + "timestamp": str(int(time.time())), + "vcode_tag": "11", + } + data = self.encode_data(data) + + try: + res = self.request(self.LIKE_URL, "post", data) + + if "forum_list" in res: + for forum_type in ["non-gconforum", "gconforum"]: + if forum_type in res["forum_list"]: + items = res["forum_list"][forum_type] + if isinstance(items, list): + forums.extend(items) + elif isinstance(items, dict): + forums.append(items) + + if res.get("has_more") != "1": + break + + page_no += 1 + time.sleep(random.uniform(1, 2)) + + except Exception as e: + print(f"获取贴吧列表出错: {e}") + break + + print(f"共获取到 {len(forums)} 个关注的贴吧") + return forums + + def sign_forums(self, forums, tbs: str) -> dict: + success_count, error_count, exist_count, shield_count = 0, 0, 0, 0 + total = len(forums) + print(f"开始签到 {total} 个贴吧") + last_request_time = time.time() + for idx, forum in enumerate(forums): + elapsed = time.time() - last_request_time + delay = max(0, 1.0 + random.uniform(0.5, 1.5) - elapsed) + time.sleep(delay) + last_request_time = time.time() + if (idx + 1) % 10 == 0: + extra_delay = random.uniform(5, 10) + print(f"已签到 {idx + 1}/{total} 个贴吧,休息 {extra_delay:.2f} 秒") + time.sleep(extra_delay) + + forum_name = forum.get("name", "") + forum_id = forum.get("id", "") + log_prefix = f"【{forum_name}】吧({idx + 1}/{total})" + + try: + data = self.SIGN_DATA.copy() + data.update( + { + "BDUSS": self.bduss, + "fid": forum_id, + "kw": forum_name, + "tbs": tbs, + "timestamp": str(int(time.time())), + } + ) + data = self.encode_data(data) + result = self.request(self.SIGN_URL, "post", data) + error_code = result.get("error_code", "") + if error_code == "0": + success_count += 1 + if "user_info" in result: + rank = result["user_info"]["user_sign_rank"] + print(f"{log_prefix} 签到成功,第{rank}个签到") + else: + print(f"{log_prefix} 签到成功") + elif error_code == "160002": + exist_count += 1 + print(f"{log_prefix} {result.get('error_msg', '今日已签到')}") + elif error_code == "340006": + shield_count += 1 + print(f"{log_prefix} 贴吧已被屏蔽") + else: + error_count += 1 + print( + f"{log_prefix} 签到失败,错误: {result.get('error_msg', '未知错误')}" + ) + + except Exception as e: + error_count += 1 + print(f"{log_prefix} 签到异常: {str(e)}") + return { + "total": total, + "success": success_count, + "exist": exist_count, + "shield": shield_count, + "error": error_count, + } + + def main(self) -> str: + try: + tbs, user_name = self.get_user_info() + if not tbs: + return f"账号: {user_name}\n登录状态: Cookie可能已过期" + forums = self.get_favorite() + + if forums: + stats = self.sign_forums(forums, tbs) + msg = [ + {"name": "帐号信息", "value": user_name}, + {"name": "贴吧总数", "value": stats["total"]}, + {"name": "签到成功", "value": stats["success"]}, + {"name": "已经签到", "value": stats["exist"]}, + {"name": "被屏蔽的", "value": stats["shield"]}, + {"name": "签到失败", "value": stats["error"]}, + ] + else: + msg = [ + {"name": "帐号信息", "value": user_name}, + {"name": "获取贴吧列表失败,无法完成签到", "value": ""}, + ] + except Exception as e: + msg = [ + {"name": "帐号信息", "value": "未知用户"}, + {"name": "签到失败", "value": str(e)}, + ] + msg = "\n".join([f"{one.get('name')}: {one.get('value')}" for one in msg]) + return msg + + +# 从青龙环境变量读取Cookie并处理多账户 +def get_tieba_cookies(): + cookies = [] + # 尝试从环境变量读取 + tieba_env = os.getenv('TIEBA', '') + + if not tieba_env: + print("未找到环境变量 TIEBA") + # 尝试从配置文件读取 + try: + with open( + os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json"), + encoding="utf-8", + ) as f: + datas = json.loads(f.read()) + config_cookies = datas.get("TIEBA", []) + for item in config_cookies: + if isinstance(item, dict) and 'cookie' in item: + cookies.append(item['cookie']) + if cookies: + print(f"从配置文件读取到 {len(cookies)} 个账号") + return cookies + except Exception as e: + print(f"读取配置文件失败: {e}") + return [] + + # 处理环境变量中的多账户(用换行分隔) + lines = tieba_env.strip().split('\n') + for line in lines: + line = line.strip() + if line: + cookies.append(line) + + print(f"从环境变量读取到 {len(cookies)} 个账号") + return cookies + +# 发送青龙面板通知 +def send_notification(title, content): + try: + # 尝试导入青龙的通知模块 + from notify import send + return send(title, content) + except Exception as e: + print(f"通知发送失败: {e}") + print(f"通知内容: {title}\n{content}") + return "通知发送失败" + +if __name__ == "__main__": + cookies = get_tieba_cookies() + if not cookies: + print("没有找到有效的百度贴吧Cookie") + exit(1) + + results = [] + for idx, cookie in enumerate(cookies, 1): + print(f"\n==== 处理第 {idx}/{len(cookies)} 个账号 ====") + try: + check_item = {"cookie": cookie} + tieba = Tieba(check_item) + result = tieba.main() + results.append(result) + print(f"账号 {idx} 处理完成") + except Exception as e: + error_msg = f"账号 {idx} 处理失败: {str(e)}" + results.append(error_msg) + print(error_msg) + + # 账号间随机延迟 + if idx < len(cookies): + delay = random.uniform(3, 8) + print(f"等待 {delay:.2f} 秒后处理下一个账号...") + time.sleep(delay) + + # 汇总所有结果并发送通知 + final_title = "百度贴吧签到结果汇总" + final_content = "\n\n".join([f"【账号 {i+1}】\n{result}" for i, result in enumerate(results)]) + + # 添加统计信息 + success_count = sum(1 for r in results if "签到成功" in r) + total_count = len(results) + final_content += f"\n\n📊 统计信息:\n成功: {success_count}/{total_count}\n失败: {total_count-success_count}/{total_count}" + + print("\n==== 开始发送通知 ====") + notify_result = send_notification(final_title, final_content) + print(f"通知发送结果: {notify_result}") + print("\n==== 全部处理完成 ====") \ No newline at end of file