Skip to content

Instantly share code, notes, and snippets.

@NanoAi
Last active April 3, 2022 08:08
Show Gist options
  • Save NanoAi/cbc79b2fa694a079bdc2588c46746681 to your computer and use it in GitHub Desktop.
Save NanoAi/cbc79b2fa694a079bdc2588c46746681 to your computer and use it in GitHub Desktop.
A Python Script that uses League APIs to attempt to close the process when the game ends, without playing out the ending animation. Because alt+f4 is now disabled... READ THE FIRST COMMENT!
from lcu_driver import Connector
import requests, webbrowser, psutil, asyncio, re
# This will automatically open porofessor & League of Graphs in champ select.
# -- ALSO --
# This will poll the League In-Game API, and watch the GameFlow sockets
# to automatically end the process of your game when the game ends.
### Comment out as you see fit!
## However: I still think using autohotkey to just kill the process with a key
## press is better...
# Set your League Directory Here.
LEAGUE_DIR = 'C:\\Riot Games\\League of Legends\\'
# Make sure to escape your backslashes by doubling them up `\` = `\\`.
# There must be a `\\` at the end of the path.
# Set this to `False` if you don't want the game to try to autoclose when it ends.
ENABLE_AUTOCLOSE = True
# This will also fix any issues with process hanging.
# I think pressing alt+f4 or using autohotkey is faster.
LOCALHOST = 'localhost'
GAME_STATUS = ''
GOT_LOBBY = False
HAS_PICKED = False
GAME_PROCESS_ID = False
GET_TASK = False
lolLock = open(LEAGUE_DIR + 'lockfile', 'r')
GAMEAPI_LOCK = lolLock.read().rsplit(':')
GAMEAPI_PORT = GAMEAPI_LOCK[2]
lolLock.close()
print('Detected Port: ' + GAMEAPI_PORT)
def killProcessId():
global GAME_PROCESS_ID
if GAME_PROCESS_ID:
proc = psutil.Process(GAME_PROCESS_ID)
proc.terminate()
proc.kill()
GAME_PROCESS_ID = False
return
async def getInGameAPI():
global GAME_PROCESS_ID, GAMEAPI_PORT
while( GAMEAPI_PORT == '' ):
await asyncio.sleep(5)
for proc in psutil.process_iter(['pid', 'name']):
try:
if proc.is_running and 'League of Legends' in proc.name():
for connection in proc.connections():
if connection.status == psutil.CONN_LISTEN:
GAMEAPI_PORT = str(connection.laddr.port)
GAME_PROCESS_ID = proc.pid
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
return GAMEAPI_PORT
async def getInGameEvents():
global GAME_PROCESS_ID
while( True ):
await asyncio.sleep(0.5)
try:
response = requests.get(f'https://{LOCALHOST}:{GAMEAPI_PORT}' + '/liveclientdata/eventdata', verify=False)
print("REQ_STATUS: " + str(response.status_code), flush=True)
if response.status_code == requests.codes.ok:
for event in response.json()['Events']:
if event['EventName'] == 'GameEnd':
print(event, flush=True)
killProcessId()
GET_TASK.cancel()
break
except (ConnectionRefusedError):
print("\n!!!!!CLOSING!!!!", flush=True)
GET_TASK.cancel()
return
conn = Connector()
# fired when League Client is closed (or disconnected from websocket)
@conn.close
async def disconnect(_):
await conn.stop()
quit()
# START - LeagueOfGraphs
@conn.ws.register('/lol-champ-select/v1/session', event_types=('UPDATE',))
async def champSelectSession(connection, event):
global GOT_LOBBY, HAS_PICKED, BROWSER
lolGraphsURL = 'https://www.leagueofgraphs.com/champions/probuilds/'
teamSearchURL = 'https://na.op.gg/multi/query='
allyNames = ''
if not HAS_PICKED:
pick = await connection.request('get', '/lol-champ-select/v1/summoners/' + str(event.data['localPlayerCellId']))
if pick.status == 200:
outputJson = await pick.json()
if outputJson['isDonePicking'] == True:
print('CHAMPION PICKED: ' + outputJson['championName'], flush=True)
webbrowser.open( lolGraphsURL + re.sub(r'[^\w]', '', outputJson['championName']).strip().lower(), new=0, autoraise=False )
HAS_PICKED = True
if GOT_LOBBY == True: return
userIds = []
summonerNames = []
for player in event.data['myTeam']:
GOT_LOBBY = True
userIds.append(str(player['summonerId']))
userIdsStringify = ','.join(map(str, userIds))
summoner = await connection.request('get', f'/lol-summoner/v2/summoner-names?ids=[{userIdsStringify}]')
if summoner.status == 200:
for summonerInfo in await summoner.json():
summonerNames.append(summonerInfo['displayName'])
allyNames = ','.join(map(str, summonerNames))
print( 'TEAM:', allyNames, flush=True )
# webbrowser.open( 'https://porofessor.gg/pregame/na/' + allyNames )
webbrowser.open( teamSearchURL + allyNames, new=1, autoraise=False )
# END - LeagueOfGraphs
@conn.ws.register('/lol-gameflow/v1/gameflow-phase', event_types=('UPDATE',))
async def gameFlowStateChange(connection, event):
global GAME_STATUS, GET_TASK, GAME_PROCESS_ID, GOT_LOBBY, HAS_PICKED
global ENABLE_AUTOCLOSE
GAME_STATUS = event.data
print('GAME STATE: ' + event.data, flush=True)
# Waits for the game to be in progress and starts the InGameAPI polling
# Comment set `ENABLE_AUTOCLOSE` to `False` to disable.
if ENABLE_AUTOCLOSE and GAME_STATUS == "InProgress":
await asyncio.sleep(10)
GAMEAPI_PORT = await getInGameAPI()
print('GAMEAPI_PORT: ' + GAMEAPI_PORT, flush=True)
print('LISTENER: Starting Tasks', flush=True)
GET_TASK = asyncio.create_task(getInGameEvents())
if GAME_STATUS == 'WaitingForStats' or GAME_STATUS == 'None':
GOT_LOBBY = False
HAS_PICKED = False
if GAME_PROCESS_ID:
killProcessId()
if GET_TASK and GET_TASK.done():
print('\nLISTENER: Closing Tasks', flush=True)
GET_TASK.cancel()
# starts the connector
conn.start()
@NanoAi
Copy link
Author

