OpenChatのSPAMが多いので形態素解析で頻度をとったメモ

ネタです。

## これはなに？

- LINEのSPAMが多い。

例:
> 小遣い稼ぎ情報💴💴
>
> 福田さんは10年間の仮想通貨分析の経験があります。
> 
> 1日300円〜30000円の収益も可能💰
> {URL}
>
> 答え: (one)(one)(one)

- LINEにはNGワード機能があるが、正規表現やAND条件などはサポートされておらず、単に出現した単語とメッセージが一致した場合にメッセージが削除されるという簡易なものである。
    - 効率的にSPAMのメッセージを自動削除したい。
    - 頻出単語を出したい。
    - Nグラムでもいいけどせっかくだし、Mecabで出した単語でやってみよう。

- SPAMは削除されると、LINE側の見える範囲で履歴が残らないので、Androidの通知履歴管理アプリの情報を使った
- 特定のオープンチャットのLINEのメッセージの通知履歴に載ったもののうち、１回しか投稿していないユーザのメッセージを抽出した。

## 実施記録

https://play.google.com/store/apps/details?id=com.lufesu.app.notification_organizer&hl=ja

このアプリを既に入れていたので、コレから、データを出力すると、`Norg_Data.norg` というファイルになっていた。なんの形式だろう。

```shell-session
$ file Norg_Data.norg 
Norg_Data.norg: Zip archive data, at least v2.0 to extract, compression method=deflate
```

ふむ、zip。

```
$ sudo apt install -y unzip
$ unzip Norg_Data.norg 
Archive:  Norg_Data.norg
  inflating: notification_organizer  
  inflating: notification_organizer-shm  
  inflating: notification_organizer-wal  
  inflating: icon-cache.zip          
  inflating: datastore.zip           
  inflating: com.lufesu.app.notification_organizer_preferences.xml  
$ 
$ file *
Norg_Data.norg:                                        Zip archive data, at least v2.0 to extract, compression method=deflate
com.lufesu.app.notification_organizer_preferences.xml: XML 1.0 document, ASCII text
datastore.zip:                                         Zip archive data, at least v2.0 to extract, compression method=deflate
icon-cache.zip:                                        Zip archive data, at least v2.0 to extract, compression method=deflate
notification_organizer:                                SQLite 3.x database, user version 3, last written using SQLite version 3032002, writer version 2, read version 2, file counter 1899, database pages 2346, cookie 0x6, schema 4, largest root page 6, UTF-8, version-valid-for 1899
notification_organizer-shm:                            data
notification_organizer-wal:                            SQLite Write-Ahead Log, version 3007000
$ 
```

ふむ。SQLiteか

```shell-session
$ sudo apt install -y sqlite3
$ sqlite3 notification_organizer
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> 
sqlite> .table
android_metadata   notification       room_master_table
```

notification、あやしい。

```sql
sqlite> sqlite> PRAGMA table_info('notification');
0|uid|INTEGER|1||1
1|id|INTEGER|1||0
2|package_name|TEXT|1||0
3|key|TEXT|1||0
4|post_time|INTEGER|1||0
5|post_date|TEXT|1||0
6|channel_id|TEXT|0||0
7|visibility|INTEGER|1||0
8|title|TEXT|0||0
9|text|TEXT|0||0
10|sub_text|TEXT|0||0
11|big_text|TEXT|0||0
12|small_icon_hash|TEXT|0||0
13|large_icon_hash|TEXT|0||0
14|picture_hash|TEXT|0||0
15|is_already_read|INTEGER|1||0
16|is_favorite|INTEGER|1||0
sqlite> 
```

これっぽい。いろいろ見てみる。調べてみると

| name           | mean                       |
| -------------- | -------------------------- |
| `package_name` | 通知元アプリのパッケージ名 |
| `title`        | LINEでは投稿者名           |
| `sub_text`     | LINEではOpenChatの名前     |
| `text`         | メッセージ本文             |

らしい。

SQLで抜く
特定のフィールドの回数を見るのは`group by`をうまく使うようだ

https://tapira.hatenablog.com/entry/20120518/1337315414


```sql
sqlite> # 特定OpenChatだけ かつ１回しか投稿（厳密には通知）してないメッセージ ;
sqlite> select text from notification where package_name == 'jp.naver.line.android' and sub_text  == 'LINUX 雑談質問部屋' group by title having (count(title) < 2);
```

sqlite3ではこうすればファイルにでるみたい。

```sql
sqlite> .output hoge.log
sqlite> select text from notification where package_name == 'jp.naver.line.android' and sub_text  == 'LINUX 雑談質問部屋' group by title having (count(title) < 2);
```

さてPython。 `venv` とかしておいたほうがいいのかな？
色々調べた結果、`mecab-python` と IPAの辞書 `ipadic` を入れれば良いようだ。

```
$ python3 -m venv hoge
$ $ source hoge/bin/activate
(hoge) $ pip3 install mecab-python
(hoge) $ pip3 install ipadic
```

