CTBAN with time and menu

A place for requesting new Source.Python plugins to be made for your server.

Please request only one plugin per thread.
PrefixV1
Junior Member
Posts: 1
Joined: Mon Sep 12, 2016 11:08 am

CTBAN with time and menu

Postby PrefixV1 » Mon Sep 12, 2016 11:11 am

Hey guys I'm here to request a plugin that allows me to bring up a Ctban menu on !ctban command.
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: CTBAN with time and menu

Postby Ayuto » Tue Sep 13, 2016 10:55 am

Something like this?

Syntax: Select all

# ==============================================================================
# >> IMPORTS
# ==============================================================================
# Python
import collections
import time
import pickle

# Source.Python
from commands.client import ClientCommandFilter
from commands.typed import TypedSayCommand
from commands import CommandReturn

from menus import PagedMenu
from menus import PagedOption

from players.entity import Player
from players.helpers import uniqueid_from_index
from players.helpers import index_from_uniqueid

from listeners import OnLevelEnd
from paths import CUSTOM_DATA_PATH
from events import Event
from filters.players import PlayerIter
from messages import SayText2
from colors import RED



# ==============================================================================
# >> CONSTANTS
# ==============================================================================
# Ban durations
DURATIONS = {
0: 'permanently',
5 * 60: '5 minutes',
15 * 60: '15 minutes',
30 * 60: '30 minutes',
60 * 60: '1 hour',
3 * 60 * 60: '3 hours',
6 * 60 * 60: '6 hours',
12 * 60 * 60: '12 hours',
24 * 60 * 60: '1 day',
3 * 24 * 60 * 60: '3 days',
7 * 24 * 60 * 60: '7 days',
}

# Path to the ban database
BAN_DATABASE = CUSTOM_DATA_PATH / 'ctban' / 'bans.db'

# Number of leavers to track
TRACKED_LEAVERS_NO = 5

# Number of freekillers to track
TRACKED_FREEKILLERS_NO = 5

# Prefix for messages
MESSAGE_PREFIX = '{}[CTBAN] \1'.format(RED)


# ==============================================================================
# >> CLASSES
# ==============================================================================
class BanSystem(dict):
def __init__(self):
self.leavers = collections.deque(maxlen=TRACKED_LEAVERS_NO)
self.freekillers = collections.deque(maxlen=TRACKED_FREEKILLERS_NO)
try:
with BAN_DATABASE.open('rb') as f:
self.update(pickle.load(f))
except FileNotFoundError:
pass

def save(self):
try:
BAN_DATABASE.parent.makedirs()
except FileExistsError:
pass

with BAN_DATABASE.open('wb') as f:
pickle.dump(self, f)

def add_ban(self, uniqueid, duration, name):
self[uniqueid] = (0 if duration == 0 else time.time() + duration, name)
try:
index = index_from_uniqueid(uniqueid)
except ValueError:
pass
else:
player = Player(index)
player.team = 2

try:
self.leavers.remove((uniqueid, name))
except ValueError:
pass

try:
self.freekillers.remove((uniqueid, name))
except ValueError:
pass

def is_banned(self, uniqueid):
try:
ban_time, name = self[uniqueid]
except KeyError:
return False

return ban_time == 0 or time.time() < ban_time

def remove_ban(self, uniqueid):
return self.pop(uniqueid, (None, None))

def cleanup(self):
now = time.time()
for uniqueid, (ban_time, name) in tuple(self.items()):
if ban_time != 0 and now >= ban_time:
del self[uniqueid]

self.save()

def track_leaver(self, uniqueid, name):
if self.is_banned(uniqueid):
return

data = (uniqueid, name)
if data not in self.leavers:
self.leavers.append(data)

def track_freekiller(self, uniqueid, name):
if self.is_banned(uniqueid):
return

data = (uniqueid, name)
if data not in self.freekillers:
self.freekillers.append(data)

ban_system = BanSystem()


# ==============================================================================
# >> BAN SYSTEM UPDATER
# ==============================================================================
@OnLevelEnd
def on_level_end():
ban_system.cleanup()


# ==============================================================================
# >> ADMIN BAN MENU
# ==============================================================================
admin_ban_menu = PagedMenu(
[
PagedOption('Ban CT', 1),
PagedOption('Ban leaver', 2),
PagedOption('Ban freekiller', 3),
PagedOption('Unban player', 4)
],
title='CTBAN'
)

@admin_ban_menu.register_select_callback
def on_admin_ban_menu_select(menu, index, option):
if option.value == 1:
return ct_menu
elif option.value == 2:
return leaver_menu
elif option.value == 3:
return freekillers_menu
elif option.value == 4:
return unban_menu


# ==============================================================================
# >> PLAYER MENU
# ==============================================================================
ct_menu = PagedMenu(
title='Ban CT',
parent_menu=admin_ban_menu)

freekillers_menu = PagedMenu(
title='Ban freekiller',
parent_menu=admin_ban_menu
)

leaver_menu = PagedMenu(
title='Ban leaver',
parent_menu=admin_ban_menu
)

