Procházet zdrojové kódy

More annotations

master
Rocketsoup před 4 roky
rodič
revize
2eb8b1fcdc
3 změnil soubory, kde provedl 82 přidání a 53 odebrání
  1. 55
    29
      rocketbot/botmessage.py
  2. 25
    22
      rocketbot/cogsetting.py
  3. 2
    2
      rocketbot/utils.py

+ 55
- 29
rocketbot/botmessage.py Zobrazit soubor

@@ -2,6 +2,8 @@
2 2
 Classes for crafting messages from the bot. Content can change as information
3 3
 changes, and mods can perform actions on the message via emoji reactions.
4 4
 """
5
+from typing import Any, Optional, Union
6
+
5 7
 from datetime import datetime
6 8
 from discord import Guild, Message, PartialEmoji, TextChannel
7 9
 
@@ -20,9 +22,16 @@ class BotMessageReaction:
20 22
 	to explain why an action is no longer available.
21 23
 	"""
22 24
 	def __init__(self, emoji: str, is_enabled: bool, description: str):
23
-		self.emoji = emoji
24
-		self.is_enabled = is_enabled
25
-		self.description = description
25
+		"""
26
+		Creates a bot message reaction.
27
+		- emoji: The emoji string
28
+		- is_enabled: Whether the emoji can be used
29
+		- description: What reacting with this emoji does (or an explanation of
30
+		  why it's disabled)
31
+		"""
32
+		self.emoji: str = emoji
33
+		self.is_enabled: bool = is_enabled
34
+		self.description: str = description
26 35
 
27 36
 	def __eq__(self, other):
28 37
 		return other is not None and \
@@ -36,7 +45,7 @@ class BotMessageReaction:
36 45
 			message_count: int = 1,
37 46
 			did_kick: bool = None,
38 47
 			did_ban: bool = None,
39
-			user_count: int = 1) -> list:
48
+			user_count: int = 1) -> list[BotMessageReaction]:
40 49
 		"""
41 50
 		Convenience factory for generating any of the three most common
42 51
 		commands: delete message(s), kick user(s), and ban user(s). All
@@ -55,7 +64,7 @@ class BotMessageReaction:
55 64
 		- user_count     How many users there are. Used for pluralizing
56 65
 		                 description. Defaults to 1. Omit if n/a.
57 66
 		"""
58
-		reactions = []
67
+		reactions: list[BotMessageReaction] = []
59 68
 		if did_delete is not None:
60 69
 			if did_delete:
61 70
 				reactions.append(BotMessageReaction(
@@ -104,29 +113,38 @@ class BotMessage:
104 113
 	actions that can be taken via a mod reacting to the message.
105 114
 	"""
106 115
 
107
-	TYPE_DEFAULT = 0
108
-	TYPE_INFO = 1
109
-	TYPE_MOD_WARNING = 2
110
-	TYPE_SUCCESS = 3
111
-	TYPE_FAILURE = 4
116
+	TYPE_DEFAULT: int = 0
117
+	TYPE_INFO: int = 1
118
+	TYPE_MOD_WARNING: int = 2
119
+	TYPE_SUCCESS: int = 3
120
+	TYPE_FAILURE: int = 4
112 121
 
113 122
 	def __init__(self,
114 123
 			guild: Guild,
115 124
 			text: str,
116 125
 			type: int = TYPE_DEFAULT, # pylint: disable=redefined-builtin
117
-			context = None,
118
-			reply_to: Message = None):
119
-		self.guild = guild
120
-		self.text = text
121
-		self.type = type
122
-		self.context = context
123
-		self.quote = None
126
+			context: Optional[Any] = None,
127
+			reply_to: Optional[Message] = None):
128
+		"""
129
+		Creates a bot message.
130
+		- guild: The Discord guild to send the message to.
131
+		- text: Main text of the message.
132
+		- type: One of the TYPE_ constants.
133
+		- context: Arbitrary value that will be passed in the callback. Can be
134
+		  associated data for performing some action. (optional)
135
+		- reply_to: Existing message this message is in reply to. (optional)
136
+		"""
137
+		self.guild: Guild = guild
138
+		self.text: str = text
139
+		self.type: int = type
140
+		self.context: Optional[Any] = context
141
+		self.quote: Optional[str] = None
124 142
 		self.source_cog = None  # Set by `BaseCog.post_message()`
125
-		self.__posted_text = None  # last text posted, to test for changes
126
-		self.__posted_emoji = set()
127
-		self.__message = None  # Message
128
-		self.__reply_to = reply_to
129
-		self.__reactions = []  # BotMessageReaction[]
143
+		self.__posted_text: str = None  # last text posted, to test for changes
144
+		self.__posted_emoji: set[str] = set()  # last emoji list posted
145
+		self.__message: Optional[Message] = None  # set once the message has been posted
146
+		self.__reply_to: Optional[Message] = reply_to
147
+		self.__reactions: list[BotMessageReaction] = []
130 148
 
131 149
 	def is_sent(self) -> bool:
132 150
 		"""
@@ -136,11 +154,11 @@ class BotMessage:
136 154
 		"""
137 155
 		return self.__message is not None
138 156
 
139
-	def message_id(self):
157
+	def message_id(self) -> Optional[int]:
140 158
 		'Returns the Message id or None if not sent.'
141 159
 		return self.__message.id if self.__message else None
142 160
 
143
-	def message_sent_at(self) -> datetime:
161
+	def message_sent_at(self) -> Optional[datetime]:
144 162
 		'Returns when the message was sent or None if not sent.'
145 163
 		return self.__message.created_at if self.__message else None
146 164
 
@@ -156,7 +174,7 @@ class BotMessage:
156 174
 		self.text = new_text
157 175
 		await self.update_if_sent()
158 176
 
159
-	async def set_reactions(self, reactions: list) -> None:
177
+	async def set_reactions(self, reactions: list[BotMessageReaction]) -> None:
160 178
 		"""
161 179
 		Replaces all BotMessageReactions with a new list. If the message has
162 180
 		been sent, it will be updated.
@@ -194,7 +212,7 @@ class BotMessage:
194 212
 			self.__reactions.append(reaction)
195 213
 		await self.update_if_sent()
196 214
 
197
-	async def remove_reaction(self, reaction_or_emoji) -> None:
215
+	async def remove_reaction(self, reaction_or_emoji: Union[BotMessageReaction, str]) -> None:
198 216
 		"""
199 217
 		Removes a reaction. Can pass either a BotMessageReaction or just the
200 218
 		emoji string. If the message has been sent, it will be updated.
@@ -207,7 +225,7 @@ class BotMessage:
207 225
 				await self.update_if_sent()
208 226
 				return
209 227
 
210
-	def reaction_for_emoji(self, emoji) -> BotMessageReaction:
228
+	def reaction_for_emoji(self, emoji) -> Optional[BotMessageReaction]:
211 229
 		"""
212 230
 		Finds the BotMessageReaction for the given emoji or None if not found.
213 231
 		Accepts either a PartialEmoji or str.
@@ -245,11 +263,15 @@ class BotMessage:
245 263
 			else:
246 264
 				channel_id = Storage.get_config_value(self.guild, ConfigKey.WARNING_CHANNEL_ID)
247 265
 				if channel_id is None:
248
-					bot_log(self.guild, None, '\u0007No warning channel set! No warning issued.')
266
+					bot_log(self.guild, \
267
+						type(self.source_cog) if self.source_cog else None, \
268
+						'\u0007No warning channel set! No warning issued.')
249 269
 					return
250 270
 				channel: TextChannel = self.guild.get_channel(channel_id)
251 271
 				if channel is None:
252
-					bot_log(self.guild, None, '\u0007Configured warning channel does not exist!')
272
+					bot_log(self.guild, \
273
+						type(self.source_cog) if self.source_cog else None, \
274
+						'\u0007Configured warning channel does not exist!')
253 275
 					return
254 276
 				self.__message = await channel.send(content=content)
255 277
 				self.__posted_text = content
@@ -267,6 +289,10 @@ class BotMessage:
267 289
 				self.__posted_emoji.remove(emoji)
268 290
 
269 291
 	def __formatted_message(self) -> str:
292
+		"""
293
+		Composes the entire message markdown from components. Includes the main
294
+		message, quoted text, summary of available reactions, etc.
295
+		"""
270 296
 		s: str = ''
271 297
 
272 298
 		if self.type == self.TYPE_INFO:

+ 25
- 22
rocketbot/cogsetting.py Zobrazit soubor

@@ -1,6 +1,9 @@
1 1
 """
2 2
 A guild configuration setting available for editing via bot commands.
3 3
 """
4
+from types import coroutine
5
+from typing import Any, Optional, Type
6
+
4 7
 from discord.ext import commands
5 8
 from discord.ext.commands import Bot, Cog, Command, Context, Group
6 9
 
@@ -11,19 +14,19 @@ from rocketbot.utils import first_command_group
11 14
 class CogSetting:
12 15
 	"""
13 16
 	Describes a configuration setting for a guild that can be edited by the
14
-	mods of those guilds. BaseCog can generate "get" and "set" commands
15
-	automatically, reducing the boilerplate of generating commands manually.
16
-	Offers simple validation rules.
17
+	mods of those guilds. BaseCog can generate "get" and "set" commands (or
18
+	"enable" and "disable" commands for boolean values) automatically, reducing
19
+	the boilerplate of generating commands manually. Offers simple validation rules.
17 20
 	"""
18 21
 	def __init__(self,
19 22
 			name: str,
20
-			datatype,
21
-			brief: str = None,
22
-			description: str = None,
23
-			usage: str = None,
24
-			min_value = None,
25
-			max_value = None,
26
-			enum_values: set = None):
23
+			datatype: Type,
24
+			brief: Optional[str] = None,
25
+			description: Optional[str] = None,
26
+			usage: Optional[str] = None,
27
+			min_value: Optional[Any] = None,
28
+			max_value: Optional[Any] = None,
29
+			enum_values: Optional[set[Any]] = None):
27 30
 		"""
28 31
 		Params:
29 32
 		- name         Setting identifier. Must follow variable naming
@@ -41,14 +44,14 @@ class CogSetting:
41 44
 		- max_value    Largest allowable value. None for no maximum.
42 45
 		- enum_values  Set of allowed values. None if unconstrained.
43 46
 		"""
44
-		self.name = name
45
-		self.datatype = datatype
46
-		self.brief = brief
47
-		self.description = description or ''  # Can't be None
48
-		self.usage = usage
49
-		self.min_value = min_value
50
-		self.max_value = max_value
51
-		self.enum_values = enum_values
47
+		self.name: str = name
48
+		self.datatype: Type = datatype
49
+		self.brief: Optional[str] = brief
50
+		self.description: str = description or ''  # Can't be None
51
+		self.usage: Optional[str] = usage
52
+		self.min_value: Optional[Any] = min_value
53
+		self.max_value: Optional[Any] = max_value
54
+		self.enum_values: Optional[set[Any]] = enum_values
52 55
 		if self.enum_values or self.min_value is not None or self.max_value is not None:
53 56
 			self.description += '\n'
54 57
 		if self.enum_values:
@@ -86,7 +89,7 @@ class CogSetting:
86 89
 			(group or bot).add_command(self.__make_setter_command(cog))
87 90
 
88 91
 	def __make_getter_command(self, cog: Cog) -> Command:
89
-		setting = self
92
+		setting: CogSetting = self
90 93
 		async def getter(cog: Cog, context: Context) -> None:
91 94
 			setting_name = setting.name
92 95
 			if context.command.parent:
@@ -144,7 +147,7 @@ class CogSetting:
144 147
 		async def setter_bool(cog, context, new_value: bool):
145 148
 			await setter_common(cog, context, new_value)
146 149
 
147
-		setter = None
150
+		setter: coroutine = None
148 151
 		if setting.datatype == int:
149 152
 			setter = setter_int
150 153
 		elif setting.datatype == float:
@@ -167,7 +170,7 @@ class CogSetting:
167 170
 				commands.guild_only(),
168 171
 			])
