1+ from sqlalchemy import Column , Integer , String , Float , Boolean , DateTime , text
2+ from sqlalchemy .ext .asyncio import create_async_engine , AsyncSession
3+ from sqlalchemy .orm import sessionmaker , declarative_base
4+ from datetime import datetime
5+ import pytz
6+ from config import SETTINGS
7+
8+ # Используем вашу таймзону
9+ TIMEZONE = pytz .timezone ('Europe/Kyiv' )
10+
11+ Base = declarative_base ()
12+
13+ class Service (Base ):
14+ __tablename__ = 'services'
15+
16+ id = Column (Integer , primary_key = True )
17+ name = Column (String , unique = True , nullable = False )
18+
19+ # Мониторинг баланса
20+ last_balance = Column (Float , default = 0.0 )
21+ low_balance_alert_sent = Column (Boolean , default = False )
22+ currency = Column (String , default = 'USD' )
23+ daily_cost = Column (Float , nullable = True )
24+ monthly_fee = Column (Float , nullable = True )
25+
26+ # Для Callii и Streamtele
27+ next_alert_date = Column (DateTime , nullable = True )
28+ next_monthly_alert = Column (DateTime , nullable = True )
29+
30+ def __repr__ (self ):
31+ return f"<Service(name='{ self .name } ', balance={ self .last_balance } )>"
32+
33+ # Инициализация
34+ async def init_db (database_url : str ):
35+ engine = create_async_engine (database_url , echo = False )
36+ async with engine .begin () as conn :
37+ await conn .run_sync (Base .metadata .create_all )
38+
39+ AsyncSessionLocal = sessionmaker (
40+ autocommit = False ,
41+ autoflush = False ,
42+ bind = engine ,
43+ class_ = AsyncSession ,
44+ expire_on_commit = False ,
45+ )
46+ return AsyncSessionLocal
47+
48+ # Вспомогательная функция для добавления начальных данных
49+ async def initialize_services (SessionLocal ):
50+ async with SessionLocal () as session :
51+ # --- simple sqlite migration to add new columns if missing ---
52+ pragma_stmt = text ("PRAGMA table_info(services)" )
53+ result = await session .execute (pragma_stmt )
54+ columns = {row [1 ] for row in result .fetchall ()}
55+
56+ alter_statements = []
57+ if 'currency' not in columns :
58+ alter_statements .append ("ALTER TABLE services ADD COLUMN currency VARCHAR" )
59+ if 'daily_cost' not in columns :
60+ alter_statements .append ("ALTER TABLE services ADD COLUMN daily_cost FLOAT" )
61+ if 'monthly_fee' not in columns :
62+ alter_statements .append ("ALTER TABLE services ADD COLUMN monthly_fee FLOAT" )
63+ if 'next_monthly_alert' not in columns :
64+ alter_statements .append ("ALTER TABLE services ADD COLUMN next_monthly_alert DATETIME" )
65+
66+ for stmt in alter_statements :
67+ try :
68+ await session .execute (text (stmt ))
69+ except Exception :
70+ # ignore if already exists or other minor issues
71+ pass
72+ if alter_statements :
73+ await session .commit ()
74+
75+ services_to_add = [
76+ # API сервисы
77+ {
78+ 'name' : 'Zadarma' ,
79+ 'last_balance' : 0.0 ,
80+ 'currency' : SETTINGS .SERVICE_CURRENCIES .get ('Zadarma' , 'USD' ),
81+ },
82+ # Wazzup разделён: подписка (ежемесячно) и баланс номера (ежедневный расход)
83+ {
84+ 'name' : 'Wazzup24 Подписка' ,
85+ 'last_balance' : 0.0 ,
86+ 'currency' : SETTINGS .SERVICE_CURRENCIES .get ('Wazzup24 Подписка' , 'RUB' ),
87+ 'monthly_fee' : SETTINGS .WAZZUP_MONTHLY_FEE ,
88+ 'next_monthly_alert' : TIMEZONE .localize (datetime (2025 , 12 , 11 , 10 , 0 )),
89+ },
90+ {
91+ 'name' : 'Wazzup24 Баланс номера' ,
92+ 'last_balance' : 0.0 ,
93+ 'currency' : SETTINGS .SERVICE_CURRENCIES .get ('Wazzup24 Баланс номера' , 'RUB' ),
94+ 'daily_cost' : SETTINGS .WAZZUP_DAILY_COST ,
95+ 'next_alert_date' : TIMEZONE .localize (datetime (2025 , 12 , 11 , 10 , 0 )),
96+ },
97+ {
98+ 'name' : 'DIDWW' ,
99+ 'last_balance' : 0.0 ,
100+ 'currency' : SETTINGS .SERVICE_CURRENCIES .get ('DIDWW' , 'USD' ),
101+ 'monthly_fee' : SETTINGS .DIDWW_MONTHLY_FEE ,
102+ 'next_monthly_alert' : TIMEZONE .localize (datetime (2025 , 12 , 20 , 10 , 0 )),
103+ },
104+ # Callii (Управляемый FSM)
105+ {
106+ 'name' : 'Callii' ,
107+ 'next_alert_date' : TIMEZONE .localize (datetime (2025 , 12 , 11 , 10 , 0 )),
108+ 'currency' : SETTINGS .SERVICE_CURRENCIES .get ('Callii' , 'USD' ),
109+ 'daily_cost' : SETTINGS .CALLII_DAILY_COST ,
110+ },
111+ # Streamtele (Ежемесячное напоминание)
112+ {
113+ 'name' : 'Streamtele' ,
114+ 'currency' : SETTINGS .SERVICE_CURRENCIES .get ('Streamtele' , 'UAH' ),
115+ 'monthly_fee' : SETTINGS .STREAMTELE_MONTHLY_FEE ,
116+ 'next_monthly_alert' : TIMEZONE .localize (datetime (2025 , 12 , 11 , 10 , 0 )),
117+ },
118+ ]
119+
120+ from sqlalchemy .future import select # Дополнительный импорт нужен для 'select'
121+
122+ for data in services_to_add :
123+ # ИСПРАВЛЕНО: Используем select и where для поиска по уникальному полю 'name'
124+ stmt = select (Service ).filter (Service .name == data ['name' ])
125+ result = await session .execute (stmt )
126+ exists = result .scalar_one_or_none ()
127+
128+ if not exists :
129+ session .add (Service (name = data ['name' ], ** {k : v for k , v in data .items () if k != 'name' }))
130+
131+ await session .commit ()
0 commit comments