Repository: Cog-Creators/Red-DiscordBot

Issue:
[V3 Streams/Core] Use of aiohttp.ClientSession assignment
### Type:

- Bug

### Brief description of the problem
In various places throughout the bot (namely in streams and core_commands), `aiohttp.ClientSession()` is used like this:
```py
session = aiohttp.ClientSession()
async with session.request(url) as r:
    # Do stuff with response
await session.close()
```

The session should be used with an async context manager, so that it closes successfully even if an error is raised, or the function returns, whilst it is being used:
```py
async with aiohttp.ClientSession() as session:
    async with session.request(url) as r:
        # Do stuff with response
```


Discussion:

Resolving patch:
diff --git a/redbot/cogs/streams/streamtypes.py b/redbot/cogs/streams/streamtypes.py
--- a/redbot/cogs/streams/streamtypes.py
+++ b/redbot/cogs/streams/streamtypes.py
@@ -25,7 +25,6 @@ def __init__(self, **kwargs):
         self.type = self.__class__.__name__
 
     async def get_community_id(self):
-        session = aiohttp.ClientSession()
         headers = {
             "Accept": "application/vnd.twitchtv.v5+json",
             "Client-ID": str(self._token)
@@ -33,9 +32,9 @@ async def get_community_id(self):
         params = {
             "name": self.name
         }
-        async with session.get(TWITCH_COMMUNITIES_ENDPOINT, headers=headers, params=params) as r:
-            data = await r.json()
-        await session.close()
+        async with aiohttp.ClientSession() as session:
+            async with session.get(TWITCH_COMMUNITIES_ENDPOINT, headers=headers, params=params) as r:
+                data = await r.json()
         if r.status == 200:
             return data["_id"]
         elif r.status == 400:
@@ -51,7 +50,6 @@ async def get_community_streams(self):
                 self.id = await self.get_community_id()
             except CommunityNotFound:
                 raise
-        session = aiohttp.ClientSession()
         headers = {
             "Accept": "application/vnd.twitchtv.v5+json",
             "Client-ID": str(self._token)
@@ -60,8 +58,9 @@ async def get_community_streams(self):
             "community_id": self.id
         }
         url = TWITCH_BASE_URL + "/kraken/streams"
-        async with session.get(url, headers=headers, params=params) as r:
-            data = await r.json()
+        async with aiohttp.ClientSession() as session:
+            async with session.get(url, headers=headers, params=params) as r:
+                data = await r.json()
         if r.status == 200:
             if data["_total"] == 0:
                 raise OfflineCommunity()
@@ -120,16 +119,15 @@ async def is_online(self):
         if not self.id:
             self.id = await self.fetch_id()
 
-        session = aiohttp.ClientSession()
         url = TWITCH_STREAMS_ENDPOINT + self.id
         header = {
             'Client-ID': str(self._token),
             'Accept': 'application/vnd.twitchtv.v5+json'
         }
 
-        async with session.get(url, headers=header) as r:
-            data = await r.json(encoding='utf-8')
-        await session.close()
+        async with aiohttp.ClientSession() as session:
+            async with session.get(url, headers=header) as r:
+                data = await r.json(encoding='utf-8')
         if r.status == 200:
             if data["stream"] is None:
                 #self.already_online = False
@@ -151,11 +149,10 @@ async def fetch_id(self):
             'Accept': 'application/vnd.twitchtv.v5+json'
         }
         url = TWITCH_ID_ENDPOINT + self.name
-        session = aiohttp.ClientSession()
 
-        async with session.get(url, headers=header) as r:
-            data = await r.json()
-        await session.close()
+        async with aiohttp.ClientSession() as session:
+            async with session.get(url, headers=header) as r:
+                data = await r.json()
 
         if r.status == 200:
             if not data["users"]:
@@ -195,13 +192,12 @@ def __repr__(self):
 
 class HitboxStream(Stream):
     async def is_online(self):
-        session = aiohttp.ClientSession()
         url = "https://api.hitbox.tv/media/live/" + self.name
 
-        async with session.get(url) as r:
-            #data = await r.json(encoding='utf-8')
-            data = await r.text()
-        await session.close()
+        async with aiohttp.ClientSession() as session:
+            async with session.get(url) as r:
+                #data = await r.json(encoding='utf-8')
+                data = await r.text()
         data = json.loads(data, strict=False)
         if "livestream" not in data:
             raise StreamNotFound()
@@ -235,11 +231,10 @@ class MixerStream(Stream):
     async def is_online(self):
         url = "https://mixer.com/api/v1/channels/" + self.name
 
-        session = aiohttp.ClientSession()
-        async with session.get(url) as r:
-            #data = await r.json(encoding='utf-8')
-            data = await r.text(encoding='utf-8')
-        await session.close()
+        async with aiohttp.ClientSession() as session:
+            async with session.get(url) as r:
+                #data = await r.json(encoding='utf-8')
+                data = await r.text(encoding='utf-8')
         if r.status == 200:
             data = json.loads(data, strict=False)
             if data["online"] is True:
@@ -278,11 +273,9 @@ class PicartoStream(Stream):
     async def is_online(self):
         url = "https://api.picarto.tv/v1/channel/name/" + self.name
 
-        session = aiohttp.ClientSession()
-
-        async with session.get(url) as r:
-            data = await r.text(encoding='utf-8')
-        await session.close()
+        async with aiohttp.ClientSession() as session:
+            async with session.get(url) as r:
+                data = await r.text(encoding='utf-8')
         if r.status == 200:
             data = json.loads(data)
             if data["online"] is True:
diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py
--- a/redbot/core/core_commands.py
+++ b/redbot/core/core_commands.py
@@ -293,10 +293,9 @@ async def modrole(self, ctx, *, role: discord.Role):
     @checks.is_owner()
     async def avatar(self, ctx, url: str):
         """Sets Red's avatar"""
-        session = aiohttp.ClientSession()
-        async with session.get(url) as r:
-            data = await r.read()
-        await session.close()
+        async with aiohttp.ClientSession() as session:
+            async with session.get(url) as r:
+                data = await r.read()
 
         try:
             await ctx.bot.user.edit(avatar=data)


Test patch: