[MIRROR] Multiple Fax machine tweaks, additions incl incorporation of staff request to main GUI (#6719)

Co-authored-by: Casey <a.roaming.shadow@gmail.com>
Co-authored-by: Raeschen <rycoop29@gmail.com>
This commit is contained in:
CHOMPStation2
2023-08-14 05:58:59 -07:00
committed by GitHub
parent d2b2cd04d4
commit b7a83db35d
7 changed files with 358 additions and 15 deletions

View File

@@ -1,16 +1,17 @@
var/list/obj/machinery/photocopier/faxmachine/allfaxes = list()
var/list/admin_departments = list("[using_map.boss_name]", "Solar Central Government", "Central Command Job Boards", "Supply") // YW EDIT
var/list/alldepartments = list()
var/global/last_fax_role_request
var/list/adminfaxes = list() //cache for faxes that have been sent to admins
/obj/machinery/photocopier/faxmachine
name = "fax machine"
desc = "Sent papers and pictures far away! Or to your co-worker's office a few doors down."
desc = "Send papers and pictures far away! Or to your co-worker's office a few doors down."
icon = 'icons/obj/library.dmi'
icon_state = "fax"
insert_anim = "faxsend"
req_one_access = list(access_lawyer, access_heads, access_armory, access_qm)
req_one_access = list()
use_power = USE_POWER_IDLE
idle_power_usage = 30
@@ -41,6 +42,109 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
else
tgui_interact(user)
/obj/machinery/photocopier/faxmachine/verb/remove_card()
set name = "Remove ID card"
set category = "Object"
set src in oview(1)
var/mob/living/L = usr
if(!L || !isturf(L.loc) || !isliving(L))
return
if(!ishuman(L) && !issilicon(L))
return
if(L.stat || L.restrained())
return
if(!scan)
to_chat(L, span_notice("There is no I.D card to remove!"))
return
scan.forceMove(loc)
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
authenticated = null
/obj/machinery/photocopier/faxmachine/verb/request_roles()
set name = "Staff Request Form"
set category = "Object"
set src in oview(1)
var/mob/living/L = usr
if(!L || !isturf(L.loc) || !isliving(L))
return
if(!ishuman(L) && !issilicon(L))
return
if(L.stat || L.restrained())
return
if(last_fax_role_request && (world.time - last_fax_role_request < 5 MINUTES))
to_chat(L, "<span class='warning'>The global automated relays are still recalibrating. Try again later or relay your request in written form for processing.</span>")
return
var/confirmation = tgui_alert(L, "Are you sure you want to send automated crew request?", "Confirmation", list("Yes", "No", "Cancel"))
if(confirmation != "Yes")
return
var/list/jobs = list()
for(var/datum/department/dept as anything in SSjob.get_all_department_datums())
if(!dept.assignable || dept.centcom_only)
continue
for(var/job in SSjob.get_job_titles_in_department(dept.name))
var/datum/job/J = SSjob.get_job(job)
if(J.requestable)
jobs |= job
var/role = tgui_input_list(L, "Pick the job to request.", "Job Request", jobs)
if(!role)
return
var/datum/job/job_to_request = SSjob.get_job(role)
var/reason = "Unspecified"
var/list/possible_reasons = list("Unspecified", "General duties", "Emergency situation")
possible_reasons += job_to_request.get_request_reasons()
reason = tgui_input_list(L, "Pick request reason.", "Request reason", possible_reasons)
var/final_conf = tgui_alert(L, "You are about to request [role]. Are you sure?", "Confirmation", list("Yes", "No", "Cancel"))
if(final_conf != "Yes")
return
var/datum/department/ping_dept = SSjob.get_ping_role(role)
if(!ping_dept)
to_chat(L, "<span class='warning'>Selected job cannot be requested for \[ERRORDEPTNOTFOUND] reason. Please report this to system administrator.</span>")
return
var/message_color = "#FFFFFF"
var/ping_name = null
switch(ping_dept.name)
if(DEPARTMENT_COMMAND)
ping_name = "Command"
if(DEPARTMENT_SECURITY)
ping_name = "Security"
if(DEPARTMENT_ENGINEERING)
ping_name = "Engineering"
if(DEPARTMENT_MEDICAL)
ping_name = "Medical"
if(DEPARTMENT_RESEARCH)
ping_name = "Research"
if(DEPARTMENT_CARGO)
ping_name = "Supply"
if(DEPARTMENT_CIVILIAN)
ping_name = "Service"
if(DEPARTMENT_PLANET)
ping_name = "Expedition"
if(DEPARTMENT_SYNTHETIC)
ping_name = "Silicon"
//if(DEPARTMENT_TALON)
// ping_name = "Offmap"
if(!ping_name)
to_chat(L, "<span class='warning'>Selected job cannot be requested for \[ERRORUNKNOWNDEPT] reason. Please report this to system administrator.</span>")
return
message_color = ping_dept.color
message_chat_rolerequest(message_color, ping_name, reason, role)
last_fax_role_request = world.time
to_chat(L, "<span class='notice'>Your request was transmitted.</span>")
/obj/machinery/photocopier/faxmachine/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -54,6 +158,7 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
data["rank"] = rank
data["isAI"] = isAI(user)
data["isRobot"] = isrobot(user)
data["adminDepartments"] = admin_departments
data["bossName"] = using_map.boss_name
data["copyItem"] = copyitem
@@ -111,6 +216,8 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
usr.put_in_hands(copyitem)
to_chat(usr, "<span class='notice'>You take \the [copyitem] out of \the [src].</span>")
copyitem = null
if("send_automated_staff_request")
request_roles()
if(!authenticated)
return
@@ -258,10 +365,8 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
C << 'sound/machines/printer.ogg'
sender.client << 'sound/machines/printer.ogg' //CHOMPEdit - The pain must be felt
// VoreStation Edit Start
var/faxid = export_fax(sent)
message_chat_admins(sender, faxname, sent, faxid, font_colour)
// VoreStation Edit End
message_chat_admins(sender, faxname, sent, faxid, font_colour) //Sends to admin chat
// Webhooks don't parse the HTML on the paper, so we gotta strip them out so it's still readable.
var/summary = make_summary(sent)
@@ -282,3 +387,80 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
"body" = summary
)
)
/*
##### ####
##### Webhook Functionality ####
##### ####
*/
/datum/configuration
var/chat_webhook_url = "" // URL of the webhook for sending announcements/faxes to discord chat.
var/chat_webhook_key = "" // Shared secret for authenticating to the chat webhook
var/fax_export_dir = "data/faxes" // Directory in which to write exported fax HTML files.
/**
* Write the fax to disk as (potentially multiple) HTML files.
* If the fax is a paper_bundle, do so recursively for each page.
* returns a random unique faxid.
*/
/obj/machinery/photocopier/faxmachine/proc/export_fax(fax)
var faxid = "[num2text(world.realtime,12)]_[rand(10000)]"
if (istype(fax, /obj/item/weapon/paper))
var/obj/item/weapon/paper/P = fax
var/text = "<HTML><HEAD><TITLE>[P.name]</TITLE></HEAD><BODY>[P.info][P.stamps]</BODY></HTML>";
file("[config.fax_export_dir]/fax_[faxid].html") << text;
else if (istype(fax, /obj/item/weapon/photo))
var/obj/item/weapon/photo/H = fax
fcopy(H.img, "[config.fax_export_dir]/photo_[faxid].png")
var/text = "<html><head><title>[H.name]</title></head>" \
+ "<body style='overflow:hidden;margin:0;text-align:center'>" \
+ "<img src='photo_[faxid].png'>" \
+ "[H.scribble ? "<br>Written on the back:<br><i>[H.scribble]</i>" : ""]"\
+ "</body></html>"
file("[config.fax_export_dir]/fax_[faxid].html") << text
else if (istype(fax, /obj/item/weapon/paper_bundle))
var/obj/item/weapon/paper_bundle/B = fax
var/data = ""
for (var/page = 1, page <= B.pages.len, page++)
var/obj/pageobj = B.pages[page]
var/page_faxid = export_fax(pageobj)
data += "<a href='fax_[page_faxid].html'>Page [page] - [pageobj.name]</a><br>"
var/text = "<html><head><title>[B.name]</title></head><body>[data]</body></html>"
file("[config.fax_export_dir]/fax_[faxid].html") << text
return faxid
/**
* Call the chat webhook to transmit a notification of an admin fax to the admin chat.
*/
/obj/machinery/photocopier/faxmachine/proc/message_chat_admins(var/mob/sender, var/faxname, var/obj/item/sent, var/faxid, font_colour="#006100")
if (config.chat_webhook_url)
spawn(0)
var/query_string = "type=fax"
query_string += "&key=[url_encode(config.chat_webhook_key)]"
query_string += "&faxid=[url_encode(faxid)]"
query_string += "&color=[url_encode(font_colour)]"
query_string += "&faxname=[url_encode(faxname)]"
query_string += "&sendername=[url_encode(sender.name)]"
query_string += "&sentname=[url_encode(sent.name)]"
world.Export("[config.chat_webhook_url]?[query_string]")
/**
* Call the chat webhook to transmit a notification of a job request
*/
/obj/machinery/photocopier/faxmachine/proc/message_chat_rolerequest(var/font_colour="#006100", var/role_to_ping, var/reason, var/jobname)
if(config.chat_webhook_url)
spawn(0)
var/query_string = "type=rolerequest"
query_string += "&key=[url_encode(config.chat_webhook_key)]"
query_string += "&ping=[url_encode(role_to_ping)]"
query_string += "&color=[url_encode(font_colour)]"
query_string += "&reason=[url_encode(reason)]"
query_string += "&job=[url_encode(jobname)]"
world.Export("[config.chat_webhook_url]?[query_string]")

