2022/01/01

このブログについて

本ブログは、原稿応援Twitterボットの@heeroyuy_genkou@duo_genkouの説明書および技術情報を掲載するものです。

基礎情報一覧

ブログには、各種改造のメモを残していきます。

原稿botとは?

botとは、あらかじめ用意された反応を自動投稿するプログラムのことです。
原稿botは、その中でも、あなたの原稿ライフを、キャラクターっぽく応援するものです。その性質上、創作・改変台詞が多いです。

  1. 元作品の制作会社および各種権利保持者とは何の関係もありません。
  2. ヒイロ(以下、原稿ヒイロ)の言動はツンデレドSですので、マゾな方にしか向きません。
  3. デュオ(以下、原稿デュオ)は応援するはずが割とマイペースなので、やっぱりマゾな方にしか向きません。
  4. 彼らの言うことを真に受けすぎると、健康を損なうことがありますので、気をつけてください。特に健康情報については一般的な情報を基にしており、医学的な正しさを保証するものではありません。
  5. 普通のふれあいは先行ボットにお任せのスタンスでおります。が、原稿催促特化ボットとして「このような反応語や機能をつけて欲しい」等ありましたら、DMでお知らせください。実装可能かどうかの判断は、制作者に委ねていただければと思います。
  6. Twitter本体の障害、ボットが稼動しているサーバの障害、その他の理由で、予告なく停止することがあります。
  7. メンテナンスのお知らせ/障害情報などについては手動で投稿します。ボットのイメージをなるべく維持したいので、簡素な報告にとどめています。詳細をお知りになりたい場合は、DMでいただければ幸いです。
  8. いろいろ機能を実験していて、ボットの調子がいろいろな意味でおかしくなることがあります。
  9. 制作者がドジなので、たまにうっかり漏れ出してきますが、広い心でご容赦ください。

掲載スクリプトについて

PG専門というわけではないので、不具合や非効率な部分があるかもしれません。そのような箇所を発見された場合は、お手数ですがお知らせいただければ幸いです。

2012/04/06

原稿ヒイロbot EasyBotter.phpスケルトン

素のEasyBotter.phpはちょっと関数の並び方の基準がよく分からなかったので、自分に分かりやすいように並び替えています。ついでに、アクセス権付加の真似事のようなこともしています。元スクリプトはメソッドは全部public扱いだったような?

<?php
class EasyBotter {

/*=========================================== 変数宣言一式 */

/*=========================== コンストラクタ・デストラクタ */

  //コンストラクタ
  public function __construct() {
  }

  //デストラクタ
  public function __destruct() {
  }

/*============================== bot.phpから利用する関数群 */

  //自動フォロー返し
  public function autoFollow(){
  }

  //ランダムにポスト
  //bot.php側で何も指定しなければdata.txtを見に行く
  public function postRandom($datafile = DIALOG_DATA){
  }  
  
  //順番にポスト
  //bot.php側で何も指定しなければdata.txtを見に行く
  public function postRotation($datafile = DIALOG_DATA, $lastPhrase = FALSE){
  }

  //リプライする
  //bot.php側で何も指定しなければDIALOG_REPLY→DIALOG_DATAの順番で見に行く
  public function reply($cron = 2, $replyFile = DIALOG_DATA, $replyPatternFile = DIALOG_REPLY){
  }

/*===================================== プライベート関数群 */
/*------------------------------------- ツイート生成関数群 */

  //通常の発言を作る
  //$file:対象ファイル指定
  //$number:対象ファイル内$number目の行を取得
  private function makeTweet($file, $number = FALSE){
  }

  //キーフレーズ置換で発言を作る
  private function makeSwapKeyPhraseTweet() {
  }

  //キーフレーズを元にした発言を作る
  //$text:キーフレーズ取得対象文字列
  private function makeKeyphraseTweet($text) {
  }

  //ガンダムリストに追加
  private function addMemberToList($user_id) {
  }
  //ガンダムリストから削除
  private function removeMemberFromList($user_id) {
  }

  //リプライを作る
  //$replies:リプライ作成元文字列(配列)
  //$replyFile:対象ランダムファイル
  //$replyPatternFile:対象パターンファイル
  private function makeReplyTweets($replies, $replyFile, $replyPatternFile){
  }
  
