2
0
jack 9 ماه پیش
کامیت
4926e73f6a

+ 67 - 0
.gitignore

@@ -0,0 +1,67 @@
+.DS_Store
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+.idea/*
+xml_files/
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+other/split_clash_config/split_config
+ai_news/save_data
+
+manual/clash/clash_each_node
+manual/singbox/singbox_each_node

+ 0 - 0
README.md


+ 4 - 0
__init__.py

@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+
+from . import controllers
+from . import models

+ 27 - 0
__manifest__.py

@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+{
+    "name": "Web3",
+    "version": "1.0",
+    "author": "Jack",
+    "category": "auto_bot/Web3",
+    'summary': 'Web3',
+    'description': """Web3""",
+    "depends": ["base"],
+    "data": [
+        "security/ir.model.access.csv",
+        "security/security.xml",
+        "views/view_web3_index.xml",
+        "views/view_daily_3dos.xml",
+        "views/view_monad_testcoin_blance.xml"
+    ],
+    'assets': {
+        'web.assets_backend': [
+            'web3/static/src/scss/style.scss',
+        ],
+    },
+    "installable": True,
+    "auto_install": True,
+    "application": True,
+    'main_data': ['home.page'],
+    'license': 'LGPL-3',
+}

+ 3 - 0
controllers/__init__.py

@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import controllers

+ 22 - 0
controllers/controllers.py

@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# from odoo import http
+
+
+# class Web3(http.Controller):
+#     @http.route('/web3/web3', auth='public')
+#     def index(self, **kw):
+#         return "Hello, world"
+
+#     @http.route('/web3/web3/objects', auth='public')
+#     def list(self, **kw):
+#         return http.request.render('web3.listing', {
+#             'root': '/web3/web3',
+#             'objects': http.request.env['web3.web3'].search([]),
+#         })
+
+#     @http.route('/web3/web3/objects/<model("web3.web3"):obj>', auth='public')
+#     def object(self, obj, **kw):
+#         return http.request.render('web3.object', {
+#             'object': obj
+#         })
+

+ 30 - 0
demo/demo.xml

@@ -0,0 +1,30 @@
+<odoo>
+    <data>
+<!--
+          <record id="object0" model="web3.web3">
+            <field name="name">Object 0</field>
+            <field name="value">0</field>
+          </record>
+
+          <record id="object1" model="web3.web3">
+            <field name="name">Object 1</field>
+            <field name="value">10</field>
+          </record>
+
+          <record id="object2" model="web3.web3">
+            <field name="name">Object 2</field>
+            <field name="value">20</field>
+          </record>
+
+          <record id="object3" model="web3.web3">
+            <field name="name">Object 3</field>
+            <field name="value">30</field>
+          </record>
+
+          <record id="object4" model="web3.web3">
+            <field name="name">Object 4</field>
+            <field name="value">40</field>
+          </record>
+-->
+    </data>
+</odoo>

+ 5 - 0
models/__init__.py

@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+
+from . import web3_index
+from . import daily_3dos
+from . import monad_testcoin_balance

+ 213 - 0
models/daily_3dos.py

@@ -0,0 +1,213 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields
+from httpx import Client
+from concurrent.futures import ThreadPoolExecutor, as_completed
+
+from odoo.exceptions import UserError
+
+
+class Daily3dos(models.Model):
+    _name = 'daily.3dos'
+    _description = 'Daily 3dos'
+
+    name = fields.Char(string='Name', default=fields.datetime.now())
+
+    state = fields.Selection([
+        ('draft', 'Draft'),
+        ('done', 'Done'),
+    ], string='State', default='draft')
+
+    daily_3dos_line_ids = fields.One2many('daily.3dos.line', 'daily_3dos_id', string='Daily 3dos Lines')
+
+    def btn_execute(self):
+        # 获取所有3DOS账户配置
+        if self.daily_3dos_line_ids:
+            all_3dos_account = []
+            for line in self.daily_3dos_line_ids:
+                if line.line_state != 'done':
+                    all_3dos_account.append(
+                        self.env['daily.3dos.config'].search([('account', '=', line.account)], limit=1))
+        else:
+            all_3dos_account = self.env['daily.3dos.config'].search([('activate', '=', True)])
+
+        if not all_3dos_account:
+            raise UserError('先设置3dos账号!')
+
+        # 创建字典, 记录每个账号的领取结果
+        save_data = {}
+
+        with ThreadPoolExecutor(max_workers=len(all_3dos_account)) as executor:
+            futures = []
+            for account in all_3dos_account:
+                email, password = account.account, account.password
+                futures.append(executor.submit(self.account_handle, email, password))
+
+            for future in as_completed(futures):
+                result = future.result()
+                if result:
+                    save_data.update(result)
+
+        for email, data in save_data.items():
+            message, state = data
+            # 判定 line 里面是否 name 是否 等于 email, 如果是, 更新当条数据, 如果不是, 创建数据
+            email_exists = any(email == line.account for line in self.daily_3dos_line_ids)
+            if email_exists:
+                line = self.env['daily.3dos.line'].search([('account', '=', email), ('daily_3dos_id', '=', self.id)])
+                line.write({
+                    'line_state': state,
+                    'message': message,
+                })
+            else:
+                self.env['daily.3dos.line'].create({
+                    'daily_3dos_id': self.id,
+                    'line_state': state,
+                    'account': email,
+                    'message': message,
+                })
+
+        return {
+            'type': 'ir.actions.client',
+            'tag': 'display_notification',
+            'params': {
+                'title': '提示',
+                'message': '操作成功!',
+                'sticky': False,
+            }
+        }
+
+    def account_handle(self, email, password):
+        save_data = {}
+        email = email
+        password = password
+
+        client = Client(proxy="http://127.0.0.1:7890")
+
+        login_url = "https://api.dashboard.3dos.io/api/auth/login"
+        login_headers = {
+            "Accept": "application/json, text/plain, */*",
+            "Accept-Encoding": "gzip, deflate, br, zstd",
+            "Accept-Language": "zh-CN,zh;q=0.9",
+            "Content-Type": "application/json",
+            "Origin": "https://dashboard.3dos.io",
+            "Priority": "u=1, i",
+            "Referer": "https://dashboard.3dos.io/",
+            "Sec-CH-UA": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
+            "Sec-CH-UA-Mobile": "?0",
+            "Sec-CH-UA-Platform": '"Windows"',
+            "Sec-Fetch-Dest": "empty",
+            "Sec-Fetch-Mode": "cors",
+            "Sec-Fetch-Site": "same-site",
+            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
+        }
+        login_payload = {
+            "email": email,
+            "password": password
+        }
+
+        try:
+            login_response = client.post(login_url, json=login_payload, headers=login_headers)
+        except Exception as e:
+            save_data[email] = [str(f"登录请求失败:{e}"), 'error']
+            return save_data
+
+        if login_response.status_code == 200:
+            login_data = login_response.json()
+            data = login_data.get("data")
+            token = data.get("access_token") or data.get("token")
+            token_type = data.get("token_type")
+            if not token or not token_type:
+                save_data[email] = [f"登录响应中未找到 Token 或 Token 类型!", 'error']
+                return save_data
+
+            authorization_header = f"{token_type} {token}"
+        else:
+            save_data[email] = [f"登录失败!状态码:{login_response.status_code}", 'error']
+            return save_data
+
+        options_url = "https://api.dashboard.3dos.io/api/claim-reward"
+        options_headers = {
+            "Accept": "*/*",
+            "Accept-Encoding": "gzip, deflate, br, zstd",
+            "Accept-Language": "zh-CN,zh;q=0.9",
+            "Access-Control-Request-Headers": "authorization,cache-control,content-type,expires,pragma",
+            "Access-Control-Request-Method": "POST",
+            "Origin": "https://dashboard.3dos.io",
+            "Priority": "u=1, i",
+            "Referer": "https://dashboard.3dos.io/",
+            "Sec-Fetch-Dest": "empty",
+            "Sec-Fetch-Mode": "cors",
+            "Sec-Fetch-Site": "same-site",
+            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
+        }
+
+        try:
+            options_response = client.options(options_url, headers=options_headers)
+        except Exception as e:
+            save_data[email] = [f"OPTIONS 请求失败:{e}", 'error']
+            return save_data
+
+        claim_url = "https://api.dashboard.3dos.io/api/claim-reward"
+        claim_headers = {
+            "Accept": "application/json, text/plain, */*",
+            "Authorization": authorization_header,
+            "Cache-Control": "no-cache",
+            "Content-Type": "application/json",
+            "Expires": "0",
+            "Origin": "https://dashboard.3dos.io",
+            "Pragma": "no-cache",
+            "Priority": "u=1, i",
+            "Referer": "https://dashboard.3dos.io/",
+            "Sec-CH-UA": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
+            "Sec-CH-UA-Mobile": "?0",
+            "Sec-CH-UA-Platform": '"Windows"',
+            "Sec-Fetch-Dest": "empty",
+            "Sec-Fetch-Mode": "cors",
+            "Sec-Fetch-Site": "same-site",
+            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
+        }
+        claim_payload = {
+            "id": "daily-reward-api"
+        }
+
+        try:
+            claim_response = client.post(claim_url, json=claim_payload, headers=claim_headers)
+        except Exception as e:
+            save_data[email] = [f"claim-reward 请求失败:{e}", 'error']
+            return save_data
+
+        tmp_message = claim_response.json()
+        message = tmp_message.get("message")
+
+        if claim_response.status_code not in [200, 429]:
+            save_data[email] = [f"claim 报错: {message}, 状态码{claim_response.status_code}", 'error']
+            return save_data
+
+        save_data[email] = [message, 'done']
+        print(f"{email}: {message}")
+        return save_data
+
+
+class Daily3dosLine(models.Model):
+    _name = 'daily.3dos.line'
+    _description = 'Daily 3dos Line'
+
+    daily_3dos_id = fields.Many2one('daily.3dos', string='Daily 3dos')
+    line_state = fields.Selection([
+        ('draft', 'Draft'),
+        ('error', 'Error'),
+        ('done', 'Done'),
+    ], string='Line State', default='draft')
+    account = fields.Char(string='Account')
+    password = fields.Char(string='Password')
+    message = fields.Char(string='Message')
+
+
+class Daily3dosConfig(models.Model):
+    _name = 'daily.3dos.config'
+    _description = 'Daily 3dos Config'
+
+    activate = fields.Boolean(string='Activate', default=True)
+    account = fields.Char(string='Account')
+    password = fields.Char(string='Password')
+    description = fields.Text(string='Description')

