# Source: https://github.com/LinYuanovo/AutoTaskScripts/blob/main/web/%E5%98%A4%E5%98%A4%E6%80%AA%E4%B9%8B%E5%AE%B6.py
# Raw: https://raw.githubusercontent.com/LinYuanovo/AutoTaskScripts/main/web/%E5%98%A4%E5%98%A4%E6%80%AA%E4%B9%8B%E5%AE%B6.py
# Repo: LinYuanovo/AutoTaskScripts
# Path: web/嘤嘤怪之家.py
# UploadedAt: 2025-08-27T15:31:08+08:00
# SHA256: 9e2e2ee0614b254534faf4e169de569e3d9bbc058a01ef97defe9b330fc9dd90
# Category: web版/账密
# Evidence: web/H5关键词 + username/password/login

"""
作者: 临渊
日期: 2025/6/8
name: 嘤嘤怪之家
入口: 网站 (https://yyg.app/)
功能: 登录、签到、评论（每日上限30积分）
变量: yyg='账号&密码'  多个账号用换行分割
    DDDD_OCR_URL (dddd_ocr地址)
定时: 一天两次
cron: 10 9,10 * * *
------------更新日志------------
2025/6/8    V1.0    初始化，完成签到功能
2025/6/10   V1.1    添加评论功能
2025/6/11   V1.2    优化代码结构，使用session管理cookie，添加查询积分功能（不一定成功）
2025/7/23   V1.3    更新域名
2025/7/28   V1.4    修改头部注释，以便拉库
2025/8/27   V1.5    增加尝试获取最新域名
"""

import requests
import os
import re
import urllib.parse
import time
import random
import logging
import traceback
from datetime import datetime
from bs4 import BeautifulSoup

DDDD_OCR_URL = os.getenv("DDDD_OCR_URL") or "" # dddd_ocr地址
DEFAULT_GUIDE_URL = "https://yyg.autos/" # 默认发布地址
DEFAULT_HOST = "yyg.app" # 默认域名

