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 = "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()
density = 0
use_power = USE_POWER_IDLE
idle_power_usage = 30
active_power_usage = 200
circuit = /obj/item/circuitboard/fax
var/obj/item/card/id/scan = null
var/authenticated = null
var/rank = null
var/sendcooldown = 0 // to avoid spamming fax messages
var/department = "Unknown" // our department
var/destination = null // the department we're sending to
var/talon = 0 // So that the talon can access their own crew roles for the request
/obj/machinery/photocopier/faxmachine/Initialize() //CHOMPedit, so new fax machines can update its list mid-round
allfaxes += src
if(!destination) destination = "[using_map.boss_name]"
if( !(("[department]" in alldepartments) || ("[department]" in admin_departments)) )
alldepartments |= department
..()
/obj/machinery/photocopier/faxmachine/attack_hand(mob/user as mob) // CH edit begins here; this allows borgs to use fax machines, meant for the Unity and Clerical modules.
user.set_machine(src)
if(issilicon(user))
authenticated = user.name
tgui_interact(user)
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_warning("The global automated relays are still recalibrating. Try again later or relay your request in written form for processing."))
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(!src.talon)
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
else
for(var/job in SSjob.get_job_titles_in_department(dept.name))
var/datum/job/J = SSjob.get_job(job)
if(J.offmap_spawn)
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_warning("Selected job cannot be requested for \[ERRORDEPTNOTFOUND] reason. Please report this to system administrator."))
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_warning("Selected job cannot be requested for \[ERRORUNKNOWNDEPT] reason. Please report this to system administrator."))
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_notice("Your request was transmitted."))
/obj/machinery/photocopier/faxmachine/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Fax", name)
ui.open()
/obj/machinery/photocopier/faxmachine/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["scan"] = scan ? scan.name : null
data["authenticated"] = authenticated
data["rank"] = rank
data["isAI"] = isAI(user)
data["isRobot"] = isrobot(user)
data["adminDepartments"] = admin_departments
data["bossName"] = using_map.boss_name
data["copyItem"] = copyitem
data["cooldown"] = sendcooldown
data["destination"] = destination
return data
/obj/machinery/photocopier/faxmachine/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
switch(action)
if("scan")
if(scan)
scan.forceMove(loc)
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
else
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/card/id))
usr.drop_item()
I.forceMove(src)
scan = I
return TRUE
if("login")
var/login_type = text2num(params["login_type"])
if(login_type == LOGIN_TYPE_NORMAL && istype(scan))
if(check_access(scan))
authenticated = scan.registered_name
rank = scan.assignment
else if(login_type == LOGIN_TYPE_AI && isAI(usr))
authenticated = usr.name
rank = JOB_AI
else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr))
authenticated = usr.name
var/mob/living/silicon/robot/R = usr
rank = "[R.modtype] [R.braintype]"
return TRUE
if("logout")
if(scan)
scan.forceMove(loc)
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
authenticated = null
return TRUE
if("remove")
if(copyitem)
if(get_dist(usr, src) >= 2)
to_chat(usr, "\The [copyitem] is too far away for you to remove it.")
return
copyitem.forceMove(loc)
usr.put_in_hands(copyitem)
to_chat(usr, span_notice("You take \the [copyitem] out of \the [src]."))
copyitem = null
if("send_automated_staff_request")
request_roles()
if(!authenticated)
return
switch(action)
if("rename")
if(copyitem)
var/new_name = tgui_input_text(usr, "Enter new paper title", "This will show up in the preview for staff chat on discord when sending \
to central.", copyitem.name, MAX_NAME_LEN)
if(!new_name)
return
copyitem.name = new_name
if("send")
if(copyitem)
if (destination in admin_departments)
if(check_if_default_title_and_rename())
return
send_admin_fax(usr, destination)
else
sendfax(destination)
if (sendcooldown)
spawn(sendcooldown) // cooldown time
sendcooldown = 0
if("dept")
var/lastdestination = destination
destination = tgui_input_list(usr, "Which department?", "Choose a department", (alldepartments + admin_departments))
if(!destination)
destination = lastdestination
return TRUE
/obj/machinery/photocopier/faxmachine/proc/check_if_default_title_and_rename()
/*
Returns TRUE only on "Cancel" or invalid newname, else returns null/false
Extracted to its own procedure for easier logic handling with paper bundles.
*/
var/question_text = "Your fax is set to its default name. It's advisable to rename it to something self-explanatory to"
if(istype(copyitem, /obj/item/paper_bundle))
var/obj/item/paper_bundle/B = copyitem
if(B.name != initial(B.name))
var/atom/page1 = B.pages[1] //atom is enough for us to ensure it has name var. would've used ?. opertor, but linter doesnt like.
var/atom/page2 = B.pages[2]
if((istype(page1) && B.name == page1.name) || (istype(page2) && B.name == page2.name) )
question_text = "Your fax is set to use the title of its first or second page. It's advisable to rename it to something \
summarizing the entire bundle succintly to"
else
return FALSE
else if(copyitem.name != initial(copyitem.name))
return FALSE
var/choice = tgui_alert(usr, "[question_text] improve response time from staff when sending to discord. \
Renaming it changes its preview in staff chat.", \
"Default name detected", list("Change Title","Continue", "Cancel"))
if(!choice || choice == "Cancel")
return TRUE
else if(choice == "Change Title")
var/new_name = tgui_input_text(usr, "Enter new fax title", "This will show up in the preview for staff chat on discord when sending \
to central.", copyitem.name, MAX_NAME_LEN)
if(!new_name)
return TRUE
copyitem.name = new_name
/obj/machinery/photocopier/faxmachine/attackby(obj/item/O as obj, mob/user as mob)
if(istype(O, /obj/item/card/id) && !scan)
user.drop_from_inventory(O)
O.forceMove(src)
scan = O
else if(O.has_tool_quality(TOOL_MULTITOOL) && panel_open)
var/input = sanitize(tgui_input_text(usr, "What Department ID would you like to give this fax machine?", "Multitool-Fax Machine Interface", department))
if(!input)
to_chat(usr, "No input found. Please hang up and try your call again.")
return
department = input
if( !(("[department]" in alldepartments) || ("[department]" in admin_departments)) && !(department == "Unknown"))
alldepartments |= department
else if(istype(O, /obj/item/toner))
if(toner <= 10) //allow replacing when low toner is affecting the print darkness
user.drop_item()
to_chat(user, span_notice("You insert the toner cartridge into \the [src]."))
playsound(loc, 'sound/machines/click.ogg', 50, 1)
var/obj/item/toner/T = O
toner += T.toner_amount
qdel(O)
else
to_chat(user, span_notice("This cartridge is not yet ready for replacement! Use up the rest of the toner."))
playsound(loc, 'sound/machines/buzz-two.ogg', 75, 1)
return
return ..()
/obj/machinery/photocopier/faxmachine/proc/sendfax(var/destination)
if(stat & (BROKEN|NOPOWER))
return
use_power(200)
var/success = 0
for(var/obj/machinery/photocopier/faxmachine/F in allfaxes)
if( F.department == destination )
success = F.receivefax(copyitem)
if (success)
visible_message("[src] beeps, \"Message transmitted successfully.\"")
//sendcooldown = 600
else
visible_message("[src] beeps, \"Error transmitting message.\"")
/obj/machinery/photocopier/faxmachine/proc/receivefax(var/obj/item/incoming)
if(stat & (BROKEN|NOPOWER))
return 0
if(department == "Unknown")
return 0 //You can't send faxes to "Unknown"
flick("faxreceive", src)
playsound(src, "sound/machines/printer.ogg", 50, 1)
// give the sprite some time to flick
sleep(20)
if (istype(incoming, /obj/item/paper))
copy(incoming)
else if (istype(incoming, /obj/item/photo))
photocopy(incoming)
else if (istype(incoming, /obj/item/paper_bundle))
bundlecopy(incoming)
else
return 0
use_power(active_power_usage)
return 1
/obj/machinery/photocopier/faxmachine/proc/send_admin_fax(var/mob/sender, var/destination)
if(stat & (BROKEN|NOPOWER))
return
use_power(200)
//received copies should not use toner since it's being used by admins only.
var/obj/item/rcvdcopy
if (istype(copyitem, /obj/item/paper))
rcvdcopy = copy(copyitem, 0)
else if (istype(copyitem, /obj/item/photo))
rcvdcopy = photocopy(copyitem, 0)
else if (istype(copyitem, /obj/item/paper_bundle))
rcvdcopy = bundlecopy(copyitem, 0)
else
visible_message("[src] beeps, \"Error transmitting message.\"")
return
rcvdcopy.loc = null //hopefully this shouldn't cause trouble
adminfaxes += rcvdcopy
//message badmins that a fax has arrived
// Sadly, we can't use a switch statement here due to not using a constant value for the current map's centcom name.
if(destination == using_map.boss_name)
message_admins(sender, "[uppertext(using_map.boss_short)] FAX", rcvdcopy, "CentComFaxReply", "#006100")
else if(destination == "Solar Central Government") // Vorestation Edit //CHOMPedit
message_admins(sender, "Solar Central Government FAX", rcvdcopy, "CentComFaxReply", "#1F66A0") // Vorestation Edit //CHOMPedit
else if(destination == "Supply")
message_admins(sender, "[uppertext(using_map.boss_short)] SUPPLY FAX", rcvdcopy, "CentComFaxReply", "#5F4519")
else
message_admins(sender, "[uppertext(destination)] FAX", rcvdcopy, "UNKNOWN")
sendcooldown = 1800
sleep(50)
visible_message("[src] beeps, \"Message transmitted successfully.\"")
// Turns objects into just text.
/obj/machinery/photocopier/faxmachine/proc/make_summary(obj/item/sent)
if(istype(sent, /obj/item/paper))
var/obj/item/paper/P = sent
return P.info
if(istype(sent, /obj/item/paper_bundle))
. = ""
var/obj/item/paper_bundle/B = sent
for(var/i in 1 to B.pages.len)
var/obj/item/paper/P = B.pages[i]
if(istype(P)) // Photos can show up here too.
if(.) // Space out different pages.
. += "
"
. += "PAGE [i] - [P.name]
"
. += P.info
/obj/machinery/photocopier/faxmachine/proc/message_admins(var/mob/sender, var/faxname, var/obj/item/sent, var/reply_type, font_colour="#006100")
var/msg = "[faxname]: [get_options_bar(sender, 2,1,1)]"
msg += "(REPLY)"
msg = span_bold(msg) + ": "
msg += "Receiving '[sent.name]' via secure connection ... view message"
msg = span_notice(msg)
for(var/client/C in GLOB.admins)
if(check_rights((R_ADMIN|R_MOD|R_EVENT),0,C))
to_chat(C,msg)
C << 'sound/machines/printer.ogg'
sender.client << 'sound/machines/printer.ogg' //CHOMPEdit - The pain must be felt
var/faxid = export_fax(sent)
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)
summary = paper_html_to_plaintext(summary)
log_game("Fax to [lowertext(faxname)] was sent by [key_name(sender)].")
log_game(summary)
var/webhook_length_limit = 1900 // The actual limit is a little higher.
if(length(summary) > webhook_length_limit)
summary = copytext(summary, 1, webhook_length_limit + 1)
summary += "\n\[Truncated\]"
SSwebhooks.send(
WEBHOOK_FAX_SENT,
list(
"name" = "[faxname] '[sent.name]' sent from [key_name(sender)]",
"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/paper))
var/obj/item/paper/P = fax
var/text = "
" \
+ "[H.scribble ? "