TwitterでReudyをベースにした人工無能、「はるか♡BOT」を設置しています。
こういったbotを作るために、何をどうやっているのかと関心を持って頂いた人のために、内部で行っている処理をまとめました。
botを作る際の参考になれば幸いです。何か質問がありましたら@iPCMまで。
以下に処理図を貼り付けておきます。括弧内は使用言語です。*1(クリックで拡大)
TwitterIrcGatewayがAPIを使用して@ha_ru_kaのタイムラインを取得しに行きます。
ReudyはIRC用のbotですので、TwitterIrcGatewayに対しての接続は難なく行われますが、文字コードが違うため化けてしまいます。 IRC受信時にUTF-8→EUC-JPへの変換を行っています。 自分宛のリプライには応答するロジックが元から入っていますので、グローバル変数を用意しそこに呼びかけられた人のidを格納しています。
ReudyのIRC投稿ロジックを潰し、ローカルにTwitterでおなじみの「@id名 投稿内容」という形式で書き出し、MdbStore.exe を即時呼び出します。
MdbStore.exeは(5)で書き出されたテキストを読み込みます。レス番号を指定してリプライを繋げるために、@idのタイムラインをwebスクレイピングを使って最大20発言取得し、「ha_ru_ka」「春香」「はるか」「閣下」のキーワードが含まれている発言番号を取得します。みつからなければ、その人の最新の発言に対しレスを付けます。
この時点で投稿内容は、「@id名 @無関係な人のid名 @無関係な人のid名 投稿内容」となっていますので、2つめ以降の@を消します。
これで投稿内容がほぼ確定します。
最後に発言キュー(Access MDB)に、@返信先,レス番号,投稿内容をセットし一連の流れは終了です。
以下はタイマーにより起動されます
MdbPost.exeがWindowsのタスクスケジューラで1分に1度起動されます。 発言キューに未発言があったら最初の1件読み込みます。
最初に読み込んだ返信先idに対する他の発言がないか先読みします。
あれば同一idの@送り先メッセージは1つの発言にまとめます。
ちょっとしたお遊びで、低確率ですがランダムで方言や語尾口調を変換するロジックが入っています。
次にその人へリプライを返して良いか最後の確認として、過去n日以内にその人から@をもらったことがあるかチェックします。
@をもらっていればその人に@を返します。@をもらっていなければ高確率で独り言へと変換されます。
ローカルのテキストファイルに春香botが呟く内容をセットします。
最後にローカルのテキストファイルをTwitterにPostするPHPをshellで呼びます。
oAuthPost.phpがローカルのテキストファイルを読み込みます。
oAuth認証をし、TwitterへとPostします。クライアント名が春香BOTとなっているはずです。
VB6でoAuth実装は断念しましたw ![[smile]](/image/face/smile.png)
Twitter特有のフォロー、リムーブ処理を自動化しています。
現在の所、フォローされていない人に@を飛ばさないようにする機能は実装していません。
はるか♡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