@ct_menu.register_build_callback
def on_active_player_menu_build(menu, index):
menu.clear()
for player in PlayerIter(['ct']):
if player.index == index:
continue

menu.append(PagedOption(player.name, (player.uniqueid, player.name)))

@leaver_menu.register_build_callback
def on_leaver_menu_build(menu, index):
menu.clear()
for uniqueid, name in ban_system.leavers:
menu.append(PagedOption(name, (uniqueid, name)))

@freekillers_menu.register_build_callback
def on_freekillers_menu_build(menu, index):
menu.clear()
for uniqueid, name in ban_system.freekillers:
menu.append(PagedOption(name, (uniqueid, name)))

@leaver_menu.register_select_callback
@ct_menu.register_select_callback
@freekillers_menu.register_select_callback
def on_active_player_menu_select(menu, index, option):
return create_ban_time_menu(menu, *option.value)


# ==============================================================================
# >> UNBAN MENU
# ==============================================================================
unban_menu = PagedMenu(
title='Unban player',
parent_menu=admin_ban_menu)

@unban_menu.register_build_callback
def on_unban_menu_build(menu, index):
menu.clear()
bans = tuple(ban_system.items())
sorted_bans = sorted(bans, key=lambda key: key[1][1])
for uniqueid, (ban_time, name) in sorted_bans:
menu.append(PagedOption('{} ({})'.format(name, uniqueid), uniqueid))

@unban_menu.register_select_callback
def on_unban_menu_select(menu, index, option):
ban_time, name = ban_system.remove_ban(option.value)
if ban_time is not None:
SayText2(
MESSAGE_PREFIX + '{} has been unbanned from the CT team.'.format(
name)).send()


# ==============================================================================
# >> BAN TIME MENU
# ==============================================================================
def create_ban_time_menu(parent_menu, uniqueid, name):
ban_time_menu = PagedMenu(title='Ban time', parent_menu=parent_menu)
for duration, display_name in sorted(DURATIONS.items()):
ban_time_menu.append(
PagedOption(display_name, (uniqueid, name, duration)))

ban_time_menu.select_callback = on_ban_time_menu_select
return ban_time_menu

def on_ban_time_menu_select(menu, index, option):
uniqueid, name, duration = option.value
ban_system.add_ban(uniqueid, duration, name)
SayText2(
MESSAGE_PREFIX + '{} has been banned from the CT team ({}).'.format(
name, option.text)).send()


# ==============================================================================
# >> SAY COMMANDS
# ==============================================================================
@TypedSayCommand('!ctban', 'ctban.open')
def on_ctban_open(info):
admin_ban_menu.send(info.index)
return CommandReturn.BLOCK


# ==============================================================================
# >> EVENTS
# ==============================================================================
@Event('player_disconnect')
def on_player_disconnect(event):
player = Player.from_userid(event['userid'])
ban_system.track_leaver(player.uniqueid, player.name)

@Event('player_death')
def on_player_death(event):
attacker_id = event['attacker']
if event['userid'] == attacker_id:
return

try:
attacker = Player.from_userid(attacker_id)
except ValueError:
return

ban_system.track_freekiller(attacker.uniqueid, attacker.name)

@ClientCommandFilter
def on_client_command(command, index):
if command[0].lower() != 'jointeam':
return

if not (len(command) == 1 or command[1] not in ('1', '2')):
return

uniqueid = uniqueid_from_index(index)
if not ban_system.is_banned(uniqueid):
return

SayText2(
MESSAGE_PREFIX + 'You have been banned from the CT team.').send(index)
return CommandReturn.BLOCK

You need to grant yourself the permission "ctban.open":
http://wiki.sourcepython.com/general/config-auth.html
User avatar
iPlayer
Developer
Posts: 590
Joined: Sat Nov 14, 2015 8:37 am
Location: Moscow
Contact:

Re: CTBAN with time and menu

Postby iPlayer » Tue Sep 13, 2016 1:17 pm

Ayuto, what happens if player chooses autobalance (0)? If it's a jail server (which I assume it is), autobalance would always send you to CT team as there're always much more T players than CT.

Autobalancing also happens not only when you do jointeam 0, but also when you call jointeam with no or with invalid arguments.
Image /id/its_iPlayer
My plugins: Map Cycle • Killstreaker • DeadChat • Infinite Jumping • TripMines • AdPurge • Bot Damage • PLRBots • Entity AntiSpam

Hail, Companion. [...] Hands to yourself, sneak thief. Image
User avatar
Kill
Member
Posts: 88
Joined: Wed Aug 31, 2016 10:05 pm

Re: CTBAN with time and menu

Postby Kill » Tue Sep 13, 2016 1:25 pm

iPlayer wrote:Ayuto, what happens if player chooses autobalance (0)? If it's a jail server (which I assume it is), autobalance would always send you to CT team as there're always much more T players than CT.

Autobalancing also happens not only when you do jointeam 0, but also when you call jointeam with no or with invalid arguments.


