League Statistic Discord Bot

Preface

Initially, I developed the League Statistic Bot with the intention to improve communication with my boyfriend. One of the challenges we face was the timing of my calls, particularly when my boyfriend was in a game of League of Legend. Understandably, interrupting him during those moments would lead to frustration, and he humorously jokes that I shouldn't call him when he is "at war". Thus, I sought a solution that would allow me to determine if he was currently playing League. By implementing the League Statistic Bot, I can now know if my boyfriend is playing League at the moment. Which enables me to wait until after his game to reach out, ensuring our conversations are more enjoyable and uninterrupted.

Further Application

League Statistic Bot has also become a helpful tool for my family, especially when it comes to my younger brother's gaming habits. He struggles with self-control and to manage his gaming time properly. So, my parents started using the bot to keep track of his playtime and help him with time management. It's made a significant difference in his academic performance. With the bot's support, my parents can better guide him and find a balance between gaming and his studies. It's been a positive intervention that's created a more supportive environment for his growth and success.

slash command

Description

The League Statistic Bot is a Discord bot designed to fetch League of Legend game data using the Riot Games API. It utilizes Discord's slash commands feature to provide users with convenient ways to track summoner's games.

Users can check if a summoner is currently playing League of Legends by using the /active slash command.

past slash command

Moreover, users can request the data for the summoner's past 10 games by using /past slash command.

past slash command

Additionally, the bot generates two bar graphs by matplotlib. The first graph displays the total time played by the summoner last week, allowing users to visualize their gaming activity over time. The second graph shows the total number of games played by the summoner last week, providing insights into their gaming frequency.

graph graph

Lastly, the bot generates a comprehensive weekly report that includes a list of games played by the summoner previous week and total time played graph and number of games played graph.

Code:

The code can be divided into 3 components. The first component, discord, is responsible for receiving commands and sending responses on Discord. It consists of two files,

discord_bot.py
and
responses.py
. The second component, riot request, is responsible for fetching data from Riot API. It comprises of the file
riot_requests.py"
. The third component entity includes four files:
summoner.py
,
team.py
,
match.py
,
weekly_report.py
.
Clean Architecture

Discord:
discord_bot.py
&
requests.py

The discord session of the code handles user requests and sending corresponding responses to user. In the file

discord_bot.py
, discord.py library is used to aid in creating the application that utilize the Discord API. By utilizing
client.tree.command
decorator from discord.py, we can create slash commands that users can use on Discord. Each command invokes a corresponding function from
responses.py
then uses
discord.Interaction
to send the responses to users (as shown below).

    @client.tree.command(name="active")
    @app_commands.describe(summoner_name="Summoner Name")
    async def active(interaction: discord.Interaction, summoner_name: str) -> None:
        """
        Sends a message to the user with the active game of the summoner

        Args:
            interaction (discord.Interaction): The Discord interaction object representing the original message
            summoner_name (str): The summoner name

        Returns:
            None
        """
        active_game = responses.active(summoner_name)
        await interaction.response.send_message(active_game)
            

'responses.py' calls the corresponding function in 'riot_requests.py', that fetch the required data from riot api. which, in turn, calls the corresponding function in ‘riot_requests.py’, that fetch the required data from riot api.

    def active(summoner_name: str) -> str:
        """
        Return a string of active game information given summoner_name.

        Args:
            summoner_name (str): summoner name

        Returns:
            active_game (str): a string of active game information
        """
        summoner = riot_requests.get_summoners_by_name(summoner_name)

        active_game = riot_requests.get_active_games_by_summoner_id(summoner.id)

        return str(active_game)
            

Riot Request:
riot_requests.py

Other classes

The Match class represents an individual game match in League of Legends. It contains attributes such as the match ID, start time, duration, and participating teams. It serves as a data transfer object (DTO) for storing match-related information. The Summoner class represents a League of Legends summoner. It contains attributes such as the summoner ID, name, level, and game statistics. It acts as a DTO for storing summoner-related data. The WeeklyReport class generates a report summarizing the summoner's activity in the previous week. It interacts with the Match and Summoner classes to gather the necessary data and produces visualizations, such as graphs depicting the total time played and total games played. the Team class, which represents a team in a League of Legends match. The Team class is a data transfer object (DTO) used to store information about a team, including its ID, list of participants (players), and the outcome of the team (win or loss).

Architecture Diagram:

Testing: PyTest

riot_requests_test

The "riot_requests_test" suite validates the functionality of Riot API calls in an ever-changing API environment. Using pytest.fixture, the tests are set up with mock API responses from JSON files. By utilizing with patch("requests.get"), the actual requests.get function is replaced with a mock response. This approach ensures that the code correctly interacts with the Riot API, accommodating potential API changes. The mock responses create a stable testing environment where the application's behavior can be verified against expected outcomes. Through these tests, the reliability of Riot API integration is maintained, promptly identifying and resolving any issues. The ultimate goal is to deliver a robust application that seamlessly interacts with the evolving Riot API, ensuring a consistent and dependable user experience.

        @pytest.fixture
        def mock_get_summoners_by_name_sucess():
            with open("test/mock_get_summoners_by_name_success.json") as f:
                return json.load(f)


        def test_get_summoners_by_name_success(mock_get_summoners_by_name_sucess):
            """
            Test get_summoners_by_name() with a valid summoner name.
            """

            summoner_name = "Kid Orpheus"
            with patch("requests.get") as mock_get:
                mock_get.return_value.json.return_value = mock_get_summoners_by_name_sucess
                summoner = get_summoners_by_name(summoner_name)

            assert expected_summoner == summoner
            

discord_bot_test

Hosting

Originally, the bot was hosted on Heroku, but recently they started charging $7 per month for command line access. Fortunately, I received a generous gift from my dad in the form of a Raspberry Pi. Now, the bot is hosted on my Raspberry Pi, allowing me to run it for free.

References

Upcoming