Skip to content

Commit b067ba4

Browse files
committed
clean up channels command implementation and output
1 parent ae43a53 commit b067ba4

File tree

1 file changed

+135
-108
lines changed

1 file changed

+135
-108
lines changed

modules/commands/channels_command.py

Lines changed: 135 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .base_command import BaseCommand
88
from ..models import MeshMessage
99
import asyncio
10+
import re
1011

1112

1213
class ChannelsCommand(BaseCommand):
@@ -47,7 +48,6 @@ def matches_keyword(self, message: MeshMessage) -> bool:
4748
return True
4849

4950
# Check for word boundary matches using regex
50-
import re
5151
# Create a regex pattern that matches the keyword at word boundaries
5252
# Use custom word boundary that treats underscores as separators
5353
# (?<![a-zA-Z0-9]) = negative lookbehind for alphanumeric characters (not underscore)
@@ -85,12 +85,17 @@ async def execute(self, message: MeshMessage) -> bool:
8585
specific_channel = sub_command
8686
sub_command = None
8787
else:
88-
# Check if this might be a channel search (not a category)
89-
# Try to find a channel that matches this name across all categories
90-
found_channel = self._find_channel_by_name(sub_command)
91-
if found_channel:
92-
specific_channel = '#' + found_channel
93-
sub_command = None
88+
# First check if this is a valid category
89+
if self._is_valid_category(sub_command):
90+
# It's a category, keep it as sub_command
91+
pass
92+
else:
93+
# Check if this might be a channel search (not a category)
94+
# Try to find a channel that matches this name across all categories
95+
found_channel = self._find_channel_by_name(sub_command)
96+
if found_channel:
97+
specific_channel = '#' + found_channel
98+
sub_command = None
9499

95100
# Handle specific channel request
96101
if specific_channel:
@@ -116,12 +121,7 @@ async def execute(self, message: MeshMessage) -> bool:
116121
messages = self._split_into_messages(channel_list, sub_command)
117122

118123
# Send each message with a small delay between them
119-
for i, msg_content in enumerate(messages):
120-
if i > 0:
121-
# Small delay between messages to prevent overwhelming the network
122-
await asyncio.sleep(0.5)
123-
124-
await self.send_response(message, msg_content)
124+
await self._send_multiple_messages(message, messages)
125125

126126
return True
127127

@@ -134,32 +134,32 @@ def _load_channels_from_config(self, sub_command: str = None) -> dict:
134134
"""Load channels from the Channels_List config section with optional sub-command filtering"""
135135
channels = {}
136136

137-
if self.bot.config.has_section('Channels_List'):
138-
for channel_name, description in self.bot.config.items('Channels_List'):
139-
# Skip empty or commented lines
140-
if channel_name.strip() and not channel_name.startswith('#'):
141-
# Strip quotes if present
142-
if description.startswith('"') and description.endswith('"'):
143-
description = description[1:-1]
144-
145-
# Handle sub-command filtering
146-
if sub_command:
147-
# Check if this channel belongs to the sub-command
148-
if not channel_name.startswith(f'{sub_command}.'):
149-
continue
150-
# Remove the sub-command prefix for display
151-
display_name = channel_name[len(sub_command) + 1:] # Remove 'subcommand.'
152-
else:
153-
# For general channels, only show channels that don't have sub-command prefixes
154-
if '.' in channel_name:
155-
continue
156-
display_name = channel_name
157-
158-
# Add # prefix if not already present
159-
if not display_name.startswith('#'):
160-
display_name = '#' + display_name
161-
162-
channels[display_name] = description
137+
for channel_name, description in self._parse_config_channels():
138+
# Handle sub-command filtering
139+
if sub_command:
140+
# Special case: "general" should show general channels (no category prefix)
141+
if sub_command == 'general':
142+
# For general channels, only show channels that don't have sub-command prefixes
143+
if '.' in channel_name:
144+
continue
145+
display_name = channel_name
146+
else:
147+
# Check if this channel belongs to the sub-command
148+
if not channel_name.startswith(f'{sub_command}.'):
149+
continue
150+
# Remove the sub-command prefix for display
151+
display_name = channel_name[len(sub_command) + 1:] # Remove 'subcommand.'
152+
else:
153+
# For general channels, only show channels that don't have sub-command prefixes
154+
if '.' in channel_name:
155+
continue
156+
display_name = channel_name
157+
158+
# Add # prefix if not already present
159+
if not display_name.startswith('#'):
160+
display_name = '#' + display_name
161+
162+
channels[display_name] = description
163163

