Slackbot-Entwicklung in Python

Einführung

Dieser Artikel ist der Artikel zum 24. Tag von SLP KBIT Adventskalender 2019. Es ist lange her, ich bin Gakki. Dieses Mal habe ich Slackbot erstellt, also werde ich eine Aufzeichnung davon schreiben.

Entwicklungsgeschichte

In meinem Kreis wird Slack als Hauptkontaktwerkzeug mit ** freiem Plan ** verwendet. Unter solchen Umständen verwenden viele Mitglieder es als Arbeitsbereich Manchmal werden Erwähnungen zu nicht verwandten Themen gemacht, deshalb habe ich mich gefragt, ob ich das lindern könnte. Ich habe beschlossen, einen Bot zu erstellen, der Benutzergruppen verwalten kann. (Es ist nicht sinnvoll, Benachrichtigungen für die meisten Nachrichten zu aktivieren ...)

Entwicklungsumgebung und verwendete Technologie

Einführung von Bots in Ihren Arbeitsbereich

Ich habe auf die Website von [hier] verwiesen (https://qiita.com/croissant1028/items/8d6334b76576762df349).

Entwicklungsfluss

Von hier aus beginnen wir mit dem Hauptthema. Ich werde es in den folgenden Inhalten separat schreiben.

Berechtigungseinstellung

Um die diesmal erstellte Benutzergruppe zu verwalten, Da dies nicht nur mit der Berechtigung als Bot-Benutzer möglich war, haben wir die erforderliche Berechtigung wie folgt festgelegt. 84.PNG

Ordnerstruktur

Die Ordnerstruktur ist wie folgt. 85.PNG Die Verarbeitung über die Funktion des Bot-Benutzers hinaus wird in my_mention.py und subMethod.py beschrieben und von run.py of main importiert.

Codierung

Dieses Mal haben wir eine Gruppe von Befehlen zum Verwalten von Benutzergruppen implementiert. Außerdem habe ich einen Fragebogen-bezogenen Befehl erstellt, sodass ich ein wenig darüber schreiben werde. Die Hauptfunktionen sind wie folgt.

Als Prämisse werde ich zunächst das Konzept der diesmal erstellten Benutzergruppe erläutern. Wie bereits erwähnt, ist der Arbeitsbereich mit dem Bot ein kostenloser Plan API-Methoden für Benutzergruppen können nicht verwendet werden. Erstellen Sie daher ein Wörterbuch mit dem Schlüssel als Benutzergruppenname und dem Wert als Array von Gruppenmitgliedern. Ich habe beschlossen, es zu einer Pseudo-Benutzergruppe zu machen, indem ich es in einer statischen Datei speichere. Bitte haben Sie Verständnis dafür, dass der Inhalt des Wörterbuchs in den nachfolgenden Programmen angezeigt wird.

Benutzergruppe erstellen

Das Programm ist wie folgt.

@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')

Es wird ausgeführt, wenn eine Nachricht im Format "create {Benutzergruppenname} {Mitgliedsname, ...}" empfangen wird. Überprüfen Sie als interner Prozess zunächst, ob die angegebene Benutzergruppe bereits erstellt wurde. Wenn es nach der Überprüfung noch nicht vorhanden ist, erstellen Sie es und fügen Sie Mitglieder hinzu. Zu diesem Zeitpunkt wird gleichzeitig bestätigt, ob das angegebene Mitglied im Arbeitsbereich vorhanden ist. Im Arbeitsbereich vorhandene Benutzer können mithilfe der user_list-Methode eine Liste abrufen. Das Problem hierbei ist jedoch das Format. In Slack können Benutzer Werte in drei Formaten haben: Benutzer-ID, vollständiger Name und Anzeigename. Da es schwierig ist, beim Senden einer Nachricht eine ID zu senden, werden Sie meines Erachtens den vollständigen Namen oder den Anzeigenamen angeben Ich habe es möglich gemacht, dort eine gegenseitige Bekehrung durchzuführen. Wenn der Benutzer nach diesem Vorgang vorhanden ist, wird die ID hinzugefügt, und wenn sie nicht vorhanden ist, wird eine Fehlermeldung gesendet. Der Grund für das Hinzufügen der ID wird im folgenden Abschnitt über die Erwähnung von Gruppen erläutert. Wenn es ausgeführt wird, ist es wie folgt. 88.PNG

Benutzergruppe löschen

Das Programm ist wie folgt.

@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')

Empfängt und führt eine Nachricht im Format "delete_usergroup {usergroup name}" aus. In dieser Hinsicht ist es fast nur eine Wörterbuchoperation. Wenn eine bestimmte Benutzergruppe vorhanden ist, löschen Sie diese einfach aus dem Wörterbuch. Wenn nicht, wird eine Fehlermeldung gesendet. Wenn ich versuche, es zu benutzen, sieht es so aus. 87.PNG

Benutzergruppenmitglieder bearbeiten

Das Programm ist wie folgt.

@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')

Hinzufügen eines Mitglieds zu einer Benutzergruppe für die Nachricht add {Benutzergruppenname} {Mitgliedsname,…} Löscht Mitglieder aus der Benutzergruppe für die Nachricht "Löschen {Benutzergruppenname} {Mitgliedsname, ...}". Die zusätzliche Verarbeitung entspricht der Erstellungsverarbeitung, wenn die angegebene Benutzergruppe vorhanden ist. Wenn beim Löschen die angegebene Benutzergruppe vorhanden ist, werden die zu dieser Benutzergruppe gehörenden Mitglieder nacheinander gelöscht. Es ist ein Fehler, wenn die angegebene Benutzergruppe und die angegebenen Mitglieder nicht vorhanden sind.

Erwähnung der Benutzergruppe

Das Programm ist wie folgt.

@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)

