Skip to content

Commit 4c7ca88

Browse files
authored
Merge pull request #20 from tazz4843/master
2 parents 1ca7947 + 6c1d864 commit 4c7ca88

File tree

4 files changed

+112
-89
lines changed

4 files changed

+112
-89
lines changed

Diff for: .gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
.vscode
1+
.vscode
2+
.idea/
3+
4+
venv/

Diff for: statcord/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
__author__ = 'statcord.com'
33
__license__ = 'MIT'
44
__copyright__ = 'Copyright 2020, statcord.com'
5-
__version__ = '3.0.7'
5+
__version__ = '3.0.8'
66

77
name = "statcord"
88

@@ -11,4 +11,4 @@
1111
from .exceptions import *
1212

1313
VersionInfo = namedtuple('VersionInfo', 'major minor micro releaselevel serial')
14-
version_info = VersionInfo(major=3, minor=0, micro=7, releaselevel='final', serial=0)
14+
version_info = VersionInfo(major=3, minor=0, micro=8, releaselevel='final', serial=0)

Diff for: statcord/client.py

+102-83
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,134 @@
1+
# coding=utf-8
12
import asyncio
3+
import contextlib
24
import aiohttp
35
import psutil
6+
import logging
7+
48
from discord import Client as DiscordClient
5-
from . import exceptions
6-
import contextlib
9+
from typing import Optional, Coroutine, Union, List, Dict, Iterable
10+
from discord.ext.commands import Context
11+
12+
# this could be relative, but apparently Python doesn't like it
13+
from statcord import exceptions
714

815

916
class Client:
1017
"""Client for using the statcord API"""
18+
1119
def __init__(self, bot, token, **kwargs):
12-
if not isinstance(bot,DiscordClient):
20+
self.logger = logging.getLogger("statcord")
21+
self.logging_level = kwargs.get("logging_level", logging.WARNING)
22+
self.logger.setLevel(self.logging_level)
23+
24+
if not isinstance(bot, DiscordClient):
1325
raise TypeError(f"Expected class deriving from discord.Client for arg bot not {bot.__class__.__qualname__}")
14-
if not isinstance(token,str):
26+
if not isinstance(token, str):
1527
raise TypeError(f"Expected str for arg token not {token.__class__.__qualname__}")
1628

17-
self.bot = bot
18-
self.key = token
19-
self.base = "https://statcord.com/logan/"
20-
self.session = aiohttp.ClientSession(loop=bot.loop)
29+
self.bot: DiscordClient = bot
30+
self.key: str = token
31+
self.base: str = "https://statcord.com/logan/"
32+
self.session: aiohttp.ClientSession = aiohttp.ClientSession(loop=bot.loop)
2133

34+
self.mem: Optional[bool] = None
2235
if kwargs.get("mem"):
23-
if isinstance(kwargs["mem"],bool):
24-
self.mem=kwargs["mem"]
36+
if isinstance(kwargs["mem"], bool):
37+
self.mem = kwargs["mem"]
2538
else:
26-
raise TypeError(f"Memory config : expected type bool not {kwargs['mem'].__class__.__qualname__}")
39+
raise TypeError(f"Memory config: expected type bool not {kwargs['mem'].__class__.__qualname__}.")
2740
else:
28-
self.mem=True
41+
self.mem = True
2942

43+
self.cpu: Optional[bool] = None
3044
if kwargs.get("cpu"):
31-
if isinstance(kwargs["cpu"],bool):
32-
self.cpu=kwargs["cpu"]
45+
if isinstance(kwargs["cpu"], bool):
46+
self.cpu = kwargs["cpu"]
3347
else:
34-
raise TypeError(f"CPU config : expected type bool not {kwargs['cpu'].__class__.__qualname__}")
48+
raise TypeError(f"CPU config: expected type bool not {kwargs['cpu'].__class__.__qualname__}")
3549
else:
3650
self.cpu = True
3751

52+
self.bandwidth: Optional[bool] = None
3853
if kwargs.get("bandwidth"):
39-
if isinstance(kwargs["bandwidth"],bool):
40-
self.bandwidth=kwargs["bandwidth"]
54+
if isinstance(kwargs["bandwidth"], bool):
55+
self.bandwidth = kwargs["bandwidth"]
4156
else:
42-
raise TypeError("Bandwidth config : expected type bool")
57+
raise TypeError("Bandwidth config: expected type bool")
4358
else:
4459
self.bandwidth = True
4560

