[PYTHON] L'histoire de la création de Botonyan qui renvoie le contenu de Google Docs en réponse à un mot-clé spécifique sur Slack

Aperçu

Contexte de l'affaire

Bonjour spin I NYA. Les gens du côté et ceux du côté écrivent souvent Python, donc quand j'ai commencé à l'écrire, je suis devenu accro et j'ai créé Slackbot avec Python + Flask.

https://github.com/supistar/Botnyan

Ingress et Slack

L'origine du problème est donc Ingress ^ 1 Je suis sûr que beaucoup d'entre vous connaissent déjà Ingress, donc je n'entrerai pas dans les détails, mais en raison de la nature du jeu, il est important pour les joueurs dans une zone spécifique de former une communauté et d'interagir et de développer des opérations à travers cette communauté. .. Dans cette communauté, certaines communautés migrent désormais vers Slack.

Consultez le blog [^ 2] ici pour la transition vers Slack.

Slack est la première étape pour rejoindre la communauté

Au fur et à mesure que la transition vers Slack progressait, il y a eu de nombreux essais et erreurs pour rendre la communauté plus pratique. L'un d'eux est un message d'orientation pour rejoindre la communauté.

Slack a un aspect spécial, et un gros obstacle pour les utilisateurs est que l'application est basée en anglais. Pour les ingénieurs, l'anglais est un monde avant le petit déjeuner, mais tout comme les agents ne sont pas des ingénieurs, il n'est pas toujours vrai que tout le monde soit habitué à l'anglais.

Des barrières plus élevées pour rejoindre la communauté réduisent la participation globale, entravant la formation de la communauté et ruinant l'objectif du déménagement vers Slack en premier lieu.

Par conséquent, afin de réduire cette barrière, nous avons préparé un manuel avec des captures d'écran et un document avec des conseils, et des améliorations ont été apportées pour guider les nouveaux membres. Cela a créé des didacticiels et des pistes pour les nouveaux utilisateurs, ce qui leur permet de participer plus facilement.

Slackbot étonnamment difficile

Cependant, ce message de guidage a été effectué manuellement au début

--Oubliez le contenu du texte ――En premier lieu, où était le modèle du texte? (Il n'apparaît pas même si vous recherchez)

En raison de problèmes fréquents tels que, le rôle a été déplacé vers Slackbot, qui est fourni depuis le début avec Slack. Cela a rendu le message de guidage automatisé, mais ce Bot est tout à fait un auteur-compositeur ...

J'ai eu le problème, "Je veux résoudre ça d'une manière ou d'une autre ...", et le résultat est "Botnyan".

Présentation de Botnyan

Botnyan est écrit basé sur Python + Flask et agit comme un bot sur Slack déclenché par Outgoing-Webhook.

slack.png

L'opération est très simple:

--Outgoing-Si la publication contient le mot-clé spécifié dans les Webhooks, accédez au point de terminaison REST spécifié (Botnyan). --Botnyan accède à Google Docs associé au mot-clé spécifié --Botnyan <-> Google Docs se connecte via un compte de service. Obtenir le contenu du document au format de fichier texte

Il est devenu.

Depuis son introduction, il a été très bien accueilli par ses collègues agents.

Après l'avoir créé, j'ai entendu dire que la communauté voisine avait un problème similaire, j'ai donc décidé de publier ce qui était géré dans le référentiel privé en tant que source publique. Au début, je l'ai fait fonctionner dans l'environnement Apache + WSGI, mais j'ai apporté quelques modifications à l'original pour qu'il puisse également fonctionner dans Heroku.

Nous avons préparé README-jp pour la méthode d'introduction, alors jetez-y également un œil! https://github.com/supistar/Botnyan/blob/master/README-jp.md

Histoire technique

Ici, je vais décrire brièvement le contenu technique utilisé dans Botonyan.

1. Limiter le point final publié par Flask

Flask vous permet de restreindre l'accès à Endpoints en utilisant plusieurs décorateurs.

Les Webhooks sortants de Slack accèderont au point de terminaison spécifié par POST + ʻapplication / x-www-form-urlencoded. Par conséquent, si vous souhaitez le limiter uniquement à ceux-ci, procédez comme suit. Puisque Cross-Origin est également autorisé ici, ajoutez également @cross_origin ()`.

python


@slack.route("/webhook", methods=['POST'])
@cross_origin()
@consumes('application/x-www-form-urlencoded')
def webhook():
    ~~~

2. Sécurité des terminaux sortants - légèrement renforcée utilisée par les Webhooks

Les Webhooks sortants doivent avoir un point de terminaison qui peut accéder au public. L'authentification de base ne peut pas être appliquée, donc si vous créez un point de terminaison sans en être conscient,

  1. Nom de l'application Heroku révélé
  2. Entrez un mot-clé approprié
  3. Si les mots-clés correspondent, le contenu du document sera divulgué! Informations importantes pour l'autre partie! !! !!

Cela pourrait être le cas (´ ・ ω ・ `)

