HAHAHHA
This commit is contained in:
55
models/add_tg_web_app_data_column.py
Normal file
55
models/add_tg_web_app_data_column.py
Normal 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()
|
||||
45
models/add_tg_web_app_data_sqlite.py
Normal file
45
models/add_tg_web_app_data_sqlite.py
Normal 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()
|
||||
@@ -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. 从池中取连接时先 ping,ping 失败则丢弃并新建连接
|
||||
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 # 指定数据库
|
||||
|
||||
Reference in New Issue
Block a user