Jofthomas commited on
Commit
5440fdd
·
verified ·
1 Parent(s): 93779c5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +213 -71
app.py CHANGED
@@ -1,93 +1,235 @@
 
1
  import gradio as gr
2
- from poke_env.player.random_player import RandomPlayer
3
- from poke_env import AccountConfiguration, ShowdownServerConfiguration
4
  import asyncio
5
  import threading
 
 
6
 
7
- # Set up the random player
8
- random_player = None
9
- player_thread = None
10
-
11
- from poke_env import Player, ServerConfiguration
12
- custom_config = ServerConfiguration(
13
- "wss://jofthomas.com/showdown/websocket", # WebSocket URL
14
- "https://jofthomas.com/showdown/action.php" # Authentication URL
15
- )# Function to start the random player in a separate thread
16
- random_player = RandomPlayer(
17
- server_configuration=custom_config,
 
 
 
 
 
 
 
 
 
18
  )
19
- def start_random_player():
20
- loop = asyncio.new_event_loop()
21
- asyncio.set_event_loop(loop)
22
- global random_player
23
-
24
- random_player = RandomPlayer(
25
- account_configuration=AccountConfiguration("huggingface_random", "huggingface_random"),
26
- server_configuration=custom_config,
27
- )
28
-
29
-
30
- # Start the random player in a background thread
31
- def initialize_random_player():
32
- global player_thread
33
- player_thread = threading.Thread(target=start_random_player)
34
- player_thread.daemon = True
35
- player_thread.start()
36
-
37
- # Function to send a battle invite
38
- async def send_battle_invite(username):
39
- if random_player is None:
40
- return f"Error: Random player not initialized. Try again."
41
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  try:
43
- # Send a challenge to the user
44
- await random_player.send_challenges(username, n_challenges=1)
45
- return f"Battle invitation sent to {username}! Check the Pokemon Showdown interface below."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  except Exception as e:
47
- return f"Error sending challenge: {str(e)}"
48
-
49
- # Wrapper for the async function to use in Gradio
50
- def invite_to_battle(username):
51
- if not username.strip():
52
- return "Please enter a valid username."
53
-
54
- loop = asyncio.new_event_loop()
55
- result = loop.run_until_complete(send_battle_invite(username))
56
- loop.close()
57
- return result
58
-
59
- # iframe code to embed Pokemon Showdown
60
  iframe_code = """
61
  <iframe
62
- src="https://jofthomas.com/play.pokemonshowdown.com/testclient.html"
63
  width="100%"
64
  height="800"
65
  style="border: none;"
66
- >
67
  </iframe>
68
  """
69
 
70
- def main():
71
- # Initialize the random player when the app starts
72
- #initialize_random_player()
73
-
74
- with gr.Blocks() as demo:
 
75
  gr.Markdown("# Pokémon Showdown Battle Bot")
76
-
 
77
  with gr.Row():
78
- with gr.Column():
79
- gr.Markdown("### Enter your Pokémon Showdown username to receive a battle invitation:")
80
- name_input = gr.Textbox(label="Your Pokémon Showdown Username", placeholder="Enter the username you're using on Showdown")
 
 
 
 
 
 
 
 
81
  battle_button = gr.Button("Send Battle Invitation")
82
-
83
- battle_button.click(fn=invite_to_battle, inputs=name_input)
84
-
85
- gr.Markdown("### Pokémon Showdown Interface")
86
- gr.Markdown("Log in to Pokémon Showdown in the interface below, using the same username you entered above.")
87
- gr.HTML(iframe_code)
 
 
 
 
 
 
 
88
 
89
  return demo
90
 
 
91
  if __name__ == "__main__":
92
- demo = main()
93
- demo.launch()
 
 
 
1
+ # app.py
2
  import gradio as gr
 
 
3
  import asyncio
4
  import threading
5
+ import os
6
+ from dotenv import load_dotenv
7
 
8
+ # Import poke-env components
9
+ from poke_env.player import Player, RandomPlayer, AccountConfiguration, ShowdownServerConfiguration
10
+
11
+ # Import your custom agent
12
+ from agent import OpenAIAgent
13
+
14
+ # Load environment variables (.env file should contain OPENAI_API_KEY)
15
+ load_dotenv()
16
+
17
+ # --- Global variables for players and thread ---
18
+ random_player: Player | None = None
19
+ openai_agent: Player | None = None
20
+ agent_init_thread: threading.Thread | None = None
21
+ init_lock = threading.Lock() # To prevent race conditions during init
22
+ agents_initialized = False
23
+
24
+ # --- Configuration ---
25
+ custom_config = ShowdownServerConfiguration(
26
+ "wss://jofthomas.com/showdown/websocket", # WebSocket URL
27
+ "https://jofthomas.com/showdown/action.php" # Authentication URL
28
  )
