aboutsummaryrefslogtreecommitdiffstats
path: root/bot/seasons/evergreen/game.py
blob: a240c69ef716766caef9e2ec84939d618a32d9ce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
import asyncio
import random
from datetime import datetime
from enum import IntEnum
from typing import Any, Dict, List, Tuple

from aiohttp import ClientSession
from discord import Embed
from discord.ext.commands import Bot, Cog, Context, group

from bot.constants import Tokens
from bot.pagination import ImagePaginator, LinePaginator

# Base URL of IGDB API
BASE_URL = "https://api-v3.igdb.com/"
IMAGE_BASE_URL = "https://images.igdb.com/igdb/image/upload/"

HEADERS = {
    "user-key": Tokens.igdb,
    "Accept": "application/json"
}


class GameStatus(IntEnum):
    """Game statuses in IGDB API."""

    Released = 0
    Alpha = 2
    Beta = 3
    Early = 4
    Offline = 5
    Cancelled = 6
    Rumored = 7


class AgeRatingCategories(IntEnum):
    """IGDB API Age Rating categories IDs."""

    ESRB = 1
    PEGI = 2


class AgeRatings(IntEnum):
    """PEGI/ESRB ratings IGDB API IDs."""

    Three = 1
    Seven = 2
    Twelve = 3
    Sixteen = 4
    Eighteen = 5
    RP = 6
    EC = 7
    E = 8
    E10 = 9
    T = 10
    M = 11
    AO = 12


