PyBugReporter 1.0.9__tar.gz → 1.0.11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/PKG-INFO +4 -2
- pybugreporter-1.0.11/PyBugReporter/_version.py +1 -0
- pybugreporter-1.0.9/PyBugReporter.egg-info/requires.txt → pybugreporter-1.0.11/PyBugReporter/requirements.txt +1 -0
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/PyBugReporter/src/BugReporter.py +79 -22
- pybugreporter-1.0.11/PyBugReporter/src/DiscordBot.py +79 -0
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/PyBugReporter.egg-info/PKG-INFO +4 -2
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/PyBugReporter.egg-info/SOURCES.txt +1 -0
- pybugreporter-1.0.11/PyBugReporter.egg-info/requires.txt +2 -0
- pybugreporter-1.0.9/PyBugReporter/_version.py +0 -1
- pybugreporter-1.0.9/PyBugReporter/requirements.txt +0 -1
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/LICENSE +0 -0
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/PyBugReporter/__init__.py +0 -0
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/PyBugReporter/src/__init__.py +0 -0
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/PyBugReporter.egg-info/dependency_links.txt +0 -0
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/PyBugReporter.egg-info/top_level.txt +0 -0
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/README.md +0 -0
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/setup.cfg +0 -0
- {pybugreporter-1.0.9 → pybugreporter-1.0.11}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: PyBugReporter
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.11
|
|
4
4
|
Summary: A python library for catching thrown exceptions and automatically creating issues on a GitHub repo.
|
|
5
5
|
Home-page: https://github.com/byuawsfhtl/PyBugReporter.git
|
|
6
6
|
Author: Record Linking Lab
|
|
@@ -9,11 +9,13 @@ Project-URL: Bug Tracker, https://github.com/byuawsfhtl/PyBugReporter/issues
|
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Requires-Dist: python-graphql-client~=0.4.3
|
|
12
|
+
Requires-Dist: discord.py~=2.0.1
|
|
12
13
|
Dynamic: author
|
|
13
14
|
Dynamic: author-email
|
|
14
15
|
Dynamic: description
|
|
15
16
|
Dynamic: description-content-type
|
|
16
17
|
Dynamic: home-page
|
|
18
|
+
Dynamic: license-file
|
|
17
19
|
Dynamic: project-url
|
|
18
20
|
Dynamic: requires-dist
|
|
19
21
|
Dynamic: summary
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '1.0.11'
|
|
@@ -2,6 +2,7 @@ import asyncio
|
|
|
2
2
|
import sys
|
|
3
3
|
import traceback
|
|
4
4
|
from functools import wraps
|
|
5
|
+
from PyBugReporter.src.DiscordBot import DiscordBot
|
|
5
6
|
|
|
6
7
|
from python_graphql_client import GraphqlClient
|
|
7
8
|
|
|
@@ -25,8 +26,11 @@ class BugHandler:
|
|
|
25
26
|
repoName: str = ''
|
|
26
27
|
orgName: str = ''
|
|
27
28
|
test: bool = False
|
|
29
|
+
useDiscord: bool = False
|
|
30
|
+
botToken: str = ''
|
|
31
|
+
channelId: str | int = ''
|
|
28
32
|
|
|
29
|
-
def __init__(self, githubKey: str, repoName: str, orgName: str, test: bool) -> None:
|
|
33
|
+
def __init__(self, githubKey: str, repoName: str, orgName: str, test: bool, useDiscord: bool = False, botToken: str = "", channelId: str | int = "") -> None:
|
|
30
34
|
"""Saves the given information in the BugHandler object.
|
|
31
35
|
|
|
32
36
|
Args:
|
|
@@ -34,11 +38,19 @@ class BugHandler:
|
|
|
34
38
|
repoName (str): the name of the repo to report to
|
|
35
39
|
orgName (str): the organization of the repo
|
|
36
40
|
test (bool): whether or not bugs in this code should actually be reported
|
|
41
|
+
useDiscord (bool): whether to send the bug report to Discord
|
|
42
|
+
botToken (str): the token for the Discord bot
|
|
43
|
+
channelId (str | int): the ID of the Discord channel to send messages to
|
|
37
44
|
"""
|
|
38
45
|
self.githubKey = githubKey
|
|
39
46
|
self.repoName = repoName
|
|
40
47
|
self.orgName = orgName
|
|
41
48
|
self.test = test
|
|
49
|
+
self.useDiscord = useDiscord
|
|
50
|
+
|
|
51
|
+
if useDiscord:
|
|
52
|
+
self.botToken = botToken
|
|
53
|
+
self.channelId = channelId
|
|
42
54
|
|
|
43
55
|
class BugReporter:
|
|
44
56
|
"""Sends errors to their corresponding repos.
|
|
@@ -64,7 +76,7 @@ class BugReporter:
|
|
|
64
76
|
self.kwargs = kwargs
|
|
65
77
|
|
|
66
78
|
@classmethod
|
|
67
|
-
def setVars(cls, githubKey: str, repoName: str, orgName: str, test: bool) -> None:
|
|
79
|
+
def setVars(cls, githubKey: str, repoName: str, orgName: str, test: bool, useDiscord: bool = False, botToken: str = "", channelId: str = "") -> None:
|
|
68
80
|
"""Sets the necessary variables to make bug reports.
|
|
69
81
|
|
|
70
82
|
Args:
|
|
@@ -73,7 +85,7 @@ class BugReporter:
|
|
|
73
85
|
orgName (str): the name of the organization
|
|
74
86
|
test (bool): whether to run in testing mode
|
|
75
87
|
"""
|
|
76
|
-
cls.handlers[repoName] = BugHandler(githubKey, repoName, orgName, test)
|
|
88
|
+
cls.handlers[repoName] = BugHandler(githubKey, repoName, orgName, test, useDiscord, botToken, channelId)
|
|
77
89
|
|
|
78
90
|
def __call__(self, func: callable) -> None:
|
|
79
91
|
"""Decorator that catches exceptions and sends a bug report to the github repository.
|
|
@@ -118,26 +130,51 @@ class BugReporter:
|
|
|
118
130
|
if self.extraInfo:
|
|
119
131
|
description += f"\nExtra Info: {self.kwargs}"
|
|
120
132
|
|
|
133
|
+
# shortened description for discord if too long (shortens the error text)
|
|
134
|
+
start = f"# {title}\n\nType: {excType}\nError text: "
|
|
135
|
+
compress = f"{e}\nTraceback: {traceback.format_exc()}"
|
|
136
|
+
end = f"\n\nFunction Name: {functionName}\nArguments: {args}\nKeyword Arguments: {kwargs}"
|
|
137
|
+
if self.extraInfo:
|
|
138
|
+
end += f"\nExtra Info: {self.kwargs}"
|
|
139
|
+
|
|
140
|
+
staticLength = len(start) + len(end)
|
|
141
|
+
if staticLength > 2000:
|
|
142
|
+
shortDescription = f"# {title}\n\n" + description[:2000 - len(f"# {title}\n\n") - 3] + "..."
|
|
143
|
+
else:
|
|
144
|
+
shortDescription = f"{start}{compress[:2000 - staticLength]}{end}"
|
|
145
|
+
|
|
146
|
+
print(f"SHORT DESCRIPTION with length {len(shortDescription)}:\n{shortDescription}")
|
|
147
|
+
|
|
148
|
+
|
|
121
149
|
# Check if we need to send a bug report
|
|
122
150
|
if not self.handlers[repoName].test:
|
|
123
|
-
self._sendBugReport(repoName, title, description)
|
|
151
|
+
self._sendBugReport(repoName, title, description, shortDescription)
|
|
124
152
|
|
|
125
153
|
print(title)
|
|
126
154
|
print(description)
|
|
127
155
|
raise e
|
|
128
156
|
|
|
129
|
-
def _sendBugReport(self, repoName: str, errorTitle: str, errorMessage: str) -> None:
|
|
157
|
+
def _sendBugReport(self, repoName: str, errorTitle: str, errorMessage: str, shortErrorMessage: str) -> None:
|
|
130
158
|
"""Sends a bug report to the Github repository.
|
|
131
159
|
|
|
132
160
|
Args:
|
|
133
161
|
errorTitle (str): the title of the error
|
|
134
162
|
errorMessage (str): the error message
|
|
135
|
-
"""
|
|
163
|
+
"""
|
|
164
|
+
asyncio.run(self._sendBugReport_async(repoName, errorTitle, errorMessage, shortErrorMessage))
|
|
165
|
+
|
|
166
|
+
async def _sendBugReport_async(self, repoName: str, errorTitle: str, errorMessage: str, shortErrorMessage: str) -> None:
|
|
167
|
+
"""Sends a bug report to the Github repository asynchronously.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
errorTitle (str): the title of the error
|
|
171
|
+
errorMessage (str): the error message
|
|
172
|
+
"""
|
|
136
173
|
client = GraphqlClient(endpoint="https://api.github.com/graphql")
|
|
137
174
|
headers = {"Authorization": f"Bearer {self.handlers[repoName].githubKey}"}
|
|
138
175
|
|
|
139
176
|
# query variables
|
|
140
|
-
repoId = self.
|
|
177
|
+
repoId = await self._getRepoId_async(self.handlers[repoName])
|
|
141
178
|
bugLabel = "LA_kwDOJ3JPj88AAAABU1q15w"
|
|
142
179
|
autoLabel = "LA_kwDOJ3JPj88AAAABU1q2DA"
|
|
143
180
|
|
|
@@ -171,10 +208,15 @@ class BugReporter:
|
|
|
171
208
|
}
|
|
172
209
|
}
|
|
173
210
|
|
|
174
|
-
issueExists = self.
|
|
211
|
+
issueExists = await self._checkIfIssueExists_async(self.handlers[repoName], errorTitle)
|
|
175
212
|
|
|
176
|
-
|
|
177
|
-
|
|
213
|
+
# Send to Discord if applicable
|
|
214
|
+
if self.handlers[repoName].useDiscord:
|
|
215
|
+
discordBot = DiscordBot(self.handlers[repoName].botToken, self.handlers[repoName].channelId)
|
|
216
|
+
await discordBot.send_message(shortErrorMessage, issueExists)
|
|
217
|
+
|
|
218
|
+
if (not issueExists):
|
|
219
|
+
result = await client.execute_async(query=createIssue, variables=variables, headers=headers)
|
|
178
220
|
print('\nThis error has been reported to the Tree Growth team.\n')
|
|
179
221
|
|
|
180
222
|
issueId = result['data']['createIssue']['issue']['id'] # Extract the issue ID
|
|
@@ -191,7 +233,7 @@ class BugReporter:
|
|
|
191
233
|
"""
|
|
192
234
|
|
|
193
235
|
# Replace with your actual project ID
|
|
194
|
-
projectId = self.
|
|
236
|
+
projectId = await self.getProjectId_async(repoName, "Tree Growth Projects")
|
|
195
237
|
|
|
196
238
|
variables = {
|
|
197
239
|
"projectId": projectId,
|
|
@@ -199,12 +241,11 @@ class BugReporter:
|
|
|
199
241
|
}
|
|
200
242
|
|
|
201
243
|
# Execute the mutation to add the issue to the project
|
|
202
|
-
|
|
244
|
+
await client.execute_async(query=addToProject, variables=variables, headers=headers)
|
|
203
245
|
else:
|
|
204
246
|
print('\nOur team is already aware of this issue.\n')
|
|
205
247
|
|
|
206
|
-
def
|
|
207
|
-
"""Retrieves the GitHub project ID for a specified repository and project name."""
|
|
248
|
+
async def getProjectId_async(self, repoName: str, projectName: str) -> str:
|
|
208
249
|
client = GraphqlClient(endpoint="https://api.github.com/graphql")
|
|
209
250
|
headers = {"Authorization": f"Bearer {self.handlers[repoName].githubKey}"}
|
|
210
251
|
|
|
@@ -228,7 +269,7 @@ class BugReporter:
|
|
|
228
269
|
}
|
|
229
270
|
|
|
230
271
|
# Execute the query
|
|
231
|
-
response =
|
|
272
|
+
response = await client.execute_async(query=query, variables=variables, headers=headers)
|
|
232
273
|
projects = response["data"]["repository"]["projectsV2"]["nodes"]
|
|
233
274
|
|
|
234
275
|
# Find the project with the matching name and return its ID
|
|
@@ -238,7 +279,7 @@ class BugReporter:
|
|
|
238
279
|
|
|
239
280
|
raise ValueError(f"Project '{projectName}' not found in repository '{repoName}'.")
|
|
240
281
|
|
|
241
|
-
def
|
|
282
|
+
async def _checkIfIssueExists_async(self, handler: BugHandler, errorTitle: str) -> bool:
|
|
242
283
|
"""Checks if an issue already exists in the repository.
|
|
243
284
|
|
|
244
285
|
Args:
|
|
@@ -276,7 +317,7 @@ class BugReporter:
|
|
|
276
317
|
"labels": autoLabel,
|
|
277
318
|
}
|
|
278
319
|
|
|
279
|
-
result =
|
|
320
|
+
result = await client.execute_async(query=findIssue, variables=variables, headers=headers)
|
|
280
321
|
nodes = result['data']['organization']['repository']['issues']['nodes']
|
|
281
322
|
|
|
282
323
|
index = 0
|
|
@@ -292,7 +333,7 @@ class BugReporter:
|
|
|
292
333
|
|
|
293
334
|
return issueExists
|
|
294
335
|
|
|
295
|
-
def
|
|
336
|
+
async def _getRepoId_async(self, handler: BugHandler) -> str:
|
|
296
337
|
"""Gets the repository ID.
|
|
297
338
|
|
|
298
339
|
Args:
|
|
@@ -318,13 +359,24 @@ class BugReporter:
|
|
|
318
359
|
"name": handler.repoName
|
|
319
360
|
}
|
|
320
361
|
|
|
321
|
-
repoID =
|
|
362
|
+
repoID = await client.execute_async(query=getID, variables=variables, headers=headers)
|
|
322
363
|
return repoID['data']['repository']['id']
|
|
323
364
|
|
|
324
365
|
@classmethod
|
|
325
366
|
def manualBugReport(cls, repoName: str, errorTitle: str, errorMessage: str) -> None:
|
|
326
367
|
"""Manually sends a bug report to the Github repository.
|
|
327
368
|
|
|
369
|
+
Args:
|
|
370
|
+
repoName (str): the name of the repo to report to
|
|
371
|
+
errorTitle (str): the title of the error
|
|
372
|
+
errorMessage (str): the error message
|
|
373
|
+
"""
|
|
374
|
+
asyncio.run(cls.manualBugReportAsync(repoName,errorTitle,errorMessage))
|
|
375
|
+
|
|
376
|
+
@classmethod
|
|
377
|
+
async def manualBugReportAsync(cls, repoName: str, errorTitle: str, errorMessage: str) -> None:
|
|
378
|
+
"""Manually sends a bug report to the Github repository.
|
|
379
|
+
|
|
328
380
|
Args:
|
|
329
381
|
repoName (str): the name of the repo to report to
|
|
330
382
|
errorTitle (str): the title of the error
|
|
@@ -340,7 +392,7 @@ class BugReporter:
|
|
|
340
392
|
headers = {"Authorization": f"Bearer {handler.githubKey}"}
|
|
341
393
|
|
|
342
394
|
# query variables
|
|
343
|
-
repoId = cls.
|
|
395
|
+
repoId = await cls._getRepoId_async(cls, handler)
|
|
344
396
|
bugLabel = "LA_kwDOJ3JPj88AAAABU1q15w"
|
|
345
397
|
autoLabel = "LA_kwDOJ3JPj88AAAABU1q2DA"
|
|
346
398
|
|
|
@@ -373,10 +425,15 @@ class BugReporter:
|
|
|
373
425
|
}
|
|
374
426
|
}
|
|
375
427
|
|
|
376
|
-
issueExists = cls.
|
|
428
|
+
issueExists = await cls._checkIfIssueExists_async(cls, handler, errorTitle)
|
|
429
|
+
|
|
430
|
+
# Send to Discord if applicable
|
|
431
|
+
if cls.handlers[repoName].useDiscord:
|
|
432
|
+
discordBot = DiscordBot(cls.handlers[repoName].botToken, cls.handlers[repoName].channelId)
|
|
433
|
+
await discordBot.send_message(f"## {repoName}: {errorTitle}\n{errorMessage}", issueExists)
|
|
377
434
|
|
|
378
435
|
if (issueExists == False):
|
|
379
|
-
result =
|
|
436
|
+
result = await client.execute_async(query=createIssue, variables=variables, headers=headers)
|
|
380
437
|
print('\nThis error has been reported to the Tree Growth team.\n')
|
|
381
438
|
else:
|
|
382
439
|
print('\nOur team is already aware of this issue.\n')
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import discord
|
|
3
|
+
|
|
4
|
+
HISTORY_LIMIT = 20
|
|
5
|
+
EMOJI = "‼"
|
|
6
|
+
|
|
7
|
+
class DiscordBot(discord.Client):
|
|
8
|
+
"""
|
|
9
|
+
A simple Discord bot that forwards the bug reports to a given Discord channel.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
token (str): bot token
|
|
13
|
+
channel_id (int): the ID of the channel to send messages to
|
|
14
|
+
_message (str): message to send
|
|
15
|
+
_alreadySent (bool): whether the message has already been sent
|
|
16
|
+
_done_future (asyncio.Future): a future that is set when the bot is done
|
|
17
|
+
"""
|
|
18
|
+
def __init__(self, token: str, channelId: str | int) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Initializes the Discord bot with the given token and channel ID.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
token (str): bot token
|
|
24
|
+
channel_id (int): the ID of the channel to send messages to
|
|
25
|
+
"""
|
|
26
|
+
self.token = token
|
|
27
|
+
self.channelId = int(channelId)
|
|
28
|
+
self._message = None
|
|
29
|
+
self._alreadySent = False
|
|
30
|
+
self._doneFuture = None
|
|
31
|
+
|
|
32
|
+
intents = discord.Intents(emojis = True,
|
|
33
|
+
guild_reactions = True,
|
|
34
|
+
message_content = True,
|
|
35
|
+
guild_messages = True,
|
|
36
|
+
guilds = True)
|
|
37
|
+
super().__init__(intents=intents)
|
|
38
|
+
|
|
39
|
+
async def send_message(self, message, alreadySent = False):
|
|
40
|
+
"""
|
|
41
|
+
Sends a message to the specified channel by setting the variables and starting the bot, then turning it off when finished.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
message (str): The message to send.
|
|
45
|
+
alreadySent (bool): Whether the message has already been sent.
|
|
46
|
+
"""
|
|
47
|
+
self._message = message
|
|
48
|
+
self._alreadySent = alreadySent
|
|
49
|
+
self._doneFuture = asyncio.get_running_loop().create_future()
|
|
50
|
+
print("Starting bot...")
|
|
51
|
+
# Start the bot as a background task
|
|
52
|
+
asyncio.create_task(self.start(self.token))
|
|
53
|
+
# Wait until the message is sent and the bot is closed
|
|
54
|
+
await self._doneFuture
|
|
55
|
+
|
|
56
|
+
async def on_ready(self):
|
|
57
|
+
"""
|
|
58
|
+
Called when the bot is ready. Also sends the message to the specified channel, or reacts if it's been sent.
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
channel = await self.fetch_channel(self.channelId)
|
|
62
|
+
if channel and not self._alreadySent:
|
|
63
|
+
await channel.send(self._message)
|
|
64
|
+
print(f"Sent message to channel {self.channelId}")
|
|
65
|
+
elif channel and self._alreadySent:
|
|
66
|
+
async for message in channel.history(limit=HISTORY_LIMIT):
|
|
67
|
+
if message.content == self._message:
|
|
68
|
+
await message.add_reaction(EMOJI)
|
|
69
|
+
break
|
|
70
|
+
else:
|
|
71
|
+
print(f"Channel with ID {self.channelId} not found.")
|
|
72
|
+
except Exception as e:
|
|
73
|
+
print(f"Error sending message: {e}")
|
|
74
|
+
finally:
|
|
75
|
+
print("Shutting down bot...")
|
|
76
|
+
await self.close()
|
|
77
|
+
# Mark the future as done so send_message can return
|
|
78
|
+
if self._doneFuture and not self._doneFuture.done():
|
|
79
|
+
self._doneFuture.set_result(True)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: PyBugReporter
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.11
|
|
4
4
|
Summary: A python library for catching thrown exceptions and automatically creating issues on a GitHub repo.
|
|
5
5
|
Home-page: https://github.com/byuawsfhtl/PyBugReporter.git
|
|
6
6
|
Author: Record Linking Lab
|
|
@@ -9,11 +9,13 @@ Project-URL: Bug Tracker, https://github.com/byuawsfhtl/PyBugReporter/issues
|
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Requires-Dist: python-graphql-client~=0.4.3
|
|
12
|
+
Requires-Dist: discord.py~=2.0.1
|
|
12
13
|
Dynamic: author
|
|
13
14
|
Dynamic: author-email
|
|
14
15
|
Dynamic: description
|
|
15
16
|
Dynamic: description-content-type
|
|
16
17
|
Dynamic: home-page
|
|
18
|
+
Dynamic: license-file
|
|
17
19
|
Dynamic: project-url
|
|
18
20
|
Dynamic: requires-dist
|
|
19
21
|
Dynamic: summary
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '1.0.9'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
python-graphql-client~=0.4.3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|