class OscarTimeoutSocket(common.socket): def tryconnect(self, ips, on_connect, on_fail, timeout = 2.0): self._connectedonce = False info('tryconnect Y=%r, N=%r', on_connect, on_fail) self.ips = self.iptuples(ips) if not callable(on_connect) or not callable(on_fail): raise TypeError( 'on_connect and on_fail must be callables' ) self.on_connect = on_connect self.on_fail = on_fail self.timetowait = timeout self._tryagain(timeout) def tryaccept(self, addr, on_connect, on_fail, timeout = 1.5): self._connectedonce = False info('tryaccept Y=%r, N=%r', on_connect, on_fail) self.ips = () self.on_connect = on_connect self.on_fail = on_fail info('listening for a connection at %s:%d', *addr) self.bind( addr ) self.listen(1) if timeout: info('timeout in %r secs', timeout) def dotimeout(): info('TIMEOUT. calling %r', self.on_fail) self.on_fail() self.timeout = Timer(timeout, dotimeout) self.timeout.start() def _tryagain(self, timetowait): # Try the next IP. addr = self.ips.pop(0) if len(self.ips) > 0: timeoutfunc = partial(self._tryagain, timetowait) else: # This is the last one. timeoutfunc = self.on_fail self.timeout = Timer(timetowait, timeoutfunc) info('%r attempting conn: %s:%d', self, *addr) self.make_socket() self.connect(addr, error=timeoutfunc) info('timeout is %r seconds...', timetowait) if self.timeout is not None: self.timeout.start() def handle_expt(self): info('handle_expt in %r', self) self.handle_disconnect() def handle_error(self, e=None): info('handle_error in %r', self) import traceback traceback.print_exc() if not self._connectedonce: self.handle_disconnect() else: self.close() def handle_disconnect(self): self.cancel_timeout() self.close() if len(self.ips) > 0: info('got an error, trying next ip immediately: ' + \ repr(self.ips[0])) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self._tryagain(self.timetowait) elif not self._connectedonce: info('no more ips to attempt, calling on_fail (%r)', self.on_fail) self.on_fail() def handle_connect(self): info('connected!') self.cancel_timeout() #self.handle_disconnect = lambda: None self._connectedonce = True self.on_connect() self.on_fail = Sentinel def handle_accept(self): self.cancel_timeout() conn, address = self.accept() info('%r connection accepted (%r), canceling timeout and calling %r', self, address, self.on_connect) self._connectedonce = True self.on_connect(conn) def cancel_timeout(self): # Cancel any timeout. if hasattr(self, 'timeout') and self.timeout is not None: self.timeout.cancel() self.timeout = None def iptuples(self, ips): if not hasattr(ips, '__len__'): raise TypeError('ips must be (host, port) or [(host,port), (host,port)]') if not hasattr(ips[0], '__len__'): ips = tuple([ips]) # ips is now a sequence of (host, port) tuples assert all(isinstance(a, basestring) and isinstance(p, int) for a, p in ips) return ips def __repr__(self): try: pn = self.getpeername() except Exception: pn = None return '<TimeoutSocket peername=%r ips=%r at 0x%08x>' % (pn, getattr(self, 'ips', None), id(self))
class OscarTimeoutSocket(common.socket): def tryconnect(self, ips, on_connect, on_fail, timeout=2.0): self._connectedonce = False info('tryconnect Y=%r, N=%r', on_connect, on_fail) self.ips = self.iptuples(ips) if not callable(on_connect) or not callable(on_fail): raise TypeError('on_connect and on_fail must be callables') self.on_connect = on_connect self.on_fail = on_fail self.timetowait = timeout self._tryagain(timeout) def tryaccept(self, addr, on_connect, on_fail, timeout=1.5): self._connectedonce = False info('tryaccept Y=%r, N=%r', on_connect, on_fail) self.ips = () self.on_connect = on_connect self.on_fail = on_fail info('listening for a connection at %s:%d', *addr) self.bind(addr) self.listen(1) if timeout: info('timeout in %r secs', timeout) def dotimeout(): info('TIMEOUT. calling %r', self.on_fail) self.on_fail() self.timeout = Timer(timeout, dotimeout) self.timeout.start() def _tryagain(self, timetowait): # Try the next IP. addr = self.ips.pop(0) if len(self.ips) > 0: timeoutfunc = partial(self._tryagain, timetowait) else: # This is the last one. timeoutfunc = self.on_fail self.timeout = Timer(timetowait, timeoutfunc) info('%r attempting conn: %s:%d', self, *addr) self.make_socket() self.connect(addr, error=timeoutfunc) info('timeout is %r seconds...', timetowait) if self.timeout is not None: self.timeout.start() def handle_expt(self): info('handle_expt in %r', self) self.handle_disconnect() def handle_error(self, e=None): info('handle_error in %r', self) import traceback traceback.print_exc() if not self._connectedonce: self.handle_disconnect() else: self.close() def handle_disconnect(self): self.cancel_timeout() self.close() if len(self.ips) > 0: info('got an error, trying next ip immediately: ' + \ repr(self.ips[0])) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self._tryagain(self.timetowait) elif not self._connectedonce: info('no more ips to attempt, calling on_fail (%r)', self.on_fail) self.on_fail() def handle_connect(self): info('connected!') self.cancel_timeout() #self.handle_disconnect = lambda: None self._connectedonce = True self.on_connect() self.on_fail = Sentinel def handle_accept(self): self.cancel_timeout() conn, address = self.accept() info('%r connection accepted (%r), canceling timeout and calling %r', self, address, self.on_connect) self._connectedonce = True self.on_connect(conn) def cancel_timeout(self): # Cancel any timeout. if hasattr(self, 'timeout') and self.timeout is not None: self.timeout.cancel() self.timeout = None def iptuples(self, ips): if not hasattr(ips, '__len__'): raise TypeError( 'ips must be (host, port) or [(host,port), (host,port)]') if not hasattr(ips[0], '__len__'): ips = tuple([ips]) # ips is now a sequence of (host, port) tuples assert all( isinstance(a, basestring) and isinstance(p, int) for a, p in ips) return ips def __repr__(self): try: pn = self.getpeername() except Exception: pn = None return '<TimeoutSocket peername=%r ips=%r at 0x%08x>' % ( pn, getattr(self, 'ips', None), id(self))
class TimeoutSocketOne(common.socket): ''' single socket timeout socket ''' @lock def try_connect(self, address, succ, fail, time_to_wait, provide_init): provide_init(self) self.real_success = succ self.fail = fail self.dead = False self.data = None #make new socket self.make_socket() self.timeoutvalid = True self.timeout = Timer(time_to_wait, self.handle_timeout) self.timeout.start() print '*'*40 from util import funcinfo print funcinfo(self.connect) print '*'*40 self.connect(address, error=self.do_fail) #do connect with callback #success indicates that the socket started, but guarantees nothing #error indicates that there was a problem, should try to close + do fail def succ(self): info('succ') self.real_success() @lock def do_fail(self, *a, **k): info('do_fail') if self.timeout is not None: self.timeout.cancel() self.timeout = None self.timeoutvalid = False self.close() print a, k self.fail(*a,**k) @lock def handle_connect(self): info('CONNECT') if self.timeout is not None: self.timeout.cancel() self.timeout = None self.timeoutvalid = False self.succ() #cancel timeout #success @lock def handle_timeout(self): info('TIMEOUT') #mark as dead if self.timeoutvalid: if self.timeout is not None: self.timeout.cancel() self.timeout = None self.timeoutvalid = False self.close() self.dead = True self.fail() @lock def collect_incoming_data(self, data): self.data += data @lock def __error(self): olddead = self.dead self.dead = True if self.timeout is not None: self.timeout.cancel() self.timeout = None self.timeoutvalid = False #cancel timeout self.close() if not olddead: self.fail() def handle_error(self, e=None): info('ERROR: %r', e) import traceback;traceback.print_exc() self.__error() def handle_expt(self): info('EXPT') self.__error() def handle_close(self): info('CLOSE') self.__error()
class Vote: def __init__(self, bot): self._bot = bot self._current_room_users = self._sort_user_by_join_time(self._bot.users.all) self._has_voted = [] # user handle of the users who voted self._votes = [] # list of tuple(User, vote) self._user_to_vote = None self._vote_session = 0 self._vote_type = '' self._vote_timer = None @property def is_active(self): """ Check for active vote session. :return: True if active vote session. :rtype: bool """ return self._has_active_session() @property def has_voted(self): """ Returns a list of user handles that have already voted. :return: A list of user handles. :rtype: list """ return self._has_voted @property def vote_user(self): """ Return the User object of the user up for vote. :return: The User object of the user up for vote. :rtype: User """ return self._user_to_vote @property def active_vote_type(self): """ Return the type of vote session. :return: Type of vote session. :rtype: str """ return self._vote_type def vote(self, user, vote): """ Add a vote to the vote session. :param user: A User object. :type user: User :param vote: A yes/no vote. :type vote: str :return: True if the vote was accepted. :rtype: bool """ if self._can_vote(user): if vote in YES or vote in NO: # there might not be a reason to store the user self._votes.append((user, vote)) self._has_voted.append(user.handle) return True return False @staticmethod def can_start(user, seconds=300): """ Helper method to check if a user is allowed to start the vote session. The intention was, that this method should be called before start(), to check if the user wanting to start the vote session, have been in the room long enough. :param user: The user to check. :type user: User :param seconds: The time the user must have been in the room. :type seconds: int :return: True if the user is allowed to start the session. :rtype: bool """ now = datetime.now() dif = now - user.join_time if dif.seconds > seconds: return True return False def start(self, user_to_vote, session, vote_type): """ Start the voting session. :param user_to_vote: The user up for voting. :type user_to_vote: User :param session: The session time in seconds. :type session: int :param vote_type: The type of vote. :type vote_type: str :return: True if the vote session was started :rtype: bool """ log.debug('%s, session=%s, vote_type=%s' % (user_to_vote, session, vote_type)) if not self._has_active_session(): self._user_to_vote = user_to_vote self._vote_session = session self._vote_type = vote_type # start the timer self._vote_timer = Timer() self._vote_timer.start(self._decide_vote, self._vote_session) return True return False def cancel(self): """ Cancel the vote session. :return: True if canceled. :rtype: bool """ if self._has_active_session(): return self._vote_timer.cancel() return False def _sort_user_by_join_time(self, users): # only allow users who have been in the room # for more than 5 minutes(300seconds) to vote sorted_users = {} now = datetime.now() for handle in users: dif = now - users[handle].join_time if dif.seconds > 300: # do not add the bot itself if handle != self._bot.users.client.handle: sorted_users[handle] = users[handle] return sorted_users def _can_vote(self, user): # was the user among the room users # when the vote session was started if user.handle in self._current_room_users: # has the user already voted if user.handle not in self._has_voted: return True return False def _has_active_session(self): # is there an active vote session running if self._vote_timer is not None: if isinstance(self._vote_timer, Timer): if self._vote_timer.is_alive: return True return False def _was_vote_yes(self): # the result of the votes, True if yes, False on tie or no # Thanks to notnola (https://github.com/notnola/pinychat) yes = 0 no = 0 for _, vote in self._votes: if vote in YES: yes += 1 else: no += 1 return yes > no def _calculate_vote_percentage(self): # calculate the voting percentage return len(self._has_voted) * 100 / len(self._current_room_users) def _decide_vote(self): # decide based on vote percentage and votes percentage = self._calculate_vote_percentage() # at least 1/3 or the room should have voted. maybe adjust this if percentage >= 33: if self._was_vote_yes(): self._bot.responder('With %s voters (%s%%) the room has decided to %s %s.' % (len(self._has_voted), percentage, self._vote_type, self._user_to_vote.nick)) self._vote_action() else: self._bot.responder('With %s voters (%s%%) the room has decided NOT to %s %s.' % (len(self._has_voted), percentage, self._vote_type, self._user_to_vote.nick)) else: self._bot.responder('With %s voters (%s%%) there were not ' 'enough votes to make a decision.' % (len(self._has_voted), percentage)) def _vote_action(self): # initiate the action of the vote, based on vote type user = self._bot.users.search(self._user_to_vote.handle) if user is None: user = self._bot.users.search_by_nick(self._user_to_vote.nick) log.debug('%s' % user) if user is not None: if self._vote_type == 'ban': self._bot.send_ban_msg(user.handle) elif self._vote_type == 'kick': self._bot.send_kick_msg(user.handle) elif self._vote_type == 'close': if user.is_broadcasting: self._bot.send_close_user_msg(user.handle) # prevent further user cam user.can_broadcast = False
class TimeoutSocket(common.socket): def tryconnect(self, ips, on_connect, on_fail, timeout=2.0): ''' Setup for a new set of ips and start the connect routine @param ips: @param on_connect: @param on_fail: @param timeout: ''' self.cancel_timeout() self.timetowait = timeout self.on_connect = on_connect self.on_fail = on_fail self._ips = iptuples(ips) self.attempts = 0 self._accepting = False self.try_connect() def try_connect(self): 'Do the connection routine.' addr = self._ips[self.attempts] log.warning('tryconnect: %r', (addr,)) self.attempts += 1 self.timeout = Timer(self.timetowait, lambda s=self.socket: self.handle_timeout(s)) self.make_socket() if self.timeout is not None: self.timeout.start() def succ(*a, **k): log.info("WIN") def fail(*a, **k): log.info("FAIL") self.connect(addr, success=succ, error=fail) def tryaccept(self, addr, on_connect, on_fail, timeout = 1.5): self._accepting = True info('tryaccept Y=%r, N=%r', on_connect, on_fail) self.on_connect = on_connect self.on_fail = on_fail info('listening for a connection at %r', (addr,)) self.make_socket() common.socket.bind( self, addr ) self.listen(1) if timeout: info('timeout in %r secs', timeout) self.timeout = Timer(timeout, lambda s=self.socket: self.handle_timeout(s)) self.timeout.start() def handle_timeout(self, socket): info('TIMEOUT %r', socket) if socket is self.socket: self.do_disconnect() elif socket is not None: socket.close() def handle_expt(self): info('handle_expt in %r', self) self.do_disconnect() def handle_error(self, e=None): info('handle_error in %r', self) import traceback traceback.print_exc() self.do_disconnect() def do_disconnect(self): ''' toss away the current connection will try the next address immediately ''' log.warning('do_disconnect') self.cancel_timeout() self.close() if not self._accepting and self.attempts < len(self._ips): self.try_connect() else: self.on_fail() def handle_connect(self): info('connected!') self.cancel_timeout() self.on_connect(self) def handle_accept(self): self.cancel_timeout() conn, address = self.accept() info('%r connection accepted (%r), canceling timeout and calling %r', self, address, self.on_connect) self.on_connect(conn) def cancel_timeout(self): # Cancel any timeout. if hasattr(self, 'timeout') and self.timeout is not None: info('cancelling timeout') self.timeout.cancel() else: log.warning('there was no timeout to cancel') self.timeout = None def __repr__(self): if hasattr(self,'ips') and len(self.ips): return '<TimeoutSocket %s:%d>' % self.ips[0] else: pn = None try: pn = self.socket.getpeername() finally: return "<%s connected to %r>" % (self.__class__.__name__,pn)