# Source: https://github.com/back-101/zyqinglong/blob/main/tclx.py
# Raw: https://raw.githubusercontent.com/back-101/zyqinglong/main/tclx.py
# Repo: back-101/zyqinglong
# Path: tclx.py
# UploadedAt: 2025-11-29T06:56:58Z
# SHA256: b49c33dc10adf577eb8b1809d98ceeddc3a58a11676c70eb65a040f3d76c9035
# Category: web版/抓包
# Evidence: web/H5关键词 + cookie/token/header

# -*- coding=UTF-8 -*-
# @Project          QL_TimingScript
# @fileName         tclx.py
# @author           Echo
# @EditTime         2025/3/14
# cron: 0 0 9 * * *
# const $ = new Env('同程旅游');
"""
开启抓包，进入app 进入‘领福利’界面，点击签到，查看https://app.17u.cn/welfarecenter/index/signIndex请求头
提取变量： apptoken、device
变量格式： phone#apptoken#device，多个账号用@隔开
"""
import asyncio
import time
from datetime import datetime

import httpx

from fn_print import fn_print
from get_env import get_env
from sendNotify import send_notification_message_collection

tc_cookies = get_env("tc_cookie", "@")


class Tclx:
    def __init__(self, cookie):
        self.client = httpx.AsyncClient(base_url="https://app.17u.cn/welfarecenter",
                                        verify=False,
                                        timeout=60)
        self.phone = cookie.split("#")[0]
        self.apptoken = cookie.split("#")[1]
        self.device = cookie.split("#")[2]
        self.headers = {
            'accept': 'application/json, text/plain, */*',
            'phone': self.phone,
            'channel': '1',
            'apptoken': self.apptoken,
            'sec-fetch-site': 'same-site',
            'accept-language': 'zh-CN,zh-Hans;q=0.9',
            'accept-encoding': 'gzip, deflate, br',
            'sec-fetch-mode': 'cors',
            'origin': 'https://m.17u.cn',
            'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 TcTravel/11.0.0 tctype/wk',
            'referer': 'https://m.17u.cn/',
            # 'content-length': str(len(payload_str.encode('utf-8'))),
            'device': self.device,
            'sec-fetch-dest': 'empty'
        }

    @staticmethod
    async def get_today_date():
        return datetime.now().strftime('%Y-%m-%d')

    async def sign_in(self):
        try:
            response = await self.client.post(
                url="/index/signIndex",
                headers=self.headers,
                json={}
            )
            data = response.json()
            if data['code'] != 2200:
                fn_print(f"用户【{self.phone}】 - token失效了，请更新")
                return None
            else:
                today_sign = data['data']['todaySign']
                mileage = data['data']['mileageBalance']['mileage']
                fn_print(f"用户【{self.phone}】 - 今日{'已' if today_sign else '未'}签到，当前剩余里程{mileage}！")
                return today_sign
        except Exception as e:
            fn_print(f"用户【{self.phone}】 - 签到请求异常！{e}")
            fn_print(response.text)
            return None

    async def do_sign_in(self):
        today_date = await self.get_today_date()
        try:
            response = await self.client.post(
                url="/index/sign",
                headers=self.headers,
                json={"type": 1, "day": today_date}
            )
            data = response.json()
            if data['code'] != 2200:
                fn_print(f"用户【{self.phone}】 - 签到失败了，尝试获取任务列表")
                return False
            else:
                fn_print(f"用户【{self.phone}】 - 签到成功！开始获取任务列表")
                return True
        except Exception as e:
            fn_print(f"用户【{self.phone}】 - 执行签到请求异常！{e}")
            fn_print(response.text)
            return False

    async def get_task_list(self):
        try:
            response = await self.client.post(
                url="/task/taskList?version=11.0.7",
                headers=self.headers,
                json={}
            )
            data = response.json()
            if data['code'] != 2200:
                fn_print(f"用户【{self.phone}】 - 获取任务列表失败了")
                return None
            else:
                tasks = []
                for task in data['data']:
                    if task['state'] == 1 and task['browserTime'] != 0:
                        tasks.append(
                            {
                                'taskCode': task['taskCode'],
                                'title': task['title'],
                                'browserTime': task['browserTime']
                            }
                        )
                return tasks
        except Exception as e:
            fn_print(f"用户【{self.phone}】 - 获取任务列表请求异常！{e}")
            fn_print(response.text)
            return None

    async def perform_tasks(self, task_code):
        try:
            response = await self.client.post(
                url="/task/start",
                headers=self.headers,
                json={"taskCode": task_code}
            )
            data = response.json()
            if data['code'] != 2200:
                fn_print(f"用户【{self.phone}】 - 执行任务【{task_code}】失败了，跳过当前任务")
                return None
            else:
                task_id = data['data']
                return task_id
        except Exception as e:
            fn_print(f"用户【{self.phone}】 - 执行任务【{task_code}】请求异常！{e}")
            fn_print(response.text)
            return None

    async def finsh_task(self, task_id):
        max_retry = 3  # 最大重试次数
        retry_delay = 2  # 重试间隔时间（秒）
        for attempt in range(max_retry):
            try:
                response = await self.client.post(
                    url="/task/finish",
                    headers=self.headers,
                    json={"id": task_id}
                )
                data = response.json()
                if data['code'] == 2200:
                    fn_print(f"用户【{self.phone}】 - 完成任务【{task_id}】成功！开始领取奖励")
                    return True
                if attempt < max_retry - 1:
                    fn_print(f"用户【{self.phone}】 - 完成任务【{task_id}】失败了，尝试重新提交（第{attempt + 1}次重试。。）")
                    await asyncio.sleep(retry_delay * (attempt + 1))
                    continue
                fn_print(f"用户【{self.phone}】 - 完成任务【{task_id}】最终失败，跳过当前任务")
                return False
            except Exception as e:
                error_msg = f"用户【{self.phone}】 - 完成任务【{task_id}】请求异常！{e}"
                if 'response' in locals():
                    error_msg += f"\n{response.text}"
                fn_print(error_msg)
                if attempt == max_retry - 1:
                    return False
                await asyncio.sleep(retry_delay * (attempt + 1))

    async def receive_reward(self, task_id):
        try:
            response = await self.client.post(
                url="/task/receive",
                headers=self.headers,
                json={"id": task_id}
            )
            data = response.json()
            if data['code'] != 2200:
                fn_print(f"用户【{self.phone}】 - 领取签到奖励失败了， 请尝试手动领取")
            else:
                fn_print(f"用户【{self.phone}】 - 领取签到奖励成功！开始下一个任务")
        except Exception as e:
            fn_print(f"用户【{self.phone}】 - 领取签到奖励请求异常！{e}")
            fn_print(response.text)

    async def get_mileage_info(self):
        try:
            response = await self.client.post(
                url="/index/signIndex",
                headers=self.headers,
                json={}
            )
            data = response.json()
            if data['code'] != 2200:
                fn_print(f"用户【{self.phone}】 - 获取积分信息失败了")
                return None
            else:
                cycle_sign_num = data['data']['cycleSighNum']
                continuous_history = data['data']['continuousHistory']
                mileage = data['data']['mileageBalance']['mileage']
                today_mileage = data['data']['mileageBalance']['todayMileage']
                fn_print(
                    f"用户【{self.phone}】 - 本月签到{cycle_sign_num}天，连续签到{continuous_history}天，今日共获取{today_mileage}里程，当前剩余里程{mileage}")
        except Exception as e:
            fn_print(f"用户【{self.phone}】 - 获取积分信息请求异常！{e}")
            fn_print(response.text)
            return None

    async def run(self):
        today_sign = await self.sign_in()
        if today_sign is None:
            return
        if today_sign:
            fn_print(f"用户【{self.phone}】 - 今日已签到，开始获取任务列表")
        else:
            if await self.do_sign_in():
                fn_print(f"用户【{self.phone}】 - 签到成功，开始获取任务列表")
        tasks = await self.get_task_list()
        if tasks:
            for task in tasks:
                task_code = task['taskCode']
                title = task['title']
                browser_time = task['browserTime']
                fn_print(f"用户【{self.phone}】 - 开始做任务【{title}】，需要浏览{browser_time}秒")
                task_id = await self.perform_tasks(task_code)
                if task_id:
                    await asyncio.sleep(browser_time)
                    if await self.finsh_task(task_id):
                        await self.receive_reward(task_id)
        await self.get_mileage_info()


async def main():
    tasks = []
    for cookie in tc_cookies:
        tclx = Tclx(cookie)
        tasks.append(tclx.run())
    await asyncio.gather(*tasks)


if __name__ == '__main__':
    asyncio.run(main())
    send_notification_message_collection(f"同程旅行签到通知 - {datetime.now().strftime('%Y/%m/%d')}")
