Développement Slackbot en Python

introduction

Cet article est l'article du 24ème jour du SLP KBIT Advent Calendar 2019. Ça fait longtemps, je suis Gakki. Cette fois, j'ai créé Slackbot, je vais donc en écrire un enregistrement.

Historique du développement

Dans mon cercle, Slack est utilisé comme principal outil de contact avec le ** plan gratuit **. Dans de telles circonstances, de nombreux membres l'utilisent comme espace de travail. Parfois, des mentions sont faites sur des sujets sans rapport, alors je me suis demandé si je pouvais atténuer cela. J'ai décidé de créer un bot capable de gérer des groupes d'utilisateurs. (Cela n'a aucun sens d'activer les notifications pour la plupart des messages ...)

Environnement de développement et technologie utilisée

Présentation des bots dans votre espace de travail

J'ai fait référence au site de ici.

Flux de développement

De là, nous allons commencer le sujet principal. Je vais l'écrire séparément dans le contenu suivant.

Paramètre d'autorisation

Afin de gérer le groupe d'utilisateurs créé cette fois, Comme il n'était pas possible de le faire uniquement avec l'autorité en tant qu'utilisateur de bot, nous définissons l'autorité nécessaire comme suit. 84.PNG

Structure des dossiers

La structure des dossiers est la suivante. 85.PNG Le traitement au-delà de la fonction de l'utilisateur du bot est décrit dans my_mention.py et subMethod.py, et il est importé par run.py de main.

codage

Cette fois, nous avons implémenté un groupe de commandes pour gérer les groupes d'utilisateurs. De plus, j'ai également créé une commande liée au questionnaire avec elle, donc j'écrirai un peu à ce sujet. Les principales fonctions sont les suivantes.

--Créer un groupe d'utilisateurs --Supprimer le groupe d'utilisateurs --Modifier les membres du groupe d'utilisateurs

Tout d'abord, comme prémisse, je vais expliquer à partir du concept de groupe d'utilisateurs créé cette fois. Comme mentionné précédemment, l'espace de travail avec le bot est un plan gratuit, donc Les méthodes API liées aux groupes d'utilisateurs ne peuvent pas être utilisées. Par conséquent, créez un dictionnaire avec la clé comme nom du groupe d'utilisateurs et la valeur comme tableau de membres du groupe. J'ai décidé d'en faire un pseudo groupe d'utilisateurs en l'enregistrant dans un fichier statique. Par conséquent, veuillez comprendre que c'est le cas lorsque le contenu lié au dictionnaire apparaît dans les programmes suivants.

Créer un groupe d'utilisateurs

Le programme est le suivant.

@respond_to('create\s([a-zA-Z0-9]*)\s([\w\s,]+)')
def create_usergroup(message, usergroup_name, member):
    usergroup = subMethod.get_usergroup_list()
    member_list = subMethod.get_member()['members']
    for usergroup_dict in usergroup:
        if usergroup_dict['usergroup_name'] == usergroup_name:
            message.send("`" + usergroup_name+' is already exist.`\n> please choose another name.')
            return
    data = {}
    member_id = []
    data['usergroup_name'] = usergroup_name
    try:
        member_name = [x.strip() for x in member.split(',')]
    except AttributeError:
        member_name = []
        member_id = member
    ml_id = [ml['id'] for ml in member_list]
    ml_name = [ml['name'] for ml in member_list]
    ml_rname = [ml['real_name'] if 'real_name' in ml else 'no_name' for ml in member_list]
    ml_dname = [ml['profile']['display_name'] for ml in member_list]
    for mn in member_name:
        if mn in ml_name:
            member_id.append(ml_id[ml_name.index(mn)])
        elif mn in ml_rname:
            member_id.append(ml_id[ml_rname.index(mn)])
        elif mn in ml_dname:
            member_id.append(ml_id[ml_dname.index(mn)])
        else:
            message.send("`" + mn + " is not in this channel`")
    data['member'] = member_id
    usergroup.append(data)
    subMethod.set_usergroup_list(usergroup)
    message.send('Created a usergroup')

Il est exécuté lorsqu'un message au format create {nom du groupe d'utilisateurs} {nom du membre,…} est reçu. En tant que processus interne, vérifiez d'abord si le groupe d'utilisateurs spécifié a déjà été créé. Après vérification, s'il n'existe pas encore, créez-le et ajoutez des membres. À ce stade, il est confirmé en même temps si le membre spécifié existe dans l'espace de travail. Les utilisateurs existant dans l'espace de travail peuvent obtenir une liste à l'aide de la méthode user_list. Cependant, le problème ici est le format. Dans slack, les utilisateurs peuvent avoir des valeurs dans trois formats: userID, fullname et displayname. Comme il est difficile d'envoyer un identifiant lors de l'envoi d'un message, je pense que vous allez spécifier le nom complet ou le nom d'affichage, donc J'y ai rendu possible une conversion mutuelle. Après ce processus, si l'utilisateur existe, l'ID est ajouté et s'il n'existe pas, un message d'erreur est envoyé. La raison de l'ajout de l'ID sera expliquée dans la section sur la mention de groupe ci-dessous. Une fois exécuté, ce sera comme ci-dessous. 88.PNG

