Implement comprehensive IGDB integration for real-time game information
## Major Features Added - **Enhanced igdblib.py**: * Added search_games() method with fuzzy game search * Added get_game_details() for comprehensive game information * Added AI-friendly data formatting with _format_game_for_ai() * Added OpenAI function definitions via get_openai_functions() - **OpenAI Function Calling Integration**: * Modified OpenAIResponder to support function calling * Added IGDB function execution with _execute_igdb_function() * Backward compatible - gracefully falls back if IGDB unavailable * Auto-detects gaming queries and fetches real-time data - **Configuration & Setup**: * Added IGDB configuration options to config.toml * Updated system prompt to inform AI of gaming capabilities * Added comprehensive IGDB_SETUP.md documentation * Graceful initialization with proper error handling ## Technical Implementation - **Function Calling**: Uses OpenAI's tools/function calling API - **Smart Game Search**: Includes ratings, platforms, developers, genres - **Error Handling**: Robust fallbacks and logging - **Data Formatting**: Optimized for AI comprehension and user presentation - **Rate Limiting**: Respects IGDB API limits ## Usage Users can now ask natural gaming questions: - "Tell me about Elden Ring" - "What are good RPG games from 2023?" - "Is Cyberpunk 2077 on PlayStation?" The AI automatically detects gaming queries, calls IGDB API, and presents accurate, real-time game information seamlessly. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from fjerkroa_bot.igdblib import IGDBQuery
|
||||
from fjerkroa_bot.openai_responder import OpenAIResponder
|
||||
|
||||
|
||||
class TestIGDBIntegration(unittest.IsolatedAsyncioTestCase):
|
||||
"""Test IGDB integration with OpenAI responder."""
|
||||
|
||||
def setUp(self):
|
||||
self.config_with_igdb = {
|
||||
"openai-key": "test_key",
|
||||
"model": "gpt-4",
|
||||
"enable-game-info": True,
|
||||
"igdb-client-id": "test_client",
|
||||
"igdb-access-token": "test_token"
|
||||
}
|
||||
|
||||
self.config_without_igdb = {
|
||||
"openai-key": "test_key",
|
||||
"model": "gpt-4",
|
||||
"enable-game-info": False
|
||||
}
|
||||
|
||||
def test_igdb_initialization_enabled(self):
|
||||
"""Test IGDB is initialized when enabled in config."""
|
||||
with patch('fjerkroa_bot.openai_responder.IGDBQuery') as mock_igdb:
|
||||
mock_igdb_instance = Mock()
|
||||
mock_igdb.return_value = mock_igdb_instance
|
||||
|
||||
responder = OpenAIResponder(self.config_with_igdb)
|
||||
|
||||
mock_igdb.assert_called_once_with("test_client", "test_token")
|
||||
self.assertEqual(responder.igdb, mock_igdb_instance)
|
||||
|
||||
def test_igdb_initialization_disabled(self):
|
||||
"""Test IGDB is not initialized when disabled."""
|
||||
responder = OpenAIResponder(self.config_without_igdb)
|
||||
self.assertIsNone(responder.igdb)
|
||||
|
||||
def test_igdb_search_games_functionality(self):
|
||||
"""Test the search_games functionality."""
|
||||
igdb = IGDBQuery("test_client", "test_token")
|
||||
|
||||
# Mock the actual API call
|
||||
mock_games = [{
|
||||
"id": 1,
|
||||
"name": "Test Game",
|
||||
"summary": "A test game",
|
||||
"first_release_date": 1577836800, # 2020-01-01
|
||||
"genres": [{"name": "Action"}],
|
||||
"platforms": [{"name": "PC"}],
|
||||
"rating": 85.5
|
||||
}]
|
||||
|
||||
with patch.object(igdb, 'generalized_igdb_query', return_value=mock_games):
|
||||
results = igdb.search_games("Test Game")
|
||||
|
||||
self.assertIsNotNone(results)
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0]["name"], "Test Game")
|
||||
self.assertIn("genres", results[0])
|
||||
self.assertIn("platforms", results[0])
|
||||
|
||||
def test_igdb_openai_functions(self):
|
||||
"""Test OpenAI function definitions."""
|
||||
igdb = IGDBQuery("test_client", "test_token")
|
||||
functions = igdb.get_openai_functions()
|
||||
|
||||
self.assertEqual(len(functions), 2)
|
||||
|
||||
# Check search_games function
|
||||
search_func = functions[0]
|
||||
self.assertEqual(search_func["name"], "search_games")
|
||||
self.assertIn("description", search_func)
|
||||
self.assertIn("parameters", search_func)
|
||||
self.assertIn("query", search_func["parameters"]["properties"])
|
||||
|
||||
# Check get_game_details function
|
||||
details_func = functions[1]
|
||||
self.assertEqual(details_func["name"], "get_game_details")
|
||||
self.assertIn("game_id", details_func["parameters"]["properties"])
|
||||
|
||||
async def test_execute_igdb_function_search(self):
|
||||
"""Test executing IGDB search function."""
|
||||
with patch('fjerkroa_bot.openai_responder.IGDBQuery') as mock_igdb_class:
|
||||
mock_igdb = Mock()
|
||||
mock_igdb.search_games.return_value = [{"name": "Test Game", "id": 1}]
|
||||
mock_igdb_class.return_value = mock_igdb
|
||||
|
||||
responder = OpenAIResponder(self.config_with_igdb)
|
||||
|
||||
result = await responder._execute_igdb_function(
|
||||
"search_games",
|
||||
{"query": "Test Game", "limit": 5}
|
||||
)
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertIn("games", result)
|
||||
mock_igdb.search_games.assert_called_once_with("Test Game", 5)
|
||||
|
||||
async def test_execute_igdb_function_details(self):
|
||||
"""Test executing IGDB game details function."""
|
||||
with patch('fjerkroa_bot.openai_responder.IGDBQuery') as mock_igdb_class:
|
||||
mock_igdb = Mock()
|
||||
mock_igdb.get_game_details.return_value = {"name": "Test Game", "id": 1}
|
||||
mock_igdb_class.return_value = mock_igdb
|
||||
|
||||
responder = OpenAIResponder(self.config_with_igdb)
|
||||
|
||||
result = await responder._execute_igdb_function(
|
||||
"get_game_details",
|
||||
{"game_id": 1}
|
||||
)
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertIn("game", result)
|
||||
mock_igdb.get_game_details.assert_called_once_with(1)
|
||||
|
||||
def test_format_game_for_ai(self):
|
||||
"""Test game data formatting for AI consumption."""
|
||||
igdb = IGDBQuery("test_client", "test_token")
|
||||
|
||||
mock_game = {
|
||||
"id": 1,
|
||||
"name": "Elden Ring",
|
||||
"summary": "A fantasy action RPG",
|
||||
"first_release_date": 1645747200, # 2022-02-25
|
||||
"rating": 96.0,
|
||||
"aggregated_rating": 90.5,
|
||||
"genres": [{"name": "Role-playing (RPG)"}, {"name": "Adventure"}],
|
||||
"platforms": [{"name": "PC (Microsoft Windows)"}, {"name": "PlayStation 5"}],
|
||||
"developers": [{"name": "FromSoftware"}]
|
||||
}
|
||||
|
||||
formatted = igdb._format_game_for_ai(mock_game)
|
||||
|
||||
self.assertEqual(formatted["name"], "Elden Ring")
|
||||
self.assertEqual(formatted["rating"], "96.0/100")
|
||||
self.assertEqual(formatted["user_rating"], "90.5/100")
|
||||
self.assertEqual(formatted["release_year"], 2022)
|
||||
self.assertIn("Role-playing (RPG)", formatted["genres"])
|
||||
self.assertIn("PC (Microsoft Windows)", formatted["platforms"])
|
||||
self.assertIn("FromSoftware", formatted["developers"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user