TwitterでReudyをベースにした人工無能、「はるか♡BOT」を設置しています。
こういったbotを作るために、何をどうやっているのかと関心を持って頂いた人のために、内部で行っている処理をまとめました。
botを作る際の参考になれば幸いです。何か質問がありましたら@iPCMまで。


「はるか♡BOT」本体 .

  • Reudy(IRC版)をベースにしています。
  • 取得部分はTwitterIrcGatewayにまかせています。
  • ReudyにはTwitter特有の文字コード変換、@返し処理などを追加しています。
  • 投稿する部分から先はかなり特異なことをしています。

処理説明 .

処理図 .

以下に処理図を貼り付けておきます。括弧内は使用言語です。*1(クリックで拡大)


ha_ru_ka_fllow.jpg

(1)〜(3) .

TwitterIrcGatewayがAPIを使用して@ha_ru_kaのタイムラインを取得しに行きます。

(4) .

ReudyはIRC用のbotですので、TwitterIrcGatewayに対しての接続は難なく行われますが、文字コードが違うため化けてしまいます。 IRC受信時にUTF-8→EUC-JPへの変換を行っています。 自分宛のリプライには応答するロジックが元から入っていますので、グローバル変数を用意しそこに呼びかけられた人のidを格納しています。

(5) .

ReudyのIRC投稿ロジックを潰し、ローカルにTwitterでおなじみの「@id名 投稿内容」という形式で書き出し、MdbStore.exe を即時呼び出します。

(6) .

MdbStore.exeは(5)で書き出されたテキストを読み込みます。レス番号を指定してリプライを繋げるために、@idのタイムラインをwebスクレイピングを使って最大20発言取得し、「ha_ru_ka」「春香」「はるか」「閣下」のキーワードが含まれている発言番号を取得します。みつからなければ、その人の最新の発言に対しレスを付けます。 この時点で投稿内容は、「@id名 @無関係な人のid名 @無関係な人のid名 投稿内容」となっていますので、2つめ以降の@を消します。 これで投稿内容がほぼ確定します。 最後に発言キュー(Access MDB)に、@返信先,レス番号,投稿内容をセットし一連の流れは終了です。

以下はタイマーにより起動されます

(7) .

MdbPost.exeがWindowsのタスクスケジューラで1分に1度起動されます。 発言キューに未発言があったら最初の1件読み込みます。

(8) .

最初に読み込んだ返信先idに対する他の発言がないか先読みします。 あれば同一idの@送り先メッセージは1つの発言にまとめます。 ちょっとしたお遊びで、低確率ですがランダムで方言や語尾口調を変換するロジックが入っています。 次にその人へリプライを返して良いか最後の確認として、過去n日以内にその人から@をもらったことがあるかチェックします。 @をもらっていればその人に@を返します。@をもらっていなければ高確率で独り言へと変換されます。
ローカルのテキストファイルに春香botが呟く内容をセットします。 最後にローカルのテキストファイルをTwitterにPostするPHPをshellで呼びます。

(9)〜(12) .

oAuthPost.phpがローカルのテキストファイルを読み込みます。 oAuth認証をし、TwitterへとPostします。クライアント名が春香BOTとなっているはずです。 VB6でoAuth実装は断念しましたw [smile]

支援 .

Twitter特有のフォロー、リムーブ処理を自動化しています。

Auto Follow返し .

  • あなたをフォローしましたよという通知メールを監視しています。
  • フォローした人のTLを最大20件取得します。
  • 日本語が含まれていればフォロー返しを自動的に行います。それ以外は無視します。
  • 現在は1分に1回チェックしています。

Auto Remove返し .

現在の所、フォローされていない人に@を飛ばさないようにする機能は実装していません。
はるか♡BOTはタイムラインに現れた人に反応して@を返すので、リムーブして通常の呟きがタイムラインに表示されなくなれば@を返すこともなくなります。(はるか♡BOTに直接@を送ったときはフォロー状態に関わりなく@を返します)
現在は10分に1回チェックしています。

処理概要 .

