diff --git a/cocobot.py b/cocobot.py index 03a10f1..c2eb449 100755 --- a/cocobot.py +++ b/cocobot.py @@ -4,7 +4,6 @@ import argparse import asyncio import logging -from tools.coco from tools.base import CoboBot from tools.processors import MessageDispatcher, CommandsDispatcherProcessor, ConnectionDispatcher from tools.processors.messages import * @@ -35,11 +34,11 @@ root_messages_dispatcher = MessageDispatcher([coco_commands]) connections_dispatcher = ConnectionDispatcher([]) if __name__ == "__main__": - logging.getLogger().setLevel(logging.INFO) + logging.getLogger().setLevel(logging.DEBUG) args = parser.parse_args() cocobot = CoboBot(args.cookie, args.channel, args.domain, args.port, args.method, - root_messages_dispatcher, connections_dispatcher) + root_messages_dispatcher, connections_dispatcher, cococlient) asyncio.get_event_loop().run_until_complete(cocobot.listen()) diff --git a/requirements.txt b/requirements.txt index 55bb886..27388e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -bidict \ No newline at end of file +bidict +websockets \ No newline at end of file diff --git a/tools/base.py b/tools/base.py index 3d85a8b..d0d6ded 100755 --- a/tools/base.py +++ b/tools/base.py @@ -2,17 +2,22 @@ import html import json import logging +from asyncio import sleep, gather + import websockets +from tools.coco.client import CocoClient from tools.commons import AbstractResponse, Message, BotMessage, AttackCommand, Sound, UserList from tools.processors import MessageDispatcher, ConnectionDispatcher class CoboBot: + COCO_PULSE_TICK = 1 # number of seconds between each check - def __init__(self, cookie : str, channel : str, domain : str, port : int, method : str, - messages_dispatcher : MessageDispatcher, - connect_dispatcher : ConnectionDispatcher): + def __init__(self, cookie: str, channel: str, domain: str, port: int, method: str, + messages_dispatcher: MessageDispatcher, + connect_dispatcher: ConnectionDispatcher, + cococlient: CocoClient): # setting up variables required by the server. The default is a Kabutops on the main lou server, I think self.cookie = cookie @@ -23,6 +28,7 @@ class CoboBot: self.msg_dispatch = messages_dispatcher self.cnt_dispatch = connect_dispatcher self.user_list = None # type: UserList + self.cococlient = cococlient async def _send_message(self, message): if isinstance(message, dict): @@ -64,6 +70,40 @@ class CoboBot: elif isinstance(response, AbstractResponse): await self._dispatch_response(response) + async def socket_listener(self): + while True: + msg = await self.socket.recv() + if type(msg) != bytes: + msg_data = json.loads(msg, encoding="utf-8") + msg_type = msg_data.get("type", "") + if msg_type == "userlist": + self.user_list = UserList(msg_data["users"]) + logging.info(str(self.user_list)) + + elif msg_type == "msg": + await self._on_message(msg_data) + + elif msg_type == "connect": + await self._on_connect(msg_data) + + elif msg_type == "disconnect": + await self._on_disconnect(msg_data) + + else: + logging.debug("Received sound file") + + async def coco_pulse(self): + while True: + logging.debug("Checking coco for new messages") + await sleep(self.COCO_PULSE_TICK) + if self.cococlient.is_connected: + new_messages = self.cococlient.pulse() + if isinstance(new_messages, list): + for response_obj in new_messages: + await self._dispatch_response(response_obj) + elif isinstance(new_messages, AbstractResponse): + await self._dispatch_response(new_messages) + async def listen(self): if self.method == "https": socket_address = 'wss://%s/socket/%s' % (self.domain, self.channel) @@ -73,25 +113,6 @@ class CoboBot: async with websockets.connect(socket_address, extra_headers={"cookie": "id=%s" % self.cookie}) as websocket: self.socket = websocket - while True: - msg = await websocket.recv() - websocket.recv() - if type(msg) != bytes: - msg_data = json.loads(msg, encoding="utf-8") - msg_type = msg_data.get("type", "") - if msg_type == "userlist": - self.user_list = UserList(msg_data["users"]) - logging.info(str(self.user_list)) - - elif msg_type == "msg": - await self._on_message(msg_data) - - elif msg_type == "connect": - await self._on_connect(msg_data) - - elif msg_type == "disconnect": - await self._on_disconnect(msg_data) - - else: - logging.debug("Received sound file") + await gather(self.socket_listener(), self.coco_pulse()) + diff --git a/tools/coco/client.py b/tools/coco/client.py index d795a9f..843f740 100644 --- a/tools/coco/client.py +++ b/tools/coco/client.py @@ -1,10 +1,11 @@ +import logging import random from typing import List, Dict, Tuple, Union, Set +from collections import defaultdict + from .requests import LoginRequest, PostLoginRequest, PulseRequest, SendMsgRequest from ..commons import BotMessage, Message, AbstractResponse -import logging - class Interlocutor: @@ -42,7 +43,7 @@ class CocoClient: def __init__(self): self.interlocutors = set() # type: Set[Interlocutor] self.current_interlocutor = None # type: Interlocutor - self.histories = {} # type:Dict[Interlocutor,List[Tuple]] + self.histories = defaultdict(list) # type:defaultdict[Interlocutor,List[Tuple]] self.user_id = None # type:str self.user_pass = None # type:str @@ -64,7 +65,7 @@ class CocoClient: self.histories[user].append((user.nick, msg)) logging.info("Msg from %s : %s" % (user.nick, msg)) - if user == self.current_interlocutor: + if self.current_interlocutor is not None and user == self.current_interlocutor: out.append(Message("💬 %s: %s" % (user.nick, msg))) else: out.append(BotMessage("💬 %s: %s" % (user.nick, msg))) @@ -72,7 +73,7 @@ class CocoClient: def connect(self, nick: str, age: int, is_female: bool, zip_code: str): self.nick = nick - self.histories = {} + self.histories = defaultdict(list) self.current_interlocutor = None login_req = LoginRequest(nick, age, is_female, zip_code) self.user_id, self.user_pass = login_req.retrieve() @@ -88,15 +89,18 @@ class CocoClient: return self.__process_and_format_received_msg(received_msg) def send_msg(self, msg: str) -> List[AbstractResponse]: - sendmsg_req = SendMsgRequest(self.user_id, self.user_pass, self.current_interlocutor.id, msg) - output = sendmsg_req.retrieve() - self.histories[self.current_interlocutor].append((self.nick, msg)) - out_msg = Message("💬 %s: %s" % (self.nick, msg)) - - if output: - return [out_msg] + self.__process_and_format_received_msg(output) + if self.current_interlocutor is not None: + sendmsg_req = SendMsgRequest(self.user_id, self.user_pass, self.current_interlocutor.id, msg) + output = sendmsg_req.retrieve() + self.histories[self.current_interlocutor].append((self.nick, msg)) + out_msg = Message("💬 %s: %s" % (self.nick, msg)) + + if output: + return [out_msg] + self.__process_and_format_received_msg(output) + else: + return [out_msg] else: - return [out_msg] + return [BotMessage("Il faut sélectionner une conversation d'abord pd")] def switch_conv(self, nick: str=None) -> Union[List[BotMessage], BotMessage]: new_interlocutor = None diff --git a/tools/coco/tools.py b/tools/coco/tools.py index 1e3a8e5..310b356 100644 --- a/tools/coco/tools.py +++ b/tools/coco/tools.py @@ -70,7 +70,7 @@ smilies = [":)", ":(", ";)", ":d", ":-o", ":s", ":$", "*-)", "-)", "^o)", ":p", "nw$", "ba$", "ao$", "db$", "si$", "oo$", "co$", "bi$", "cc$", "ye$", "mo$", "aa$", "ci$", "uu$", "ff$", "zz$", "gt$", "ah$", "mm$", "?$", "xx$"] -special_chars = bidict.bidict({" ": "~", "!": "!", "\"": "*8", "$": "*7", "%": "*g", "'": "*8", "(": "(", ")": ")", "*": "*s", "=": "*h", +special_chars = bidict.bidict({" ": "~", "!": "!", "$": "*7", "%": "*g", "'": "*8", "(": "(", ")": ")", "*": "*s", "=": "*h", "?": "=", "@": "*m", "^": "*l", "_": "*0", "€": "*d", "à": "*a", "â": "*k", "ç": "*c", "è": "*e", "é": "*r", "ê": "*b", "î": "*i", "ï": "*j", "ô": "*o", "ù": "*f", "û": "*u"}) diff --git a/tools/processors/messages.py b/tools/processors/messages.py index 59905f3..778dfca 100755 --- a/tools/processors/messages.py +++ b/tools/processors/messages.py @@ -28,7 +28,7 @@ class CommandsDispatcherProcessor(DispatcherBotProcessor): or text.upper().startswith("/" + trigger) def process(self, text: str, sender_id : str, users_list: UserList): - without_cmd = text[len(users_list.my_name)+1:] + without_cmd = text[len(self.trigger)+1:] response = super().process(without_cmd, sender_id, users_list) return Message(self.default_response) if response is None else response @@ -65,7 +65,8 @@ class CocoConnectCommand(BaseCocobotCommand): if len(zip_code) != 5 or not zip_code.isnumeric(): return Message("Le code postal c'est 5 chiffres, pd") - return self.cococlient.connect(nick, int(age), True, zip_code) + self.cococlient.connect(nick, int(age), True, zip_code) + return BotMessage("Connecté en tant que %s, de %s ans" % (nick, age)) class CocoMsgCommand(BaseCocobotCommand):