Added more logging to Bot; Connection restarting and authentication checking now...
authorGeorgios Atheridis <atheridis@tutamail.com>
Thu, 2 Jun 2022 17:52:22 +0000 (20:52 +0300)
committerGeorgios Atheridis <atheridis@tutamail.com>
Thu, 2 Jun 2022 17:52:22 +0000 (20:52 +0300)
aptbot/args.py
aptbot/bot.py
aptbot/constants.py
aptbot/main.py
requirements.txt
ttv_api/__init__.py

index 6f9801bbb64bcab90468189d06a0392e81595fa5..6cae837a9cb6f847c6b1f584415e39a021255646 100644 (file)
@@ -4,49 +4,49 @@ import argparse
 def parse_arguments() -> argparse.Namespace:
     arg_parser = argparse.ArgumentParser(
         prog="aptbot",
-        description="A chat bot for twitch.tv"
+        description="A chat bot for twitch.tv",
     )
 
     arg_parser.add_argument(
         "-a",
         "--add-account",
         type=str,
-        help=f"Add an account to connect with the bot"
+        help=f"Add an account to connect with the bot",
     )
 
     arg_parser.add_argument(
         "-d",
         "--disable-account",
         type=str,
-        help=f"Disable an account from the bot"
+        help=f"Disable an account from the bot",
     )
 
     arg_parser.add_argument(
         "-s",
         "--send-message",
         type=str,
-        help=f"Send a message to skgyorugo"
+        help=f"Send a message to skgyorugo",
     )
 
     arg_parser.add_argument(
         "--enable",
         default=False,
         action="store_true",
-        help=f"Enable the bot"
+        help=f"Enable the bot",
     )
 
     arg_parser.add_argument(
         "--disable",
         default=False,
         action="store_true",
-        help=f"Disable the bot"
+        help=f"Disable the bot",
     )
 
     arg_parser.add_argument(
         "--update",
         default=False,
         action="store_true",
-        help=f"Update the bot"
+        help=f"Update the bot",
     )
 
     return arg_parser.parse_args()
index c4c548f9533cee154984456c804378dcc2521da8..e9f5d46c94dea6aea17f280ca494e8b0e05d2df7 100644 (file)
@@ -1,6 +1,7 @@
 import logging
 import re
 import socket
+import sys
 import time
 from abc import ABC, abstractmethod
 from dataclasses import dataclass, field
@@ -47,15 +48,22 @@ class Bot(ABCBot):
         self._nick = nick
         self._oauth_token = oauth_token
         self._connected_channels = set()
+        self._buffered_messages = []
 
     def _send_command(self, command: str):
         if "PASS" not in command:
-            logger.debug(f"< {command}")
+            logger.info(f"< {command}")
         self._irc.send((command + "\r\n").encode())
 
-    def connect(self):
+    def connect(self) -> bool:
+        self._connect()
+        connected = self._connected()
+        return connected
+
+    def _connect(self) -> None:
         self._irc = socket.socket()
         self._irc.connect((self._server, self._port))
+        logger.debug("Connecting...")
         self._send_command(f"PASS oauth:{self._oauth_token}")
         self._send_command(f"NICK {self._nick}")
         self._send_command(
@@ -112,7 +120,7 @@ class Bot(ABCBot):
     @staticmethod
     def _parse_message(received_msg: str) -> Message:
         split = re.search(
-            r"(?:(?:@(.+))\s)?:(?:(?:(\w+)!\w+@\w+\.)?.+)\s(\w+)\s\#(\w+)\s:?(.+)?",
+            r"(?:@(.+)\s)?:(?:(?:(\w+)!\w+@\w+\.)?.+)\s(\w+)\s(?:\#(\w+)|\*)\s:?(.+)?",
             received_msg,
         )
 
@@ -127,13 +135,16 @@ class Bot(ABCBot):
                     tag_value = Bot._replace_escaped_space_in_tags(tag.split("=")[1])
                 tags[tag_name] = " ".join(tag_value.split())
 
-        nick = split[2]
+        nick = split[2] if split[2] else ""
         try:
             command = Commands[split[3]]
         except KeyError:
             return Message()
-        channel = split[4]
-        value = " ".join(split[5].split())
+        channel = split[4] if split[4] else ""
+        try:
+            value = " ".join(split[5].split())
+        except AttributeError:
+            value = ""
 
         return Message(
             tags=tags,
@@ -144,7 +155,7 @@ class Bot(ABCBot):
         )
 
     def _handle_message(self, received_msg: str) -> Message:
-        logger.debug(received_msg)
+        logger.info(f"> {received_msg}")
         if received_msg == "PING :tmi.twitch.tv":
             self._send_command("PONG :tmi.twitch.tv")
             return Message()
@@ -152,9 +163,8 @@ class Bot(ABCBot):
             return Message()
         return Bot._parse_message(received_msg)
 
-    def receive_messages(self) -> list[Message]:
-        messages = []
-        while True:
+    def _receive_messages(self) -> bytes:
+        for _ in range(10):
             try:
                 received_msgs = self._irc.recv(2048)
             except ConnectionResetError as e:
@@ -163,17 +173,44 @@ class Bot(ABCBot):
                 self._restart_connection()
             else:
                 break
-        for received_msgs in received_msgs.decode("utf-8").split("\r\n"):
-            messages.append(self._handle_message(received_msgs))
+        else:
+            logger.error("Unable to connect to twitch. Exiting")
+            sys.exit(1)
+        return received_msgs
+
+    def _connected(self) -> bool:
+        received_msgs = self._receive_messages()
+        for received_msg in received_msgs.decode("utf-8").split("\r\n"):
+            self._buffered_messages.append(self._handle_message(received_msg))
+        if self._buffered_messages[0] == Message(
+            {},
+            "",
+            Commands.NOTICE,
+            "",
+            "Login authentication failed",
+        ):
+            logger.debug(f"Not connected")
+            return False
+        logger.debug(f"Connected")
+        return True
+
+    def get_messages(self) -> list[Message]:
+        messages = []
+        messages.extend(self._buffered_messages)
+        self._buffered_messages = []
+        received_msgs = self._receive_messages()
+        for received_msg in received_msgs.decode("utf-8").split("\r\n"):
+            messages.append(self._handle_message(received_msg))
         return messages
 
     def disconnect(self) -> None:
+        logger.debug("Disconnecting...")
         self._irc.close()
 
-    def _restart_connection(self) -> None:
+    def _restart_connection(self):
         self.disconnect()
         time.sleep(5)
-        self.connect()
+        self._connect()
         self.join_channels(self._connected_channels)
         time.sleep(2)
 
index 77ac5206e1cf8b2752a4994713f2aa0c210511d5..6916bad7adbeda38cea038aba82c5077ecca3c15 100644 (file)
@@ -21,6 +21,8 @@ else:
     print("Your OS is not supported", file=sys.stderr)
     sys.exit(1)
 
+MAIN_FILE_NAME = "main.py"
+
 
 PORT = 26538
 LOCALHOST = "127.0.0.1"
index 8b0bc9ab13d7e8fa086664216f99b487c988ca17..5d14c837ad0b3701c414cc1f6147607ff57f9093 100644 (file)
@@ -15,7 +15,14 @@ from dotenv import load_dotenv
 from . import args_logic
 from .args import parse_arguments
 from .bot import Bot, Message
-from .constants import CONFIG_LOGS, CONFIG_PATH, LOCALHOST, LOGGING_DICT, PORT
+from .constants import (
+    CONFIG_LOGS,
+    CONFIG_PATH,
+    LOCALHOST,
+    LOGGING_DICT,
+    MAIN_FILE_NAME,
+    PORT,
+)
 
 logging.config.dictConfig(LOGGING_DICT)
 logger = logging.getLogger(__name__)
@@ -24,8 +31,9 @@ load_dotenv()
 
 
 def handle_message(bot: Bot, modules: dict[str, ModuleType]):
+    logger.debug("in handle_message thread")
     while True:
-        messages = bot.receive_messages()
+        messages = bot.get_messages()
         for message in messages:
             if not message.channel:
                 continue
@@ -76,15 +84,15 @@ def load_modules(modules: dict[str, ModuleType]):
     ]
     channels = filter(lambda x: not x.startswith("."), channels)
     for channel in channels:
