def run(self, profile_path="/data/firefox_cache"):
        """
        Faz a coleta dos metadados de grupos de Whatsapp de acordo
        com os parâmetros fornecidos na criação do objeto de coleta.

        Parâmetros
        ------------
            profile_path : str
                Caminho para um profile alternativo do navegador
                utilizado na coleta.
        """

        today = datetime.date.today().strftime('%Y-%m-%d')
        all_groups_filename = '/data/all_grupos_%s.json' % (today)
        with open(all_groups_filename, 'w') as json_file:
            print('Collecting metadata for groups at %s' % (today))

        if not os.path.exists(profile_path):
            os.makedirs(profile_path)

        driver = WhatsAPIDriver(loadstyles=True,
                                profile=profile_path,
                                client="remote",
                                command_executor=os.environ["SELENIUM"])

        try:
            print("Waiting for WhatsApp Web Login")
            driver.wait_for_login()
            print("Saving session")
            driver.save_firefox_profile(remove_old=False)
            print("Bot started")

            pathlib.Path("/data/grupos").mkdir(parents=True, exist_ok=True)

            print('>>>>>>>>>>> Loading chat ids')
            chats = driver.get_all_chats()

            for chat in (chats):
                # Does not collect direct messages, only group chats
                if not chat._js_obj['isGroup']:
                    continue

                gid = chat.id
                gid = gid.split('@')[0]
                s_name = self._process_string(chat.name)

                # Skip group if it is on blacklist (can be name or groupID)
                if (s_name in self.group_blacklist
                        or gid in self.group_blacklist):
                    continue

                group = dict()

                _id = chat.id
                creator = _id.split('-')[0]
                timestamp = _id.split('-')[-1].split('@')[0]
                date = convert_data_from_timestamp(float(timestamp))
                str_date = date.strftime('%Y-%m-%d %H:%M:%S')
                name = chat.name.strip().replace('\t', ' ')

                kind = chat._js_obj["kind"]

                participants = list()
                for member in driver.group_get_participants(_id):
                    user = dict()
                    user['name'] = member.verified_name
                    user['short_name'] = member.short_name
                    user['nome_formatado'] = member.formatted_name
                    user['number'] = member.id
                    user['isBusiness'] = member.is_business
                    user['profile_pic'] = member.profile_pic
                    participants.append(user)

                group['group_id'] = _id
                group['creator'] = creator
                group['kind'] = kind
                group['creation'] = dict()
                group['creation']['creation_date'] = str_date
                group['creation']['creation_timestamp'] = timestamp
                group['title'] = name
                group['members'] = participants

                path = '/data/grupos/'
                filename = '%sgrupos_%s.json' % (path,
                                                 _id.split('@')[0].strip())
                print(group)
                with open(filename, 'w') as json_file:
                    json.dump(group, json_file)
                    print('', file=json_file)
                with open(all_groups_filename, 'a') as json_file:
                    json.dump(group, json_file)
                    print('', file=json_file)

            driver.close()
        except Exception as e:
            print(e)
            driver.close()
            raise Exception(e)