I've never seen a JB server with autobalance, if someone does so, its his/her problem, not the plugin maker.
User avatar
iPlayer
Developer
Posts: 590
Joined: Sat Nov 14, 2015 8:37 am
Location: Moscow
Contact:

Re: CTBAN with time and menu

Postby iPlayer » Tue Sep 13, 2016 1:26 pm

I've never seen a JB server with autobalance, if someone does so, it his/her problem, not the plugin maker.

There's always an autobalance option in team selection screen.
Banned players can possibly exploit it to bypass the ban.
Image /id/its_iPlayer
My plugins: Map Cycle • Killstreaker • DeadChat • Infinite Jumping • TripMines • AdPurge • Bot Damage • PLRBots • Entity AntiSpam

Hail, Companion. [...] Hands to yourself, sneak thief. Image
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: CTBAN with time and menu

Postby Ayuto » Tue Sep 13, 2016 1:28 pm

Thanks for pointing that out! I will update the plugin tomorrow.
User avatar
iPlayer
Developer
Posts: 590
Joined: Sat Nov 14, 2015 8:37 am
Location: Moscow
Contact:

Re: CTBAN with time and menu

Postby iPlayer » Tue Sep 13, 2016 1:30 pm

Okay, to avoid confusion, I should've probably called it "auto-selection option", not autobalance.

I didn't mean that thing that switches players from time to time automatically, I specifically meant "auto-select" button when you press M or join the server.
Image /id/its_iPlayer
My plugins: Map Cycle • Killstreaker • DeadChat • Infinite Jumping • TripMines • AdPurge • Bot Damage • PLRBots • Entity AntiSpam

Hail, Companion. [...] Hands to yourself, sneak thief. Image
User avatar
Kill
Member
Posts: 88
Joined: Wed Aug 31, 2016 10:05 pm

Re: CTBAN with time and menu

Postby Kill » Tue Sep 13, 2016 1:39 pm

iPlayer wrote:Okay, to avoid confusion, I should've probably called it "auto-selection option", not autobalance.

I didn't mean that thing that switches players from time to time automatically, I specifically meant "auto-select" button when you press M or join the server.


You're right, just tested it, You can join CT by using the auto-selection
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: CTBAN with time and menu

Postby Ayuto » Wed Sep 14, 2016 10:37 am

Okay, I have updated the script above to handle auto-selection as well.
User avatar
iPlayer
Developer
Posts: 590
Joined: Sat Nov 14, 2015 8:37 am
Location: Moscow
Contact:

Re: CTBAN with time and menu

Postby iPlayer » Wed Sep 14, 2016 5:09 pm

Great job! Now the only thing left is invalid arguments :D

Code: Select all

jointeam exploited

Image


Edit: Possible solution:

Syntax: Select all

if len(command) != 1:
return CommandReturn.BLOCK

try:
new_team = int(command[1])
except ValueError:
return CommandReturn.BLOCK

# other checks


Edit2: I'd also replace SayText2 with TextMsg (center of the screen) and would do this just before blocking the command:

Syntax: Select all

VGUIMenu('team', show=True).send(index)


Also, if somebody did manage to exploit this plugin, maybe register a player_team event listener and move such players back?
Image /id/its_iPlayer
My plugins: Map Cycle • Killstreaker • DeadChat • Infinite Jumping • TripMines • AdPurge • Bot Damage • PLRBots • Entity AntiSpam

Hail, Companion. [...] Hands to yourself, sneak thief. Image
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: CTBAN with time and menu

Postby Ayuto » Wed Sep 14, 2016 5:56 pm

Updated :D

Also updated the add_ban method to remove a banned player from leavers and freekillers. Moreover, track_leaver and track_freekiller will no longer track banned players.

iPlayer wrote:Also, if somebody did manage to exploit this plugin, maybe register a player_team event listener and move such players back?

I was also thinking about using a hook to completely disallow that, but I try to avoid hooks if possible.
User avatar
Kill
Member
Posts: 88
Joined: Wed Aug 31, 2016 10:05 pm

Re: CTBAN with time and menu

Postby Kill » Wed Sep 14, 2016 10:50 pm

Ayuto wrote:Updated :D

Also updated the add_ban method to remove a banned player from leavers and freekillers. Moreover, track_leaver and track_freekiller will no longer track banned players.

iPlayer wrote:Also, if somebody did manage to exploit this plugin, maybe register a player_team event listener and move such players back?

I was also thinking about using a hook to completely disallow that, but I try to avoid hooks if possible.


What about this: Play a sound, and show the selection menu again, instead of SayText use TextMsg?
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: CTBAN with time and menu

Postby Ayuto » Thu Sep 15, 2016 6:40 am

Well, that's a matter of preference. If anyone wants that, he is free to use my code and adapt it.
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: CTBAN with time and menu

Postby Ayuto » Thu Sep 15, 2016 11:09 am

I have created a GitHub repo:
https://github.com/Ayuto/CT-Ban

If anyone wants to make changes, he can create a PR or if he wants to develop that further, I can also add him as a collaborator.

Return to “Plugin Requests”

Who is online

Users browsing this forum: No registered users and 20 guests