Skip to content

Commit fa7ef33

Browse files
authored
Merge pull request #31 from hummingbot/feat/add_terminal_ui
(feat) fix executor issues reported by ralph
2 parents 055a11d + 32cdf27 commit fa7ef33

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+14435
-8472
lines changed

config_manager.py

Lines changed: 263 additions & 185 deletions
Large diffs are not rendered by default.

docker-compose.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ services:
66
environment:
77
TELEGRAM_TOKEN: ${TELEGRAM_TOKEN}
88
ADMIN_USER_ID: ${ADMIN_USER_ID}
9-
# Where to store the bot persistence file inside the container
10-
CONDOR_PERSISTENCE_FILE: /app/data/condor_bot_data.pickle
119
env_file:
1210
- .env
1311
volumes:
14-
# The pickle file will be created as /app/data/condor_bot_data.pickle.
12+
# Persistence data (pickle file will be created as data/condor_bot_data.pickle)
1513
- ./data:/app/data
1614
# Mount config (servers, users, permissions)
1715
- ./config.yml:/app/config.yml

handlers/__init__.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,16 @@ def is_gateway_network(connector_name: str) -> bool:
1919

2020
# Known Gateway network patterns
2121
gateway_patterns = [
22-
'solana', 'ethereum', 'base', 'arbitrum', 'polygon',
23-
'optimism', 'avalanche', 'mainnet', 'devnet', 'testnet'
22+
"solana",
23+
"ethereum",
24+
"base",
25+
"arbitrum",
26+
"polygon",
27+
"optimism",
28+
"avalanche",
29+
"mainnet",
30+
"devnet",
31+
"testnet",
2432
]
2533

2634
for pattern in gateway_patterns:

handlers/admin/__init__.py

Lines changed: 102 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
import logging
77
from datetime import datetime
88

9-
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
9+
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
1010
from telegram.ext import ContextTypes
1111

12-
from utils.auth import admin_required
1312
from config_manager import (
14-
get_config_manager,
1513
UserRole,
14+
get_config_manager,
1615
)
16+
from utils.auth import admin_required
1717
from utils.telegram_formatters import escape_markdown_v2
1818

1919
logger = logging.getLogger(__name__)
@@ -60,6 +60,7 @@ def _format_timestamp(ts: float) -> str:
6060
async def admin_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
6161
"""Handle /admin command - show admin panel."""
6262
from handlers import clear_all_input_states
63+
6364
clear_all_input_states(context)
6465

6566
cm = get_config_manager()
@@ -74,14 +75,14 @@ async def admin_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> N
7475
)
7576

7677
await update.message.reply_text(
77-
message,
78-
parse_mode="MarkdownV2",
79-
reply_markup=_get_admin_menu_keyboard()
78+
message, parse_mode="MarkdownV2", reply_markup=_get_admin_menu_keyboard()
8079
)
8180

8281

8382
@admin_required
84-
async def admin_callback_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
83+
async def admin_callback_handler(
84+
update: Update, context: ContextTypes.DEFAULT_TYPE
85+
) -> None:
8586
"""Handle admin panel callbacks."""
8687
query = update.callback_query
8788

@@ -137,9 +138,7 @@ async def _show_admin_menu(query, context: ContextTypes.DEFAULT_TYPE) -> None:
137138
)
138139

139140
await query.edit_message_text(
140-
message,
141-
parse_mode="MarkdownV2",
142-
reply_markup=_get_admin_menu_keyboard()
141+
message, parse_mode="MarkdownV2", reply_markup=_get_admin_menu_keyboard()
143142
)
144143

145144

@@ -149,40 +148,41 @@ async def _show_pending_users(query, context: ContextTypes.DEFAULT_TYPE) -> None
149148
pending = cm.get_pending_users()
150149

151150
if not pending:
152-
message = (
153-
"👥 *Pending Requests*\n\n"
154-
"No pending access requests\\."
155-
)
151+
message = "👥 *Pending Requests*\n\n" "No pending access requests\\."
156152
keyboard = [[InlineKeyboardButton("🔙 Back", callback_data="admin:back")]]
157153
await query.edit_message_text(
158154
message,
159155
parse_mode="MarkdownV2",
160-
reply_markup=InlineKeyboardMarkup(keyboard)
156+
reply_markup=InlineKeyboardMarkup(keyboard),
161157
)
162158
return
163159