164164
return channels
165165

@@ -181,10 +181,7 @@ async def _show_all_categories(self, message: MeshMessage):
181181
messages = self._split_into_messages(category_list, "Available categories")
182182

183183
# Send each message with a small delay between them
184-
for i, msg_content in enumerate(messages):
185-
if i > 0:
186-
await asyncio.sleep(0.5)
187-
await self.send_response(message, msg_content)
184+
await self._send_multiple_messages(message, messages)
188185

189186
except Exception as e:
190187
self.logger.error(f"Error showing categories: {e}")
@@ -194,42 +191,36 @@ def _get_all_categories(self) -> dict:
194191
"""Get all available channel categories and their channel counts"""
195192
categories = {}
196193

197-
if self.bot.config.has_section('Channels_List'):
198-
for channel_name, description in self.bot.config.items('Channels_List'):
199-
# Skip empty or commented lines
200-
if channel_name.strip() and not channel_name.startswith('#'):
201-
# Check if this is a sub-command channel (has a dot)
202-
if '.' in channel_name:
203-
category = channel_name.split('.')[0]
204-
if category not in categories:
205-
categories[category] = 0
206-
categories[category] += 1
207-
else:
208-
# General channels (no category)
209-
if 'general' not in categories:
210-
categories['general'] = 0
211-
categories['general'] += 1
194+
for channel_name, description in self._parse_config_channels():
195+
# Check if this is a sub-command channel (has a dot)
196+
if '.' in channel_name:
197+
category = channel_name.split('.')[0]
198+
if category not in categories:
199+
categories[category] = 0
200+
categories[category] += 1
201+
else:
202+
# General channels (no category)
203+
if 'general' not in categories:
204+
categories['general'] = 0
205+
categories['general'] += 1
212206

213207
return categories
214208

215209
def _find_channel_by_name(self, search_name: str) -> str:
216210
"""Find a channel by partial name match across all categories"""
217211
search_name_lower = search_name.lower()
218212

219-
if self.bot.config.has_section('Channels_List'):
220-
for config_name, description in self.bot.config.items('Channels_List'):
221-
# Skip empty or commented lines
222-
if config_name.strip() and not config_name.startswith('#'):
223-
# Handle sub-command channels
224-
if '.' in config_name:
225-
category, name = config_name.split('.', 1)
226-
# Check if the channel name matches (case insensitive)
227-
if name.lower() == search_name_lower:
228-
return name
229-
else:
230-
# Check general channels
231-
if config_name.lower() == search_name_lower:
232-
return config_name
213+
for config_name, description in self._parse_config_channels():
214+
# Handle sub-command channels
215+
if '.' in config_name:
216+
category, name = config_name.split('.', 1)
217+
# Check if the channel name matches (case insensitive)
218+
if name.lower() == search_name_lower:
219+
return name
220+
else:
221+
# Check general channels
222+
if config_name.lower() == search_name_lower:
223+
return config_name
233224

234225
return None
235226

@@ -240,22 +231,19 @@ async def _show_specific_channel(self, message: MeshMessage, channel_name: str):
240231
found_channel = None
241232
found_category = None
242233

243-
if self.bot.config.has_section('Channels_List'):
244-
for config_name, description in self.bot.config.items('Channels_List'):
245-
# Skip empty or commented lines
246-
if config_name.strip() and not config_name.startswith('#'):
247-
# Handle sub-command channels
248-
if '.' in config_name:
249-
category, name = config_name.split('.', 1)
250-
display_name = '#' + name
251-
else:
252-
display_name = '#' + config_name
253-
254-
# Check if this matches the requested channel
255-
if display_name.lower() == channel_name.lower():
256-
found_channel = display_name
257-
found_category = category if '.' in config_name else 'general'
258-
break
234+
for config_name, description in self._parse_config_channels():
235+
# Handle sub-command channels
236+
if '.' in config_name:
237+
category, name = config_name.split('.', 1)
238+
display_name = '#' + name
239+
else:
240+
display_name = '#' + config_name
241+
242+
# Check if this matches the requested channel
243+
if display_name.lower() == channel_name.lower():
244+
found_channel = display_name
245+
found_category = category if '.' in config_name else 'general'
246+
break
259247

260248
if found_channel:
261249
# Get the description
@@ -284,23 +272,14 @@ def _split_into_messages(self, channel_list: list, sub_command: str = None) -> l
284272
messages = []
285273

286274
# Set appropriate header based on sub-command
287-
if sub_command == "Available categories":
288-
current_message = "Available Categories: "
289-
elif sub_command:
290-
current_message = f"{sub_command.title()}: "
291-
else:
292-
current_message = "Common channels: "
293-
275+
current_message = self._get_header_for_subcommand(sub_command)
294276
current_length = len(current_message)
295277

296278
for channel in channel_list:
297279
# Check if adding this channel would exceed the limit
298280
if current_length + len(channel) + 2 > 130: # +2 for ", " separator
299281
# Start a new message
300-
if sub_command == "Available categories":
301-
expected_header = "Available Categories: "
302-
else:
303-
expected_header = f"{sub_command.title()} channels: " if sub_command else "Common channels: "
282+
expected_header = self._get_continuation_header_for_subcommand(sub_command)
304283

305284
if current_message != expected_header:
306285
messages.append(current_message.rstrip(", "))
@@ -314,21 +293,16 @@ def _split_into_messages(self, channel_list: list, sub_command: str = None) -> l
314293
continue
315294

316295
# Add channel to current message
317-
if sub_command == "Available categories":
318-
header = "Available Categories: "
319-
else:
320-
header = f"{sub_command.title()} channels: " if sub_command else "Common channels: "
321-
if current_message == header or current_message == "Channels (cont): ":
296+
initial_header = self._get_header_for_subcommand(sub_command)
297+
continuation_header = self._get_continuation_header_for_subcommand(sub_command)
298+
if current_message == initial_header or current_message == continuation_header or current_message == "Channels (cont): ":
322299
current_message += channel
323300
else:
324301
current_message += f", {channel}"
325302
current_length = len(current_message)
326303

327304
# Add the last message if it has content
328-
if sub_command == "Available categories":
329-
header = "Available Categories: "
330-
else:
331-
header = f"{sub_command.title()} channels: " if sub_command else "Common channels: "
305+
header = self._get_continuation_header_for_subcommand(sub_command)
332306
if current_message != header and current_message != "Channels (cont): ":
333307
messages.append(current_message)
334308

@@ -340,3 +314,56 @@ def _split_into_messages(self, channel_list: list, sub_command: str = None) -> l
340314
messages.append("No channels configured")
341315

342316
return messages
317+
318+
def _get_header_for_subcommand(self, sub_command: str = None) -> str:
319+
"""Get the appropriate header for a sub-command"""
320+
if sub_command == "Available categories":
321+
return "Available Categories: "
322+
elif sub_command and sub_command != "general":
323+
return f"{sub_command.title()}: "
324+
else:
325+
return "Common channels: "
326+
327+
def _get_continuation_header_for_subcommand(self, sub_command: str = None) -> str:
328+
"""Get the appropriate header for continuation messages"""
329+
if sub_command == "Available categories":
330+
return "Available Categories: "
331+
elif sub_command and sub_command != "general":
332+
return f"{sub_command.title()} channels: "
333+
else:
334+
return "Common channels: "
335+
336+
async def _send_multiple_messages(self, message: MeshMessage, messages: list):
337+
"""Send multiple messages with delays between them"""
338+
for i, msg_content in enumerate(messages):
339+
if i > 0:
340+
# Small delay between messages to prevent overwhelming the network
341+
await asyncio.sleep(0.5)
342+
await self.send_response(message, msg_content)
343+
344+
def _parse_config_channels(self):
345+
"""Parse all channels from config, returning a generator of (name, description) tuples"""
346+
if not self.bot.config.has_section('Channels_List'):
347+
return
348+
349+
for channel_name, description in self.bot.config.items('Channels_List'):
350+
# Skip empty or commented lines
351+
if channel_name.strip() and not channel_name.startswith('#'):
352+
# Strip quotes if present
353+
if description.startswith('"') and description.endswith('"'):
354+
description = description[1:-1]
355+
yield channel_name, description
356+
357+
def _is_valid_category(self, category_name: str) -> bool:
358+
"""Check if a category name is valid (has channels with that prefix)"""
359+
if not category_name:
360+
return False
361+
362+
# Check if there are any channels with this category prefix
363+
for channel_name, description in self._parse_config_channels():
364+
if '.' in channel_name:
365+
category = channel_name.split('.')[0]
366+
if category.lower() == category_name.lower():
367+
return True
368+
369+
return False

0 commit comments

Comments
 (0)