This commit is contained in:
Your Name
2026-02-13 22:59:13 +08:00
parent 984223c720
commit e28338f34d
13925 changed files with 385 additions and 154 deletions

BIN
haha.db

Binary file not shown.

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""
为 tg_phone_devices_copy1 表添加 tg_web_app_data 列(若不存在)。
解决: Unknown column 't1.tg_web_app_data' in 'field list'
"""
import sys
from pathlib import Path
project_root = Path(__file__).resolve().parent.parent
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
from models.tg_models import db
TABLE = "tg_phone_devices_copy1"
COLUMN = "tg_web_app_data"
SPEC = "VARCHAR(2048) NULL COMMENT 'Blum WebApp tgWebAppData token'"
def column_exists(cursor, table, column):
cursor.execute(
"""
SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = %s
AND COLUMN_NAME = %s
""",
(table, column),
)
return cursor.fetchone()[0] > 0
def main():
if not db.is_closed():
db.connect()
try:
with db.connection() as conn:
cursor = conn.cursor()
if column_exists(cursor, TABLE, COLUMN):
print(f"{TABLE} 已存在列 {COLUMN},无需添加。")
return
sql = f"ALTER TABLE `{TABLE}` ADD COLUMN `{COLUMN}` {SPEC};"
cursor.execute(sql)
print(f"已在表 {TABLE} 添加列 {COLUMN}")
finally:
if not db.is_closed():
try:
db.close()
except Exception:
pass
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""
为 SQLite haha.db 中 tg_phone_devices_copy1 表添加 tg_web_app_data 列(若不存在)。
解决 踢设备.py 报错: no such column: t1.tg_web_app_data
"""
import sys
from pathlib import Path
project_root = Path(__file__).resolve().parent.parent
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
from models.tg_phone_devices import db
TABLE = "tg_phone_devices_copy1"
COLUMN = "tg_web_app_data"
def column_exists_sqlite(cursor, table, column):
cursor.execute("PRAGMA table_info({})".format(table))
return any(row[1] == column for row in cursor.fetchall())
def main():
db.connect()
try:
with db.connection() as conn:
cursor = conn.cursor()
cursor.execute("PRAGMA table_info({})".format(TABLE))
if any(row[1] == COLUMN for row in cursor.fetchall()):
print(f"{TABLE} 已存在列 {COLUMN},无需添加。")
return
cursor.execute("ALTER TABLE {} ADD COLUMN {} TEXT".format(TABLE, COLUMN))
print(f"已在表 {TABLE}SQLite添加列 {COLUMN}")
finally:
if not db.is_closed():
try:
db.close()
except Exception:
pass
if __name__ == "__main__":
main()

View File

@@ -1,26 +1,136 @@
from peewee import *
from pathlib import Path
from datetime import datetime
import threading
import time
import pymysql
from peewee import MySQLDatabase
from playhouse.pool import PooledMySQLDatabase
# 安装 pymysql 作为 MySQLdb
pymysql.install_as_MySQLdb()
# 断连相关的 MySQL 错误码
_RETRY_ERRORS = (
0, # InterfaceError: (0, '')
2006, # MySQL server has gone away
2013, # Lost connection to MySQL server during query
)
class SingleConnectionMySQLDatabase(MySQLDatabase):
"""
多线程共用一个 MySQL 连接:
- 使用 thread_safe=False 让所有线程共享同一个 _state一个连接
- 用 RLock 串行化所有 connect/close/execute_sql保证同一时刻只有一个线程在用连接
"""
def __init__(self, *args, **kwargs):
# thread_safe=False → _state 为共享的 _ConnectionState不按线程隔离
super().__init__(*args, thread_safe=False, **kwargs)
self._lock = threading.RLock()
def connect(self, reuse_if_open=False):
with self._lock:
if self.deferred:
raise InterfaceError('Error, database must be initialized before opening a connection.')
conn = self._state.conn
if conn is not None and not self._state.closed:
if reuse_if_open:
return False
try:
conn.ping(reconnect=True)
except Exception:
try:
self._close(conn)
except Exception:
pass
self._state.reset()
if self._state.closed:
self._state.reset()
try:
self._state.set_connection(self._connect())
if self.server_version is None:
self._set_server_version(self._state.conn)
self._initialize_connection(self._state.conn)
except Exception:
self._state.reset()
raise
return True
def execute_sql(self, sql, params=None, commit=True):
with self._lock:
try:
return super().execute_sql(sql, params, commit)
except (OperationalError, InterfaceError) as e:
err_code = getattr(e, 'args', (None,))[0]
if err_code in _RETRY_ERRORS or (isinstance(err_code, int) and err_code in _RETRY_ERRORS):
try:
self.close()
except Exception:
pass
return super().execute_sql(sql, params, commit)
raise
def close(self):
with self._lock:
return super().close()
class ReconnectPooledMySQLDatabase(PooledMySQLDatabase):
"""
带自动重连的 MySQL 连接池:
1. 从池中取连接时先 pingping 失败则丢弃并新建连接
2. SQL 执行遇到断连错误时,关闭坏连接并自动重试一次
"""
def _connect(self):
"""新建连接后设置 autocommit避免长事务导致连接被 MySQL 断开。"""
conn = super()._connect()
return conn
def connection(self):
"""取连接时先 ping 探活,不通就关掉让池重新创建。"""
conn = super().connection()
try:
conn.ping(reconnect=True)
except Exception:
# ping 失败,连接已死,关掉让 peewee 重新获取
try:
self.close()
except Exception:
pass
conn = super().connection()
return conn
def execute_sql(self, sql, params=None, commit=True):
"""执行 SQL遇到断连错误自动重试一次。"""
try:
return super().execute_sql(sql, params, commit)
except (OperationalError, InterfaceError) as e:
err_code = getattr(e, 'args', (None,))[0]
if err_code in _RETRY_ERRORS or (isinstance(err_code, int) and err_code in _RETRY_ERRORS):
# 关闭坏连接,下次 connection() 会重新获取
try:
self.close()
except Exception:
pass
# 重试一次
return super().execute_sql(sql, params, commit)
raise
# 数据库配置
db_config = {
'database': 'lm',
'user': 'lm',
'password': 'sAn5MfjKApiTBrx4',
'host': '172.16.197.130',
'port': 3306
'host': '199.168.137.123',
'port': 3309
}
# 全局数据库实例
db = MySQLDatabase(
# 全局数据库实例:多线程共用一个连接(串行化访问,无 MaxConnectionsExceeded
db = SingleConnectionMySQLDatabase(
db_config['database'],
user=db_config['user'],
password=db_config['password'],
@@ -88,6 +198,7 @@ class TgPhoneDevices1(Model):
user = CharField(null=True, ) # 电话号码
pwd = CharField(null=True, ) # 电话号码
device_start = IntegerField(null=True, ) # API ID
tg_web_app_data = CharField(null=True, max_length=2048, help_text='Blum WebApp tgWebAppData token') # Blum token
class Meta:
database = db # 指定数据库

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More