示例#1
0
 def _rpc_version_warning(self, version):
     """
     Add a warning to the log if the Transmission RPC version is lower then the provided version.
     """
     if self.rpc_version < version:
         LOGGER.warning('Using feature not supported by server. RPC version for server %d, feature introduced in %d.'
                        % (self.rpc_version, version))
示例#2
0
文件: client.py 项目: shnabz/mrmike
 def _rpc_version_warning(self, version):
     """
     Add a warning to the log if the Transmission RPC version is lower then the provided version.
     """
     if self.rpc_version < version:
         LOGGER.warning('Using feature not supported by server. RPC version for server %d, feature introduced in %d.'
             % (self.rpc_version, version))
示例#3
0
def rectilinear_shape(population):
    try:
        pos = population.positions
    except Exception, e:
        LOGGER.warning(("Could not retrieve units positions for population "
                         "%s; assuming square shape."), population.label)
        if not is_square(population.size):
            raise TypeError(("The shape population %s is not square and could "
                              "neither be retreived nor guessed."), population.label)
        dim1 = dim2 = int(math.sqrt(population.size))
示例#4
0
 def __init__(self,
              address='localhost',
              port=DEFAULT_PORT,
              user=None,
              password=None,
              http_handler=None,
              timeout=None):
     if isinstance(timeout, (integer_types, float)):
         self._query_timeout = float(timeout)
     else:
         self._query_timeout = DEFAULT_TIMEOUT
     urlo = urlparse(address)
     if urlo.scheme == '':
         base_url = 'http://' + address + ':' + str(port)
         self.url = base_url + '/transmission/rpc'
     else:
         if urlo.port:
             self.url = urlo.scheme + '://' + urlo.hostname + ':' + str(
                 urlo.port) + urlo.path
         else:
             self.url = urlo.scheme + '://' + urlo.hostname + urlo.path
         LOGGER.info('Using custom URL "' + self.url + '".')
         if urlo.username and urlo.password:
             user = urlo.username
             password = urlo.password
         elif urlo.username or urlo.password:
             LOGGER.warning(
                 'Either user or password missing, not using authentication.'
             )
     if http_handler is None:
         self.http_handler = DefaultHTTPHandler()
     else:
         if hasattr(http_handler, 'set_authentication') and hasattr(
                 http_handler, 'request'):
             self.http_handler = http_handler
         else:
             raise ValueError('Invalid HTTP handler.')
     if user and password:
         self.http_handler.set_authentication(self.url, user, password)
     elif user or password:
         LOGGER.warning(
             'Either user or password missing, not using authentication.')
     self._sequence = 0
     self.session = None
     self.session_id = 0
     self.server_version = None
     self.protocol_version = None
     self.get_session()
     self.torrent_get_arguments = get_arguments('torrent-get',
                                                self.rpc_version)
示例#5
0
def population_adpater_provider(pop_prov_dict,
                                provided_class,
                                population):
    """Factory function providing an adapter of the specified class
    for the population parameter. pop_prov_dict is a dictionary taking
    a (population, provided_class) tuple as key, and returning an
    instance of provided_class initialized with 3 arguments: the
    population, its size in the first dimension, and its size in the
    second dimension."""
    key = (population, provided_class)
    if pop_prov_dict.has_key(key):
        return pop_prov_dict[key]
    else:
        LOGGER.warning("No %s for population %s, creating one.",
                       provided_class.__name__, population.label)
        dim1, dim2 = rectilinear_shape(population)
        inst = provided_class(population, dim1, dim2)
    return pop_prov_dict.setdefault(key, inst)