Supprimer le groupe d'utilisateurs

Le programme est le suivant.

@respond_to('delete_usergroup\s([a-zA-Z0-9]*)')
def delete_usergroup(message, usergroup_name):
    usergroup = subMethod.get_usergroup_list()
    usergroup_name_list = [x['usergroup_name'] for x in usergroup]
    if usergroup_name not in usergroup_name_list:
        message.send("`" + usergroup_name + ' is not exist.`\n> type `@secretary list` and check usergroup_name')
        return
    new_usergroup = []
    for usergroup_dict in usergroup:
        if usergroup_dict['usergroup_name'] == usergroup_name:
            continue
        new_usergroup.append(usergroup_dict)
    subMethod.set_usergroup_list(new_usergroup)
    message.send('Deleted a usergroup')

Reçoit et exécute un message au format delete_usergroup {usergroup name}. À cet égard, il s'agit presque simplement d'une opération de dictionnaire. S'il existe un groupe d'utilisateurs spécifié, supprimez-le simplement du dictionnaire. Sinon, un message d'erreur sera envoyé. Quand j'essaye de l'utiliser, ça ressemble à ça. 87.PNG

Modification des membres du groupe d'utilisateurs

Le programme est le suivant.

@respond_to('add\s([a-zA-Z0-9]*)\s([\w\s,]+)')
def add_member(message, usergroup_name, member):
    usergroup = subMethod.get_usergroup_list()
    usergroup_name_list = [usergroup_dict['usergroup_name'] for usergroup_dict in usergroup]
    if usergroup_name not in usergroup_name_list:
        message.send("`" + usergroup_name + " is not exist`\n> please type `@secretary list` and check usergroup_name.")
        return
    member_list = subMethod.get_member()['members']
    usergroup_member = subMethod.get_usergroup_member(usergroup_name)

    member_id = []
    try:
        member_name = [x.strip() for x in member.split(',')]
    except AttributeError:
        member_name = []
        member_id = member
    add_member_name = []
    for mn in member_name:
        if mn not in usergroup_member:
            add_member_name.append(mn)
        else:
            message.send("`" + mn + ' already belongs`')
    ml_id = [ml['id'] for ml in member_list]
    ml_name = [ml['name'] for ml in member_list]
    ml_rname = [ml['real_name'] if 'real_name' in ml else 'no_name' for ml in member_list]
    ml_dname = [ml['profile']['display_name'] for ml in member_list]
    for mn in add_member_name:
        if mn in ml_name:
            member_id.append(ml_id[ml_name.index(mn)])
        elif mn in ml_rname:
            member_id.append(ml_id[ml_rname.index(mn)])
        elif mn in ml_dname:
            member_id.append(ml_id[ml_dname.index(mn)])
        else:
            message.send("`" + mn + " is not in this channel`")
    if len(member_id) == 0:
        message.send("`No one will add`")
        return
    for usergroup_dict in usergroup:
        if usergroup_dict['usergroup_name'] == usergroup_name:
            usergroup_dict['member'].extend(member_id)
            usergroup_dict['member'] = list(set(usergroup_dict['member']))
            break
    subMethod.set_usergroup_list(usergroup)
    message.send('Added some member')
@respond_to('delete\s([a-zA-Z0-9]*)\s([\w\s,]+)')
def delete_member(message, usergroup_name, member):
    usergroup = subMethod.get_usergroup_list()
    usergroup_name_list = [usergroup_dict['usergroup_name'] for usergroup_dict in usergroup]
    if usergroup_name not in usergroup_name_list:
        message.send("`" + usergroup_name + " is not exist`\n> type `@secretary list` and check usergroup_name")
        return
    member_list = subMethod.get_member()['members']
    member_id = []
    try:
        member_name = [x.strip() for x in member.split(',')]
    except AttributeError:
        member_name = []
        member_id = member
    ml_id = [ml['id'] for ml in member_list]
    ml_name = [ml['name'] for ml in member_list]
    ml_rname = [ml['real_name'] if 'real_name' in ml else 'no_name' for ml in member_list]
    ml_dname = [ml['profile']['display_name'] for ml in member_list]
    for mn in member_name:
        if mn in ml_name:
            member_id.append(ml_id[ml_name.index(mn)])
        elif mn in ml_rname:
            member_id.append(ml_id[ml_rname.index(mn)])
        elif mn in ml_dname:
            member_id.append(ml_id[ml_dname.index(mn)])
        else:
            message.send("`" + mn + " is not in this channel`")
    if len(member_id) == 0:
        message.send("`No one will delete`")
        return
    for usergroup_dict in usergroup:
        if usergroup_dict['usergroup_name'] == usergroup_name:
            for mi in member_id:
                if mi not in usergroup_dict['member']:
                    message.send("`" + ml_name[ml_id.index(mi)] + " doesn't belong to this`")
                else:
                    usergroup_dict['member'].remove(mi)
            break
    subMethod.set_usergroup_list(usergroup)
    message.send('Deleted some member')

