Add greeter command configuration to config.ini.example and implement greeter functionality in message handling. Introduce favorite status management for repeaters, allowing users to toggle favorite status via API. Update path command and test command to incorporate favorite bias in scoring. Enhance database schema to support star status tracking for repeaters and roomservers.

This commit is contained in:
agessaman committed 2025-11-25 17:26:40 -08:00
1 parent 732b7551ba
commit 82aa5ee160
8 files changed
+1127 -17

No files matched your search

+62 -2
View File
@@ -464,6 +464,64 @@ class BotDataViewer:
except Exception as e:
self.logger.error(f"Error geocoding contact: {e}", exc_info=True)
return jsonify({'error': str(e)}), 500
@self.app.route('/api/toggle-star-contact', methods=['POST'])
def api_toggle_star_contact():
"""Toggle star status for a contact by public_key (only for repeaters and roomservers)"""
try:
data = request.get_json()
if not data or 'public_key' not in data:
return jsonify({'error': 'public_key is required'}), 400
public_key = data['public_key']
# Get contact data from database
conn = self._get_db_connection()
cursor = conn.cursor()
# Check if contact exists and is a repeater or roomserver
cursor.execute('''
SELECT name, is_starred, role FROM complete_contact_tracking
WHERE public_key = ?
''', (public_key,))
contact = cursor.fetchone()
if not contact:
conn.close()
return jsonify({'error': 'Contact not found'}), 404
# Only allow starring repeaters and roomservers
# sqlite3.Row objects use dictionary-style access with []
role = contact['role']
if role and role.lower() not in ('repeater', 'roomserver'):
conn.close()
return jsonify({'error': 'Only repeaters and roomservers can be starred'}), 400
# Toggle star status
# sqlite3.Row objects use dictionary-style access with []
current_starred = contact['is_starred']
new_star_status = 1 if not current_starred else 0
cursor.execute('''
UPDATE complete_contact_tracking
SET is_starred = ?
WHERE public_key = ?
''', (new_star_status, public_key))
conn.commit()
conn.close()
action = 'starred' if new_star_status else 'unstarred'
self.logger.info(f"Contact {contact['name']} ({public_key[:16]}...) {action}")
return jsonify({
'success': True,
'is_starred': bool(new_star_status),
'message': f'Contact {action} successfully'
})
except Exception as e:
self.logger.error(f"Error toggling star status: {e}", exc_info=True)
return jsonify({'error': str(e)}), 500
def _setup_socketio_handlers(self):
"""Setup SocketIO event handlers using modern patterns"""
@@ -1169,6 +1227,7 @@ class BotDataViewer:
snr, hop_count, first_heard, last_heard,
advert_count, is_currently_tracked,
raw_advert_data, signal_strength,
is_starred,
COUNT(*) as total_messages,
MAX(last_advert_timestamp) as last_message
FROM complete_contact_tracking
@@ -1176,7 +1235,7 @@ class BotDataViewer:
latitude, longitude, city, state, country,
snr, hop_count, first_heard, last_heard,
advert_count, is_currently_tracked,
raw_advert_data, signal_strength
raw_advert_data, signal_strength, is_starred
ORDER BY last_heard DESC
""")
@@ -1218,7 +1277,8 @@ class BotDataViewer:
'signal_strength': row['signal_strength'],
'total_messages': row['total_messages'],
'last_message': row['last_message'],
'distance': distance
'distance': distance,
'is_starred': bool(row['is_starred'] if row['is_starred'] is not None else 0)
})
# Get server statistics for daily tracking using direct database queries
@@ -419,6 +419,12 @@ class ModernContactsManager {
<td><span class="badge bg-success">${contact.advert_count || 0}</span></td>
<td>
<div class="btn-group" role="group">
${contact.role && (contact.role.toLowerCase() === 'repeater' || contact.role.toLowerCase() === 'roomserver') ?
`<button class="btn btn-sm ${contact.is_starred ? 'btn-warning' : 'btn-outline-warning'}" onclick="contactsManager.toggleStar('${contact.user_id.replace(/'/g, "\\'")}', this)" title="${contact.is_starred ? 'Unstar contact (removes path bias)' : 'Star contact (adds strong path bias)'}">
<i class="fas ${contact.is_starred ? 'fa-star' : 'fa-star'}"></i>
</button>` :
''
}
<button class="btn btn-sm btn-outline-info" onclick="contactsManager.viewAdvertData('${contact.user_id}')" title="View Advertisement Data">
<i class="fas fa-info-circle"></i>
</button>
@@ -882,6 +888,73 @@ class ModernContactsManager {
}
}
async toggleStar(userId, buttonElement = null) {
// Find the contact data
const contact = this.filteredData.find(c => c.user_id === userId);
if (!contact) {
this.showError('Contact data not found');
return;
}
// Get the button element - use passed element or find it
const button = buttonElement || document.querySelector(`button[onclick*="toggleStar('${userId.substring(0, 16)}"]`);
if (!button) {
this.showError('Button not found');
return;
}
// Disable button and show loading state
const originalHTML = button.innerHTML;
button.disabled = true;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
try {
// Call the toggle star API
const response = await fetch('/api/toggle-star-contact', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
public_key: userId
})
});
const data = await response.json();
if (!response.ok || !data.success) {
throw new Error(data.error || 'Failed to toggle star status');
}
// Update the contact data in our local array
const contactIndex = this.filteredData.findIndex(c => c.user_id === userId);
if (contactIndex !== -1) {
this.filteredData[contactIndex].is_starred = data.is_starred;
}
// Also update in the main contacts data
const mainContactIndex = this.contactsData.tracking_data.findIndex(c => c.user_id === userId);
if (mainContactIndex !== -1) {
this.contactsData.tracking_data[mainContactIndex].is_starred = data.is_starred;
}
// Re-render the table to show updated star status
this.renderContactsData();
// Show success message
const action = data.is_starred ? 'starred' : 'unstarred';
this.showSuccess(`Contact ${action} successfully. Path command will ${data.is_starred ? 'strongly prefer' : 'no longer prefer'} this repeater.`);
} catch (error) {
console.error('Error toggling star status:', error);
this.showError('Failed to toggle star status: ' + error.message);
} finally {
// Restore button state
button.disabled = false;
button.innerHTML = originalHTML;
}
}
showError(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'alert alert-danger alert-dismissible fade show';