mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-10-23 22:32:29 +00:00
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:
1 parent
732b7551ba
commit
82aa5ee160
8 files changed
+1127
-17
No files matched your search
@@ -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';
|
||||
|
||||
Reference in new issue
Block a user