# Source: https://github.com/lksky8/sign-ql/blob/main/3dmgame.py
# Raw: https://raw.githubusercontent.com/lksky8/sign-ql/main/3dmgame.py
# Repo: lksky8/sign-ql
# Path: 3dmgame.py
# UploadedAt: 2026-03-27T20:17:30Z
# SHA256: 2262c9f7c2d40c8ef9822325fadd2e8ed3c43ba0f609c072a27c49b02edc2eaa
# Category: web版/账密
# Evidence: web/H5关键词 + username/password/login

"""
作者：https://github.com/lksky8/sign-ql/
日期：2026-3-28
网站：3dmgame论坛签到
功能：签到、抽奖，金币可换现金买游戏
食用方法：登录论坛后，浏览器F12打开抓包，https://bbs.3dmgame.com/home.php?mod=spacecp&ac=credit&showcredit=1抓这个url请求头的cookie包
变量：bbs3dmck='cookie'  多个账号用换行分割
定时一天三次
青龙需要安装lxml模块
cron: 0 9 */8 * * *
"""

import time
import requests
from lxml import etree
import re
import random
import os


send_msg = ''
one_msg = ''

def Log(cont=''):
    global send_msg, one_msg
    if cont:
        one_msg += f'{cont}\n'
        send_msg += f'{cont}\n'

# 发送通知消息
def send_notification_message(title):
    try:
        from notify import send
        print("加载通知服务成功！")
        send(title, send_msg)
    except Exception as e:
        print('发送通知消息失败！' + str(e))