Example #2
0
    def run(self, profile_path="/data/firefox_cache"):
        """
        Faz a coleta das mensagens de grupos de Whatsapp de acordo
        com os parâmetros fornecidos na criação do objeto de coleta.

        Parâmetros
        ------------
            profile_path : str
                Caminho para um profile alternativo do navegador
                utilizado na coleta.
        """
        if not os.path.exists(profile_path):
            os.makedirs(profile_path)
        driver = WhatsAPIDriver(loadstyles=True, profile=profile_path,
                                client="remote",
                                command_executor=os.environ["SELENIUM"])

        pathlib.Path("/data/mensagens").mkdir(parents=True, exist_ok=True)
        pathlib.Path("/data/image").mkdir(parents=True, exist_ok=True)
        pathlib.Path("/data/audio").mkdir(parents=True, exist_ok=True)
        pathlib.Path("/data/video").mkdir(parents=True, exist_ok=True)
        pathlib.Path("/data/mensagens_grupo").mkdir(parents=True, exist_ok=True)
        pathlib.Path("/data/notificacoes").mkdir(parents=True, exist_ok=True)
        pathlib.Path("/data/mids").mkdir(parents=True, exist_ok=True)

        min_date = self.start_date
        max_date = self.end_date
        include_notf = self.collect_notifications
        looping = True
        if (self.collection_mode == 'period') and (min_date < '2020-01-01'):
            raise Exception("Can't start collection without a start and end"
                            " date.")

        while looping:

            if self.collection_mode == 'continuous':
                looping = True
            else:
                looping = False

            try:
                print("Waiting for WhatsApp Web Login")
                driver.wait_for_login()
                print("Saving session")
                driver.save_firefox_profile(remove_old=False)
                print("Bot started")

                print('>>>>>>>>>>> Loading previous saved Messages')
                messagesID = self._get_load_messages()
                notificationsID = self._get_load_notifications()

                today_date = datetime.date.today().strftime("%Y-%m-%d")
                date_format = "%Y-%m-%d"
                file_name = "/data/mensagens/mensagens_" + today_date + ".json"
                start_date = min_date

                print('>>>>>>>>>>>>Getting Groups Messages...', end=' ')
                chats = driver.get_all_chats()
                count = 0
                all_chats = list(chats)

                print(' DONE! %d chats loaded!' % (len(all_chats)))
                random.shuffle(all_chats)

                for chat in (all_chats):
                    # Does not collect direct messages, only group chats
                    if not chat._js_obj['isGroup']:
                        continue

                    gid = chat.id
                    gid = gid.split('@')[0]
                    s_name = self._process_string(chat.name)

                    # Skip group if it is on blacklist (can be name or groupID)
                    if (s_name in self.group_blacklist or
                            gid in self.group_blacklist):
                        continue

                    # PRINT CHAT INFORMATION
                    members = chat._js_obj['groupMetadata']['participants']
                    timestamp = gid.split('-')[-1]
                    date = convert_data_from_timestamp(float(timestamp))
                    str_date = date.strftime('%Y-%m-%d %H:%M:%S')

                    chat_print = "<Group chat - {name}: {id}, {participants} " \
                        "participants - at {time}!!>".format(
                            name=s_name, id=gid, participants=len(members),
                            time=str_date)
                    print('>>>>>Loading messages from', chat_print)

                    if gid not in messagesID:
                        messagesID[gid] = dict()
                        messagesID[gid]['messages'] = set()
                        messagesID[gid]['date'] = '2000-01-01'

                    # PROCESS PREVIOUS LOADED MESSAGES ID AND LAST DATE
                    if self.collection_mode == 'continuous':
                        if messagesID[gid]['date'] > max_date:
                            continue
                        if messagesID[gid]['date'] > min_date:
                            start_date = messagesID[gid]['date']
                            till_date = datetime.datetime.strptime(start_date,
                                                                   date_format)
                        else:
                            start_date = min_date
                            till_date = datetime.datetime.strptime(start_date,
                                                                   date_format)

                        # LOAD MESSAGES FROM WHATSAPP SINCE MIN_DATE
                        messages = chat.load_earlier_messages_till(till_date)
                        messages = driver.get_all_message_ids_in_chat(
                            chat, include_notifications=include_notf)

                    elif self.collection_mode == 'period':
                        till_date = datetime.datetime.strptime(start_date,
                                                               date_format)
                        # LOAD MESSAGES FROM WHATSAPP SINCE MIN_DATE
                        messages = chat.load_earlier_messages_till(till_date)
                        messages = driver.get_all_message_ids_in_chat(
                            chat, include_notifications=include_notf)

                    elif self.collection_mode == 'unread':
                        # LOAD UNREAD MESSAGES FROM WHATSAPP
                        messages = chat.get_unread_messages(
                            include_me=False,
                            include_notifications=include_notf)

                    print('>>>>>Total messages %d' % (len(messages)))
                    count += 1

                    for msg in messages:
                        count += 1
                        gid = gid.split('@')[0]
                        mid = msg

                        if self._is_notification(mid):
                            if gid not in notificationsID.keys():
                                notificationsID[gid] = set()
                            if mid.strip() in notificationsID[gid]:
                                continue
                            j = driver.get_message_by_id(mid)
                            self._save_notification_(j, gid)
                            continue

                        if mid.strip() in messagesID[gid]['messages']:
                            print('Message: %d >>> %s from %s was CHECKED' %
                                  (count, mid, gid))
                            continue

                        else:
                            try:
                                j = driver.get_message_by_id(mid)
                            except Exception as e:
                                print('Error getting a message >>', e)
                                continue
                            if not j:
                                continue

                        sender = j.sender.id
                        sender = sender.replace(' ', '').strip()
                        sender = sender.split('@')[0]
                        if (sender in self.user_blacklist or
                                '+' + sender in self.user_blacklist):
                            continue

                        try:
                            date = self._get_date_from_message(j)
                        except Exception:
                            continue

                        if (date > max_date) and (self.collection_mode == 'period'):
                            break
                        if (date < start_date):
                            continue

                        # Update day
                        if today_date != date:
                            today_date = date
                            file_name = "/data/mensagens/mensagens_" + today_date + ".json"

                        if self.collect_images:
                            try:
                                self._get_image_from_message(j)
                            except Exception as ei:
                                print('!!!!Error getting image!!!! ', ei)

                        if self.collect_videos:
                            try:
                                self._get_video_from_message(j)
                            except Exception as ev:
                                print('!!!!Error getting video!!!! ', ev)

                        if self.collect_audios:
                            try:
                                self._get_audio_from_message(j)
                            except Exception as ea:
                                print('!!!!Error getting audio!!!! ', ea)

                        if self.collect_messages:
                            self._save_message(j, s_name, gid, mid, file_name)

                driver.close()
            except Exception as e:
                print(e)
                driver.close()
                raise Exception(e)
            if looping:
                print('Waiting code to start again...')
                time.sleep(3600)