  //タイムラインへの反応を作る
  //$timeline:
  //$replyPatternFile:対象パターンファイル
  private function makeReplyTimelineTweets($timeline, $replyPatternFile){
  }

/*------------------------------------- ツイート操作関数群 */

  //タイムラインの最近の呟きからランダムに一つを取得
  private function getRandomTweet(){
  }

  //つぶやきの中から$minute分以内のものと、最後にリプライしたもの以降のものだけを返す
  private function getRecentTweets($response,$minute){
  }

  //必要なつぶやきのみに絞る
  private function selectTweets($response) {
  }

  //リプライ一覧から自分が既に返事したものを除く
  private function removeRepliedTweets($response){
  }

/*----------------------------------- キーワード変換関数群 */

  //文章を変換する
  private function convertText($text, $reply = FALSE){
  }

  //日付・時刻を変換
  private function convertDateAndTime($text) {
  }

  //カウントダウンを変換
  private function convertCountDown($text) {
  }

/*----------------------------------------- HTML出力関数群 */

  //HTML冒頭部分出力
  private function printHeader() {
  }

  //HTML末尾部分出力
  private function printFooter() {
  }

  //結果を表示する
  private function showResult($response) {
  }

/*----------------------------------- 内部データ保存関数群 */

  //リプライツイートIDをログファイルに記録(どこまでリプライしたかを覚えておく)
  private function saveLog() {
  }

  //ログの順番を並び替える(というかたぶん使用した行を最下部に移動するだけ)
  private function rotateData($file) {
  }

/*-------------------------------------- ファイルI/O関数群 */

  //つぶやきデータを読み込む
  private function readDataFile($file) {
  }

  //リプライパターンデータを読み込む
  private function readPatternFile($file) {
  }

/*------------------------------- TwitterAPIアクセス関数群 */

  //基本的なAPIを叩く
  private function _setData($url, $value = array()) {
  }

  private function _getData($url) {
  }

  private function setUpdate($value) {
  }

  private function getFriendsTimeline() {
  }

  private function getReplies($page = false) {
  }

  private function getFriends($id = null) {
  }

  private function getFollowers() {
  }

  private function followUser($screen_name) {
  }

  private function createListMember($list_id, $user_id) {
  }

  private function destroyListMember($list_id, $user_id) {
  }
}

/*=========================================== その他関数群 */

function is_noun($word) {
}

?>

2012/04/05

名詞入れ替えで作文

EasyBotter.phpを以下のように改造して様子見中。あまり正規表現に詳しくないので悲惨なことになっております。

template.csvはあらかじめ作成していたものを、word.csvは、キーフレーズ抽出時にこっそり保存していたものを流用しています。

class EasyBotter {
  // キーフレーズ置換で発言を作る
  private function makeSwapKeyPhraseTweet() {
    $keyphrase = array();
    $poem = array();
    $table = "。";
    $status = "";
    // キーフレーズCSVを読み込み
    $fp = fopen('word.csv', 'r') or die('ファイルが開けません');
    while(($columns = fgetcsv($fp, 4096, ",", '"')) !== FALSE) {
      array_push($keyphrase, $columns[0]);
    }
    fclose($fp);
    shuffle($keyphrase);
    // 原文CSVを読み込み
    $fp = fopen('template.csv', 'r') or die('原文ファイルが開けません');
    while(($columns = fgetcsv($fp, 4096, ",", '"')) !== FALSE) {
      array_push($poem, array($columns[0], $columns[1]));
    }
    fclose($fp);
    // 名詞を適当に入れ替える
    $keyphrase_num = count($keyphrase);
    $rd_keys = array_rand(array_filter($poem, 'is_noun'), $keyphrase_num);
    for($i=0; $i<$keyphrase_num; $i++) {
      $poem[$rd_keys[$i]][0] = $keyphrase[$i];
    }
    // テーブル作成
    for($i=0; $i<count($poem); $i++) {
      $table .= $poem[$i][0];
    }
    // 一致している間は選びなおす
    $status_1 = "";
    $status_2 = "";
    while($status_1 === $status_2) {
      // キーフレーズを選出×2
      $rd_keys = array_rand($keyphrase, 2);
      
      $matches_1 = array();
      $matches_2 = array();
      
      $pattern_1 = '/^(.*)[。?](.*?)'.$keyphrase[$rd_keys[0]].'(.*?[。?])/u';
      $pattern_2 = '/^(.*)[。?](.*?)'.$keyphrase[$rd_keys[1]].'(.*?[。?])/u';
      
      preg_match_all($pattern_1, $table, $matches_1);
      preg_match_all($pattern_2, $table, $matches_2);
      
      $status_1 = $matches_1[2][0].$keyphrase[$rd_keys[0]].$matches_1[3][0];
      $status_2 = $matches_2[2][0].$keyphrase[$rd_keys[1]].$matches_2[3][0];
    }
    $status = "「".$status_1.$status_2."」";
    return $status;
  }