View File

@@ -7,21 +7,26 @@ import { LoginScreen } from './common/LoginScreen';
export const Fax = (props, context) => {
const { data } = useBackend(context);
const { authenticated } = data;
const { authenticated, copyItem } = data;
let variableHeight = 340;
if (copyItem) {
variableHeight = 358;
}
if (!authenticated) {
return (
<Window width={600} height={250} resizable>
<Window.Content>
<RemoveItem />
<LoginScreen />
<LoginScreen machineType="Fax" />
</Window.Content>
</Window>
);
}
return (
<Window width={600} height={250} resizable>
<Window width={600} height={variableHeight} resizable>
<Window.Content>
<RemoveItem />
<LoginInfo />
@@ -34,7 +39,8 @@ export const Fax = (props, context) => {
export const FaxContent = (props, context) => {
const { act, data } = useBackend(context);
const { bossName, copyItem, cooldown, destination } = data;
const { bossName, copyItem, cooldown, destination, adminDepartments } = data;
const staffRequestDepartment = new Set(adminDepartments);
return (
<Section>
@@ -48,6 +54,7 @@ export const FaxContent = (props, context) => {
{bossName} Quantum Entanglement Network
</LabeledList.Item>
</LabeledList>
{(copyItem && (
<Box mt={1}>
<LabeledList>
@@ -70,6 +77,7 @@ export const FaxContent = (props, context) => {
/>
</Box>
)) || <Box mt={1}>Please insert item to transmit.</Box>}
<AutomatedStaffRequest />
</Section>
);
};
@@ -94,3 +102,40 @@ const RemoveItem = (props, context) => {
</Box>
);
};
const AutomatedStaffRequest = (props, context) => {
const { act, data } = useBackend(context);
const { adminDepartments, destination, copyItem } = data;
const staffRequestDepartment = new Set(adminDepartments);
let flexiblePadding = '1rem';
if (copyItem) {
flexiblePadding = '1.5rem';
}
if (!copyItem || (copyItem && staffRequestDepartment.has(destination))) {
return (
<Box mt="1.5rem">
<b>Or submit an automated staff request.</b> <br /> <br />
<i>
The automated staff request form automatically populates the company
job board ((sends to discord, but does not ping.)) without requiring
intervention from central command clerks and officers. <br />
It also works without requiring a written request to be composed.
</i>
<br />
<Box mt="1.5rem">
<Button
icon="share-square"
onClick={() => act('send_automated_staff_request')}
content="Send Automated Staff Request"
fluid
/>
</Box>
</Box>
);
} else {
return null;
}
};

View File

@@ -5,6 +5,9 @@ import { FullscreenNotice } from './FullscreenNotice';
/**
* Displays a login screen that users can interact with
* using an ID card in their hand.
* Special elements can be invoked by defining machineType prop
* possible string arguments are defined in SpecialMachineInteraction in loginScreen.js
*
* Required data fields:
* * `scan` — The name of the currently inserted ID
* * `isAI` — Whether the user is an AI. If true, shows "Login as AI"
@@ -26,6 +29,7 @@ import { FullscreenNotice } from './FullscreenNotice';
export const LoginScreen = (_properties, context) => {
const { act, data } = useBackend(context);
const { scan, isAI, isRobot } = data;
const { machineType } = _properties;
return (
<FullscreenNotice title="Welcome">
<Box fontSize="1.5rem" bold>
@@ -51,6 +55,7 @@ export const LoginScreen = (_properties, context) => {
})
}
/>
{!!isAI && (
<Button
icon="sign-in-alt"
@@ -73,6 +78,37 @@ export const LoginScreen = (_properties, context) => {
}
/>
)}
<Box>
<SpecialMachineInteraction specialType={machineType} />
</Box>
</FullscreenNotice>
);
};
/**
* Special login screen elements that we want to appear for specific machines.
* Props: "specialType", arguemnt: string
* specialType definitions are defined in LoginScreen.js SpecialMachineInteraction
* currently supported: "Fax"
*/
export const SpecialMachineInteraction = (_properties, context) => {
const { act } = useBackend(context);
const { specialType } = _properties;
if (!specialType) {
return null;
} else if (specialType === 'Fax') {
return (
<Button
position="relative"
content="Send Automated Fax Request"
bottom="152px"
left="188px"
icon="share-square"
onClick={() => act('send_automated_staff_request')}
tooltip={
"Automated Fax Requests do not require staff to post on discord, but won't ping the related roles."
}
/>
);
}
};

View File

@@ -7,21 +7,26 @@ import { LoginScreen } from './common/LoginScreen';
export const Fax = (props, context) => {
const { data } = useBackend(context);
const { authenticated } = data;
const { authenticated, copyItem } = data;
let variableHeight = 340;
if (copyItem) {
variableHeight = 358;
}
if (!authenticated) {
return (
<Window width={600} height={250} resizable>
<Window.Content>
<RemoveItem />
<LoginScreen />
<LoginScreen machineType="Fax" />
</Window.Content>
</Window>
);
}
return (
<Window width={600} height={250} resizable>
<Window width={600} height={variableHeight} resizable>
<Window.Content>
<RemoveItem />
<LoginInfo />
@@ -34,7 +39,8 @@ export const Fax = (props, context) => {
export const FaxContent = (props, context) => {
const { act, data } = useBackend(context);
const { bossName, copyItem, cooldown, destination } = data;
const { bossName, copyItem, cooldown, destination, adminDepartments } = data;
const staffRequestDepartment = new Set(adminDepartments);
return (
<Section>
@@ -48,6 +54,7 @@ export const FaxContent = (props, context) => {
{bossName} Quantum Entanglement Network
</LabeledList.Item>
</LabeledList>
{(copyItem && (
<Box mt={1}>
<LabeledList>
@@ -70,6 +77,7 @@ export const FaxContent = (props, context) => {
/>
</Box>
)) || <Box mt={1}>Please insert item to transmit.</Box>}
<AutomatedStaffRequest />
</Section>
);
};
@@ -94,3 +102,40 @@ const RemoveItem = (props, context) => {
</Box>
);
};
const AutomatedStaffRequest = (props, context) => {
const { act, data } = useBackend(context);
const { adminDepartments, destination, copyItem } = data;
const staffRequestDepartment = new Set(adminDepartments);
let flexiblePadding = '1rem';
if (copyItem) {
flexiblePadding = '1.5rem';
}
if (!copyItem || (copyItem && staffRequestDepartment.has(destination))) {
return (
<Box mt="1.5rem">
<b>Or submit an automated staff request.</b> <br /> <br />
<i>
The automated staff request form automatically populates the company
job board ((sends to discord, but does not ping.)) without requiring
intervention from central command clerks and officers. <br />
It also works without requiring a written request to be composed.
</i>
<br />
<Box mt="1.5rem">
<Button
icon="share-square"
onClick={() => act('send_automated_staff_request')}
content="Send Automated Staff Request"
fluid
/>
</Box>
</Box>
);
} else {
return null;
}
};

View File

@@ -5,6 +5,9 @@ import { FullscreenNotice } from './FullscreenNotice';
/**
* Displays a login screen that users can interact with
* using an ID card in their hand.
* Special elements can be invoked by defining machineType prop
* possible string arguments are defined in SpecialMachineInteraction in loginScreen.js
*
* Required data fields:
* * `scan` — The name of the currently inserted ID
* * `isAI` — Whether the user is an AI. If true, shows "Login as AI"
@@ -26,6 +29,7 @@ import { FullscreenNotice } from './FullscreenNotice';
export const LoginScreen = (_properties, context) => {
const { act, data } = useBackend(context);
const { scan, isAI, isRobot } = data;
const { machineType } = _properties;
return (
<FullscreenNotice title="Welcome">
<Box fontSize="1.5rem" bold>
@@ -51,6 +55,7 @@ export const LoginScreen = (_properties, context) => {
})
}
/>
{!!isAI && (
<Button
icon="sign-in-alt"
@@ -73,6 +78,37 @@ export const LoginScreen = (_properties, context) => {
}
/>
)}
<Box>
<SpecialMachineInteraction specialType={machineType} />
</Box>
</FullscreenNotice>
);
};
/**
* Special login screen elements that we want to appear for specific machines.
* Props: "specialType", arguemnt: string
* specialType definitions are defined in LoginScreen.js SpecialMachineInteraction
* currently supported: "Fax"
*/
export const SpecialMachineInteraction = (_properties, context) => {
const { act } = useBackend(context);
const { specialType } = _properties;
if (!specialType) {
return null;
} else if (specialType === 'Fax') {
return (
<Button
position="relative"
content="Send Automated Fax Request"
bottom="152px"
left="188px"
icon="share-square"
onClick={() => act('send_automated_staff_request')}
tooltip={
"Automated Fax Requests do not require staff to post on discord, but won't ping the related roles."
}
/>
);
}
};

File diff suppressed because one or more lines are too long

View File

@@ -3727,7 +3727,6 @@
#include "code\modules\paperwork\carbonpaper.dm"
#include "code\modules\paperwork\clipboard.dm"
#include "code\modules\paperwork\faxmachine.dm"
#include "code\modules\paperwork\faxmachine_vr.dm"
#include "code\modules\paperwork\filingcabinet.dm"
#include "code\modules\paperwork\folders.dm"
#include "code\modules\paperwork\handlabeler.dm"