22
33import asyncio
44import re
5- from typing import Optional , Dict , List , Tuple , Callable
5+ from typing import Optional , Dict , List , Tuple
66from datetime import datetime
77import logging
88
1111
1212class TCPConnection :
1313 """Manages a single TCP connection with buffer and trigger support."""
14-
14+
1515 def __init__ (self , connection_id : str , host : str , port : int ):
1616 self .connection_id = connection_id
1717 self .host = host
1818 self .port = port
1919 self .reader : Optional [asyncio .StreamReader ] = None
2020 self .writer : Optional [asyncio .StreamWriter ] = None
2121 self .buffer : List [bytes ] = []
22- self .triggers : Dict [str , Tuple [str , bytes ]] = {} # pattern -> (trigger_id, response)
22+ self .triggers : Dict [
23+ str , Tuple [str , bytes ]
24+ ] = {} # pattern -> (trigger_id, response)
2325 self .connected = False
2426 self .created_at = datetime .now ()
2527 self .bytes_sent = 0
2628 self .bytes_received = 0
2729 self ._read_task : Optional [asyncio .Task ] = None
2830 self ._lock = asyncio .Lock ()
29-
31+
3032 async def connect (self ) -> bool :
3133 """Establish TCP connection."""
3234 try :
33- self .reader , self .writer = await asyncio .open_connection (self .host , self .port )
35+ self .reader , self .writer = await asyncio .open_connection (
36+ self .host , self .port
37+ )
3438 self .connected = True
3539 # Start reading task
3640 self ._read_task = asyncio .create_task (self ._read_loop ())
37- logger .info (f"Connection { self .connection_id } established to { self .host } :{ self .port } " )
41+ logger .info (
42+ f"Connection { self .connection_id } established to { self .host } :{ self .port } "
43+ )
3844 return True
3945 except Exception as e :
4046 logger .error (f"Failed to connect { self .connection_id } : { e } " )
4147 self .connected = False
4248 return False
43-
49+
4450 async def disconnect (self ):
4551 """Close the TCP connection."""
4652 self .connected = False
47-
53+
4854 if self ._read_task :
4955 self ._read_task .cancel ()
5056 try :
5157 await self ._read_task
5258 except asyncio .CancelledError :
5359 pass
54-
60+
5561 if self .writer :
5662 try :
5763 self .writer .close ()
5864 await self .writer .wait_closed ()
5965 except Exception as e :
6066 logger .error (f"Error closing connection { self .connection_id } : { e } " )
61-
67+
6268 self .reader = None
6369 self .writer = None
6470 logger .info (f"Connection { self .connection_id } closed" )
65-
71+
6672 async def send (self , data : bytes ) -> bool :
6773 """Send data over the connection."""
6874 if not self .connected or not self .writer :
6975 return False
70-
76+
7177 try :
7278 self .writer .write (data )
7379 await self .writer .drain ()
@@ -78,7 +84,7 @@ async def send(self, data: bytes) -> bool:
7884 logger .error (f"Error sending data on { self .connection_id } : { e } " )
7985 self .connected = False
8086 return False
81-
87+
8288 async def _read_loop (self ):
8389 """Continuously read data from the connection."""
8490 while self .connected and self .reader :
@@ -88,33 +94,35 @@ async def _read_loop(self):
8894 logger .info (f"Connection { self .connection_id } closed by remote" )
8995 self .connected = False
9096 break
91-
97+
9298 async with self ._lock :
9399 self .buffer .append (data )
94100 self .bytes_received += len (data )
95-
101+
96102 # Check triggers
97103 await self ._check_triggers (data )
98-
104+
99105 except asyncio .CancelledError :
100106 break
101107 except Exception as e :
102108 logger .error (f"Error reading from { self .connection_id } : { e } " )
103109 self .connected = False
104110 break
105-
111+
106112 async def _check_triggers (self , data : bytes ):
107113 """Check if received data matches any triggers."""
108114 try :
109- data_str = data .decode (' utf-8' , errors = ' ignore' )
115+ data_str = data .decode (" utf-8" , errors = " ignore" )
110116 for pattern , (trigger_id , response ) in self .triggers .items ():
111117 if re .search (pattern , data_str ):
112118 logger .info (f"Trigger { trigger_id } matched on { self .connection_id } " )
113119 await self .send (response )
114120 except Exception as e :
115121 logger .error (f"Error checking triggers: { e } " )
116-
117- async def read_buffer (self , index : Optional [int ] = None , count : Optional [int ] = None ) -> List [bytes ]:
122+
123+ async def read_buffer (
124+ self , index : Optional [int ] = None , count : Optional [int ] = None
125+ ) -> List [bytes ]:
118126 """Read data from buffer."""
119127 async with self ._lock :
120128 if index is None :
@@ -126,13 +134,17 @@ async def read_buffer(self, index: Optional[int] = None, count: Optional[int] =
126134 else :
127135 # Return specific range
128136 end_index = min (index + count , len (self .buffer ))
129- return self .buffer [index :end_index ].copy () if index < len (self .buffer ) else []
130-
137+ return (
138+ self .buffer [index :end_index ].copy ()
139+ if index < len (self .buffer )
140+ else []
141+ )
142+
131143 async def clear_buffer (self ):
132144 """Clear the buffer."""
133145 async with self ._lock :
134146 self .buffer .clear ()
135-
147+
136148 async def get_buffer_info (self ) -> Dict :
137149 """Get information about the buffer."""
138150 async with self ._lock :
@@ -143,28 +155,24 @@ async def get_buffer_info(self) -> Dict:
143155 "total_bytes" : total_bytes ,
144156 "bytes_sent" : self .bytes_sent ,
145157 "bytes_received" : self .bytes_received ,
146- "connected" : self .connected
158+ "connected" : self .connected ,
147159 }
148-
160+
149161 def add_trigger (self , trigger_id : str , pattern : str , response : bytes ):
150162 """Add a trigger pattern and auto-response."""
151163 self .triggers [pattern ] = (trigger_id , response )
152-
164+
153165 def remove_trigger (self , trigger_id : str ) -> bool :
154166 """Remove a trigger by ID."""
155167 for pattern , (tid , _ ) in list (self .triggers .items ()):
156168 if tid == trigger_id :
157169 del self .triggers [pattern ]
158170 return True
159171 return False
160-
172+
161173 def get_triggers (self ) -> List [Dict ]:
162174 """Get all triggers for this connection."""
163175 return [
164- {
165- "trigger_id" : tid ,
166- "pattern" : pattern ,
167- "response_size" : len (response )
168- }
176+ {"trigger_id" : tid , "pattern" : pattern , "response_size" : len (response )}
169177 for pattern , (tid , response ) in self .triggers .items ()
170- ]
178+ ]
0 commit comments