NanoAi commented Oct 31, 2020

Requirements

This requires lcu_driver from https://github.com/sousa-andre/lcu-driver to work, if it doesn't work you're probably missing that requirement.

Make sure you also have the following... requests, webbrowser, psutil, asyncio.

Run this with python -W ignore LeagueListener.py unless you want to get your console spammed with warnings.

Testing

This was tested on Windows with MINGW64 and $ pip install -U psutil==5.6.5, the Python Version was Python 3.8.5.

Updates

1.0

  1. I didn't want users to have to install extra stuff such as browser drivers, so this will still open stuff in a new tab and bring your browser to the front. Sorry.
  2. Now automatically finds the LCU API port.
  3. Now uses OP.GG (for party) & League Of Graphs (for champion) by default.
  4. Prints the names of everyone in your lobby. (Sorry for encoding limitations).

Endorsement

LeagueListener.py isn’t endorsed by Riot Games and doesn’t reflect the views or opinions of Riot Games or anyone officially involved in producing or managing League of Legends. League of Legends and Riot Games are trademarks or registered trademarks of Riot Games, Inc. League of Legends © Riot Games, Inc.

@Avnsx
Copy link

Avnsx commented Apr 3, 2022

As soon as the the ingame client starts I only keep getting REQ_STATUS: 404 @NanoAi

@Avnsx
Copy link

Avnsx commented Apr 3, 2022

Has this ever worked?

response = requests.get(f'https://{LOCALHOST}:{GAMEAPI_PORT}' + '/liveclientdata/eventdata', verify=False)

Because nowadays the ingame client is hardset to port 2999, but I was trying to use this code in hopes that you were somehow able to communicate with the league ingame client on a different port @NanoAi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment