@@ -22,6 +22,15 @@ public async Task<string> CreateLoginStateAsync(
2222 TelegramChatType chatType ,
2323 CancellationToken ct = default )
2424 {
25+ // Remove any existing pending states for this chat
26+ var staleStates = await masterUow . Repository < TelegramLoginState > ( )
27+ . GetListAsync ( s => s . ChatId == chatId && ! s . IsConsumed , ct : ct ) ;
28+
29+ foreach ( var stale in staleStates )
30+ {
31+ masterUow . Repository < TelegramLoginState > ( ) . Delete ( stale ) ;
32+ }
33+
2534 var state = GenerateState ( ) ;
2635
2736 var loginState = new TelegramLoginState
@@ -37,6 +46,27 @@ public async Task<string> CreateLoginStateAsync(
3746 return state ;
3847 }
3948
49+ /// <summary>
50+ /// Removes expired and consumed login states older than the specified age.
51+ /// </summary>
52+ public async Task CleanupStaleStatesAsync ( CancellationToken ct = default )
53+ {
54+ var cutoff = DateTime . UtcNow . AddHours ( - 1 ) ;
55+ var staleStates = await masterUow . Repository < TelegramLoginState > ( )
56+ . GetListAsync ( s => s . ExpiresAt < cutoff , ct : ct ) ;
57+
58+ if ( staleStates . Count == 0 )
59+ return ;
60+
61+ foreach ( var state in staleStates )
62+ {
63+ masterUow . Repository < TelegramLoginState > ( ) . Delete ( state ) ;
64+ }
65+
66+ await masterUow . SaveChangesAsync ( ct ) ;
67+ logger . LogInformation ( "Cleaned up {Count} stale Telegram login states" , staleStates . Count ) ;
68+ }
69+
4070 /// <summary>
4171 /// Polls for a consumed login state. Returns the context if login is complete, null if still pending.
4272 /// </summary>
0 commit comments