mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
Adds Internet admin midi (#30457)
* Adds Internet admin midi * Moves global regex into proc (shell_url_scrub) * Moves shelleo to world Adds quotes around the command in shelleo, to encapsulate it * Admins can stop playing web sounds * Revised internet midi extension order Moved m4a to least preferred, as it can potentially carry non-aac or non-mpeg-3 audio * Kills \n in web sound * play_web_sound availability based on config * play web sound and shelleo tweaks istypeless for in play web sound & readability parenthesis around binary AND add return index defines for shelleo proc * Security fix for play web sound
This commit is contained in:
@@ -441,3 +441,8 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
|
||||
#define MOUSE_OPACITY_TRANSPARENT 0
|
||||
#define MOUSE_OPACITY_ICON 1
|
||||
#define MOUSE_OPACITY_OPAQUE 2
|
||||
|
||||
//world/proc/shelleo
|
||||
#define SHELLEO_ERRORLEVEL 1
|
||||
#define SHELLEO_STDOUT 2
|
||||
#define SHELLEO_STDERR 3
|
||||
|
||||
57
code/__HELPERS/shell.dm
Normal file
57
code/__HELPERS/shell.dm
Normal file
@@ -0,0 +1,57 @@
|
||||
//Runs the command in the system's shell, returns a list of (error code, stdout, stderr)
|
||||
|
||||
#define SHELLEO_NAME "data/shelleo."
|
||||
#define SHELLEO_ERR ".err"
|
||||
#define SHELLEO_OUT ".out"
|
||||
/world/proc/shelleo(command)
|
||||
var/static/list/shelleo_ids = list()
|
||||
var/stdout = ""
|
||||
var/stderr = ""
|
||||
var/errorcode = 1
|
||||
var/shelleo_id
|
||||
var/out_file = ""
|
||||
var/err_file = ""
|
||||
var/static/list/interpreters = list("[MS_WINDOWS]" = "cmd /c", "[UNIX]" = "sh -c")
|
||||
var/interpreter = interpreters["[world.system_type]"]
|
||||
if(interpreter)
|
||||
for(var/seo_id in shelleo_ids)
|
||||
if(!shelleo_ids[seo_id])
|
||||
shelleo_ids[seo_id] = TRUE
|
||||
shelleo_id = "[seo_id]"
|
||||
break
|
||||
if(!shelleo_id)
|
||||
shelleo_id = "[shelleo_ids.len + 1]"
|
||||
shelleo_ids += shelleo_id
|
||||
shelleo_ids[shelleo_id] = TRUE
|
||||
out_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_OUT]"
|
||||
err_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_ERR]"
|
||||
errorcode = shell("[interpreter] \"[command]\" > [out_file] 2> [err_file]")
|
||||
if(fexists(out_file))
|
||||
stdout = file2text(out_file)
|
||||
fdel(out_file)
|
||||
if(fexists(err_file))
|
||||
stderr = file2text(err_file)
|
||||
fdel(err_file)
|
||||
shelleo_ids[shelleo_id] = FALSE
|
||||
else
|
||||
CRASH("Operating System: [world.system_type] not supported") // If you encounter this error, you are encouraged to update this proc with support for the new operating system
|
||||
. = list(errorcode, stdout, stderr)
|
||||
#undef SHELLEO_NAME
|
||||
#undef SHELLEO_ERR
|
||||
#undef SHELLEO_OUT
|
||||
|
||||
/proc/shell_url_scrub(url)
|
||||
var/static/regex/bad_chars_regex = regex("\[^#%&./:=?\\w]*", "g")
|
||||
var/scrubbed_url = ""
|
||||
var/bad_match = ""
|
||||
var/last_good = 1
|
||||
var/bad_chars = 1
|
||||
do
|
||||
bad_chars = bad_chars_regex.Find(url)
|
||||
scrubbed_url += copytext(url, last_good, bad_chars)
|
||||
if(bad_chars)
|
||||
bad_match = url_encode(bad_chars_regex.match)
|
||||
scrubbed_url += bad_match
|
||||
last_good = bad_chars + length(bad_match)
|
||||
while(bad_chars)
|
||||
. = scrubbed_url
|
||||
@@ -15,7 +15,7 @@ GLOBAL_PROTECT(config_dir)
|
||||
return ..()
|
||||
|
||||
/datum/configuration/vv_edit_var(var_name, var_value)
|
||||
var/static/list/banned_edits = list("cross_address", "cross_allowed", "autoadmin", "autoadmin_rank")
|
||||
var/static/list/banned_edits = list("cross_address", "cross_allowed", "autoadmin", "autoadmin_rank", "invoke_youtubedl")
|
||||
if(var_name in banned_edits)
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -93,6 +93,8 @@ GLOBAL_PROTECT(config_dir)
|
||||
var/panic_server_name
|
||||
var/panic_address //Reconnect a player this linked server if this server isn't accepting new players
|
||||
|
||||
var/invoke_youtubedl
|
||||
|
||||
//IP Intel vars
|
||||
var/ipintel_email
|
||||
var/ipintel_rating_bad = 1
|
||||
@@ -475,6 +477,8 @@ GLOBAL_PROTECT(config_dir)
|
||||
if("panic_server_address")
|
||||
if(value != "byond://address:port")
|
||||
panic_address = value
|
||||
if("invoke_youtubedl")
|
||||
invoke_youtubedl = value
|
||||
if("show_irc_name")
|
||||
showircname = 1
|
||||
if("see_own_notes")
|
||||
|
||||
@@ -261,6 +261,8 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
verbs += GLOB.admin_verbs_poll
|
||||
if(rights & R_SOUNDS)
|
||||
verbs += GLOB.admin_verbs_sounds
|
||||
if(config.invoke_youtubedl)
|
||||
verbs += /client/proc/play_web_sound
|
||||
if(rights & R_SPAWN)
|
||||
verbs += GLOB.admin_verbs_spawn
|
||||
|
||||
@@ -283,6 +285,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
/client/proc/stealth,
|
||||
GLOB.admin_verbs_poll,
|
||||
GLOB.admin_verbs_sounds,
|
||||
/client/proc/play_web_sound,
|
||||
GLOB.admin_verbs_spawn,
|
||||
/*Debug verbs added by "show debug verbs"*/
|
||||
/client/proc/Cell,
|
||||
|
||||
@@ -36,7 +36,11 @@
|
||||
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.client.prefs.toggles & SOUND_MIDI)
|
||||
var/user_vol = M.client.chatOutput.adminMusicVolume
|
||||
if(user_vol)
|
||||
admin_sound.volume = vol * (user_vol / 100)
|
||||
SEND_SOUND(M, admin_sound)
|
||||
admin_sound.volume = vol
|
||||
|
||||
SSblackbox.add_details("admin_verb","Play Global Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -52,6 +56,62 @@
|
||||
playsound(get_turf(src.mob), S, 50, 0, 0)
|
||||
SSblackbox.add_details("admin_verb","Play Local Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/play_web_sound()
|
||||
set category = "Fun"
|
||||
set name = "Play Internet Sound"
|
||||
if(!check_rights(R_SOUNDS))
|
||||
return
|
||||
|
||||
if(!config.invoke_youtubedl)
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl was not configured, action unavailable</span>") //Check config.txt for the INVOKE_YOUTUBEDL value
|
||||
return
|
||||
|
||||
var/web_sound_input = input("Enter content URL (supported sites only, leave blank to stop playing)", "Play Internet Sound via youtube-dl") as text|null
|
||||
if(istext(web_sound_input))
|
||||
var/web_sound_url = ""
|
||||
var/pitch
|
||||
if(length(web_sound_input))
|
||||
|
||||
web_sound_input = trim(web_sound_input)
|
||||
var/static/regex/html_protocol_regex = regex("https?://")
|
||||
if(findtext(web_sound_input, ":") && !findtext(web_sound_input, html_protocol_regex))
|
||||
to_chat(src, "<span class='boldwarning'>Non-http(s) URIs are not allowed.</span>")
|
||||
to_chat(src, "<span class='warning'>For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website.</span>")
|
||||
return
|
||||
var/shell_scrubbed_input = shell_url_scrub(web_sound_input)
|
||||
var/list/output = world.shelleo("[config.invoke_youtubedl] --format \"bestaudio\[ext=aac]/bestaudio\[ext=mp3]/bestaudio\[ext=m4a]\" --get-url \"[shell_scrubbed_input]\"")
|
||||
var/errorlevel = output[SHELLEO_ERRORLEVEL]
|
||||
var/stdout = output[SHELLEO_STDOUT]
|
||||
var/stderr = output[SHELLEO_STDERR]
|
||||
if(!errorlevel)
|
||||
var/static/regex/content_url_regex = regex("https?://\\S+")
|
||||
if(content_url_regex.Find(stdout))
|
||||
web_sound_url = content_url_regex.match
|
||||
|
||||
if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS])
|
||||
pitch = pick(0.5, 0.7, 0.8, 0.85, 0.9, 0.95, 1.1, 1.2, 1.4, 1.6, 2.0, 2.5)
|
||||
to_chat(src, "You feel the Honkmother messing with your song...")
|
||||
|
||||
log_admin("[key_name(src)] played web sound: [web_sound_input]")
|
||||
message_admins("[key_name(src)] played web sound: [web_sound_input]")
|
||||
else
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl URL retrieval FAILED:</span>")
|
||||
to_chat(src, "<span class='warning'>[stderr]</span>")
|
||||
|
||||
else //pressed ok with blank
|
||||
log_admin("[key_name(src)] stopped web sound")
|
||||
message_admins("[key_name(src)] stopped web sound")
|
||||
web_sound_url = " "
|
||||
|
||||
if(web_sound_url)
|
||||
for(var/m in GLOB.player_list)
|
||||
var/mob/M = m
|
||||
var/client/C = M.client
|
||||
if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
C.chatOutput.sendMusic(web_sound_url, pitch)
|
||||
|
||||
SSblackbox.add_details("admin_verb","Play Internet Sound")
|
||||
|
||||
/client/proc/set_round_end_sound(S as sound)
|
||||
set category = "Fun"
|
||||
set name = "Set Round End Sound"
|
||||
|
||||
@@ -144,6 +144,9 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglemidis)()
|
||||
else
|
||||
to_chat(usr, "You will no longer hear sounds uploaded by admins")
|
||||
usr.stop_sound_channel(CHANNEL_ADMIN)
|
||||
var/client/C = usr.client
|
||||
if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
C.chatOutput.sendMusic(" ")
|
||||
SSblackbox.add_details("preferences_verb","Toggle Hearing Midis|[usr.client.prefs.toggles & SOUND_MIDI]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
/datum/verbs/menu/Settings/Sound/togglemidis/Get_checked(client/C)
|
||||
return C.prefs.toggles & SOUND_MIDI
|
||||
|
||||
@@ -13,6 +13,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
|
||||
var/cookieSent = FALSE // Has the client sent a cookie for analysis
|
||||
var/broken = FALSE
|
||||
var/list/connectionHistory //Contains the connection history passed from chat cookie
|
||||
var/adminMusicVolume = 100 //This is for the Play Global Sound verb
|
||||
|
||||
/datum/chatOutput/New(client/C)
|
||||
owner = C
|
||||
@@ -79,6 +80,9 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
|
||||
if("analyzeClientData")
|
||||
data = analyzeClientData(arglist(params))
|
||||
|
||||
if("setMusicVolume")
|
||||
data = setMusicVolume(arglist(params))
|
||||
|
||||
if(data)
|
||||
ehjax_send(data = data)
|
||||
|
||||
@@ -120,6 +124,16 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
|
||||
data = json_encode(data)
|
||||
C << output("[data]", "[window]:ehjaxCallback")
|
||||
|
||||
/datum/chatOutput/proc/sendMusic(music, pitch)
|
||||
var/list/music_data = list("adminMusic" = url_encode(url_encode(music)))
|
||||
if(pitch)
|
||||
music_data["musicRate"] = pitch
|
||||
ehjax_send(data = music_data)
|
||||
|
||||
/datum/chatOutput/proc/setMusicVolume(volume = "")
|
||||
if(volume)
|
||||
adminMusicVolume = Clamp(text2num(volume), 0, 100)
|
||||
|
||||
//Sends client connection details to the chat to handle and save
|
||||
/datum/chatOutput/proc/sendClientData()
|
||||
//Get dem deets
|
||||
|
||||
@@ -101,7 +101,7 @@ a.popt {text-decoration: none;}
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
#options a {
|
||||
#options .optionsCell {
|
||||
background: #ddd;
|
||||
height: 30px;
|
||||
padding: 5px 0;
|
||||
@@ -111,7 +111,7 @@ a.popt {text-decoration: none;}
|
||||
line-height: 28px;
|
||||
border-top: 1px solid #b4b4b4;
|
||||
}
|
||||
#options a:hover {background: #ccc;}
|
||||
#options .optionsCell:hover {background: #ccc;}
|
||||
#options .toggle {
|
||||
width: 40px;
|
||||
background: #ccc;
|
||||
@@ -121,7 +121,7 @@ a.popt {text-decoration: none;}
|
||||
}
|
||||
#options .sub {clear: both; display: none; width: 160px;}
|
||||
#options .sub.scroll {overflow-y: scroll;}
|
||||
#options .sub a {padding: 3px 0 3px 8px; line-height: 30px; font-size: 0.9em; clear: both;}
|
||||
#options .sub.optionsCell {padding: 3px 0 3px 8px; line-height: 30px; font-size: 0.9em; clear: both;}
|
||||
#options .sub span {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
@@ -136,6 +136,13 @@ a.popt {text-decoration: none;}
|
||||
line-height: 30px;
|
||||
float: right;
|
||||
}
|
||||
#options .sub input {
|
||||
position: absolute;
|
||||
padding: 7px 5px;
|
||||
width: 121px;
|
||||
line-height: 30px;
|
||||
float: left;
|
||||
}
|
||||
#options .decreaseFont {border-top: 0;}
|
||||
|
||||
/* POPUPS */
|
||||
|
||||
@@ -28,17 +28,19 @@
|
||||
<span class="ms" id="pingMs">--ms</span>
|
||||
</div>
|
||||
<div id="options">
|
||||
<a href="#" class="toggle" id="toggleOptions" title="Options"><i class="icon-cog"></i></a>
|
||||
<a href="#" class="optionsCell toggle" id="toggleOptions" title="Options"><i class="icon-cog"></i></a>
|
||||
<div class="sub" id="subOptions">
|
||||
<a href="#" class="decreaseFont" id="decreaseFont"><span>Decrease font size</span> <i class="icon-font">-</i></a>
|
||||
<a href="#" class="increaseFont" id="increaseFont"><span>Increase font size</span> <i class="icon-font">+</i></a>
|
||||
<a href="#" class="togglePing" id="togglePing"><span>Toggle ping display</span> <i class="icon-circle"></i></a>
|
||||
<a href="#" class="highlightTerm" id="highlightTerm"><span>Highlight string</span> <i class="icon-tag"></i></a>
|
||||
<a href="#" class="saveLog" id="saveLog"><span>Save chat log</span> <i class="icon-save"></i></a>
|
||||
<a href="#" class="clearMessages" id="clearMessages"><span>Clear all messages</span> <i class="icon-eraser"></i></a>
|
||||
<a href="#" class="optionsCell decreaseFont" id="decreaseFont"><span>Decrease font size</span> <i class="icon-font">-</i></a>
|
||||
<a href="#" class="optionsCell increaseFont" id="increaseFont"><span>Increase font size</span> <i class="icon-font">+</i></a>
|
||||
<a href="#" class="optionsCell togglePing" id="togglePing"><span>Toggle ping display</span> <i class="icon-circle"></i></a>
|
||||
<a href="#" class="optionsCell highlightTerm" id="highlightTerm"><span>Highlight string</span> <i class="icon-tag"></i></a>
|
||||
<a href="#" class="optionsCell saveLog" id="saveLog"><span>Save chat log</span> <i class="icon-save"></i></a>
|
||||
<a href="#" class="optionsCell clearMessages" id="clearMessages"><span>Clear all messages</span> <i class="icon-eraser"></i></a>
|
||||
<span class="optionsCell" id="musicVolumeSpan"><input type="range" class="hidden" id="musicVolume"><span id="musicVolumeText">Admin music volume</span><i class="icon-music"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<audio class="hidden" id="adminMusic" autoplay></audio>
|
||||
<script type="text/javascript" src="browserOutput.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -61,8 +61,17 @@ var opts = {
|
||||
'clientDataLimit': 5,
|
||||
'clientData': [],
|
||||
|
||||
//Admin music volume update
|
||||
'volumeUpdateDelay': 5000, //Time from when the volume updates to data being sent to the server
|
||||
'volumeUpdating': false, //True if volume update function set to fire
|
||||
'updatedVolume': 0, //The volume level that is sent to the server
|
||||
|
||||
};
|
||||
|
||||
function clamp(val, min, max) {
|
||||
return Math.max(min, Math.min(val, max))
|
||||
}
|
||||
|
||||
function outerHTML(el) {
|
||||
var wrap = document.createElement('div');
|
||||
wrap.appendChild(el.cloneNode(true));
|
||||
@@ -95,6 +104,15 @@ function linkify(text) {
|
||||
});
|
||||
}
|
||||
|
||||
function byondDecode(message) {
|
||||
// Basically we url_encode twice server side so we can manually read the encoded version and actually do UTF-8.
|
||||
// The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b.
|
||||
// Marvelous.
|
||||
message = message.replace(/\+/g, "%20");
|
||||
message = decoder(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
//Actually turns the highlight term match into appropriate html
|
||||
function addHighlightMarkup(match) {
|
||||
var extra = '';
|
||||
@@ -176,11 +194,7 @@ function output(message, flag) {
|
||||
if (flag !== 'internal')
|
||||
opts.lastPang = Date.now();
|
||||
|
||||
// Basically we url_encode twice server side so we can manually read the encoded version and actually do UTF-8.
|
||||
// The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b.
|
||||
// Marvelous.
|
||||
message = message.replace(/\+/g, "%20")
|
||||
message = decoder(message)
|
||||
message = byondDecode(message)
|
||||
|
||||
//The behemoth of filter-code (for Admin message filters)
|
||||
//Note: This is proooobably hella inefficient
|
||||
@@ -423,6 +437,21 @@ function ehjaxCallback(data) {
|
||||
var firebugEl = document.createElement('script');
|
||||
firebugEl.src = 'https://getfirebug.com/firebug-lite-debug.js';
|
||||
document.body.appendChild(firebugEl);
|
||||
} else if (data.adminMusic) {
|
||||
if (typeof data.adminMusic === 'string') {
|
||||
var adminMusic = byondDecode(data.adminMusic);
|
||||
adminMusic = adminMusic.match(/https?:\/\/\S+/) || '';
|
||||
if (data.musicRate) {
|
||||
var newRate = Number(data.musicRate);
|
||||
if(newRate) {
|
||||
$('#adminMusic').prop('defaultPlaybackRate', newRate);
|
||||
}
|
||||
} else {
|
||||
$('#adminMusic').prop('defaultPlaybackRate', 1.0);
|
||||
}
|
||||
$('#adminMusic').prop('src', adminMusic);
|
||||
$('#adminMusic').trigger("play");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,6 +475,13 @@ function toggleWasd(state) {
|
||||
opts.wasd = (state == 'on' ? true : false);
|
||||
}
|
||||
|
||||
function sendVolumeUpdate() {
|
||||
opts.volumeUpdating = false;
|
||||
if(opts.updatedVolume) {
|
||||
runByond('?_src_=chat&proc=setMusicVolume¶m[volume]='+opts.updatedVolume);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************
|
||||
*
|
||||
* DOM READY
|
||||
@@ -486,6 +522,7 @@ $(function() {
|
||||
'spingDisabled': getCookie('pingdisabled'),
|
||||
'shighlightTerms': getCookie('highlightterms'),
|
||||
'shighlightColor': getCookie('highlightcolor'),
|
||||
'smusicVolume': getCookie('musicVolume'),
|
||||
};
|
||||
|
||||
if (savedConfig.sfontSize) {
|
||||
@@ -517,6 +554,14 @@ $(function() {
|
||||
opts.highlightColor = savedConfig.shighlightColor;
|
||||
internalOutput('<span class="internal boldnshit">Loaded highlight color of: '+savedConfig.shighlightColor+'</span>', 'internal');
|
||||
}
|
||||
if (savedConfig.smusicVolume) {
|
||||
var newVolume = clamp(savedConfig.smusicVolume, 0, 100);
|
||||
$('#adminMusic').prop('volume', newVolume / 100);
|
||||
$('#musicVolume').val(newVolume);
|
||||
opts.updatedVolume = newVolume;
|
||||
sendVolumeUpdate();
|
||||
internalOutput('<span class="internal boldnshit">Loaded music volume of: '+savedConfig.smusicVolume+'</span>', 'internal');
|
||||
}
|
||||
|
||||
(function() {
|
||||
var dataCookie = getCookie('connData');
|
||||
@@ -835,6 +880,26 @@ $(function() {
|
||||
opts.messageCount = 0;
|
||||
});
|
||||
|
||||
$('#musicVolumeSpan').hover(function() {
|
||||
$('#musicVolumeText').addClass('hidden');
|
||||
$('#musicVolume').removeClass('hidden');
|
||||
}, function() {
|
||||
$('#musicVolume').addClass('hidden');
|
||||
$('#musicVolumeText').removeClass('hidden');
|
||||
});
|
||||
|
||||
$('#musicVolume').change(function() {
|
||||
var newVolume = $('#musicVolume').val();
|
||||
newVolume = clamp(newVolume, 0, 100);
|
||||
$('#adminMusic').prop('volume', newVolume / 100);
|
||||
setCookie('musicVolume', newVolume, 365);
|
||||
opts.updatedVolume = newVolume;
|
||||
if(!opts.volumeUpdating) {
|
||||
setTimeout(sendVolumeUpdate, opts.volumeUpdateDelay);
|
||||
opts.volumeUpdating = true;
|
||||
}
|
||||
});
|
||||
|
||||
$('img.icon').error(iconError);
|
||||
|
||||
|
||||
|
||||
@@ -195,6 +195,14 @@ CHECK_RANDOMIZER
|
||||
## Ban appeals URL - usually for a forum or wherever people should go to contact your admins.
|
||||
# BANAPPEALS http://justanotherday.example.com
|
||||
|
||||
## System command that invokes youtube-dl, used by Play Internet Sound.
|
||||
## You can install youtube-dl with
|
||||
## "pip install youtube-dl" if you have pip installed
|
||||
## from https://github.com/rg3/youtube-dl/releases
|
||||
## or your package manager
|
||||
## The default value assumes youtube-dl is in your system PATH
|
||||
# INVOKE_YOUTUBEDL youtube-dl
|
||||
|
||||
## In-game features
|
||||
##Toggle for having jobs load up from the .txt
|
||||
# LOAD_JOBS_FROM_TXT
|
||||
|
||||
@@ -93,6 +93,7 @@
|
||||
#include "code\__HELPERS\pronouns.dm"
|
||||
#include "code\__HELPERS\radio.dm"
|
||||
#include "code\__HELPERS\sanitize_values.dm"
|
||||
#include "code\__HELPERS\shell.dm"
|
||||
#include "code\__HELPERS\text.dm"
|
||||
#include "code\__HELPERS\time.dm"
|
||||
#include "code\__HELPERS\type2type.dm"
|
||||
|
||||
Reference in New Issue
Block a user