+ 67 - 0
models/monad_testcoin_balance.py

@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields
+from web3 import Web3
+import time
+
+
+class MonadTestcoinBalance(models.Model):
+    _name = 'monad.testcoin.balance'
+    _description = 'Monad Testcoin Balance'
+
+    wallet_address = fields.Char(string='Wallet Address')
+
+    balance = fields.Float(string='Balance')
+
+    message = fields.Char(string='Message')
+
+    def execute(self):
+        rpc_url = "https://testnet-rpc.monad.xyz"
+
+        web3 = Web3(Web3.HTTPProvider(rpc_url))
+        if not web3.is_connected():
+            print("无法连接到链节点,请检查 URL 是否正确")
+        else:
+            print("已成功连接到链节点。正在查询钱包余额...")
+
+        for record in self:
+            retry_count = 0
+            max_retries = 3
+            delay = 2
+
+            result_message = ''
+            result_balance = -1
+
+            while retry_count < max_retries:
+                try:
+                    checksum_address = web3.to_checksum_address(record.wallet_address)
+                    balance = web3.eth.get_balance(checksum_address)
+                    balance_eth = web3.from_wei(balance, "ether")
+                    result_message = f"Wallet address: {record.wallet_address}, balance: {balance_eth:.8f} $MON"
+                    result_balance = "{:.8f}".format(balance_eth)
+                    print(result_message)
+                    break
+                except Exception as e:
+                    print(f"查询钱包 {record.wallet_address} 余额时发生错误: {e}")
+                    retry_count += 1
+                    print(f"正在重试...(第 {retry_count} 次)")
+                    time.sleep(delay)
+
+            if retry_count == max_retries:
+                result_message = f"钱包 {record.wallet_address} 查询余额失败,已达到最大重试次数。"
+                print(result_message)
+
+            record.update({
+                'balance': result_balance,
+                'message': result_message
+            })
+
+        return {
+            'type': 'ir.actions.client',
+            'tag': 'display_notification',
+            'params': {
+                'title': '提示',
+                'message': '操作成功!',
+                'sticky': False,
+            }
+        }

+ 9 - 0
models/web3_index.py

@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models
+
+
+class HomePage(models.TransientModel):
+    _name = 'web3.index'
+    _description = 'Web3 Index'
+    _transient = True

+ 8 - 0
security/ir.model.access.csv

@@ -0,0 +1,8 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_home_page_access,"Home Page",model_web3_index,base.group_user,1,1,1,1
+
+access_daily_3dos_access,"Daily 3dos",model_daily_3dos,base.group_user,1,1,1,1
+access_daily_3dos_line_access,"Daily 3dos line",model_daily_3dos_line,base.group_user,1,1,1,1
+access_daily_3dos_config_access,"Daily 3dos config",model_daily_3dos_config,base.group_user,1,1,1,1
+
+access_monad_testcoin_balance_access,"Monad Testcoin Balance",model_monad_testcoin_balance,base.group_user,1,1,1,1

+ 12 - 0
security/security.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<odoo>
+    <record id="home_page_access" model="ir.model.access">
+        <field name="name">Home Page Access</field>
+        <field name="model_id" ref="model_web3_index"/>
+        <field name="group_id" ref="base.group_user"/>
+        <field name="perm_read" eval="1"/>
+        <field name="perm_write" eval="1"/>
+        <field name="perm_create" eval="1"/>
+        <field name="perm_unlink" eval="1"/>
+    </record>
+</odoo>