29
+
30
+
31
+ # Define distinct account configurations
32
+ # IMPORTANT: Replace with your actual bot usernames/passwords or leave None for guest accounts
33
+ # Using the usernames from your original app.py for RandomPlayer
34
+ random_account_config = AccountConfiguration("huggingface_random", None) # Replace password or set None
35
+ # Define a different username for the OpenAI agent
36
+ openai_account_config = AccountConfiguration("hgface_openai_bot", None) # Replace or set None
37
+
38
+ DEFAULT_BATTLE_FORMAT = "gen9randombattle"
39
+
40
+ # --- Agent Initialization ---
41
+ def initialize_agents_sync():
42
+ """Initializes both player agents in a background thread."""
43
+ global random_player, openai_agent, agents_initialized
44
+ # Ensure this runs only once
45
+ with init_lock:
46
+ if agents_initialized:
47
+ print("Agents already initialized.")
48
+ return
49
+ print("Initializing agents...")
50
+ # We need an event loop in this new thread for player initialization
51
+ loop = asyncio.new_event_loop()
52
+ asyncio.set_event_loop(loop)
53
+ try:
54
+ # Initialize Random Player
55
+ print(f"Initializing RandomPlayer ({random_account_config.username})...")
56
+ random_player = RandomPlayer(
57
+ account_configuration=random_account_config,
58
+ server_configuration=custom_config,
59
+ battle_format=DEFAULT_BATTLE_FORMAT
60
+ )
61
+ print("RandomPlayer initialized.")
62
+
63
+ # Initialize OpenAI Agent
64
+ print(f"Initializing OpenAIAgent ({openai_account_config.username})...")
65
+ # Ensure API key is available via .env (checked inside OpenAIAgent.__init__)
66
+ openai_agent = OpenAIAgent(
67
+ account_configuration=openai_account_config,
68
+ server_configuration=custom_config,
69
+ battle_format=DEFAULT_BATTLE_FORMAT
70
+ )
71
+ print("OpenAIAgent initialized.")
72
+
73
+ agents_initialized = True
74
+ print("Agent initialization complete.")
75
+ except Exception as e:
76
+ print(f"!!! Error during agent initialization: {e}")
77
+ # Reset globals if init fails partially
78
+ random_player = None
79
+ openai_agent = None
80
+ agents_initialized = False
81
+ # Note: Don't close the loop here, the players might need it running implicitly
82
+ # loop.close()
83
+
84
+ # Function to start the initialization thread
85
+ def start_agent_initialization():
86
+ """Starts the agent initialization thread if not already running."""
87
+ global agent_init_thread
88
+ if not agents_initialized and (agent_init_thread is None or not agent_init_thread.is_alive()):
89
+ print("Starting agent initialization thread...")
90
+ agent_init_thread = threading.Thread(target=initialize_agents_sync, daemon=True)
91
+ agent_init_thread.start()
92
+ elif agents_initialized:
93
+ print("Agents already initialized, no need to start thread.")
94
+ else:
95
+ print("Initialization thread already running.")
96
+
97
+ # --- Battle Invitation Logic ---
98
+ async def send_battle_invite_async(player: Player, username: str, battle_format: str = DEFAULT_BATTLE_FORMAT):
99
+ """Sends a challenge using the provided player object."""
100
+ if player is None:
101
+ return "Error: The selected player is not available/initialized."
102
+ if not username or not username.strip():
103
+ return "Error: Please enter a valid Showdown username."
104
+
105
+ try:
106
+ print(f"Attempting to send challenge from {player.username} to {username} in format {battle_format}")
107
+ # Using send_challenges which should handle login implicitly if needed
108
+ await player.send_challenges(username, n_challenges=1, packed_team=None, battle_format=battle_format)
109
+ return f"Battle invitation ({battle_format}) sent to {username} from bot {player.username}! Check Showdown."
110
+ except Exception as e:
111
+ # Log the full error for debugging
112
+ import traceback
113
+ print(f"Error sending challenge from {player.username}:")
114
+ traceback.print_exc() # Print stack trace
115
+ return f"Error sending challenge: {str(e)}. Check console logs."
116
+
117
+ # Wrapper for the async function to use in Gradio, handling agent selection
118
+ def invite_to_battle(agent_choice: str, username: str):
119
+ """Selects the agent and initiates the battle invitation."""
120
+ global random_player, openai_agent, agents_initialized
121
+
122
+ if not agents_initialized:
123
+ # Try to initialize if the button is clicked before init finishes
124
+ start_agent_initialization()
125
+ return "Agents are initializing, please wait a few seconds and try again."
126
+
127
+ selected_player: Player | None = None
128
+ if agent_choice == "Random Player":
129
+ selected_player = random_player
130
+ if selected_player is None:
131
+ return "Error: Random Player is not available. Initialization might have failed."
132
+ elif agent_choice == "OpenAI Agent":
133
+ selected_player = openai_agent
134
+ if selected_player is None:
135
+ # Check if API key might be the issue
136
+ if not os.getenv("OPENAI_API_KEY"):
137
+ return "Error: OpenAI Agent not available. OPENAI_API_KEY is missing."
138
+ else:
139
+ return "Error: OpenAI Agent not available. Initialization might have failed. Check logs."
140
+ else:
141
+ return "Error: Invalid agent choice selected."
142
+
143
+ # Ensure username is provided
144
+ username_clean = username.strip()
145
+ if not username_clean:
146
+ return "Please enter your Showdown username."
147
+
148
+ # Run the async challenge function in a managed event loop
149
  try:
150
+ # Try to get the existing loop from the init thread or create a new one
151
+ try:
152
+ loop = asyncio.get_running_loop()
153
+ except RuntimeError:
154
+ print("No running event loop, creating new one for challenge.")
155
+ loop = asyncio.new_event_loop()
156
+ asyncio.set_event_loop(loop)
157
+
158
+ # If the main loop is running, schedule the task; otherwise, run until complete
159
+ if loop.is_running():
160
+ # Schedule the coroutine and wait for its result
161
+ future = asyncio.run_coroutine_threadsafe(
162
+ send_battle_invite_async(selected_player, username_clean), loop
163
+ )
164
+ result = future.result(timeout=30) # Add a timeout
165
+ else:
166
+ result = loop.run_until_complete(
167
+ send_battle_invite_async(selected_player, username_clean)
168
+ )
169
+
170
+ return result
171
+ except TimeoutError:
172
+ return "Error: Sending challenge timed out."
173
  except Exception as e:
174
+ print(f"Unexpected error in invite_to_battle's async execution: {e}")
175
+ import traceback
176
+ traceback.print_exc()
177
+ return f"An unexpected error occurred: {e}"
178
+
179
+
180
+ # --- Gradio UI Definition ---
181
+ # iframe code to embed Pokemon Showdown (using official URL)
 
 
 
 
 
182
  iframe_code = """
183
  <iframe
184
+ src="https://play.pokemonshowdown.com/"
185
  width="100%"
186
  height="800"
187
  style="border: none;"
188
+ referrerpolicy="no-referrer">
189
  </iframe>
190
  """
191
 
192
+ def main_app():
193
+ """Creates and returns the Gradio application interface."""
194
+ # Start agent initialization when the app is defined/loaded
195
+ start_agent_initialization()
196
+
197
+ with gr.Blocks(title="Pokemon Showdown Bot") as demo:
198
  gr.Markdown("# Pokémon Showdown Battle Bot")
199
+ gr.Markdown("Select a bot agent, enter **your** Showdown username (the one you are logged in with below), and click Send Invite.")
200
+
201
  with gr.Row():
202
+ with gr.Column(scale=1):
203
+ gr.Markdown("### Configuration")
204
+ agent_dropdown = gr.Dropdown(
205
+ label="Select Bot Agent",
206
+ choices=["Random Player", "OpenAI Agent"],
207
+ value="Random Player" # Default choice
208
+ )
209
+ name_input = gr.Textbox(
210
+ label="Your Pokémon Showdown Username",
211
+ placeholder="Enter username used in Showdown below"
212
+ )
213
  battle_button = gr.Button("Send Battle Invitation")
214
+ status_output = gr.Textbox(label="Status", interactive=False, lines=2) # Added for feedback
215
+
216
+ with gr.Column(scale=3):
217
+ gr.Markdown("### Pokémon Showdown Interface")
218
+ gr.Markdown("Log in/use the username you entered above.")
219
+ gr.HTML(iframe_code)
220
+
221
+ # Connect button click to the invitation function
222
+ battle_button.click(
223
+ fn=invite_to_battle,
224
+ inputs=[agent_dropdown, name_input],
225
+ outputs=status_output # Display result/status here
226
+ )
227
 
228
  return demo
229
 
230
+ # --- Main execution block ---
231
  if __name__ == "__main__":
232
+ # Create and launch the Gradio app
233
+ app = main_app()
234
+ # You might need to configure server_name and server_port depending on your deployment environment
235
+ app.launch() # server_name="0.0.0.0" # To make accessible on network