Diese Methode unterscheidet sich von den zuvor geschriebenen Inhalten. Die vorherigen Methoden können nur in Verbindung mit der Erwähnung an den Bot ausgeführt werden. Diese Methode antwortet auf Nachrichten der Form @ {Benutzergruppenname} {Nachricht} ohne sie. Die als Antwort geschriebene Methode muss erwähnt werden, und die als Listen geschriebene Methode ist nicht erforderlich. Obwohl es sich um einen internen Prozess handelt, wird das am Anfang der Nachricht enthaltene @ {Benutzergruppenname} extrahiert und Extrahiert den Wert des Elements mit dem angegebenen Benutzergruppennamen als Schlüssel. Wie bereits erläutert, ist value ein Array-Format. Die for-Anweisung nimmt die Elemente einzeln heraus und kombiniert sie, um eine Nachricht zu erstellen. Dies ist die zu erwähnende Nachricht. Hier wird ID beim Erwähnen im Leerlauf verwendet. Daher war es notwendig, sich die ID zu merken. Wenn die erstellte Nachricht an TL gesendet wird, wird sie nur dann gestört, wenn sich eine große Anzahl von Mitgliedern in der Benutzergruppe befindet. Daher wird die Nachricht dieses Mal in Form eines Threads für die ursprüngliche Nachricht gesendet. Die Methode zum Senden an einen Thread lautet "message.send (Satz, thread_ts = message.thread_ts)". Der eigentliche Bildschirm sieht wie folgt aus. 65.PNG

Die Methoden für Benutzergruppen lauten wie folgt. Zusätzlich zu den oben genannten eine Liste von Gruppen und Mitgliedern, Es gibt Dinge wie das Ändern des Gruppennamens und das Kombinieren von Benutzergruppen, aber ich werde sie weglassen, da sie bisher kombiniert werden können.

Bonus

Als ich einen Fragebogen mit Slack nahm, versuchte ich, eine Reaktion auf die Nachricht zu bekommen. Bei diesem Format war es sehr mühsam, die Reaktionen einzeln zu sehen und zu bestätigen, wer wo abgestimmt hat. Wenn Sie eine Benutzergruppe erstellen und die Personen, die den Fragebogen beantworten, in einer Benutzergruppe zusammenfassen, Ich dachte, dass der Bot damit umgehen könnte, also machte ich ungefähr zwei Funktionen. (Es scheint, dass kürzlich eine einfache Umfrage veröffentlicht wurde ...)

Fragebogen insgesamt

Das Programm ist wie folgt.

