アイマスPなのでプログラムで最上静香を喋らせる

f:id:runaru_dearly1641:20180428012033j:plain

(マルコフ連鎖)あーそーゆーことね完全に理解した

はじめに

ミリ5thまさかのweb先行が当たってしまった…

もう行けないイベントのつもりでいた上に、当たったのが静香がいる2日目じゃなく初日だから本命日に参加できないのに東京まで交通費かけて行く必要があると考えると、嬉しいんだけど素直に手を上げて喜べない…

本題

経緯

これまでTwitterAPIでオタクブロックや懸賞漁りをしてきたのですが、ただ検索結果をちょっと見て処理するだけだから大した面白味もないし、他にAPI使った面白いことも思いつかない、ということで

h1dia.hateblo.jp

前々から興味のあったこの記事の内容に挑戦しようと思った次第です。

上記事の雰囲気から、台詞さえ集めることができればどんなキャラクターでも同じことができると感じたので、最上静香というキャラで同じことをやってみようとおもいます。

台詞集め

最上静香はアイドルマスターミリオンライブというソシャゲのキャラなので、ミリオンライブのwikiからテキストを集めます。ソシャゲはテキストの入手難易度が低めで良いですね。

最上静香 - ミリオンライブWiki

このページだけでは足りないかなと考えて、カードの台詞や一部チョコイべの台詞も少し加えたものがこんな感じ。全部で400行くらいになるまで集めました。

f:id:runaru_dearly1641:20180422230749p:plain

これで下準備完了です。

台詞生成プログラムを動かす

テキスト集めが終わったので、とうとう本番の台詞生成と洒落込みます!

初めに紹介したブログにプログラムのソースコードが載っているので、それをそのまま動かすだけです!


...と言いたいところですが、pythonしかわからない私にこのコードは読めません。

そこで[マルコフ連鎖 文章生成 python]、でググって見たところ、このサイトにうまくいきそうなソースコードが載っていました。

[Python]マルコフ連鎖で自動文章 | IT屋シミダイの頭の中

これならpythonで書いてあるのでそのまま動かせます!早速実行

実行結果

…え?点数をよく見ろ?わぁ…美也さんは、今日できることを子供扱いせずに真剣にやって、歌えるってことは、不安になった時に、見たままのものじゃないですか?もう、ふざけないでください!…個性的なメンバーだな、なにしてるんですよ? もっと近くに新しいおうどん屋さんが入るとチームに貢献できたみたい。時間は、アイドルなんですか?へえ…お仕事やレッスンの予定を確認するのに、便利そうですか?ええ、私のこと、信じて下さい!なんでアイドルが合戦なんですね!みんなの様子を見られたら、出発しましょうね…。私、頑張ります!プロデューサー…私、型通りに動くことにばかり気を遣ってくれませんでした…。私のことは、久しぶりにっ!前回と同じでちょっとなつかしいですね。それって、プロデューサー…? これからもずっと一緒に歌ってください。「私には、え…?ふきこぼれてるわよ、うどん!じゃあ、私の誕生日は、仲間ですから。今日のうちに、どうぞ!…み、みんなで力を合わせたから…嬉しいです。アイドルしながらでもありますから…!サッカーの基本的なメンバーだな、なんですか。美也さん、詳しいですね!脱走が成功したんですから…!
  (中略)  
昨日のお昼はきつねにします!…プロデューサー。あの、実は…。私、すごく厳しいことをしても平気なので…私も…よろしくお願いします。…仕事もやりきったような顔してくれるまで誰も帰しません!』…って。…やっぱり私がなんとかしてあげます。うどんにはレッスンの予定を確認するのに、どうしたみたいで…。かわいい私物をチェックします。うどんにしようと?プロとして全力を出し切りました。プロデューサーがそばにいたら、みんな驚いてくれたら、そのリアクションは!?…!私は大丈夫です。校門前の


なんやコレ一体…

中略してありますが、実際にはルイズコピペが霞んで見えるほどの長文が生まれてきました…その理由を知るためにはプログラムのコードと向き合うしかなさそうです…メンドクサイ…

君もプログラムと向き合う時なんだ

作成部分のコードに注目してみます。

    # 文章の自動作成
    count = 0
    sentence = ""
    w1, w2  = random.choice(list(markov.keys()))
    while count < len(wordlist):
        tmp = random.choice(markov[(w1, w2)])
        sentence += tmp
        w1, w2 = w2, tmp
        count += 1

wordlistは配列で、下準備で集めた台詞全部を単語ごとに区切ったその各単語が要素になっています。なんでlen(wordlist)は総単語数で、1づつ増やされていくcountがそれを超えるまでループされます。

つまり、whileは総単語数分実行されることになります。


実行で作成されたヴェノミナーガのテキストのような長文は、総単語数が膨大で、その膨大な総単語数の数だけ単語をを繋げて文章を作るように設定されていたせいで生まれてきたわけです。

len(wordlist)を10とか50とかに変えれば程よい台詞を作ってくれるハズです。

おわり

プログラムを読んでマルコフ連鎖とやらの仕組みをなんとなく把握したので気が向いたら続きを書く

