7
6

More than 5 years have passed since last update.

Slackの秩序を守れ!! ~ slack_emoji_auditor ~

Posted at

はじめに

こんにちは、
皆さん仕事であったりプライベートであったり、普段からSlackを利用していますか?
筆者もSlackを仕事で利用することが多いです。

そんなSlackユーザになると利用したくなるのがカスタム絵文字ですね。
筆者の会社でも以下のように、さまざまなカスタム絵文字が日常的に利用されています。

スクリーンショット 2018-06-27 17.17.52.png

しかし、ときには心無いユーザによって不愉快なカスタム絵文字が作成されているかもしれません!
皆さんもそんな経験ありませんか?

そこで今回はそんな荒れたSlackカスタム絵文字社会の秩序をGoogle Cloud Vision APIを利用して正すことを考えました。

Google Cloud Vision API

Google Cloud Vision APIは、言わずと知れたGoogle CloudのAPIサービスになります。APIを利用することによって画像内のオブジェクトを検出したり、画像内のテキスト情報を抽出したりすることができます。

実際にQuickstartでサンプル画像に対してAPIを実行すると以下のように解析してくれます。画像内の犬を検出してくれているのがわかります。

スクリーンショット 2018-06-27 17.40.51.png

さて、上記のようにVision APIでは画像に対していろいろな解析をすることができますが、その中でもセーフサーチ検出という機能があります。セーフサーチ検出ではアダルトコンテンツや暴力的コンテンツなど、画像に含まれる不適切なコンテンツを検出してくれる機能となります。

実際にAPIで問い合わせを行うと以下のように、adult,violenceなどの観点に関して、対象の画像が不正コンテンツであるかの度合いを返します。

{
  "responses": [
    {
      "safeSearchAnnotation": {
        "adult": "VERY_UNLIKELY",
        "spoof": "VERY_LIKELY",
        "medical": "UNLIKELY",
        "violence": "UNLIKELY"
      }
    }
  ]
}

不正コンテンツの度合い(Likelihood)は以下の6段階に分類されます(以下Vision API Document参照)。

レベル 説明
UNKNOWN Unknown likelihood.
VERY_UNLIKELY It is very unlikely that the image belongs to the specified vertical.
UNLIKELY It is unlikely that the image belongs to the specified vertical.
POSSIBLE It is possible that the image belongs to the specified vertical.
LIKELY It is likely that the image belongs to the specified vertical.
VERY_LIKELY It is very likely that the image belongs to the specified vertical.

今回は、このセーフサーチ検出の機能をカスタム絵文字に適用することによって、不正な絵文字を検出することを考えていきます。

事前準備

今回はPythonを利用して実装を行なっていきます。事前にSlack APIのトークン情報と、Vision APIのクライアントライブラリの取得を行います。

Slack APIのトークン

Slack APIトークンは以下を参考に生成しておきます。
API トークンの生成と再生成

Vision APIクライアント

PythonのVision APIクライアントは以下のサイトの手順を参考にセットアップしておきます。
Vision API Client Libraries

実装

今回の実装は以下を参考に、Vision APIの問い合わせ機能を追加しながら実装しました。
https://github.com/jkloo/slack-emojis

以下が実装コード(https://github.com/fuku68/slack_emoji_auditor)。

# coding:utf-8
import os
import argparse
import requests
import logging

from google.cloud import vision
from google.cloud.vision import types

logging.basicConfig(level=logging.INFO, format='%(message)s')

SLACK_URL = 'https://{slack}.slack.com/api/emoji.list?token={token}'
RESPONSE_TYPE = ('adult', 'spoof', 'medical', 'violence', 'racy')
LIKEHOOD_NAME = ('UNKNOWN', 'VERY_UNLIKELY', 'UNLIKELY', 'POSSIBLE','LIKELY', 'VERY_LIKELY')

def main(args):
  # formatting result
  result = {}
  for type in RESPONSE_TYPE:
    result[type] = {}
    for name in LIKEHOOD_NAME:
      result[type][name] = []
      result[type][name + '_COUNT'] = 0 

  res = requests.get(SLACK_URL.format(slack=args.slack, token=args.token))

  # Google Cloud Vision API Client
  client = vision.ImageAnnotatorClient()
  image = types.Image()

  if res.status_code == 200:
    emoji = res.json().get('emoji', {})
    for k, v in emoji.items():
      image.source.image_uri = v
      logging.info('requesting... safe_search emoji: ' + k)
      gooleo_res = client.safe_search_detection(image=image)

      safe = gooleo_res.safe_search_annotation
      result['adult'][LIKEHOOD_NAME[safe.adult]].append(k)
      result['adult'][LIKEHOOD_NAME[safe.adult] + "_COUNT"] += 1
      result['spoof'][LIKEHOOD_NAME[safe.spoof]].append(k)
      result['spoof'][LIKEHOOD_NAME[safe.spoof] + "_COUNT"] += 1
      result['medical'][LIKEHOOD_NAME[safe.medical]].append(k)
      result['medical'][LIKEHOOD_NAME[safe.medical] + "_COUNT"] += 1
      result['violence'][LIKEHOOD_NAME[safe.violence]].append(k)
      result['violence'][LIKEHOOD_NAME[safe.violence] + "_COUNT"] += 1
      result['racy'][LIKEHOOD_NAME[safe.racy]].append(k)
      result['racy'][LIKEHOOD_NAME[safe.racy] + "_COUNT"] += 1

    result_print(result)

  else:
    logging.warning("can't get emoji list")


def result_print(result):
  print('----------- result ---------------')
  for type in RESPONSE_TYPE:
    print(type + ':')
    print('  VERY_LIKELY: ' + unicode_array_to_str(result[type]['VERY_LIKELY']))
    print('       LIKELY: ' + unicode_array_to_str(result[type]['LIKELY']))
    print('     POSSIBLE: ' + unicode_array_to_str(result[type]['POSSIBLE']))
    print('')
    print('  statistics:')
    for name in LIKEHOOD_NAME: 
      print('    ' + '{0:>13}'.format(name) + ': ' + str( result[type][name + '_COUNT']))
    print('')

def unicode_array_to_str(array):
  str = ', '.join(array)
  return '[' + str + ']'

if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument('slack', help='name of your slack group.')
  parser.add_argument('token', help='auth token for slack.')
  args = parser.parse_args()
  main(args)

それぞれの観点に関してPOSSIBLE、LIKELY、VERY_LIKELYに対応する絵文字と、それぞれのLikelihoodの統計情報を出力するように実装しました。

コマンドラインから以下の記述で実行します。

python auditor.py <Slackワークスペース名> <Slack APIトークン>

出力結果は以下。

---------- result ---------------
adult:
  VERY_LIKELY: []
       LIKELY: []
     POSSIBLE: []

  statistics:
          UNKNOWN: 4
    VERY_UNLIKELY: 364
         UNLIKELY: 20
         POSSIBLE: 0
           LIKELY: 0
      VERY_LIKELY: 0

spoof:
  VERY_LIKELY: [trollface, idobata_02, dodo_02, miyata_02]
       LIKELY: [utsunomiya_01, yakyu_01, eyes2]
     POSSIBLE: [utsunomiya_02, panda, tagawa, gudetama_03, ryoukai, tennen, daijobu, misawa_04, misawa_05, siba01, taiko_01, kurara]

  statistics:
          UNKNOWN: 4
    VERY_UNLIKELY: 319
         UNLIKELY: 46
         POSSIBLE: 12
           LIKELY: 3
      VERY_LIKELY: 4

medical:
  VERY_LIKELY: []
       LIKELY: []
     POSSIBLE: [thumbsup_all, fu, ee, kesseki, maguro_04, metal, funa, wakaru, kupo, elixir]

  statistics:
          UNKNOWN: 4
    VERY_UNLIKELY: 331
         UNLIKELY: 43
         POSSIBLE: 10
           LIKELY: 0
      VERY_LIKELY: 0

violence:
  VERY_LIKELY: []
       LIKELY: []
     POSSIBLE: []

  statistics:
          UNKNOWN: 4
    VERY_UNLIKELY: 360
         UNLIKELY: 24
         POSSIBLE: 0
           LIKELY: 0
      VERY_LIKELY: 0

racy:
  VERY_LIKELY: [yamamoto_maika, yoga_2, ee, kusu, maguro_04, haggar2, kotowaru]
       LIKELY: [vega, fu, haggar, yuriko, dodo_08, piggy, ogawa, misawa_03, misawa_05, elixir, kurara]
     POSSIBLE: [negi, cody, 天馬くん, bowtie, miyata_01, bdash2, dodo_05, metal, nandato, arai_01, fs-e-hiroaki-idobata, kesu, penguins, funa, kupo, yareyare, glitch_crab, dead, misawa_04, masaru-san, char, conan, matsuda_01, dusty_stick, はると]

  statistics:
          UNKNOWN: 4
    VERY_UNLIKELY: 243
         UNLIKELY: 98
         POSSIBLE: 25
           LIKELY: 11
      VERY_LIKELY: 7

ヒットしたら面白そうなadultviolenceでは対応する絵文字がありませんでした...(期待はずれ)。

ちなみにspoofのVERY_LIKELYと認識されたのは以下の絵文字たち

スクリーンショット 2018-06-27 18.39.15.png

なんか顔が多い...

またracyのVERY_LIKELYと認識されたのは以下の絵文字たち
スクリーンショット 2018-06-27 18.40.16.png

こちらの基準はさっぱり...

最後に

今回はSlackのカスタム絵文字に対してVision APIを利用して、コンテンツとして不正なものが無いかのチェックを行いました。
皆さんも是非、自分のSlackのワークスペースで試してみてください。そして不正なカスタム絵文字の魔の手から、Slackの秩序を守りましょう!

...こうして業務時間を浪費することによって会社の秩序は乱れていくのであった。

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6