164160
message = f"👥 *Pending Requests* \\({len(pending)}\\)\n\n"
165161

166162
keyboard = []
167163
for user in pending:
168-
user_id = user['user_id']
169-
username = user.get('username') or 'N/A'
170-
created = _format_timestamp(user.get('created_at', 0))
164+
user_id = user["user_id"]
165+
username = user.get("username") or "N/A"
166+
created = _format_timestamp(user.get("created_at", 0))
171167

172168
message += f"• `{user_id}` \\(@{escape_markdown_v2(username)}\\)\n"
173169
message += f" Requested: {escape_markdown_v2(created)}\n\n"
174170

175-
keyboard.append([
176-
InlineKeyboardButton(f"✓ Approve {user_id}", callback_data=f"admin:approve_{user_id}"),
177-
InlineKeyboardButton(f"✕ Reject", callback_data=f"admin:reject_{user_id}"),
178-
])
171+
keyboard.append(
172+
[
173+
InlineKeyboardButton(
174+
f"✓ Approve {user_id}", callback_data=f"admin:approve_{user_id}"
175+
),
176+
InlineKeyboardButton(
177+
f"✕ Reject", callback_data=f"admin:reject_{user_id}"
178+
),
179+
]
180+
)
179181

180182
keyboard.append([InlineKeyboardButton("🔙 Back", callback_data="admin:back")])
181183

182184
await query.edit_message_text(
183-
message,
184-
parse_mode="MarkdownV2",
185-
reply_markup=InlineKeyboardMarkup(keyboard)
185+
message, parse_mode="MarkdownV2", reply_markup=InlineKeyboardMarkup(keyboard)
186186
)
187187

188188

@@ -197,20 +197,25 @@ async def _show_all_users(query, context: ContextTypes.DEFAULT_TYPE) -> None:
197197
await query.edit_message_text(
198198
message,
199199
parse_mode="MarkdownV2",
200-
reply_markup=InlineKeyboardMarkup(keyboard)
200+
reply_markup=InlineKeyboardMarkup(keyboard),
201201
)
202202
return
203203

204204
# Group by role
205205
by_role = {}
206206
for user in users:
207-
role = user.get('role', 'unknown')
207+
role = user.get("role", "unknown")
208208
by_role.setdefault(role, []).append(user)
209209

210210
message = f"📋 *All Users* \\({len(users)}\\)\n\n"
211211

212212
# Show in order: admin, user, pending, blocked
213-
role_order = [UserRole.ADMIN.value, UserRole.USER.value, UserRole.PENDING.value, UserRole.BLOCKED.value]
213+
role_order = [
214+
UserRole.ADMIN.value,
215+
UserRole.USER.value,
216+
UserRole.PENDING.value,
217+
UserRole.BLOCKED.value,
218+
]
214219

215220
keyboard = []
216221
for role in role_order:
@@ -222,16 +227,18 @@ async def _show_all_users(query, context: ContextTypes.DEFAULT_TYPE) -> None:
222227
message += f"*{badge} {role.title()}* \\({len(role_users)}\\)\n"
223228

224229
for user in role_users[:5]: # Limit to 5 per role in message
225-
user_id = user['user_id']
226-
username = user.get('username') or 'N/A'
230+
user_id = user["user_id"]
231+
username = user.get("username") or "N/A"
227232
message += f" • `{user_id}` @{escape_markdown_v2(username)}\n"
228233

229-
keyboard.append([
230-
InlineKeyboardButton(
231-
f"{badge} {user_id} (@{username[:10]})",
232-
callback_data=f"admin:user_{user_id}"
233-
)
234-
])
234+
keyboard.append(
235+
[
236+
InlineKeyboardButton(
237+
f"{badge} {user_id} (@{username[:10]})",
238+
callback_data=f"admin:user_{user_id}",
239+
)
240+
]
241+
)
235242

236243
if len(role_users) > 5:
237244
message += f" _\\.\\.\\. and {len(role_users) - 5} more_\n"
@@ -241,13 +248,13 @@ async def _show_all_users(query, context: ContextTypes.DEFAULT_TYPE) -> None:
241248
keyboard.append([InlineKeyboardButton("🔙 Back", callback_data="admin:back")])
242249