文章の終わりが「校門の」と文としては不自然、その辺りの"文の自然さ"を少しでもマシになるように色々試したので気が向いたら続き書く

フォロー制限に引っかかった プログラムの欠陥か

f:id:runaru_dearly1641:20180408203434j:plain

バカヤローだ!

はじめに

釈迦が始まったら一部のアイマスオタクが好きな越境()とやらの仲間入りするんんだろうなと考えると鬱だ

越境オタクは、ASグリモバホモでアイマスが全てみたいな物言いなのと、どれも等しく愛してる自分が好きみたいな動物愛護団体地味たエゴを感じるからきらい、どんなアイマスでも愛してるんなら876+ゼノグラ+魔王エンジェルスとかで描いてみろよと

本題

フォロー制限に引っかかった

前回の

 

naru1641.hatenadiary.jp

この記事のプログラムを改変して、ツイッターのよくあるRT&フォロー懸賞企画を漁ってRTフォローするプログラムを作って回していたところ、フォロー制限に引っかかってしまいました。

 

表示されたエラーメッセージはこんなの

 TwythonError: Twitter API returned a 403 (Forbidden), You are unable to follow more people at this time. Learn more <a href='http://support.twitter.com/articles/66885-i-can-t-follow-people-follow-limits'>here.

 

原因を考えてみる

一気に何百人もフォローとかはしていないし何故制限に触れたのだろう、と少し考えて思い当たったのは

 

フォローしてるユーザーじゃないときにフォローするように条件記述をしていたつもりだったけれどその部分が意図した通りに機能しておらずにフォロー申請を飛ばしまくっていた

 

という可能性.

 

怪しい部分のコード

if not tweet['statuses']['user']['follow']:
            twitter.create_friendship(screen_name = tweet['statuses']['user']['idName'], user_id = tweet['statuses']['user']['userId'], follow = True)

ここで見ているのはツイートのデータの中のをしたユーザーに関する部分(User object)です。

User object — Twitter Developers

 

そのなかには"following"という項目があり、説明はこうなっています。

Deprecated. Nullable . Perspectival . Deprecated. When true, indicates that the authenticating user is following this user. Some false negatives are possible when set to “false,” but these false negatives are increasingly being represented as “null” instead. 

 

私は英語が読めないので、ツイートしてるユーザーをフォローしていたらここの値はtrueになるのだと予想して、上プログラムで if not (略)['following']としました。

(if not でその中身がTrueでない(フォローしていない)ときcreate_friendship(ユーザーフォロー)が行われる、という動作になることを期待していた)

 

しかし制限にかかった後、確認のためにフォローしてる人のfollowingの中身を見てみると、その中身はNone。

 

Trueと思っていた値がNoneだったので、これでは今までのプログラムはずっと既にフォローしていた人にもcreate_friendshipをしていた可能性があります。
これは規制の原因になり得る。

(followingの勘違い以前に、どうやらDeprecatedというのは非推奨という意味らしくそれを使っていた時点でよろしくなかったよう)

ここのコードの解決策

パッっと思いつくのは、めんどくさそうだけど、sqlやらに自分のフォローの一覧を作って毎回それと照合。リストにいなかったときにフォローする、という方法です。めんどくさそうだけど。

 

StreamingAPIも余命二ヶ月で、このプログラムもうすぐ使えなくなるし改善とかするの時間の無駄じゃないか、とか思ったり。

 

pythonで釈迦事前登録ツイートしてる垢をブロックする アイマスオタク殺すべし

f:id:runaru_dearly1641:20180402174723p:plain
東京湾に叩き込めー!

はじめに

これはtumblrにかいたものの転載です。tumblrではプログラム載せづらいので

一応断っておきますが、思いついたから作っただけで実際にユーザーブロックはしていません。私はあふがおではないので。

以下本文

アイマス最高
プロデューサー最低

   〜トリプルデイP〜


"キャラクターの誕生日に絵描きの絵をさも自分が描いたのように無断でアップロード" なんてのは序の口で

"アイマスで役を持っている声優が出ているだけでその作品を実質アイマス!など言って騒ぐ" といった、アイマスが全ての前提になっている行動をとって他のコンテンツを軽んじたり

"1クールで終わるアニメと違って、こうやって何年も続くコンテンツで同じ役を持ち続けられるアイマス声優は幸せなんだよ?" とか
"アイマスで役が決まった声優のSNSのアカウントに「アイマスの世界へようこそ!」というメッセージを送りつける" のように、自分の立ち位置がただ金を払うだけの搾取される消費者なのが自覚できず何様なモノの語りをしたり

更にひどいものでは
"ツイキャスでライブのBDを垂れ流す違法行為"や
"イベント会場で鯖を振り回す"、
そんな社会不適合生物のアイマスオタクの中で今最もホットな迷惑行為は「#283プロ事前登録ガシャ」というツイートによるタイムライン汚染