Pythonのコードは、複数サイトから引用...もとい参考にしながら進める。いくつかの品詞がじゃまなので捨てる

[1] 一番参考になったページ: https://zenn.dev/flutternyumon/articles/624bdc2d28c606
[2] IPADICってなんぞや: https://zenn.dev/sinozu/articles/44f58dddb25c03f8f05e
[3] どうやっていれればいいんや: https://zenn.dev/yasude/articles/aa28d7ab20b2f1823d4b
[4] 辞書ってなんだ: https://zenn.dev/suyaa/articles/cb8a7e0c2767b3
[5] メッセージ全行をくっつけて出す短いやり方ってどうすりゃあ良いんだ？: https://maku77.github.io/python/io/read-text-file.html

```python3
#!/usr/bin/env python3
import sys
import MeCab
import ipadic
from pprint import pprint

t = MeCab.Tagger(ipadic.MECAB_ARGS) # [3]から
s = [line.rstrip('\r\n') for line in open('hoge.log')] # [5]から
s = ''.join(s)

# 文末に表示される''を消す。こっからほぼ [1]
words = t.parse(s).split('\n')[:-2]

l = []

for e in words:
    word = e.split('\t')
    word_str = word[0]
    wtypes = word[1].split(',')
    # 邪魔なのでちょこちょこ条件加える
    if "助詞" in wtypes or "助動詞" in wtypes:
        continue
    if "動詞" in wtypes or "記号" in wtypes:
        continue

    # DEBUG: 最頻出の単語がどういうので使われているか
    # if "、" == word_str:
        # pprint(word_str)

    l.append(word_str)

# DEBUG: ここで終了させる場合
# sys.exit(0)

wordFreq = {}

for word in l:
    if word in wordFreq:
        wordFreq[word] += 1
    else:
        wordFreq[word] = 1

# 集計した単語の,出現回数を出力
for word, count in wordFreq.items():
    print(count, word)
```

出してみる

```shell-session
$ ./run.py | sort -nr | head -40
8 1
7 通貨
6 さん
6 tony
6 one
5 自分
5 経験
5 仮想
5 円
5 の
5 cn
4 万
4 損失
4 時間
4 市場
4 コミュニティ
4 zhiurl
4 4
3 良い
3 無料
3 毎日
3 方
3 募集
3 分析
3 必要
3 日
3 答え
3 電話
3 送信
3 手当
3 私
3 行
3 携帯
3 ニュース
3 よろしく
3 ため
3 ご
3 お願い
3 ありがとう
3 C
```

## 分析

上位40件のうち、特徴的なワードとその特徴的なワードでテキストを`grep`して得られた考察を上げる。

| ヒット数 | ワード | 考察 |
|-----|----|----|
| 7件 | `通貨` | 5件 `仮想` 4件 `市場` を組み合わせて仮想通貨市場を指し示していることが多い
| 6件 | `tony` | どうもtonyさんという人が`コミュニティ`（4件ヒット）の主らしい
| 6件 | `one` |入室パスワードか、絵文字でよく `(one) (one) (one)` と３つ並べるのでよく引っかかる。何故ゾロ目？
| 5件 | `経験` |仕事の`募集`（3件ヒット）では経験か未経験かが重要らしく、そういう文言が多い
| 5件 | `円` | 求人系のSPAMだと `万`（4件ヒット）と組み合わせて「5万円」などと書いていることが多い
| 5件 | `cn` | TLDとして引っかかっている。`zhiurl`（4件ヒット）と同じ理由、後述。
| 3件 | `答え` | 「パスワード」とやるとNGワードに引っかかる可能性だろうか、入室パスワードと同じパスコードの説明箇所に `答え` と書く傾向がある
| 3件 | `電話` | `携帯`（3件ヒット）と併用し、「携帯電話を使うだけのバイト」として募っている模様
| 3件 | `手当` | バイトでも手当は大事らしい。「【手当】」とよく書かれている

### zhiurl[.]cn について

どうも中国語ユーザ（具体的に広東とか北京とか簡体字とか繁体字とかわからんですが）の短縮URLサービスでAPIで叩けるので大量生成につかわれているのではと推測。

http://zhiurl.cn/

## 感想

上記の短縮URLはどうも中国の大学生が作ったサービスの模様で、こういうのは日本でも発生しうる可能性があるなと感じた。

最近、学生がアプリケーションを作っている下記のようなコンテストもあるし、これからは、学生が積極的にアプリケーションを作っていく環境を整えつつ悪用のリスクへの教導が必要になるのではと感じた。私自身、なにかしらのサービスを作るときは悪用防止の策を十分に考えなければなと、遊んでいただけなのに、色々、別の意味で学ぶことがあった。

https://applikoshien.jp/

ちなみに、所属しているオープンチャットでは、早速上記短縮URLドメインのNGワードを入れたところ、既に2件ほど引っかかっていて効果を発揮してくれた。



> あそびおわり。