diff --git a/blockplacemods.py b/blockplacemods.py new file mode 100644 index 0000000..6258a13 --- /dev/null +++ b/blockplacemods.py @@ -0,0 +1,118 @@ +from helpers import * +from basecommands import simplecommand + +denyslabcorrection = open_json_file("denyslabcorrection", []) #Players that don't want slabs corrected +denyautofill = open_json_file("denyautocauldronfill", []) +denyautolevel = open_json_file("denyautocauldronlevel", []) + +def saveslabs(): + save_json_file("denyslabcorrection", denyslabcorrection) +def savecauldrons(): + save_json_file("denyautocauldronfill", denyautofill) +def savelevels(): + save_json_file("denyautocauldronlevel", denyautolevel) + +@simplecommand("autofillcauldron", + aliases = ["fillcauldronautomatically"], + usage = "on/off", + helpNoargs = True, + description = "Sets whether you want placed cauldrons to fill \nautomatically", + amax = 1, + senderLimit = 0) +def autofillcauldron_command(sender, command, label, args): + uuid = uid(server.getPlayer(sender.getName())) + if args[0].lower() == "off": + if uuid in denyautofill: + return "&cAuto fillment of cauldrons is already disabled" + denyautofill.append(uuid) + savecauldrons() + return "&aFilling cauldrons will no longer happen automatically" + if args[0].lower() == "on": + if uuid not in denyautofill: + return "&cAuto fillment of cauldrons is already enabled" + denyautofill.remove(uuid) + savecauldrons() + return "&aFilling cauldrons will happen automatically from now" + return "HELP" + + +@simplecommand("autoflipslab", + aliases = ["autoflipstep", "flipslabautomatically", "flipstepautomatically"], + usage = "on/off", + helpNoargs = True, + description = "Sets whether you want placed slabs to be turned \nupside-down", + amax = 1, + senderLimit = 0) +def autoflipslab_command(sender, command, label, args): + uuid = uid(server.getPlayer(sender.getName())) + if args[0].lower() == "off": + if uuid in denyslabcorrection: + return "&cAuto flipping of slabs is already disabled" + denyslabcorrection.append(uuid) + saveslabs() + return "&aFlipping slabs will no longer happen automatically" + if args[0].lower() == "on": + if uuid not in denyslabcorrection: + return "&cAuto flipping of slabs is already enabled" + denyslabcorrection.remove(uuid) + saveslabs() + return "&aFlipping slabs will happen automatically from now" + return "HELP" + + +@simplecommand("autotakewater", + aliases = ["autocauldronlevel"], + usage = "on/off", + helpNoargs = True, + description = "Sets whether you want right clicking cauldrons \nwith empty hand or redstone dust \nto lower water level", + amax = 1, + senderLimit = 0) +def autoflipslab_command(sender, command, label, args): + uuid = uid(server.getPlayer(sender.getName())) + if args[0].lower() == "off": + if uuid in denyautolevel: + return "&cTaking water with hand/redstone is already disabled" + denyautolevel.append(uuid) + savelevels() + return "&aYou can no longer take water with hand/redstone" + if args[0].lower() == "on": + if uuid not in denyautolevel: + return "&cTaking water with hand/redstone is already enabled" + denyautolevel.remove(uuid) + savelevels() + return "&aYou can take water with hand/redstone from now" + return "HELP" + + +@hook.event("block.BlockPlaceEvent", "monitor") +def on_block_place(event): + if event.isCancelled(): + return + player = event.getPlayer() + if player.getWorld().getName() not in ("Creative", "Trusted", "world"): + return + uuid = uid(player) + block = event.getBlockPlaced() + if uuid not in denyslabcorrection and str(block.getType()) in ("WOOD_STEP", "STEP") and block.getData() < 8: + block.setData(block.getData() + 8) # Flip upside down + elif uuid not in denyautofill and str(block.getType()) == "CAULDRON": + block.setData(3) #3 layers of water, 3 signal strength + + +@hook.event("player.PlayerInteractEvent", "monitor") +def on_interact(event): + player = event.getPlayer() + if uid(player) in denyautolevel or player.getWorld().getName() not in ("Creative", "Trusted", "world"): + return + if str(event.getAction()) != "RIGHT_CLICK_BLOCK": + return + if event.hasItem() and not str(event.getItem().getType()) == "REDSTONE": + return + block = event.getClickedBlock() + if str(block.getType()) == "CAULDRON" and block.getData() > 0: + block.setData(block.getData() - 1) #Lower water level by one + + + + + diff --git a/check.py b/check.py index 8bbfdeb..90faa53 100644 --- a/check.py +++ b/check.py @@ -11,7 +11,7 @@ from helpers import * # receive info based on the user's IP. information provided by ipinfo.io def ip_info(player): if player.isOnline(): - return json.load(urllib2.urlopen("http://ipinfo.io%s/json" % str(player.getAddress().getAddress()))) + return json.load(urllib2.urlopen("https://ipinfo.io%s/json" % str(player.getAddress().getAddress()))) else: return {} @@ -42,11 +42,6 @@ def get_website_data(player): return ("http://redstoner.com/users/%s" % results[0][0], results[0][1]) if results else (None, None) -# receive country based on the user's IP -def get_country(data): - return str(data.get("country")) - - def get_all_names(player): uuid = str(uid(player)).replace("-", "") names = json.load(urllib2.urlopen("https://api.mojang.com/user/profiles/%s/names" % uuid)) @@ -69,7 +64,8 @@ def get_all_data(sender, player): msg(sender, "&6> Website account: &e%s" % website[0]) msg(sender, "&6> email: &e%s" % website[1]) msg(sender, "&7 -- Data provided by ipinfo.io") - msg(sender, "&6> Country: &e%s" % get_country(data)) + msg(sender, "&6> Country: &e%s" % str(data.get("country"))) + msg(sender, "&6> Network: &e%s" % str(data.get("org"))) msg(sender, "&7 -- Data provided by Mojang") msg(sender, "&6> All ingame names used so far: &e%s" % get_all_names(player)) except: diff --git a/loginsecurity.py b/loginsecurity.py new file mode 100644 index 0000000..6822ad9 --- /dev/null +++ b/loginsecurity.py @@ -0,0 +1,121 @@ +from helpers import * +from basecommands import simplecommand +import bcrypt +from time import time as now +import thread + + +wait_time = 60 #seconds +admin_perm = "utils.loginsecurity.admin" +min_pass_length = 6 + +run_kick_thread = True +blocked_events = ["block.BlockBreakEvent", "block.BlockPlaceEvent", "player.PlayerMoveEvent"] +passwords = open_json_file("loginpasswords", {}) + +#if passwords == None: (set default value to None ^^) +# Keep everyone from playing? (Insecure) + +withholdings = {} #pname : jointime + + +def save_passwords(): + save_json_file("loginpasswords", passwords) + + +def matches(password, user): + hashed = passwords.get(uid(user)) + return bcrypt.hashpw(password, hashed) == hashed + + +@simplecommand("login", + usage = "", + description = "Logs you in if matches your password.", + senderLimit = 0, + helpNoargs = True) +def login_command(sender, command, label, args): + password = " ".join(args) + if matches(password, sender): + del withholdings[sender.getName()] + return "&aLogged in successfully!" + return "&cInvalid password" + + +@simplecommand("register", + usage = "", + description = "Registers you with . Next time you join, log in with /login", + senderLimit = 0, + helpNoArgs = True) +def register_command(sender, command, label, args): + uuid = uid(sender) + if uuid in passwords: + return "&cYou are already registered!" + password = " ".join(args) + if len(password) < min_pass_length: + return "&cThe password has to be made up of at least 8 characters!" + hashed = bcrypt.hashpw(password, bcrypt.gensalt(16)) + passwords[uuid] = hashed + return "&cPassword set. Use /login upon join." + + +@simplecommand("rmpass", + usage = "", + description = "Removes your password if the password matches", + senderLimit = 0, + helpNoArgs = True) +def rmpass_command(sender, command, label, args): + password = " ".join(args) + if matches(password, sender): + del passwords[uuid(sender)] + return "&aPassword removed successfully. You will not be prompted anymore." + return "&cInvalid password" + + +@simplecommand("rmotherpass", + aliases = ["lacrmpass"], + usage = "", + description = "Removes password of and sends them a notification", + helpNoArgs = True) +def rmotherpass_command(sender, command, label, args): + user = server.getOfflinePlayer(args[0]) + if user: + del passwords[uid(user)] + runas(server.getConsoleSender(), colorify("mail send %s &cYour password was reset by a staff member. Use &o/register&c to set a new one.")) + return "&sPassword of %s reset successfully" % user.getName() + return "&cThat player could not be found" + + +@hook.event("player.PlayerJoinEvent", "highest") +def on_join(event): + user = event.getPlayer() + if get_id(user) in passwords: + withholdings[user.getName()] = now() + + +@hook.event("player.PlayerQuitEvent", "normal") +def on_quit(event): + del withholdings[event.getPlayer().getName()] + + +def kick_thread(): + wait_time_millis = wait_time * 1000 + while True: + if not run_kick_thread: + info("Exiting LoginSecurity kicking thread!") + thread.exit() + time.sleep(1) + moment = now() + for name, jointime in withholdings.iteritems(): + if moment - jointime > wait_time_millis: + server.getPlayer(name).kickPlayer(colorify("&cLogin timed out")) + + +thread.start_new_thread(kick_thread, ()) + +for blocked_event in blocked_events: + @hook.event(blocked_event, "low") + def on_blocked_event(event): + user = event.getPlayer() + if user.getName() in withholdings: + event.setCancelled(True) + msg(user, "&cYou have to log in first! Use /login ") diff --git a/main.py b/main.py index 7101f69..8395f3b 100644 --- a/main.py +++ b/main.py @@ -38,6 +38,8 @@ shared["load_modules"] = [ "adminchat", # Adds /badge, allows to give players achievements "badges", + # Adds a few block placement corrections/mods + "blockplacemods", # Adds /calc, toggles automatic solving of Math expressions in chat "calc", # Plugin to locate laggy chunks. /lc lists chunks with more than n entities @@ -73,7 +75,9 @@ shared["load_modules"] = [ #adds snowbrawl minigame #"snowbrawl", # Adds /tm [player] for a messages to be sent to this player via /msg - "pmtoggle" + "pmtoggle", + # Replacement for LoginSecurity + "loginsecurity" # NOTICE: If you add something here, please add a small descriptive comment above! ] shared["modules"] = {} diff --git a/misc.py b/misc.py index a530424..554d095 100644 --- a/misc.py +++ b/misc.py @@ -182,16 +182,44 @@ def eval_argument_thread(event): thread.exit() """ + @simplecommand("pyeval", - usage = "[code..]", - description = "Runs python [code..] and returns the result", - helpNoargs = True) + usage = "[code..]", + description = "Runs python [code..] and returns the result", + helpNoargs = True) def on_pyeval_command(sender, command, label, args): msg(sender, " ".join(args), False, "e") thread.start_new_thread(eval_thread, (sender, " ".join(args))) return None +@simplecommand("tempadd", + usage = " [duration]", + description = "Temporarily adds to for \n[duration] minutes. Defaults to 1 week.", + helpNoargs = True, + helpSubcmd = True, + amin = 2, + amax = 3) +def tempadd_command(sender, command, label, args): + if not sender.hasPermission("permissions.manage.membership." + args[1]): + return "&cYou do not have permission to manage that group!" + if len(args) == 3: + if not args[2].isdigit(): + return "&cThats not a number!" + duration = int(args[2]) * 60 + else: + duration = 604800 + if duration <= 0: + return "&cThats too short!" + cmd = "pex user %s group add %s * %s" % (args[0], args[1], duration) + runas(sender, cmd) + + m, s = divmod(duration, 60) + h, m = divmod(m, 60) + d, h = divmod(h, 24) + return "&aAdded to group for %dd%dh%dm" % (d, h, m) + + @hook.command("modules") def on_modules_command(sender, command, label, args): """ @@ -228,9 +256,6 @@ def rs_material_broken_by_flow(material): return length > 1 and (parts[0] == "DIODE" or parts[1] in ("TORCH", "WIRE", "BUTTON", "HOOK") or (length == 3 and parts[1] == "COMPARATOR")) - - - """ @hook.event("player.AsyncPlayerChatEvent", "lowest") def on_chat(event):