text: from selenium import webdriver
  from selenium.webdriver.common.by import By
  from selenium.webdriver.firefox.options import Options
  from selenium.webdriver.common.keys import Keys
  import pickle
  from datetime import datetime
  from datetime import timedelta
  import time
  import pathlib
  import os
  import math
  
  from colorama import init
  from threading import Thread
  init()
  
  class ChatBot:
      browser = None
      channel = ''
      error = 0
      isReady = 0
      refreshHour = None
      isClosed = 0
      callback_error = None
  
      chat_message_callback = None
      
      stored_msgs = []
      avg_delay = 0
      mps = 0
      
      automatic_list = True
  
      def auto_list(self):
          self.list_chat()
          self.list_deleted()
          time.sleep(0.1)
          Thread(target=self.auto_list).start()
  
      def empty_callback_error(self, function, channel, msg, info = ''):
          print(function, msg, info)
  
      def __init__(self, channel, chat_message_callback = None):
          try:
              self.channel = channel.strip()
              self.chat_message_callback = chat_message_callback
          except Exception as e:
              self.error = 1
              self.callback_error('ChatBot.py > __init__()', self.channel, str(e), '')
  
      def start(self):
          try:
              print('\x1b[2;30;46m' + 'Iniciando: ' + self.channel + '\x1b[0m')
  
              if self.callback_error == None:
                  self.callback_error = self.empty_callback_error
  
              options = Options()
              options.add_argument("--start-minimized")
              options.add_argument('--headless')
              options.add_argument('--disable-gpu')
              self.browser = webdriver.Firefox(options=options)
              self.browser.install_addon(os.path.join(pathlib.Path(__file__).parent.resolve(), 'betterttv-7.4.25-fx.xpi'))
              url = 'https://www.twitch.tv/popout/' + self.channel + '/chat?popout='
  
              self.browser.get(url.lower())
              self.refreshHour = datetime.now() + timedelta(minutes=60)
              self.isReady = 1
              print('Configuração concluída')
              if self.automatic_list:
                  self.auto_list()
          except Exception as e:
              self.error = 1
              self.callback_error('ChatBot.py > start()', self.channel, str(e), '')
  
      def close(self):
          try:
              self.isClosed = 1
              self.browser.close()
              self.browser.quit()
              del self.browser
              try:
                  del self
              except Exception as e:
                  print('del self - ' + self.channel + ' ' + str(e))
          except Exception as e:
              self.callback_error('ChatBot.py > close()', self.channel, str(e), '')
  
      def list_deleted(self):
          msgs = []
          try:
              if len(self.browser.find_elements(By.CLASS_NAME, 'chat-line__message--deleted-notice')) > 0:
                  chatElements = self.browser.find_elements(By.CLASS_NAME, 'captured')
                  for chatElement in chatElements:
                      if (chatElement != []):
                          message = ""
                          for chatText in chatElement.find_elements(By.CLASS_NAME, 'chat-line__message--deleted-notice'):
                              message += chatText.text
                              self.browser.execute_script(
                                  "var element = arguments[0];" +
                                  "if (element.className.includes('chat-line__message--deleted-notice'))" +
                                  "{element.setAttribute('class','');}",
                                  chatText)
  
                          if (message == 'message excluída por um moderador.'):
                              msgs.append(chatElement.get_attribute("id"))
  
                              self.browser.execute_script(
                                  "var element = arguments[0];" +
                                  "if (element.className === 'captured')" +
                                  "{element.setAttribute('class','del');}",
                                  chatElement)
          except Exception as e:
              self.callback_error('ChatBot.py > listDeleted()', self.channel, str(e), '')
          return msgs
  
      def calc_duration(function):
          def wrapper(*args, **kwargs):
              start_time = time.time()
              result = function(*args, **kwargs)
              result['delay'] = int((time.time() - start_time) * 1000)
              return result
          return wrapper
  
      @calc_duration
      def process_messges(self, chatElement):
          chatMsg = {
              "id": "",
              "channel": "",
              "user": "",
              "message": "",
              "badges": [],
              "emotes": [],
              "datetime": "",
              "color": ""
          }
  
          try:
              chatMsg['channel'] = self.channel
              chatMsg['datetime'] = datetime.now()
              chatMsg['id'] = hex(int(chatMsg['datetime'].strftime("%Y%m%d%H%M%S%f")) - 20000000000000000000).replace('0x', '')
          except Exception as e:
              self.callback_error('ChatBot.py > process_messges() > configurando', 
                  self.channel, str(e), str(chatElement.get_attribute('innerHTML')))
  
          try:
              for chatUser in chatElement.find_elements(By.CLASS_NAME, 'chat-author__display-name'):
                  chatMsg['user'] = chatUser.text
                  chatMsg['color'] = chatUser.get_attribute("style")
          except Exception as e:
              self.callback_error('ChatBot.py > process_messges() > usuário e cor', 
                  self.channel, str(e), str(chatElement.get_attribute('innerHTML')))
          
          try:
              message = ""
              for chatText in chatElement.find_elements(By.CSS_SELECTOR,
                      ".text-fragment, .link-fragment, .mention-fragment, .chat-line__message--deleted-notice, .chat-line__message--emote"):
                  if "chat-line__message--emote" in chatText.get_attribute("class"):
                      try:
                          chatMsg['emotes'].append({"alt": chatText.get_attribute("alt"), "src": chatText.get_attribute("src")})
                          message += chatText.get_attribute("alt")
                          message += ' '
                      except:
                          pass
                  else:
                      message += chatText.text + ' '
              chatMsg['message'] = message.strip()
          except Exception as e:
              self.callback_error('ChatBot.py > process_messges() > message e emotes', 
                  self.channel, str(e), str(chatElement.get_attribute('innerHTML')))
  
          try:
              for badgeElement in chatElement.find_elements(By.CLASS_NAME, 'chat-badge'):
                  src = badgeElement.get_attribute("src")
                  src = src[0: len(src) - 1] + '2'
                  alt = badgeElement.get_attribute("alt")
                  chatMsg['badges'].append({"src": src, "alt": alt})
          except Exception as e:
              self.callback_error('ChatBot.py > process_messges() > capturar badges', 
                  self.channel, str(e), str(chatElement.get_attribute('innerHTML')))
  
          try:
              self.browser.execute_script(
                  "let element = arguments[0];" +
                  "let clsname = element.className;"
                  "if (clsname.includes('chat-line__message') || clsname.includes('chat-line--inline'))" +
                  "{element.setAttribute('class','captured');" +
                  "element.setAttribute('id','" + chatMsg['id'] + "');}",
                  chatElement)
          except Exception as e:
              self.callback_error('ChatBot.py > process_messges() > executar script de captura', 
                  self.channel, str(e), str(chatElement.get_attribute('innerHTML')))
  
          return chatMsg
  
      def list_chat(self):
          msgs = []
          try:
              if (self.channel.lower() in self.browser.current_url.lower()):
                  chatElements = self.browser.find_elements(By.CLASS_NAME, 'chat-line__message')
                  for chatElement in chatElements:
                      if (chatElement != []):
                          chatMsg = self.process_messges(chatElement)
                          msgs.append(chatMsg)
                          
                          if self.chat_message_callback != None:
                              self.chat_message_callback(chatMsg)
                          chatElement = []
  
                          self.stored_msgs.append({
                              'delay': chatMsg['delay'],
                              'datetime': datetime.now()
                          })
                          self.stored_msgs = self.stored_msgs[-100:]
                          delay_total = 0
                          for msg in self.stored_msgs:
                              delay_total += msg['delay']
                          self.avg_delay = int(delay_total / len(self.stored_msgs))
  
                          if len(self.stored_msgs) > 10:
                              stored_time = abs(self.stored_msgs[-1]['datetime']-self.stored_msgs[0]['datetime']).seconds
                              self.mps = len(self.stored_msgs) / stored_time
                              factor = 10.0 ** 2
                              self.mps = math.trunc(self.mps * factor) / factor
              else:
                  self.error = 1
          except Exception as e:
              self.callback_error('ChatBot.py > listChat()', self.channel, str(e), '')
              self.error = 1
  
          if self.isReady:
              # recarrega a pagina em um tempo determinado
              if (datetime.now() >= self.refreshHour):
                  try:
                      self.refreshHour = datetime.now() + timedelta(minutes=60)
                      self.browser.refresh()
                      time.sleep(1)
                  except Exception as e:
                      self.callback_error('ChatBot.py > listChat() > refresh', self.channel, str(e), '')
              #################################################
          return msgs
  