added audio output for demo songs since spotify api doesnt support audio streaming
This commit is contained in:
parent
6b93f1206d
commit
a91654df03
14 changed files with 1261 additions and 77 deletions
|
@ -0,0 +1,243 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class SpamProtectionService extends ChangeNotifier {
|
||||
// Vote limits per user
|
||||
static const int maxVotesPerHour = 20;
|
||||
static const int maxVotesPerMinute = 5;
|
||||
static const int maxSuggestionsPerHour = 10;
|
||||
static const int maxSuggestionsPerMinute = 3;
|
||||
|
||||
// Cooldown periods (in seconds)
|
||||
static const int voteCooldown = 2;
|
||||
static const int suggestionCooldown = 10;
|
||||
|
||||
// User activity tracking
|
||||
final Map<String, List<DateTime>> _userVotes = {};
|
||||
final Map<String, List<DateTime>> _userSuggestions = {};
|
||||
final Map<String, DateTime> _lastVoteTime = {};
|
||||
final Map<String, DateTime> _lastSuggestionTime = {};
|
||||
final Map<String, int> _consecutiveActions = {};
|
||||
|
||||
// Blocked users (temporary)
|
||||
final Map<String, DateTime> _blockedUsers = {};
|
||||
|
||||
String getCurrentUserId() {
|
||||
// In a real app, this would come from authentication
|
||||
return 'current_user_${DateTime.now().millisecondsSinceEpoch ~/ 1000000}';
|
||||
}
|
||||
|
||||
// Check if user can vote
|
||||
bool canVote(String userId) {
|
||||
// Check if user is temporarily blocked
|
||||
if (_isUserBlocked(userId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check cooldown
|
||||
if (_isOnCooldown(userId, _lastVoteTime, voteCooldown)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check rate limits
|
||||
if (!_checkRateLimit(userId, _userVotes, maxVotesPerMinute, maxVotesPerHour)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if user can suggest songs
|
||||
bool canSuggest(String userId) {
|
||||
// Check if user is temporarily blocked
|
||||
if (_isUserBlocked(userId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check cooldown
|
||||
if (_isOnCooldown(userId, _lastSuggestionTime, suggestionCooldown)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check rate limits
|
||||
if (!_checkRateLimit(userId, _userSuggestions, maxSuggestionsPerMinute, maxSuggestionsPerHour)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Record a vote action
|
||||
void recordVote(String userId) {
|
||||
final now = DateTime.now();
|
||||
|
||||
// Add to vote history
|
||||
_userVotes.putIfAbsent(userId, () => []).add(now);
|
||||
_lastVoteTime[userId] = now;
|
||||
|
||||
// Track consecutive actions for spam detection
|
||||
_incrementConsecutiveActions(userId);
|
||||
|
||||
// Clean old entries
|
||||
_cleanOldEntries(_userVotes[userId]!);
|
||||
|
||||
// Check for suspicious behavior
|
||||
_checkForSpam(userId);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Record a suggestion action
|
||||
void recordSuggestion(String userId) {
|
||||
final now = DateTime.now();
|
||||
|
||||
// Add to suggestion history
|
||||
_userSuggestions.putIfAbsent(userId, () => []).add(now);
|
||||
_lastSuggestionTime[userId] = now;
|
||||
|
||||
// Track consecutive actions for spam detection
|
||||
_incrementConsecutiveActions(userId);
|
||||
|
||||
// Clean old entries
|
||||
_cleanOldEntries(_userSuggestions[userId]!);
|
||||
|
||||
// Check for suspicious behavior
|
||||
_checkForSpam(userId);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Get remaining cooldown time in seconds
|
||||
int getVoteCooldownRemaining(String userId) {
|
||||
final lastVote = _lastVoteTime[userId];
|
||||
if (lastVote == null) return 0;
|
||||
|
||||
final elapsed = DateTime.now().difference(lastVote).inSeconds;
|
||||
return (voteCooldown - elapsed).clamp(0, voteCooldown);
|
||||
}
|
||||
|
||||
int getSuggestionCooldownRemaining(String userId) {
|
||||
final lastSuggestion = _lastSuggestionTime[userId];
|
||||
if (lastSuggestion == null) return 0;
|
||||
|
||||
final elapsed = DateTime.now().difference(lastSuggestion).inSeconds;
|
||||
return (suggestionCooldown - elapsed).clamp(0, suggestionCooldown);
|
||||
}
|
||||
|
||||
// Get user activity stats
|
||||
Map<String, int> getUserStats(String userId) {
|
||||
final now = DateTime.now();
|
||||
final hourAgo = now.subtract(const Duration(hours: 1));
|
||||
final minuteAgo = now.subtract(const Duration(minutes: 1));
|
||||
|
||||
final votesThisHour = _userVotes[userId]?.where((time) => time.isAfter(hourAgo)).length ?? 0;
|
||||
final votesThisMinute = _userVotes[userId]?.where((time) => time.isAfter(minuteAgo)).length ?? 0;
|
||||
final suggestionsThisHour = _userSuggestions[userId]?.where((time) => time.isAfter(hourAgo)).length ?? 0;
|
||||
final suggestionsThisMinute = _userSuggestions[userId]?.where((time) => time.isAfter(minuteAgo)).length ?? 0;
|
||||
|
||||
return {
|
||||
'votesThisHour': votesThisHour,
|
||||
'votesThisMinute': votesThisMinute,
|
||||
'suggestionsThisHour': suggestionsThisHour,
|
||||
'suggestionsThisMinute': suggestionsThisMinute,
|
||||
'maxVotesPerHour': maxVotesPerHour,
|
||||
'maxVotesPerMinute': maxVotesPerMinute,
|
||||
'maxSuggestionsPerHour': maxSuggestionsPerHour,
|
||||
'maxSuggestionsPerMinute': maxSuggestionsPerMinute,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if user is blocked
|
||||
bool _isUserBlocked(String userId) {
|
||||
final blockTime = _blockedUsers[userId];
|
||||
if (blockTime == null) return false;
|
||||
|
||||
// Unblock after 5 minutes
|
||||
if (DateTime.now().difference(blockTime).inMinutes >= 5) {
|
||||
_blockedUsers.remove(userId);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check cooldown
|
||||
bool _isOnCooldown(String userId, Map<String, DateTime> lastActionTime, int cooldownSeconds) {
|
||||
final lastAction = lastActionTime[userId];
|
||||
if (lastAction == null) return false;
|
||||
|
||||
return DateTime.now().difference(lastAction).inSeconds < cooldownSeconds;
|
||||
}
|
||||
|
||||
// Check rate limits
|
||||
bool _checkRateLimit(String userId, Map<String, List<DateTime>> userActions, int maxPerMinute, int maxPerHour) {
|
||||
final actions = userActions[userId] ?? [];
|
||||
final now = DateTime.now();
|
||||
|
||||
// Count actions in the last minute
|
||||
final actionsLastMinute = actions.where((time) =>
|
||||
now.difference(time).inMinutes < 1).length;
|
||||
if (actionsLastMinute >= maxPerMinute) return false;
|
||||
|
||||
// Count actions in the last hour
|
||||
final actionsLastHour = actions.where((time) =>
|
||||
now.difference(time).inHours < 1).length;
|
||||
if (actionsLastHour >= maxPerHour) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clean old entries (older than 1 hour)
|
||||
void _cleanOldEntries(List<DateTime> entries) {
|
||||
final oneHourAgo = DateTime.now().subtract(const Duration(hours: 1));
|
||||
entries.removeWhere((time) => time.isBefore(oneHourAgo));
|
||||
}
|
||||
|
||||
// Track consecutive actions for spam detection
|
||||
void _incrementConsecutiveActions(String userId) {
|
||||
_consecutiveActions[userId] = (_consecutiveActions[userId] ?? 0) + 1;
|
||||
|
||||
// Reset after some time of inactivity
|
||||
Timer(const Duration(seconds: 30), () {
|
||||
_consecutiveActions[userId] = 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Check for spam behavior and block if necessary
|
||||
void _checkForSpam(String userId) {
|
||||
final consecutive = _consecutiveActions[userId] ?? 0;
|
||||
|
||||
// Block user if too many consecutive actions
|
||||
if (consecutive > 15) {
|
||||
_blockedUsers[userId] = DateTime.now();
|
||||
if (kDebugMode) {
|
||||
print('User $userId temporarily blocked for spam behavior');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get block status message
|
||||
String? getBlockMessage(String userId) {
|
||||
final blockTime = _blockedUsers[userId];
|
||||
if (blockTime == null) return null;
|
||||
|
||||
final remaining = 5 - DateTime.now().difference(blockTime).inMinutes;
|
||||
if (remaining <= 0) {
|
||||
_blockedUsers.remove(userId);
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'You are temporarily blocked for $remaining more minutes due to excessive activity.';
|
||||
}
|
||||
|
||||
// Clear all data (for testing)
|
||||
void clearAllData() {
|
||||
_userVotes.clear();
|
||||
_userSuggestions.clear();
|
||||
_lastVoteTime.clear();
|
||||
_lastSuggestionTime.clear();
|
||||
_consecutiveActions.clear();
|
||||
_blockedUsers.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue