見出し画像

TwitterAPIで不具合報告監視(通称:REALITYバグひろったー) - REALITY Advent Calendar #20

REALITY Advent Calendar 20日目担当は約3ヶ月前にサーバチームにジョインしたsola-msrです!
入社後初の記事執筆ですのでお手柔らかにお願いいたします・・m(_ _)m

自分は日頃から趣味で推しのVTuberの配信活動を監視する(?)botなどを作ったりしてTwitterAPIと戯れているのですが、この合宿中でもTwitterAPIを使って何か作ってみたいと思い色々考えてみた結果、次のWebサービスみたいなのを作ろうと思いました。

Downdetectorぽいのを作りたい

皆さんは「Downdetector」というWebサービスをご存知でしょうか?

どういうサービスなのかは以下引用文を参照

Downdetectorは、SNSの書き込みを利用することにより、様々なWebサービス・通信サービスなどの障害発生状況をリアルタイムに推測しているWebサイト・スマートフォン向けアプリケーションである。後述するように、SNSの投稿を情報源としているため、誤検知を含むことがある。

引用 - https://ja.wikipedia.org/wiki/Downdetector

Wikipediaから引用してきた通りで、TwitterなどのSNSで挙げられた報告などから、リアルタイムで一部のWebサービスの状態を可視化する、というようなことを提供しているWebサービスです。

Webサービスの状態を可視化するにあたって、全部が全部SNSの情報を使って行ってはいないようですが、自分の経験上、Webサービスやアプリに何かしらの障害が発生すると必ずと言っていいほどTwitter上に不具合報告があがっているのをよく目にします。

FireShot Capture 018 - REALITYバグひろったー(仮) - Google スライド - docs.google.com

そういうツイートをTwitterAPIを使って監視・収集することで、REALITYの不具合などのツイートを収集したり、既存の障害発生アラート以外などの外部からもサービスの状態を知ることができるようなDowndetectorぽいツールREALITYバグひろったー」を作ろうと思いチャレンジしてみました!

使用するTwitterAPIの機能

とりあえずツイートを検索して取得する必要があるので、以下の検索APIの機能を使用していきます。

Using standard search | Docs | Twitter Developer Platform

ポジティブツイート or ネガティヴツイート

画像2

上記のAPIドキュメントを見て初めて知ったことなのですが、実はツイートを取得する際にポジティブなツイートを取得するネガティヴなツイートを取得するかを顔文字「:)」or「:(」のパラメータで指定できるようです。

画像1

ポジティブなツイートの例

movie -scary :)       containing “movie”, but not “scary”, and with a positive attitude.

訳:「映画」を含むが「怖い」ではなくポジティブなツイートが検索対象

ネガティヴなツイートの例

flight :(       containing “flight” and with a negative attitude.

訳:「飛行」を含んだネガティヴなツイートが検索対象

とりあえずこのパラメータを駆使すればそれっぽいツイートを取得できそう・・・ということで使ってみました!

アプリを作っていく

とりあえず合宿中は時間がないので、自分がある程度書けて環境構築もサクッとできるPHPを使用するようにしました。

まずTwitterAPIを利用するにあたってライブラリ「abraham/twitteroauth」をcomposer(PHPのパッケージ管理ツール)でインストール

composer.json

{
   "require": {
       "abraham/twitteroauth": "3.0.0"
   }
   ,"autoload": {
       "psr-4": {
           "Common\\" : "Classes/common"
       }
   }
}​

あとは検索処理と画面に表示させるコードを書いていく

index.php

<?php
date_default_timezone_set('Asia/Tokyo');
require_once __DIR__ . '/vendor/autoload.php';

try {
    // TwitterOAuthインスタンス取得
   $twitter = new \Common\Twitter(1);
   
   // 検索ワード
   $words = ["REALITYバグ","REALITY壊れた","REALITY不具合","REALITY開けない","REALITY重い","REALITY酷い","REALITY運営","REALITY良い"];

   echo "<h1>REALITYバグひろったー(仮)</h1>";
   $link = "";
   foreach($words as $word) {
       $link .= "<a href='#{$word}'>{$word}</a>|";
   }
   echo $link;

   $date = new \DateTime();
   $nowDateTime = $date->format('Y-m-d');

   foreach($words as $word) {
   
       // ここで検索ワードと取得件数、最近のツイートor人気のツイートor両方、ネガorポジを指定
       $results = $twitter->searchTweets($word, 60, "resent", "nega");
       
       echo "<h2 id='{$word}'>{$word}</h1>";
       
       foreach ($results->statuses as $key => $tweet) {
           // 以下は検索精度を高めるためあまり関係ないツイートの場合は表示させないようにスキップしている
           if(strpos($tweet->text,'REALITY') === false && strpos($tweet->text,'reality') === false){
               continue;
           }
           if(strpos($tweet->text,'配信中!! #REALITY ') !== false){
               continue;
           }
           if(strpos($tweet->source,'REALITY–バーチャルライブ配信アプリ') !== false){
               continue;
           }
           
           // 表示させるために整形
           $text = '';
           $text .= "<img src='".$tweet->user->profile_image_url."'>";
           $text .= "<b><a href='https://twitter.com/{$tweet->user->screen_name}' target='_blank'> {$tweet->user->name}@{$tweet->user->screen_name}</a></b>";
           $text .= "<p>".$tweet->text."</p>";
           $text .= "<p>{$tweet->created_at} by {$tweet->source}</p>";
           $text .= "<p><a href='https://twitter.com/{$tweet->user->screen_name}/status/{$tweet->id_str}' target='_blank'>詳細なツイートを見る</a></p><hr/>";
           echo $text;
       }
       echo "<br><br><hr width='400' size='10' noshade=''/><br><br>";
   }

   return;
} catch (\Throwable $exception) {
   header('Content-Type: text/plain; charset=UTF-8', true, 500);
   exit($exception->getMessage());
}

Classes/common/Twitter.php

<?php
namespace Common;

use Abraham\TwitterOAuth\TwitterOAuth;

class Twitter
{
   /**
    * TwitterOAuth connection
    */
   private $connection;

   /**
    * コンストラクタ
    */
   function __construct(protected int $ver = 1)
   {
       try {
           // TwitterOAuthインスタンス取得
           $this->connection = new TwitterOAuth("[consumerKey]", "[consumerKeySercret]", "[accessToken]", "[accessTokenSecret]");
           
           // バージョン
           if ($ver != 1 && $ver == 2) {
               $this->connection->setApiVersion('2');
           }

           return;
       } catch (\Exception $e) {
           header('Content-Type: text/plain; charset=UTF-8', true, 500);
           exit($e->getMessage());
       }
   }

   /**
    * ツイート検索
    *
    * @param string $text
    * @return
    */
   function searchTweets(string $text, int $count = 15, string $resultType = "resent", string $negaposi = "none")
   {
       if ($negaposi === "none" || $negaposi !== "nega" || $negaposi !== "posi") {
           $searchText = $text;
       }elseif($negaposi === "nega"){
           $searchText = "{$text}%20%3A("; // :( ネガティヴ
       }elseif($negaposi === "posi"){
           $searchText = "{$text}%20%3A)"; // ;) ポジティブ
       }

       return $result = $this->connection->get("search/tweets", [
           "q"                => "{$searchText}",
           "lang"             => "ja",
           "locale"           => "ja",
           "include_entities" => true,
           "count"            => $count,
           "result_type"      => $resultType,
       ]);
   }
}

めちゃくちゃ雑&記事用に少し変更している部分もあるのですがだいたいこんな感じのコードを書きました。

完成!!!?

とりあえず「REALITY バグ」や「REALITY 開けない」などのワードでかつネガティヴ寄りなツイートを取得するようにし、一覧で画面に表示させるアプリが完成しました!

以下が実際にできたアプリの画面です。

FireShot_Capture_008_-__-_localhostのコピー

(※取得したツイートの内容はモザイクをかけさせていただいております。)

検索する際に引数に検索ワード取得件数最近のツイートor人気のツイートor両方ネガティブなツイートorポジティブなツイートを指定してます。

// ここで検索ワードと取得件数、最近のツイートor人気のツイートor両方、ネガorポジを指定
$results = $twitter->searchTweets($word, 60, "resent", "nega");
   /**
    * ツイート検索
    *
    * @param string $text
    * @return
    */
   function searchTweets(string $text, int $count = 15, string $resultType = "resent", string $negaposi = "none")
   {
       if ($negaposi === "none" || $negaposi !== "nega" || $negaposi !== "posi") {
           $searchText = $text;
       }elseif($negaposi === "nega"){
           $searchText = "{$text}%20%3A("; // :( ネガティヴ
       }elseif($negaposi === "posi"){
           $searchText = "{$text}%20%3A)"; // ;) ポジティブ
       }

       return $result = $this->connection->get("search/tweets", [
           "q"                => "{$searchText}",
           "lang"             => "ja",
           "locale"           => "ja",
           "include_entities" => true,
           "count"            => $count,
           "result_type"      => $resultType,
       ]);
   }

実際に取得したツイートを見ると色々な不具合がツイート上で報告されているのがわかりました。数々のツイートを眺めているとちょっと胃が痛くなってきました。
もちろん報告いただいている不具合の中には既知の問題もあり、日々修正・改善の対応しておりますので、対応が完了するまでもう少しお待ちいただければと思います・・!

気を取り直して試しにポジティブなツイートも取得してみようと思い検索条件を以下のように変更

// ポジティブな感じのワードに書き換え
$words = ["REALITY運営","REALITY良い"];

// ~~ 略 ~~

// 第4引数を"nega"から"posi"へ変更
$results = $twitter->searchTweets($word, 50, "resent", "posi");

すると一転して暖かいお言葉が並ぶツイートがずらり表示されました。

画像5

中には「頑張ってください!」などの応援ツイートなどもあり、嬉しさのあまり泣きそうになりました・・・!

REALITYバグひろったー」構想

合宿中はただの検索画面を作っただけで終わってしまいましたが、理想は初めの方にも書いたDowndetectorぽいものを作りたい!なので、時間がある時にコツコツと作っていきたいと思っています。

今後の構想としては

●不具合系のツイートを取得(cronなどで一定時間間隔で定期実行させる)●データストアにある一定の時間で取得できたツイート数を保存
●保存したツイート数や内容を精査する
(グラフ画像などを生成し視覚的に見えるようにする)
●しきい値を超えた場合や取得したツイートの内容次第でSlackへ通知を送るようにする

ができるようになればDowndetectorぽいものになるかなーと考えています。

おわり

もちろん日々、新機能の開発等も行っておりますが、並行して不具合の修正・改修も同時に行っております。(特にクリティカルな不具合は発見次第、修正対応しております。)
しかしながら、まだまだ対応しきれていない部分があるのも事実です。
ツイート上で報告いただいている不具合の一部も把握しており、日々改善を行っておりますので、どうか対応完了までお待ちいただければと思います・・!

明日のアドベントカレンダーは

明日の21日目はUnityエンジニアのあさださんによる「お絵かきギフトを作ってみた」です!
お楽しみに!