Merge pull request 'perf: cache user team lookup in player_autocomplete, reduce limit to 25' (#100) from ai/major-domo-v2#99 into next-release
Some checks failed
Build Docker Image / build (push) Has been cancelled
Some checks failed
Build Docker Image / build (push) Has been cancelled
Reviewed-on: #100
This commit is contained in:
commit
8862850c59
@ -3,10 +3,16 @@ Tests for shared autocomplete utility functions.
|
||||
|
||||
Validates the shared autocomplete functions used across multiple command modules.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from utils.autocomplete import player_autocomplete, team_autocomplete, major_league_team_autocomplete
|
||||
import utils.autocomplete
|
||||
from utils.autocomplete import (
|
||||
player_autocomplete,
|
||||
team_autocomplete,
|
||||
major_league_team_autocomplete,
|
||||
)
|
||||
from tests.factories import PlayerFactory, TeamFactory
|
||||
from models.team import RosterType
|
||||
|
||||
@ -14,6 +20,13 @@ from models.team import RosterType
|
||||
class TestPlayerAutocomplete:
|
||||
"""Test player autocomplete functionality."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clear_user_team_cache(self):
|
||||
"""Clear the module-level user team cache before each test to prevent interference."""
|
||||
utils.autocomplete._user_team_cache.clear()
|
||||
yield
|
||||
utils.autocomplete._user_team_cache.clear()
|
||||
|
||||
@pytest.fixture
|
||||
def mock_interaction(self):
|
||||
"""Create a mock Discord interaction."""
|
||||
@ -26,41 +39,43 @@ class TestPlayerAutocomplete:
|
||||
"""Test successful player autocomplete."""
|
||||
mock_players = [
|
||||
PlayerFactory.mike_trout(id=1),
|
||||
PlayerFactory.ronald_acuna(id=2)
|
||||
PlayerFactory.ronald_acuna(id=2),
|
||||
]
|
||||
|
||||
with patch('utils.autocomplete.player_service') as mock_service:
|
||||
with patch("utils.autocomplete.player_service") as mock_service:
|
||||
mock_service.search_players = AsyncMock(return_value=mock_players)
|
||||
|
||||
choices = await player_autocomplete(mock_interaction, 'Trout')
|
||||
choices = await player_autocomplete(mock_interaction, "Trout")
|
||||
|
||||
assert len(choices) == 2
|
||||
assert choices[0].name == 'Mike Trout (CF)'
|
||||
assert choices[0].value == 'Mike Trout'
|
||||
assert choices[1].name == 'Ronald Acuna Jr. (OF)'
|
||||
assert choices[1].value == 'Ronald Acuna Jr.'
|
||||
assert choices[0].name == "Mike Trout (CF)"
|
||||
assert choices[0].value == "Mike Trout"
|
||||
assert choices[1].name == "Ronald Acuna Jr. (OF)"
|
||||
assert choices[1].value == "Ronald Acuna Jr."
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_player_autocomplete_with_team_info(self, mock_interaction):
|
||||
"""Test player autocomplete with team information."""
|
||||
mock_team = TeamFactory.create(id=499, abbrev='LAA', sname='Angels', lname='Los Angeles Angels')
|
||||
mock_team = TeamFactory.create(
|
||||
id=499, abbrev="LAA", sname="Angels", lname="Los Angeles Angels"
|
||||
)
|
||||
mock_player = PlayerFactory.mike_trout(id=1)
|
||||
mock_player.team = mock_team
|
||||
|
||||
with patch('utils.autocomplete.player_service') as mock_service:
|
||||
with patch("utils.autocomplete.player_service") as mock_service:
|
||||
mock_service.search_players = AsyncMock(return_value=[mock_player])
|
||||
|
||||
choices = await player_autocomplete(mock_interaction, 'Trout')
|
||||
choices = await player_autocomplete(mock_interaction, "Trout")
|
||||
|
||||
assert len(choices) == 1
|
||||
assert choices[0].name == 'Mike Trout (CF - LAA)'
|
||||
assert choices[0].value == 'Mike Trout'
|
||||
assert choices[0].name == "Mike Trout (CF - LAA)"
|
||||
assert choices[0].value == "Mike Trout"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_player_autocomplete_prioritizes_user_team(self, mock_interaction):
|
||||
"""Test that user's team players are prioritized in autocomplete."""
|
||||
user_team = TeamFactory.create(id=1, abbrev='POR', sname='Loggers')
|
||||
other_team = TeamFactory.create(id=2, abbrev='LAA', sname='Angels')
|
||||
user_team = TeamFactory.create(id=1, abbrev="POR", sname="Loggers")
|
||||
other_team = TeamFactory.create(id=2, abbrev="LAA", sname="Angels")
|
||||
|
||||
# Create players - one from user's team, one from other team
|
||||
user_player = PlayerFactory.mike_trout(id=1)
|
||||
@ -71,32 +86,35 @@ class TestPlayerAutocomplete:
|
||||
other_player.team = other_team
|
||||
other_player.team_id = other_team.id
|
||||
|
||||
with patch('utils.autocomplete.player_service') as mock_service, \
|
||||
patch('utils.autocomplete.get_user_major_league_team') as mock_get_team:
|
||||
|
||||
mock_service.search_players = AsyncMock(return_value=[other_player, user_player])
|
||||
with (
|
||||
patch("utils.autocomplete.player_service") as mock_service,
|
||||
patch("utils.autocomplete.get_user_major_league_team") as mock_get_team,
|
||||
):
|
||||
mock_service.search_players = AsyncMock(
|
||||
return_value=[other_player, user_player]
|
||||
)
|
||||
mock_get_team.return_value = user_team
|
||||
|
||||
choices = await player_autocomplete(mock_interaction, 'player')
|
||||
choices = await player_autocomplete(mock_interaction, "player")
|
||||
|
||||
assert len(choices) == 2
|
||||
# User's team player should be first
|
||||
assert choices[0].name == 'Mike Trout (CF - POR)'
|
||||
assert choices[1].name == 'Ronald Acuna Jr. (OF - LAA)'
|
||||
assert choices[0].name == "Mike Trout (CF - POR)"
|
||||
assert choices[1].name == "Ronald Acuna Jr. (OF - LAA)"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_player_autocomplete_short_input(self, mock_interaction):
|
||||
"""Test player autocomplete with short input returns empty."""
|
||||
choices = await player_autocomplete(mock_interaction, 'T')
|
||||
choices = await player_autocomplete(mock_interaction, "T")
|
||||
assert len(choices) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_player_autocomplete_error_handling(self, mock_interaction):
|
||||
"""Test player autocomplete error handling."""
|
||||
with patch('utils.autocomplete.player_service') as mock_service:
|
||||
with patch("utils.autocomplete.player_service") as mock_service:
|
||||
mock_service.search_players.side_effect = Exception("API Error")
|
||||
|
||||
choices = await player_autocomplete(mock_interaction, 'Trout')
|
||||
choices = await player_autocomplete(mock_interaction, "Trout")
|
||||
assert len(choices) == 0
|
||||
|
||||
|
||||
@ -114,35 +132,35 @@ class TestTeamAutocomplete:
|
||||
async def test_team_autocomplete_success(self, mock_interaction):
|
||||
"""Test successful team autocomplete."""
|
||||
mock_teams = [
|
||||
TeamFactory.create(id=1, abbrev='LAA', sname='Angels'),
|
||||
TeamFactory.create(id=2, abbrev='LAAMIL', sname='Salt Lake Bees'),
|
||||
TeamFactory.create(id=3, abbrev='LAAAIL', sname='Angels IL'),
|
||||
TeamFactory.create(id=4, abbrev='POR', sname='Loggers')
|
||||
TeamFactory.create(id=1, abbrev="LAA", sname="Angels"),
|
||||
TeamFactory.create(id=2, abbrev="LAAMIL", sname="Salt Lake Bees"),
|
||||
TeamFactory.create(id=3, abbrev="LAAAIL", sname="Angels IL"),
|
||||
TeamFactory.create(id=4, abbrev="POR", sname="Loggers"),
|
||||
]
|
||||
|
||||
with patch('utils.autocomplete.team_service') as mock_service:
|
||||
with patch("utils.autocomplete.team_service") as mock_service:
|
||||
mock_service.get_teams_by_season = AsyncMock(return_value=mock_teams)
|
||||
|
||||
choices = await team_autocomplete(mock_interaction, 'la')
|
||||
choices = await team_autocomplete(mock_interaction, "la")
|
||||
|
||||
assert len(choices) == 3 # All teams with 'la' in abbrev or sname
|
||||
assert any('LAA' in choice.name for choice in choices)
|
||||
assert any('LAAMIL' in choice.name for choice in choices)
|
||||
assert any('LAAAIL' in choice.name for choice in choices)
|
||||
assert any("LAA" in choice.name for choice in choices)
|
||||
assert any("LAAMIL" in choice.name for choice in choices)
|
||||
assert any("LAAAIL" in choice.name for choice in choices)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_team_autocomplete_short_input(self, mock_interaction):
|
||||
"""Test team autocomplete with very short input."""
|
||||
choices = await team_autocomplete(mock_interaction, '')
|
||||
choices = await team_autocomplete(mock_interaction, "")
|
||||
assert len(choices) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_team_autocomplete_error_handling(self, mock_interaction):
|
||||
"""Test team autocomplete error handling."""
|
||||
with patch('utils.autocomplete.team_service') as mock_service:
|
||||
with patch("utils.autocomplete.team_service") as mock_service:
|
||||
mock_service.get_teams_by_season.side_effect = Exception("API Error")
|
||||
|
||||
choices = await team_autocomplete(mock_interaction, 'LAA')
|
||||
choices = await team_autocomplete(mock_interaction, "LAA")
|
||||
assert len(choices) == 0
|
||||
|
||||
|
||||
@ -157,101 +175,197 @@ class TestMajorLeagueTeamAutocomplete:
|
||||
return interaction
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_major_league_team_autocomplete_filters_correctly(self, mock_interaction):
|
||||
async def test_major_league_team_autocomplete_filters_correctly(
|
||||
self, mock_interaction
|
||||
):
|
||||
"""Test that only major league teams are returned."""
|
||||
# Create teams with different roster types
|
||||
mock_teams = [
|
||||
TeamFactory.create(id=1, abbrev='LAA', sname='Angels'), # ML
|
||||
TeamFactory.create(id=2, abbrev='LAAMIL', sname='Salt Lake Bees'), # MiL
|
||||
TeamFactory.create(id=3, abbrev='LAAAIL', sname='Angels IL'), # IL
|
||||
TeamFactory.create(id=4, abbrev='FA', sname='Free Agents'), # FA
|
||||
TeamFactory.create(id=5, abbrev='POR', sname='Loggers'), # ML
|
||||
TeamFactory.create(id=6, abbrev='PORMIL', sname='Portland MiL'), # MiL
|
||||
TeamFactory.create(id=1, abbrev="LAA", sname="Angels"), # ML
|
||||
TeamFactory.create(id=2, abbrev="LAAMIL", sname="Salt Lake Bees"), # MiL
|
||||
TeamFactory.create(id=3, abbrev="LAAAIL", sname="Angels IL"), # IL
|
||||
TeamFactory.create(id=4, abbrev="FA", sname="Free Agents"), # FA
|
||||
TeamFactory.create(id=5, abbrev="POR", sname="Loggers"), # ML
|
||||
TeamFactory.create(id=6, abbrev="PORMIL", sname="Portland MiL"), # MiL
|
||||
]
|
||||
|
||||
with patch('utils.autocomplete.team_service') as mock_service:
|
||||
with patch("utils.autocomplete.team_service") as mock_service:
|
||||
mock_service.get_teams_by_season = AsyncMock(return_value=mock_teams)
|
||||
|
||||
choices = await major_league_team_autocomplete(mock_interaction, 'l')
|
||||
choices = await major_league_team_autocomplete(mock_interaction, "l")
|
||||
|
||||
# Should only return major league teams that match 'l' (LAA, POR)
|
||||
choice_values = [choice.value for choice in choices]
|
||||
assert 'LAA' in choice_values
|
||||
assert 'POR' in choice_values
|
||||
assert "LAA" in choice_values
|
||||
assert "POR" in choice_values
|
||||
assert len(choice_values) == 2
|
||||
# Should NOT include MiL, IL, or FA teams
|
||||
assert 'LAAMIL' not in choice_values
|
||||
assert 'LAAAIL' not in choice_values
|
||||
assert 'FA' not in choice_values
|
||||
assert 'PORMIL' not in choice_values
|
||||
assert "LAAMIL" not in choice_values
|
||||
assert "LAAAIL" not in choice_values
|
||||
assert "FA" not in choice_values
|
||||
assert "PORMIL" not in choice_values
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_major_league_team_autocomplete_matching(self, mock_interaction):
|
||||
"""Test search matching on abbreviation and short name."""
|
||||
mock_teams = [
|
||||
TeamFactory.create(id=1, abbrev='LAA', sname='Angels'),
|
||||
TeamFactory.create(id=2, abbrev='LAD', sname='Dodgers'),
|
||||
TeamFactory.create(id=3, abbrev='POR', sname='Loggers'),
|
||||
TeamFactory.create(id=4, abbrev='BOS', sname='Red Sox'),
|
||||
TeamFactory.create(id=1, abbrev="LAA", sname="Angels"),
|
||||
TeamFactory.create(id=2, abbrev="LAD", sname="Dodgers"),
|
||||
TeamFactory.create(id=3, abbrev="POR", sname="Loggers"),
|
||||
TeamFactory.create(id=4, abbrev="BOS", sname="Red Sox"),
|
||||
]
|
||||
|
||||
with patch('utils.autocomplete.team_service') as mock_service:
|
||||
with patch("utils.autocomplete.team_service") as mock_service:
|
||||
mock_service.get_teams_by_season = AsyncMock(return_value=mock_teams)
|
||||
|
||||
# Test abbreviation matching
|
||||
choices = await major_league_team_autocomplete(mock_interaction, 'la')
|
||||
choices = await major_league_team_autocomplete(mock_interaction, "la")
|
||||
assert len(choices) == 2 # LAA and LAD
|
||||
choice_values = [choice.value for choice in choices]
|
||||
assert 'LAA' in choice_values
|
||||
assert 'LAD' in choice_values
|
||||
assert "LAA" in choice_values
|
||||
assert "LAD" in choice_values
|
||||
|
||||
# Test short name matching
|
||||
choices = await major_league_team_autocomplete(mock_interaction, 'red')
|
||||
choices = await major_league_team_autocomplete(mock_interaction, "red")
|
||||
assert len(choices) == 1
|
||||
assert choices[0].value == 'BOS'
|
||||
assert choices[0].value == "BOS"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_major_league_team_autocomplete_short_input(self, mock_interaction):
|
||||
"""Test major league team autocomplete with very short input."""
|
||||
choices = await major_league_team_autocomplete(mock_interaction, '')
|
||||
choices = await major_league_team_autocomplete(mock_interaction, "")
|
||||
assert len(choices) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_major_league_team_autocomplete_error_handling(self, mock_interaction):
|
||||
async def test_major_league_team_autocomplete_error_handling(
|
||||
self, mock_interaction
|
||||
):
|
||||
"""Test major league team autocomplete error handling."""
|
||||
with patch('utils.autocomplete.team_service') as mock_service:
|
||||
with patch("utils.autocomplete.team_service") as mock_service:
|
||||
mock_service.get_teams_by_season.side_effect = Exception("API Error")
|
||||
|
||||
choices = await major_league_team_autocomplete(mock_interaction, 'LAA')
|
||||
choices = await major_league_team_autocomplete(mock_interaction, "LAA")
|
||||
assert len(choices) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_major_league_team_autocomplete_roster_type_detection(self, mock_interaction):
|
||||
async def test_major_league_team_autocomplete_roster_type_detection(
|
||||
self, mock_interaction
|
||||
):
|
||||
"""Test that roster type detection works correctly for edge cases."""
|
||||
# Test edge cases like teams whose abbreviation ends in 'M' + 'IL'
|
||||
mock_teams = [
|
||||
TeamFactory.create(id=1, abbrev='BHM', sname='Iron'), # ML team ending in 'M'
|
||||
TeamFactory.create(id=2, abbrev='BHMIL', sname='Iron IL'), # IL team (BHM + IL)
|
||||
TeamFactory.create(id=3, abbrev='NYYMIL', sname='Staten Island RailRiders'), # MiL team (NYY + MIL)
|
||||
TeamFactory.create(id=4, abbrev='NYY', sname='Yankees'), # ML team
|
||||
TeamFactory.create(
|
||||
id=1, abbrev="BHM", sname="Iron"
|
||||
), # ML team ending in 'M'
|
||||
TeamFactory.create(
|
||||
id=2, abbrev="BHMIL", sname="Iron IL"
|
||||
), # IL team (BHM + IL)
|
||||
TeamFactory.create(
|
||||
id=3, abbrev="NYYMIL", sname="Staten Island RailRiders"
|
||||
), # MiL team (NYY + MIL)
|
||||
TeamFactory.create(id=4, abbrev="NYY", sname="Yankees"), # ML team
|
||||
]
|
||||
|
||||
with patch('utils.autocomplete.team_service') as mock_service:
|
||||
with patch("utils.autocomplete.team_service") as mock_service:
|
||||
mock_service.get_teams_by_season = AsyncMock(return_value=mock_teams)
|
||||
|
||||
choices = await major_league_team_autocomplete(mock_interaction, 'b')
|
||||
choices = await major_league_team_autocomplete(mock_interaction, "b")
|
||||
|
||||
# Should only return major league teams
|
||||
choice_values = [choice.value for choice in choices]
|
||||
assert 'BHM' in choice_values # Major league team
|
||||
assert 'BHMIL' not in choice_values # Should be detected as IL, not MiL
|
||||
assert 'NYYMIL' not in choice_values # Minor league team
|
||||
assert "BHM" in choice_values # Major league team
|
||||
assert "BHMIL" not in choice_values # Should be detected as IL, not MiL
|
||||
assert "NYYMIL" not in choice_values # Minor league team
|
||||
|
||||
# Verify the roster type detection is working
|
||||
bhm_team = next(t for t in mock_teams if t.abbrev == 'BHM')
|
||||
bhmil_team = next(t for t in mock_teams if t.abbrev == 'BHMIL')
|
||||
nyymil_team = next(t for t in mock_teams if t.abbrev == 'NYYMIL')
|
||||
bhm_team = next(t for t in mock_teams if t.abbrev == "BHM")
|
||||
bhmil_team = next(t for t in mock_teams if t.abbrev == "BHMIL")
|
||||
nyymil_team = next(t for t in mock_teams if t.abbrev == "NYYMIL")
|
||||
|
||||
assert bhm_team.roster_type() == RosterType.MAJOR_LEAGUE
|
||||
assert bhmil_team.roster_type() == RosterType.INJURED_LIST
|
||||
assert nyymil_team.roster_type() == RosterType.MINOR_LEAGUE
|
||||
assert nyymil_team.roster_type() == RosterType.MINOR_LEAGUE
|
||||
|
||||
|
||||
class TestGetCachedUserTeam:
|
||||
"""Test the _get_cached_user_team caching helper.
|
||||
|
||||
Verifies that the cache avoids redundant get_user_major_league_team calls
|
||||
on repeated invocations within the TTL window, and that expired entries are
|
||||
re-fetched.
|
||||
"""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clear_cache(self):
|
||||
"""Isolate each test from cache state left by other tests."""
|
||||
utils.autocomplete._user_team_cache.clear()
|
||||
yield
|
||||
utils.autocomplete._user_team_cache.clear()
|
||||
|
||||
@pytest.fixture
|
||||
def mock_interaction(self):
|
||||
interaction = MagicMock()
|
||||
interaction.user.id = 99999
|
||||
return interaction
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_caches_result_on_first_call(self, mock_interaction):
|
||||
"""First call populates the cache; API function called exactly once."""
|
||||
user_team = TeamFactory.create(id=1, abbrev="POR", sname="Loggers")
|
||||
|
||||
with patch(
|
||||
"utils.autocomplete.get_user_major_league_team", new_callable=AsyncMock
|
||||
) as mock_get_team:
|
||||
mock_get_team.return_value = user_team
|
||||
|
||||
from utils.autocomplete import _get_cached_user_team
|
||||
|
||||
result1 = await _get_cached_user_team(mock_interaction)
|
||||
result2 = await _get_cached_user_team(mock_interaction)
|
||||
|
||||
assert result1 is user_team
|
||||
assert result2 is user_team
|
||||
# API called only once despite two invocations
|
||||
mock_get_team.assert_called_once_with(99999)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_re_fetches_after_ttl_expires(self, mock_interaction):
|
||||
"""Expired cache entries cause a fresh API call."""
|
||||
import time
|
||||
|
||||
user_team = TeamFactory.create(id=1, abbrev="POR", sname="Loggers")
|
||||
|
||||
with patch(
|
||||
"utils.autocomplete.get_user_major_league_team", new_callable=AsyncMock
|
||||
) as mock_get_team:
|
||||
mock_get_team.return_value = user_team
|
||||
|
||||
from utils.autocomplete import _get_cached_user_team, _USER_TEAM_CACHE_TTL
|
||||
|
||||
# Seed the cache with a timestamp that is already expired
|
||||
utils.autocomplete._user_team_cache[99999] = (
|
||||
user_team,
|
||||
time.time() - _USER_TEAM_CACHE_TTL - 1,
|
||||
)
|
||||
|
||||
await _get_cached_user_team(mock_interaction)
|
||||
|
||||
# Should have called the API to refresh the stale entry
|
||||
mock_get_team.assert_called_once_with(99999)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_caches_none_result(self, mock_interaction):
|
||||
"""None (user has no team) is cached to avoid repeated API calls."""
|
||||
with patch(
|
||||
"utils.autocomplete.get_user_major_league_team", new_callable=AsyncMock
|
||||
) as mock_get_team:
|
||||
mock_get_team.return_value = None
|
||||
|
||||
from utils.autocomplete import _get_cached_user_team
|
||||
|
||||
result1 = await _get_cached_user_team(mock_interaction)
|
||||
result2 = await _get_cached_user_team(mock_interaction)
|
||||
|
||||
assert result1 is None
|
||||
assert result2 is None
|
||||
mock_get_team.assert_called_once()
|
||||
|
||||
@ -4,16 +4,33 @@ Autocomplete Utilities
|
||||
Shared autocomplete functions for Discord slash commands.
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
import time
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
import discord
|
||||
from discord import app_commands
|
||||
|
||||
from config import get_config
|
||||
from models.team import RosterType
|
||||
from models.team import RosterType, Team
|
||||
from services.player_service import player_service
|
||||
from services.team_service import team_service
|
||||
from utils.team_utils import get_user_major_league_team
|
||||
|
||||
# Cache for user team lookups: user_id -> (team, cached_at)
|
||||
_user_team_cache: Dict[int, Tuple[Optional[Team], float]] = {}
|
||||
_USER_TEAM_CACHE_TTL = 60 # seconds
|
||||
|
||||
|
||||
async def _get_cached_user_team(interaction: discord.Interaction) -> Optional[Team]:
|
||||
"""Return the user's major league team, cached for 60 seconds per user."""
|
||||
user_id = interaction.user.id
|
||||
if user_id in _user_team_cache:
|
||||
team, cached_at = _user_team_cache[user_id]
|
||||
if time.time() - cached_at < _USER_TEAM_CACHE_TTL:
|
||||
return team
|
||||
team = await get_user_major_league_team(user_id)
|
||||
_user_team_cache[user_id] = (team, time.time())
|
||||
return team
|
||||
|
||||
|
||||
async def player_autocomplete(
|
||||
interaction: discord.Interaction, current: str
|
||||
@ -34,12 +51,12 @@ async def player_autocomplete(
|
||||
return []
|
||||
|
||||
try:
|
||||
# Get user's team for prioritization
|
||||
user_team = await get_user_major_league_team(interaction.user.id)
|
||||
# Get user's team for prioritization (cached per user, 60s TTL)
|
||||
user_team = await _get_cached_user_team(interaction)
|
||||
|
||||
# Search for players using the search endpoint
|
||||
players = await player_service.search_players(
|
||||
current, limit=50, season=get_config().sba_season
|
||||
current, limit=25, season=get_config().sba_season
|
||||
)
|
||||
|
||||
# Separate players by team (user's team vs others)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user