class ThreeDMGame:
    def __init__(self, cookies):
        headers = {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Origin': 'https://bbs.3dmgame.com',
            'Pragma': 'no-cache',
            'Sec-Fetch-Dest': 'iframe',
            'Sec-Fetch-Mode': 'navigate',
            'Sec-Fetch-Site': 'same-origin',
            'Sec-Fetch-User': '?1',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36',
            'sec-ch-ua': '"Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146"',
            'sec-ch-ua-mobile': '?0',
            'sec-ch-ua-platform': '"Windows"',
            'Cookie': cookies,
        }
        self.session = requests.Session()
        self.session.headers.update(headers)

    def user_info(self):
        try:
            response = self.session.get('https://bbs.3dmgame.com/home.php?mod=spacecp&ac=credit&showcredit=1')
            html = etree.HTML(response.text)
            user_id = html.xpath('//*[@id="hd"]/div/div[1]/p/strong/a/text()')[0]
            points = html.xpath('//*[@id="extcreditmenu"]/text()')[0]
            gold = html.xpath('//*[@id="ct"]/div[1]/div/ul[2]/li[1]/text()')[0]
            print(f'用户ID：[{user_id}] {points} 金元：{gold}')
            Log(f'\n用户ID：[{user_id}] {points} 金元：{gold}')
        except Exception as e:
            print('3dmgame论坛用户信息获取失败：', e)
            Log(f'\n3dmgame论坛用户信息获取失败')

    def task_view(self, t_id):
        try:
            response = self.session.get(f'https://bbs.3dmgame.com/home.php?mod=task&do=view&id={t_id}')
            html = etree.HTML(response.text)
            task_name = html.xpath('//h1[contains(@class, "xs2") and contains(@class, "ptm") and contains(@class, "pbm")]/text()')[0].strip()
            if 'do=apply' in response.text:
                print(f'任务：《{task_name}》 可申请')
            elif '后可以再次申请' in response.text:
                print(f'任务：《{task_name}》 未到申请时间')
            elif '您所在的用户组无法申请此任务' in response.text:
                print(f'任务：《{task_name}》 您所在的用户组无法申请此任务')
            elif 'static/image/task/reward.gif' in response.text:
                print(f'任务：《{task_name}》 任务已完成，待领取奖励')
            else:
                print(f'任务：《{task_name}》 未知状态')
        except Exception as e:
            print(f'查看任务信息失败：{t_id} {e}')

    def check_task(self):
        try:
            response = self.session.get('https://bbs.3dmgame.com/home.php?mod=task')
            # print(response.text)
            html = etree.HTML(response.text)
            task_rows = html.xpath('//div[@class="ptm"]//table//tr')

            # for row in task_rows:
            #     # 提取任务名称
            #     task_name = row.xpath('.//td[2]//h3/a/text()')[0] if row.xpath('.//td[2]//h3/a/text()') else ""
            #
            #     # 提取任务描述
            #     description = row.xpath('.//td[2]/p/text()')[0] if row.xpath('.//td[2]/p/text()') else ""
            #
            #     # 提取任务奖励
            #     reward = row.xpath('.//td[3]/text()')[0].strip() if row.xpath('.//td[3]/text()') else ""
            #
            #     # 提取任务状态（可申请/不可申请）
            #     is_available = len(row.xpath('.//td[4]//img[@alt="apply"]')) > 0
            #
            #     apply_link = row.xpath('.//td[4]/a[contains(@href, "do=apply")]/@href')[0] if row.xpath('.//td[4]/a[contains(@href, "do=apply")]/@href') else "无可用申请链接"
            #
            #     print(f"任务名称: {task_name}")
            #     print(f"申请链接: {apply_link}")
            #     print(f"描述: {description}")
            #     print(f"奖励: {reward}")
            #     print(f"是否可申请: {'是' if is_available else '否'}")
            #     print("---")
            task_list = {}
            for row in task_rows:
                task_name = row.xpath('.//td[2]//h3/a/text()')[0] if row.xpath('.//td[2]//h3/a/text()') else ""
                description = row.xpath('.//td[2]/p/text()')[0] if row.xpath('.//td[2]/p/text()') else ""
                reward = row.xpath('.//td[3]/text()')[0].strip() if row.xpath('.//td[3]/text()') else ""
                apply_links = row.xpath('.//td[4]/a[contains(@href, "do=apply")]/@href')

                # 只处理有可用申请链接的任务
                if apply_links:
                    apply_link = apply_links[0]
                    print(f"任务名称: {task_name}")
                    print(f"描述: {description}")
                    print(f"奖励: {reward}")
                    # print(f"任务id: {apply_link.split('id=')[-1]}")
                    print("-" * 50)
                    task_list.update({task_name: apply_link.split('id=')[-1]})
            if len(task_list) > 0:
                print(f'获取到{len(task_list)}个可接任务')
                for name, link_id in task_list.items():
                    print('*' * 30)
                    print(f'尝试领取任务：{name}')
                    response = self.session.get(f'https://bbs.3dmgame.com/home.php?mod=task&do=apply&id={link_id}')
                    if 'do=draw' in response.text:
                        response = self.session.get(f'https://bbs.3dmgame.com/home.php?mod=task&do=draw&id={link_id}')
                        if '恭喜您，任务已成功完成，您将收到奖励通知，请注意查收' in response.text:
                            print(f'任务：{name} 直接完成')
                        else:
                            print(f'任务：{name} 失败' + response.text)
                    elif '申请此任务需要先完成另一个任务' in response.text:
                        html_message = etree.HTML(response.text)
                        qianzhi_task = html_message.xpath('//div[@id="messagetext"]//p[@class="alert_btnleft"]/a/@href')[0].split("id=")[-1]
                        print(f'任务：{name} 领取失败 | 原因是：前置任务未完成，跳转前置任务id：{qianzhi_task}')
                        self.task_view(qianzhi_task)
                    elif '任务申请成功' in response.text:
                        print(f'任务：{name} 领取成功')
                    elif '不是进行中的任务' in response.text:
                        print(f'任务：{name} 无法直接完成')
                    else:
                        print(f'任务：{name} 领取失败')
                        print(response.text)
                    time.sleep(1)
                print('-' * 50)
            else:
                print('没有可接任务')
                # Log(f'\n没有可接任务')
                print('-' * 50)
        except Exception as e:
            print(f'检查任务信息失败：{e}')

    def check_task_doing(self):
        try:
            response = self.session.get('https://bbs.3dmgame.com/home.php?mod=task&item=doing')
            # print(response.text)
            html = etree.HTML(response.text)
            task_rows = html.xpath('//div[@class="ptm"]//table//tr')
            task_list = []
            for row in task_rows:
                # 提取任务名称
                task_name = row.xpath('.//td[2]//h3/a/text()')[0] if row.xpath('.//td[2]//h3/a/text()') else ""

                # 提取任务ID
                task_id = row.xpath('.//td[2]//h3/a/@href')[0].split('id=')[-1] if row.xpath('.//td[2]//h3/a/@href') else ""

                # 提取do=view链接（任务详情链接）
                # view_link = row.xpath('.//td[2]//h3/a[contains(@href, "do=view")]/@href')[0] if row.xpath('.//td[2]//h3/a[contains(@href, "do=view")]/@href') else ""

                # 提取领取链接(do=draw类型)
                # draw_links = row.xpath('.//td[4]/p/a[contains(@href, "do=draw")]/@href')[0] if row.xpath('.//td[4]/p/a/@href') else ""
                print(f"进行中的任务: 《{task_name}》")
                task_list.append({task_name: task_id})
                print('-' * 30)
            if len(task_list) > 0:
                return task_list
            else:
                print('没有进行中的任务')
                # Log(f'\n没有进行中的任务')
                return []
        except Exception as e:
            print(f'检查任务进行中失败：{e}')

    def do_task(self, tk_list):
        print(f'获取到{len(tk_list)}个待完成任务')
        for task_name, task_id in tk_list.items():
            print(f'尝试完成任务：《{task_name}》')
            response = self.session.get(f'https://bbs.3dmgame.com/home.php?mod=task&do=view&id={task_id}')
            if 'static/image/task/reward.gif' in response.text:
                response = self.session.get(f'https://bbs.3dmgame.com/home.php?mod=task&do=draw&id={task_id}')
                if '恭喜您，任务已成功完成，您将收到奖励通知，请注意查收' in response.text:
                    print(f'任务：《{task_name}》 完成')
                else:
                    print(f'任务：《{task_name}》 失败')
                    print(response.text)
            elif '完成此任务所需条件' in response.text:
                print(f'任务：《{task_name}》 无完成条件')
                response = self.session.get(f'https://bbs.3dmgame.com/home.php?mod=task&do=draw&id={task_id}')
                if '恭喜您，任务已成功完成，您将收到奖励通知，请注意查收' in response.text:
                    print(f'任务：《{task_name}》 完成')
                else:
                    print(f'任务：《{task_name}》 失败')
                    print(response.text)
            else:
                print(f'任务：《{task_name}》 有完成条件')
                tree = etree.HTML(response.text)
                tables = tree.xpath("//table[@class='tfm']//td[@class='bbda']/a/@href")[0]
                # print(tables)
                reply_td = tree.xpath("//table[@class='tfm']//td[@class='bbda'][contains(., '次')]/text()[normalize-space()]")[1]
                reply_td = reply_td.replace(' ', '')
                # print(reply_td)
                reply_count = re.search(r'(\d+)次', reply_td).group(1) if re.search(r'(\d+)次', reply_td) else "未知"
                if 'thread' in tables:
                    response = self.session.get(f'https://bbs.3dmgame.com/{tables}')
                    formhash = re.search(r'name="formhash" value="(\w+)"', response.text).group(1)
                    fid_match = re.search(r"fid\s*=\s*parseInt\('(\d+)'\)", response.text).group(1)
                    tid_match = re.search(r"tid\s*=\s*parseInt\('(\d+)'\)", response.text).group(1)
                    print('回复次数:' + reply_count)
                    if int(reply_count) > 5:
                        print('回复次数大于5次，任务跳过')
                    else:
                        for _ in range(int(reply_count)):
                            for i in range(3):
                                result = self.reply(tid_match, fid_match, formhash)
                                if result:
                                    break
                                time.sleep(10)
                            time.sleep(35)
                    print(f'已完成任务 《{task_name}》 回复指定文章要求')
                else:
                    response = self.session.get(f'https://bbs.3dmgame.com/{tables}')
                    result = re.findall(r'mod=redirect&tid=(\d+)&goto', response.text)
                    if len(result) > 0:
                        response = self.session.get(f'https://bbs.3dmgame.com/thread-{random.choice(result)}-1-1.html')
                        formhash = re.search(r'name="formhash" value="(\w+)"', response.text).group(1)
                        fid_match = re.search(r"fid\s*=\s*parseInt\('(\d+)'\)", response.text).group(1)
                        tid_match = re.search(r"tid\s*=\s*parseInt\('(\d+)'\)", response.text).group(1)
                        for _ in range(int(reply_count)):
                            for i in range(3):
                                result = self.reply(tid_match, fid_match, formhash)
                                if result:
                                    break
                                time.sleep(10)
                            time.sleep(35)
                    print(f'已完成任务 《{task_name}》 回复指定主题要求')
                response = self.session.get(f'https://bbs.3dmgame.com/home.php?mod=task&do=draw&id={task_id}')
                if '恭喜您，任务已成功完成，您将收到奖励通知，请注意查收' in response.text:
                    print(f'任务：《{task_name}》 完成')
                elif '您已完成该任务的' in response.text:
                    html = etree.HTML(response.text)
                    b = html.xpath('//div[@id="messagetext"]/p/text()')[0]
                    print(f'任务：《{task_name}》 失败：' + b.strip())
                elif '您还没有开始执行任务，赶快哦' in response.text:
                    print(f'任务：《{task_name}》 失败：因任务特殊跳过该任务')
                elif '您还没有开始执行任务' in response.text:
                    print(f'任务：《{task_name}》 失败：因任务特殊跳过该任务')
                else:
                    print(f'任务：《{task_name}》 失败')
                    print(response.text)
            time.sleep(3)

    def reply(self, tid, fid, formhash):
        response = requests.get("https://www.mxnzp.com/api/jokes/list?page=1&app_id=vqohyieoq7qklmgp&app_secret=FCc9Uf0h1c0LNkqeLRolebStTGfds3Fx")
        response_json = response.json()
        if response_json['code'] == 1 :
            message = response_json['data']['list'][0]['content']
        else:
            message = '我是新玩家，搞不懂' + random.choice(['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'])
        data = {
            'file': '',
            'message': message,
            'posttime': str(int(time.time())),
            'formhash': formhash,
            'usesig': '1',
            'subject': '  ',
        }
        response = self.session.post(f'https://bbs.3dmgame.com/forum.php?mod=post&action=reply&fid={fid}&tid={tid}&extra=page%3D1&replysubmit=yes&infloat=yes&handlekey=fastpost&inajax=1', data=data)
        if '回复发布成功' in response.text:
            print('回帖成功,等待35秒后回复下个帖子')
            return True
        else:
            print('回复失败，未知原因')
            return False


    def main(self):
        self.user_info()
        print('-' * 50)
        print('开始做论坛任务-->>>>>>>>')
        self.check_task()
        task_list = self.check_task_doing()
        if task_list:
            for task in task_list:
                self.do_task(task)
        print('等待30秒进行下一个账号')
        self.session.close()
        print('*' * 50)
        time.sleep(30)
        Log('-' * 30)


if __name__ == '__main__':
    try:
        if 'bbs3dmck' in os.environ:
            bbs3dmck = re.split("@|&", os.environ.get("bbs3dmck"))
            print(f'查找到{len(bbs3dmck)}个账号\n')
        else:
            bbs3dmck = None
            print('无bbs3dmck变量')
            Log(f'\n未填入bbs3dmck变量')

        if bbs3dmck:
            z = 1
            for ck in bbs3dmck:
                print(f'登录第{z}个账号')
                threeDM = ThreeDMGame(ck)
                threeDM.main()
                z+= 1
    except Exception as e:
        print(e)
    try:
        send_notification_message(title='3dmgame论坛签到')  # 发送通知
    except Exception as e:
        print(e)