  // 途中省略

  private function makeReplyTweets($replies, $replyFile, $replyPatternFile){
    if(empty($this->_replyPatternData[$replyPatternFile]) && !empty($replyPatternFile)){
      $this->_replyPatternData[$replyPatternFile] = $this->readPatternFile($replyPatternFile);
    }
    $replyTweets = array();
    foreach($replies as $reply){
      $status = "";
      // 追加ここから
      if(strpos((string)$reply->text, "作文して") !== FALSE) {
        // キーフレーズで作文
        $status = $this->makeSwapKeyPhraseTweet();
      }
      // 追加ここまで
    // 以下省略
    }
  }
}

各データファイルの構造

word.csv

1行1カラムの単純なデータです。相手のキーフレーズ+定型文で作文 でこっそり保存していたものです。

"スカイツリー"
"お台場"
"実績取り"
"競合他社"
"以下略"
"デスマッチ"
"中心"
"沿岸部"

template.csv

元となる作文をあらかじめ形態素解析しておいたものです。この中から名詞をランダムに取り出して、word.csvの中身と入れ替えています。

"太陽系","名詞"
"に","助詞"
"生物","名詞"
"が","助詞"
"生息","名詞"
"する","助動詞"
"こと","名詞"
"を","助詞"
"奇跡的","形容動詞"
"に","助詞"
"可能","名詞"
"に","助詞"
"し","動詞"
"た","助動詞"
"惑星","名詞"
"、","特殊"
"それ","名詞"
"が","助詞"
"地球","名詞"
"で","助動詞"
"ある","助動詞"
"。","特殊"

特定の日付までのカウントダウン

EasyBotter.phpを以下のように改造します。
例によってあまり賢い方法ではありません……。

class EasyBotter {

  // 途中省略

  // 文章を変換する
  private function convertText($text, $reply = FALSE){
    // 途中省略

    // 追加ここから
    // カウントダウン
    if(strpos($text, "{countdown}") !== FALSE){
      $text = $this->convertCountDown($text);
    }
    // 追加ここまで

    // フッターを追加
    $text .= $this->_footer;
    return $text;
  }

  // カウントダウンを変換
  private function convertCountDown($text) {
    $return_text = "";
    // デリミタで分割
    $event_array = explode( ",", $text);
    $target_array = explode("-", $event_array[0]);
    // 目標の日付
    $target_date = mktime(0, 0, 0, $target_array[1], $target_array[2], $target_array[0]);
    // 今日の日付
    $current_date = mktime(0, 0, 0, date("n"), date("j"), date("Y"));
    // 差を計算
    $interval = ($target_date - $current_date) / 86400;
    // 日付を日本語表記に
    $event_array[0] = date("Y年n月j日", $target_date);
    
    if($interval > 0) {
    // 日程より前
      $return_text = $event_array[0].$event_array[1]."の".$event_array[2]."まで、あと".$interval.$event_array[4]."……。";
    } elseif($interval == 0) {
    // 当日
      $return_text = "今日は".$event_array[1]."の".$event_array[2]."当日だ。任務了解。明朝より作戦行動に入る。";
    } elseif($interval == -1) {
    // 昨日
      $return_text = $event_array[0].$event_array[1]."は、昨日".$event_array[2]."を完了している……任務、完了。";
    } else {
    // 2日前以前
      $return_text = $event_array[0].$event_array[1]."は、".abs($interval).$event_array[4]."前に".$event_array[2]."を完了している……。";
    }
    return $return_text;
  }

  // 以降省略
}

パターンファイルやランダムファイルの方では、例えば以下のように記述しています。

2012-4-26,ガンダムエース6月号,発売,{countdown},日
2012-5-3,SUPER COMIC CITY 21 1日目,開催,{countdown},日