Advertisement
danhezee

Untitled

Jan 4th, 2013
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.25 KB | None | 0 0
  1. # maintained by triplefox
  2.  
  3. # Copyright (c) James Hofmann 2012.
  4.  
  5. # This file is part of pyspades.
  6.  
  7. # pyspades is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11.  
  12. # pyspades is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. # GNU General Public License for more details.
  16.  
  17. # You should have received a copy of the GNU General Public License
  18. # along with pyspades.  If not, see <http://www.gnu.org/licenses/>.
  19.  
  20. from twisted.internet.reactor import seconds
  21. from scheduler import Scheduler
  22. from commands import name, add, get_player, join_arguments, InvalidPlayer, admin, alias
  23.  
  24. REQUIRE_REASON = True
  25.  
  26. S_NO_VOTEKICK = 'No votekick in progress'
  27. S_DEFAULT_REASON = 'NO REASON GIVEN'
  28. S_IN_PROGRESS = 'Votekick already in progress'
  29. S_SELF_VOTEKICK = "You can't votekick yourself"
  30. S_NOT_ENOUGH_PLAYERS = "There aren't enough players to vote"
  31. S_VOTEKICK_IMMUNE = "You can't votekick this player"
  32. S_NOT_YET = "You can't start another votekick yet!"
  33. S_NEED_REASON = 'You must provide a reason for the votekick'
  34. S_CANT_CANCEL = "You didn't start the votekick!"
  35. S_YES = '{player} voted YES'
  36. S_ENDED = 'Votekick for {victim} has ended. {result}'
  37. S_RESULT_TIMED_OUT = 'Votekick timed out'
  38. S_RESULT_CANCELLED = 'Cancelled'
  39. S_RESULT_BANNED = 'Banned by admin'
  40. S_RESULT_KICKED = 'Kicked by admin'
  41. S_RESULT_INSTIGATOR_KICKED = 'Instigator kicked by admin'
  42. S_RESULT_LEFT = '{victim} left during votekick'
  43. S_RESULT_INSTIGATOR_LEFT = 'Instigator {instigator} left'
  44. S_RESULT_PASSED = 'Player kicked'
  45. S_ANNOUNCE_IRC = '* {instigator} started a votekick against player {victim}. ' \
  46.     'Reason: {reason}'
  47. S_ANNOUNCE = '{instigator} started a VOTEKICK against {victim}. Say /Y to agree'
  48. S_ANNOUNCE_SELF = 'You started a votekick against {victim}. Say /CANCEL to ' \
  49.     'stop it'
  50. S_UPDATE = '{instigator} is votekicking {victim}. /Y to vote ({needed} left)'
  51. S_REASON = 'Reason: {reason}'
  52.  
  53. class VotekickFailure(Exception):
  54.     pass
  55.  
  56. @name('votekick')
  57. def start_votekick(connection, *args):
  58.     protocol = connection.protocol
  59.     if connection not in protocol.players:
  60.         raise KeyError()
  61.     player = connection
  62.    
  63.     if protocol.votekick_enabled == False:
  64.         return "Votekicking disabled"
  65.     if player.votekick_enabled == False:
  66.         return "You are not allowed to initiate a votekick."
  67.    
  68.     if not args:
  69.         if protocol.votekick:
  70.             # player requested votekick info
  71.             protocol.votekick.send_chat_update(player)
  72.             return
  73.         raise ValueError()
  74.    
  75.     value = args[0]
  76.     try:
  77.         # vanilla aos behavior
  78.         victim = get_player(protocol, '#' + value)
  79.     except InvalidPlayer:
  80.         victim = get_player(protocol, value)
  81.     reason = join_arguments(args[1:])
  82.    
  83.     try:
  84.         # attempt to start votekick
  85.         votekick = Votekick.start(player, victim, reason)
  86.         protocol.votekick = votekick
  87.     except VotekickFailure as err:
  88.         return str(err)
  89.  
  90. @name('cancel')
  91. def cancel_votekick(connection):
  92.     protocol = connection.protocol
  93.     votekick = protocol.votekick
  94.     if not votekick:
  95.         return S_NO_VOTEKICK
  96.     if connection in protocol.players:
  97.         player = connection
  98.         if (player is not votekick.instigator and not player.admin and
  99.             not player.rights.cancel):
  100.             return S_CANT_CANCEL
  101.    
  102.     votekick.end(S_RESULT_CANCELLED)
  103.  
  104. @name('y')
  105. def vote_yes(connection):
  106.     protocol = connection.protocol
  107.     if connection not in protocol.players:
  108.         raise KeyError()
  109.     player = connection
  110.    
  111.     votekick = protocol.votekick
  112.     if not votekick:
  113.         return S_NO_VOTEKICK
  114.    
  115.     votekick.vote(player)
  116.  
  117. @alias('tvk')
  118. @admin
  119. def togglevotekick(connection, *args):
  120.     protocol = connection.protocol
  121.     if len(args) == 0:
  122.         protocol.votekick_enabled = not protocol.votekick_enabled
  123.         return "Votekicking globally %s." % ['disabled', 'enabled'][protocol.votekick_enabled]
  124.     try:
  125.         player = get_player(protocol, '#' + args[0])
  126.     except InvalidPlayer:
  127.         player = get_player(protocol, args[0])
  128.     player.votekick_enabled = not player.votekick_enabled
  129.     return "Votekicking is %s for %s." % (['disabled', 'enabled'][player.votekick_enabled], player.name)
  130.  
  131. add(start_votekick)
  132. add(cancel_votekick)
  133. add(vote_yes)
  134. add(togglevotekick)
  135.  
  136. class Votekick(object):
  137.     duration = 120.0 # 2 minutes
  138.     interval = 2 * 60.0 # 3 minutes
  139.     ban_duration = 15.0
  140.     public_votes = True
  141.     schedule = None
  142.    
  143.     def _get_votes_remaining(self):
  144.         return self.protocol.get_required_votes() - len(self.votes) + 1
  145.     votes_remaining = property(_get_votes_remaining)
  146.    
  147.     @classmethod
  148.     def start(cls, instigator, victim, reason = None):
  149.         protocol = instigator.protocol
  150.         last_votekick = instigator.last_votekick
  151.         reason = reason.strip() if reason else None
  152.         if protocol.votekick:
  153.             raise VotekickFailure(S_IN_PROGRESS)
  154.         elif instigator is victim:
  155.             raise VotekickFailure(S_SELF_VOTEKICK)
  156.         elif protocol.get_required_votes() <= 0:
  157.             raise VotekickFailure(S_NOT_ENOUGH_PLAYERS)
  158.         elif victim.admin or victim.rights.cancel:
  159.             raise VotekickFailure(S_VOTEKICK_IMMUNE)
  160.         elif not instigator.admin and (last_votekick is not None and
  161.             seconds() - last_votekick < cls.interval):
  162.             raise VotekickFailure(S_NOT_YET)
  163.         elif REQUIRE_REASON and not reason:
  164.             raise VotekickFailure(S_NEED_REASON)
  165.        
  166.         result = protocol.on_votekick_start(instigator, victim, reason)
  167.         if result is not None:
  168.             raise VotekickFailure(result)
  169.        
  170.         reason = reason or S_DEFAULT_REASON
  171.         return cls(instigator, victim, reason)
  172.    
  173.     def __init__(self, instigator, victim, reason):
  174.         self.protocol = protocol = instigator.protocol
  175.         self.instigator = instigator
  176.         self.victim = victim
  177.         self.reason = reason
  178.         self.votes = {instigator : True}
  179.         self.ended = False
  180.        
  181.         protocol.irc_say(S_ANNOUNCE_IRC.format(instigator = instigator.name,
  182.             victim = victim.name, reason = self.reason))
  183.         protocol.send_chat(S_ANNOUNCE.format(instigator = instigator.name,
  184.             victim = victim.name), sender = instigator)
  185.         protocol.send_chat(S_REASON.format(reason = self.reason),
  186.             sender = instigator)
  187.         instigator.send_chat(S_ANNOUNCE_SELF.format(victim = victim.name))
  188.        
  189.         schedule = Scheduler(protocol)
  190.         schedule.call_later(self.duration, self.end, S_RESULT_TIMED_OUT)
  191.         schedule.loop_call(30.0, self.send_chat_update)
  192.         self.schedule = schedule
  193.    
  194.     def vote(self, player):
  195.         if self.victim is player:
  196.             return
  197.         elif player in self.votes:
  198.             return
  199.         if self.public_votes:
  200.             self.protocol.send_chat(S_YES.format(player = player.name))
  201.         self.votes[player] = True
  202.         if self.votes_remaining <= 0:
  203.             # vote passed, ban or kick accordingly
  204.             victim = self.victim
  205.             self.end(S_RESULT_PASSED)
  206.             print '%s votekicked' % victim.name
  207.             if self.ban_duration > 0.0:
  208.                 victim.ban(self.reason, self.ban_duration)
  209.             else:
  210.                 victim.kick(silent = True)
  211.    
  212.     def release(self):
  213.         self.instigator = None
  214.         self.victim = None
  215.         self.votes = None
  216.         if self.schedule:
  217.             self.schedule.reset()
  218.         self.schedule = None
  219.         self.protocol.votekick = None
  220.        
  221.     def end(self, result):
  222.         self.ended = True
  223.         message = S_ENDED.format(victim = self.victim.name, result = result)
  224.         self.protocol.send_chat(message, irc = True)
  225.         if not self.instigator.admin:
  226.             self.instigator.last_votekick = seconds()
  227.         self.protocol.on_votekick_end()
  228.         self.release()
  229.    
  230.     def send_chat_update(self, target = None):
  231.         # send only to target player if provided, otherwise broadcast to server
  232.         target = target or self.protocol
  233.         target.send_chat(S_UPDATE.format(instigator = self.instigator.name,
  234.             victim = self.victim.name, needed = self.votes_remaining))
  235.         target.send_chat(S_REASON.format(reason = self.reason))
  236.  
  237. def apply_script(protocol, connection, config):
  238.     Votekick.ban_duration = config.get('votekick_ban_duration', 15.0)
  239.     Votekick.public_votes = config.get('votekick_public_votes', True)
  240.     required_percentage = config.get('votekick_percentage', 25.0)
  241.    
  242.     class VotekickProtocol(protocol):
  243.         votekick = None
  244.         votekick_enabled = True
  245.        
  246.         def get_required_votes(self):
  247.             # votekicks are invalid if this returns <= 0
  248.             player_count = sum(not player.disconnected for player in
  249.                 self.players.itervalues()) - 1
  250.             return int(player_count / 100.0 * required_percentage)
  251.        
  252.         def on_map_leave(self):
  253.             if self.votekick:
  254.                 self.votekick.release()
  255.             protocol.on_map_leave(self)
  256.        
  257.         def on_ban(self, banee, reason, duration):
  258.             votekick = self.votekick
  259.             if votekick and votekick.victim is self:
  260.                 votekick.end(S_RESULT_BANNED)
  261.             protocol.on_ban(self, connection, reason, duration)
  262.        
  263.         def on_votekick_start(self, instigator, victim, reason):
  264.             pass
  265.        
  266.         def on_votekick_end(self):
  267.             pass
  268.    
  269.     class VotekickConnection(connection):
  270.         last_votekick = None
  271.         votekick_enabled = True
  272.        
  273.         def on_disconnect(self):
  274.             votekick = self.protocol.votekick
  275.             if votekick:
  276.                 if votekick.victim is self:
  277.                     # victim leaves, gets votekick ban
  278.                     reason = votekick.reason
  279.                     votekick.end(S_RESULT_LEFT.format(victim = self.name))
  280.                     self.ban(reason, Votekick.ban_duration)
  281.                 elif votekick.instigator is self:
  282.                     # instigator leaves, votekick is called off
  283.                     s = S_RESULT_INSTIGATOR_LEFT.format(instigator = self.name)
  284.                     votekick.end(s)
  285.                 else:
  286.                     # make sure we still have enough players
  287.                     votekick.votes.pop(self, None)
  288.                     if votekick.votes_remaining <= 0:
  289.                         votekick.end(S_NOT_ENOUGH_PLAYERS)
  290.             connection.on_disconnect(self)
  291.        
  292.         def kick(self, reason = None, silent = False):
  293.             votekick = self.protocol.votekick
  294.             if votekick:
  295.                 if votekick.victim is self:
  296.                     votekick.end(S_RESULT_KICKED)
  297.                 elif votekick.instigator is self:
  298.                     votekick.end(S_RESULT_INSTIGATOR_KICKED)
  299.             connection.kick(self, reason, silent)
  300.    
  301.     return VotekickProtocol, VotekickConnection
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement