An all-inclusive Python framework for the Riot Games League of Legends API. We focus on making the data easy and fun to work with, while providing all the tools necessary to create a website or do data analysis.

Overview

MIT Licensed Documentation Status DOI

Cassiopeia

A Python adaptation of the Riot Games League of Legends API (https://developer.riotgames.com/).

Cassiopeia is the sister library to Orianna (Java). It's been designed with usability in mind - making sure all the bookkeeping is done right so you can focus on getting the data you need and building your application.

Installation

pip install cassiopeia or see here for more information.

Match-V5 Update

On Monday, September 13th, 2021 Riot deprecated the match-v4 endpoints in favor of match-v5. The codebase was migrated to use the new endpoints, but it's likely that some bugs exist due to all the complex functionality between data types in Cass. Please submit PRs (preferably) or issues if you find bugs, and feel free to message the developers by creating an issue if you would like to contribute but don't know how to go about fixing a bug.

Why use Cass?

  • An excellent user interface that makes working with data from the Riot API easy and fun.

  • "Perfect" rate limiting.

  • Guaranteed optimal usage of your API key.

  • Built in caching and (coming) the ability to easily hook into a database for offline storage of data.

  • Extendability to non-Riot data. Because Cass is a framework and not just an API wrapper, you can integrate your own data sources into your project. Cass already supports Data Dragon and the champion.gg API in addition to the Riot API.

  • Dynamic settings so you can configure Cass for your specific use case.

Documentation and Examples

Cassiopeia's documentation and examples may be a little out of date, but should be quite helpful when getting started. Please submit a PR for any changes.

Example

Here's an example of a basic use of the API. The full documentation can be found at http://cassiopeia.readthedocs.org/en/latest/.

import random

import cassiopeia as cass

cass.set_riot_api_key("YOUR_KEY")  # This overrides the value set in your configuration/settings.

summoner = cass.get_summoner(name="Perkz", region="NA")
print("{name} is a level {level} summoner on the {region} server.".format(name=summoner.name,
                                                                          level=summoner.level,
                                                                          region=summoner.region))

champions = cass.get_champions(region="NA")
random_champion = random.choice(champions)
print("He enjoys playing champions such as {name}.".format(name=random_champion.name))

challenger_league = cass.get_challenger_league(queue=cass.Queue.ranked_solo_fives)
best_na = challenger_league[0].summoner
print("He's not as good as {name} at League, but probably a better python programmer!".format(name=best_na.name))

Django web Framework

There is an integration of cassiopeia to the popular python web framework Django made by Mori(Paaksing), this integration is aimed to fix most issues/conflicts related to co-ocurrence of cassiopeia and Django. In this integration will give you better tools for building your Django/DRF based app, you will have the ability to use any production tested cache backends that Django's cache framework supports.

A datastore called Omnistone is introduced in response to issue #1 of this repo, this is a refined version of Cache that automatically deletes expired objects when MAX_ENTRIES is hit, then culls the datastore according to the CULL_FRECUENCY given. The culling strategy used is the same as Django Cache Framework, which is LRU culling (Least Recently Used).

  • Link to django-cassiopeia repository (If you love using it, make sure to star!).
  • Link to django-cassiopeia documentations (Production Release v2.0).
  • If you have any issues or feature requests with django-cassiopeia, tag Mori in our discord server, or fire an issue in the repository.

Unfortunately, we currently don't have an integration to Flask and any contribution is welcome.

Questions/Contributions

Feel free to send pull requests or to contact us via github or discord. More information can be found in our documentation.

Bugs

If you find bugs please let us know via an issue or pull request. If you would like to help maintain Cassiopeia, let us know and we will invite you to our discord server.

Citing Cassiopeia

If you used Cassiopeia for your research, please cite the project.

Support Us

If you've loved using Cassiopeia, consider supporting us through PayPal or Patreon.

Disclaimer

Cassiopeia 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.

Comments
  • Fixes more issues with python 27

    Fixes more issues with python 27

    Resolves issue #25

    TODO

    • [x] Write a few tests
    • [x] Get passing on CircleCI
    • [x] Get passing using Python 3
    • [x] Conditional imports (Python 3 wont have future)
    • [x] TRIED!! Add super() overrides to some __init__ more simply
    • [x] Fix str/unicode problem
    opened by ckcollab 24
  • Rate limiters

    Rate limiters "leaks" calls

    Testing a (single-threaded) script of mine I got blacklisted( 2 times!!) overflow of limit

    Did not make any call from the web interface. Just run the program and went to sleep. (No API calls in the previous hours either). All the requests went through the baseriotapi.

    I don't think the problem is related to https://github.com/robrua/cassiopeia/pull/14 , as my program is not multi thread and thus there can not happen the race condition.

    At a first glance I can not spot the problem, but I'm not really an export in python threading.

    opened by MakersF 23
  • Summoner Match History Limited to 20

    Summoner Match History Limited to 20

    Hello,

    I am coming back to work with Cass since the Cass 3.0/Riot v5 update.

    Previously I was able to make a call in the following fashion to get every match for a particular summoner

    summoner = cass.get_summoner(name=summoner_name, region="NA")
    total_games = len(summoner.match_history)
    

    For some reason now I only get 20 matches back regardless of settings. Documentation seems to still support that it should return all matches and not just the latest 20, but maybe I'm missing something?

    opened by WeldFire 18
  • Bug : Some statistics on champions are not accessible

    Bug : Some statistics on champions are not accessible

    Good morning,

    I tried to use your library to get some information on base stats champions and I had trouble accessing these informations :+1:

    • 'StatsData' object has no attribute 'criticalStrikeChance'
    • 'StatsData' object has no attribute 'attackSpeedOffset'
    • 'StatsData' object has no attribute 'attackDamage'

    The code I used was like this :

    champions = cass.get_champions() champions[0].stats.attack_damage champions[0].stats.critical_strike_chance champions[0].stats.attack_speed

    I didn't try for the per_level of each but I guess the same issue can exist too.

    Thanks for your help !

    opened by Simire 12
  • Getting summoner names with non-ASCII characters fails

    Getting summoner names with non-ASCII characters fails

    import os
    
    import cassiopeia as cass
    
    cass.set_riot_api_key(os.getenv("RIOT_API_KEY"))
    
    s = cass.get_summoner(name="é", region="EUW")
    s.exists
    

    Outputs Making call: https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/\xc3\xa9

    Then fails with UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 65: ordinal not in range(128)

    I believe that the problem is this line: url = "https://{platform}.api.riotgames.com/lol/summoner/v4/summoners/by-name/{name}".format(platform=query["platform"].value.lower(), name=query["name"].replace(" ", "")).encode("utf-8").

    Removing the final .encode("utf-8") fixes the problem for me. I was surprised by the encode since the other urls aren't encoded at this stage and I would expect requests or urllib to handle the encoding.

    opened by blimmo 11
  • Add the ability to track the number of requests issued

    Add the ability to track the number of requests issued

    This PR adds the ability (disabled by default) of tracking the requests which are made.

    This allows users of the library to track of the usage of their limit, allowing to better investigate performances and efficiency of their code.

    I find this fundamental as I try a different architectural approach for my library, to be able to compare if I'm effectively improving the performances or not.

    This would make debugging issues like #16 a lot easier as well.

    Accept one PR, get one fix for free: fixed a typo in the documentation

    opened by MakersF 11
  • Minor

    Minor "bugs"

    #Participantstats item list position not respected

    #Match.py

    @lazy_property
        @load_match_on_attributeerror
        def items(self) -> List[Item]:
            ids = [self._data[ParticipantStatsData].item0,
                   self._data[ParticipantStatsData].item1,
                   self._data[ParticipantStatsData].item2,
                   self._data[ParticipantStatsData].item3,
                   self._data[ParticipantStatsData].item4,
                   self._data[ParticipantStatsData].item5,
                   self._data[ParticipantStatsData].item6
            ]
            version = _choose_staticdata_version(self.__match)
            return SearchableList([Item(id=id, version=version, region=self.__match.region) for id in ids if id])
    

    Currently the list will only contain items that exist. It would be useful to have the position of each item preserved as well (eg. to see a relationship between active items and key binds).

    I can think of 2 ways of solving this:

    1. Add properties for item0-6 to the stats object
    2. Add empty items to the list to let it contain everything (or at least an option to with a param).
    opened by tyuo9980 10
  • So many service 429s

    So many service 429s

    Hello!

    Background: Kinda-ish new to Python, and really new to rate limits/calls.

    I'm running a code where I'm pulling all Master tier matches from 4 October. I'm using the code you've kindly provided "pull_masters_tier.py" as a base. To make sure things are running smoothly, I have the line "print("Got {0}".format(match))" to show me that it is gathering matches correctly. However, I'm getting a lot of "SERVICE 429." It looks something like this as the script is running: Got Match #2310443223 SERVICE 429 SERVICE 429 Got Match #2310385404 Got Match #2310326909 Got Match #2310279651 Got Match #2310264967 Got Match #2310198579 Got Match #2310203223 Got Match #2309999296 SERVICE 429 SERVICE 429 Got Match #2309988823 Got Match #2309994894 Got Match #2309974510 SERVICE 429 SERVICE 429 SERVICE 429 SERVICE 429

    Is this related to my rate limit? I don't want to get blacklisted and need to know if things are going alright. The data is getting saved to a file nicely, so that is alright. But I'm working on a very large project with this data and will need to gather data a LOT in the near future.

    Sorry for the noob question and thank you in advance!

    opened by alyssa-adams 10
  • Ghost match

    Ghost match

    Hey guys,

    I spent half an hour to understand an error happened when I used Cass. Firstly you can try this query, and will find a match_id '2405929288':

    https://na.api.pvp.net/api/lol/na/v2.2/matchlist/by-summoner/73099728?rankedQueues=RANKED_SOLO_5x5&seasons=PRESEASON2016&api_key=XXX

    Then you try to get the details of this match, and it will return 404, which means even if we can find a match_id in one match list, we are not 100% sure it has record from the match method:

    https://na.api.pvp.net/api/lol/na/v2.2/match/2405929288?includeTimeline=0&api_key=XXX

    {
        "status": {
            "message": "Not Found",
            "status_code": 404
        }
    }
    

    Because of this, we may get an None when we use match = riotapi.get_match(mf) in Cass. Right now I choose to use if match is None to avoid this (can not use '==' because Match.eq use Match.id).

    Update: And then I found the region of this match is 'EUW', which means the region setting is useless?

    Update 2: I reconsidered this problem, and found that if Cass. returned 404 correctly, there should not be this problem. So I think the exception part of Cass. should be checked.

    opened by simoncos 10
  • participantstats

    participantstats

    So, I'm trying to work through this, form your example match.py code you have a list of p = match.participants[summoner]. So if I wanted the stats of these participants wouldn't it just be p = match.participantstats[summoner]? That doesn't run and I'm unsure of why.

    Also thanks for your work so far!

    opened by aluckyhit 9
  • begin_time seems to be ignored during API calls in get_match_history

    begin_time seems to be ignored during API calls in get_match_history

    I set begin_time, but cass makes a bunch of calls like: Making call: https://na1.api.riotgames.com/lol/match/v3/matchlists/by-account/#########?beginIndex=0&endIndex=100 Making call: https://na1.api.riotgames.com/lol/match/v3/matchlists/by-account/#########?beginIndex=100&endIndex=200 Making call: https://na1.api.riotgames.com/lol/match/v3/matchlists/by-account/#########?beginIndex=200&endIndex=300

    And only then it filters needed matches. So there are lots of unnecessary API calls.

    opened by InvalidPointer 9
  • Adding match_statistics.py example, solved type hinting issue in participants method

    Adding match_statistics.py example, solved type hinting issue in participants method

    A new example was created to showcase match statistics, please see examples/match_statistics.py

    Adjusted participants method in Match class to properly hint a SearchableList, see issue:

    https://github.com/meraki-analytics/cassiopeia/issues/431#issue-1474219230

    opened by alexf-git 0
  • CumulativeTimeline object doesn't have all the info available by the API

    CumulativeTimeline object doesn't have all the info available by the API

    Accessing a ParticipantState in a CumulativeTimeline object (i.e. at a particular timestamp) doesn't fetch all the info available by the API.

    For example, there is no attribute champion stats with ability haste, ability power, etc ...

    This is however available on Riot's API: image

    help wanted 
    opened by JasonMendoza2008 2
  • Type-hinting of Match.participants is wrong?

    Type-hinting of Match.participants is wrong?

    In the example file: https://github.com/meraki-analytics/cassiopeia/blob/master/examples/timeline.py, we do:

        summoner = Summoner(name=name, region=region)
    
        match_history = summoner.match_history
        match = match_history[0]
        p = match.participants[summoner]
    

    match.participants is supposed to be a List[Participant] (or a SearchableListmore precisely), but the type-checker detects an unexpected type when feeding a Summoner to it: image

    help wanted 
    opened by JasonMendoza2008 1
  • item undo bug

    item undo bug

    In match.py:

    The undo function is defined by the following script.

    def undo(self, event: Event): assert event.after_id == 0 or event.before_id == 0 item_id = event.before_id or event.after_id prev = None while prev is None or prev.item_id != item_id: prev = self._events.pop() if prev.type == "ITEM_PURCHASED": self.destroy(prev.item_id) elif prev.type == "ITEM_DESTROYED": self.add(prev.item_id) elif prev.type == "ITEM_SOLD": self.add(prev.item_id) else: raise TypeError(f"Unexpected event type {prev.type}")

    The event of synthetic an item is down by destroying the previous item first and then purchasing a new item. If an undo event happens to this item, the item will be destroyed directly. However, the correct undo of synthetic is disassembled this item.

    help wanted 
    opened by RogerLiu-Yuxuan 0
  • Pypi releases not reflected with git tags on the repo

    Pypi releases not reflected with git tags on the repo

    There is currently no new release on this repo since v4.0.5

    This doesn't really matter so to speak, but it's a bit confusing when looking at the releases on pypi where currently the latest release is 5.0.1

    Can it be possible to add the tags also in the repo, this way I can easily determine if a bug was fixed between latest release and master.

    help wanted 
    opened by UmBsublime 3
  • SimpleKVDiskStore causes match.load() to throw an exception

    SimpleKVDiskStore causes match.load() to throw an exception

    First I get the match history for a summoner. Then on the first match I get an exception when I call match.load():

    Traceback (most recent call last):
      File "C:\Users\Patrick\code\LeagueStats\lol.py", line 28, in <module>
        main(sys.argv)
      File "C:\Users\Patrick\code\LeagueStats\lol.py", line 24, in main
        command.run(argv[2:])
      File "C:\Users\Patrick\code\LeagueStats\lol\command.py", line 93, in run
        self._run_impl(argv)
      File "C:\Users\Patrick\code\LeagueStats\lol\commands\update_matches.py", line 43, in _run_impl
        match_data = match.load().to_dict()
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\cassiopeia\core\common.py", line 226, in load
        self.__load__()
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\cassiopeia\core\common.py", line 239, in __load__
        self.__load__(group)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\cassiopeia\core\common.py", line 246, in __load__
        data = configuration.settings.pipeline.get(type=self._load_types[load_group], query=query)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\datapipelines\pipelines.py", line 459, in get
        return handler.get(query, context)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\datapipelines\pipelines.py", line 185, in get
        result = self._source.get(self._source_type, deepcopy(query), context)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\datapipelines\sources.py", line 120, in get
        return source.get(type, deepcopy(query), context)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\datapipelines\sources.py", line 69, in wrapper
        return call(self, query, context=context)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\datapipelines\queries.py", line 325, in wrapped
        validator(query)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\datapipelines\queries.py", line 209, in __call__
        return self._root.evaluate(query, context)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\datapipelines\queries.py", line 60, in evaluate
        child.evaluate(query, context)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\datapipelines\queries.py", line 198, in evaluate
        self.child.evaluate(query, context)
      File "C:\Users\Patrick\AppData\Roaming\Python\Python39\site-packages\datapipelines\queries.py", line 173, in evaluate
        raise WrongValueTypeError("{key} must be of type {type} in query! Got {badtype}.".format(key=self.key, type=self, badtype=type(value)))
    ValueError: invalid literal for int() with base 10: 'NA1_4055198406'
    

    When I remove

        "SimpleKVDiskStore": {
          "package": "cassiopeia_diskstore",
          "path": "D:/Data/lol_cache"
        },
    

    from my settings. the call succeeds. I tried clearing my lol cache folder with no success.

    opened by PeteyPii 1
Releases(v5.0.3)
Owner
Meraki Analytics
Meraki Analytics
🕹️ Jeu Azul en Python avec 4 IAs 🤖 implémentées, jouable de 1 à 4 joueurs

Projet jeu Azul 🕹️ Jeu Azul en Python avec 4 IAs 🤖 implémentées, jouable de 1 à 4 joueurs Par : Berachem MARKRIA et Tristan MARTINEZ Projet réalisé

Berachem Markria 2 Jun 07, 2022
シューティングゲームぽい?未完成ですけど

シューティングゲームぽい?未完成ですけど

kawamineka 64 Jun 25, 2022
A script to install a Windows game through GOG on Linux

proton-gog-install A script to install a Windows game through GOG on Linux. Based on this gist. Tested on Arch, might work elsewhere. Requirements: Im

5 Dec 28, 2022
The Original Snake Game. Maneuver a snake in its burrow and earn points while avoiding the snake itself and the walls of the snake burrow.

Maneuver a snake in its burrow and earn points while avoiding the snake itself and the walls of the snake burrow. The snake grows when it eats an apple by default which can be disabled in the setting

17 Nov 12, 2022
A 16x16 clone of Minecraft Classic, written in Python with the Ursina Engine

VoxelCraft A 16x16 clone of Minecraft Classic, written in Python with the Ursina Engine Features:Trees, Water(But there's no gravity, so if you break

2 Jun 23, 2022
Rock Paper Scissors Game with PyQt5

Rock-Paper-Scissors-Game rock paper scissors is a old game that all of us played it but this time let's play with computer Description This is Rock Pa

MohammadAli.HBA 4 Nov 11, 2021
A Python Sudoku Game Made with Pygame.

A Python Sudoku Game Made with Pygame. A Begginer Aimed at Learning Git, This Game Uses a Puzzle Generator Made by RutledgePaulV, Link to his Repo:

helaxious 3 Jun 29, 2022
Wordle-prophecy - The comprehensive list of all Wordle answers, past and future

About This repo contains the comprehensive list of all Wordle answers, past and

Hayden Moritz 2 Dec 15, 2022
Inject custom C++ code into GameMaker Studio 2 YYC builds

YYC Boost Inject custom C++ code into GameMaker Studio 2 YYC builds! WARNING: This tool is currently in an early stage of development and it is not gu

Patrik Kraif 7 Dec 30, 2022
This is an amazing game make using pygame.

This is an awesome balloon game. It is made in python using Pygame library. It is a project game while learning game development.

Rishikesh Kumar 2 Oct 10, 2021
Code for an arcade pop-a-shot style basketball game on Raspberry Pi

Basketball-Game Code for an arcade pop-a-shot style basketball game on Raspberry Pi, made over the course of winter break 2022. How To Run: Running th

Seth Reis 1 Jan 21, 2022
Aftermath is an anti token grabber written in Python3.

🎈 Aftermath 🎈 Aftermath is an anti token grabber written in Python3. This tool with a GUI setup is checking the Downloads folder to search for token

Billy 39 Dec 16, 2022
Crazy fast kahoot game flooder with a GUI and multi OS support.

kahoot flooder Crazy fast kahoot game flooder with a GUI and multi OS support. Made in python using tkinter and KahootPY with toast notifications. Req

Ben Tettmar 1 Apr 09, 2022
16-bit PvP platform minigame made for a final high-school project

16-bit PvP platform minigame made for a final high-school project

Flavien 1 Feb 10, 2022
Wordle is a web-based word game. Players have six attempts to guess a five-letter word;

Wordle is a web-based word game. Players have six attempts to guess a five-letter word; feedback is given for each guess, in the form of colored tiles, indicating when letters match or occupy the cor

Abhishek 2 May 21, 2022
Wordle-player - An optimal player for Wordle. Based on a rough understanding of information theory

Wordle-player - An optimal player for Wordle. Based on a rough understanding of information theory

Neill Johnston 3 Feb 26, 2022
Gamelib is a pure-Python single-file library/framework for writing simple games.

Gamelib is a pure-Python single-file library/framework for writing simple games. It is intended for educational purposes (e.g. to be used in b

Diego Essaya 15 Dec 22, 2022
A fun discord bot for music, mini games, admin controls, economy, ai chatbot and levelling system

A fun discord bot for music, mini games, admin controls, economy, ai chatbot and levelling system. This bot was specially made for Dspark discord server.

2 Aug 30, 2022
AI Mario challenges you to clear all stage of Super Mario game.

mario-ai-challenge Challenge AI Mario to clear all stages of Super Mario. GitHub Pages Site Rules Enjoy building AI Mario. Share information. Use Goog

karaage 48 Dec 10, 2022
A sprite ripper and converter for Com2uS' 2007 game Music World.

Music World Sprite Dumper This repository contains a python script reads an UNCOMPRESSED Music World pxo file and attempts to dump sprites from it. Th

Buu342 1 Mar 16, 2022