def setup_socket(self): """ Set up socket to start communicating to workers. """ if self.bypass_server_setup: return log_utils.print_and_log(logging.INFO, 'Local: Setting up WebSocket...', should_print=True) self.app_token = self.get_app_token() # Set up receive socket_use_url = self.server_url if self.opt['local']: # skip some hops for local stuff socket_use_url = 'https://localhost' # socket_use_url = 'https://localhost:8399/api/ws' socket_use_url = 'https://michinai.cf/api/ws' # socket_use_url = 'https://d4008293ad0.ngrok.io/api/ws' # socket_use_url = 'https://jig7357-parlai-messenger-chatb.herokuapp.com/webhook' self.port = 8399 self.socket = ChatServiceMessageSocket(socket_use_url, self.port, self._handle_webhook_event) self.sender = MessageSender(self.app_token, self.socket) log_utils.print_and_log(logging.INFO, 'done with websocket', should_print=True)
def send_fb_message( self, receiver_id, message, is_response, quick_replies=None, persona_id=None ): """ Sends a message directly to messenger. """ api_address = f'https://graph.facebook.com/{API_VERSION}/me/messages' if quick_replies is not None: quick_replies = [create_reply_option(x, x) for x in quick_replies] ms = create_text_message(message, quick_replies) results = [] for m in ms: if m['text'] == '': continue # Skip blank messages payload = { "messaging_type": 'RESPONSE' if is_response else 'UPDATE', "recipient": {"id": receiver_id}, "message": m, } if persona_id is not None: payload['persona_id'] = persona_id response = requests.post(api_address, params=self.auth_args, json=payload) result = response.json() if 'error' in result: if result['error']['code'] == 1200: # temporary error please retry response = requests.post( api_address, params=self.auth_args, json=payload ) result = response.json() log_utils.print_and_log( logging.INFO, '"Facebook response from message send: {}"'.format(result) ) results.append(result) return results
def _world_function(): world_generator = utils.get_world_fn_attr(self._world_module, overworld_name, "generate_world") overworld = world_generator(self.opt, [overworld_agent]) while not overworld.episode_done() and not self.system_done: world_type = overworld.parley() if world_type is None: time.sleep(0.5) continue if world_type == self.manager.EXIT_STR: self.manager._remove_agent(overworld_agent.id) return world_type # perform onboarding onboard_type = onboard_map.get(world_type) if onboard_type: onboard_id = 'onboard-{}-{}'.format( overworld_agent.id, time.time()) agent = self.manager._create_agent(onboard_id, overworld_agent.id) agent.data = overworld_agent.data agent_state.set_active_agent(agent) agent_state.assign_agent_to_task(agent, onboard_id) _, onboard_data = self._run_world(task, onboard_type, [agent]) agent_state.onboard_data = onboard_data agent_state.data = agent.data self.manager.add_agent_to_pool(agent_state, world_type) log_utils.print_and_log(logging.INFO, 'onboarding/overworld complete') return world_type
def on_disconnect(*args): """ Disconnect event is a no-op for us, as the server reconnects automatically on a retry. """ log_utils.print_and_log( logging.INFO, 'World server disconnected: {}'.format(args)) self.alive = False self._ensure_closed()
def _log_missing_agent(self, agent_id, assignment_id): """ Log the occurence of a missing agent. """ log_utils.print_and_log( logging.WARN, 'Expected to have an agent for {}_{}, yet none was found'.format( agent_id, assignment_id), )
def upload_fb_attachment(self, payload): """ Uploads an attachment using the Attachment Upload API and returns an attachment ID. """ api_address = f'https://graph.facebook.com/{API_VERSION}/me/message_attachments' assert payload['type'] in [ 'image', 'video', 'file', 'audio', ], 'unsupported attachment type' if 'url' in payload: message = { "message": { "attachment": { "type": payload['type'], "payload": { "is_reusable": "true", "url": payload['url'] }, } } } response = requests.post(api_address, params=self.auth_args, json=message) elif 'filename' in payload: message = { "attachment": { "type": payload['type'], "payload": { "is_reusable": "true" }, } } with open(payload['filename'], 'rb') as f: filedata = { "filedata": ( payload['filename'], f, payload['type'] + '/' + payload['format'], ) } response = requests.post( api_address, params=self.auth_args, data={"message": json.dumps(message)}, files=filedata, ) result = response.json() log_utils.print_and_log( logging.INFO, '"Facebook response from attachment upload: {}"'.format(result), ) return result
def _done_callback(fut): e = fut.exception() if e is not None: log_utils.print_and_log( logging.ERROR, 'World {} had error {}'.format(task_id, repr(e)), should_print=True, ) traceback.print_exc(file=sys.stdout) if self.debug: raise e
def delete_persona(self, persona_id): """ Deletes the persona. """ api_address = 'https://graph.facebook.com/' + persona_id response = requests.delete(api_address, params=self.auth_args) result = response.json() log_utils.print_and_log( logging.INFO, '"Facebook response from delete persona: {}"'.format(result)) return result
def create_persona(self, name, image_url): """ Creates a new persona and returns persona_id. """ api_address = 'https://graph.facebook.com/me/personas' message = {'name': name, "profile_picture_url": image_url} response = requests.post(api_address, params=self.auth_args, json=message) result = response.json() log_utils.print_and_log( logging.INFO, '"Facebook response from create persona: {}"'.format(result) ) return result
def wrapper(self, *args, **kwargs): response = function(self, *args, **kwargs).json() if response['ok']: if 'description' in response: log_utils.print_and_log( logging.INFO, f"Telegram API response: {response['description']}", should_print=True ) return response['result'] else: raise Exception(response['description'])
def send_fb_message(self, receiver_id, message, is_response, quick_replies=None, persona_id=None): """ Sends a message directly to messenger. """ # api_address = f'https://graph.facebook.com/{API_VERSION}/me/messages' # if quick_replies is not None: # quick_replies = [create_reply_option(x, x) for x in quick_replies] # ms = create_text_message(message, quick_replies) results = [] if message == '': return payload = { "message_type": 'RESPONSE' if is_response else 'UPDATE', "recipient": receiver_id, "text": message, } # if persona_id is not None: # payload['persona_id'] = persona_id # response = requests.post(api_address, params=self.auth_args, json=payload) # self.socket._safe_send(json.dumps(payload)) # print(requests.post("http://localhost:8399/api/test", json=payload).text) print(requests.post("https://michinai.cf/api/test", json=payload).text) # print(requests.post("http://d4008293ad0.ngrok.io/api/ws", json=payload).text) print('sended ressult : ', payload) # result = response.json() import random result = { 'recipient_id': receiver_id, 'message_id': str(random.random()) } # if 'error' in result: # if result['error']['code'] == 1200: # # temporary error please retry # response = requests.post( # api_address, params=self.auth_args, json=payload # ) # result = response.json() log_utils.print_and_log( logging.INFO, '"Facebook response from message send: {}"'.format(result)) results.append(result) return results
def on_message(*args): """ Incoming message handler for messages from the FB user. """ packet_dict = json.loads(args[1]) if packet_dict['type'] == 'conn_success': self.alive = True return # No action for successful connection if packet_dict['type'] == 'pong': self.last_pong = time.time() return # No further action for pongs message_data = packet_dict['content'] log_utils.print_and_log(logging.DEBUG, f'Message data received: {message_data}') self.message_callback(message_data)
def setup_server(self): """ Prepare the Messenger server for handling messages. """ if self.bypass_server_setup: return log_utils.print_and_log( logging.INFO, '\nYou are going to allow people on Facebook to be agents in ' 'ParlAI.\nDuring this process, Internet connection is required, ' 'and you should turn off your computer\'s auto-sleep ' 'feature.\n', should_print=True, ) input('Please press Enter to continue... ') log_utils.print_and_log(logging.NOTSET, '', True) if self.opt['local'] is True: log_utils.print_and_log( logging.INFO, 'In order to run the server locally, you will need ' 'to have a public HTTPS endpoint (SSL signed) running on ' 'the server you are currently excecuting ParlAI on. Enter ' 'that public URL hostname when prompted and ensure that the ' 'port being used by ParlAI (usually 3000) has external ' 'traffic routed to it.', should_print=True, ) input('Please press Enter to continue... ') log_utils.print_and_log( logging.INFO, 'Setting up Messenger webhook...', should_print=True ) # Setup the server with a task name related to the current task task_name = '{}-{}'.format('ParlAI-Messenger', self.opt['task']) self.server_task_name = ''.join( e for e in task_name.lower() if e.isalnum() or e == '-' ) self.server_url = server_utils.setup_server( self.server_task_name, local=self.opt['local'] ) log_utils.print_and_log( logging.INFO, 'Webhook address: {}/webhook'.format(self.server_url), should_print=True, )
def shutdown(self): """ Handle any client shutdown cleanup. """ try: self.is_running = False self.world_runner.shutdown() if not self.bypass_server_setup: self.socket.keep_running = False self._expire_all_conversations() except BaseException as e: log_utils.print_and_log(logging.ERROR, f'world ended in error: {e}') finally: if not self.bypass_server_setup: server_utils.delete_server(self.server_task_name, self.opt['local'])
def send_fb_payload(self, receiver_id, payload, quick_replies=None, persona_id=None): """ Sends a payload to messenger, processes it if we can. """ api_address = f'https://graph.facebook.com/{API_VERSION}/me/messages' if payload['type'] == 'list': data = create_compact_list_message(payload['data']) elif payload['type'] in ['image', 'video', 'file', 'audio']: data = create_attachment(payload) else: data = payload['data'] message = { "messaging_type": 'RESPONSE', "recipient": { "id": receiver_id }, "message": { "attachment": data }, } if quick_replies is not None: quick_replies = [create_reply_option(x, x) for x in quick_replies] message['message']['quick_replies'] = quick_replies if persona_id is not None: payload['persona_id'] = persona_id response = requests.post(api_address, params=self.auth_args, json=message) result = response.json() if 'error' in result: if result['error']['code'] == 1200: # temporary error please retry response = requests.post(api_address, params=self.auth_args, json=message) result = response.json() log_utils.print_and_log( logging.INFO, '"Facebook response from message send: {}"'.format(result)) return result
def on_error(ws, error): try: if error.errno == errno.ECONNREFUSED: self._ensure_closed() self.use_socket = False raise Exception("Socket refused connection, cancelling") else: log_utils.print_and_log( logging.WARN, 'Socket logged error: {}'.format(repr(error))) except BaseException: if type(error) is websocket.WebSocketConnectionClosedException: return # Connection closed is noop log_utils.print_and_log( logging.WARN, 'Socket logged error: {} Restarting'.format(repr(error)), ) self._ensure_closed()
def run_socket(*args): url_base_name = self.server_url.split('https://')[1] while self.keep_running: try: sock_addr = "wss://{}/".format(url_base_name) self.ws = websocket.WebSocketApp( sock_addr, on_message=on_message, on_error=on_error, on_close=on_disconnect, ) self.ws.on_open = on_socket_open self.ws.run_forever(ping_interval=1, ping_timeout=0.9) except Exception as e: log_utils.print_and_log( logging.WARN, 'Socket error {}, attempting restart'.format(repr(e)), ) time.sleep(0.2)
def setup_server(self): """ Prepare the Telegram server for handling messages. """ if self.bypass_server_setup: return log_utils.print_and_log( logging.INFO, '\nYou are going to allow people on Telegram to be agents in ' 'ParlAI.\nDuring this process, Internet connection is required, ' 'and you should turn off your computer\'s auto-sleep ' 'feature.\n', should_print=True, ) input('Please press Enter to continue... ') log_utils.print_and_log( logging.INFO, 'Setting up Telegram webhook...', should_print=True ) # Setup the server with a task name related to the current task task_name = f'ParlAI-Telegram-{self.opt["task"]}' self.server_task_name = ''.join( ch for ch in task_name.lower() if ch.isalnum() or ch == '-' ) self.server_url = server_utils.setup_server(self.server_task_name, self.opt['local']) log_utils.print_and_log( logging.INFO, f'Webhook address: {self.server_url}/webhook', should_print=True, )
def setup_socket(self): """ Set up socket to start communicating to workers. """ if self.bypass_server_setup: return log_utils.print_and_log( logging.INFO, 'Local: Setting up WebSocket...', should_print=True ) self.app_token = self.get_app_token() self.sender = MessageSender(self.app_token) # Set up receive socket_use_url = self.server_url if self.opt['local']: # skip some hops for local stuff socket_use_url = 'https://localhost' self.socket = ChatServiceMessageSocket( socket_use_url, self.port, self._handle_webhook_event ) log_utils.print_and_log(logging.INFO, 'done with websocket', should_print=True)
def _done_callback(fut): """ Log and raise exception of task world, if there is one. Additionally, set active agent to overworld agent. """ e = fut.exception() if e is not None: log_utils.print_and_log( logging.ERROR, 'World {} had error {}'.format(world_type, repr(e)), should_print=True, ) traceback.print_exc(file=sys.stdout) for agent in agents: self.observe_message( agent.id, 'Sorry, this world closed. Returning to overworld.') else: log_utils.print_and_log( logging.INFO, 'World {} had no error'.format(world_type), should_print=True, ) self.active_worlds[task_id] = None for agent in agents: self.after_agent_removed(agent.id) agent_state = self.get_agent_state(agent.id) agent_state.data = agent.data next_task = agent.data.get("next_task") log_utils.print_and_log(logging.INFO, "Next task: {}".format(next_task)) if next_task is None: self._launch_overworld(agent.id) overworld_agent = agent_state.get_overworld_agent() overworld_agent.data = agent_state.data agent_state.set_active_agent(overworld_agent) elif next_task == self.EXIT_STR: self._remove_agent(agent.id) else: self.add_agent_to_pool(agent_state, next_task)
def _manager_loop_fn(self): """ An iteration of the manager's main loop to launch worlds. """ with self.agent_pool_change_condition: valid_pools = self._get_unique_pool() for world_type, agent_pool in valid_pools.items(): # check if agent has exceeded max time in pool world_config = self.task_configs[world_type] if world_config.max_time_in_pool is not None: self.check_timeout_in_pool( world_type, agent_pool, world_config.max_time_in_pool, world_config.backup_task, ) needed_agents = self.max_agents_for[world_type] if len(agent_pool) >= needed_agents: log_utils.print_and_log(logging.INFO, 'starting pool', should_print=True) # enough agents in pool to start new conversation self.conversation_index += 1 task_id = 't_{}'.format(self.conversation_index) # Add the required number of valid agents to the conv agent_states = [w for w in agent_pool[:needed_agents]] agents = [] for state in agent_states: agent = self._create_agent(task_id, state.get_id()) agent.onboard_data = state.onboard_data agent.data = state.data state.assign_agent_to_task(agent, task_id) state.set_active_agent(agent) agents.append(agent) # reset wait message state state.stored_data['seen_wait_message'] = False assign_role_function = utils.get_assign_roles_fn( self.world_module, self.taskworld_map[world_type]) if assign_role_function is None: assign_role_function = utils.default_assign_roles_fn assign_role_function(agents) # Allow task creator to filter out workers and run # versions of the task that require fewer agents for a in agents: # Remove selected workers from the agent pool self.remove_agent_from_pool( self.get_agent_state(a.id), world_type=world_type, mark_removed=False, ) for a in agents: partner_list = agents.copy() partner_list.remove(a) a.message_partners = partner_list done_callback = self._get_done_callback_for_agents( task_id, world_type, agents) # launch task world. future = self.world_runner.launch_task_world( task_id, self.taskworld_map[world_type], agents) future.add_done_callback(done_callback) self.active_worlds[task_id] = future
def _world_fn(): log_utils.print_and_log(logging.INFO, 'Starting task {}...'.format(task_name)) return self._run_world(task, world_name, agents)
def _log_debug(self, text: str): time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') log_utils.print_and_log(logging.DEBUG, f'{time}: {text}', should_print=True)
def on_socket_open(*args): log_utils.print_and_log(logging.DEBUG, 'Socket open: {}'.format(args)) self._send_world_alive()
def on_socket_open(*args): log_utils.print_and_log(logging.DEBUG, f'Socket open: {args}') self._send_world_alive()