Skip to content

Commit c2c330f

Browse files
Add bug report tgui (#6907)
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may not be viewable. --> ## About The Pull Request Replaces the double input prompt with a tgui interface which allows for extra tags and severity to be set. <img width="603" height="394" alt="image" src="https://github.com/user-attachments/assets/5ac525e7-ce48-41f1-b9f8-6c00363466ac" /> If you need to report without TGUI then the OOC tab has the legacy report. <!-- Describe The Pull Request. Please be sure every change is documented or this can delay review and even discourage maintainers from merging your PR! --> ## Why It's Good For The Game Easier to edit, extensible if we want to add more info to bug reports <!-- Argue for the merits of your changes and how they benefit the game, especially if they are controversial and/or far reaching. If you can't actually explain WHY what you are doing will improve the game, then it probably isn't good for the game in the first place. --> ## Changelog <!-- If your PR modifies aspects of the game that can be concretely observed by players or admins you should add a changelog. If your change does NOT meet this description, remove this section. Please note that maintainers freely reserve the right to remove and add tags should they deem it appropriate. You can attempt to finagle the system all you want, but it's best to shoot for clear communication right off the bat. --> <!-- !! Do not add whitespace in-between the entries, do not change the tags and do not leave the tags without any entries. --> :cl: add: Added bug report TGUI. Legacy version available under OOC verbs. /:cl: <!-- Both :cl:'s are required for the changelog to work! You can put your name to the right of the first :cl: if you want to overwrite your GitHub username as author ingame. --> <!-- You can use multiple of the same prefix (they're only used for the icon ingame) and delete the unneeded ones. Despite some of the tags, changelogs should generally represent how a player might be affected by the changes rather than a summary of the PR's contents. --> ## Pre-Merge Checklist <!-- Don't bother filling these in while creating your Pull Request, just click the checkboxes after the Pull Request is opened and you are redirected to the page. --> - [ ] You tested this on a local server. - [ ] This code did not runtime during testing. - [ ] You documented all of your changes. <!-- Neither the compiler nor workflow checks are perfect at detecting runtimes and errors. It is important to test your code/feature/fix locally. -->
1 parent ee0528f commit c2c330f

4 files changed

Lines changed: 361 additions & 0 deletions

File tree

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/datum/bug_report
2+
3+
/datum/bug_report/ui_state(mob/user)
4+
return GLOB.always_state
5+
6+
/datum/bug_report/ui_close(mob/user)
7+
qdel(src)
8+
9+
/datum/bug_report/ui_interact(mob/user, datum/tgui/ui)
10+
ui = SStgui.try_update_ui(user, src, ui)
11+
if(!ui)
12+
ui = new(user, src, "BugReport")
13+
ui.open()
14+
15+
/datum/bug_report/ui_data(mob/user)
16+
var/list/data = list()
17+
var/client/user_client = user.client
18+
data["byond"] = "[user_client.byond_version].[user_client.byond_build]"
19+
data["ckey"] = "[user_client.ckey]"
20+
return data
21+
22+
/datum/bug_report/ui_static_data(mob/user)
23+
var/list/data = list()
24+
data["roundid"] = GLOB.round_id || "Unknown"
25+
data["map"] = SSmapping.config.map_name
26+
return data
27+
28+
/datum/bug_report/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
29+
. = ..()
30+
if(.)
31+
return TRUE
32+
33+
if(action != "submit")
34+
return
35+
36+
var/client/user_client = ui.user?.client
37+
if(!user_client)
38+
return
39+
40+
var/githuburl = CONFIG_GET(string/githuburl)
41+
if(!githuburl)
42+
to_chat(user_client, span_danger("The Github URL is not set in the server configuration."))
43+
return
44+
45+
var/issue_key = CONFIG_GET(string/issue_key)
46+
if(!issue_key)
47+
to_chat(user_client, span_danger("Issue Reporting is not properly configured."))
48+
return
49+
50+
var/user_data = params["user_data"]
51+
var/ckey = user_data["ckey"]
52+
var/byond = user_data["byond"]
53+
var/round_id = user_data["round_id"]
54+
var/map = user_data["map"]
55+
56+
var/report_title = params["report_title"]
57+
if(!report_title)
58+
to_chat(user_client, span_danger("Please enter a title"))
59+
return
60+
61+
var/report_content = params["report_body"]
62+
if(!report_content)
63+
to_chat(user_client, span_danger("Please enter a body"))
64+
return
65+
66+
var/severity = params["severity"]
67+
var/list/labels = params["labels"]
68+
69+
if(severity != "Not set") // Value from TGUI
70+
labels += severity
71+
else
72+
severity = null
73+
74+
// Keep a static version of the template to avoid reading file
75+
var/static/issue_template = file2text(".github/ISSUE_TEMPLATE/bug_report.md")
76+
77+
// Get a local copy of the template for modification
78+
var/local_template = issue_template
79+
80+
local_template = replacetext(local_template, "## Map:\n", "## Map:\n[map]")
81+
82+
// Insert round
83+
if(round_id != "Unknown")
84+
local_template = replacetext(local_template, "## Round ID:\n", "## Round ID:\n[round_id]")
85+
86+
// Insert testmerges
87+
if(length(GLOB.revdata.testmerge))
88+
var/list/all_tms = list()
89+
for(var/datum/tgs_revision_information/test_merge/tm as anything in GLOB.revdata.testmerge)
90+
all_tms += "- \[[tm.title]\]([githuburl]/pull/[tm.number])"
91+
var/all_tms_joined = all_tms.Join("\n") // for some reason this can't go in the []
92+
local_template = replacetext(local_template, "## Testmerges:\n", "## Testmerges:\n[all_tms_joined]")
93+
94+
local_template = replacetext(local_template, "## Reproduction:\n", "## Reproduction:\n[report_content]")
95+
96+
var/list/client_info = list()
97+
client_info += "BYOND: [byond]"
98+
client_info += "Ckey: [ckey]"
99+
100+
local_template = replacetext(local_template, "## Client Information:\n", "## Client Information:\n[client_info.Join("\n")]")
101+
102+
var/title_prefix = ""
103+
if(severity)
104+
title_prefix = "\[[severity]\] "
105+
106+
var/issue_title = "[title_prefix][report_title]"
107+
108+
var/list/body_structure = list(
109+
"title" = issue_title,
110+
"body" = local_template,
111+
"labels" = labels,
112+
)
113+
114+
var/datum/http_request/issue_report = new
115+
rustg_file_write(local_template, "[GLOB.log_directory]/issue_reports/[ckey]-[world.time]-[SANITIZE_FILENAME(issue_title)].txt")
116+
message_admins("BUGREPORT: Bug report filed by [ADMIN_LOOKUPFLW(user_client)], Title: [strip_html(issue_title)]")
117+
issue_report.prepare(
118+
RUSTG_HTTP_METHOD_POST,
119+
"https://api.github.com/repos/[CONFIG_GET(string/issue_slug)]/issues",
120+
json_encode(body_structure), //this is slow slow slow but no other options buckaroo
121+
list(
122+
"Accept"="application/vnd.github+json",
123+
"Authorization"="Bearer [issue_key]",
124+
"X-GitHub-Api-Version"="2022-11-28"
125+
)
126+
)
127+
to_chat(user_client, span_notice("Sending issue report..."))
128+
//SEND_SOUND(user_client, 'sound/misc/compiler-stage1.ogg')
129+
issue_report.begin_async()
130+
UNTIL(issue_report.is_complete() || !user_client) //Client fuckery.
131+
var/datum/http_response/issue_response = issue_report.into_response()
132+
if(issue_response.errored || issue_response.status_code != 201)
133+
//SEND_SOUND(user_client, 'sound/misc/compiler-failure.ogg')
134+
to_chat(user_client, "[span_alertwarning("Bug report FAILED!")]\n\
135+
[span_warning("Please adminhelp immediately!")]\n\
136+
[span_notice("Code:[issue_response.status_code || "9001 CATASTROPHIC ERROR"]")]")
137+
138+
log_runtime(
139+
"Failed to send issue report. errored=[issue_response.errored], status_code=[isnull(issue_response.status_code) ? "(null)" : issue_response.status_code]",
140+
list(
141+
"status_code" = issue_response.status_code,
142+
"errored" = issue_response.errored,
143+
"headers" = issue_response.headers?.Copy(),
144+
"error" = issue_response.error,
145+
"body" = issue_report.body,
146+
)
147+
)
148+
149+
return
150+
//SEND_SOUND(user_client, 'sound/misc/compiler-stage2.ogg')
151+
to_chat(user_client, span_notice("Bug submitted successfully."))
152+
qdel(src)

interface/interface.dm

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,33 @@
6464
set desc = "Report an issue"
6565
set hidden = 1
6666

67+
if(!CONFIG_GET(string/githuburl))
68+
to_chat(src, span_danger("The Github URL is not set in the server configuration."))
69+
return
70+
71+
if(!CONFIG_GET(string/issue_key))
72+
to_chat(src, span_danger("Issue Reporting is not properly configured."))
73+
return
74+
75+
var/testmerge_data = GLOB.revdata.testmerge
76+
var/has_testmerge_data = (length(testmerge_data) != 0)
77+
78+
var/message = "This will start reporting an issue, gathering some information from the server and your client, before submitting it to github."
79+
if(has_testmerge_data)
80+
message += "<br>The following experimental changes are active and may be the cause of any new or sudden issues:<br>"
81+
message += GLOB.revdata.GetTestMergeInfo(FALSE)
82+
83+
if(browser_alert(src, message, "Report Issue", DEFAULT_INPUT_CHOICES) != CHOICE_YES)
84+
return
85+
86+
var/datum/bug_report/reporter = new()
87+
reporter.ui_interact(mob)
88+
89+
/client/verb/reportissue_legacy()
90+
set name = "report-issue-legacy"
91+
set desc = "Report an issue without TGUI."
92+
set category = "OOC"
93+
6794
var/githuburl = CONFIG_GET(string/githuburl)
6895
if(!githuburl)
6996
to_chat(src, span_danger("The Github URL is not set in the server configuration."))
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import { useState } from 'react';
2+
import {
3+
Button,
4+
TextArea,
5+
Section,
6+
Stack,
7+
Input,
8+
Dropdown,
9+
} from 'tgui-core/components';
10+
import { useBackend } from '../backend';
11+
import { Window } from '../layouts';
12+
13+
type Data = {
14+
byond: string;
15+
ckey: string;
16+
round_id: string;
17+
map: string;
18+
}
19+
20+
// Labels should match a label on the github
21+
const LABELS = [
22+
"Antagonist",
23+
"Medical",
24+
"Interface",
25+
"Mapping",
26+
"Other",
27+
]
28+
29+
const SEVERITY_NONE = "Not set"
30+
31+
const SEVERITIES = [
32+
"Critical",
33+
"Major",
34+
"Minor",
35+
"Trivial",
36+
SEVERITY_NONE,
37+
]
38+
39+
export const sanitizeMultiline = (toSanitize: string) => {
40+
return toSanitize.replace(/(\n|\r\n){3,}/, '\n\n');
41+
};
42+
43+
export const BugReport = () => {
44+
const { act, data } = useBackend<Data>();
45+
const {
46+
byond,
47+
ckey,
48+
round_id,
49+
map,
50+
} = data;
51+
52+
const [title, setTitle] = useState('');
53+
const [input, setInput] = useState('');
54+
const [severity, setSeverity] = useState(SEVERITY_NONE);
55+
const [labels, setLabels] = useState<string[]>([]);
56+
57+
const updateLabel = (value: string) => {
58+
const newLabel = [...labels]
59+
if (newLabel.includes(value)) {
60+
newLabel.splice(newLabel.indexOf(value));
61+
} else {
62+
newLabel.push(value);
63+
}
64+
setLabels(newLabel);
65+
}
66+
67+
const onTypeInput = (value: string) => {
68+
if (value === input) {
69+
return;
70+
}
71+
setInput(sanitizeMultiline(value));
72+
};
73+
74+
const onTypeTitle = (value: string) => {
75+
if (value === title) {
76+
return;
77+
}
78+
setTitle(value);
79+
}
80+
81+
return (
82+
<Window
83+
title="Bug Report"
84+
width={600}
85+
height={400}
86+
>
87+
<Window.Content>
88+
<Stack fill vertical>
89+
<Stack.Item height={'85%'}>
90+
<Stack fill>
91+
<Stack.Item width={'25%'}>
92+
<Section fill title="Extra info">
93+
<Stack vertical>
94+
<Stack.Item>
95+
Ckey: {ckey}
96+
</Stack.Item>
97+
<Stack.Item>
98+
Byond: {byond}
99+
</Stack.Item>
100+
<Stack.Item>
101+
Round ID: {round_id ? round_id : "Unknown"}
102+
</Stack.Item>
103+
<Stack.Item>
104+
Map: {map}
105+
</Stack.Item>
106+
</Stack>
107+
</Section>
108+
</Stack.Item>
109+
<Stack.Item grow>
110+
<Section fill title="Content">
111+
<Input
112+
autoFocus
113+
autoSelect
114+
fluid
115+
mb={1}
116+
onChange={onTypeTitle}
117+
placeholder="Your title..."
118+
value={title}
119+
/>
120+
<TextArea
121+
fluid
122+
height={'90%'}
123+
maxLength={1024}
124+
onChange={onTypeInput}
125+
placeholder="Type your report..."
126+
value={input}
127+
/>
128+
</Section>
129+
</Stack.Item>
130+
<Stack.Item width={'25%'}>
131+
<Stack fill vertical>
132+
<Stack.Item height={'70%'}>
133+
<Section fill title="Labels">
134+
{LABELS.map((value, i) => (
135+
<Button
136+
key={i}
137+
color={labels.includes(value) ? "good" : null}
138+
onClick={() => updateLabel(value)}
139+
>
140+
{value}
141+
</Button>
142+
))}
143+
</Section>
144+
</Stack.Item>
145+
<Stack.Item height={'30%'}>
146+
<Section fill title="Severity">
147+
<Dropdown
148+
options={SEVERITIES}
149+
selected={severity}
150+
onSelected={(value) => setSeverity(value)}
151+
/>
152+
</Section>
153+
</Stack.Item>
154+
</Stack>
155+
</Stack.Item>
156+
</Stack>
157+
</Stack.Item>
158+
<Stack.Item height={'5%'}>
159+
<Section fill>
160+
<Button
161+
color="good"
162+
textAlign="center"
163+
fluid
164+
onClick={() => act('submit', {
165+
user_data: data,
166+
report_title: title,
167+
report_body: input,
168+
labels: labels,
169+
severity: severity,
170+
})}
171+
px={4}
172+
>
173+
Submit
174+
</Button>
175+
</Section>
176+
</Stack.Item>
177+
</Stack>
178+
</Window.Content>
179+
</Window>
180+
)
181+
}

vanderlin.dme

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2498,6 +2498,7 @@
24982498
#include "code\modules\awaymissions\corpse.dm"
24992499
#include "code\modules\awaymissions\zlevel.dm"
25002500
#include "code\modules\balloon_alert\balloon_alert.dm"
2501+
#include "code\modules\bug_report\bug_report.dm"
25012502
#include "code\modules\buildmode\_build_screens.dm"
25022503
#include "code\modules\buildmode\_building_menu.dm"
25032504
#include "code\modules\buildmode\_list_generation.dm"

0 commit comments

Comments
 (0)