61+
self.debug: Optional[bool] = None
4662
if kwargs.get("debug"):
47-
if isinstance(kwargs["debug"],bool):
48-
self.debug=kwargs["debug"]
63+
if isinstance(kwargs["debug"], bool):
64+
self.debug = kwargs["debug"]
4965
else:
50-
raise TypeError(f"Debug config : expected type bool not {kwargs['debug'].__class__.__qualname__}")
66+
raise TypeError(f"Debug config: expected type bool not {kwargs['debug'].__class__.__qualname__}")
5167
else:
5268
self.debug = False
5369

54-
self.custom1 = kwargs.get("custom1") or False
55-
self.custom2 = kwargs.get("custom2") or False
56-
self.active = []
57-
self.commands = 0
58-
self.popular = []
59-
self.previous_bandwidth = psutil.net_io_counters().bytes_sent + psutil.net_io_counters().bytes_recv
60-
psutil.cpu_percent()
70+
self.custom1: Optional[Coroutine] = kwargs.get("custom1") or None
71+
self.custom2: Optional[Coroutine] = kwargs.get("custom2") or None
72+
self.active: List[int] = []
73+
self.commands: int = 0
74+
self.popular: List[Dict[str, Union[str, int]]] = []
75+
self.previous_bandwidth: int = psutil.net_io_counters().bytes_sent + psutil.net_io_counters().bytes_recv
6176

62-
if self.debug:
63-
print("Statcord debug mode enabled")
77+
self.logger.debug("Statcord debug mode enabled")
6478

65-
def __headers(self):
79+
@staticmethod
80+
def __headers() -> Dict[str, str]:
6681
return {'Content-Type': 'application/json'}
6782

83+
# noinspection SpellCheckingInspection
6884
async def __handle_response(self, res: aiohttp.ClientResponse) -> dict:
6985
try:
7086
msg = await res.json() or {}
7187
except aiohttp.ContentTypeError:
7288
msg = await res.text()
89+
self.logger.debug(f"Handling response ({res!r}): {msg!s}")
7390
status = res.status
7491
if status == 200:
92+
self.logger.debug(f"Code 200 OK")
7593
return msg
7694
elif status == 429:
77-
raise exceptions.TooManyRequests(status,msg,int(msg.get("wait")))
95+
self.logger.debug(f"Code 429 Too Many Requests: ratelimited for {msg.get('timeleft')}")
96+
raise exceptions.TooManyRequests(status, msg, int(msg.get("timeleft")))
7897
else:
79-
raise exceptions.RequestFailure(status=status,response=msg)
80-
81-
return msg
98+
self.logger.debug(f"Code {status}")
99+
raise exceptions.RequestFailure(status=status, response=msg)
82100

83101
@property
84-
def servers(self):
102+
def servers(self) -> str:
85103
return str(len(self.bot.guilds))
86104

87105
@property
88-
def _user_counter(self):
106+
def _user_counter(self) -> Iterable[int]:
89107
for g in self.bot.guilds:
90108
with contextlib.suppress(AttributeError):
91109
yield g.member_count
92110

93111
@property
94-
def users(self):
112+
def users(self) -> str:
95113
return str(sum(self._user_counter))
96114

97-
async def post_data(self):
98-
id = str(self.bot.user.id)
115+
async def post_data(self) -> None:
116+
self.logger.debug("Got request to post data.")
117+
bot_id = str(self.bot.user.id)
99118
commands = str(self.commands)
100119

101120
if self.mem:
102121
mem = psutil.virtual_memory()
103-
memactive = str(mem.used)
104-
memload = str(mem.percent)
122+
mem_used = str(mem.used)
123+
mem_load = str(mem.percent)
105124
else:
106-
memactive = "0"
107-
memload = "0"
125+
mem_used = "0"
126+
mem_load = "0"
108127

109128
if self.cpu:
110-
cpuload = str(psutil.cpu_percent())
129+
cpu_load = str(psutil.cpu_percent())
111130
else:
112-
cpuload = "0"
131+
cpu_load = "0"
113132

114133
if self.bandwidth:
115134
current_bandwidth = psutil.net_io_counters().bytes_sent + psutil.net_io_counters().bytes_recv
@@ -119,76 +138,76 @@ async def post_data(self):
119138
bandwidth = "0"
120139

121140
if self.custom1:
141+
# who knows why PyCharm gets annoyed there /shrug
142+
# noinspection PyCallingNonCallable
122143
custom1 = str(await self.custom1())
123144
else:
124145
custom1 = "0"
125146

