- Timestamp:
- 12/16/11 02:37:52 (5 months ago)
- Files:
-
- trunk/idc/dice.d (modified) (1 diff)
- trunk/idc/dsss.conf (modified) (1 diff)
- trunk/idc/idc.d (modified) (53 diffs)
- trunk/idc/irc.d (modified) (10 diffs)
- trunk/idc/pad/cgi.d (modified) (9 diffs)
- trunk/idc/pad/engine.d (modified) (14 diffs)
- trunk/idc/pad/mainloop.d (modified) (8 diffs)
- trunk/idc/pad/utils.d (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/idc/dice.d
r723 r842 289 289 res = stuple(res, times) /apply/ (typeof(res) dg, typeof(times) times) { 290 290 auto tv = times(); 291 if (tv.sum > 128) throw new Exception("Cannot roll more than 128 dice. "); 291 292 Result r; 292 293 // r.text = Format(tv.info, "x ["); trunk/idc/dsss.conf
r648 r842 5 5 # [dice.d] 6 6 # target=dice 7 [pad/cgi.d] 8 target=cgi 9 buildflags=-g -L-lbz2 -J. 10 [pad/console.d] 11 target=console 12 buildflags=-g -L-lbz2 -J. trunk/idc/idc.d
r723 r842 1 1 module idc; // Internet delay chat! Yay! 2 2 3 import tools.fixed_socket, tools.base , std.string;3 import tools.fixed_socket, tools.base; 4 4 import tools.log, tools.threads, tools.threadpool; 5 5 import tools.time; 6 6 7 7 import irc; 8 import std.process;9 alias std.process.system system;10 8 11 9 import cah; 10 11 extern(C) void exit(int); 12 13 alias irc.tolower tolower; 12 14 13 15 string characters(string s) { … … 19 21 20 22 string clean(string s) { 21 return s.tolower().replace("'", "").replace("_", ""); 23 auto res = s.tolower().replace("'", "").replace("_", ""); 24 if (res == "127.0.0.1") res = "teentropers"; 25 return res; 22 26 } 23 27 … … 76 80 77 81 import std.file; 82 import std.process; 83 alias std.process.system system; 84 78 85 void reload(Query query) { 79 86 if (system("sh -c \"make >/dev/null 2> /tmp/idc_errors || exit 1\"") != 0) { … … 105 112 if (site) q = "site:"~site~" "~q; 106 113 auto hash = "http://www.google.com/uds/?file=search&v=1.0".download().between("JSHash = '", "'"); 107 auto url = "http://www.google.com/uds/GwebSearch?context=0&callback= google.search.WebSearch.RawCompletion&hl=en&v=1.0&q="~q.urlencode();114 auto url = "http://www.google.com/uds/GwebSearch?context=0&callback=GwebSearch.RawCompletion&hl=en&v=1.0&q="~q.urlencode(); 108 115 // if (site) url ~= "&as_sitesearch="~site; 109 auto res = url.download().between("unescapedUrl\":\"", "\"").replace("\\u003d", "\u003d"); 110 if (!res.length) throw new Exception("No results! "); 116 auto sitedata = url.download(); 117 auto res = sitedata.between("unescapedUrl\":\"", "\"").replace("\\u003d", "\u003d"); 118 if (!res.length) 119 if (sitedata.length < 132) throw new Exception(Format("No results! '", sitedata, "'")); 120 else throw new Exception(Format("No results! ", sitedata.length)); 111 121 // if (!res.length) throw new Exception("Cannot identify Google result for "~url); 112 122 if (!res.startsWith("http://")) res = "http://"~res; … … 212 222 bool pwn_site(string url, ref string[string] urls, ref string src) { 213 223 if (url.find("youtube.com") != -1) { 214 if (!src) src = url.download(); 215 // Youtube is in the sucking-of-ass business. This won't work. 216 /*if (src.find(`id="verify-age"`) != -1) { 217 url = "http://www.youtube.com/verify_age?action_confirm=Confirm Birth Date&next_url="~url.between("youtube.com", "").urlencode(); 218 src = url.download(); 219 }*/ 220 urls["Sorry"] = "Youtube downloading appears broken by a recent update. "; 221 return false; 222 auto 223 hd = src.between("isHDAvailable = ", ";") == "true", 224 t = src.between(`"t": "`, `"`), id = src.between(`"video_id": "`, `"`); 225 auto hqurl = url.followLink("/get_video?fmt=22&video_id="~id~"&t="~t); 226 auto mp4url = url.followLink("/get_video?fmt=18&video_id="~id~"&t="~t); 227 if (hd) urls["hd"] = hqurl.mktiny(); 228 urls["mp4"] = mp4url.mktiny(); 224 string[int] fmt_map; 225 bool[int] checked; 226 void check(string url, int fmt = 0) { 227 if (fmt) { 228 if (fmt in checked) return; 229 checked[fmt] = true; 230 url ~= Format("&fmt=", fmt); 231 } 232 auto src = url.download(); 233 auto swfhtml = src.between("swfHTML", "</script"); 234 auto str = swfhtml.between("? \"", "noembed>\";").replace("\\\"", "\"").unescape(); 235 auto videos = str.between("fmt_url_map=", "&csi_page_type=").split(",") /map/ (string s) { return stuple(s.slice("|").atoi(), s); }; 236 bool matched; 237 foreach (video; videos) { 238 if (video._0 == fmt) 239 matched = true; 240 fmt_map[video._0] = video._1; 241 checked[video._0] = true; 242 } 243 } 244 245 check(url); 246 foreach (type; [5, 17, 18, 22, 34, 35, 37, 43, 45]) { 247 if (type in checked) continue; 248 check(url, type); 249 } 250 251 string[int] id_map = [ 252 5: "flv/h.263 240p"[], 34: "flv/h.264 360p", 35: "flv/h.264 480p", 22: "mp4 720p", 37: "mp4 1080p", 18: "mp4 480x360", 253 43: "WebM 480p", 45: "WebM 720p", 17: "3gp 176x144", 254 0: "flv/h.263 320x240", 6: "flv/h.263 480x360", 13: "3gp 176x144" 255 ]; 256 foreach (key, value; fmt_map) { 257 if (key in id_map) 258 urls[id_map[key]] = value.mktiny(); 259 else 260 urls[Format(key)] = value.mktiny(); 261 } 229 262 return true; 230 263 } … … 254 287 } 255 288 void do_megavideo(string hit) { 256 if (hit.find("/v/") != -1) { hit = hit.replace("/v/", "/?v="); } 257 if (hit.find("megavideo.com/?v=") == -1) { 258 string redir; 259 hit.download_first(&redir); 260 if (redir) hit = redir ~ "&"; // hackaround: make .between(foo, "&") work 261 } else hit ~= "&"; 289 string redir; 290 hit.download_first(&redir); 291 if (redir) hit = redir ~ "&"; // hackaround: make .between(foo, "&") work 292 else hit ~= "&"; 262 293 auto link = hit.between("v=", "&"); 263 294 if (link.find("\"") != -1) link = link.between("", "\""); … … 304 335 } 305 336 306 extern(C) { 307 int pipe(int* fd); 308 int close(int); 309 FILE *fdopen(int fd, char* mode); 310 } 311 312 string readStream(InputStream IS) { 313 string res; 314 ubyte[512] buffer; 315 int i; 316 do { 317 i = IS.read(buffer); 318 if (i < 0) throw new Exception(Format("Read error: ", i)); 319 res ~= cast(string) buffer[0 .. i]; 320 } while (i); 321 return res; 322 } 323 324 string readback(string cmd) { 325 int[2] fd; // read end, write end 326 if (-1 == pipe(fd.ptr)) throw new Exception(Format("Can't open pipe! ")); 327 scope(exit) close(fd[0]); 328 auto cmdstr = Format(cmd, " >&", fd[1], " &"); 329 // throw new Exception("test: "~cmdstr); 330 system(cmdstr); 331 close(fd[1]); 332 scope fs = new CFile(fdopen(fd[0], "r"), FileMode.In); 333 return readStream(fs); 334 } 337 import readback; 335 338 336 339 void gun(Query query) { 337 auto gs = read back("echo $(fwoosh/fwoosh)");340 auto gs = readBack("echo $(fwoosh/fwoosh)"); 338 341 query.answer(gs); 339 342 } 340 343 341 344 void fwoosh_upd(Query query) { 342 query.answer(read back("cd fwoosh; ./scriptfile.sbcl |wc -l"));345 query.answer(readBack("cd fwoosh; ./scriptfile.sbcl |wc -l")); 343 346 } 344 347 345 348 import std.utf; 346 349 string htmlFormat(string s) { 347 wstring res;350 dstring res; 348 351 s.glomp_parse([ 349 "<sup >"[]: (string pre, ref string post) { res ~= pre.toUTF16(); },352 "<sup"[]: (string pre, ref string post) { res ~= pre.toUTF32(); post.slice(">"); }, 350 353 "</sup>": (string pre, ref string post) { 351 354 bool onlyAsciiHighables = true, onlyUnicodeHighables = true; 352 355 foreach (ch; pre) { 353 356 if (ch < '1' || ch > '3') onlyAsciiHighables = false; 354 if (ch /notin/ Range['0' .. '9'].endIncl && ch != '-' && ch != '+' && ch != ' ') onlyUnicodeHighables = false;357 if (ch /notin/ Range['0' .. '9'].endIncl && " +-=[]()Nn".find(ch) == -1) onlyUnicodeHighables = false; 355 358 } 356 359 if (onlyAsciiHighables) { … … 360 363 case '2': res ~= '²'; break; 361 364 case '3': res ~= '³'; break; 362 case ' ': res ~= ' '; break;363 365 default: assert(false); 364 366 } … … 375 377 case '+': res ~= 'âº'; break; 376 378 case '-': res ~= 'â»'; break; 379 case '=': res ~= 'âŒ'; break; 380 case '[', '(': res ~= 'âœ'; break; 381 case ']', ')': res ~= 'âŸ'; break; 382 case 'N', 'n': res ~= 'â¿'; break; 377 383 default: assert(false); 378 384 } 379 385 } 380 } else res ~= ("^"~pre).toUTF16(); 386 } else res ~= ("^"~pre~"^").toUTF32(); 387 }, 388 "<sub": (string pre, ref string post) { res ~= pre.toUTF32(); post.slice(">"); }, 389 "</sub>": (string pre, ref string post) { 390 bool onlyUnicodeLowables = true; 391 foreach (ch; pre) { 392 if (ch /notin/ Range['0' .. '9'].endIncl && " +-=[]()AaEeOoXx".find(ch) == -1) onlyUnicodeLowables = false; 393 } 394 if (onlyUnicodeLowables) { 395 foreach (ch; pre) { 396 if (ch in Range['0' .. '9'].endIncl) { res ~= "ââââââ 397 ââââ"w[ch - '0']; continue; } 398 switch (ch) { 399 case ' ': res ~= ' '; break; 400 case '+': res ~= 'â'; break; 401 case '-': res ~= 'â'; break; 402 case '=': res ~= 'â'; break; 403 case '[', '(': res ~= 'â'; break; 404 case ']', ')': res ~= 'â'; break; 405 case 'A', 'a': res ~= 'â'; break; 406 case 'E', 'e': res ~= 'â'; break; 407 case 'O', 'o': res ~= 'â'; break; 408 case 'X', 'x': res ~= 'â'; break; 409 default: assert(false); 410 } 411 } 412 } else res ~= ("_"~pre~"_").toUTF32(); 381 413 }, 382 414 "<font": (string pre, ref string post) { 383 res ~= pre.toUTF 16();384 post = post[post.find(">")+1 .. $];415 res ~= pre.toUTF32(); 416 post.slice(">"); 385 417 }, 386 "</font>": (string pre, ref string post) { res ~= pre.toUTF16(); } 387 ], (string rest) { res ~= rest.toUTF16(); }); 388 return res.toUTF8().replace("×", "Ã"); 418 "</font>": (string pre, ref string post) { res ~= pre.toUTF32(); }, 419 "<img": (string pre, ref string post) { 420 res ~= pre.toUTF32(); 421 post.slice(">"); 422 }, 423 "</img>": (string pre, ref string post) { } 424 ], (string rest) { res ~= rest.toUTF32(); }); 425 return res.toUTF8().replace("×", "Ã").replace(" ", " "); 389 426 } 390 427 … … 419 456 string res; 420 457 const string exclude = "-intitle:\"Discussion\" -inurl:editors.php -inurl:inboundcount.php -inurl:posts.php"; 421 try 458 try { 459 if (j.length) throw new Exception("mew"); // lal 422 460 res = (exclude~" \"pmwiki.php/main/"~q~"\"").googleQuery("tvtropes.org"); 423 catch (Exception ex) try 461 } catch (Exception ex) try { 462 sleep(2); 424 463 res = (exclude~" \""~q~"\"").googleQuery("tvtropes.org"); 425 catch (Exception ex) 464 } catch (Exception ex) { 465 sleep(2); 426 466 res = (exclude~" "~q).googleQuery("tvtropes.org"); 467 } 427 468 auto alt = res.between("", "?"); 428 469 query.answer(alt?alt:res); … … 701 742 } 702 743 unref; 703 res = res /qsort/ ex!(" fn -> a, b -> fn(a.count, b.count)")(&cmp!(int));744 res = res /qsort/ ex!("a, b -> a.count < b.count"); 704 745 if (!res[$-1].count) { 705 746 return query.say("Time's up and nobody voted! Sorry. Better luck next time!"); … … 941 982 (ref int[string] stats, ref string[string] namestats, string user, string host) { 942 983 string country; 984 if (host.endsWith(".hmsk")) return;; 943 985 auto parts = host.split("."); 944 986 while (parts.length > 1) { … … 948 990 country = GeoIP.lookup(ip); 949 991 break; 950 } catch (Exception ex) { parts = parts[1 .. $]; }992 } catch (Exception ex) { logln("fail: ", ex); parts = parts[1 .. $]; } 951 993 } 952 994 if (!country) return; … … 960 1002 } 961 1003 }, stuple(stats, namestats, query) /apply/ (ref int[string] stats, ref string[string] namestats, Query query) { 1004 if (!stats.length) return query.answer("No country stats available. This is probably due to hostmasking. "); 962 1005 auto rstats = invert(stats), top = rstats.keys.sort; 963 1006 string[][int] res; … … 1010 1053 1011 1054 import std.stream; 1012 File[string] bridget;1055 File[string] readers, writers; 1013 1056 1014 1057 void bridge(Query query) { … … 1022 1065 // query.say("Setting up read pipe on |", params[2], "|"); 1023 1066 bridge_read = new File(params[2], FileMode.In); 1067 readers[ch] = bridge_read; 1024 1068 } 1025 1069 void setupWrite() { 1026 1070 // query.say("Setting up write pipe on |", params[3], "|"); 1027 bridget[ch] = new File(params[3], FileMode.Out);1071 writers[ch] = new File(params[3], FileMode.Out); 1028 1072 } 1029 1073 if (order == "first") { … … 1041 1085 } 1042 1086 1087 void listen(Query query) { 1088 if (query.name != root_user) return query.answer("You are not authorized to set up a listen pipe! "); 1089 auto file = new File(query.param, FileMode.In); 1090 readers["<main>"] = file; 1091 while (true) { 1092 auto cmd = file.readLine(); 1093 query.connection.raw_sendln(IRCFormat(cmd)); 1094 } 1095 } 1096 1043 1097 void wbOnJoin(Query query) { 1044 1098 // sleep(1); … … 1047 1101 // q2.notice("No it hasn't. STFU GreetBot you tard. "); 1048 1102 // query.answer(Format("wbOnJoin(", query, ")")); 1103 if (query.channel == "#yackfest") return; 1049 1104 auto ch = query.channel.replace("#", ""), name = cast(string) query.name; 1050 1105 if (!wbfile.has(ch.clean(), name)) return; … … 1064 1119 // else q.say(": not in file"); 1065 1120 1066 notes ~= notefile.get!(Stuple!(string, typeof(µsec()), string)[])("global", name, null);1121 auto privnotes = notefile.get!(Stuple!(string, typeof(µsec()), string)[])("global", name, null); 1067 1122 if (notefile.has("global", name)) notefile.del("global", name); 1068 1123 1069 foreach (note; notes) q.answer(note._2.replace("$WHEN", timediff(µsec() - note._1))); 1070 } 1124 string countercheck(string msg) { 1125 if (auto rest = msg.startsWith("CHECKED ")) { 1126 msg = rest; 1127 auto name = msg.between("", " left"); 1128 auto notes = notefile.get!(Stuple!(string, typeof(µsec()), string)[])("global", name, null); 1129 notes ~= stuple(cast(string) name.tolower(), µsec(), Format("Your note to ", .nick(host), " was delivered $WHEN. ")); 1130 notefile.set("global", name, notes); 1131 } 1132 return msg; 1133 } 1134 1135 foreach (note; notes) { 1136 q.answer(countercheck(note._2).replace("$WHEN", timediff(µsec() - note._1))); 1137 } 1138 foreach (note; privnotes) { 1139 q.notice(countercheck(note._2).replace("$WHEN", timediff(µsec() - note._1))); 1140 } 1141 } 1142 } 1143 1144 void onNickChange(IRCconn ic, string channel, hostmask from, hostmask to) { 1145 seenfile.set(channel.clean(), (cast(string) .nick(from)).tolower(), stuple("\x00NICK "~cast(string) .nick(to), µsec())); 1071 1146 } 1072 1147 1073 1148 void dbg(Query q) { 1074 q.notice("Users: ", q.connection.users); 1149 q.notice("This does nothing currently. "); 1150 return; 1151 // q.notice("Users: ", q.connection.users); 1075 1152 } 1076 1153 1077 1154 void addnote(Query q) { 1078 bool global ;1155 bool global, checked; 1079 1156 if (auto rest = q.param.startsWith("global ")) { 1080 1157 q.param = rest; 1081 1158 global = true; 1082 1159 } 1083 if (!q.param.length) return q.answer("Usage: addnote <target name> <message>"); 1160 if (auto rest = q.param.startsWith("/checked ")) { 1161 q.param = rest; 1162 checked = true; 1163 } 1164 if (!q.channel.startsWith("#")) global = true; // privmsg notes are always global 1165 if (!q.param.length) return q.answer("Usage: note <target name> <message>"); 1084 1166 // if (!q.connection.registered(q.name)) { 1085 1167 // return q.answer("You have to be registered to do that! "); … … 1099 1181 foreach (note; notes) 1100 1182 if (note._0 == cast(string) q.name.tolower()) count++; 1101 if (count >= 3) return q.answer("Cannot leave more than three messages for somebody! "); 1102 notes ~= stuple(cast(string) q.name.tolower(), µsec(), Format(q.name, " left a note $WHEN: ", q.param)); 1183 if (a == "global") { 1184 if (count >= 12) return q.answer("Cannot leave more than twelve global messages for somebody! "); 1185 } else { 1186 if (count >= 3 && a != "#teentropers" && a != "#127.0.0.1"&& q.name != root_user) 1187 return q.answer("Cannot leave more than three messages for somebody! "); 1188 } 1189 notes ~= stuple(cast(string) q.name.tolower(), µsec(), Format(checked?"CHECKED ":"", q.name, " left a note $WHEN: ", q.param)); 1103 1190 notefile.set(a, b, notes); 1104 1191 return q.notice("Note saved! "); … … 1122 1209 } 1123 1210 auto ch = query.channel.replace("#", ""); 1124 auto nick = query.param.slice(" ") ;1211 auto nick = query.param.slice(" ").strip(); 1125 1212 if (nick.startsWith("<")) { 1126 1213 query.param = nick ~ " " ~ query.param; // part of message 1127 1214 nick = nick[1 .. $]; 1128 nick = nick.slice(">"); 1129 while (nick.length && "%@+-*".find(nick[0]) != -1) nick = nick[1 .. $]; 1130 if (!nick.length) return query.answer("Invalid nickname! "); 1131 } 1215 nick = nick.slice(">").strip(); 1216 } 1217 if (nick.startsWith("(")) { 1218 nick = nick[1 .. $]; 1219 nick = nick.slice(")").strip(); 1220 query.param = "<" ~ nick ~ "> " ~ query.param; 1221 } 1222 if (nick.startsWith("*")) { 1223 query.param = nick ~ " " ~ query.param; // dito 1224 nick = nick[nick.find("*")+1 .. $]; 1225 } 1226 while (nick.length && "%@+-*".find(nick[0]) != -1) nick = nick[1 .. $]; 1227 if (!nick.length) return query.answer("Invalid nickname: "~nick~"!"); 1132 1228 if (cast(nickname) nick == query.name && query.name != root_user) return query.answer("Please don't selfquote! "); 1133 1229 last_added[ch.clean() ~ "\x00" ~ cast(string) query.name] = nick; … … 1223 1319 } 1224 1320 auto text = std.string.join(parts, "\n ==========\n"); 1321 if (text.length > 64000) { 1322 query.answer("Hosting locally. "); 1323 auto fn = "quotes_"~ch~".txt"; 1324 auto localfn = "/home/mathis/public_html/"~fn; 1325 localfn.write(text); 1326 system("chmod a+r "~localfn); 1327 query.answer("http://demented.no-ip.org/~feep/"~fn); 1328 return; 1329 } 1330 query.answer("Uploading ", text.length, "b .. "); 1225 1331 return query.answer(pastepost(text, query.connection.nick, "d")); 1226 1332 } … … 1239 1345 temp[] flat; 1240 1346 foreach (key, value; answer) flat ~= temp(value, key); 1241 flat = flat.qsortfn(ex!(" fn -> a, b -> fn(a.key, b.key)")(&cmp!(int)));1347 flat = flat.qsortfn(ex!("a, b -> a.key < b.key")); 1242 1348 query.answer("WB stats: ", std.string.join(flat /map/ ex!("a -> a.val"), "; ")); 1243 1349 } … … 1248 1354 struct PixivSession { 1249 1355 string session_id; 1250 const User = " qqqwwwqqq", Pass = "qqqwwwqqq";1356 const User = "guest1234", Pass = "bugmenot"; 1251 1357 string relogin() { 1252 1358 logln("Acquiring new session id"); … … 1299 1405 string getTitle(string url, string src = null) { 1300 1406 try return _getTitle(url, src); 1301 catch (Exception ex) return Format("[", ex, "]"); 1407 catch (Exception ex) { 1408 auto res = Format("[", ex, "]"); 1409 if (res.startsWith ("[SSL/TLS is not")) return null; 1410 return res; 1411 } 1302 1412 } 1303 1413 … … 1334 1444 if (!src) src = url.download_first(); 1335 1445 if (!src) src = url.download(); 1336 if (src.startsWith("module")) return "D code. ";1446 if (src.startsWith("module")) return null; 1337 1447 1338 1448 // return first paragraph … … 1340 1450 1341 1451 string para = src; 1342 if (string box = para.between("<table", "infobox", "</table "))1452 if (string box = para.between("<table", "infobox", "</table>")) 1343 1453 para = para.after(box); 1454 if (string nav = para.between("<table", "navbox", "</table>")) 1455 para = para.after(nav); 1344 1456 para = para.between("<p>", "</p>"); 1457 1458 para = htmlFormat(para); 1345 1459 1346 1460 void rmTag(string tag) { 1347 1461 while (true) { 1348 auto betw = para.between_incl("<"~tag , ">");1462 auto betw = para.between_incl("<"~tag~" ", ">"); 1349 1463 if (!betw) break; 1350 1464 para = para.remove(betw); 1351 1465 } 1466 para = para.replace("<"~tag~">", ""); 1352 1467 para = para.replace("</"~tag~">", ""); 1353 1468 } 1354 1469 rmTag("a"); 1470 rmTag("abbr"); 1355 1471 rmTag("sup"); 1356 1472 rmTag("span"); 1357 1473 rmTag("small"); 1474 rmTag("big"); 1475 para = para.replace("<br />", "").replace("<br/>", ""); 1358 1476 bool rmDot; 1359 1477 auto Limit = 384 - url.length - 32; // safety margin, magic number! yay. … … 1399 1517 if (pwn_site(url, extras, src)) { 1400 1518 foreach (key, value; extras) 1401 extra ~= Format("[", key, " ", value, " ]");1402 } 1403 if (url.ifind("escapistmagazine.com/videos/view/ zero") != -1) {1519 extra ~= Format("[", key, " ", value, " ]"); 1520 } 1521 if (url.ifind("escapistmagazine.com/videos/view/") != -1) { 1404 1522 src = src.replace(""", "\""); 1405 1523 auto js_url = src.between("flashvars=\"config=", "\""); … … 1420 1538 } 1421 1539 mistake: // Wait, my mistake. Not a ZP video after all. 1540 if (title.length > 128) title = title[0..128] ~ "[snip]"; 1422 1541 auto res = title.replaceEntities().replace("\n", "").strip() ~ extra; 1423 1542 bool booru = … … 1454 1573 1455 1574 void ip(Query query) { 1456 query.answer("I'm online from ", " whatismyip.com/automation/n09230945.asp".download());1575 query.answer("I'm online from ", "automation.whatismyip.com/n09230945.asp".download()); 1457 1576 } 1458 1577 1459 1578 void control_char(Query query) { 1579 if (query.name != root_user) return query.answer("You are not authorized to do that."); 1460 1580 setCtrl(query.channel, query.param); 1461 if (query.name != root_user) return query.answer("You are not authorized to do that.");1462 1581 query.answer("Control character is now `", CTRL(query.channel), "'."); 1463 1582 } … … 1490 1609 void seen(Query query) { 1491 1610 if (!query.param.length) return query.answer("Usage: "~CTRL(query.channel)~"seen <nick>"); 1492 if (!seenfile.has(query.channel.clean(), query.param.tolower())) return query.answer("I have not seen "~query.param~" in this channel!"); 1493 auto entry = seenfile.get!(Stuple!(string, typeof(µsec())))(query.channel.clean(), query.param.tolower(), { fail; return stuple("", µsec()); }()); 1611 bool talking; 1612 if (auto rest = query.param.strip().startsWith("talking ")) { talking = true; query.param = rest; } 1613 if (auto time = query.param.strip().startsWith("within ")) { 1614 typeof(µsec()) limit; 1615 if (auto t = time.endsWith("d")) limit = t.atoi() * 3600L * 24L * 1_000_000L; 1616 else if (auto t = time.endsWith("h")) limit = t.atoi() * 3600L * 1_000_000L; 1617 else { 1618 query.answer("Unknown time format: ", time); 1619 } 1620 auto cur = µsec(); 1621 auto strlist = seenfile.section(query.channel.clean()); 1622 Stuple!(string, typeof(µsec()))[string] list; 1623 foreach (key, value; seenfile.section_map(query.channel.clean())) { 1624 try list[key] = deserialize!(Stuple!(string, typeof(µsec()))) (value); 1625 catch (Exception ex) { } 1626 } 1627 bool[string] names; 1628 bool similar(string a, string b) { 1629 if (a.length > b.length) swap(a, b); 1630 auto rest1 = b.startsWith(a), rest2 = b.endsWith(a); 1631 if (rest1 && "_|[".find(rest1[0]) != -1) return true; 1632 if (rest2 && "_|]".find(rest2[$-1]) != -1) return true; 1633 return false; 1634 } 1635 string shorter(string a, string b) { 1636 if (a.length < b.length) return a; 1637 return b; 1638 } 1639 outer:foreach (key, value; list) { 1640 auto dist = cur - value._1; 1641 if (dist < limit && (!talking || value._0 != "JOIN")) { 1642 auto name = key.dup; 1643 auto keys = names.keys.dup; // ouch 1644 foreach (key2; keys) { 1645 if (similar(key2, name)) { 1646 key2 = key2.dup; 1647 names.remove(key2); 1648 names[shorter(name, key2)] = true; 1649 continue outer; 1650 } 1651 } 1652 names[name] = true; 1653 } 1654 } 1655 query.notice("Seen within the last ", time, ": ", std.string.join(names.keys, ", ")); 1656 return; 1657 } 1658 Stuple!(string, typeof(µsec())) entry; 1659 string extra = " "; 1660 if (auto rest = query.param.strip().startsWith("global ")) { 1661 query.param = rest; 1662 auto ids = seenfile.sections(); 1663 typeof(µsec()) highest_time = 0; 1664 bool anyHit; 1665 foreach (id; ids) { 1666 bool hit = true; 1667 auto ent = seenfile.get!(Stuple!(string, typeof(µsec())))(id, query.param.tolower(), { hit = false; return stuple("", µsec()); }()); 1668 if (!hit) continue; 1669 anyHit = true; 1670 if (ent._1 > highest_time) { 1671 highest_time = ent._1; 1672 entry = ent; 1673 extra = format(" in ", id, " "); 1674 } 1675 } 1676 if (!anyHit) return query.answer("I have not seen anybody named \""~query.param~"\" anywhere at all! "); 1677 } else { 1678 if (!seenfile.has(query.channel.clean(), query.param.tolower())) return query.answer("I have not seen anybody named \""~query.param~"\" in this channel!"); 1679 entry = seenfile.get!(Stuple!(string, typeof(µsec())))(query.channel.clean(), query.param.tolower(), { fail; return stuple("", µsec()); }()); 1680 } 1494 1681 string mesg; 1495 if (auto rest = entry._0.startsWith("\x01ACTION ")) { 1682 if (auto rest = entry._0.startsWith("\x00NICK ")) { 1683 mesg = Format(" changing nick to ", rest); 1684 } else if (auto rest = entry._0.startsWith("\x01ACTION ")) { 1496 1685 rest = rest[0 .. $-1]; 1497 1686 mesg = Format(" doing \" * ", query.param, " ", rest, "\"."); 1498 1687 } else mesg = Format(" saying \"", entry._0, "\"."); 1499 query.answer("I have last seen ", query.param, " ", timediff(µsec() - entry._1), mesg); 1688 query.answer("I have last seen ", query.param, extra, timediff(µsec() - entry._1), mesg); 1689 } 1690 1691 void peak(Query query) { 1692 if (query.param.length) return query.answer("Usage: "~CTRL(query.channel)~"peak"); 1693 if (!IRCconfig.has(query.connection.host, "peak_"~query.channel.clean())) return query.answer("No data."); 1694 auto entry = IRCconfig.get!(Stuple!(int, typeof(µsec()), string)) (query.connection.host, "peak_"~query.channel.clean(), { fail; return stuple(0, µsec(), ""); }()); 1695 query.answer("User peak was ", timediff(µsec() - entry._1), " when ", entry._2, " brought the total to ", entry._0, "."); 1500 1696 } 1501 1697 … … 1530 1726 return; 1531 1727 } catch (Exception ex) { 1728 logln("Closing pipes. "); 1729 foreach (reader; readers) reader.close; 1730 foreach (writer; writers) writer.close; 1532 1731 logln(ex, "! Restarting in 10s, resetting resume. "); 1533 1732 foreach (i, arg; args) if (arg == "--resume") { args = args[0 .. i]; break; } … … 1563 1762 New(tgtp, Threadpool.GROW); 1564 1763 threadgens["ps"] = (nickname nick) { 1565 auto conduit = new MessageMultiChannel!(Query, true, false);1764 auto conduit = new MessageMultiChannel!(Query, false, false); 1566 1765 tp.addTask(stuple(conduit, nick) /apply/ (typeof(conduit) conduit, nickname nick) { 1567 start: scope(exit) goto start;1568 1766 Query last; 1569 1767 string getCmd() { last = conduit.get(); return last.param; } … … 1590 1788 int[6] dest = points; 1591 1789 foreach (part; parts) { 1790 part = part.strip(); 1592 1791 if (auto rest = part.startsWith("add " /or/ "put ")) { 1593 1792 auto name = rest.slice(" "), value = rest; … … 1981 2180 } 1982 2181 2182 void dcode(Query query) { 2183 auto code = query.param.strip(); 2184 if (!code.length) { query.answer("Usage: <D program>"); return; } 2185 auto response = ("POST=lang=D&code="~urlencode(code)~"&run=True&submit=Submit http://codepad.org/").download(); 2186 auto output = response.between("<span class=\"heading\">Output:", "</table>").between("</pre>", "</pre>").between("<pre>", "").strip().replace("\n", "\\"); 2187 if (output.length > 512) output = output[0 .. 512] ~ Format("[...] ", output.length - 512, " omitted. "); 2188 while (true) { 2189 auto pos1 = output.find("<a href"); 2190 if (pos1 != -1) { 2191 output = output[0 .. pos1] ~ output.between("<a href", "").between(">", ""); 2192 continue; 2193 } 2194 auto pos2 = output.find("</a>"); 2195 if (pos2 != -1) { 2196 output = output[0 .. pos2] ~ output.between("</a>", ""); 2197 continue; 2198 } 2199 break; 2200 } 2201 query.answer(output.replace(""", "\"")); 2202 } 2203 2204 void magic(Query q) { 2205 if (!q.param.length) { 2206 return q.answer("Usage: magic <card name>"); 2207 } 2208 auto site = googleQuery("site:magiccards.info -inurl:query "~q.param).download(); 2209 auto name = site.between("<title>", "</title>").strip(); 2210 auto type = site 2211 .between("0 0 0.5", "").between("<p>", "</p>").replace("\n", ""); 2212 auto pricelink = site.between("href=\"", "magic.tcgplayer.com/db", "\""); 2213 string priceinfo; 2214 if (!pricelink) priceinfo = "No price info. "; 2215 else { 2216 auto pricesite = pricelink.download(); 2217 auto pricedata = pricesite.between("#D9FCD1", "</div").betweens("<B>", "</b>"); // lol 2218 assert(pricedata.length == 3); 2219 priceinfo = Format("L ", pricedata[2], ", M ", pricedata[1], ", H ", pricedata[0]); 2220 } 2221 return q.answer(name, ", ", type, ": ", priceinfo, " ", mktiny(pricelink)); 2222 } 2223 2224 import neatcode; 2225 1983 2226 import vars, loli, maid, std.date: getUTCtime, dateToString = toString; 1984 2227 // Organically grown. … … 2011 2254 anypos["trope"] = anypos["roll"] = true; 2012 2255 bool mask_rp; 2256 auto d_imports = "import std.stdio, std.file, std.stream, std.string, std.math; "; 2013 2257 foreach (key, value; [ 2014 2258 "roll"[]: &roll /todg, "dice": &roll /todg, "google": &google /todg, "gcalc": &gcalc /todg, 2015 2259 "anidb": (Query q) { return anidb(q, 0, "", false); }, "decide": &decide /todg, 2016 "vote": &vote /todg,"rr": &rr /todg, "rps": &rps /todg,2260 /*"vote": &vote /todg, */"rr": &rr /todg, "rps": &rps /todg, 2017 2261 "more": &more /todg, "join": &join /todg, "part": &part /todg, 2018 2262 "help": &help, "cstats": &cstats /todg, "cfind": &cfind /todg, "vstats": &vstats /todg, … … 2030 2274 "note": &addnote /todg, "dbg": &dbg /todg, "overlap": &overlap /todg, 2031 2275 "fwoosh": &gun /todg, "fwoosh_up": &fwoosh_upd /todg, 2032 "markov": &markov /todg, 2276 "markov": &markov /todg, "peak": &peak /todg, 2277 "dsource": (Query q) { q.answer("http://downforeveryoneorjustme.com/dsource.org".getTitle()); }, 2278 "down": (Query q) { q.answer(("http://downforeveryoneorjustme.com/"~q.param).getTitle()); }, 2033 2279 "rp": (Query q) { 2034 2280 bool mask = true; … … 2038 2284 q.channel = "#fetishfuel"; q.answer(mask?"#ffsa masked for RP. ":"#ffsa unmasked. "); 2039 2285 q.channel = "#ffsa"; q.answer(mask?"#fetishfuel masked for RP. ":"#fetishfuel unmasked. "); 2040 }, 2286 }, "forcequit": (Query q) { if (q.name != root_user) return q.answer("Unauthorized access! "); exit(0); }, 2041 2287 "woof": (Query q) { 2042 2288 if (rand()%100>95) return q.answer("I'm a doggie!"); … … 2051 2297 if (q.param.length) q.answer("<b>", q.param.getTitle(), "</b>"); 2052 2298 else synchronized(last_url_sync) if (auto p = q.channel in last_url) q.answer("<b>", (*p).getTitle(), "</b>"); 2053 } 2299 }, 2300 "dcode": &dcode /todg, 2301 "dstmt": (Query q) { q.param = d_imports ~ "void main() { "~q.param~" }"; dcode(q); }, 2302 "dexpr": (Query q) { q.param = d_imports ~ "void main() { auto value = "~q.param~"; writefln(\"%s\", value); }"; dcode(q); }, 2303 "neat": &neat /todg, 2304 "magic": &magic /todg, "listen": &listen /todg 2054 2305 ]) commands[key] = value; 2055 safelist ~= ["roll"[], "dice", "wp", "trope", "seen", "df", "woof", "title", "decide", "google"]; 2056 auto conn = new IRCconn(args[0], cast(nickname) args[1], 6667, resume_handle); 2306 safelist ~= ["roll"[], "dice", "wp", "trope", "seen", "df", "woof", "title", "decide", "google", "magic"]; 2307 short port = 6667; 2308 if (args[0].find(":") != -1) { 2309 string portstr = args[0]; 2310 args[0] = portstr.slice(":"); 2311 port = cast(short) portstr.atoi(); 2312 } 2313 auto conn = new IRCconn(args[0], cast(nickname) args[1], port, resume_handle); 2057 2314 conn.onFind = conn /apply/ &onFind; 2315 conn.onNickChange = conn /apply/ &onNickChange; 2058 2316 auto aborted = new bool; 2059 2317 scope(exit) *aborted = true; … … 2077 2335 // int[hostmask] strikes; 2078 2336 int freebird; auto lastbird = sec(); 2079 with (conn) { 2080 defaultChanHandler = (string channel, hostmask host, string msg) { 2337 conn.defaultChanHandler = stuple(conn, channel_guards, justJoined) /apply/ (typeof(conn) conn, ref typeof(channel_guards) channel_guards, typeof(justJoined) justJoined, string channel, hostmask _host, string msg) { 2338 with (conn) { 2339 auto host = _host; 2081 2340 if (!channel.length && notice_cb) { notice_cb(host, msg); return true; } 2082 2341 logln("< ", msg); … … 2089 2348 if (!netsplit) wbOnJoin(q); 2090 2349 justJoined[host] = true; 2350 auto entry = IRCconfig.get!(Stuple!(int, typeof(µsec()), string)) (conn.host, "peak_"~channel.clean(), { return stuple(0, µsec(), ""); }()); 2351 if (auto p = channel in conn.users) { 2352 auto users = p.length; 2353 if (users >= entry._0) 2354 IRCconfig.set(conn.host, "peak_"~channel.clean(), stuple(conn.users[channel].length, µsec(), cast(string) .nick(host))); 2355 } 2091 2356 } else { 2092 2357 if (host in justJoined) { … … 2112 2377 if (channel.tolower() == "#himitsu" && msg.startsWith("!list" /or/ "!new")) { 2113 2378 tp.addTask(stuple(Query(conn, .nick(host), channel), conn) /apply/ (Query q, IRCconn ic) { 2114 if (ic.exists(cast(nickname) "Himitsu _Millenium_Robot")) return;2379 if (ic.exists(cast(nickname) "HimitsuBot")) return; 2115 2380 q.notice("Our bot isn't here right now. Perhaps try the archive bots in #lurk? "); 2116 2381 }); … … 2120 2385 seenfile.set(channel.clean(), (cast(string) .nick(host)).tolower(), stuple(msg, µsec())); 2121 2386 bool permit = true; 2387 permit &= .nick(host) != cast(nickname) "retardedFaggot"; 2122 2388 // if (channel.endsWith("tropers")) permit = false; // fucking bot nazis 2123 2389 if (channel.tolower().endsWith("anime")) permit = false; // moar nazis 2390 if (channel.tolower().endsWith("lesswrong")) permit = false; // nice people 2124 2391 permit &= .nick(host) != cast(nickname) "TropeBot"; 2125 2392 // echo is a silly feature … … 2142 2409 */ 2143 2410 // uncomment for awesome 2144 /*if (msg.ifind("freebird") != -1) {2411 if (msg.ifind("__freebird") != -1) { 2145 2412 auto q = Query(conn, .nick(host), channel); 2146 2413 if (freebird == -1 && sec() - lastbird > 300) { … … 2157 2424 else freebird ++; 2158 2425 } 2159 } */2426 } 2160 2427 if (false && msg.ifind("stop") != -1 && isPoking()) { 2161 2428 auto r = rand(); … … 2166 2433 cooldown = sec(); 2167 2434 } 2168 auto animes = msg.ifind("animes"); 2435 if (msg == "\x01VERSION\x01") { 2436 Query(conn, .nick(host), channel).notice("\x01VERSION feepbot\x01"); 2437 return true; 2438 } 2439 if (msg.startsWith("\x01PING")) { 2440 Query(conn, .nick(host), channel).notice(msg); 2441 return true; 2442 } 2443 /*auto animes = msg.ifind("animes"); 2444 auto msg2 = msg.tolower(); 2445 if (msg2.between("", "animes").endsWith("the ")) animes = -1; 2169 2446 const vocals="aeiouAEIOUqQ"; 2170 if (animes != -1 && (animes+6 == msg.length || vocals.find(msg[animes+6]) == -1) ) {2171 Query(conn, .nick(host), channel).answer(2447 if (animes != -1 && (animes+6 == msg.length || vocals.find(msg[animes+6]) == -1) && channel.ifind("yackfest") == -1) { 2448 auto answer = 2172 2449 ["Anime."[], "It's anime.", "Anime is its own plural. ", "Anime.", "Anime >_>", "Just \"anime\"."] 2173 [rand()%$] 2174 );2450 [rand()%$]; 2451 Query(conn, .nick(host), channel).answer(answer); 2175 2452 return true; 2176 } 2453 }*/ 2177 2454 if (onFind) onFind(channel, host); 2178 2455 auto name = .nick(host); … … 2181 2458 auto lpos = msg.find("http://"); 2182 2459 if (auto rest = msg.startsWith(CTRL(channel))) { 2183 try roll2(Query(conn, name, channel, rest), true); 2184 catch (Exception ex) { 2185 Query(conn, name, channel, rest).say("Error: ", ex); 2186 } 2460 auto q = Query(conn, name, channel, rest); 2461 tp.addTask(q /apply/ (Query q) { 2462 try roll2(q, true); 2463 catch (Exception ex) { 2464 // q.say("Error: ", ex); 2465 } 2466 }); 2187 2467 } 2188 2468 if (auto rest = msg.startsWith("> ")) { … … 2198 2478 } 2199 2479 bool flooded; 2200 if (channel /notin/ channel_guards) 2201 channel_guards[channel] = floodguard!(false)(6, 3, { flooded = true; }, { flooded = false; }); 2202 channel_guards[channel](1); 2480 synchronized(SyncObj!(channel_guards)) { 2481 if (channel /notin/ channel_guards) 2482 channel_guards[channel] = floodguard!(false)(6, 3, { flooded = true; }, { flooded = false; }); 2483 channel_guards[channel](1); 2484 } 2203 2485 auto query = Query(conn, name, channel, cmd); 2204 2486 if (flooded && (name != root_user)) query.notice("You have triggered my flood guard prevention. Please wait a bit."); … … 2220 2502 } 2221 2503 permit &= link.ifind("scp-wiki.wikidot.com") == -1; 2222 permit &= channel.tolower() != "#d" /or/ "#d.gdc"; 2504 permit &= channel.tolower() != 2505 "#d" /or/ "#d.gdc" /or/ "#yackfest" /or/ "#mcdevs" /or/ "#tgchan" 2506 /or/ "#c++"; 2507 permit &= .nick(host) != cast(nickname) "CIA-110"; 2508 permit &= .nick(host) != cast(nickname) "CIA-81"; 2509 permit &= .nick(host) != cast(nickname) "gh-clutter"; 2223 2510 if (permit) { 2224 2511 tp.addTask(stuple(link, channel, Query(conn, name, channel, ""), lpos + link.length + 2) 2225 2512 /apply/ (string link, string channel, Query query, int ll) { 2226 2513 auto title = link.getTitle(); 2514 if (!title) return; 2227 2515 if (channel.endsWith("tropers")) { 2228 2516 string rss; … … 2259 2547 string f; 2260 2548 if (channel.endsWith("tropers")) f = "b"; 2549 else if (channel.endsWith("lets-read")) f = "u"; 2261 2550 if (title.length) { 2262 2551 if (f) query.say(/*foo, */"<", f, ">", title, "</", f, ">"); … … 2269 2558 if (key.strip() /notin/ anypos) continue; 2270 2559 if (match != -1) { 2271 tp.addTask(&runQuery /fix/ Query(conn, name, channel, msg[match+1 .. $], 0, true)); 2560 if (.nick(host) == cast(nickname) "Lymia") { Query(conn, .nick(host), channel).say("Fuck you, ", .nick(host), "."); return true; } 2561 else { 2562 tp.addTask(&runQuery /fix/ Query(conn, name, channel, msg[match+1 .. $], 0, true)); 2563 } 2272 2564 wasCommand = true; 2273 2565 } … … 2282 2574 } 2283 2575 if (!wasCommand && !(mask_rp && (channel == "#fetishfuel" /or/ "#ffsa"))) { 2284 if (auto fdp = channel in bridget) { 2285 auto mname = name[0]~""~cast(string) name[1..$]; 2286 if (auto rest = msg.startsWith("\x01ACTION ")) { 2287 fdp.writeLine(Format("* ", mname, " ", rest[0 .. $-1])); 2288 } else if (msg == IRCconn.JOIN) { 2289 fdp.writeLine(Format("* ", mname, " has joined. ")); 2290 } else if (msg == IRCconn.PART) { 2291 fdp.writeLine(Format("* ", mname, " has left. ")); 2292 } else { 2293 fdp.writeLine(Format("<", mname, "> ", msg)); 2576 if (auto fdp = channel in writers) { 2577 // non-breaking space 2578 string mname = name; 2579 int last_vocal = -1; 2580 foreach_reverse(i, ch; mname) if ("aeiouAEIOU".find(ch) != -1) last_vocal = i; 2581 if (last_vocal != -1) mname = mname[0 .. last_vocal] ~ mname[last_vocal] ~ mname[last_vocal .. $]; 2582 try { 2583 if (auto rest = msg.startsWith("\x01ACTION ")) { 2584 fdp.writeLine(Format("* ", mname, " ", rest[0 .. $-1])); 2585 } else if (msg == IRCconn.JOIN) { 2586 fdp.writeLine(Format("* ", mname, " has joined. ")); 2587 } else if (msg == IRCconn.PART) { 2588 fdp.writeLine(Format("* ", mname, " has left. ")); 2589 } else { 2590 fdp.writeLine(Format("<", mname, "> ", msg)); 2591 } 2592 } catch (Exception ex) { 2593 logln(ex, ". Ignoring. "); 2294 2594 } 2295 2595 } 2296 2596 } 2297 2597 return false; 2298 }; 2598 } 2599 }; 2600 with (conn) { 2299 2601 privmsg = defaultChanHandler /fix/ ""; 2300 2602 tp.addTask(aborted /apply/ (ref bool aborted) { 2301 sleep (10); 2603 sleep(2); 2604 auto onstart = onStart(args[0]); 2605 if (onstart.length) { 2606 auto mesg1 = cast(hostmask) (cast(string) root_user ~ "!@"), mesg2 = onstart.take(); 2607 logln("first mesg ", mesg1, ", ", mesg2); 2608 privmsg(mesg1, mesg2); 2609 } 2610 sleep (6); 2302 2611 if (aborted) return; 2303 2612 join(args[2], defaultChanHandler); 2304 foreach (line; on Start(args[0])) {2613 foreach (line; onstart) { 2305 2614 privmsg(cast(hostmask) (cast(string) root_user ~ "!@"), line); 2306 2615 } … … 2321 2630 message(channel, ex.toString~". Time to die. :("); 2322 2631 }*/ 2632 conn.invalidateHandlers(); 2323 2633 logln(ex, " Rethrowing. "); 2324 2634 throw ex; trunk/idc/irc.d
r723 r842 8 8 9 9 import tools.time; 10 11 string tolower(string s) { 12 s = s.dup; 13 foreach (ref ch; s) { if (ch >= 'A' && ch <= 'Z') ch = 'a' + (ch - 'A'); } 14 return s; 15 } 10 16 11 17 template GuardTypes(bool B) { … … 37 43 while (sum > calls) { 38 44 cleanup(); 39 lock.Unsynchronized = { slowyield(); };45 lock.Unsynchronized = { usleep(200_000); }; 40 46 } 41 47 } else cleanup(); … … 88 94 void delegate(int) guard; 89 95 void delegate(string channel, hostmask host) onFind; 96 void delegate(string channel, hostmask from, hostmask to) onNickChange; 90 97 void delegate(string) onJoin; 98 void invalidateHandlers() { 99 onFind = null; 100 onJoin = null; 101 channels = null; 102 onNickChange = null; 103 } 91 104 bool registered(nickname n) { 92 105 auto waiter = new Semaphore; … … 114 127 this(string host, nickname nick, int port = 6667, uint resume_handle = -1) { 115 128 this.host = host; 116 guard = floodguard!(true)(1 00_000, 60); // 100 kb per 60s129 guard = floodguard!(true)(1_000, 5); // 1 kB / 5s 117 130 if (resume_handle == -1) { 118 131 auto dpos = host.find(":"); … … 189 202 logln(chan, " -> users: ", users[chan]); 190 203 } 191 if (auto rest = line.startsWith("JOIN")) { 192 auto channel = rest[2 .. $].tolower(); // "JOIN :[channel]" 204 if (auto rest = line.startsWith("JOIN ")) { 205 auto channel = rest.tolower(); 206 if (channel.startsWith(":")) channel = channel[1 .. $]; 193 207 if (onFind) onFind(channel, host); 194 208 if (channel /notin/ channels) … … 209 223 return; 210 224 } 211 if (auto rest = line.startsWith("NICK ")) {225 if (auto rest = line.startsWith("NICK :")) { 212 226 outer:foreach (channel, value; users) { 213 227 foreach (ref person; value) { 214 228 if (person == cast(string) .nick(host)) { 215 229 if (onFind) onFind(channel, cast(hostmask) host.replace(person, rest)); 230 if (onNickChange) onNickChange(channel, host, cast(hostmask) host.replace(person, rest)); 216 231 break outer; 217 232 } … … 222 237 if (auto rest = line.startsWith("433")) { 223 238 if (line.find("already in use") != -1) { 224 throw new Exception("Nick already in use! ");239 throw new Exception("Nick already in use! '"~line~"'"); 225 240 } 226 241 } … … 321 336 } 322 337 323 string IRCFormat(T...)(T t) { 338 struct FormatContext { 339 int bold_depth, ul_depth, inv_depth; 324 340 string res; 325 int bold_depth, ul_depth, inv_depth; 326 void delegate(string, ref string)[string] commands = 327 ["<b>"[]: (string pre, ref string post) { 328 if (!bold_depth) res ~= pre~"\x02"; 329 bold_depth++; 330 }, "</b>": (string pre, ref string post) { 331 bold_depth--; 332 if (!bold_depth) res ~= pre~"\x02"; 333 }, "<u>": (string pre, ref string post) { 334 if (!ul_depth) res ~= pre~"\x1F"; 335 ul_depth++; 336 }, "</u>": (string pre, ref string post) { 337 ul_depth--; 338 if (!ul_depth) res ~= pre~"\x1F"; 339 }, "<i>": (string pre, ref string post) { 340 if (!inv_depth) res ~= pre~"\x16"; 341 inv_depth++; 342 }, "</i>": (string pre, ref string post) { 343 inv_depth--; 344 if (!inv_depth) res ~= pre~"\x16"; 345 }, "<br>": (string pre, ref string post) { 346 res ~= pre~"\n"; 341 void openBold(string pre, ref string post) { 342 if (!bold_depth) res ~= pre ~ "\x02"; 343 bold_depth ++; 344 } 345 void closeBold(string pre, ref string post) { 346 bold_depth --; 347 if (!bold_depth) res ~= pre ~ "\x02"; 348 } 349 void openUnderline(string pre, ref string post) { 350 if (!ul_depth) res ~= pre ~ "\x1F"; 351 ul_depth ++; 352 } 353 void closeUnderline(string pre, ref string post) { 354 ul_depth --; 355 if (!ul_depth) res ~= pre ~ "\x1F"; 356 } 357 void openItalic(string pre, ref string post) { 358 if (!inv_depth) res ~= pre ~ "\x16"; 359 inv_depth ++; 360 } 361 void closeItalic(string pre, ref string post) { 362 inv_depth --; 363 if (!inv_depth) res ~= pre ~ "\x16"; 364 } 365 void insertNewline(string pre, ref string post) { 366 res ~= pre ~ "\n"; 347 367 // recreate formatting 348 368 if (bold_depth) res ~= "\x02"; 349 369 if (ul_depth) res ~= "\x1F"; 350 370 if (inv_depth) res ~= "\x16"; 351 }]; 371 } 372 void delegate(string, ref string)[string] map; 373 void setupMap() { 374 map["<b>"] = &openBold; 375 map["</b>"] = &closeBold; 376 map["<u>"] = &openUnderline; 377 map["</u>"] = &closeUnderline; 378 map["<i>"] = &openItalic; 379 map["</i>"] = &closeItalic; 380 map["<br>"] = &insertNewline; 381 } 382 } 383 384 TLS!(FormatContext) foco; 385 386 static this() { New(foco, { auto res = new FormatContext; res.setupMap(); return res; }); } 387 388 string IRCFormat(T...)(T t) { 352 389 auto base = Format(t); 353 390 auto nfpos = base.find("<noformat/>"); 354 391 if (nfpos != -1) return IRCFormat(base[0 .. nfpos]) ~ base[nfpos + 8+3 .. $]; 355 base.glomp_parse(commands, (string rest) { res ~= rest; }); 356 return res; 392 auto ctx = foco.ptr(), map = ctx.map; 393 ctx.res = null; 394 base.glomp_parse(map, (string rest) { ctx.res ~= rest; }); 395 return ctx.res; 357 396 } 358 397 … … 369 408 } 370 409 void notice(T...)(T t) { 371 connection.raw_sendln(IRCFormat("NOTICE ", name, " :", t));410 output((string line) { connection.raw_sendln(IRCFormat("NOTICE ", name, " :", line)); }, t); 372 411 } 373 412 void answer(T...)(T t) { say(cast(string) name, ": ", t); } 374 void say(T...)(T t) { 413 void say(T...)(T t) { output((string line) { connection.message(channel, line); }, t); } 414 void output(T...)(void delegate(string) dg, T t) { 375 415 auto msg = Format(t); 376 416 string pre_line; … … 408 448 else breakIt(msg); 409 449 auto lines = IRCFormat(pre_line).split("\n"); 410 foreach (line; lines) 411 connection.message(channel, line); 450 foreach (line; lines) dg(line); 412 451 } 413 452 void act(T...)(T t) { connection.message(channel, "\x01"~Format("ACTION ", t).IRCFormat()~"\x01"); } trunk/idc/pad/cgi.d
r719 r842 72 72 else res ~= "<"~ent~">"; 73 73 } 74 foreach (ent; Tuple!("br", "b", "/b", "i", "/i" )) {74 foreach (ent; Tuple!("br", "b", "/b", "i", "/i", "/p", "/a")) { 75 75 ent_map["<"~ent~">"] = ent.dup /apply/ &fun; 76 76 } … … 79 79 auto imgbody = post.slice(">"); 80 80 if (imgbody.find(" on") != -1) throw new Exception("JavaScript is a security risk, and not supported. "); 81 res ~= "<img"~imgbody ~">";81 res ~= "<img"~imgbody.replace(""", "\"")~">"; 82 82 }; 83 83 ent_map["<p"] = (string pre, ref string post) { … … 86 86 if (peabody.find(" on") != -1) throw new Exception("JavaScript is a security risk, and not supported. "); 87 87 res ~= "<p"~peabody~">"; 88 }; 89 ent_map["<a"] = (string pre, ref string post) { 90 res ~= pre; 91 auto abody = post.slice(">"); 92 if (abody.find(" on") != -1) throw new Exception("JavaScript is a security risk, and not supported. "); 93 res ~= "<a"~abody.replace(""", "\"")~">"; 88 94 }; 89 95 ent_map["http://"] = (string pre, ref string post) { … … 159 165 if (back && lines.length) lines = lines[0 .. $-1]; 160 166 string default_input; 167 string[] input_lines = lines; 161 168 if (auto lstr = "backToLine" in chunks) { 162 169 auto line = (*lstr).atoi(); … … 193 200 output = null; 194 201 ex.msg = ex.msg.replace("<", "<").replace(">", ">"); 195 error = Format("<h3>An error has occurred. </h3><h4>", ex, "</h4><a href=\"javascript:history.go(-1); \">Back</a>"); 202 error = Format("<h3>An error has occurred. </h3><h4>", ex, "</h4><a href=\"javascript:history.go(-1); \">Back</a>"/*<br> 203 The commands leading up to this were: ", Format(input_lines).replace(", ", ", <br>")*/, " "); 196 204 } 197 205 auto needed = sec() - start; … … 214 222 postText = "</div>"; 215 223 } 224 string res; 216 225 "Content-type: text/html; charset=utf-8\r\n 217 226 <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" … … 220 229 <head> 221 230 <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" /> 222 <title>IF Web Interface - generated in %TIME%s + %TIME2%s , %MEM%MB used</title>231 <title>IF Web Interface - generated in %TIME%s + %TIME2%s for I/O, %MEM%MB used</title> 223 232 </head> 224 233 <body> … … 254 263 </body> 255 264 </html> 256 ".dynReplace((string s) { printf("%.*s", s); },265 ".dynReplace((string s) { printf("%.*s", s); res ~= s; }, 257 266 "%PREVENCODED%", encoded, 258 267 "%PREV_REENCODED%", encoded.entitiesEncode(), … … 273 282 "%MEM%", { GCStats gs = void; getStats(gs); return Format(gs.poolsize / 1_000_000f); }() 274 283 ); 284 "/tmp/lastpage.html".write(res); 275 285 } 276 286 trunk/idc/pad/engine.d
r719 r842 37 37 } 38 38 } 39 foreach (ch; cast(ubyte[]) predata) data ~= map[ch]; 40 if (sub_bin) sub_bin ~= "."; 41 // auto url = "POST=parent_pid=&format=text&code2="~data~"&poster=&paste=Send&expiry="~expiry~"&email= " 42 // "http://"~sub_bin~"pastebin.com/pastebin.php"; 43 // Pastebin v2 .. 44 auto url = "POST=submit=submit&paste_parent_key=&paste_subdomain="~sub_bin~ 45 "&paste_code="~data~"&paste_format=1&paste_expire_date="~expiry~"&paste_private=1&paste_name=&paste_remember=0&submit=submit " 46 "http://"~sub_bin~"pastebin.com/post.php"; 47 string redir; 48 url.download(&redir); 49 if (redir.startsWith("COOKIE=")) redir.slice(" "); 50 return redir; 39 foreach (ch; cast(ubyte[]) predata) { 40 if (ch > 'a' && ch < 'z' || ch > 'A' && ch < 'Z') data ~= ch; 41 else data ~= map[ch]; 42 } 43 // Pastebin public api .. 44 auto url = "POST=submit=submit&paste_subdomain="~sub_bin~ 45 "&paste_code="~data~"&paste_format=text&paste_expire_date="~expiry~"&paste_private=1&paste_name=&paste_remember=0 " 46 "http://pastebin.com/api_public.php"; 47 // auto url = "POST=api=1&code="~data~"&chk_private http://www.webpaste.net/"; 48 return url.download(); 51 49 } 52 50 … … 202 200 auto res = s2.take(matched); 203 201 if (!res.length) { 204 if (mode != TokenMode.Normal || !s2.length) { *failtext.ptr() = "Invalid token "~st.strip().next_text(); return false; } 202 if (mode != TokenMode.Normal || !s2.length) { 203 *failtext.ptr() = *failtext.ptr() ~ Format("Invalid token ", st.strip().next_text(), ", matched ", matched, ", mode ", mode, ". "); 204 return false; 205 } 205 206 st = s2; s = st.take(1); // single-symbol token 206 207 return true; … … 408 409 | bool | int | string | float | Scope 409 410 -----------+---------------+-------------+----------------------+---------+---------- 410 Boolean | b | b | b?q{true}p:q{false}p | Þ | Þ411 Integer | i != 0 | i | Format(i) | i | Þ412 String | s == q{true}p | atoi(s) | s | atof(s) | Þ413 Float | Þ | cast(int) f | Format(f) | f | Þ414 ScopeRef | !!sr | Þ | (sr?sr.fqn:q{(null:r)}p) | Þ| sr411 Boolean | b | b | b?q{true}p:q{false}p | | 412 Integer | i != 0 | i | Format(i) | i | 413 String | s == q{true}p | atoi(s) | s | atof(s) | 414 Float | | cast(int) f | Format(f) | f | 415 ScopeRef | !!sr | | (sr?sr.fqn:q{(null:r)}p) | | sr 415 416 ScopeValue | sr.value().to!(T) | sr.value().to!(T) | sr.value().to!(T) | sr.value().to!(T) | sr`; 416 417 mixin(ctTableUnrollColMajor(Table, … … 422 423 else `, 423 424 `case FlatType.$ROW: 424 static if ( q{$CELL}p == "Þ")425 static if (!q{$CELL}p.length) 425 426 throw new Exception(q{Cannot convert $ROW to $COL: }p~to!(string)~q{! }p); 426 else return $CELL;427 else { return $CELL; } 427 428 ` 428 429 ).litstring_expand() ~ `static assert(false, "Unsupported type: "~T.stringof); `); … … 1224 1225 } else break; 1225 1226 } 1226 if (!accept(s2, "}")) return false; 1227 if (!accept(s2, "}")) { 1228 throw new Exception(Format("Expected closing bracket at ", s2.next_text())); 1229 } 1227 1230 s = s2; 1228 1231 return true; … … 1532 1535 static void getThis(ref string s, out Scope sc) { 1533 1536 try _getThis(s, sc); 1534 catch (Exception ex) throw new Exception(Format("Failed to parse Scope : ", ex, ". "));1537 catch (Exception ex) throw new Exception(Format("Failed to parse Scope '", sc?sc.name:"", "': ", ex, ". ")); 1535 1538 } 1536 1539 static void _getThis(ref string s, out Scope sc) { … … 1575 1578 scp.replant(this); 1576 1579 } 1580 } 1581 bool noStmts() { 1582 foreach (statement; statements) 1583 if (auto sc = castToScope(statement)) { 1584 if (sc.name == "default") return false; // SORT of a statement 1585 } else return false; 1586 return true; 1577 1587 } 1578 1588 // breaks on gdc with final … … 1682 1692 Scope lookdown(string var, ref RecursionCheck visited, bool userquery = false, bool below = false) { 1683 1693 if (!var.length) { 1684 if (userquery && "failLookup" in this) return null;1694 if (userquery && ("failLookup" in this || noStmts)) return null; 1685 1695 return this; 1686 1696 } … … 1721 1731 } 1722 1732 return cur.lookdown(var, visited, userquery, below); 1723 } else if (!below && isNamed(first) ) return lookdown(var, visited, userquery, below);1733 } else if (!below && isNamed(first) && !parent) return lookdown(var, visited, userquery, below); // only do this if we're the root!! 1724 1734 else { 1725 1735 Scope[] fallbacks; … … 1731 1741 // pure recursion 1732 1742 if (!mst.checkingRightNow) // a scope can't depend on itself 1733 if (auto res = mst.src.lookdown(var_backup, visited, userquery, below)) return res; 1743 if (auto res = mst.src.lookdown(var_backup, visited, userquery, below)) 1744 if (!(userquery && res.noStmts)) return res; 1734 1745 } 1735 1746 if (auto ast = cast(AliasStatement) entry) { … … 1761 1772 } 1762 1773 if (sc.isNamed(first)) { 1763 if (auto res = sc.lookdown(var, visited, userquery)) return res; 1774 if (auto res = sc.lookdown(var, visited, userquery)) 1775 if (!(userquery && res.noStmts)) return res; 1764 1776 } 1765 1777 } … … 1767 1779 // Multiple fallbacks because instances might introduce more than one "any" block 1768 1780 if (fallbacks) { 1769 foreach (fallback; fallbacks) if (auto res = fallback.lookdown(var, visited, userquery)) {1781 foreach (fallback; fallbacks) if (auto res = fallback.lookdown(var, visited, userquery)) if (!(userquery && res.noStmts)) { 1770 1782 fallback.value = Value(first); 1771 1783 return res; … … 1797 1809 auto from = re.from(sc).to!(string), to = re.to(sc).to!(string); 1798 1810 auto alt = regex_sub(name, from, to); 1811 // throw new Exception(Format("|", from, "| |", to, "| |", name, "| |", alt, "|")); 1799 1812 if (alt != name) { 1800 1813 auto res = lookup(alt, userquery, vp); … … 1807 1820 if (sc.global) { 1808 1821 auto rest = name, first = rest.slice("."); 1809 if (auto res = sc.lookdown(first, *vp, userquery)) { 1810 if (!res.local) { 1822 { 1823 auto res = sc.isNamed(first)?sc:null; 1824 if (!res) res = sc.lookdown(first, *vp, !rest.length /* don't block if there's more to come */); 1825 if (res && !res.local) { 1811 1826 if (auto res2 = res.lookdown(rest, *vp, userquery)) return stuple(res2, true); 1812 1827 } trunk/idc/pad/mainloop.d
r715 r842 1 1 module pad.mainloop; 2 2 3 import pad.engine, pad.utils, tools.log, tools.compat : string;3 import pad.engine, pad.utils, tools.log, tools.compat; 4 4 static import tools.compat; 5 5 … … 71 71 return runLoop(src, "", write, read, readFS, dontSave, stopstart); 72 72 } 73 74 import tools.rd: next_text; 73 75 74 76 const bool ReadFS = true, DontSave = true; … … 88 90 Scope.getThis(s2, root); 89 91 } 92 if (s2.strip().length) { 93 throw new PadException(Format("Unknown/leftover text at `", s2.strip().next_text(), "'")); 94 } 90 95 if (full || !stream) stream = new SaveStream; 91 96 if (!root) return; … … 93 98 location = root.eval(initial, root); 94 99 if (!location) { 95 throw new PadException("Evaluating root didn't generate a redirect! " );100 throw new PadException("Evaluating root didn't generate a redirect! "/* ~ data*/); 96 101 } 97 102 if (verbose) write(initial.collate); … … 108 113 } 109 114 while (line) { 110 auto chunk = line.slice(";") ;115 auto chunk = line.slice(";").strip(); 111 116 auto parts = chunk.split(" "); 112 117 if (parts.length == 1) cmd = parts[0]; … … 139 144 write("Forwarded. "); 140 145 } else reset(true); 141 string lastLoaded; 146 string lastLoaded = src; 147 int lines; 142 148 while (true) { 143 149 auto line = read(); 150 lines ++; 144 151 if (line == "quit" || line == "q") return write("Bye! "); 145 152 if (line == "forcequit") return; … … 162 169 auto url = pastepost(postdata); 163 170 write(Format("Saved ", postdata.length, " (", predata.length, ") to ", url)); 171 continue; 172 } 173 if (line == "info") { 174 write(Format("You are playing ", lastLoaded, ", ", lines, " lines in. ")); 164 175 continue; 165 176 } … … 191 202 continue; 192 203 } 193 if ( auto from = line.startsWith("open ")) {204 if (!src) if (auto from = line.startsWith("open ")) { 194 205 if (from.find(" ") != -1) throw new Exception("Invalid URL: \""~from~"\". "); // No POST hacking! Bad user! 195 206 if (stopstart) stopstart(); trunk/idc/pad/utils.d
r713 r842 22 22 string data2; 23 23 foreach (entry; data.betweens("<pre class=\"code\">", "<")) 24 data2 ~= entry; 24 data2 ~= entry 25 .replace(""", "\"") 26 .replace("<", "<") 27 .replace(">", ">") ~ "\n"; 25 28 data = data2; 29 } 30 if (data.find("/ep/pad/view/") != -1) { // EtherPad 31 auto id = data.between(`href="`, `" id="docbarslider`, GLOMP_RIGHT); 32 auto redir = addr.followLink(id)~"?pt=1"; 33 data = redir.download(); 26 34 } 27 35 if (data.find("%PAD START%") != -1) { … … 39 47 if (inTarget) 40 48 data ~= line.between("content=\"", "")[0 .. $-1] 41 .replace(`\n`, "\n").replace(`\"`, "\"").replace(`\\`, "\\"); 49 .replace(`\n`, "\n") 50 .replace(`\r`, "\r") 51 .replace(`\t`, "\t") 52 .replace(`\"`, "\"") 53 .replace(`\\`, "\\"); 42 54 if (line.endsWith("title=\""~addr~"\"")) inTarget = true; 43 55 } … … 97 109 string res; 98 110 int nest_level = 0; 99 void eatTo Break(ref string s) {111 void eatToNewline(ref string s) { 100 112 auto pos = s.find("\n"); 101 113 if (pos == -1) return; … … 104 116 text.glomp_parse([ 105 117 "\""[]: (string pre, ref string post) { if (!nest_level) { res ~= pre ~ "\"" ~ getLiteral(post, true) ~ "\""; } }, 106 "//": (string pre, ref string post) { if (!nest_level) { res ~= pre; post.eatTo Break(); } },118 "//": (string pre, ref string post) { if (!nest_level) { res ~= pre; post.eatToNewline(); } }, 107 119 "/*": (string pre, ref string post) { if (!nest_level) res ~= pre; nest_level ++; }, 108 120 "*/": (string pre, ref string post) { if (!nest_level) throw new Exception("Too many */"); nest_level --; }