class AutoTask:
    def __init__(self, site_name):
        """
        初始化自动任务类
        :param site_name: 站点名称，用于日志显示
        """
        self.site_name = site_name
        self.setup_logging()

    def setup_logging(self):
        """
        配置日志系统
        """
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s\t- %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S',
            handlers=[
                # logging.FileHandler(f'{self.site_name}_{datetime.now().strftime("%Y%m%d")}.log', encoding='utf-8'),  # 保存日志
                logging.StreamHandler()
            ]
        )

    def check_cookie(self):
        """
        检查cookie
        :return: 用户名和密码
        """
        try:
            # 从环境变量获取cookie
            cookie = os.getenv(f"yyg")
            if not cookie:
                logging.error(f"[检查cookie]没有找到环境变量yyg")
                return
            # 多个账号用换行分割
            cookies = cookie.split('\n')
            for cookie in cookies:
                # 解析cookie字符串，提取用户名和密码
                username, password = cookie.split('&')
                yield username, password
        except Exception as e:
            logging.error(f"[检查cookie]发生错误: {str(e)}\n{traceback.format_exc()}")
            raise

    def get_host(self):
        """
        获取最新域名
        :return: 域名
        """
        try:
            url = DEFAULT_GUIDE_URL
            response = requests.get(url)
            response.encoding = 'utf-8'
            soup = BeautifulSoup(response.text, 'html.parser')
            links = soup.find_all('a')
            for link in links:
                link_text = link.get_text(strip=True)
                if re.search(r'访问最新域名', link_text):
                    href_value = link.get('href')
                    if href_value.startswith("http"):
                        host = href_value.split("//")[1]
                        logging.info(f"[获取最新域名]最新域名: {host}")
                        return host
                    else:
                        return href_value
            return DEFAULT_HOST
        except Exception as e:
            logging.error(f"[获取最新域名]发生错误: {str(e)}\n{traceback.format_exc()}")
            return DEFAULT_HOST

    def get_captcha_img(self, host, session, type):
        """
        获取验证码图片
        :param host: 域名
        :param session: 会话对象
        :param type: 验证码类型
        :return: 验证码base64
        """
        max_retries = 3  # 最大重试次数
        retry_count = 0

        while retry_count < max_retries:
            try:
                url = f"https://{host}/wp-content/themes/zibll/action/captcha.php?type=image&id={type}"
                headers = {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
                }
                response = session.get(url, headers=headers).json()
                if 'img' in response:
                    return response['img']
                else:
                    retry_count += 1
                    logging.warning(f"[获取验证码]第{retry_count}次获取失败，正在重试...")
                    continue
            except Exception as e:
                retry_count += 1
                if retry_count >= max_retries:
                    logging.error(f"[获取验证码]重试次数已达上限")
                    return None
                continue

        return None

    def get_captcha_text(self, img):
        """
        获取验证码文字
        :param img: 验证码图片base64
        :return: 验证码
        """
        try:
            url = DDDD_OCR_URL
            payload = {
                'image': img
            }
            response = requests.post(url, data=payload).json()
            if response['code'] == 200:
                return response['data']
            else:
                logging.error(f"[获取验证码]发生错误: {response['message']}")
                return None
        except Exception as e:
            logging.error(f"[获取验证码]发生错误: {str(e)}\n{traceback.format_exc()}")
            return None

    def login_in(self, host, username, password, text, session):
        """
        登录
        :param host: 域名
        :param username: 用户名
        :param password: 密码
        :param text: 验证码
        :param session: 会话对象
        :return: 是否成功
        """
        max_retries = 3  # 最大重试次数
        retry_count = 0

        while retry_count < max_retries:
            try:
                url = f"https://{host}/wp-admin/admin-ajax.php"
                headers = {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
                }
                payload = f"username={username}&password={password}&canvas_yz={text}&remember=forever&action=user_signin"
                response = session.post(url, headers=headers, data=payload)
                response_json = response.json()

                if response_json['error'] == 0:
                    logging.info(f"[登录]成功")
                    return True
                else:
                    logging.error(f"[登录]失败: {response_json['msg']}")
                    if "重新获取" in response_json['msg'] or "请输入图形验证码" in response_json['msg']:
                        retry_count += 1
                        if retry_count >= max_retries:
                            logging.error(f"[登录]验证码重试次数已达上限")
                            return False

                        img = self.get_captcha_img(host, session, "img_yz_signin")
                        while not img:
                            img = self.get_captcha_img(host, session, "img_yz_signin")
                        text = self.get_captcha_text(img)
                        if not text:
                            logging.error(f"[登录]获取验证码文字失败")
                            return False
                        continue
                    return False

            except requests.RequestException as e:
                logging.error(f"[登录]发生网络错误: {str(e)}\n{traceback.format_exc()}")
                return False
            except Exception as e:
                logging.error(f"[登录]发生未知错误: {str(e)}\n{traceback.format_exc()}")
                return False

        return False

    def sign_in(self, host, session):
        """
        执行签到
        :param host: 域名
        :param session: 会话对象
        :return: 是否签到成功
        """
        try:
            url = f"https://{host}/wp-admin/admin-ajax.php"
            headers = {
                'Host': host,
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
            }
            payload = "action=user_checkin"
            response = session.post(url, headers=headers, data=payload)
            response_json = response.json()
            # 处理响应
            logging.info(f"[签到]{response_json['msg']}")
            return response_json['msg']
        except requests.RequestException as e:
            logging.error(f"[签到]发生网络错误: {str(e)}\n{traceback.format_exc()}")
            return False
        except Exception as e:
            logging.error(f"[签到]发生未知错误: {str(e)}\n{traceback.format_exc()}")
            return False

    def get_post_id(self, host, session, retry_count=0):
        """
        获取帖子id
        :param host: 域名
        :param session: 会话对象
        :param retry_count: 重试次数
        :return: 帖子id列表
        """
        try:
            url = f"https://{host}/category/pcgame?orderby=modified" # 最新排列
            headers = {
                'Host': host,
                'Referer': f"https://{host}/category/pcgame",
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
            }
            response = session.get(url, headers=headers)
            soup = BeautifulSoup(response.text, 'html.parser')

            post_ids = []
            for i in range(3,7):
                # 找到所有posts元素
                links = soup.select(f'body > main > div > div > div:nth-child(2) > posts:nth-child({i}) > div.item-body.flex.xx.flex1.jsb > h2 > a')
                if links:  # 如果找到了链接
                    link = links[0]  # 获取第一个链接
                    # 提取帖子ID
                    if link.get('href'):
                        # 从URL中提取ID (例如从 https://yyg.boats/14819.html 提取 14819)
                        post_id = link['href'].split('/')[-1].replace('.html', '')
                        post_ids.append(post_id)

            # 如果没有获取到帖子ID且重试次数小于3次，则重试
            if not post_ids and retry_count < 3:
                logging.info(f"[获取帖子ID]未获取到帖子ID，第{retry_count + 1}次重试")
                time.sleep(random.randint(5, 10))
                return self.get_post_id(host, session, retry_count + 1)
            elif not post_ids and retry_count >= 3:
                post_ids = ['14818', '14817', '14816', '14815']
                logging.warning(f"[获取帖子ID]三次未获取到帖子ID，使用默认帖子ID: {post_ids}")

            logging.info(f"[获取帖子ID]成功获取到 {len(post_ids)} 个帖子ID: {post_ids}")
            return post_ids

        except requests.RequestException as e:
            logging.error(f"[获取帖子ID]发生网络错误: {str(e)}\n{traceback.format_exc()}")
            return []
        except Exception as e:
            logging.error(f"[获取帖子ID]发生未知错误: {str(e)}\n{traceback.format_exc()}")
            return []

    def submit_comment(self, host, session, captcha, post_id, retry_count=0):
        """
        提交评论
        :param host: 域名
        :param session: 会话对象
        :param captcha: 验证码
        :param post_id: 帖子ID
        :param retry_count: 重试次数
        """
        try:
            url = f"https://{host}/wp-admin/admin-ajax.php"
            headers = {
                'Host': host,
                'Referer': f"https://{host}/{post_id}.html",
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
            }
            payload = f"comment=%E6%84%9F%E8%B0%A2%E5%88%86%E4%BA%AB&canvas_yz={captcha}&comment_post_ID={post_id}&comment_parent=0&action=submit_comment"
            response = session.post(url, headers=headers, data=payload)
            response_json = response.json()
            logging.info(f"[评论帖子{post_id}]{response_json['msg']}")

            if "图形验证码错误" in response_json['msg']:
                if retry_count >= 3:
                    logging.warning(f"[评论{post_id}]验证码错误重试次数已达上限，跳过此评论")
                    return False

                logging.info(f"[评论帖子{post_id}]验证码错误，第{retry_count + 1}次重试")
                submit_comment_captcha = self.get_captcha_img(host, session, "submit_comment")
                while not submit_comment_captcha:
                    submit_comment_captcha = self.get_captcha_img(host, session, "submit_comment")
                submit_comment_text = self.get_captcha_text(submit_comment_captcha)
                if not submit_comment_text:
                    logging.error(f"[评论帖子{post_id}]获取验证码文字失败")
                    return False
                return self.submit_comment(host, session, submit_comment_text, post_id, retry_count + 1)

            return response_json['msg']
        except Exception as e:
            logging.error(f"[评论帖子{post_id}]发生未知错误: {str(e)}\n{traceback.format_exc()}")
            return False

    def get_user_balance(self, host, session):
        """
        获取用户积分
        :param host: 域名
        :param session: 会话对象
        :return: 用户积分
        """
        try:
            url = f"https://{host}/user/balance"
            headers = {
                'Host': host,
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
            }
            response = session.get(url, headers=headers)
            # 正则匹配积分记录里第一个，即 积分: xxx
            balance = re.search(r'积分: (\d+)', response.text)
            if balance:
                return balance.group(1)
            else:
                logging.error(f"[获取用户积分]未找到积分记录")
                return 0
        except Exception as e:
            logging.error(f"[获取用户积分]发生未知错误: {str(e)}\n{traceback.format_exc()}")
            return 0

    def do_task(self, host, session):
        """
        执行任务
        :param host: 域名
        :param session: 会话对象
        """
        # 执行签到
        self.sign_in(host, session)
        # 获取帖子id
        post_ids = self.get_post_id(host, session)
        # 获取评论验证码图片(四次)
        for post_id in post_ids:
            captcha = self.get_captcha_img(host, session, "submit_comment")
            while not captcha:
                captcha = self.get_captcha_img(host, session, "submit_comment")
            text = self.get_captcha_text(captcha)
            if not text:
                logging.error(f"[评论{post_id}]获取验证码文字失败")
                continue
            # 提交评论
            self.submit_comment(host, session, text, post_id)
            time.sleep(random.randint(16, 30))
        # 获取用户积分
        balance = self.get_user_balance(host, session)
        logging.info(f"[账号]当前积分: {balance}")

    def run(self):
        """
        运行任务
        """
        try:
            logging.info(f"【{self.site_name}】开始执行任务")
            host = self.get_host()

            for index, (username, password) in enumerate(self.check_cookie(), 1):
                logging.info("")
                logging.info(f"------【账号{index}】开始执行任务------")

                # 创建会话
                session = requests.Session()

                # 获取登录验证码图片
                login_in_img = self.get_captcha_img(host, session, "img_yz_signin")
                while not login_in_img:
                    login_in_img = self.get_captcha_img(host, session, "img_yz_signin")
                # 获取登录验证码文字
                login_in_text = self.get_captcha_text(login_in_img)
                if not login_in_text:
                    logging.error(f"[{self.site_name}]获取登录验证码文字失败")
                    continue

                # 登录
                if self.login_in(host, username, password, login_in_text, session):
                    # 执行任务
                    self.do_task(host, session)

                logging.info(f"------【账号{index}】执行任务完成------")
                logging.info("")

        except Exception as e:
            logging.error(f"【{self.site_name}】执行过程中发生错误: {str(e)}\n{traceback.format_exc()}")


if __name__ == "__main__":
    auto_task = AutoTask("嘤嘤怪之家")
    auto_task.run()