126147
if self.custom2:
148+
# who knows why PyCharm gets annoyed there /shrug
149+
# noinspection PyCallingNonCallable
127150
custom2 = str(await self.custom2())
128151
else:
129152
custom2 = "0"
130153

154+
# noinspection SpellCheckingInspection
131155
data = {
132-
"id":id,
133-
"key":self.key,
134-
"servers":self.servers,
135-
"users":self.users,
136-
"commands":commands,
137-
"active":self.active,
138-
"popular":self.popular,
139-
"memactive":memactive,
140-
"memload":memload,
141-
"cpuload":cpuload,
142-
"bandwidth":bandwidth,
143-
"custom1":custom1,
144-
"custom2":custom2,
156+
"id": bot_id,
157+
"key": self.key,
158+
"servers": self.servers,
159+
"users": self.users,
160+
"commands": commands,
161+
"active": self.active,
162+
"popular": self.popular,
163+
"memactive": mem_used,
164+
"memload": mem_load,
165+
"cpuload": cpu_load,
166+
"bandwidth": bandwidth,
167+
"custom1": custom1,
168+
"custom2": custom2,
145169
}
146-
if self.debug:
147-
print("Posting data")
148-
print(data)
170+
self.logger.debug(f"Posting stats: {data!s}")
149171
self.active = []
150172
self.commands = 0
151173
self.popular = []
152174

153175
async with self.session.post(url=self.base + "stats", json=data, headers=self.__headers()) as resp:
154-
res = await self.__handle_response(resp)
155-
if self.debug:
156-
print(res)
176+
await self.__handle_response(resp)
157177

158-
def start_loop(self):
178+
def start_loop(self) -> None:
159179
self.bot.loop.create_task(self.__loop())
160180

161-
def command_run(self,ctx):
181+
def command_run(self, ctx: Context) -> None:
162182
self.commands += 1
163183
if ctx.author.id not in self.active:
164184
self.active.append(ctx.author.id)
165185

166186
command = ctx.command.name
167-
found = False
168-
popular = list(self.popular)
169-
self.popular= []
170-
for cmd in popular:
171-
if cmd["name"] == command:
172-
found = True
173-
cmd["count"] = str(int(cmd["count"]) + 1)
174-
self.popular.append(cmd)
175-
176-
if not found:
177-
self.popular.append({"name":command,"count":"1"})
178-
179-
async def __loop(self):
187+
self.logger.debug(f"Command {command} has been run by {ctx.author.id}")
188+
for cmd in filter(lambda x: x["name"] == command, self.popular):
189+
cmd["count"] = str(int(cmd["count"]) + 1)
190+
break
191+
else:
192+
self.popular.append({"name": command, "count": "1"})
193+
194+
async def __loop(self) -> None:
180195
"""
181196
The internal loop used for automatically posting server/guild count stats
182197
"""
183198
await self.bot.wait_until_ready()
184199
if self.debug:
185-
print("Statcord Auto Post has started!")
200+
self.logger.debug("Statcord Auto Post has started!")
186201
while not self.bot.is_closed():
202+
self.logger.debug("Posting stats...")
187203
try:
188204
await self.post_data()
189205
except Exception as e:
206+
self.logger.debug("Got error, dispatching error handlers.")
190207
await self.on_error(e)
208+
else:
209+
self.logger.debug("Posted stats successfully.")
191210
await asyncio.sleep(60)
192211

193-
async def on_error(self,error):
194-
print(f"Statcord posting exception occured: {error.__class__.__qualname__} - {error}")
212+
async def on_error(self, error: BaseException) -> None:
213+
self.logger.exception("Statcord posting exception occurred.", exc_info=error)

Diff for: statcord/exceptions.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
# coding=utf-8
12
class StatcordException(Exception):
23
def __init__(self, *args, **kwargs):
3-
super().__init__(*args, **kwargs)
4+
super().__init__(*args)
45

56

67
class RequestFailure(StatcordException):
@@ -9,6 +10,6 @@ def __init__(self, status: int, response: str):
910

1011

1112
class TooManyRequests(RequestFailure):
12-
def __init__(self, status: int, response: str, wait:int):
13+
def __init__(self, status: int, response: str, wait: int):
1314
self.wait = wait
14-
super().__init__("{}: {}".format(status, response))
15+
super().__init__(status, response)

0 commit comments

Comments
 (0)