タグミュートをすればタイムラインに現れなくなるためそれで済む話ではあるが、アイマスという名のブランド名がついていればどんなモノでも無条件に受け入れて批判的ユーザーは排除する、カルト宗教アイマス教の信者はブロックするにこしたことはない

そこでTwitterAPIを使って件のタグをストリームして、得たツイートのユーザーをブロックするプログラムをpythonで書いたのでそれを以下に示す(関数やクラスを使ったり、動作毎にファイル分けたり、そんな高度な最適化をする知能はないので雑なプログラムになっています)

# -*- coding: utf-8 -*-
import oauth2 as oauth
import webbrowser as web
from twython import Twython, TwythonStreamer, TwythonError
import sys

#&で文を分割する関数
def parse_qsl(url):
    param = {}
    for i in url.split('&'):
        _p = i.split('=')
        param.update({_p[0]: _p[1]})
    return param

def user_block(twitter, name, screenName, userId):
    try:
        twitter.create_block(screen_name=screenName, user_id=userId)
        print(name,"さんをブロックしました")
    except TwythonError as e:
        print(name,"さんのブロックに失敗しました")
        
#streamerのクラス
class MyStreamer(TwythonStreamer):   
    def on_success(self, data):
        if 'text' in data:
            text = data['text']
            screenName = data['user']['screen_name'] 
            userId     = data['user']['id']          
	    userName   = data['user']['name']                 
            if 'retweeted_status' in data: 
                text = data['retweeted_status']['text']
                screenName = data['retweeted_status']['user']['screen_name']
                userId     = data['retweeted_status']['user']['id']
                userName   = data['retweeted_status']['user']['name']
            user_block(twitter, userName, screenName, userId)

        # Want to disconnect after the first result?
        # self.disconnect()

    def on_error(self, status_code, data):
        print (status_code, data)

request_token_url = 'https://twitter.com/oauth/request_token'
access_token_url = 'https://twitter.com/oauth/access_token'
authenticate_url = 'https://twitter.com/oauth/authenticate'

consumer_key = '自分のAPIキーを入れて'
consumer_secret = '自分のAPIシークレット入れて'

consumer = oauth.Consumer(key=consumer_key, secret=consumer_secret)
client = oauth.Client(consumer)
resp, content = client.request(request_token_url, "GET")
print("■取得したtoken")
print(content)
print("----")

# Tokenを辞書型にセット
strH = content.decode('utf-8')
print(strH)
print("----")
request_token = dict(parse_qsl(strH))

# 認証用ページを開く
url = "https://api.twitter.com/oauth/authorize?oauth_token=" + request_token['oauth_token']
print(url)
#url = "https://api.twitter.com/oauth/authorize?oauth_token=" + d['oauth_token']
web.open(url)

pinNum = input("Please Pin Code: ")

pin = pinNum

otoken = request_token["oauth_token"].encode('utf-8')
otsecret = request_token["oauth_token_secret"].encode('utf-8')

print("■設定するやつ")
print("・oauth token: ")
print(otoken)
print("・oauth token secret: ")
print(otsecret)
print("--")

# Tokenを取得する
token = oauth.Token(otoken, otsecret)
clientOauth = oauth.Client(consumer, token)
nresp, contentOauth = clientOauth.request("https://api.twitter.com/oauth/access_token", "POST", body="oauth_verifier={0}".format(pin))
    
print(contentOauth)
print(nresp)

strX = contentOauth.decode('utf-8')
print("----")
print(strX)
print("----")
oauthToken = dict(parse_qsl(strX))

print(oauthToken)

twitter = Twython(consumer_key, consumer_secret, oauthToken['oauth_token'], oauthToken['oauth_token_secret'])

tweetStream = MyStreamer(consumer_key, consumer_secret, oauthToken['oauth_token'], oauthToken['oauth_token_secret'])
tweetStream.statuses.filter(track="283プロ事前登録ガシャ")

諸注意

Twitter Developerに登録してAPIキーと、APIシークレットを取得してくださいね、ググればすぐにやり方出てきます。電話番号認証したアカウントじゃないと登録できないので注意。

Developerに登録したアカウントだけでプログラム動かすなら、APIキーとか載ってるとこでアクセストークン手に入るのでOauth認証いりません。なのでtwitterオブジェクトの作成までのPIN入力したりの文は全て不要になります、多分。これもググればすぐに出てきます。

twythonとかは入ってなかったらpip installなりでいれてね

参考にしたサイト

pythonでoauth認証

pythonでoauth認証
http://pika-shi.hatenablog.com/entry/20120210/1328866010

urlを分割するparse_qslはここの丸パクリです

Pythonからユーザ認証→ツイートしてみた!Twythonとoauth2ライブラリ

Pythonからユーザ認証→ツイートしてみた!Twythonとoauth2ライブラリ
http://www.lisz-works.com/entry/twython-oauth1-tweet

認証の基本的な流れはここを参考(丸パクリ)しました

あとはStreamの使い方だけ調べれば誰でも作れます、多分

月1アクセスあるかないかのtumblrの記事でしっかりプログラムの解説をするのはアホらしいのでやりませんが、需要があればやります。9割以上参考サイトの切り貼りですけど。