Ajouter un membre au groupe d'utilisateurs pour le message ʻadd {nom du groupe d'utilisateurs} {nom du membre,…} ` Supprime les membres du groupe d'utilisateurs pour le message «delete {nom du groupe d'utilisateurs} {nom du membre,…}». Le traitement supplémentaire est le même que le traitement de création si le groupe d'utilisateurs spécifié existe. Dans le processus de suppression, lorsque le groupe d'utilisateurs spécifié existe, les membres appartenant à ce groupe d'utilisateurs sont supprimés un par un. Il s'agit d'une erreur si le groupe d'utilisateurs et les membres spécifiés n'existent pas.

Mention du groupe d'utilisateurs

Le programme est le suivant.

@listen_to('@[a-zA-Z0-9]+\s([\s\S]*)')
def reply_to_thread(message, text):
    usergroup = subMethod.get_usergroup_list()
    message.body['text'].replace('\n', ' ')
    mention = message.body['text'].split()[0].strip('@')
    mention_dict = []
    for dictionary in usergroup:
        if dictionary['usergroup_name'] == mention:
            mention_dict = dictionary
            break
    if len(mention_dict) == 0:
        message.send('`' + mention + ' is not exist`')
        return
    sentence = ""
    for member in mention_dict['member']:
        sentence = sentence + "<@" + member + "> "
    sentence = sentence + "\n"
    message.send(sentence, 
            thread_ts=message.thread_ts)

Cette méthode est différente du contenu écrit précédemment. Les méthodes précédentes ne peuvent être exécutées qu'en conjonction avec la mention au bot, Cette méthode répond aux messages de la forme @ {nom du groupe d'utilisateurs} {message} sans elle. La méthode écrite en tant que respond nécessite une mention, et la méthode écrite en tant qu'écoute n'est pas requise. Bien qu'il s'agisse d'un processus interne, le @ {nom du groupe d'utilisateurs} inclus au début du message est extrait et Extrait la valeur de l'élément avec le nom de groupe d'utilisateurs spécifié comme clé. Comme expliqué précédemment, value est un format de tableau, donc l'instruction for retire les éléments un par un et les combine pour créer un message. C'est le message à mentionner. Ici, ID est utilisé dans slack lors de la mention. Par conséquent, il était nécessaire de se souvenir de la pièce d'identité. Si le message créé est envoyé à TL, il ne gênera que s'il y a un grand nombre de membres dans le groupe d'utilisateurs. Par conséquent, cette fois, le message est envoyé sous la forme d'un fil pour le message d'origine. La méthode d'envoi à un fil est message.send (phrase, thread_ts = message.thread_ts). L'écran réel ressemble à celui ci-dessous. 65.PNG

Les méthodes liées aux groupes d'utilisateurs sont les suivantes. En plus de ceux mentionnés ci-dessus, une liste de groupes et de membres, Il y a des choses comme changer le nom du groupe et combiner des groupes d'utilisateurs, mais je les omettrai car ils peuvent être combinés jusqu'à présent.

prime

Quand j'ai répondu à un questionnaire avec du mou, j'essayais d'obtenir une réaction au message. Avec ce format, c'était très gênant de voir les réactions une par une et de confirmer qui votait où. Lorsque vous créez un groupe d'utilisateurs, si vous regroupez les personnes qui répondront au questionnaire dans un groupe d'utilisateurs, Je pensais que le bot pouvait le gérer, alors j'ai fait environ deux fonctions. (Il semble qu'un simple sondage ait été publié récemment ...)

Total du questionnaire

Le programme est le suivant.

@respond_to('count')
def count_up_reaction(message):
    response = subMethod.get_message(message.body['channel'], 
                                    message.thread_ts)
    if not response:
        message.direct_reply("Can't use count method in DM")
        return
    sentence = ''
    if 'reactions' in response['messages'][0]:
        data = response['messages'][0]['reactions']
        sorted_data = sorted(data, reverse=True, key=lambda x:x['count'])
        sentence = response['messages'][0]['text'] + '\n\n*Result*\n'
        for datum in sorted_data:
            sentence = sentence + ":" + datum['name'] + ":" + " "
            for user in datum['users']:
                sentence = sentence + "<@" + user + "> "
            sentence = sentence + "\n"
    else:
        sentence = 'No reactions'
    message.direct_reply(sentence)

Il est exécuté en envoyant count au fil du message pour lequel vous souhaitez agréger les réactions. En tant que processus, lorsque vous recevez le message en haut du fil de discussion, il y a une partie qui résume les données de réaction, alors organisez-la. Les données de réaction sont incluses dans le formulaire ci-dessous. Après tout, puisqu'il s'agit d'un dictionnaire, il acquiert des données, les façonne et les envoie à l'utilisateur qui a envoyé «count» par DM. L'écran d'exécution est illustré ci-dessous.

89.PNG90.PNG

Agrégation des différences de questionnaire

Le programme est le suivant.

@respond_to('diff')
def check_reactor(message):
    response = subMethod.get_message(message.body['channel'],
                                    message.thread_ts)
    if not response:
        message.direct_reply("Can't use count method in DM")
        return
    target_usergroup = response['messages'][0]['text'].replace('\n', ' ').split()[0].strip('@')
    all_target_audience = subMethod.get_usergroup_member_id(target_usergroup)
    if len(all_target_audience) == 0:
        sentence = 'No specified user group'
    elif 'reactions' in response['messages'][0]:
        data = response['messages'][0]['reactions']
        reacted_users = []
        reacted_users.extend([user for datum in data for user in datum['users']])
        target_audience = []
        target_audience.extend([user for user in all_target_audience if user not in reacted_users])
        sentence = "*Hasn't yet reacted*\n"
        for user in target_audience:
            sentence = sentence + "<@" + user + ">\n"
    else:
        sentence = "*Hasn't yet reacted*\n"
        for user in all_target_audience:
            sentence = sentence + "<@" + user + ">\n"
    message.direct_reply(sentence)

Comme pour les mentions de groupe d'utilisateurs, spécifiez le groupe d'utilisateurs cible au début du message. Ensuite, envoyez diff au thread du message cible pour effectuer l'agrégation. Tout d'abord, extrayez le groupe d'utilisateurs cible du message et obtenez le tableau de membres. Ensuite, obtenez une liste d'utilisateurs avec des réactions d'une manière ou d'une autre. Si vous pouvez obtenir ces deux, extrayez les utilisateurs qui n'existent que dans l'un des deux tableaux. Organisez-les et envoyez-les par DM. L'écran d'exécution est illustré ci-dessous. 91.PNG92.PNG

en conclusion

L'introduction de slackbot peut se faire intuitivement à l'aide de l'interface graphique, et Quant à l'implémentation de la fonction, j'ai eu l'impression qu'il est relativement facile de travailler sur Python car il existe une bonne bibliothèque. Actuellement, il y a encore peu de fonctions, donc je continuerai à coder à l'avenir. Aussi, comme toujours, j'écris le même processus à divers endroits, Le nom du fichier est également mauvais et il est dans un état terrible. Je veux faire le refactoring fermement. Enfin, ce code a été téléchargé sur GitHub, alors jetez un œil si vous le souhaitez. J'ai également écrit sur Docker, donc si vous avez un environnement dans lequel vous pouvez utiliser Docker, je pense que vous pouvez l'utiliser dès que vous définissez le jeton.

Recommended Posts

Développement Slackbot en Python
Développement de framework avec Python
Environnement de développement en Python
Développement Python avec Visual Studio 2017
Développement Python avec Visual Studio
Quadtree en Python --2
Python en optimisation
CURL en Python
Métaprogrammation avec Python
Python 3.3 avec Anaconda
Géocodage en python
SendKeys en Python
Méta-analyse en Python
Unittest en Python
Époque en Python
Discord en Python
Allemand en Python
DCI en Python
tri rapide en python
nCr en python
Plink en Python
Constante en Python
FizzBuzz en Python
Sqlite en Python
Étape AIC en Python
LINE-Bot [0] en Python
Assemblage inversé avec Python
Réflexion en Python
Constante en Python
format en python
Scons en Python 3
Puyopuyo en python
python dans virtualenv
PPAP en Python
Quad-tree en Python
Réflexion en Python
Chimie avec Python
Hashable en Python
DirectLiNGAM en Python
Aplatir en Python
Aplatir en python
Liste triée en Python
Texte de cluster en Python
AtCoder # 2 tous les jours avec Python
Daily AtCoder # 6 en Python
Daily AtCoder # 18 en Python
Modifier les polices en Python
Motif singleton en Python
Opérations sur les fichiers en Python
Lire DXF avec python
Daily AtCoder # 53 en Python
Séquence de touches en Python
Utilisez config.ini avec Python
Daily AtCoder # 33 en Python
Résoudre ABC168D en Python
Distribution logistique en Python
Décomposition LU en Python
Une doublure en Python