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 # 指定数据库
|
||||
|
||||
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
Reference in New Issue
Block a user