169 172
 		# Passing `cog` in init gets ignored and set to `None` so set after.
170
-		# This ensures the callback is passed `self`.
173
+		# This ensures the callback is passed the cog as `self` argument.
171 174
 		command.cog = cog
172 175
 		return command
173 176
 
@@ -218,7 +221,7 @@ class CogSetting:
218 221
 		return command
219 222
 
220 223
 	@classmethod
221
-	def set_up_all(cls, cog: Cog, bot: Bot, settings: list) -> None:
224
+	def set_up_all(cls, cog: Cog, bot: Bot, settings: list[CogSetting]) -> None:
222 225
 		"""
223 226
 		Sets up editing commands for a list of CogSettings and adds them to a
224 227
 		cog. If the cog has a command Group, commands will be added to it.

+ 2
- 2
rocketbot/utils.py Zobrazit soubor

@@ -3,7 +3,7 @@ General utility functions.
3 3
 """
4 4
 import re
5 5
 from datetime import datetime, timedelta
6
-from typing import Any, Union
6
+from typing import Any, Optional, Type, Union
7 7
 
8 8
 from discord import Guild
9 9
 from discord.ext.commands import Cog, Group
@@ -86,7 +86,7 @@ def first_command_group(cog: Cog) -> Group:
86 86
 			return member
87 87
 	return None
88 88
 
89
-def bot_log(guild: Guild, cog_class, message: Any) -> None:
89
+def bot_log(guild: Optional[Guild], cog_class: Optional[Type], message: Any) -> None:
90 90
 	'Logs a message to stdout with time, cog, and guild info.'
91 91
 	now: datetime = datetime.now() # local
92 92
 	s = f'[{now.strftime("%Y-%m-%dT%H:%M:%S")}|'

Načítá se…
Zrušit
Uložit