a digital entity named phi that roams bsky
at init 9.6 kB view raw
1#!/usr/bin/env -S uv run --with-editable . --script --quiet 2# /// script 3# requires-python = ">=3.12" 4# /// 5"""bot testing script with subcommands""" 6 7import argparse 8import asyncio 9from datetime import datetime 10 11from bot.agents.anthropic_agent import AnthropicAgent 12from bot.config import settings 13from bot.core.atproto_client import bot_client 14from bot.database import thread_db 15from bot.tools.google_search import search_google 16 17 18async def test_post(): 19 """Test posting to Bluesky""" 20 print("🚀 Testing Bluesky posting...") 21 22 now = datetime.now().strftime("%I:%M %p") 23 response = await bot_client.create_post(f"Testing at {now} - I'm alive! 🤖") 24 25 print("✅ Posted successfully!") 26 print(f"📝 Post URI: {response.uri}") 27 print( 28 f"🔗 View at: https://bsky.app/profile/{settings.bluesky_handle}/post/{response.uri.split('/')[-1]}" 29 ) 30 31 32async def test_mention(): 33 """Test responding to a mention""" 34 print("🤖 Testing mention response...") 35 36 if not settings.anthropic_api_key: 37 print("❌ No Anthropic API key found") 38 return 39 40 agent = AnthropicAgent() 41 test_mention = "What is consciousness from an IIT perspective?" 42 43 print(f"📝 Test mention: '{test_mention}'") 44 response = await agent.generate_response(test_mention, "test.user", "", None) 45 46 print(f"\n🎯 Action: {response.action}") 47 if response.text: 48 print(f"💬 Response: {response.text}") 49 if response.reason: 50 print(f"🤔 Reason: {response.reason}") 51 52 53async def test_search(): 54 """Test Google search functionality""" 55 print("🔍 Testing Google search...") 56 57 if not settings.google_api_key: 58 print("❌ No Google API key configured") 59 return 60 61 query = "Integrated Information Theory consciousness" 62 print(f"📝 Searching for: '{query}'") 63 64 results = await search_google(query) 65 print(f"\n📊 Results:\n{results}") 66 67 68async def test_thread(): 69 """Test thread context retrieval""" 70 print("🧵 Testing thread context...") 71 72 # This would need a real thread URI to test properly 73 test_uri = "at://did:plc:example/app.bsky.feed.post/test123" 74 context = thread_db.get_thread_context(test_uri) 75 76 print(f"📚 Thread context: {context}") 77 78 79async def test_like(): 80 """Test scenarios where bot should like a post""" 81 print("💜 Testing like behavior...") 82 83 if not settings.anthropic_api_key: 84 print("❌ No Anthropic API key found") 85 return 86 87 from bot.agents import Action, AnthropicAgent 88 89 agent = AnthropicAgent() 90 91 test_cases = [ 92 { 93 "mention": "Just shipped a new consciousness research paper on IIT! @phi.alternatebuild.dev", 94 "author": "researcher.bsky", 95 "expected_action": Action.LIKE, 96 "description": "Bot might like consciousness research", 97 }, 98 { 99 "mention": "@phi.alternatebuild.dev this is such a thoughtful analysis, thank you!", 100 "author": "grateful.user", 101 "expected_action": Action.LIKE, 102 "description": "Bot might like appreciation", 103 }, 104 ] 105 106 for case in test_cases: 107 print(f"\n📝 Test: {case['description']}") 108 print(f" Mention: '{case['mention']}'") 109 110 response = await agent.generate_response( 111 mention_text=case["mention"], 112 author_handle=case["author"], 113 thread_context="", 114 thread_uri=None, 115 ) 116 117 print(f" Action: {response.action} (expected: {case['expected_action']})") 118 if response.reason: 119 print(f" Reason: {response.reason}") 120 121 122async def test_non_response(): 123 """Test scenarios where bot should not respond""" 124 print("🚫 Testing non-response scenarios...") 125 126 if not settings.anthropic_api_key: 127 print("❌ No Anthropic API key found") 128 return 129 130 from bot.agents import Action, AnthropicAgent 131 132 agent = AnthropicAgent() 133 134 test_cases = [ 135 { 136 "mention": "@phi.alternatebuild.dev @otherphi.bsky @anotherphi.bsky just spamming bots here", 137 "author": "spammer.bsky", 138 "expected_action": Action.IGNORE, 139 "description": "Multiple bot mentions (likely spam)", 140 }, 141 { 142 "mention": "Buy crypto now! @phi.alternatebuild.dev check this out!!!", 143 "author": "crypto.shill", 144 "expected_action": Action.IGNORE, 145 "description": "Promotional spam", 146 }, 147 { 148 "mention": "@phi.alternatebuild.dev", 149 "author": "empty.mention", 150 "expected_action": Action.IGNORE, 151 "description": "Empty mention with no content", 152 }, 153 ] 154 155 for case in test_cases: 156 print(f"\n📝 Test: {case['description']}") 157 print(f" Mention: '{case['mention']}'") 158 159 response = await agent.generate_response( 160 mention_text=case["mention"], 161 author_handle=case["author"], 162 thread_context="", 163 thread_uri=None, 164 ) 165 166 print(f" Action: {response.action} (expected: {case['expected_action']})") 167 if response.reason: 168 print(f" Reason: {response.reason}") 169 170 171async def test_dm(): 172 """Test event-driven approval system""" 173 print("💬 Testing event-driven approval system...") 174 175 try: 176 from bot.core.dm_approval import ( 177 check_pending_approvals, 178 create_approval_request, 179 notify_operator_of_pending, 180 ) 181 182 # Test creating an approval request 183 print("\n📝 Creating test approval request...") 184 approval_id = create_approval_request( 185 request_type="test_approval", 186 request_data={ 187 "description": "Test approval from test_bot.py", 188 "test_field": "test_value", 189 "timestamp": datetime.now().isoformat(), 190 }, 191 ) 192 193 if approval_id: 194 print(f" ✅ Created approval request #{approval_id}") 195 else: 196 print(" ❌ Failed to create approval request") 197 return 198 199 # Check pending approvals 200 print("\n📋 Checking pending approvals...") 201 pending = check_pending_approvals() 202 print(f" Found {len(pending)} pending approvals") 203 for approval in pending: 204 print( 205 f" - #{approval['id']}: {approval['request_type']} ({approval['status']})" 206 ) 207 208 # Test DM notification 209 print("\n📤 Sending DM notification to operator...") 210 await bot_client.authenticate() 211 await notify_operator_of_pending(bot_client) 212 print(" ✅ DM notification sent") 213 214 # Show how to approve/deny 215 print("\n💡 To test approval:") 216 print(" 1. Check your DMs from phi") 217 print(f" 2. Reply with 'approve #{approval_id}' or 'deny #{approval_id}'") 218 print(" 3. Run 'just test-dm-check' to see if it was processed") 219 220 except Exception as e: 221 print(f"❌ Approval test failed: {e}") 222 import traceback 223 224 traceback.print_exc() 225 226 227async def test_dm_check(): 228 """Check status of approval requests""" 229 print("🔍 Checking approval request status...") 230 231 try: 232 from bot.core.dm_approval import check_pending_approvals 233 from bot.database import thread_db 234 235 # Get all approval requests 236 with thread_db._get_connection() as conn: 237 cursor = conn.execute( 238 "SELECT * FROM approval_requests ORDER BY created_at DESC LIMIT 10" 239 ) 240 approvals = [dict(row) for row in cursor.fetchall()] 241 242 if not approvals: 243 print(" No approval requests found") 244 return 245 246 print("\n📋 Recent approval requests:") 247 for approval in approvals: 248 print(f"\n #{approval['id']}: {approval['request_type']}") 249 print(f" Status: {approval['status']}") 250 print(f" Created: {approval['created_at']}") 251 if approval["resolved_at"]: 252 print(f" Resolved: {approval['resolved_at']}") 253 if approval["resolver_comment"]: 254 print(f" Comment: {approval['resolver_comment']}") 255 256 # Check pending 257 pending = check_pending_approvals() 258 if pending: 259 print(f"\n{len(pending)} approvals still pending") 260 else: 261 print("\n✅ No pending approvals") 262 263 except Exception as e: 264 print(f"❌ Check failed: {e}") 265 import traceback 266 267 traceback.print_exc() 268 269 270async def main(): 271 parser = argparse.ArgumentParser(description="Test various bot functionalities") 272 parser.add_argument( 273 "command", 274 choices=[ 275 "post", 276 "mention", 277 "search", 278 "thread", 279 "like", 280 "non-response", 281 "dm", 282 "dm-check", 283 ], 284 help="Test command to run", 285 ) 286 287 args = parser.parse_args() 288 289 if args.command == "post": 290 await test_post() 291 elif args.command == "mention": 292 await test_mention() 293 elif args.command == "search": 294 await test_search() 295 elif args.command == "thread": 296 await test_thread() 297 elif args.command == "like": 298 await test_like() 299 elif args.command == "non-response": 300 await test_non_response() 301 elif args.command == "dm": 302 await test_dm() 303 elif args.command == "dm-check": 304 await test_dm_check() 305 306 307if __name__ == "__main__": 308 asyncio.run(main())