示例#6
0
文件: client.py 项目: shnabz/mrmike
 def __init__(self, address='localhost', port=DEFAULT_PORT, user=None, password=None, http_handler=None, timeout=None):
     if isinstance(timeout, (integer_types, float)):
         self._query_timeout = float(timeout)
     else:
         self._query_timeout = DEFAULT_TIMEOUT
     urlo = urlparse(address)
     if urlo.scheme == '':
         base_url = 'http://' + address + ':' + str(port)
         self.url = base_url + '/transmission/rpc'
     else:
         if urlo.port:
             self.url = urlo.scheme + '://' + urlo.hostname + ':' + str(urlo.port) + urlo.path
         else:
             self.url = urlo.scheme + '://' + urlo.hostname + urlo.path
         LOGGER.info('Using custom URL "' + self.url + '".')
         if urlo.username and urlo.password:
             user = urlo.username
             password = urlo.password
         elif urlo.username or urlo.password:
             LOGGER.warning('Either user or password missing, not using authentication.')
     if http_handler is None:
         self.http_handler = DefaultHTTPHandler()
     else:
         if hasattr(http_handler, 'set_authentication') and hasattr(http_handler, 'request'):
             self.http_handler = http_handler
         else:
             raise ValueError('Invalid HTTP handler.')
     if user and password:
         self.http_handler.set_authentication(self.url, user, password)
     elif user or password:
         LOGGER.warning('Either user or password missing, not using authentication.')
     self._sequence = 0
     self.session = None
     self.session_id = 0
     self.server_version = None
     self.protocol_version = None
     self.get_session()
     self.torrent_get_arguments = get_arguments('torrent-get'
                                                , self.rpc_version)
示例#7
0
async def handle_raw_updates(client: Client, update: Update, user: dict,
                             chat: dict):
    if isinstance(update, UpdateGroupCallParticipants):
        if not Config.CURRENT_CALL:
            a = await client.send(
                GetFullChannel(
                    channel=(await client.resolve_peer(Config.CHAT))))
            if a.full_chat.call is not None:
                Config.CURRENT_CALL = a.full_chat.call.id
        if Config.CURRENT_CALL and update.call.id == Config.CURRENT_CALL:
            all = update.participants
            old = list(
                filter(
                    lambda k: k.peer.user_id if hasattr(k.peer, 'user_id') else
                    k.peer.channel_id == Config.USER_ID, all))
            if old:
                for me in old:
                    if me.volume:
                        Config.VOLUME = round(int(me.volume) / 100)

    if isinstance(update,
                  UpdateGroupCall) and (update.chat_id
                                        == int(-1000000000000 - Config.CHAT)):
        if update.call is None:
            Config.IS_ACTIVE = False
            Config.CURRENT_CALL = None
            LOGGER.warning("No Active Group Calls Found.")
            if Config.IS_RECORDING:
                Config.WAS_RECORDING = True
                await stop_recording()
                LOGGER.warning(
                    "Group call was ended and hence stoping recording.")
            Config.HAS_SCHEDULE = False
            await sync_to_db()
            return

        else:
            call = update.call
            if isinstance(call, GroupCallDiscarded):
                Config.CURRENT_CALL = None
                Config.IS_ACTIVE = False
                if Config.IS_RECORDING:
                    Config.WAS_RECORDING = True
                    await stop_recording()
                LOGGER.warning("Group Call Was ended")
                Config.CALL_STATUS = False
                await sync_to_db()
                return
            Config.IS_ACTIVE = True
            Config.CURRENT_CALL = call.id
            if Config.IS_RECORDING and not call.record_video_active:
                Config.LISTEN = True
                await stop_recording()
                LOGGER.warning(
                    "Recording was ended by user, hence stopping the schedules."
                )
                return
            if call.schedule_date:
                Config.HAS_SCHEDULE = True
            else:
                Config.HAS_SCHEDULE = False
        await sync_to_db()