@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)

Es wird ausgeführt, indem "count" an den Thread der Nachricht gesendet wird, für die Sie die Reaktionen aggregieren möchten. Wenn Sie die Nachricht oben im Thread erhalten, gibt es einen Teil, der die Reaktionsdaten zusammenfasst. Organisieren Sie sie also. Die Reaktionsdaten sind in der folgenden Form enthalten. Da es sich um ein Wörterbuch handelt, erfasst es Daten, formt sie und sendet sie an den Benutzer, der per DM "count" gesendet hat. Der Ausführungsbildschirm ist wie unten gezeigt.

89.PNG90.PNG

Fragebogen-Differenzaggregation

Das Programm ist wie folgt.

@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)

Geben Sie wie bei Erwähnungen von Benutzergruppen die Zielbenutzergruppe am Anfang der Nachricht an. Senden Sie dann "diff" an den Thread der Zielnachricht, um die Aggregation durchzuführen. Extrahieren Sie zunächst die Zielbenutzergruppe aus der Nachricht und rufen Sie das Mitgliederarray ab. Als nächstes erhalten Sie eine Liste der Benutzer mit Reaktionen auf irgendeine Weise. Wenn Sie diese beiden erhalten können, extrahieren Sie die Benutzer, die nur in einem der beiden Arrays vorhanden sind. Ordnen Sie sie an und senden Sie sie per DM. Der Ausführungsbildschirm ist wie unten gezeigt. 91.PNG92.PNG

abschließend

Die Einführung von Slackbot kann intuitiv über die GUI erfolgen, und Bei der Funktionsimplementierung hatte ich den Eindruck, dass es relativ einfach ist, an Python zu arbeiten, da es eine gute Bibliothek gibt. Derzeit gibt es noch wenige Funktionen, daher werde ich auch in Zukunft weiter programmieren. Außerdem schreibe ich wie immer den gleichen Prozess an verschiedenen Stellen, Der Dateiname ist ebenfalls schlecht, daher befindet er sich in einem schrecklichen Zustand. Ich möchte das Refactoring fest machen. Schließlich wurde dieser Code auf GitHub hochgeladen. Schauen Sie also bitte nach, wenn Sie möchten. Ich habe auch über Docker geschrieben. Wenn Sie also eine Umgebung haben, in der Sie Docker verwenden können, können Sie es meiner Meinung nach verwenden, sobald Sie das Token festgelegt haben.

Recommended Posts

Slackbot-Entwicklung in Python
Framework-Entwicklung mit Python
Entwicklungsumgebung in Python
Python-Entwicklung mit Visual Studio 2017
Python-Entwicklung mit Visual Studio
Quadtree in Python --2
Python in der Optimierung
CURL in Python
Metaprogrammierung mit Python
Python 3.3 mit Anaconda
Geokodierung in Python
SendKeys in Python
Metaanalyse in Python
Unittest in Python
Epoche in Python
Zwietracht in Python
Deutsch in Python
DCI in Python
Quicksort in Python
nCr in Python
Plink in Python
Konstante in Python
FizzBuzz in Python
SQLite in Python
Schritt AIC in Python
LINE-Bot [0] in Python
Reverse Assembler mit Python
Reflexion in Python
Konstante in Python
Format in Python
Scons in Python 3
Puyopuyo in Python
Python in Virtualenv
PPAP in Python
Quad-Tree in Python
Reflexion in Python
Chemie mit Python
Hashbar in Python
DirectLiNGAM in Python
In Python reduzieren
In Python flach drücken
Sortierte Liste in Python
Clustertext in Python
AtCoder # 2 jeden Tag mit Python
Täglicher AtCoder # 6 in Python
Täglicher AtCoder # 18 in Python
Bearbeiten Sie Schriftarten in Python
Singleton-Muster in Python
Dateioperationen in Python
Lesen Sie DXF mit Python
Täglicher AtCoder # 53 in Python
Tastenanschlag in Python
Verwenden Sie config.ini mit Python
Täglicher AtCoder # 33 in Python
Löse ABC168D in Python
Logistische Verteilung in Python
LU-Zerlegung in Python
Ein Liner in Python