A)はるか♡BOTがフォローしている人の一覧を取得します。
B)はるか♡BOTをフォローしている人の一覧を取得します。

A - Bで残った人をAPIでリムーブします。

実際に動かしているVBのソースを置いときます

Option Explicit
Private Const sTwitterId   As String = "*******"
Private Const sPasswordId  As String = "*******"

Sub Main()

    On Error Resume Next

    Dim objHttp  As New MSXML2.XMLHTTP
    Dim objXML As New MSXML2.DOMDocument
    Dim objIDs  As New MSXML2.DOMDocument
    Dim cXmlRoot    As Collection
    Dim cIds        As Collection
    Dim vLoop       As Variant
    Dim sKey        As String
    
    Dim vFrendIds       As Variant
    Dim vFollowersIds   As Variant
    Dim fp          As Integer
    Dim fp2         As Integer
    
    Dim sReadBuf    As String
    Dim cSkipIds    As New Collection
    
    Dim cId As New Collection
    Dim sSkipFile   As String
    
    objHttp.Open "GET", "http://twitter.com/friends/ids/" & sTwitterId & ".xml", False, sTwitterId, sPasswordId
    objHttp.send
    
    If objHttp.Status = 200 Then
        objXML.async = False
        objXML.loadXML objHttp.responseText
        Set cXmlRoot = ExpandXML(objXML)
        
        objIDs.async = False
        objIDs.loadXML cXmlRoot("ids")(1).xml
        vFrendIds = Split(objIDs.childNodes(0).Text, " ")
    
        objHttp.Open "GET", "http://twitter.com/followers/ids/" & sTwitterId & ".xml", False, sTwitterId, sPasswordId
        objHttp.send
        If objHttp.Status = 200 Then
            objXML.async = False
            objXML.loadXML objHttp.responseText
            Set cXmlRoot = ExpandXML(objXML)
            objIDs.async = False
            objIDs.loadXML cXmlRoot("ids")(1).xml
            vFollowersIds = Split(objIDs.childNodes(0).Text, " ")
        Else
            End
        End If
    Else
        End
    End If
    
    On Error Resume Next
    sSkipFile = App.Path & "\SkipIds_" & sTwitterId & ".txt"
    fp = FreeFile
    If Dir(sSkipFile) <> "" Then
        Open sSkipFile For Input As #fp
        Do While Not EOF(fp)
            Line Input #fp, sReadBuf
            cSkipIds.Add sReadBuf
        Loop
        Close #fp
    End If
    For Each vLoop In vFrendIds
        cId.Add Key:="KEY:" & (vLoop), Item:=(vLoop)
    Next
    
    For Each vLoop In vFollowersIds
        cId.Remove "KEY:" & (vLoop)
    Next
    
    For Each vLoop In cSkipIds
        cId.Remove "KEY:" & (vLoop)
    Next
    
    
    fp = FreeFile
    Open App.Path & "\RemoveIds_" & sTwitterId & ".txt" For Append As #fp
    
    Print #fp, Now, "RemoveCount = " & cId.Count
    If cId.Count > 3 Then
        Close #fp
        End
    End If
    
    For Each vLoop In cId
    '
        objHttp.Open "POST", "http://twitter.com/friendships/destroy/" & vLoop & ".xml", False, sTwitterId, sPasswordId
        objHttp.send

        
        Print #fp, Now, "(" & objHttp.Status & ")Remove", "http://twitter.com/statuses/user_timeline/" & vLoop & ".rss"
        If objHttp.Status = 404 Then
            fp2 = FreeFile
            Open sSkipFile For Append As #fp2
            Print #fp2, (vLoop)
            Close #fp2
        End If
    Next
    
    Close #fp
   
    End
    
End Sub

'childNodes展開
Private Function ExpandXML(ByRef objData As Variant) As Collection
    Dim vLoop   As Variant
    Dim cRet    As New Collection
    For Each vLoop In objData.childNodes
        cRet.Add Key:=vLoop.baseName, Item:=Array(vLoop.baseName, vLoop)
    Next
    Set ExpandXML = cRet
End Function

Link .


*1 TwitterIrcGatewayはC#でした