From 62059e613ed2a075d9720994e2f90d3f106c770e Mon Sep 17 00:00:00 2001 From: PanFritz Date: Thu, 16 Apr 2015 12:28:58 +0200 Subject: [PATCH 01/10] Added perm check for Admin notes notification --- adminnotes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/adminnotes.py b/adminnotes.py index b49ac0f..967f2ca 100644 --- a/adminnotes.py +++ b/adminnotes.py @@ -125,6 +125,9 @@ def adminnotes_command(sender, command, label, args): @hook.event("player.PlayerJoinEvent", "monitor") def on_an_join(event): + if not sender.hasPermission(an_permission): + noperm(sender) + return if len(notes) > 0: msg(event.getPlayer(), "&cThere are currently %s open notes!" % len(notes)) elif len(notes) == 0: From a37fb90b0690666f0677cb940f60b6ec045ce0f9 Mon Sep 17 00:00:00 2001 From: PanFritz Date: Thu, 16 Apr 2015 12:36:16 +0200 Subject: [PATCH 02/10] Tab indent fix --- adminnotes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adminnotes.py b/adminnotes.py index 967f2ca..874ea68 100644 --- a/adminnotes.py +++ b/adminnotes.py @@ -125,7 +125,7 @@ def adminnotes_command(sender, command, label, args): @hook.event("player.PlayerJoinEvent", "monitor") def on_an_join(event): - if not sender.hasPermission(an_permission): + if not sender.hasPermission(an_permission): noperm(sender) return if len(notes) > 0: From 7c30af86f22b2db6255de7d722408eeaa53cf3f7 Mon Sep 17 00:00:00 2001 From: PanFritz Date: Thu, 16 Apr 2015 12:40:30 +0200 Subject: [PATCH 03/10] Fix missing var --- adminnotes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adminnotes.py b/adminnotes.py index 874ea68..35579f9 100644 --- a/adminnotes.py +++ b/adminnotes.py @@ -124,9 +124,9 @@ def adminnotes_command(sender, command, label, args): @hook.event("player.PlayerJoinEvent", "monitor") -def on_an_join(event): - if not sender.hasPermission(an_permission): - noperm(sender) +def on_an_join(event): + if not event.getPlayer().hasPermission(an_permission): + noperm(event.getPlayer()) return if len(notes) > 0: msg(event.getPlayer(), "&cThere are currently %s open notes!" % len(notes)) From ab1087f210267b5f06bed7224ddc49db1518ac44 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Thu, 16 Apr 2015 18:33:09 +0200 Subject: [PATCH 04/10] Adminnotes fix continued message used for next note Also code cleanup --- adminnotes.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/adminnotes.py b/adminnotes.py index 35579f9..a30f77d 100644 --- a/adminnotes.py +++ b/adminnotes.py @@ -13,10 +13,9 @@ def save_notes(): def r_ago(unit, i): # Return ago + plural - i = i if i != 1: - unit +="s" - return str(i)+" "+unit+" ago" + unit += "s" + return "%s %s ago" % (i, unit) def calc_diff(time_ago): @@ -41,6 +40,7 @@ def calc_diff(time_ago): def show_all_notes(sender): if len(notes) == 0: msg(sender, "&aNo open notes at the moment!") + return for i in range(0, len(notes)): arr = notes[i] name = arr[0] @@ -76,17 +76,18 @@ def adminnotes_command(sender, command, label, args): arglen = len(args) # arg length not valid - if arglen < 1: + if arglen == 0: show_all_notes(sender) return # Shows note help - if args[0].lower() == "help": + subcmd = args[0].lower() + if subcmd == "help": show_an_help(sender) return # Delete note - if args[0].lower() == "del": + if subcmd == "del": if arglen != 2: show_an_help(sender) return @@ -106,29 +107,29 @@ def adminnotes_command(sender, command, label, args): message = " ".join(args) name = sender.getName() if name in continued_messages: - message = continued_messages[name] + message + message = continued_messages[name] + " " + message + del continued_messages[name] if message[-2:] == "++": message = message[:-2] - if message[-1:] != " ": - message += " " continued_messages[name] = message msg(sender, "&6You can continue writing by using &e/an ") else: notes.append([name, message, time.time()]) save_notes() - msg(sender, "&eNew note:&6 "+message) + msg(sender, "&eNew note:&6 " + message) broadcast(an_permission, "&a%s just added a new note! &2Type /an" % name) except: - print(print_traceback()) + error(print_traceback()) @hook.event("player.PlayerJoinEvent", "monitor") -def on_an_join(event): - if not event.getPlayer().hasPermission(an_permission): - noperm(event.getPlayer()) +def on_an_join(event): + player = event.getPlayer() + if not player.hasPermission(an_permission): + noperm(player) return if len(notes) > 0: - msg(event.getPlayer(), "&cThere are currently %s open notes!" % len(notes)) + msg(player, "&cThere are currently %s open notes!" % len(notes)) elif len(notes) == 0: - msg(event.getPlayer(), "&aThere are currently no open notes!") \ No newline at end of file + msg(player, "&aThere are currently no open notes!") \ No newline at end of file From 487375dd077e1c9a3b601699e209c3831a084065 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Fri, 17 Apr 2015 00:10:21 +0200 Subject: [PATCH 05/10] Added loginsecurity module Added to make an easily adjustible version of LoginSecurity. LoginSecurity does not (seem to) support name changes since it retrieves the UUID used from a byte[] of "OfflinePlayer:" which means the UUID changes when a player changes their name. This should fix /rmpass. It uses BCrypt. I'll test this on the dev server (hoping bcrypt is installed) --- loginsecurity.py | 121 +++++++++++++++++++++++++++++++++++++++++++++++ main.py | 4 +- 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 loginsecurity.py 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..cfda6aa 100644 --- a/main.py +++ b/main.py @@ -73,7 +73,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"] = {} From 181c6ccfeb04f5030f0230dce93b8a76c43ecc56 Mon Sep 17 00:00:00 2001 From: jomo Date: Mon, 20 Apr 2015 20:39:00 +0200 Subject: [PATCH 06/10] show network AS in /check --- check.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/check.py b/check.py index 8bbfdeb..2129a3b 100644 --- a/check.py +++ b/check.py @@ -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: From 62569c736148aae34c9bafbfceb1033b88871daf Mon Sep 17 00:00:00 2001 From: jomo Date: Mon, 20 Apr 2015 21:02:07 +0200 Subject: [PATCH 07/10] use https for ipinfo.io --- check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check.py b/check.py index 2129a3b..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 {} From 1cac4c6ed5911371595c699f4a626e7ef16c1c50 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Tue, 28 Apr 2015 00:08:33 +0200 Subject: [PATCH 08/10] Added /tempadd command: temporary group membership Just a simpler way of writing /pex user group add * 604800 --- misc.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/misc.py b/misc.py index a530424..d2054a8 100644 --- a/misc.py +++ b/misc.py @@ -182,6 +182,7 @@ def eval_argument_thread(event): thread.exit() """ + @simplecommand("pyeval", usage = "[code..]", description = "Runs python [code..] and returns the result", @@ -192,6 +193,33 @@ def on_pyeval_command(sender, command, label, 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): """ From c451b2247b9f5446ef060dab7e22f5f8223c1211 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Wed, 29 Apr 2015 03:27:37 +0200 Subject: [PATCH 09/10] Added blockplacemods module Adds a few nifty features with per player customization: - Placing a slab automatically turns it upside-down - Placing a cauldron automatically fills it - Right clicking a cauldron with empty hand or redstone dust lowers water level --- blockplacemods.py | 118 ++++++++++++++++++++++++++++++++++++++++++++++ main.py | 2 + 2 files changed, 120 insertions(+) create mode 100644 blockplacemods.py 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/main.py b/main.py index cfda6aa..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 From 1afaec2a0804f466a02f063a33086ef857e63432 Mon Sep 17 00:00:00 2001 From: Dico200 Date: Wed, 29 Apr 2015 03:27:52 +0200 Subject: [PATCH 10/10] Small misc cleanup --- misc.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/misc.py b/misc.py index d2054a8..554d095 100644 --- a/misc.py +++ b/misc.py @@ -184,9 +184,9 @@ def eval_argument_thread(event): @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))) @@ -194,12 +194,12 @@ def on_pyeval_command(sender, command, label, args): @simplecommand("tempadd", - usage = " [duration]", - description = "Temporarily adds to for \n[duration] minutes. Defaults to 1 week.", - helpNoargs = True, - helpSubcmd = True, - amin = 2, - amax = 3) + 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!" @@ -256,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):