-        account_path = os.path.join(CONFIG_PATH, f"{channel}")
+        account_path = os.path.join(CONFIG_PATH, channel)
         sys.path.append(account_path)
-        module_path = os.path.join(account_path, "main.py")
+        module_path = os.path.join(account_path, MAIN_FILE_NAME)
         spec = importlib.util.spec_from_file_location(
             "main",
             module_path,
         )
         if not spec or not spec.loader:
-            print("Problem loading spec")
+            logger.warning(f"Problem loading for {channel}")
             sys.path.remove(account_path)
             continue
         module = importlib.util.module_from_spec(spec)
@@ -99,6 +107,7 @@ def load_modules(modules: dict[str, ModuleType]):
 
 
 def initialize(bot: Bot):
+    logger.debug("Initializing...")
     channels = [
         c
         for c in os.listdir(CONFIG_PATH)
@@ -114,13 +123,15 @@ def listener():
     if NICK and OAUTH:
         bot = Bot(NICK, OAUTH)
     else:
-        print(
-            "Please set the environment variables:\nAPTBOT_NICK\nAPTBOT_PASS",
-            file=sys.stderr,
+        logger.error(
+            "The environment variables:\nAPTBOT_NICK\nAPTBOT_PASS\nare not set."
         )
         time.sleep(3)
         sys.exit(1)
-    bot.connect()
+    if not bot.connect():
+        logger.error("Twitch couldn't authenticate your credentials")
+        time.sleep(3)
+        sys.exit(1)
     modules = {}
     message_loop = Thread(
         target=start,
@@ -164,16 +175,6 @@ def listener():
         time.sleep(1)
 
 
-def send(func):
-    def inner(*args, **kwargs):
-        s = socket.socket()
-        s.connect((LOCALHOST, PORT))
-        func(s, *args, **kwargs)
-        s.close()
-
-    return inner
-
-
 def main():
     argsv = parse_arguments()
     os.makedirs(CONFIG_PATH, exist_ok=True)
index 55ab89d8ffdebab5f7ae208dd29671aa29dae645..8465e5d792cef13dcdc1e1746d1f431229ddbcf8 100644 (file)
@@ -1,13 +1,20 @@
+attrs==21.4.0
 black==22.3.0
 click==8.1.3
 flake8==4.0.1
 flake8-black==0.3.3
+iniconfig==1.1.1
 mccabe==0.6.1
 mypy-extensions==0.4.3
+packaging==21.3
 pathspec==0.9.0
 platformdirs==2.5.2
+pluggy==1.0.0
+py==1.11.0
 pycodestyle==2.8.0
 pyflakes==2.4.0
+pyparsing==3.0.9
+pytest==7.1.2
 python-dotenv==0.20.0
 tomli==2.0.1
 urllib3==1.26.9
index 3f55784ac65e94259457786a49a7b42129eade81..92fb7f8d8b2cf4e429dba334c09ebaaff2f68cd1 100644 (file)
@@ -1,13 +1,14 @@
-import os
-import urllib3
 import json
-from datetime import datetime
-from typing import Optional, Union
+import os
 from dataclasses import dataclass
+from datetime import datetime
 from enum import Enum
-# from dotenv import load_dotenv
+from typing import Optional, Union
+
+import urllib3
+from dotenv import load_dotenv
 
-load_dotenv()
+load_dotenv()
 
 NICK = os.getenv("APTBOT_NICK")
 OAUTH = os.getenv("APTBOT_OAUTH")