diff --git a/Attrap_bot.py b/Attrap_bot.py index 6970c93a2703665dc7cb6143a18e42e2db559220..18d9ff9e29b20a36927aa7dd9ed2748c7d278ec4 100644 --- a/Attrap_bot.py +++ b/Attrap_bot.py @@ -19,13 +19,15 @@ logger = logging.getLogger(__name__) class Attrap_bot: - class SMTP_sender: + class Email_sender: port = 587 ssl = False starttls = True - def __init__(self, hostname): + def __init__(self, hostname, email_from, email_to): self.hostname = hostname + self.email_from = email_from + self.email_to = email_to.split(',') def set_username(self, username): self.username = username @@ -49,11 +51,11 @@ class Attrap_bot: if starttls is True: self.ssl = False - def send(self, email_from, email_to, email_subject, email_message): + def send(self, email_subject, email_message): message = email.message.EmailMessage() message['Subject'] = email_subject - message['From'] = email_from + message['From'] = self.email_from message['Date'] = email.utils.formatdate() message.set_content(email_message) @@ -68,21 +70,27 @@ class Attrap_bot: if self.username: smtp.login(self.username, self.password) - for address in email_to.split(','): + for address in self.email_to: del message['To'] message['To'] = address del message['Message-ID'] - message['Message-ID'] = email.utils.make_msgid(domain=email_from.split('@')[-1]) + message['Message-ID'] = email.utils.make_msgid(domain=self.email_from.split('@')[-1]) smtp.send_message(message) + time.sleep(3) + logger.debug('Attente 3 secondes après envoi de l\'email...') smtp.quit() class Mastodon_sender: mastodon = None - def __init__(self, instance, access_token): + def __init__(self, instance, access_token, toot): self.instance = instance self.access_token = access_token + if not toot: + self.toot = "{name}\n\n{url}" + else: + self.toot = toot def send(self, message): if not self.mastodon: @@ -92,6 +100,8 @@ class Attrap_bot: ) self.mastodon.toot(message) + time.sleep(3) + logger.debug('Attente 3 secondes après envoi sur Mastodon...') def __init__(self, config): # On surcharge la configuration par défaut avec la configuration donnée @@ -103,6 +113,10 @@ class Attrap_bot: def analyze(self): if self.config.get('queries'): + # Liste des RAA à publier + raa_to_publish_mastodon = {} + raa_to_publish_email = {} + for i in self.config['queries']: query = self.config['queries'][i] query_id = i @@ -110,21 +124,27 @@ class Attrap_bot: administration = "" if query.get('administration') and query['administration']: administration = query['administration'] + if query.get('hashtag'): + hashtag = query['hashtag'] + else: + hashtag = query_id + + # Est-ce qu'il faudra envoyer le résultat sur Mastodon ? + if query.get('mastodon'): + send_to_mastodon = True + else: + send_to_mastodon = False + + # Est-ce qu'il faudra envoyer le résultat par mail ? + if query.get('email'): + send_email = True + else: + send_email = False logger.info(f'Démarrage de la recherche {query_id}') logger.info(f'Requête : {search}') logger.info(f'Administration : {administration}') - # On récupère de quoi envoyer par mail et sur Mastodon - smtp_sender = None - mastodon_sender = None - if query.get('email') and query['email'].get('smtp'): - logger.debug('Configuration SMTP trouvée') - smtp_sender = self.get_smtp_sender(query['email']['smtp']) - if query.get('mastodon') and query['mastodon'].get('instance'): - logger.debug('Configuration Mastodon trouvée') - mastodon_sender = self.get_mastodon_sender(query['mastodon']['instance']) - # On fabrique l'URL de requête one_week_ago = datetime.datetime.today() - datetime.timedelta(days=7) request_url = self.config['data_source'] @@ -172,49 +192,35 @@ class Attrap_bot: # Maintenant, on prend les derniers RAA à analyser, pour commencer par les plus anciens en premier for result in raa_candidates[::-1]: status = result['id'] - raa_name = result['name'] - raa_date = result['date'] - raa_administration = result['administration'] - raa_administration_name = result['administration_name'] - raa_url = result['url'] # On affiche le résultat dans la console if (self.config['console_output']): - print(f'\033[92m{raa_name}\033[0m ({raa_date}) : {raa_url}') - - # On publie sur Mastodon - if mastodon_sender and send_results: - toot = f'{raa_name} ({raa_date})\n\n{raa_url}' - if query['mastodon']['prefix'] and not query['mastodon']['prefix'] == '': - prefix = query['mastodon']['prefix'] - prefix = prefix.replace('{id}', query_id) - prefix = prefix.replace('{administration}', raa_administration) - prefix = prefix.replace('{administration_name}', raa_administration_name) - toot = f"{prefix}\n\n{toot}" - if query['mastodon']['suffix'] and not query['mastodon']['suffix'] == '': - suffix = query['mastodon']['suffix'] - suffix = suffix.replace('{id}', query_id) - suffix = suffix.replace('{administration}', raa_administration) - suffix = suffix.replace('{administration_name}', raa_administration_name) - toot = f"{toot}\n\n{suffix}" - mastodon_sender.send(toot) - time.sleep(3) - logger.debug('Attente 3 secondes après envoi sur Mastodon...') - - # On complète le mail - if smtp_sender and send_results: - if email_message == "": - email_message = f"Attrap ({query_id})\nRequête : {search}" - email_message = f"{email_message}\n\n{raa_administration_name} :\n{raa_name} ({raa_date})\nURL : {raa_url}" - - # On envoie le mail si le message est prêt - if not email_message == "" and send_results: - smtp_sender.send( - query['email']['from'], - query['email']['to'], - f"[Attrap] [{query_id}] Nouveaux éléments trouvés", - email_message - ) + print(f"\033[92m{result['name']}\033[0m ({result['date']}) : {result['url']}") + + # On ajoute le résultat à la liste des RAA à publier + if send_to_mastodon: + if not raa_to_publish_mastodon.get(query['mastodon']): + raa_to_publish_mastodon[query['mastodon']] = {} + # On regroupe les toots par RAA (un toot par RAA) + if not raa_to_publish_mastodon[query['mastodon']].get(result['id']): + raa_to_publish_mastodon[query['mastodon']][result['id']] = result + # On indique le hashtag de la requête + if not raa_to_publish_mastodon[query['mastodon']][result['id']].get('hashtags'): + raa_to_publish_mastodon[query['mastodon']][result['id']]['hashtags'] = [] + raa_to_publish_mastodon[query['mastodon']][result['id']]['hashtags'].append(hashtag) + + if send_email: + if not raa_to_publish_email.get(query['email']): + raa_to_publish_email[query['email']] = {} + # On regroupe les mails par administration (un mail par administration) + if not raa_to_publish_email[query['email']].get(result['administration']): + raa_to_publish_email[query['email']][result['administration']] = {} + if not raa_to_publish_email[query['email']][result['administration']].get(result['id']): + raa_to_publish_email[query['email']][result['administration']][result['id']] = result + # On indique le hashtag de la requête + if not raa_to_publish_email[query['email']][result['administration']][result['id']].get('queries'): + raa_to_publish_email[query['email']][result['administration']][result['id']]['queries'] = [] + raa_to_publish_email[query['email']][result['administration']][result['id']]['queries'].append(query_id) if os.path.isfile(status_file_path): os.remove(status_file_path) @@ -222,27 +228,93 @@ class Attrap_bot: status_file.write(status) status_file.close() + # On envoie un toot par RAA + if len(raa_to_publish_mastodon) > 0: + logger.info('Envoi des toots') + for sender_id in raa_to_publish_mastodon: + sender = self.get_mastodon_sender(sender_id) + if sender: + for raa_id in raa_to_publish_mastodon[sender_id]: + raa = raa_to_publish_mastodon[sender_id][raa_id] + raa['hashtags'] = list(dict.fromkeys(raa['hashtags'])) # On supprime les doublons + raa['hashtags_str'] = '' + for hashtag in raa['hashtags']: + if raa['hashtags_str'] == '': + raa['hashtags_str'] = f'#{hashtag}' + else: + raa['hashtags_str'] = f"{raa['hashtags_str']} #{hashtag}" + # On construit le texte du toot + toot = sender.toot + toot = toot.replace('{administration}', raa['administration']) + toot = toot.replace('{administration_name}', raa['administration_name']) + toot = toot.replace('{date}', raa['date']) + toot = toot.replace('{id}', raa['id']) + toot = toot.replace('{name}', raa['name']) + toot = toot.replace('{url}', raa['url']) + toot = toot.replace('{hashtags}', raa['hashtags_str']) + sender.send(toot) + + # On envoie un mail par administration + if len(raa_to_publish_email) > 0: + logger.info('Envoi des mails') + for sender_id in raa_to_publish_email: + sender = self.get_email_sender(sender_id) + if sender: + for administration_id in raa_to_publish_email[sender_id]: + # On construit le début du mail (on prend le pr) + message_header = f'Attrap : {administration_id}' + + queries = [] + + message_raa_list = 'RAA trouvés :' + # On ajoute chaque RAA au contenu du mail + for raa_id in raa_to_publish_email[sender_id][administration_id]: + raa = raa_to_publish_email[sender_id][administration_id][raa_id] + for query in raa['queries']: + queries.append(query) # On renseigne la liste des requêtes qui ont retourné un résultat pour les afficher plus bas + if not raa.get('queries_str'): + raa['queries_str'] = f'{query}' + else: + raa['queries_str'] = f"{raa['queries_str']}, {query}" + message_raa_list = f"{message_raa_list}\n - {raa['name']}\n URL : {raa['url']}\n Date : {raa['date']}\n Trouvé dans les requêtes : {raa['queries_str']}" + + # On construit la liste des requêtes + message_queries_overview = 'Requêtes :' + for query in queries: + message_queries_overview = f"{message_queries_overview}\n - {query} : {self.config['queries'][query]['search']}" + + # On construit le message et on l'envoie + message = f"{message_header}\n\n{message_queries_overview}\n\n{message_raa_list}" + subject = f'[Attrap][{administration_id}] Nouveaux éléments trouvés' + sender.send(subject, message) + def get_mastodon_sender(self, config_id): if self.config.get('mastodon'): if self.config['mastodon'].get(config_id) and self.config['mastodon'][config_id]['instance'] and self.config['mastodon'][config_id]['access_token']: - return Attrap_bot.Mastodon_sender(self.config['mastodon'][config_id]['instance'], self.config['mastodon'][config_id]['access_token']) + if self.config['mastodon'][config_id].get('toot'): + toot = self.config['mastodon'][config_id]['toot'] + else: + toot = None + return Attrap_bot.Mastodon_sender(self.config['mastodon'][config_id]['instance'], self.config['mastodon'][config_id]['access_token'], toot) logger.warning(f"La configuration Mastodon est invalide (id: {config_id})") return None - def get_smtp_sender(self, config_id): - if self.config.get('smtp'): - if self.config['smtp'].get(config_id) and self.config['smtp'][config_id]['hostname']: - smtp_sender = Attrap_bot.SMTP_sender(self.config['smtp'][config_id]['hostname']) - if self.config['smtp'][config_id].get('username'): - smtp_sender.set_username(self.config['smtp'][config_id]['username']) - if self.config['smtp'][config_id].get('password'): - smtp_sender.set_password(self.config['smtp'][config_id]['password']) - if self.config['smtp'][config_id].get('port'): - smtp_sender.set_port(self.config['smtp'][config_id]['port']) - if self.config['smtp'][config_id].get('ssl'): - smtp_sender.set_ssl(self.config['smtp'][config_id]['ssl']) - if self.config['smtp'][config_id].get('starttls'): - smtp_sender.set_starttls(self.config['smtp'][config_id]['starttls']) - return smtp_sender - logger.warning(f"La configuration Mastodon est invalide (id: {config_id})") + def get_email_sender(self, config_id): + if self.config.get('email'): + if self.config['email'].get(config_id) and self.config['email'][config_id]['hostname'] and self.config['email'][config_id]['from'] and self.config['email'][config_id]['to']: + email_sender = Attrap_bot.Email_sender(self.config['email'][config_id]['hostname'], self.config['email'][config_id]['from'], self.config['email'][config_id]['to']) + if self.config['email'][config_id].get('username'): + email_sender.set_username(self.config['email'][config_id]['username']) + if self.config['email'][config_id].get('password'): + email_sender.set_password(self.config['email'][config_id]['password']) + if self.config['email'][config_id].get('port'): + email_sender.set_port(self.config['email'][config_id]['port']) + if self.config['email'][config_id].get('ssl'): + email_sender.set_ssl(self.config['email'][config_id]['ssl']) + if self.config['email'][config_id].get('starttls'): + email_sender.set_starttls(self.config['email'][config_id]['starttls']) + return email_sender + logger.warning(f"La configuration Email est invalide (id: {config_id})") + if self.config['email'].get(config_id): + logger.debug(f"Configuration : {self.config['email'][config_id]}") return None diff --git a/README.md b/README.md index 487f34ccbd59a21a5321de98a33e82e3c7cd7474..4357cfe918c352f9afb790c209b6fef8e99146e7 100644 --- a/README.md +++ b/README.md @@ -35,20 +35,18 @@ queries: vsa_paris: search: "\"traitement algorithmique\" AND 2023-380" administration: "ppparis,pref75" - email: - from: "attrap@example.org" - to: "vsa@example.org" - smtp: example_smtp + email: example_smtp + hashtag: vsa vsa: search: "\"traitement algorithmique\" AND 2023-380" administration: - mastodon: - prefix: "[{administration_name}]" - suffix: "#{id} #{administration}" - instance: mastodon_social + mastodon: mastodon_social + hashtag: vsa -smtp: +email: example_smtp: + from: "attrap@example.org" + to: "vsa@example.org" hostname: "smtp.example.org" port: 587 username: "attrap@example.org" @@ -60,6 +58,7 @@ mastodon: mastodon_social: instance: "mastodon.social" access_token: "a-secret-access-token-here" + toot: "[{administration_name}]\n\n{name} ({date})\n\n{url}\n\n{hashtags} #{administration}" ``` ### Variables d'environnement @@ -69,21 +68,21 @@ Toutes les variables commencent par `ATTRAP_BOT__`. Les noms des variables sont ```bash ATTRAP_BOT__QUERIES__VSA_PARIS__SEARCH="\"traitement algorithmique\" AND 2023-380" \ ATTRAP_BOT__QUERIES__VSA_PARIS__SEARCH="ppparis,pref75" \ -ATTRAP_BOT__QUERIES__VSA_PARIS__EMAIL__FROM="attrap@example.org" \ -ATTRAP_BOT__QUERIES__VSA_PARIS__EMAIL__TO="vsa@example.org" \ -ATTRAP_BOT__QUERIES__VSA_PARIS__EMAIL__SMTP="example_smtp" \ +ATTRAP_BOT__QUERIES__VSA_PARIS__EMAIL="example_smtp" \ +ATTRAP_BOT__QUERIES__VSA_PARIS__HASHTAG="vsa" \ ATTRAP_BOT__QUERIES__VSA__SEARCH="\"traitement algorithmique\" AND 2023-380" \ -ATTRAP_BOT__QUERIES__VSA__MASTODON__PREFIX="[{administration_name}]" \ -ATTRAP_BOT__QUERIES__VSA__MASTODON__SUFFIX="#{id} #{administration}" \ -ATTRAP_BOT__QUERIES__VSA__MASTODON__INSTANCE="mastodon_social" \ -ATTRAP_BOT__SMTP__EXAMPLE_SMTP__HOSTNAME="smtp.example.org" \ -ATTRAP_BOT__SMTP__EXAMPLE_SMTP__PORT=587 \ -ATTRAP_BOT__SMTP__EXAMPLE_SMTP__USERNAME="attrap@example.org" \ -ATTRAP_BOT__SMTP__EXAMPLE_SMTP__PASSWORD="secr3t\!" \ -ATTRAP_BOT__SMTP__EXAMPLE_SMTP__SSL="False" \ -ATTRAP_BOT__SMTP__EXAMPLE_SMTP__STARTTLS="True" \ +ATTRAP_BOT__QUERIES__VSA__MASTODON="mastodon_social" \ +ATTRAP_BOT__EMAIL__EXAMPLE_SMTP__HOSTNAME="smtp.example.org" \ +ATTRAP_BOT__EMAIL__EXAMPLE_SMTP__FROM="attrap@example.org" \ +ATTRAP_BOT__EMAIL__EXAMPLE_SMTP__TO="vsa@example.org" \ +ATTRAP_BOT__EMAIL__EXAMPLE_SMTP__PORT=587 \ +ATTRAP_BOT__EMAIL__EXAMPLE_SMTP__USERNAME="attrap@example.org" \ +ATTRAP_BOT__EMAIL__EXAMPLE_SMTP__PASSWORD="secr3t\!" \ +ATTRAP_BOT__EMAIL__EXAMPLE_SMTP__SSL="False" \ +ATTRAP_BOT__EMAIL__EXAMPLE_SMTP__STARTTLS="True" \ ATTRAP_BOT__MASTODON__MASTODON_SOCIAL__INSTANCE="mastodon.social" \ ATTRAP_BOT__MASTODON__MASTODON_SOCIAL__ACCESS_TOKEN="a-secret-access-token-here" \ +ATTRAP_BOT__MASTODON__MASTODON_SOCIAL__TOOT="[{administration_name}]\n\n{name} ({date})\n\n{url}\n\n{hashtags} #{administration}" \ ./cli.py ```