+ 5 - 0
static/src/scss/style.scss

@@ -0,0 +1,5 @@
+.o_form_sheet {
+    width: 100%;
+    max-width: 100%;
+    margin: 0 auto;
+}

+ 111 - 0
views/view_daily_3dos.xml

@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<odoo>
+    <!-- Daily 3dos 树形视图 -->
+    <record id="view_daily_3dos_tree" model="ir.ui.view">
+        <field name="name">daily.3dos.tree</field>
+        <field name="model">daily.3dos</field>
+        <field name="arch" type="xml">
+            <list string="Daily 3dos">
+                <field name="name"/>
+                <field name="state"/>
+            </list>
+        </field>
+    </record>
+
+    <!-- Daily 3dos 表单视图 -->
+    <record id="view_daily_3dos_form" model="ir.ui.view">
+        <field name="name">daily.3dos.form</field>
+        <field name="model">daily.3dos</field>
+        <field name="arch" type="xml">
+            <form string="Daily 3dos">
+                <header>
+                    <field name="state" widget="statusbar"/>
+                    <button name="btn_execute" string="Execute" type="object" class="oe_highlight"/>
+                </header>
+                <sheet>
+                    <group>
+                        <group>
+                            <field name="name" readonly="1"/>
+                        </group>
+                        <group>
+                            <field name="write_date" readonly="1"/>
+                        </group>
+                    </group>
+                    <notebook>
+                        <page string="Lines">
+                            <field name="daily_3dos_line_ids">
+                                <list string="Lines" editable="bottom">
+                                    <field name="account"/>
+                                    <field name="message"/>
+                                    <field name="line_state"/>
+                                </list>
+                                <form string="Line">
+                                    <group>
+                                        <field name="account"/>
+                                        <field name="line_state"/>
+                                        <field name="message"/>
+                                    </group>
+                                </form>
+                            </field>
+                        </page>
+                    </notebook>
+                </sheet>
+            </form>
+        </field>
+    </record>
+
+    <!-- Daily 3dos Line 表单视图 -->
+    <record id="view_daily_3dos_line_form" model="ir.ui.view">
+        <field name="name">daily.3dos.line.form</field>
+        <field name="model">daily.3dos.line</field>
+        <field name="arch" type="xml">
+            <form string="Daily 3dos Line">
+                <sheet>
+                    <group>
+                        <field name="daily_3dos_id" readonly="1"/>
+                        <field name="account"/>
+                    </group>
+                </sheet>
+            </form>
+        </field>
+    </record>
+
+    <!-- Daily 3dos Config 表单视图 -->
+    <record id="view_daily_3dos_config_tree" model="ir.ui.view">
+        <field name="name">daily.3dos.config.tree</field>
+        <field name="model">daily.3dos.config</field>
+        <field name="type">list</field>
+        <field name="arch" type="xml">
+            <list string="Daily 3dos Config" editable="bottom">
+                <field name="activate"/>
+                <field name="account"/>
+                <field name="password" password="True"/>
+                <field name="description"/>
+            </list>
+        </field>
+    </record>
+
+    <!-- Daily 3dos config 动作 -->
+    <record id="action_daily_3dos_config" model="ir.actions.act_window">
+        <field name="name">Daily 3dos config</field>
+        <field name="res_model">daily.3dos.config</field>
+        <field name="view_mode">list</field>
+    </record>
+
+    <!-- Daily 3dos 动作 -->
+    <record id="action_daily_3dos" model="ir.actions.act_window">
+        <field name="name">Daily 3dos</field>
+        <field name="res_model">daily.3dos</field>
+        <field name="view_mode">list,form</field>
+        <field name="help" type="html">
+            <p class="oe_view_nocontent_create">Create a new Daily 3dos</p>
+        </field>
+    </record>
+
+    <!-- Daily 3dos 菜单 -->
+    <menuitem id="menu_daily_3dos" name="Daily 3dos" parent="menu_web3_index" sequence="2"/>
+    <menuitem id="menu_daily_3dos_execute" name="Daily 3dos" action="action_daily_3dos" parent="menu_daily_3dos"
+              sequence="1"/>
+    <menuitem id="menu_daily_3dos_config" name="Daily 3dos Config" action="action_daily_3dos_config"
+              parent="menu_daily_3dos" sequence="2"/>
+</odoo>

