USCF Chess Rating Monitor

Published on 1 December 2024 at 19:18

This post is obsolete as I just found out that USCF has a feature to email you when your rating changes - I mean why wouldn't they?  I am leaving the post here as a reference should someone want/need to extract ratings for other players from the site.  If you have a team/club and want to keep track of everyone's rating, this can be easily extended to pull the data from the site for each chess id.

 

 

Chess results take hours to days to post on the USCF site depending on when the tournament director posts the results.  Instead of monitoring the site every few hours to see if there is an update, this tool polls the site every few minutes and sends you a notification when the rating changes.

 

This tool is VERY complicated to setup, but the basic steps are

  1. Create a free AWS Account (do not need a paid version)
  2. Create a lambda
  3. Enter the code below and adjust for your USCF ID
  4. Be ready to debug and pull your hair out - may require a degree in CS or help from a kid that knows Python

 

This tool meets our own needs, but If there is sufficient interest, I will make this into a web app usable by anyone and support notification via email or txt message.  Send me an email if you would like to see this made into an app.

 

chesstools.net@gmail.com

 

 

 

###################Begin Python Code#########################

 

import json
import requests
#from botocore.vendored import requests
import time
import boto3

from bs4 import BeautifulSoup, Comment

def lambda_handler(event, context):  
    ##################

    # Update slack secret key here

    ##################
    slack_webhook_url = "https://hooks.slack.com/services/T5BND675M/B06S7UX2UCD/<SECRET KEY>"
    #send_slack_notification(slack_webhook_url, "test message")
    check_count(slack_webhook_url)

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }


# Function to send notification to Slack via webhook
def send_slack_notification(webhook_url, message):
    payload = {
        "text": message
    }
    response = requests.post(webhook_url, json=payload)
    if response.status_code != 200:
        print(f"Failed to send notification to Slack. Status code: {response.status_code}")

# Function to get the current count
def get_count():

    ####################

    # Update USCF ID HERE

    ####################

    url = "https://www.uschess.org/msa/MbrDtlTnmtHst.php?<USCF_CHESS_ID>"
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')
    # Find all <b> tags
    b_tags = soup.find_all('b')
    # Iterate through <b> tags to find the one containing the desired text
    for b in b_tags:
        if "Events for this player since late 1991:" in b.text:
            count_text = b.text
            break
    else:
        raise ValueError("Text not found")
    # Extract the count from the text
    count = int(count_text.split(":")[-1].strip())
    return count


def extract_detail(html_response):
    # Parse the HTML
    soup = BeautifulSoup(html_response, 'html.parser')

    # Find the comment containing the desired detail
    detail_comment = soup.find(string=lambda text: isinstance(text, Comment) and 'Detail: 1' in text)

    if detail_comment:
        print(detail_comment)
        # Extract the detail from the comment's parent element
        detail_table = detail_comment.find_parent('table')


        if detail_table:
            detail_rows = detail_table.find_all('tr')[2:3]
            detail_string = ""
            for row in detail_rows:
                cells = row.find_all('td')
                if cells:
                    end_date = cells[0].get_text().strip()
                    event_name = cells[1].get_text().strip()
                    regular_rating = cells[2].get_text().strip()
                    quick_rating = cells[3].get_text().strip()
                    blitz_rating = cells[4].get_text().strip()
                    detail_string += f"End Date: {end_date}\n"
                    detail_string += f"Event Name: {event_name}\n"
                    detail_string += f"Regular Rating: {regular_rating}\n"
                    detail_string += f"Quick Rating: {quick_rating}\n"
                    detail_string += f"Blitz Rating: {blitz_rating}\n"
            return detail_string.strip()
        else:
            print("detail table not found")
    else:
        print("Detail not found")
        return "Detail not found."

def get_count_and_table_data():

    # #######################
    # Set USCF ID HERE
    # #######################
    url = "https://www.uschess.org/msa/MbrDtlTnmtHst.php?<USCF_CHESS_ID>"
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')

    # Get the count
    b_tags = soup.find_all('b')
    for b in b_tags:
        if "Events for this player since late 1991:" in b.text:
            count_text = b.text
            break
    else:
        raise ValueError("Text not found")
    count = int(count_text.split(":")[-1].strip())

    table_data = extract_detail(response.content)
    print(table_data)

    return count, table_data

def store_count_to_s3(count):
    # Initialize the S3 client
    s3_client = boto3.client('s3')

    # Specify the bucket name and object key
    bucket_name = 'uscf-chess-monitor'
    object_key = 'count.txt'

    # Convert count to string before storing
    count_str = str(count)

    # Upload count to S3
    s3_client.put_object(Bucket=bucket_name, Key=object_key, Body=count_str)

def retrieve_count_from_s3():
    # Initialize the S3 client
    s3_client = boto3.client('s3')

    # Specify the bucket name and object key
    bucket_name = 'uscf-chess-monitor'
    object_key = 'count.txt'

    try:
        # Retrieve count from S3
        response = s3_client.get_object(Bucket=bucket_name, Key=object_key)
        count_str = response['Body'].read().decode('utf-8')
        count = int(count_str)
        return count
    except Exception as e:
        # Handle exceptions (e.g., object not found)
        print(f"Error retrieving count from S3: {e}")
        return None
       

# Function to continuously check for the count increase and send Slack notification
def check_count(webhook_url):
    current_count = retrieve_count_from_s3()
    if current_count == None:
        message = "Failed to get current count from S3, defaulting to 0"
        current_count=0
        send_slack_notification(webhook_url, message)
        print(message)

    new_count, new_table_data = get_count_and_table_data()
    print("New Count: ("+str(new_count)+") current count ("+str(current_count)+")")
    if new_count > current_count:
        message = f"The count has increased to {new_count}. Latest results:\n{new_table_data}"
        print("Sending slack notification of new count")
        print("----------------------")
        print(message)
        print("----------------------")
        send_slack_notification(webhook_url, message)
        current_count = new_count
        store_count_to_s3(new_count)


###################End Python Code#########################

Create Your Own Website With Webador