Cependant, Outgoing-Webhooks de Slack accorde des jetons aux requêtes. Tout d'abord, vérifions les jetons que Outgoing-Webhooks vous donnera. Jetez un œil aux intégrations de Slack en bas.

01.png

C'est parfait avec ce jeton. Ensuite, stockez ce jeton côté application et comparez-le avec le jeton inclus dans la demande réelle. Si le jeton n'est pas défini du côté de l'application, ou si le jeton est différent de celui de la requête, renvoyez une erreur avec ʻabort (401) `.

form = request.form
request_token = Utils().parse_dic(form, 'token', 400)
token = os.environ.get('SLACK_WEBHOOK_TOKEN')
if not token or token != request_token:
    abort(401)

3. Comment gérer la clé privée du compte de service

Pour être honnête, j'étais le plus inquiet à ce sujet: peur: Ce serait plus facile si je mettais le fichier p12 directement dans le référentiel, mais je l'ai rejeté immédiatement parce que je l'ai rendu public. La méthode alternative est de mettre la clé privée dans Config Variables.

Tout d'abord, retirez la clé privée du fichier p12

cd path/to/p12directory
openssl pkcs12 -passin pass:notasecret -in privatekey.p12 -nocerts -passout pass:notasecret -out key.pem
openssl pkcs8 -nocrypt -in key.pem -passin pass:notasecret -topk8 -out google-services-private-key.pem
rm key.pem
# google-services-private-key.pem est la clé privée! Tu l'as fait!

Réglez ceci sur «Variables de configuration».

# heroku-Utilisation de la ceinture porte-outils...Par ici
heroku config:add GOOGLE_PRIVATE_KEY=`cat path/to/p12directory/google-services-private-key.pem`

Du côté du code Python, accédez-y avec ʻos.environ. Le reste est identique à la lecture d'un fichier p12. Si la clé privée n'est pas définie, il est plus facile à comprendre en appelant ʻabort () pour renvoyer un code d'état spécifique.

private_key = os.environ['GOOGLE_PRIVATE_KEY']
if not private_key:
    abort(401)
credentials = SignedJwtAssertionCredentials(os.environ['GOOGLE_CLIENT_EMAIL'],
                                            private_key,
                                            'https://www.googleapis.com/auth/drive',
                                            sub=os.environ['GOOGLE_OWNER_EMAIL'])
http = httplib2.Http()
credentials.authorize(http)
service = build('drive', 'v2', http=http)

4. Récupérez le contenu du document à partir de l'ID de document GoogleDocs

C'est le cœur de Botnyan. Obtenez le fichier à l'aide de l'instance de service et de l'ID de document créés dans (3).

Cependant, si vous l'obtenez simplement, les fichiers au format Office tomberont, il est donc difficile à utiliser. Alors prenons-le dans un format de fichier text / plain. Si vous procédez comme suit, le contenu du document sera stocké sous forme de chaîne dans content.

f = service.files().get(fileId=doc_id).execute()
if 'exportLinks' in f and 'text/plain' in f['exportLinks']:
    download = f['exportLinks']['text/plain']
    resp, content = service._http.request(download)
else:
    content = 'Échec de lecture'

5.Faites parler le bot avec le contenu acquis

C'est très simple. Il renvoie simplement une réponse JSON similaire à la suivante en réponse à une requête Outgoing-Webhooks.

{"text": "C'est le contenu du document! ∧_∧"}