示例#8
0
def launch_ec2_instance(region, instance_type, ami_id):
    """
    Note: This function relies on CODEBUILD environment variables. If this function is used outside of CODEBUILD,
    modify the function accordingly.
    Spins up an ec2 instance, clones the current Github Pull Request commit id on the instance, and runs sanity test on it.
    Prints the output of the command executed.
    """
    github_repo = os.environ.get(
        "CODEBUILD_SOURCE_REPO_URL",
        "https://github.com/pytorch/serve.git").strip()
    github_pr_commit_id = os.environ.get("CODEBUILD_RESOLVED_SOURCE_VERSION",
                                         "HEAD").strip()
    github_hookshot = os.environ.get("CODEBUILD_SOURCE_VERSION",
                                     "job-local").strip()
    github_hookshot = github_hookshot.replace("/", "-")

    # Extract the PR number or use the last 6 characters of the commit id
    github_pull_request_number = github_hookshot.split(
        "-")[1] if "-" in github_hookshot else github_hookshot[-6:]

    ec2_client = boto3.client("ec2",
                              config=Config(retries={"max_attempts": 10}),
                              region_name=region)
    random.seed(f"{datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')}")
    ec2_key_name = f"{github_hookshot}-ec2-instance-{random.randint(1, 1000)}"

    # Spin up ec2 instance and run tests
    try:
        key_file = ec2_utils.generate_ssh_keypair(ec2_client, ec2_key_name)
        instance_details = ec2_utils.launch_instance(
            ami_id,
            instance_type,
            ec2_key_name=ec2_key_name,
            region=region,
            user_data=None,
            iam_instance_profile_name=ec2_utils.EC2_INSTANCE_ROLE_NAME,
            instance_name=ec2_key_name,
        )

        instance_id = instance_details["InstanceId"]
        ip_address = ec2_utils.get_public_ip(instance_id, region=region)

        LOGGER.info(f"*** Waiting on instance checks to complete...")
        ec2_utils.check_instance_state(instance_id,
                                       state="running",
                                       region=region)
        ec2_utils.check_system_state(instance_id,
                                     system_status="ok",
                                     instance_status="ok",
                                     region=region)
        LOGGER.info(
            f"*** Instance checks complete. Running commands on instance.")

        # Create a fabric connection to the ec2 instance.
        ec2_connection = ec2_utils.get_ec2_fabric_connection(
            instance_id, key_file, region)

        LOGGER.info(f"Running update command. This could take a while.")
        ec2_connection.run(f"sudo apt update")

        # Update command takes a while to run, and should ideally run uninterrupted
        time.sleep(300)

        with ec2_connection.cd("/home/ubuntu"):
            LOGGER.info(
                f"*** Cloning the PR related to {github_hookshot} on the ec2 instance."
            )
            ec2_connection.run(f"git clone {github_repo}")
            ec2_connection.run(
                f"cd serve && git fetch origin pull/{github_pull_request_number}/head:pull && git checkout pull"
            )

            ec2_connection.run(f"sudo apt-get install -y python3-venv")
            # Following is necessary on Base Ubuntu DLAMI because the default python is python2
            # This will NOT fail for other AMI where default python is python3
            ec2_connection.run(
                f"sudo cp /usr/local/bin/pip3 /usr/local/bin/pip && pip install --upgrade pip",
                warn=True)
            ec2_connection.run(
                f"sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 1",
                warn=True)

        is_gpu = True if instance_type[:2] in GPU_INSTANCES else False

        command_return_value_map = run_commands_on_ec2_instance(
            ec2_connection, is_gpu)

        if any(command_return_value_map.values()):
            raise ValueError(
                f"*** One of the commands executed on ec2 returned a non-zero value."
            )
        else:
            LOGGER.info(
                f"*** All commands executed successfully on ec2. command:return_value map is as follows:"
            )
            LOGGER.info(command_return_value_map)

    except ValueError as e:
        LOGGER.error(f"*** ValueError: {e}")
        LOGGER.error(
            f"*** Following commands had the corresponding return value:")
        LOGGER.error(command_return_value_map)
        raise e
    except Exception as e:
        LOGGER.error(f"*** Exception occured. {e}")
        raise e
    finally:
        LOGGER.warning(
            f"*** Terminating instance-id: {instance_id} with name: {ec2_key_name}"
        )
        ec2_utils.terminate_instance(instance_id, region)
        LOGGER.warning(f"*** Destroying ssh key_pair: {ec2_key_name}")
        ec2_utils.destroy_ssh_keypair(ec2_client, ec2_key_name)
