Riftsync - Cloud Settings Sync App for League of Legends
Electron app that syncs League of Legends settings across accounts using AWS API Gateway, Lambda, S3, and Riot’s LCU API.
1. Overview
League stores settings in PersistedSettings.json on
the local pc when an account logs into the league client. Riftsync
makes it easier to keep the same settings across PCs and accounts.
2. System Design
On launch, Riftsync:
- Reads the Riot lockfile to authenticate into LCU API
- Identifies the correct settings path for the logged-in account
- Pushes or pulls the JSON file via AWS API Gateway, Lambda, and S3
3. Push Request Flow
- Initiated when the user clicks the Push Settings button.
- 1. The Riftsync client sends a request to the League client API to get the Riot ID for the currently logged-in account.
- 2. The League client API responds to the Riftsync client with the Riot ID.
-
3. The Riftsync client retrieves
PersistedSettings.jsonfrom the local file system. -
4. The Riftsync client sends a
POSTrequest with the settings to AWS API Gateway. - 5. API Gateway invokes a push Lambda function.
- 6. The Lambda function writes the settings file to S3 using an object key that corresponds to the user’s Riot ID.
- 7. S3 responds to the Lambda function indicating that the upload was successful.
- 8. The Lambda function returns a success response to API Gateway.
- 9. API Gateway responds to the Riftsync client, and the app displays to the user that the push completed successfully.
3.1 Lambda Push Function
Here are the contents of the Lambda push function that creates an S3 object containing the settings JSON.
import json
import os
import boto3
import requests
import urllib.parse
from get_puuid import get_puuid
def lambda_handler(event, context):
bucket_name = 'riftsync-settings-bucket'
body_json = event.get('body')
if body_json:
try:
body = json.loads(body_json)
except Exception:
body = {}
else:
body = {}
gameName = body.get('gameName')
tagLine = body.get('tagLine')
settings_content = body.get('settings_content')
# Validate required fields
if not gameName or not tagLine or not settings_content:
return {
'statusCode': 400,
'body': json.dumps('Error: gameName, tagLine, and settings_content are required.')
}
# Get PUUID using helper function
try:
puuid = get_puuid(gameName, tagLine)
except Exception as e:
return {
'statusCode': 400,
'body': json.dumps(f"Error fetching PUUID from Riot API: {str(e)}")
}
# Create S3 client and store settings
s3 = boto3.client('s3')
print(f"PUUID: {puuid} gameName: {gameName} tagLine: {tagLine}")
try:
s3.put_object(
Bucket=bucket_name,
Key=puuid,
Body=settings_content
)
return {
'statusCode': 200,
'body': json.dumps('Object created successfully')
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps(f'Error: {str(e)}')
}
4. Pull Request Flow
- Initiated when the user enters a valid Riot ID into the Pull Settings text box and clicks the pull button.
- 1. The Riftsync client sends a request to the League client API to confirm that a user is currently logged in.
- 2. The League client API responds to the Riftsync client indicating whether a user is logged in. If there is no logged-in user, the flow stops here.
-
3. If a user is logged in, the Riftsync client
sends a
GETrequest to AWS API Gateway, including the Riot ID the user entered. - 4. API Gateway invokes a pull Lambda function.
- 5. The Lambda function reads the settings file from S3 corresponding to the specified Riot ID.
- 6. S3 responds to the Lambda function with the settings file.
- 7. The Lambda function returns the settings JSON to API Gateway.
- 8. API Gateway responds to the Riftsync client with the settings payload.
-
9. The Riftsync client writes the downloaded
JSON to the local
PersistedSettings.json, updating the local settings.
4.1 Lambda Pull Function
Here are the contents of the Lambda pull function that retrieves the settings JSON from S3.
import json
import os
import boto3
import requests
import urllib.parse
from get_puuid import get_puuid
def lambda_handler(event, context):
bucket_name = 'riftsync-settings-bucket'
params = event.get('queryStringParameters') or {}
gameName = params.get('gameName')
tagLine = params.get('tagLine')
# Validate required fields
if not gameName or not tagLine:
return {
'statusCode': 400,
'body': json.dumps('Error: gameName, and tagLine are required.')
}
# Get PUUID using helper function
try:
puuid = get_puuid(gameName, tagLine)
except Exception as e:
return {
'statusCode': 400,
'body': json.dumps(f"Error fetching PUUID from Riot API: {str(e)}")
}
# Create S3 client and pull settings
s3 = boto3.client('s3')
print(f"PUUID: {puuid} gameName: {gameName} tagLine: {tagLine}")
try:
response = s3.get_object(Bucket=bucket_name, Key=puuid)
contents = response['Body'].read().decode('utf-8')
return {
'statusCode': 200,
'body': contents # No json.dumps() here!
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}
5. Demo
Push & Pull actions shown in the app.
6. Takeaways & Future Work
What I gained from this project:
- Practical experience wiring together AWS API Gateway, Lambda, and S3 to push and pull user settings in a real application.
- Hands-on familiarity with the Lambda development workflow. This includes iterating on code locally, packing dependencies, and deploying updates to Lambda.
Future improvements I’d like to explore:
- Integrating Riot’s OAuth2 flow so that only the authenticated owner of an account can read or write the S3 object associated with that account’s settings.
- Adding proper code signing (e.g., a trusted code-signing certificate and signed installer) so Windows Defender / SmartScreen treats the app as a trusted application instead of warning users on first run.
- Packaging and distributing Riftsync through a public website with a simple download page so other players can easily install and update the app.