Il renvoie simplement JSON en utilisant le contenu obtenu dans (4). Spécifiez ʻapplication / json` pour le Content-Type de la réponse.

dic = {"text": content}
return Response(Utils().dump_json(dic), mimetype='application/json')

Défis restants

Cela ne veut pas dire que tout est résolu.

--Outgoing-Webhook ne peut actuellement pas recevoir les remarques de PrivateRoom --Il semble que vous puissiez le récupérer en utilisant l'adaptateur de mou de hubot (RTM), mais si vous l'utilisez, les sauts de ligne dans le document ne fonctionneront pas ...

Alors

De quel côté de l'agent suis-je ... sans ici: fantôme: Je pense que les problèmes de communication ne sont pas seulement dans l'équipe Ingress mais aussi au travail, donc si vous l'aimez, veuillez l'utiliser.

Je serais très heureux si cela aide! Alors! : cat2:

Site de référence

Recommended Posts

L'histoire de la création de Botonyan qui renvoie le contenu de Google Docs en réponse à un mot-clé spécifique sur Slack
L'histoire de la création d'un Bot qui affiche les membres actifs dans un canal spécifique de Slack avec Python
Une histoire sur la création d'un programme qui augmentera le nombre d'abonnés Instagram de 0 à 700 en une semaine
L'histoire de la création d'une base de données à l'aide de l'API Google Analytics
Je suis à Singapour en ce moment Une histoire sur la création d'un LineBot et la volonté de faire un travail mémorable
L'histoire de la création d'un site qui répertorie les dates de sortie des livres
Créez une fonction pour obtenir le contenu de la base de données dans Go
[Python] Un programme qui fait pivoter le contenu de la liste vers la gauche
[Google Photo & Slack Photo Bot] Une histoire sur la création d'un bot qui acquiert une photo dans Google Photo et l'envoie à Slack.
Comment exécuter le code pratique du livre "Making Profitable AI with Python" sur Google Colaboratory
L'histoire de la recherche d'un magasin BOT (AI LINE BOT) pour Go To EAT dans la préfecture de Chiba (1)
Comment copier et coller le contenu d'une feuille au format JSON avec une feuille de calcul Google (en utilisant Google Colab)
Créez un bot qui publie sur Slack le nombre de personnes positives pour le nouveau virus corona à Tokyo
Un simple serveur simulé qui incorpore simplement l'en-tête de la requête HTTP dans le corps de la réponse et le renvoie.
Histoire de faire une recherche de magasin BOT (AI LINE BOT) pour Go To EAT dans la préfecture de Chiba (2) [Présentation]
Comment mentionner un groupe d'utilisateurs avec une notification de mou, comment vérifier l'ID d'un groupe d'utilisateurs
L'histoire de l'adresse IPv6 que je souhaite conserver au minimum
L'histoire de Django créant une bibliothèque qui pourrait être un peu plus utile
[Python] Changer la couleur du texte et la couleur d'arrière-plan d'un mot clé spécifique dans la sortie d'impression
Une histoire d'essais et d'erreurs essayant de créer un groupe d'utilisateurs dynamique dans Slack
Un script qui transfère les tweets contenant des mots-clés spécifiques sur Twitter vers Slack en temps réel.
Une histoire sur la tentative d'introduire Linter au milieu d'un projet Python (Flask)
Publié le nombre de nouveaux positifs corona à Tokyo sur Slack (déployés sur Heroku)
Une histoire qui réduit l'effort de fonctionnement / maintenance
#Une fonction qui renvoie le code de caractère d'une chaîne de caractères
Une histoire qui a analysé la livraison de Nico Nama.
Un serveur qui renvoie le nombre de personnes devant la caméra avec bottle.py et OpenCV
Créez un bot qui ne renvoie que le résultat de l'analyse morphologique avec MeCab avec Discord
L'histoire de la création d'un «espace de discussion sur l'esprit et le temps» exclusivement pour les ingénieurs de l'entreprise
Obtenir la valeur d'une clé spécifique jusqu'à l'index spécifié de la liste de dictionnaires en Python
Résumé des points à garder à l'esprit lors de l'écriture d'un programme qui s'exécute sur Python 2.5
[Python] Programmation pour trouver le nombre de a dans une chaîne de caractères qui se répète un nombre spécifié de fois.
[Note] Un script shell qui vérifie l'utilisation du processeur d'un processus spécifique dans une boucle while.
Une histoire qui rend le débogage de modèle plus facile à voir dans l'environnement Django + SQLAlchemy
Une histoire qui contribue à une nouvelle analyse corona à l'aide d'un essai gratuit de Google Cloud Platform
Notification Slack lorsqu'un mot spécifique est murmuré sur Twitter en utilisant Heroku avec python
Comment dessiner facilement la structure d'un réseau de neurones sur Google Colaboratory à l'aide de "convnet-tiroir"
J'ai essayé de créer un script qui retrace les tweets d'un utilisateur spécifique sur Twitter et enregistre l'image publiée à la fois
L'histoire de la création d'un canal VIP dans le chatwork en interne
[Ubuntu] Comment supprimer tout le contenu du répertoire
Remarque sur le comportement par défaut de collate_fn dans PyTorch
Django renvoie le contenu du fichier sous forme de réponse HTTP
Obtenez le nombre d'éléments spécifiques dans la liste python
Script Python qui compare le contenu de deux répertoires
Comment connecter le contenu de la liste dans une chaîne de caractères
Publiez le script shell créé pour réduire les problèmes de création de LiveUSB sous Linux
Une petite histoire addictive avec les permissions du répertoire spécifié par expdp (pour les débutants)
Rendement dans la classe qui a hérité de l'unittest.TestCase ne fonctionnait pas avec le nez (selon la version du nez?)
Une histoire qui n'a pas fonctionné lorsque j'ai essayé de me connecter avec le module de requêtes Python
L'histoire de la création d'un outil qui fonctionne sur Mac et Windows sur le site de développement de jeux
[Python scraping] Affiche l'URL et le titre du site contenant un mot-clé spécifique dans un fichier texte
[Python] À propos de la création d'un outil pour créer un nouveau courrier Outlook basé sur les données du fichier JSON et de la partie qui a été interceptée
Traitez le contenu du fichier dans l'ordre avec un script shell
L'histoire selon laquelle la version de python 3.7.7 n'était pas adaptée à Heroku
pandas Récupère le nom d'une colonne contenant un caractère spécifique
Comment vérifier la taille de la mémoire d'une variable en Python