示例#9
0
class Config:
    #Telegram API Stuffs
    load_dotenv()  # load enviroment variables from .env file
    ADMIN = os.environ.get("ADMINS", '')
    SUDO = [int(admin) for admin in (ADMIN).split()] # Exclusive for heroku vars configuration.
    ADMINS = [int(admin) for admin in (ADMIN).split()] #group admins will be appended to this list.
    API_ID = int(os.environ.get("API_ID", ''))
    API_HASH = os.environ.get("API_HASH", "")
    BOT_TOKEN = os.environ.get("BOT_TOKEN", "")     
    SESSION = os.environ.get("SESSION_STRING", "")

    #Stream Chat and Log Group
    CHAT = int(os.environ.get("CHAT", ""))
    LOG_GROUP=os.environ.get("LOG_GROUP", "")

    #Stream 
    STREAM_URL=os.environ.get("STARTUP_STREAM", "https://www.youtube.com/watch?v=zcrUCvBD16k")
   
    #Database
    DATABASE_URI=os.environ.get("DATABASE_URI", None)
    DATABASE_NAME=os.environ.get("DATABASE_NAME", "VCPlayerBot")


    #heroku
    API_KEY=os.environ.get("HEROKU_API_KEY", None)
    APP_NAME=os.environ.get("HEROKU_APP_NAME", None)


    #Optional Configuration
    SHUFFLE=is_enabled(os.environ.get("SHUFFLE", 'True'))
    ADMIN_ONLY=is_enabled(os.environ.get("ADMIN_ONLY", "False"))
    REPLY_MESSAGE=os.environ.get("REPLY_MESSAGE", False)
    EDIT_TITLE = os.environ.get("EDIT_TITLE", True)
    #others
    
    RECORDING_DUMP=os.environ.get("RECORDING_DUMP", False)
    RECORDING_TITLE=os.environ.get("RECORDING_TITLE", False)
    TIME_ZONE = os.environ.get("TIME_ZONE", "Asia/Kolkata")    
    IS_VIDEO=is_enabled(os.environ.get("IS_VIDEO", 'True'))
    IS_LOOP=is_enabled(os.environ.get("IS_LOOP", 'True'))
    DELAY=int(os.environ.get("DELAY", '10'))
    PORTRAIT=is_enabled(os.environ.get("PORTRAIT", 'False'))
    IS_VIDEO_RECORD=is_enabled(os.environ.get("IS_VIDEO_RECORD", 'True'))
    DEBUG=is_enabled(os.environ.get("DEBUG", 'False'))
    PTN=is_enabled(os.environ.get("PTN", "False"))

    #Quality vars
    E_BITRATE=os.environ.get("BITRATE", False)
    E_FPS=os.environ.get("FPS", False)
    CUSTOM_QUALITY=os.environ.get("QUALITY", "100")

    #Search filters for cplay
    FILTERS =  [filter.lower() for filter in (os.environ.get("FILTERS", "video document")).split(" ")]


    #Dont touch these, these are not for configuring player
    GET_FILE={}
    DATA={}
    STREAM_END={}
    SCHEDULED_STREAM={}
    DUR={}
    msg = {}

    SCHEDULE_LIST=[]
    playlist=[]
    CONFIG_LIST = ["ADMINS", "IS_VIDEO", "IS_LOOP", "REPLY_PM", "ADMIN_ONLY", "SHUFFLE", "EDIT_TITLE", "CHAT", 
    "SUDO", "REPLY_MESSAGE", "STREAM_URL", "DELAY", "LOG_GROUP", "SCHEDULED_STREAM", "SCHEDULE_LIST", 
    "IS_VIDEO_RECORD", "IS_RECORDING", "WAS_RECORDING", "RECORDING_TITLE", "PORTRAIT", "RECORDING_DUMP", "HAS_SCHEDULE", 
    "CUSTOM_QUALITY"]

    STARTUP_ERROR=None

    ADMIN_CACHE=False
    CALL_STATUS=False
    YPLAY=False
    YSTREAM=False
    CPLAY=False
    STREAM_SETUP=False
    LISTEN=False
    STREAM_LINK=False
    IS_RECORDING=False
    WAS_RECORDING=False
    PAUSE=False
    MUTED=False
    HAS_SCHEDULE=None
    IS_ACTIVE=None
    VOLUME=100
    CURRENT_CALL=None
    BOT_USERNAME=None
    USER_ID=None

    if LOG_GROUP:
        LOG_GROUP=int(LOG_GROUP)
    else:
        LOG_GROUP=None
    if not API_KEY or \
       not APP_NAME:
       HEROKU_APP=None
    else:
       HEROKU_APP=heroku3.from_key(API_KEY).apps()[APP_NAME]


    if EDIT_TITLE in ["NO", 'False']:
        EDIT_TITLE=False
        LOGGER.info("Title Editing turned off")
    if REPLY_MESSAGE:
        REPLY_MESSAGE=REPLY_MESSAGE
        REPLY_PM=True
        LOGGER.info("Reply Message Found, Enabled PM MSG")
    else:
        REPLY_MESSAGE=False
        REPLY_PM=False

    if E_BITRATE:
       try:
          BITRATE=int(E_BITRATE)
       except:
          LOGGER.error("Invalid bitrate specified.")
          E_BITRATE=False
          BITRATE=48000
       if not BITRATE >= 48000:
          BITRATE=48000
    else:
       BITRATE=48000
    
    if E_FPS:
       try:
          FPS=int(E_FPS)
       except:
          LOGGER.error("Invalid FPS specified")
          E_FPS=False
       if not FPS >= 30:
          FPS=30
    else:
       FPS=30
    try:
       CUSTOM_QUALITY=int(CUSTOM_QUALITY)
       if CUSTOM_QUALITY > 100:
          CUSTOM_QUALITY = 100
          LOGGER.warning("maximum quality allowed is 100, invalid quality specified. Quality set to 100")
       elif CUSTOM_QUALITY < 10:
          LOGGER.warning("Minimum Quality allowed is 10., Qulaity set to 10")
          CUSTOM_QUALITY = 10
       if  66.9  < CUSTOM_QUALITY < 100:
          if not E_BITRATE:
             BITRATE=48000
       elif 50 < CUSTOM_QUALITY < 66.9:
          if not E_BITRATE:
             BITRATE=36000
       else:
          if not E_BITRATE:
             BITRATE=24000
    except:
       if CUSTOM_QUALITY.lower() == 'high':
          CUSTOM_QUALITY=100
       elif CUSTOM_QUALITY.lower() == 'medium':
          CUSTOM_QUALITY=66.9
       elif CUSTOM_QUALITY.lower() == 'low':
          CUSTOM_QUALITY=50
       else:
          LOGGER.warning("Invalid QUALITY specified.Defaulting to High.")
          CUSTOM_QUALITY=100



    #help strings 
    PLAY_HELP="""
__You can play using any of these options__

1. Play a video from a YouTube link.
Command: **/play**
__You can use this as a reply to a YouTube link or pass link along command. or as a reply to message to search that in YouTube.__

2. Play from a telegram file.
Command: **/play**
__Reply to a supported media(video and documents or audio file ).__
Note: __For both the cases /fplay also can be used by admins to play the song immediately without waiting for queue to end.__

3. Play from a YouTube playlist
Command: **/yplay**
__First get a playlist file from @GetPlaylistBot or @DumpPlaylist and reply to playlist file.__

4. Live Stream
Command: **/stream**
__Pass a live stream URL or any direct URL to play it as stream.__

5. Import an old playlist.
Command: **/import**
__Reply to a previously exported playlist file. __

6. Channel Play
Command: **/cplay**
__Use `/cplay channel username or channel id` to play all the files from the given channel.
By default both video files and documents will be played . You can add or remove the file type using `FILTERS` var. 
For example , to stream audio, video and document from the channel use `/env FILTERS video document audio` . If you need only audio , you can use `/env FILTERS video audio` and so on.
To set up the files from a channel as STARTUP_STREAM, so that the files will be automatically added to playlist on startup of bot. use `/env STARTUP_STREAM channel username or channel id`

Note that for public channels you should use username of channels along with '@' and for private channels you should use channel id.
For private channels , make sure both the bot and USER account is a member of channel.__
"""
    SETTINGS_HELP="""
**You can easily customize you player as per you needs. The following configurations are available:**

🔹Command: **/settings**

🔹AVAILABLE CONFIGURATIONS:

**Player Mode** -  __This allows you to run your player as 24/7 music player or only when there is song in queue. 
If disabled, player will leave from the call when the playlist is empty.
Otherwise STARTUP_STREAM will be streamed when playlist id empty.__

**Video Enabled** -  __This allows you to switch between audio and video.
if disabled, video files will be played as audio.__

**Admin Only** - __Enabling this will restrict non-admin users from using play command.__

**Edit Title** - __Enabling this will edit your VideoChat title to current playing songs name.__

**Shuffle Mode** - __Enabling this will shuffle the playlist whenever you import a playlist or using /yplay __

**Auto Reply** - __Choose whether to reply the PM messages of playing user account.
You can  set up a custom reply message using `REPLY_MESSAGE` confug.__

"""
    SCHEDULER_HELP="""
__VCPlayer allows you to schedule a stream. 
This means you can schedule a stream for a future date and on the scheduled date, stream will be played automatically.
At present you can schedule a stream for even one year!!. Make sure you have set up a databse, else you will loose your schedules whenever the player restarts. __

Command: **/schedule**

__Reply to a file or a youtube video or even a text message with schedule command.
The replied media or youtube video will be scheduled and will be played on the scheduled date.
The scheduling time is by default in IST and you can change the timezone using `TIME_ZONE` config.__

Command: **/slist**
__View your current scheduled streams.__

Command: **/cancel**
__Cancel a schedule by its schedule id, You can get the schedule id using /slist command__

Command: **/cancelall**
__Cancel all the scheduled streams__
"""
    RECORDER_HELP="""
__With VCPlayer you can easily record all your video chats.
By default telegram allows you to record for a maximum duration of 4 hours. 
An attempt to overcome this limit has been made by automatically restarting the recording after  4 hours__

Command: **/record**

AVAILABLE CONFIGURATIONS:
1. Record Video: __If enabled both the video and audio of the stream will be recorded, otherwise only audio will be recorded.__

2. Video dimension: __Choose between portrait and landscape dimensions for your recording__

3. Custom Recording Title: __Set up a custom recording title for your recordings. Use a command /rtitle to configure this.
To turn off the custom title, use `/rtitle False `__

4. Recording Dumb: __You can set up forwarding all your recordings to a channel, this will be useful since otherwise recordings are sent to saved messages of streaming account.
Setup using `RECORDING_DUMP` config.__

⚠️ If you start a recording with vcplayer, make sure you stop the same with vcplayer.

"""

    CONTROL_HELP="""
__VCPlayer allows you to control your streams easily__
1. Skip a song.
Command: **/skip**
__You can pass a number greater than 2 to skip the song in that position.__

2. Pause the player.
Command: **/pause**

3. Resume the player.
Command: **/resume**

4. Change Volume.
Command: **/volume**
__Pass the volume in between 1-200.__

5. Leave the VC.
Command: **/leave**

6. Shuffle the playlist.
Command: **/shuffle**

7. Clear the current playlist queue.
Command: **/clearplaylist**

8. Seek the video.
Command: **/seek**
__You can pass number of seconds to be skipped. Example: /seek 10 to skip 10 sec. /seek -10 to rewind 10 sec.__

9. Mute the player.
Command: **/vcmute**

10. Unmute the player.
Command : **/vcunmute**

11. Shows the playlist.
Command: **/playlist** 
__Use /player to show with control buttons__
"""

    ADMIN_HELP="""
__VCPlayer allows to control admins, that is you can add admins and remove them easily.
It is recommended to use a MongoDb database for better experience, else all you admins will get reset after restart.__

Command: **/vcpromote**
__You can promote a admin with their username or user id or by replying to that users message.__

Command: **/vcdemote**
__Remove an admin from admin list__

Command: **/refresh**
__Refresh the admin list of chat__
"""

    MISC_HELP="""
Command: **/export**
__VCPlayer allows you to export your current playlist for future use.__
__A json file will be sent to you and the same can be used along /import command.__

Command : **/logs**
__If your player went something gone wrong, you can easily check the logs using /logs__
 
Command : **/env**
__Setup your config vars with /env command.__
__Example: To set up a__ `REPLY_MESSAGE` __use__ `/env REPLY_MESSAGE=Hey, Check out @subin_works rather than spamming in my PM`__
__You can delete a config var by ommiting a value for that, Example:__ `/env LOG_GROUP=` __this will delete the existing LOG_GROUP config.

Command: **/config**
__Same as using /env**

Command: **/update**
__Updates youe bot with latest changes__

Tip: __You can easily change the CHAT config by adding the user account and bot account to any other group and any command in new group__

"""
    ENV_HELP="""
示例#10
0
    def handle_read(self):
        data = self.recv(BUF_SIZE)
        if not data:
            return
        if self.token:
            self.server.add_traffic(self.token, len(data) / (1024.0 * 1024.0))
        self.buffer_recv += data
        ddata, dlen = self.cipher.decrypt_all(self.buffer_recv)
        self.buffer_recv_raw += ddata
        self.buffer_recv = self.buffer_recv[dlen:]

        #LOGGER.debug('%s local recv %s', id(self), data)
        while True:
            if self.stage == STAGE_INIT:
                if len(self.buffer_recv_raw) < TOKEN_LEN + MAGIC_LEN:
                    return
                token = self.buffer_recv_raw[0:TOKEN_LEN]
                self.token = token
                magic = self.buffer_recv_raw[TOKEN_LEN:TOKEN_LEN + MAGIC_LEN]
                LOGGER.info("%s accept token: %s, magic: %s", id(self),
                            token.hex(), magic.hex())

                # auth
                if not self.server.query_token(token):
                    LOGGER.warning('%s token not exist!', id(self))
                    self.handle_close()
                    return
                if not self.server.query_magic(magic):
                    LOGGER.warning('%s duplicated magic!', id(self))
                    self.handle_close()
                    return

                self.buffer_recv_raw = self.buffer_recv_raw[TOKEN_LEN +
                                                            MAGIC_LEN:]
                self.stage = STAGE_HANDSHAKE
                continue
            elif self.stage == STAGE_HANDSHAKE:
                if len(self.buffer_recv_raw) < 1:
                    return
                atyp = self.buffer_recv_raw[0]
                addr = ""
                port = 0
                if atyp == 1:
                    if len(self.buffer_recv_raw) < 7:
                        return
                    addr = str(self.buffer_recv_raw[1]) + '.' + str(self.buffer_recv_raw[2]) + \
                        '.' + str(self.buffer_recv_raw[3]) + '.' + str(self.buffer_recv_raw[4])
                    port = self.buffer_recv_raw[
                        5] * 256 + self.buffer_recv_raw[6]
                    self.buffer_recv_raw = self.buffer_recv_raw[7:]
                elif atyp == 3:
                    if len(self.buffer_recv_raw) < 2:
                        return
                    alen = self.buffer_recv_raw[1]
                    if len(self.buffer_recv_raw) < 2 + alen + 2:
                        return
                    addr = self.buffer_recv_raw[2:2 + alen].decode('utf-8')
                    port = self.buffer_recv_raw[
                        2 + alen] * 256 + self.buffer_recv_raw[2 + alen + 1]
                    self.buffer_recv_raw = self.buffer_recv_raw[2 + alen + 2:]
                LOGGER.info('%s local handshake: %s:%d', id(self), addr, port)
                try:
                    self.remote = RemoteConnection(addr, port)
                except Exception:
                    LOGGER.error('%s cannot connect to %s:%d', id(self), addr,
                                 port)
                    return
                self.remote.local = self
                self.remote.buffer += self.buffer_recv_raw
                self.buffer_recv_raw = b''
                self.stage = STAGE_STREAM
                continue
            elif self.stage == STAGE_STREAM:
                self.remote.buffer += self.buffer_recv_raw
                self.buffer_recv_raw = b''
            return