class Games(Cog):
    """Games Cog contains commands that collect data from IGDB."""

    def __init__(self, bot: Bot):
        self.bot = bot
        self.http_session: ClientSession = bot.http_session

        # Initialize genres
        asyncio.get_event_loop().create_task(self._get_genres())

    async def _get_genres(self) -> None:
        """Create genres variable for games command."""
        body = "fields name; limit 100;"
        async with self.http_session.get(BASE_URL + "genres", data=body, headers=HEADERS) as resp:
            result = await resp.json()

        self.genres = {genre['name'].capitalize(): genre['id'] for genre in result}

        # Manual check genres, replace sentences with words
        for genre in self.genres.keys():
            if genre == "Role-playing (rpg)":
                self.genres["Rpg"] = self.genres.pop(genre)
            elif genre == "Turn-based strategy (tbs)":
                self.genres["Tbs"] = self.genres.pop(genre)
            elif genre == "Real time strategy (rts)":
                self.genres["Rts"] = self.genres.pop(genre)
            elif genre == "Hack and slash/beat 'em up":
                self.genres["Hack-and-slash"] = self.genres.pop(genre)

    @group(name='games', aliases=['game'], invoke_without_command=True)
    async def games(self, ctx: Context, genre: str = "", amount: int = 5) -> None:
        """
        Get random game(s) by genre from IGDB. Use .movies genres command to get all available genres.

        Also support amount parameter, what max is 25 and min 1, default 5. Use quotes ("") for genres with multiple
        words.
        """
        # Capitalize genre for check
        genre = genre.capitalize()

        # Check for amounts, max is 25 and min 1
        if amount > 25:
            await ctx.send("You can't get more than 25 games at once.")
            return
        elif amount < 1:
            await ctx.send("You can't get less than 1 game.")
            return

        # Get games listing, if genre don't exist, show help.
        try:
            games = await self.get_games_list(self.http_session, amount, self.genres[genre],
                                              offset=random.randint(0, 150))
        except KeyError:
            await ctx.send_help('games')
            return

        # Create pages and paginate
        pages = await self.get_pages(games)

        await ImagePaginator.paginate(pages, ctx, Embed(title=f'Random {genre} Games'))

    @games.command(name='top', aliases=['t'])
    async def top(self, ctx: Context, amount: int = 10) -> None:
        """
        Get current Top games in IGDB.

        Support amount parameter. Max is 25, min is 1.
        """
        if amount > 25:
            await ctx.send("You can't get more than top 25 games.")
            return
        elif amount < 1:
            await ctx.send("You can't get less than 1 top game.")
            return

        games = await self.get_games_list(self.http_session, amount, sort='total_rating desc',
                                          additional_body="where total_rating >= 90; sort total_rating_count desc;")

        pages = await self.get_pages(games)
        await ImagePaginator.paginate(pages, ctx, Embed(title=f'Top {amount} Games'))

    @games.command(name='genres', aliases=['genre', 'g'])
    async def genres(self, ctx: Context) -> None:
        """Get all available genres."""
        await ctx.send(f"Currently available genres: {', '.join(f'`{genre}`' for genre in self.genres.keys())}")

    @games.command(name='search', aliases=['s'])
    async def search(self, ctx: Context, *, search: str) -> None:
        """Find games by name."""
        lines = await self.search_games(self.http_session, search)

        await LinePaginator.paginate((line for line in lines), ctx, Embed(title=f'Game Search Results: {search}'))

    @games.command(name='company', aliases=['companies'])
    async def company(self, ctx: Context, amount: int = 5) -> None:
        """
        Get random Game Companies companies from IGDB API.

        Support amount parameter. Max is 25, min is 1.
        """
        if amount > 25:
            await ctx.send("You can't get more than 25 companies at once.")
            return
        elif amount < 1:
            await ctx.send("You can't get less than 1 company.")
            return

        companies = await self.get_companies_list(self.http_session, amount, random.randint(0, 150))
        pages = await self.get_company_pages(companies)

        await ImagePaginator.paginate(pages, ctx, Embed(title='Random Game Companies'))

    async def get_games_list(self,
                             client: ClientSession,
                             limit: int,
                             genre: str = None,
                             sort: str = None,
                             additional_body: str = "",
                             offset: int = 0) \
            -> List[Dict[str, Any]]:
        """Get Games List from IGDB API."""
        # Create body of IGDB API request, define fields, sorting, offset, limit and genre
        body = f"""fields cover.image_id, first_release_date, total_rating, name, storyline, url, platforms.name,
status, involved_companies.company.name, summary, age_ratings.category, age_ratings.rating, total_rating_count;
{f'sort {sort};' if sort else ''}
offset {offset};
limit {limit};
{f'where genres = ({genre});' if genre else ''}
{additional_body}"""

        # Do request to IGDB API, create headers, URL, define body, return result
        async with client.get(
                url=BASE_URL + "games",
                data=body,
                headers=HEADERS
        ) as resp:
            return await resp.json()

    async def get_pages(self, data: List[Dict[str, Any]]) -> List[Tuple[str, str]]:
        """Generate all game pages, do additional requests to IGDB API."""
        pages = []
        for game in data:
            pages.append(await self.create_page(game))
        return pages

    async def create_page(self, data: Dict[str, Any]) -> Tuple[str, str]:
        """Create content of Game Page."""
        # Create page content variable, what will be returned
        page = ""

        # Get keys to one variable. Used for code style
        keys = data.keys()

        # If game have cover, generate URL of Cover, if not, let url empty
        if 'cover' in keys:
            url = f"{IMAGE_BASE_URL}t_cover_big/{data['cover']['image_id']}.jpg"
        else:
            url = ""

        # Add title with hyperlink and check for storyline
        page += f"**[{data['name']}]({data['url']})**\n"
        page += data['summary'] + "\n\n" if 'summary' in keys else "\n"

        # Add release date if key is in game information
        if 'first_release_date' in keys:
            page += f"**Release Date:** {datetime.utcfromtimestamp(data['first_release_date']).date()}\n"

        # Add other information
        page += f"**Rating:** {'{0:.2f}'.format(data['total_rating']) if 'total_rating' in keys else '?'}/100 "
        page += f":star: (based on {data['total_rating_count'] if 'total_rating_count' in keys else '?'})\n"

        page += f"**Platforms:** "
        page += f"{', '.join(pf['name'] for pf in data['platforms']) if 'platforms' in keys else '?'}\n"

        page += f"**Status:** {GameStatus(data['status']).name if 'status' in keys else '?'}\n"

        if 'age_ratings' in keys:
            rating = f"""{', '.join(AgeRatingCategories(age['category']).name + ' ' + AgeRatings(age['rating']).name
                                    for age in data['age_ratings'])}"""
            page += f"**Age Ratings:** {rating}\n"

        if 'involved_companies' in data:
            companies = ', '.join(co['company']['name'] for co in data['involved_companies'])
        else:
            companies = "?"
        page += f"**Made by:** {companies}\n"

        page += "\n"
        page += data['storyline'] if 'storyline' in data.keys() else ''

        return page, url

    async def search_games(self, client: ClientSession, search: str) -> List[str]:
        """Search game from IGDB API by string, return listing of pages."""
        lines = []

        # Define request body of IGDB API request and do request
        body = f"""fields name, url, storyline, total_rating, total_rating_count;
search "{search}";
limit 50;"""

        async with client.get(
                url=BASE_URL + "games",
                data=body,
                headers=HEADERS) as resp:
            data = await resp.json()

        # Loop over games, format them to good format, make line and append this to total lines
        for game in data:
            line = ""

            # Add games name and rating, also attach URL to title
            line += f"**[{game['name']}]({game['url']})**\n"
            line += f"""{'{0:.2f}'.format(game['total_rating'] if 'total_rating' in game.keys() else 0)}/100 :star: (by {
            game['total_rating_count'] if 'total_rating_count' in game.keys() else '?'} users)"""

            lines.append(line)

        return lines

    async def get_companies_list(self, client: ClientSession, limit: int, offset: int = 0) -> List[Dict[str, Any]]:
        """Get random Game Companies from IGDB API."""
        # Create request body, define included fields, limit and offset, do request
        body = f"""fields name, url, start_date, logo.image_id, developed.name, published.name, description;
limit {limit};
offset {offset};"""

        async with client.get(
                url=BASE_URL + "companies",
                data=body,
                headers=HEADERS
        ) as resp:
            return await resp.json()

    async def get_company_pages(self, data: List[Dict[str, Any]]) -> List[Tuple[str, str]]:
        """Get all good formatted pages about Game Companies."""
        pages = []

        # Loop over companies, add them to pages listing and return pages
        for co in data:
            pages.append(await self.create_company_page(co))

        return pages

    async def create_company_page(self, data: Dict[str, Any]) -> Tuple[str, str]:
        """Create good formatted Game Company page."""
        page = ""

        # Generate URL of company logo
        url = f"{IMAGE_BASE_URL}t_logo_med/{data['logo']['image_id'] if 'logo' in data.keys() else ''}.png"

        # Add name and description of company, attach URL to title
        page += f"[{data['name']}]({data['url']})\n"
        page += data['description'] + "\n\n" if 'description' in data.keys() else '\n'

        # Add other information
        if 'start_date' in data.keys():
            founded = datetime.utcfromtimestamp(data['start_date']).date()
        else:
            founded = "?"
        page += f"**Founded:** {founded}\n"

        page += "**Developed:** "
        page += f"{', '.join(game['name'] for game in data['developed']) if 'developed' in data.keys() else '?'}\n"

        page += "**Published:** "
        page += f"{', '.join(game['name'] for game in data['published']) if 'published' in data.keys() else '?'}"

        return page, url


def setup(bot: Bot) -> None:
    """Add/Load Games cog."""
    bot.add_cog(Games(bot))