77# Requirements
88from apscheduler .schedulers .asyncio import AsyncIOScheduler
99from apscheduler .triggers .interval import IntervalTrigger
10- from dotenv import load_dotenv
10+ from databases import Database
1111from telebot import types
1212from telebot .async_telebot import AsyncTeleBot
13- import pymysql
13+ import dotenv
1414
1515
1616# Configuration
17- load_dotenv ()
17+ dotenv . load_dotenv ()
1818DB_USERNAME = os .environ .get ('DB_USERNAME' , 'wwcs' )
1919DB_PASSWORD = os .environ ['DB_PASSWORD' ]
2020BOT_TOKEN = os .environ ['BOT_TOKEN' ]
2121LANGUAGE = os .environ .get ('LANGUAGE' , 'en' )
2222TIMEZONE = os .environ .get ('TIMEZONE' ) # Defaults to local timezone
2323
24+ # Database
25+ DATABASE_URL = f'mysql+asyncmy://{ DB_USERNAME } :{ DB_PASSWORD } @localhost:3306/WWCServices'
26+ database = Database (DATABASE_URL )
27+
2428# Initialize gettext
2529root = pathlib .Path (__file__ ).parent
2630translation = gettext .translation (
@@ -97,35 +101,10 @@ async def remove_all_jobs(self, chat_id):
97101 22 : 1.91 ,
98102 23 : 2.13 ,
99103 24 : 2.37 ,
100- 25 : 2.63
104+ 25 : 2.63 ,
101105}
102106
103107
104- # Подключение к базе данных MySQL
105- def get_db_connection ():
106- return pymysql .connect (
107- host = "localhost" ,
108- user = DB_USERNAME ,
109- password = DB_PASSWORD ,
110- cursorclass = pymysql .cursors .DictCursor
111- )
112-
113-
114- async def execute_query (query , params = None ):
115- def sync_execute ():
116- connection = get_db_connection ()
117- try :
118- with connection .cursor () as cursor :
119- cursor .execute (query , params or ())
120- result = cursor .fetchall ()
121- connection .commit ()
122- return result
123- finally :
124- connection .close ()
125-
126- return await asyncio .to_thread (sync_execute )
127-
128-
129108async def get_irrigation_data ():
130109 query = """
131110 SELECT
@@ -147,13 +126,13 @@ async def get_irrigation_data():
147126 WHERE s.irrigation = 1 AND i.PHIc < i.PHIt AND i.irrigationApp = 0
148127 AND i.date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
149128 """
150- return await execute_query ( query )
129+ return await database . fetch_all ( query = query )
151130
152131
153132BUTTONS = {
154133 "send_recommendation" : _ ("Send recommendation" ),
155134 "no_water" : _ ("No water" ),
156- "save_data" : _ ("Save data" )
135+ "save_data" : _ ("Save data" ),
157136}
158137
159138
@@ -492,18 +471,16 @@ async def handle_send_data(message):
492471 area = float (row ['area' ])
493472 actual_mm = (data ['total_used_m3' ] * float (row ['ie' ])) / (10 * area * float (row ['wa' ]))
494473
495- await execute_query (
496- """UPDATE WWCServices.Irrigation
497- SET irrigationApp = %s
498- WHERE siteID = %s
499- AND date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)""" ,
500- (actual_mm , row ['siteID' ])
501- )
502-
503- await bot .send_message (
504- chat_id ,
505- _ ("✅ Data saved! Used: {used_m3:.2f} m³" ).format (used_m3 = data ['total_used_m3' ])
506- )
474+ query = """
475+ UPDATE WWCServices.Irrigation SET irrigationApp = :actual_mm
476+ WHERE siteID = :siteID AND date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
477+ """
478+ values = {'actual_mm' : actual_mm , 'siteID' : row ['siteID' ]}
479+ await database .execute (query = query , values = values )
480+
481+ message = _ ("✅ Data saved! Used: {used_m3:.2f} m³" ).format (used_m3 = data ['total_used_m3' ])
482+ await bot .send_message (chat_id , message )
483+
507484 data ['is_active' ] = False
508485 return
509486 except Exception as e :
@@ -521,7 +498,6 @@ async def handle_send_data(message):
521498 await bot .send_message (chat_id , _ ("❌ Your data was not found in the system" ))
522499
523500
524-
525501@bot .message_handler (func = lambda message : user_states .get (message .chat .id ) == 'waiting_for_traditional_start' )
526502async def handle_traditional_start (message ):
527503 chat_id = message .chat .id
@@ -539,8 +515,6 @@ async def handle_traditional_start(message):
539515 await bot .send_message (chat_id , _ ("⚠️ Type correct number (like 125.5)" ))
540516
541517
542-
543-
544518@bot .message_handler (func = lambda message : user_states .get (message .chat .id ) == 'waiting_for_traditional_end' )
545519async def handle_traditional_end (message ):
546520 chat_id = message .chat .id
@@ -566,23 +540,19 @@ async def handle_traditional_end(message):
566540 area = float (row ['area' ])
567541 actual_mm = (used_m3 * float (row ['ie' ])) / (10 * area * float (row ['wa' ]))
568542
569- await execute_query (
570- """UPDATE WWCServices.Irrigation
571- SET irrigationApp = %s
572- WHERE siteID = %s
573- AND date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)""" ,
574- (actual_mm , row ['siteID' ])
575- )
543+ query = """
544+ UPDATE WWCServices.Irrigation SET irrigationApp = :actual_mm
545+ WHERE siteID = :siteID AND date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
546+ """
547+ values = {'actual_mm' : actual_mm , 'siteID' : row ['siteID' ]}
548+ await database .execute (query = query , values = values )
576549
577550 await bot .send_message (
578551 chat_id ,
579552 _ ("✅ Data saved!\n "
580553 "Water used: {used_m3:.2f} m³\n "
581554 "Equivalent to: {actual_mm:.2f} mm"
582- ).format (
583- used_m3 = used_m3 ,
584- actual_mm = actual_mm
585- )
555+ ).format (used_m3 = used_m3 , actual_mm = actual_mm )
586556 )
587557 break
588558
@@ -599,7 +569,6 @@ async def handle_traditional_end(message):
599569 del user_irrigation_data [chat_id ]
600570
601571
602-
603572@bot .message_handler (func = lambda message : user_states .get (message .chat .id ) == 'waiting_for_counter_end' )
604573async def handle_counter_end (message ):
605574 chat_id = message .chat .id
@@ -627,13 +596,12 @@ async def handle_counter_end(message):
627596 area = float (row ['area' ])
628597 actual_mm = (used_m3 * float (row ['ie' ])) / (10 * area * float (row ['wa' ]))
629598
630- await execute_query (
631- """UPDATE WWCServices.Irrigation
632- SET irrigationApp = %s
633- WHERE siteID = %s
634- AND date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)""" ,
635- (actual_mm , row ['siteID' ])
636- )
599+ query = """
600+ UPDATE WWCServices.Irrigation SET irrigationApp = :actual_mm
601+ WHERE siteID = :siteID AND date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
602+ """
603+ values = {'actual_mm' : actual_mm , 'siteID' : row ['siteID' ]}
604+ await database .execute (query = query , values = values )
637605
638606 await bot .send_message (
639607 chat_id ,
@@ -660,7 +628,6 @@ async def handle_counter_end(message):
660628 del user_irrigation_data [chat_id ]
661629
662630
663-
664631@bot .message_handler (func = lambda message : user_states .get (message .chat .id ) == 'waiting_for_actual_data' )
665632async def handle_actual_data (message ):
666633 chat_id = message .chat .id
@@ -673,13 +640,12 @@ async def handle_actual_data(message):
673640 area = float (row ['area' ])
674641 actual_mm = (actual_m3 * float (row ['ie' ])) / (10 * area * float (row ['wa' ]))
675642
676- await execute_query (
677- """UPDATE WWCServices.Irrigation
678- SET irrigationApp = %s
679- WHERE siteID = %s
680- AND date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)""" ,
681- (actual_mm , row ['siteID' ])
682- )
643+ query = """
644+ UPDATE WWCServices.Irrigation SET irrigationApp = :actual_mm
645+ WHERE siteID = :siteID AND date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
646+ """
647+ values = {'actual_mm' : actual_mm , 'siteID' : row ['siteID' ]}
648+ await database .execute (query = query , values = values )
683649
684650 await bot .send_message (
685651 chat_id ,
@@ -735,23 +701,22 @@ async def send_recommendation(chat_id, fieldtype, irrigation_need, area, ie, wa,
735701
736702
737703async def main ():
738- # Очищаем старые задания при запуске
739- for job in scheduler .get_jobs ():
740- job .remove ()
741-
742- scheduler .add_job (
743- check_all_users ,
744- 'cron' ,
745- hour = 7 ,
746- minute = 0 , # Every day at 7 am
747- timezone = TIMEZONE ,
748- )
704+ await database .connect ()
705+
706+ # Clearing old tasks on startup
707+ # XXX Do we need this? It's AsyncIOScheduler so jobs should not persist
708+ scheduler .remove_all_jobs ()
709+
710+ # Check irrigation for all users, every day at 7 am
711+ scheduler .add_job (check_all_users , 'cron' , hour = 7 , minute = 0 , timezone = TIMEZONE )
749712 scheduler .start ()
750713
714+ # Start bot
751715 try :
752716 await bot .polling ()
753717 finally :
754718 scheduler .shutdown ()
719+ await database .disconnect ()
755720
756721
757722if __name__ == "__main__" :
0 commit comments