243250
await query.edit_message_text(
244-
message,
245-
parse_mode="MarkdownV2",
246-
reply_markup=InlineKeyboardMarkup(keyboard)
251+
message, parse_mode="MarkdownV2", reply_markup=InlineKeyboardMarkup(keyboard)
247252
)
248253

249254

250-
async def _show_user_details(query, context: ContextTypes.DEFAULT_TYPE, user_id: int) -> None:
255+
async def _show_user_details(
256+
query, context: ContextTypes.DEFAULT_TYPE, user_id: int
257+
) -> None:
251258
"""Show details for a specific user."""
252259
cm = get_config_manager()
253260
user = cm.get_user(user_id)
@@ -256,12 +263,12 @@ async def _show_user_details(query, context: ContextTypes.DEFAULT_TYPE, user_id:
256263
await query.answer("User not found", show_alert=True)
257264
return
258265

259-
role = user.get('role', 'unknown')
260-
username = user.get('username') or 'N/A'
261-
created = _format_timestamp(user.get('created_at', 0))
262-
approved_at = _format_timestamp(user.get('approved_at'))
263-
approved_by = user.get('approved_by')
264-
notes = user.get('notes') or 'None'
266+
role = user.get("role", "unknown")
267+
username = user.get("username") or "N/A"
268+
created = _format_timestamp(user.get("created_at", 0))
269+
approved_at = _format_timestamp(user.get("approved_at"))
270+
approved_by = user.get("approved_by")
271+
notes = user.get("notes") or "None"
265272

266273
badge = _format_user_role_badge(role)
267274

@@ -277,7 +284,7 @@ async def _show_user_details(query, context: ContextTypes.DEFAULT_TYPE, user_id:
277284
message += f"*Approved:* {escape_markdown_v2(approved_at)}\n"
278285
if approved_by:
279286
message += f"*Approved By:* `{approved_by}`\n"
280-
if notes != 'None':
287+
if notes != "None":
281288
message += f"*Notes:* {escape_markdown_v2(notes)}\n"
282289

283290
# Show servers owned by user
@@ -301,29 +308,45 @@ async def _show_user_details(query, context: ContextTypes.DEFAULT_TYPE, user_id:
301308
admin_id = cm.admin_id
302309

303310
if role == UserRole.PENDING.value:
304-
keyboard.append([
305-
InlineKeyboardButton("✓ Approve", callback_data=f"admin:approve_{user_id}"),
306-
InlineKeyboardButton("✕ Reject", callback_data=f"admin:reject_{user_id}"),
307-
])
311+
keyboard.append(
312+
[
313+
InlineKeyboardButton(
314+
"✓ Approve", callback_data=f"admin:approve_{user_id}"
315+
),
316+
InlineKeyboardButton(
317+
"✕ Reject", callback_data=f"admin:reject_{user_id}"
318+
),
319+
]
320+
)
308321
elif role == UserRole.BLOCKED.value:
309-
keyboard.append([
310-
InlineKeyboardButton("🔓 Unblock", callback_data=f"admin:unblock_{user_id}"),
311-
])
322+
keyboard.append(
323+
[
324+
InlineKeyboardButton(
325+
"🔓 Unblock", callback_data=f"admin:unblock_{user_id}"
326+
),
327+
]
328+
)
312329
elif role == UserRole.USER.value and user_id != admin_id:
313-
keyboard.append([
314-
InlineKeyboardButton("🚫 Block", callback_data=f"admin:block_{user_id}"),
315-
])
330+
keyboard.append(
331+
[
332+
InlineKeyboardButton(
333+
"🚫 Block", callback_data=f"admin:block_{user_id}"
334+
),
335+
]
336+
)
316337

317-
keyboard.append([InlineKeyboardButton("🔙 Back to Users", callback_data="admin:users")])
338+
keyboard.append(
339+
[InlineKeyboardButton("🔙 Back to Users", callback_data="admin:users")]
340+
)
318341

319342
await query.edit_message_text(
320-
message,
321-
parse_mode="MarkdownV2",
322-
reply_markup=InlineKeyboardMarkup(keyboard)
343+
message, parse_mode="MarkdownV2", reply_markup=InlineKeyboardMarkup(keyboard)
323344
)
324345

325346

326-
async def _approve_user(query, context: ContextTypes.DEFAULT_TYPE, user_id: int) -> None:
347+
async def _approve_user(
348+
query, context: ContextTypes.DEFAULT_TYPE, user_id: int
349+
) -> None:
327350
"""Approve a pending user."""
328351
cm = get_config_manager()
329352
admin_id = query.from_user.id
@@ -338,7 +361,7 @@ async def _approve_user(query, context: ContextTypes.DEFAULT_TYPE, user_id: int)
338361
"Your access request has been approved\\.\n"
339362
"Use /start to begin\\."
340363
),
341-
parse_mode="MarkdownV2"
364+
parse_mode="MarkdownV2",
342365
)
343366
except Exception as e:
344367
logger.warning(f"Failed to notify user {user_id} of approval: {e}")
@@ -379,7 +402,9 @@ async def _block_user(query, context: ContextTypes.DEFAULT_TYPE, user_id: int) -
379402
await _show_user_details(query, context, user_id)
380403

381404

382-
async def _unblock_user(query, context: ContextTypes.DEFAULT_TYPE, user_id: int) -> None:
405+
async def _unblock_user(
406+
query, context: ContextTypes.DEFAULT_TYPE, user_id: int
407+
) -> None:
383408
"""Unblock a user."""
384409
cm = get_config_manager()
385410
admin_id = query.from_user.id
@@ -404,21 +429,21 @@ async def _show_audit_log(query, context: ContextTypes.DEFAULT_TYPE) -> None:
404429
await query.edit_message_text(
405430
message,
406431
parse_mode="MarkdownV2",
407-
reply_markup=InlineKeyboardMarkup(keyboard)
432+
reply_markup=InlineKeyboardMarkup(keyboard),
408433
)
409434
return
410435

411436
message = "📜 *Audit Log* \\(Recent 10\\)\n\n"
412437

413438
for entry in entries:
414-
ts = _format_timestamp(entry.get('timestamp', 0))
415-
action = entry.get('action', 'unknown')
416-
actor = entry.get('actor_id', 0)
417-
target_type = entry.get('target_type', '')
418-
target_id = entry.get('target_id', '')
439+
ts = _format_timestamp(entry.get("timestamp", 0))
440+
action = entry.get("action", "unknown")
441+
actor = entry.get("actor_id", 0)
442+
target_type = entry.get("target_type", "")
443+
target_id = entry.get("target_id", "")
419444

420445
# Format action nicely
421-
action_display = action.replace('_', ' ').title()
446+
action_display = action.replace("_", " ").title()
422447

423448
message += f"• *{escape_markdown_v2(ts)}*\n"
424449
message += f" {escape_markdown_v2(action_display)}\n"
@@ -427,9 +452,7 @@ async def _show_audit_log(query, context: ContextTypes.DEFAULT_TYPE) -> None:
427452
keyboard = [[InlineKeyboardButton("🔙 Back", callback_data="admin:back")]]
428453

429454
await query.edit_message_text(
430-
message,
431-
parse_mode="MarkdownV2",
432-
reply_markup=InlineKeyboardMarkup(keyboard)
455+
message, parse_mode="MarkdownV2", reply_markup=InlineKeyboardMarkup(keyboard)
433456
)
434457

435458

@@ -444,7 +467,7 @@ async def _show_stats(query, context: ContextTypes.DEFAULT_TYPE) -> None:
444467
# Count by role
445468
role_counts = {}
446469
for user in users:
447-
role = user.get('role', 'unknown')
470+
role = user.get("role", "unknown")
448471
role_counts[role] = role_counts.get(role, 0) + 1
449472

450473
# Count servers by owner
@@ -471,14 +494,12 @@ async def _show_stats(query, context: ContextTypes.DEFAULT_TYPE) -> None:
471494
keyboard = [[InlineKeyboardButton("🔙 Back", callback_data="admin:back")]]
472495

473496
await query.edit_message_text(
474-
message,
475-
parse_mode="MarkdownV2",
476-
reply_markup=InlineKeyboardMarkup(keyboard)
497+
message, parse_mode="MarkdownV2", reply_markup=InlineKeyboardMarkup(keyboard)
477498
)
478499

479500

480501
__all__ = [
481-
'admin_command',
482-
'admin_callback_handler',
483-
'_show_admin_menu',
502+
"admin_command",
503+
"admin_callback_handler",
504+
"_show_admin_menu",
484505
]

0 commit comments

Comments
 (0)