Example #3
0
                    # Deactivating reload
                    with db_conn.cursor() as cur:
                        cur.execute(deactivate_reload, (
                            process_note,
                            reload_contact_row_id,
                        ))
                        db_conn.commit()
                    logging.info("Reloading deactivated for " +
                                 str(reload_contact_row_sender))
                    chat.send_seen()

        else:
            logging.debug("Nothing to reload, continue")

except Exception as e:
    logging.exception(e)
    if 'driver' in locals() and driver is not None:
        driver.close()
        logging.info('Selenium driver connection closed')
    if 'db_conn' in locals() and db_conn is not None:
        db_conn.close()
        logging.info('Database connection closed')
    raise
finally:
    if 'driver' in locals() and driver is not None:
        driver.close()
        logging.info('Selenium driver connection closed')
    if 'db_conn' in locals() and db_conn is not None:
        db_conn.close()
        logging.info('Database connection closed')
Example #4
0
class WPChannelBot():
    def __init__(self):
        self.model = WPChannelBotModel()
        self.data = self.model.get_all()
        self.convs = self.model.get_convs()
        self.convs_state = self.model.get_convs_state()

        self.simple_steps = True
        self.log_file = "log/chatbot.log"

        self.cmd_wait_from = None
        self.cmd_wait = False

        self.profile = "profile"
        self.driver = None

    def start(self):
        print("Iniciando bot...")
        self.driver = WhatsAPIDriver(profile=self.profile)
        time.sleep(3)
        if not self.driver.get_status() == "LoggedIn":
            print("Carregando QRCode")
            self.driver.get_qr("qrcode.png")

            print("Escaneie o QRCode no arquivo qrcode.png")
            self.driver.wait_for_login()

        print("Bot iniciado")
        self.driver.save_firefox_profile()

        while True:
            time.sleep(1)
            for contact in self.driver.get_unread(include_me=False,
                                                  include_notifications=True,
                                                  use_unread_count=True):
                if len(contact.messages) == 1:
                    for message in contact.messages:
                        if isinstance(message, Message):
                            self.new_message(message.content, contact)
                            self.driver.chat_send_seen(contact.chat.id)
                            time.sleep(3)
                else:
                    contact.chat.send_message(
                        "Fico confuso com muitas mensagens :S Por favor, envie uma de cada vez e espere eu responder tá?"
                    )
                    contact.chat.send_message(CHANNEL_ASK_KEYWORD)

    def new_message(self, message, contact):
        if not self._is_cmd(message):
            if self.cmd_wait and contact.chat.id == self.cmd_wait_from:
                self._cmd_envio(message, contact.chat)

            elif not contact.chat.id in self.convs:
                self._proc_etapa(contact.chat.id, message, contact.chat, 2)
            else:
                for conv in self.convs_state:
                    if conv['id'] == contact.chat.id:
                        e = self._proc_etapa(contact.chat.id, message,
                                             contact.chat, conv['etapa'])
                        conv['etapa'] = e

                        self.model.conv_update(contact.chat.id, e)
        else:
            print("ADMINISTRADOR")
            self._run_cmd(message, contact.chat)

    def shutdown(self):
        print("Desconectando...")
        self.driver.close()
        time.sleep(3)
        print("Desconectado")

    def _already_user(self, id, chat):
        if isinstance(self.model.get(id), dict):
            chat.send_message(
                "Olá, você já está cadastrado neste canal. Assim que tiver novidade você vai receber!"
            )
            return True
        else:
            return False

    def _is_keyword(self, content, chat):
        if content.lower() == CHANNEL_KEYWORD:
            return True
        else:
            chat.send_message(CHANNEL_ASK_KEYWORD)
            return False

    def _proc_etapa(self, id, content, chat, etapa):
        if etapa == 2:
            if not self._already_user(id, chat) and self._is_keyword(
                    content, chat):
                # Efetua registros
                self.convs.append(id)
                self.convs_state.append({"id": id, "etapa": 4})
                self.model.conv_add(id, 4)

                # Introdução do canal - Solicita nome
                chat.send_message(CHANNEL_INTRO)
                chat.send_message(CHANNEL_MSGS[0])
                self._to_log("Iniciando cadastro: %s" % id)

        elif etapa == 4:
            # Armazena nome - Solicita cidade
            if self.simple_steps:
                self.data.append({"id": id, "nome": content})
                # Salva no banco de dados
                self.model.add(id, content)

                chat.send_message((CHANNEL_MSGS[3] % content))
                self._remove_convs(id)

                self._to_log("Finalizado cadastro: %s - %s" % (id, content))
            else:
                self.data.append({
                    "id": id,
                    "nome": content,
                    "cidade": "",
                    "bairro": ""
                })
                chat.send_message(CHANNEL_MSGS[1])
                # Salva no banco de dados
                self.model.add(id, content)

                self._to_log("Registrado nome: %s - %s" % (id, content))
                return 6

        elif etapa == 6:
            # Implementar veficação de validade de cidade
            # Verifica cidade - volta ao 5 : armazena cidade - solicita bairro ou passo
            for obj in self.data:
                if obj["id"] == id:
                    obj["cidade"] = content

                    self.model.update(id=id, cidade=content)
                    chat.send_message(CHANNEL_MSGS[2])

                    self._to_log("Registrado cidade: %s - %s" % (id, content))
            return 7
        elif etapa == 7:
            # Implementar veficação de validade de bairro
            if content == "passo":
                # Finaliza caso não seja informado bairro
                chat.send_message((CHANNEL_MSGS[3] % self._get_conv_nome(id)))

                self._remove_convs(id)
                self._to_log("Finalizado cadastro: %s - %s" % (id, content))
            else:
                # Armazena bairro - Finaliza cadastro
                for obj in self.data:
                    if obj["id"] == id:
                        obj["bairro"] = content
                        self.model.update(id=id, bairro=content)

                        chat.send_message(
                            (CHANNEL_MSGS[3] % self._get_conv_nome(id)))

                        self._remove_convs(id)
                        self._to_log("Finalizado cadastro: %s - %s" %
                                     (id, content))

    def _to_log(self, log):
        file = open(self.log_file, "a")
        file.write("\n>> %s " % log)
        file.close()
        return

    def _get_conv_nome(self, id):
        for obj in self.data:
            if obj["id"] == id:
                return obj["nome"]

    def _remove_convs(self, id):
        self.convs.remove(id)
        for conv in self.convs_state:
            if conv["id"] == id:
                self.convs_state.remove(conv)
                self.model.conv_delete(id)

    def _is_cmd(self, content):
        if content[:4] == "/cmd":
            return True
        else:
            return False

    def _run_cmd(self, content, chat):
        cmd = content[5:]
        if not self.model.check_admin(chat.id) == False:
            if cmd == "usuarios":
                self._cmd_usuarios(chat)
            elif cmd == "envio":
                self.cmd_wait = True
                self.cmd_wait_from = chat.id
                chat.send_message(
                    "*ENVIE A SEGUIR A MENSAGEM A SER ENVIADA PARA O CANAL*")
            else:
                chat.send_message("*COMANDO NÃO RECONHECIDO*")
        elif self.model.check_admin(id=None,
                                    all=True) == False and cmd[:5] == "admin":
            print("Cadastrando novo admin")
            self.model.add_admin(chat.id, content[11:])

            chat.send_message("*ADMINISTRADOR CADASTRADO*")
        else:
            chat.send_message(CHANNEL_ASK_KEYWORD)

    def _cmd_usuarios(self, chat):
        response = "*USUÁRIOS CADASTRADOS*\n\n"

        i = 0
        users = self.model.get_all()
        for user in users:
            i += 1
            response += "\n%d) %s - %s" % (i, user['id'], user['nome'])

        chat.send_message(response)

    def _cmd_envio(self, content, chat):
        i = 0
        users = self.model.get_all()
        for user in users:
            i += 1
            self.driver.send_message_to_id(user['id'], content)

        self.cmd_wait_from = None
        self.cmd_wait = False
        chat.send_message("*MENSAGEM ENVIADA PARA %d USUÁRIOS DO CANAL*" % i)