+ 30 - 0
views/view_monad_testcoin_blance.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<odoo>
+    <data>
+        <record id="view_monad_testcoin_balance_list" model="ir.ui.view">
+            <field name="name">monad.testcoin.balance.list</field>
+            <field name="model">monad.testcoin.balance</field>
+            <field name="arch" type="xml">
+                <list editable="bottom" create="true" delete="true">
+                    <header>
+                        <button name="execute" string="Execute" type="object" class="oe_highlight"/>
+                    </header>
+                    <field name="wallet_address" colspan="4"/>
+                    <field name="balance" colspan="4"/>
+                    <field name="message" colspan="6"/>
+                </list>
+            </field>
+        </record>
+
+        <record id="action_monad_testcoin_balance" model="ir.actions.act_window">
+            <field name="name">Monad Testcoin Balance</field>
+            <field name="res_model">monad.testcoin.balance</field>
+            <field name="view_mode">list</field>
+            <field name="view_id" ref="view_monad_testcoin_balance_list"/>
+        </record>
+
+        <menuitem id="menu_monad" name="Monad" parent="menu_web3_index" sequence="3"/>
+        <menuitem id="menu_monad_testcoin_balance" name="Monad Testcoin Balance" parent="menu_monad"
+                  action="action_monad_testcoin_balance" sequence="1"/>
+    </data>
+</odoo>

+ 18 - 0
views/view_web3_index.xml

@@ -0,0 +1,18 @@
+<odoo>
+    <record id="web3_index_form_view" model="ir.ui.view">
+        <field name="name">web3.index</field>
+        <field name="model">web3.index</field>
+        <field name="arch" type="xml">
+            <list string="Web3 Index" create="0">
+            </list>
+        </field>
+    </record>
+
+    <record id="action_web3_index" model="ir.actions.act_window">
+        <field name="name">Web3 Index</field>
+        <field name="res_model">web3.index</field>
+        <field name="view_mode">list</field>
+    </record>
+
+    <menuitem id="menu_web3_index" name="Web3 Index" action="action_web3_index" sequence="3"/>
+</odoo>