Warning: session_start(): open(/var/lib/php/sessions/sess_ehqr6ub2gflegg2tcvc1577ia4, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
// Gazelle's "left brain hemisphere" (where all the bots run)
set flag DynModule. // for transpilation
//set flag dm_evalJava_withModule_debug.
//set flag veryQuickJava3_debug.
// store bot data per conversation
concept Conversation {
S cookie;
S dataStruct;
}
asclass DynGazelleMultiBot > DynGazelleBot {
switchable int maxEvalResultLength = oneMegabyte_int();
switchable double evalTimeout = 30.0;
switchable long botProcessed; // timestamp of last post processed by bots
switchable int maxBotAnswerLength = 100000;
switchable bool runAutoBots = true;
switchable bool doPython = true;
switchable int maxRuntimeMinutes = 60;
transient new L bots;
Map allPosts = syncTreeMap();
transient Map loadedCodePosts = syncTreeMap();
transient bool considerMasterPostsSafe;
sclass CouldntLoadCode {}
transient Lock codeLoadLock = lock();
volatile long requestsServed;
transient double systemLoad;
transient long processSize;
transient Lock statsLock = lock();
transient ReliableSingleThread rstPersistRarely = dm_rstWithPreDelay(this, 60.0, r change);
set flag NoNanoHTTPD.
!include #1029545 // API for Eleu
class Bot {
S name;
*() {}
*(S *name) {}
*(S *name, IVF1 *handlePost) {}
swappable void handlePost(GazellePost post) {}
GazelleBotCred cred() { ret GazelleBotCred(_user, _botToken, name).server(gazelleServer); }
void postReply(GazellePost post, S text, S type, S title default null) {
if (empty(text) && empty(title))
text = "";
gazelle_createPost(cred(), text, type, refs := post.id, +title);
}
// post: post we are replying to
void createPostFromBotResult(GazellePost post, IF0 f, IF1 modifyBotInfo default null) {
S text, type = "Code Result", title = "", botInfo = "";
bool overrideLastPost = false;
try {
O result = f!;
if (result == null) ret;
print("Result type: " + className(result));
if (result cast CreatePost) {
print("Is CreatePost");
O[] params = toObjectArray(result.params);
text = (S) optPar text(params);
type = (S) optPar type(params, type);
title = (S) optPar title(params);
botInfo = (S) optPar botInfo(params);
overrideLastPost = boolPar overrideLastPost(params);
} else
text = str_shortenSyntheticAndStandardToString(result);
} catch print e {
text = getStackTrace(e);
}
if (empty(text) && empty(title))
text = "";
botInfo = callFOrKeep(modifyBotInfo, botInfo);
GazelleBotCred cred = cred();
if (nempty(botInfo))
cred.botInfo = botInfo;
if (overrideLastPost) {
// find last post
S _botInfo = botInfo;
GazellePost lastPost = firstThat(repliesTo(post), p -> eqic(p.botInfo, _botInfo));
print("Last post with bot info " + botInfo + ": " + lastPost);
if (lastPost != null) {
gazelle_editPost(cred, lastPost.id, text, type, +title, refs := post.id);
ret;
}
}
gazelle_createPost(cred, text, type, +title, refs := post.id);
}
}
srecord CreatePost(L params) {}
start {
dm_require("#1017856/SystemLoad");
dm_vmBus_onMessage systemLoad(voidfunc(double load) {
if (setField_noPersist(systemLoad := load))
distributeDivChanges("serverLoadLeftHemi");
});
dm_vmBus_onMessage processSize(voidfunc(long processSize) {
if (setField_noPersist(+processSize))
distributeDivChanges("memLeftHemi");
});
// test if we can share classes with dynamically loaded code
hotwire_addSharedClasses(Pair);
dm_useLocalMechListCopies();
dbIndexing(Conversation, "cookie");
grabLoop.handlePosts = posts -> {
dm_mediumRefreshTranspiler();
grabLoop.handlePosts_base(posts);
for (GazellePost post : posts)
setField(botProcessed := max(botProcessed, post.modified));
};
// legacy conversion to sort allPosts
setField_noCheck(allPosts := asSyncTreeMap(allPosts));
bots.add(new Bot("Math Bot", post -> {
if (post.creating) ret;
gazelle_mathBot1_handlePost_2(_user, _botToken, post);
}));
bots.add(new Bot("Code Safety Checker") {
void handlePost(GazellePost post) {
if (post.creating) ret;
if (post.isJavaXCode())
gazelle_createPost(cred(), codeSafetyCheckResult(post.text), "Code Safety", refs := post.id);
}
});
bots.add(new Bot("Python Code Safety Checker") {
void handlePost(GazellePost post) {
if (post.creating) ret;
if (post.isPythonCode())
gazelle_createPost(cred(), pythonCodeSafetyCheckResult(post.text), "Python Code Safety", refs := post.id);
}
});
bots.add(new Bot("Safe Code Runner") {
void handlePost(GazellePost post) {
if (post.creating) ret;
if (post.isJavaXCode()) {
int runtime = min(maxRuntimeMinutes, parseFirstInt(jextractIC("runtime minutes", post.type)));
S code = post.text;
if (isCodePostSafe(post)) {
S _code = prepareCode(code, post);
//S out = shorten(maxEvalResultLength, runCode(code));
createPostFromBotResult(post, () -> evalCode(runtime*60.0, _code, post));
}
}
}
});
bots.add(new Bot("Python Safe Code Runner") {
void handlePost(GazellePost post) {
if (post.creating) ret;
if (!doPython) ret;
if (post.isPythonCode()) {
S code = post.text;
if (isPythonCodeSafe(code)) {
IF0 f = () -> {
PythonInterpreter interpreter = jython();
PyCode compiled = interpreter.compile(code);
TraceFunction traceFunction = new {
@Override
public TraceFunction traceCall(PyFrame frame) {
ret null with ping();
}
@Override
public TraceFunction traceReturn(PyFrame frame, PyObject ret) {
ret null with ping();
}
@Override
public TraceFunction traceLine(PyFrame frame, int line) {
ret null with ping();
}
@Override
public TraceFunction traceException(PyFrame frame, PyException exc) {
ret null with ping();
}
};
Py.getThreadState().tracefunc = traceFunction;
ret interpreter.eval(compiled);
};
createPostFromBotResult(post, () -> evalWithTimeoutOrFail(evalTimeout, f));
}
}
}
});
bots.add(new Bot("Run Code On All Posts") {
void handlePost(GazellePost post) {
if (post.creating) ret;
if (eqic(post.type, "Instruction") && match("Please run this code on all posts", post.text)) {
long ref = gazelle_firstPostRef(post.id);
if (ref == 0) ret;
S code = gazelle_text(ref);
if (!isCodeSafe(code)) ret;
S code2 = "ret func(S post) { " + code + " };";
O function = evalCode(code2);
new LS lines;
S out = shorten(maxEvalResultLength, runCode(code));
for (GazellePost post2 : cloneValues(allPosts)) {
lines.add("Post " + post2.id + " (" + quote(shorten(20, post2.text)) + "): " + shorten(80, runFunc(() -> callF(function, post2.text))));
}
gazelle_createPost(cred(), lines(lines), "Code Result", refs := post.id);
}
}
});
bots.add(new Bot("Mark identifiers safe") {
void handlePost(GazellePost post) {
if (post.creating) ret;
if (/*eqic(post.type, "Instruction")
&&*/ post.creator.isMaster
&& match("Mark safe", post.text)) {
S text = getPost(first(post.postRefs)).text;
LS ids = tok_identifiersInOrder(regexpFirstGroupIC("Unknown identifiers: (.+)", text));
print("Marking safe: " + ids);
postReply(post, markSafe(ids), "Marked safe");
}
}
});
bots.add(new Bot("Post Deleter") {
void handlePost(GazellePost post) {
if (post.creating) ret;
if (/*eqic(post.type, "Instruction")
&&*/ post.creator.isMaster
&& match("Delete posts", post.text)) {
long ref = gazelle_firstPostRef(post.id);
if (ref == 0) ret;
S text = gazelle_text(ref);
L postIDs = allToLong(regexpAllFirstGroups(gazelle_deletePostRegexp(), text));
print("Deleting posts: " + postIDs);
if (nempty(postIDs)) {
Map result = gazelle_deletePosts(cred(), postIDs);
postReply(post, str(result), "Deletion result");
} else
postReply(post, "No mentioned posts found", "Deletion result");
}
}
});
bots.add(new Bot("Detector Runner") {
void handlePost(GazellePost post) {
if (post.creating) ret;
ret unless eqic(post.type, "Instruction")
&& match("Please run detector", post.text);
try {
long detectorID = post.refWithTagOrFail("detector");
long posExamplesID = post.refWithTagOrFail("positive examples");
long negExamplesID = post.refWithTagOrFail("negative examples");
S code = getPost(detectorID).text;
LS posExamples = tlft(getPost(posExamplesID).text);
LS negExamples = tlft(getPost(negExamplesID).text);
LPair examples = trueFalseBPairs(posExamples, negExamples);
if (!isCodeSafe(code)) fail("Detector code not safe");
IF1 detector = proxy IF1(evalCode(code));
new LS good;
new LS bad;
new Scorer scorer;
long time = sysNow();
evalWithTimeoutOrFail(evalTimeout, r {
for (Pair example : examples) {
S result = runFunc(() -> detector.get(example.a));
bool ok = eq(result, str(example.b));
scorer.add(ok);
S line = (ok ? "OK" : example.b ? "False negative" : "False positive")
+ " (" + shorten(10, result) + "): " + example.a;
(ok ? good : bad).add(line);
}
});
time = sysNow()-time;
S text = "Detector code:\n\n" + indentx(code) + "\n\n"
+ n2(good, "correct answer") + ", " + n2(bad, "bad answer") + ". Runtime: " + n2(time) + " ms\n\n"
+ or2(trim(lines(concatLists(bad, ll(""), good))), "No errors");
S title = "Score for detector " + detectorID + ": " + scorer;
gazelle_createPost(cred(), text, "Detector Score", +title,
refs := joinWithSpace(ll(post.id, detectorID, posExamplesID, negExamplesID)),
refTags := linesLL_rtrim("", "detector", "positive examples", "negative examples"));
} catch e {
postReply(post, getStackTrace(e), "Error");
}
}
});
bots.add(new Bot("Auto Bot Runner") {
void handlePost(GazellePost post) {
if (!runAutoBots) ret;
if (post.creating) ret;
print("Auto Bot Runner: " + post.id);
for (GazellePost botPost : values(allPosts)) pcall {
continue unless eqicOneOf(botPost.type, "JavaX Code (Bot run on every post)", "JavaX Code (Live Auto Bot)")
&& botPost.creator.isMaster;
print("Processing auto-bot " + botPost.id + " on " + post.id);
O code = codeForPost(botPost);
if (shortNameIs CouldntLoadCode(code))
continue with print("Couldn't load code for auto-bot " + botPost.id);
print("Code type: " + className(code));
O botInstance = evalWithTimeoutOrFail(evalTimeout, () -> callFOrNewInstance(code));
print("Bot instance type: " + className(botInstance));
setOpt(botInstance, postID := post.id);
setOpt(botInstance, post := post.text);
setOpt(botInstance, postType := post.type);
// prevent endless loop of auto-bots replying to themselves or each other
// (unless they explicitly request this)
if (post.isAutoBotMade()) {
// not a bot by master user? don't allow override
if (!botPost.isMasterMade()) continue;
// check for override request
if (!isTrue(evalWithTimeoutOrFail(evalTimeout,
() -> call(botInstance, "runOnAutoBotPost")))) continue;
}
createPostFromBotResult(post, () -> evalWithTimeoutOrFail(evalTimeout,
() -> call(botInstance, "calc")),
botInfo -> joinNemptiesWithColon("Auto-Bot " + botPost.id, botInfo));
}
}
});
dm_doEvery(60.0, 3600.0, r removeDeletedPosts);
}
void handlePost(GazellePost post) {
allPosts.put(post.id, post);
change();
loadedCodePosts.remove(post.id);
if (post.modified > botProcessed) {
print("modified post: " + post.id + " - " + post.modified + "/" + botProcessed);
for (Bot bot : bots) pcall {
bot.handlePost(post);
}
}
}
// if post != null, store transpilation
O evalCode(S code, GazellePost post default null) {
ret evalCode(evalTimeout, code, post);
}
O evalCode(double timeout, S code, GazellePost post default null) {
//printWithIndent("CODE> ", code);
veryQuickJava_transpiled.set(post != null ? "" : null); // request transpilation
try {
// TODO: don't makeDependent
ret dm_javaEvalWithTimeout(timeout, code);
} finally {
if (post != null) {
S java = veryQuickJava_transpiled!;
print("Transpilation for " + post.id + ": " + shorten(java));
saveTextFile(transpilationFile(post.id), nullOnEmpty(java));
}
}
}
// assumes code is safety-checked
S runCode(S code) {
printWithIndent("CODE> ", code);
ret runFunc(() -> str_shortenSyntheticAndStandardToString(dm_javaEval(code)));
}
// run IF0 with timeout, exception to string, convert result to string
S runFunc(IF0 f) {
ret str_shortenSyntheticAndStandardToString(evalWithTimeoutOrException(evalTimeout, func {
try {
ret str_shortenSyntheticAndStandardToString(f!);
} catch e {
ret getStackTrace(e);
}
}));
}
L repliesTo(GazellePost post) {
ret filter(values(allPosts), p -> contains(p.postRefs, post.id));
}
L repliesWithTag(GazellePost post, S tag) {
Pair pair = pair(post.id, upper(tag));
ret filter(repliesTo(post), p -> contains(mapPairsB toUpper(p.taggedRefs()), pair));
}
GazellePost getPost(long id) {
ret allPosts.get(id);
}
S getPostText(long id) {
ret getPost(id).text;
}
Cl getAllPosts() {
ret values(allPosts);
}
CreatePost createPost(O... _) {
ret new CreatePost(asList(_));
}
CodeSafetyChecker codeSafetyChecker() {
new CodeSafetyChecker checker;
checker.init();
checker.markSafe("getAllPosts");
ret checker;
}
PythonCodeSafetyChecker pythonCodeSafetyChecker() {
new PythonCodeSafetyChecker checker;
checker.init();
checker.markSafe("getAllPosts");
ret checker;
}
S codeSafetyCheckResult(S code) {
CodeSafetyChecker checker = codeSafetyChecker();
checker.checkCode(code);
ret checker.verbalCheckResult();
}
S pythonCodeSafetyCheckResult(S code) {
PythonCodeSafetyChecker checker = pythonCodeSafetyChecker();
checker.checkCode(code);
ret checker.verbalCheckResult();
}
bool isCodePostSafe(GazellePost post) {
ret considerMasterPostsSafe && post.creator.isMaster
|| isCodeSafe(post.text);
}
bool isCodeSafe(S code) {
CodeSafetyChecker checker = codeSafetyChecker();
checker.checkCode(code);
ret checker.isSafe();
}
bool isPythonCodeSafe(S code) {
PythonCodeSafetyChecker checker = pythonCodeSafetyChecker();
checker.checkCode(code);
ret checker.isSafe();
}
S prepareCode(S code, GazellePost post) {
if (tok_isStaticLevelCode(code)) ret code;
code = tok_addReturn(code);
CodeInRewriting cir = new(javaTok(code));
augmentCode(cir, post);
ret cir!;
}
// define implicit vars and functions
void augmentCode(CodeInRewriting cir, GazellePost post) {
// post = text of parent post
if (cir.contains("post")) {
long ref = gazelle_firstPostRef(post.id);
if (ref != 0)
cir.add("S post = " + quote(gazelle_text(ref)) + ";");
}
// post = text of grandparent post
if (cir.contains("post2")) {
long ref = gazelle_firstPostRef(gazelle_firstPostRef(post.id));
if (ref != 0)
cir.add("S post2 = " + quote(gazelle_text(ref)) + ";");
}
// postType = typeof parent post
if (cir.contains("postType")) {
GazellePost post2 = getPost(gazelle_firstPostRef(post.id));
if (post2 != null)
cir.add("S postType = " + quote(post2.type) + ";");
}
if (cir.contains("getAllPosts"))
cir.add([[
embedded Cl getAllPosts() {
ret lazyMap_bitSet quickImport(asList((Cl) dm_call(dm_current_generic(), "getAllPosts")));
}
]]);
if (cir.contains("createPost"))
cir.add([[
embedded O createPost(O... _) {
ret dm_call(dm_current_generic(), "createPost", _);
}
]]);
// optimize gazelle_text
if (cir.contains("gazelle_text"))
cir.add([[
embedded S gazelle_text(long id) {
ret (S) dm_call(dm_current_generic(), "getPostText", id);
}
]]);
}
class CodeInRewriting {
LS tok;
new LS additions;
*() {}
*(LS *tok) {}
bool contains(S token) {
ret containsToken(tok, token);
}
S get() {
ret lines(additions) + join(tok);
}
void add(S code) {
addIfNempty(additions, code);
}
}
O html(IWebRequest req) {
{
lock statsLock;
requestsServed++;
rstPersistRarely.trigger();
requestServed();
}
new Matches m;
if (eqic(req.uri(), "/favicon.ico"))
ret serveFile(loadLibrary(#1400439), faviconMimeType());
if (startsWith(req.uri(), "/htmlBot/", m) && isInteger(m.rest())) {
req.noSpam();
long postID = parseLong(m.rest());
GazellePost post = getPost(postID);
if (post == null) ret serve404("Post " + postID + " not found");
O code = codeForPost(post);
IF0 calc;
// Case 1: Static page (code just returns a string)
if (code instanceof S)
calc = () -> code;
// Case 2: Code is an argumentless function
else if (implementsInterfaceShortNamed IF0(code))
calc = toIF0(code);
else {
// Case 3: Code is IF1
// Sadly, for a lambda like (IF1) req -> ..., we can't find the
// IWebRequest type by reflection. So we find the IWebRequest interface by name.
Class reqType = getClassInRealm("main$IWebRequest", code);
//print(+reqType);
if (reqType == null) fail("IWebRequest not found in bot");
O wrappedReq = proxy(reqType, req);
calc = () -> callF(code, wrappedReq);
}
O result = evalWithTimeoutOrFail(evalTimeout, () -> calc!);
ret hcomment("Made by HTML bot " + postID) + "\n" + str(result);
}
if (startsWith(req.uri(), "/chatBotReply/", m) && isInteger(m.rest())) {
req.noSpam();
long postID = parseLong(m.rest());
GazellePost post = getPost(postID);
if (post == null) ret serve404("Post " + postID + " not found");
O code = codeForPost(post);
if (shortNameIs CouldntLoadCode(code)) ret withHeader(serve500("Couldn't load code for post"));
S q = req.params().get("q"); // user input
bool initial = eq(req.params().get("initial"), "1");
S cookie = req.params().get("cookie");
if (!initial && empty(q)) ret withHeader(serveText(""));
S answer = "";
if (implementsInterfaceShortNamed IF1(code)) { // stateless bot
if (!initial)
answer = evalWithTimeoutOrFail(evalTimeout, () -> strOrEmpty(callF(code, q)));
} else {
print("Stateful bot. cookie: " + cookie);
if (empty(cookie)) ret withHeader(serve500("Need cookie for stateful bot"));
Conversation conv = uniq(Conversation, +cookie);
O instance = code;
if (instance == null) ret withHeader(serve500("No bot instance"));
print("Stateful bot instance: " + instance);
if (nempty(conv.dataStruct)) {
O data = unstructureInRealm(conv.dataStruct, instance); // hopefully this is safe
print("Unstructured: " + data);
copyAllThisDollarFields(instance, data); // probably not needed anymore
instance = data;
}
O _instance = instance;
answer = evalWithTimeoutOrFail(evalTimeout, () -> strOrEmpty(
initial ? callOpt(_instance, "initialMessage") : call(_instance, "answer", q)));
cset(conv, dataStruct := structure(instance));
print("Structured: " + conv.dataStruct);
}
if (initial && empty(answer))
answer = "Bot " + post.id + " ready";
ret withHeader(serveJSON(litorderedmap(answer := shorten(maxBotAnswerLength, answer))));
}
if (startsWith(req.uri(), "/transpilation/", m) && isInteger(m.rest())) {
long postID = parseLong(m.rest());
GazellePost post = getPost(postID);
if (post == null) ret serve404("Post " + postID + " not found");
S src = loadTextFile(transpilationFile(post.id));
if (empty(src))
src = "No transpilation found for post " + postID + "." +
(!post.isJavaXCode() ? " Code is not a code post." : " Please try \"Touch post\" and wait a few seconds");
else pcall {
src = javaPrettyPrint(src);
}
ret serveText(src);
}
if (eq(req.uri(), "/")) {
ret "Loaded code posts:"
+ ul(keys(loadedCodePosts));
}
ret serve404();
}
O codeForPost(GazellePost post) {
lock codeLoadLock;
O code = loadedCodePosts.get(post.id);
if (code == null) {
try {
S codeText = post.text;
if (!isCodePostSafe(post)) fail("Code is not safe: " + codeSafetyCheckResult(codeText));
codeText = prepareCode(codeText, null);
code = evalCode(codeText, post);
} catch print e {
code = new CouldntLoadCode;
}
loadedCodePosts.put(post.id, code);
}
dm_pointSubmoduleToMe(mainClass(code));
ret code;
}
File transpilationFile(long postID) {
ret programFile("Post-Transpilations/" + postID + ".java");
}
O withHeader(O response) {
call(response, 'addHeader, "Access-Control-Allow-Origin", "*");
ret response;
}
O _getReloadData() {
ret loadedCodePosts;
}
void _setReloadData(Map data) {
if (data != null)
loadedCodePosts = data;
}
void forgetLoadedCodePosts { clear(loadedCodePosts); }
void enhanceFrame(Container f) {
super.enhanceFrame(f);
internalFramePopupMenuItems(f,
"Forget loaded code", rEnter forgetLoadedCodePosts,
"Remove deleted posts", rThreadEnter removeDeletedPosts);
}
void removeDeletedPosts {
Cl posts = asSet(grabLoop.allPostIDs());
print("Keeping " + nPosts(posts));
if (syncRemoveAllExcept(allPosts, posts)) change();
print("New count: " + nPosts(allPosts));
removeAllExcept(loadedCodePosts, posts);
}
// web sockets
class WebSocketInfo {
S uri;
SS params;
new Set> liveDivs; // id/content info
}
transient Map webSockets = syncWeakHashMap();
void cleanMeUp_webSockets {
closeAllKeysAndClear((Map) webSockets);
}
void handleWebSocket(virtual WebSocket ws) {
set(ws, onClose := r { webSockets.remove(ws) });
set(ws, onOpen := rEnter {
S uri = cast rcall getUri(ws);
SS params = cast rcall getParms(ws);
print("WebSocket opened! uri: " + uri + ", params: " + params);
new WebSocketInfo info;
info.uri = uri;
info.params = params;
webSockets.put(ws, info);
});
setFieldToIVF1Proxy(ws, onMessage := msg -> { temp enter(); pcall {
WebSocketInfo info = webSockets.get(ws);
if (info == null) ret;
S data = rcall_string getTextPayload(msg);
Map map = jsonDecodeMap(data);
O div = map.get("liveDiv");
if (div cast S) {
S contentDesc = div;
syncAdd(info.liveDivs, pair(div, (O) contentDesc));
reloadDiv(ws, div, calcDivContent(contentDesc));
}
}});
}
transient ReliableSingleThread_Multi rstDistributeDivChanges = new(1000, lambda1 distributeDivChanges_impl);
void distributeDivChanges(S contentDesc) {
rstDistributeDivChanges.add(contentDesc);
}
void distributeDivChanges_impl(S contentDesc) enter {
//print("distributeDivChanges_impl " + contentDesc);
S content = null;
for (Pair p : syncMapToPairs(webSockets)) {
for (S div : asForPairsWithB(p.b.liveDivs, contentDesc)) {
if (content == null) content = calcDivContent(contentDesc);
if (content == null) { print("No content for " + contentDesc); ret; }
reloadDiv(p.a, div, content);
}
}
}
void reloadDiv(virtual WebSocket ws, S div, S content) {
print("Reloading div " + div + " through WebSocket");
S jsCode = replaceDollarVars(
[[ $("#" + $div).html($content);]],
div := jsQuote(div), content := jsQuote(content));
dm_call(ws, "send", jsonEncode(litmap(eval := jsCode)));
}
S calcDivContent(S contentDesc) {
if (eq(contentDesc, "webRequestsLeftHemi"))
ret n2(requestsServed);
if (eq(contentDesc, "serverLoadLeftHemi"))
ret formatDoubleX(systemLoad, 1);
if (eq(contentDesc, "memLeftHemi"))
ret str_toM(processSize);
null;
}
void requestServed {
distributeDivChanges("webRequestsLeftHemi");
}
void setDontHandleOnFirstGrab {
grabLoop.dontHandleOnFirstGrab = true;
change();
}
} // end of module