Commit 956ae483 authored by Your Name's avatar Your Name

A lot of changes for the r22

parent 513abac6
...@@ -2,6 +2,25 @@ ...@@ -2,6 +2,25 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [10.0.22] - 2026-03-16
### Fixed
- **Critical**: Fixed extraction algorithm double processing of UNDER/OVER bets causing incorrect win amounts and report duplication
- **Bet Resolution Logic**: Corrected UNDER/OVER bet processing to ensure each bet is resolved exactly once with proper coefficient calculation
- **Extraction Association Filtering**: Implemented proper filtering to prevent UNDER/OVER outcomes from being processed in both dedicated and associated outcome sections
### Enhanced
- **Extraction Algorithm Robustness**: Improved bet result update logic to handle edge cases and ensure consistent win amount calculations
- **Report Accuracy**: Eliminated double counting of UNDER/OVER bets in statistics and reports
- **Version Management**: Updated version numbers across all components (main.py, build.py, web dashboard, settings, __init__.py)
- **User Agent String**: Updated user agent from MbetterClient/1.0r21 to MbetterClient/10.0r22
### Technical Details
- **Bet Processing Architecture**: Separated UNDER/OVER bet handling from complex outcome associations to prevent duplication
- **Database Transaction Safety**: Ensured all bet updates occur within proper transaction boundaries
- **Coefficient Calculation**: Maintained accurate win amount calculations using match outcome coefficients
- **Cross-Component Version Sync**: Synchronized version numbers across CLI, build system, web interface, and internal modules
## [1.0.13] - 2026-02-02 ## [1.0.13] - 2026-02-02
### Changed ### Changed
......
...@@ -14,7 +14,7 @@ from typing import List, Dict, Any ...@@ -14,7 +14,7 @@ from typing import List, Dict, Any
# Build configuration # Build configuration
BUILD_CONFIG = { BUILD_CONFIG = {
'app_name': 'MbetterClient', 'app_name': 'MbetterClient',
'app_version': '1.0.16', 'app_version': '10.0.22',
'description': 'Cross-platform multimedia client application', 'description': 'Cross-platform multimedia client application',
'author': 'MBetter Team', 'author': 'MBetter Team',
'entry_point': 'main.py', 'entry_point': 'main.py',
......
...@@ -217,7 +217,7 @@ Examples: ...@@ -217,7 +217,7 @@ Examples:
parser.add_argument( parser.add_argument(
'--version', '--version',
action='version', action='version',
version='MbetterClient 1.0.20' version='MbetterClient 10.0.22'
) )
# Timer options # Timer options
......
...@@ -4,7 +4,7 @@ MbetterClient - Cross-platform multimedia client application ...@@ -4,7 +4,7 @@ MbetterClient - Cross-platform multimedia client application
A multi-threaded application with video playback, web dashboard, and REST API integration. A multi-threaded application with video playback, web dashboard, and REST API integration.
""" """
__version__ = "1.0.20" __version__ = "10.0.22"
__author__ = "MBetter Project" __author__ = "MBetter Project"
__email__ = "dev@mbetter.net" __email__ = "dev@mbetter.net"
__description__ = "Cross-platform multimedia client with video overlay and web dashboard" __description__ = "Cross-platform multimedia client with video overlay and web dashboard"
......
...@@ -2352,33 +2352,34 @@ class APIClient(ThreadedComponent): ...@@ -2352,33 +2352,34 @@ class APIClient(ThreadedComponent):
logger.error(f"Failed to refresh endpoints from config: {e}") logger.error(f"Failed to refresh endpoints from config: {e}")
def _get_last_fixture_timestamp(self) -> Optional[str]: def _get_last_fixture_timestamp(self) -> Optional[str]:
"""Get the server activation timestamp of the last active fixture in the database""" """Get the server activation timestamp of the last active fixture template in the database"""
try: try:
# Update heartbeat before database operation # Update heartbeat before database operation
self.heartbeat() self.heartbeat()
session = self.db_manager.get_session() session = self.db_manager.get_session()
try: try:
# Get the most recent match with fixture_active_time set # Get the most recent match template with fixture_active_time set
last_active_match = session.query(MatchModel).filter( # Use MatchTemplateModel (templates table) to track what we've synced from server
MatchModel.fixture_active_time.isnot(None) last_active_template = session.query(MatchTemplateModel).filter(
).order_by(MatchModel.fixture_active_time.desc()).first() MatchTemplateModel.fixture_active_time.isnot(None)
).order_by(MatchTemplateModel.fixture_active_time.desc()).first()
# Update heartbeat after database query # Update heartbeat after database query
self.heartbeat() self.heartbeat()
if last_active_match and last_active_match.fixture_active_time: if last_active_template and last_active_template.fixture_active_time:
# Return Unix timestamp as string (long integer number) # Return Unix timestamp as string (long integer number)
return str(last_active_match.fixture_active_time) return str(last_active_template.fixture_active_time)
else: else:
# No fixtures with activation time found - don't send 'from' parameter # No fixture templates with activation time found - don't send 'from' parameter
return None return None
finally: finally:
session.close() session.close()
except Exception as e: except Exception as e:
logger.error(f"Failed to get last fixture activation timestamp: {e}") logger.error(f"Failed to get last fixture template activation timestamp: {e}")
# Update heartbeat even on error # Update heartbeat even on error
self.heartbeat() self.heartbeat()
return None return None
......
...@@ -262,7 +262,7 @@ class ApiConfig: ...@@ -262,7 +262,7 @@ class ApiConfig:
# Request settings # Request settings
verify_ssl: bool = True verify_ssl: bool = True
user_agent: str = "MbetterClient/1.0r20" user_agent: str = "MbetterClient/10.0r22"
max_response_size_mb: int = 100 max_response_size_mb: int = 100
# Additional API client settings # Additional API client settings
...@@ -403,7 +403,7 @@ class AppSettings: ...@@ -403,7 +403,7 @@ class AppSettings:
timer: TimerConfig = field(default_factory=TimerConfig) timer: TimerConfig = field(default_factory=TimerConfig)
# Application settings # Application settings
version: str = "1.0.19" version: str = "10.0.22"
debug_mode: bool = False debug_mode: bool = False
dev_message: bool = False # Enable debug mode showing only message bus messages dev_message: bool = False # Enable debug mode showing only message bus messages
debug_messages: bool = False # Show all messages passing through the message bus on screen debug_messages: bool = False # Show all messages passing through the message bus on screen
......
...@@ -3718,6 +3718,8 @@ class GamesThread(ThreadedComponent): ...@@ -3718,6 +3718,8 @@ class GamesThread(ThreadedComponent):
# Filter winning outcomes to only include those available in the match fixture # Filter winning outcomes to only include those available in the match fixture
extraction_winning_outcome_names = [outcome for outcome in extraction_winning_outcome_names if outcome in available_outcome_names] extraction_winning_outcome_names = [outcome for outcome in extraction_winning_outcome_names if outcome in available_outcome_names]
# Filter out UNDER/OVER as they are ALWAYS handled separately in the UNDER/OVER logic
extraction_winning_outcome_names = [outcome for outcome in extraction_winning_outcome_names if outcome not in ['UNDER', 'OVER']]
logger.info(f"🏆 [EXTRACTION DEBUG] Winning outcomes for result '{selected_result}': {extraction_winning_outcome_names}") logger.info(f"🏆 [EXTRACTION DEBUG] Winning outcomes for result '{selected_result}': {extraction_winning_outcome_names}")
# Log expected redistribution and actual selected payout # Log expected redistribution and actual selected payout
...@@ -3824,21 +3826,13 @@ class GamesThread(ThreadedComponent): ...@@ -3824,21 +3826,13 @@ class GamesThread(ThreadedComponent):
try: try:
from ..database.models import MatchOutcomeModel from ..database.models import MatchOutcomeModel
# For UNDER/OVER outcomes, get from fixture coefficients # For all outcomes (including UNDER/OVER), get from match outcomes
if outcome in ['UNDER', 'OVER']:
fixture_id = session.query(MatchModel.fixture_id).filter(MatchModel.id == match_id).first()
if fixture_id:
return self._get_fixture_coefficients(fixture_id[0], session)[0 if outcome == 'UNDER' else 1] or 1.0
return 1.0
# For other outcomes, get from match outcomes
match_outcome = session.query(MatchOutcomeModel).filter( match_outcome = session.query(MatchOutcomeModel).filter(
MatchOutcomeModel.match_id == match_id, MatchOutcomeModel.match_id == match_id,
MatchOutcomeModel.column_name == outcome MatchOutcomeModel.column_name == outcome
).first() ).first()
return match_outcome.float_value if match_outcome else 1.0 return match_outcome.float_value if match_outcome else 1.0
except Exception as e: except Exception as e:
logger.error(f"Failed to get coefficient for outcome {outcome}: {e}") logger.error(f"Failed to get coefficient for outcome {outcome}: {e}")
return 1.0 return 1.0
...@@ -3869,8 +3863,12 @@ class GamesThread(ThreadedComponent): ...@@ -3869,8 +3863,12 @@ class GamesThread(ThreadedComponent):
logger.info(f"DEBUG _update_bet_results: Found {len(under_over_bets)} winning {under_over_outcome} bets") logger.info(f"DEBUG _update_bet_results: Found {len(under_over_bets)} winning {under_over_outcome} bets")
# Get coefficient for the UNDER/OVER outcome
under_over_coefficient = self._get_outcome_coefficient(match_id, under_over_outcome, session)
logger.info(f"DEBUG _update_bet_results: under_over_coefficient = {under_over_coefficient}")
for bet in under_over_bets: for bet in under_over_bets:
win_amount = bet.amount * win_coefficient win_amount = bet.amount * under_over_coefficient
bet.result = 'win' bet.result = 'win'
bet.win_amount = win_amount bet.win_amount = win_amount
logger.info(f"DEBUG _update_bet_results: Set bet {bet.id} to win with amount {win_amount}") logger.info(f"DEBUG _update_bet_results: Set bet {bet.id} to win with amount {win_amount}")
...@@ -3913,8 +3911,8 @@ class GamesThread(ThreadedComponent): ...@@ -3913,8 +3911,8 @@ class GamesThread(ThreadedComponent):
logger.info(f"DEBUG _update_bet_results: Updating bets for {len(extraction_winning_outcome_names)} associated winning outcomes: {extraction_winning_outcome_names}") logger.info(f"DEBUG _update_bet_results: Updating bets for {len(extraction_winning_outcome_names)} associated winning outcomes: {extraction_winning_outcome_names}")
for outcome_name in extraction_winning_outcome_names: for outcome_name in extraction_winning_outcome_names:
# Skip if this outcome is already handled above (selected_result) # Skip if this outcome is already handled above (selected_result or UNDER/OVER when under_over_outcome is set)
if outcome_name == selected_result: if outcome_name == selected_result or (outcome_name in ['UNDER', 'OVER'] and under_over_outcome is not None):
continue continue
# Get coefficient for this associated outcome # Get coefficient for this associated outcome
......
...@@ -230,7 +230,7 @@ class WebDashboard(ThreadedComponent): ...@@ -230,7 +230,7 @@ class WebDashboard(ThreadedComponent):
def inject_globals(): def inject_globals():
return { return {
'app_name': 'MbetterClient', 'app_name': 'MbetterClient',
'app_version': '1.0.16', 'app_version': '10.0.22',
'current_time': time.time(), 'current_time': time.time(),
} }
......
...@@ -42,12 +42,22 @@ def is_bet_detail_winning(detail, match, session): ...@@ -42,12 +42,22 @@ def is_bet_detail_winning(detail, match, session):
Returns: Returns:
bool: True if the bet detail is winning bool: True if the bet detail is winning
""" """
# Direct win check # Direct win check - this is the authoritative source set by _update_bet_results
if detail.result in ['won', 'win']: if detail.result in ['won', 'win']:
return True return True
# Check if match has extraction data # If result is not 'win', check other conditions only for non-UNDER/OVER bets
# For UNDER/OVER bets, we rely solely on the direct result check above
if match: if match:
# Check if this is an UNDER/OVER outcome
is_under_over = detail.outcome and ('UNDER' in detail.outcome.upper() or 'OVER' in detail.outcome.upper())
# For UNDER/OVER bets, we've already checked the direct result above
# No need to check winning_outcomes or under_over_result as they could cause duplicate detection
if is_under_over:
return False
# For non-UNDER/OVER bets, check other conditions
# Check winning_outcomes array (contains all winning outcomes from extraction) # Check winning_outcomes array (contains all winning outcomes from extraction)
if match.winning_outcomes: if match.winning_outcomes:
try: try:
...@@ -63,10 +73,6 @@ def is_bet_detail_winning(detail, match, session): ...@@ -63,10 +73,6 @@ def is_bet_detail_winning(detail, match, session):
except (json.JSONDecodeError, TypeError): except (json.JSONDecodeError, TypeError):
pass pass
# Check under_over_result
if match.under_over_result and detail.outcome == match.under_over_result:
return True
# Check extraction associations if match has extraction result # Check extraction associations if match has extraction result
if match.result: if match.result:
from ..database.models import ExtractionAssociationModel from ..database.models import ExtractionAssociationModel
......
# MbetterClient v1.0.16
Cross-platform multimedia client application
## Installation
1. Extract this package to your desired location
2. Run the executable file
3. The application will create necessary configuration files on first run
## System Requirements
- **Operating System**: Linux 6.12.15-amd64
- **Architecture**: x86_64
- **Memory**: 512 MB RAM minimum, 1 GB recommended
- **Disk Space**: 100 MB free space
## Configuration
The application stores its configuration and database in:
- **Windows**: `%APPDATA%\MbetterClient`
- **macOS**: `~/Library/Application Support/MbetterClient`
- **Linux**: `~/.config/MbetterClient`
## Web Interface
By default, the web interface is available at: http://localhost:5001
Default login credentials:
- Username: admin
- Password: admin
**Please change the default password after first login.**
## Support
For support and documentation, please visit: https://git.nexlab.net/mbetter/mbetterc
## Version Information
- Version: 1.0.16
- Build Date: sissy
- Platform: Linux-6.12.15-amd64-x86_64-with-glibc2.42
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment