グラブルのリアルタイムtwitter救援取得アプリを作ってみた3
グラブルのリアルタイムtwitter救援取得アプリを作ってみた2
グラブルのリアルタイムtwitter救援取得アプリを作ってみた1
gbf-raiderが閉鎖したので作ってみました。
※面倒なので携帯で見るの考慮してません。
dockerとかreactの練習と、nodejsのsocket使ってます。
下記URL
テストなので四大天司のみです。
過去の物は出てきません、あくまでリアルタイムで取得します。
最初は何もないですが、とりあえず待ってればいつか救援出てきます。
今後の予定(gbf-raiderにあった機能)
・デスクトップ通知したい
・音声で伝えたい
・見たい救援IDの選択
・もうちょっとグラブル感出したい
※サーバーのスペックがごみなので、負荷かけると多分すぐ落ちます。落ちたら増強考えます。(むしろ面白そうなので常識的な範囲で負荷かけて下さい)
docker(docker-compose)で10分でnginx+mysql+php-fpmの環境を作る
docker-composeを使ったら非常に簡単にWEBの環境が作れました
ホスト環境
centos7
docker-compose、dcokerインストール
yum update #dockerインストール curl -sSL https://get.docker.com/ | sh #docker-composeインストール curl -L https://github.com/docker/compose/releases/download/1.11.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose
ファイル構造(最終的にdata/php/publicをweb上に公開します)
# tree . ├── containers │ ├── mysql │ │ └── Dockerfile │ ├── nginx │ │ ├── conf │ │ │ └── default.conf │ │ └── Dockerfile │ └── php-fpm │ ├── conf │ │ └── php.ini │ └── Dockerfile │ ├── data │ ├── mariadb │ └── php │ └── public └── docker-compose.yml
各ファイル
docker-compose.yml
#docker-compose.yml version: "2" services: php: build: ./containers/php-fpm volumes: - "./data/php:/var/www/html" environment: DB_HOST: db DB_PORT: 5432 nginx: build: ./containers/nginx ports: - 80:80 - 443:443 links: - php mysql: build: ./containers/mysql volumes: - "./data/mariadb:/var/lib/mysql" environment: MYSQL_ROOT_PASSWORD: root MYSQL_PASSWORD: ryouzi01 MYSQL_USER: abot MYSQL_DB: abot expose: - "3306"
php Dockerfile
FROM php:fpm WORKDIR /var/www/html RUN apt-get update && docker-php-ext-install mbstring mysqli pdo_mysql ADD conf/php.ini /usr/local/etc/php/conf.d
nginx Dockerfile
FROM nginx:1.9.12 ADD conf/default.conf /etc/nginx/conf.d
mysql Dockerfile
FROM mariadb:10.1.18 VOLUME /etc/localtime:/etc/localtime:ro RUN sed -i -e "s/^#bind-addresss*=s*0.0.0.0/bind-address=0.0.0.0/" /etc/mysql/my.cnf
nginx.conf
server { listen 80 default; server_name localhost; charset utf-8; root /var/www/html; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; #rewrite ^(.+)/$ $1; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { root /var/www/html/public; fastcgi_pass php:9000; fastcgi_index index.php; #fastcgi_split_path_info ^(.+\.php)(.+)$; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
php.conf
#以下php.iniで使うものを設定 date.timezone = Asia/Tokyo
上記のファイルが全部そろったらdockerコンテナ起動
#docker-compose.ymlがある階層で docker-compose build docker-compose up -d docker-compose stop
以上、非常に簡単に環境が作れて、かつ再構築も簡単
グラブルのwiki救援を自動取得するアプリを作った(四大天司対応)
ーーーーーーーーーーーーーーーーーーーーーーーーーー
2017/06/05
機能はこっちに引き継ぎました。
ーーーーーーーーーーーーーーーーーーーーーーーーーー
以下概要
上記の様な簡単アプリで自動で裏でwikiの内容を取得してるだけ。
様はヤフーのtwitterリアルタイム検索のwiki版で、自動的にクリップボードに救援IDを設定する。
リアルタイム検索はtwitterAPI使ってると思うけどこっちは力技です。
WebBrowserコントロールを使っています。
仕組みは以下の通り
グラブルのマルチ(グランデ、よわバハ)の救援ページ
通常マルチバトル救援募集板 - グランブルーファンタジー(グラブル)攻略wiki
・ページの仕様
1.上記の内容は全部inputタグで囲まれている
2.救援のINPUTタグにはclass="pcmt"が設定されている(救援以外もあるが)
3.各救援は昇順
・アプリ仕様
1.HtmlElementCollectionにINPUTタグを検索して格納、
そのINPUTのclassタグにpcmtが設定されていればそれを救援と認識
それを行ごとに画面出力
2.最新の行から\s[0-9a-z]{8}で救援IDを取得してクリップボードに張り付ける。(簡単に貼り付けで救援に入れるように)
3.取得間隔は10秒
4.新しい救援取得時にWindowsのNotifyIconを使いタスクトレイに通知
・問題
1.サイトのURLが変更されたらいちいち更新が面倒
2.サイトの表示が変わったらおしまい
3.使う人の書き方が変わったらおしまい
・利点
これを使ってからグランデに入れなかったことありません(キリッ
・・・としょうもないものを作ってしまった。
iphoneでの自動取得のほうが結構需要ありそうだけど、都度のクロールのせいで通信食いそう。
直接アプリに救援ID張り付けようと思ったけど、さすがにアプリに介入するといろいろまずそうなのでここまで。ほしい人いるだろうか・・・。
・追記
dropboxに上げてみました。
取得間隔は10秒固定です。(wikiに負荷をかけてほしくないため)
グランデ、よわバハのページだけです。要望があれば作るかもしれませんが・・・
使用は自己責任でお願いします。いかなる不利益を被っても責任は負いかねます。
サイトの構造が変わったりしてidが取得できなくなった場合、連絡いただければ調べる
(かも)しれませんが期待しないでください。
Dropbox - グラブルwiki救援自動クリップボード貼り付けアプリ.zip
・追記2 09/14 12:02
1.正規表現でのID抽出部分を変更(全角でID書く人とか居たのと、先頭にID書いた場合に抽出できなかったので)
2.初回起動時、IDが見つからない場合は通知しないように修正
・追記3 09/21 18:57
特定環境において起動時にエラーが出ていたので、dllも付属するように変更。
どのようなDLLかは↓の方の記事を参照
mshtml.dllの配布に関するメモ - wave1008の日記
追記4 2017/03/18
なんかアクセスめっちゃ増えていて、また開発環境も手元に戻ってきたので改良してます。(自分も使いたいので)
多分四大天司の影響だと思いますが・・・。
ちなみに引退してましたがバハ引いたので復帰しました。( ・´ー・`)
こんな感じになると思います。
・抽出ワードは「ウリエル」と入れた場合にサイトに下記の行があったとき「ウリエル」の行だけ認識するものです。(表示自体は全行出します)
ウリエル 416500C6
ラファエル e6c1c3d0
・保存は次回起動時に覚えておくため用です。radioボタンを変えてもすぐに反映されます。
※通常マルチも要望がありましたがあまり更新されないので、twitterで検索したほうがいいと思います。
また、コメントにスマホのアプリとありましたがそれ用に作っていないので多分無理だと思います。最近twitterでどなたかが作っているアプリ見かけた気がするけど・・・。
※追記5 2017/03/19 四大天司対応させました。
https://www.axfc.net/u/3788133
パスワードは wiki です。
ソース残ってなかったから新規に作り直したので、不具合出たらご連絡ください。
意見もお待ちしております。
グラブルのフレンドも募集中です ID:6372142
..etc
いまの10秒更新だとほぼ無理ですね・・・。
3秒更新だとID入力した後の表示人数1/30人で入ることが3連続で出来たので、落ち着いたら配布します。
6人HELLは上手くいってないです。
意外に簡単?swiftで動的にカメラ内の顔を検出して処理する(CIDetector)
最近流行りのVRもいいけど、現実が全部嫁になればいいのにと日々思っているこの頃
そんな幻想を抱きながら顔を置き換える処理を、Swiftで簡単に顔検出が出来るみたいなのでやってみました
class TestViewController: UIViewController,UIGestureRecognizerDelegate,AVCaptureVideoDataOutputSampleBufferDelegate { let captureSession = AVCaptureSession() let videoDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo) let audioDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio) //let fileOutput = AVCaptureMovieFileOutput() var videoOutput = AVCaptureVideoDataOutput() var hideView = UIView() override func viewDidLoad() { super.viewDidLoad() //各デバイスの登録(audioは実際いらない) do { let videoInput = try AVCaptureDeviceInput(device: self.videoDevice) as AVCaptureDeviceInput self.captureSession.addInput(videoInput) } catch let error as NSError { print(error) } do { let audioInput = try AVCaptureDeviceInput(device: self.audioDevice) as AVCaptureInput self.captureSession.addInput(audioInput) } catch let error as NSError { print(error) } self.videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey : Int(kCVPixelFormatType_32BGRA)] //フレーム毎に呼び出すデリゲート登録 let queue:dispatch_queue_t = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL); self.videoOutput.setSampleBufferDelegate(self, queue: queue) self.videoOutput.alwaysDiscardsLateVideoFrames = true self.captureSession.addOutput(self.videoOutput) let videoLayer : AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession) videoLayer.frame = self.view.bounds videoLayer.videoGravity = AVLayerVideoGravityResizeAspectFill self.view.layer.addSublayer(videoLayer) //カメラ向き for connection in self.videoOutput.connections { if let conn = connection as? AVCaptureConnection { if conn.supportsVideoOrientation { conn.videoOrientation = AVCaptureVideoOrientation.Portrait } } } hideView = UIView(frame: self.view.bounds) self.view.addSubview(hideView) self.captureSession.startRunning() } func imageFromSampleBuffer(sampleBuffer: CMSampleBufferRef) -> UIImage { //バッファーをUIImageに変換 let imageBuffer: CVImageBufferRef = CMSampleBufferGetImageBuffer(sampleBuffer)! CVPixelBufferLockBaseAddress(imageBuffer, 0) let baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0) let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer) let width = CVPixelBufferGetWidth(imageBuffer) let height = CVPixelBufferGetHeight(imageBuffer) let colorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = (CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue) let context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo) let imageRef = CGBitmapContextCreateImage(context) CVPixelBufferUnlockBaseAddress(imageBuffer, 0) let resultImage: UIImage = UIImage(CGImage: imageRef!) return resultImage } func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) { //同期処理(非同期処理ではキューが溜まりすぎて画像がついていかない) dispatch_sync(dispatch_get_main_queue(), { //バッファーをUIImageに変換 var image = self.imageFromSampleBuffer(sampleBuffer) let ciimage:CIImage! = CIImage(image: image) //CIDetectorAccuracyHighだと高精度(使った感じは遠距離による判定の精度)だが処理が遅くなる var detector : CIDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options:[CIDetectorAccuracy: CIDetectorAccuracyLow] ) var faces : NSArray = detector.featuresInImage(ciimage) // 検出された顔データを処理 for subview:UIView in self.hideView.subviews { subview.removeFromSuperview() } var feature : CIFaceFeature = CIFaceFeature() for feature in faces { // 座標変換 var faceRect : CGRect = feature.bounds var widthPer = (self.view.bounds.width/image.size.width) var heightPer = (self.view.bounds.height/image.size.height) // UIKitは左上に原点があるが、CoreImageは左下に原点があるので揃える faceRect.origin.y = image.size.height - faceRect.origin.y - faceRect.size.height //倍率変換 faceRect.origin.x = faceRect.origin.x * widthPer faceRect.origin.y = faceRect.origin.y * heightPer faceRect.size.width = faceRect.size.width * widthPer faceRect.size.height = faceRect.size.height * heightPer // 顔を隠す画像を表示 let hideImage = UIImageView(image:UIImage(named:"trump.jpg")) hideImage.frame = faceRect self.hideView.addSubview(hideImage) } }) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
左が参考オバマ、右が処理後
感想
・今の状態だとiphone越しなのでVRみたいにステレオマッチングしたら面白そう、ゴーグルかけて隠す画像も3Dモデルにして、顔の向きに応じて3Dモデルも回転したら・・・
・実際上書きしたところを想像すると、もしストローとかポッキーを相手が咥えていたら刺さりそう
・正面画像の検出精度はLowでもなかなかだが、横顔はかなり厳しい印象
・細かい精度を求めないのであれば、かなり簡単なので結構応用出来そう
・CIDetectorではなくOpenCVだと顔以外も検出できるらしい
参考
swiftでAVCaptureVideoDataOutputを使ったカメラのテンプレート - Qiita
twilioを使って友達がいるアピールをしよう!
twilioについては↓
Twilio for KDDI Web Communications | クラウド電話API
これを使い自分に電話を掛け、あたかも友達から電話が掛ってきたように見せかける。
まず契約だが、twilioには1番号につき月108円(友達料)が必要だ。
缶ジュース1本で友達が出来るなら安いだろう。
取得した番号を携帯の連絡先に登録することも忘れるなよ!
番号を取得した後 AccountSid と AuthToken が貰えるので控えておこう。
今回はPHPを利用する。
1.自分のサーバー上に↓のライブラリを入れる。(サーバーない人はAWSやらHerokuやらgoogle cloud platformやらで作っておこう)
2.ライブラリを呼び出し電話をかけるphpを書く
<?
require('/path/to/twilio-php/Services/Twilio.php');
$account_sid = '[取得したAccountSid]';
$auth_token = '[取得したAuthToken]';
$client = new Services_Twilio($account_sid, $auth_token);
'FallbackMethod' => 'GET',
'StatusCallbackMethod' => 'GET',
'Record' => 'false'
?>
3.xmlファイルを作りに電話でなにをするか書く。
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say language="ja-jp">はじめまして</Say>
</Response>
↑の例では機械音声を流しているだけだが、女の子が入っている音声ファイルを流せば彼女と会話しているようにも出来るぞ!
4.自分で2のphpにアクセスする、またはクーロンとかで自動で指定時間にphpを実行するようにする。
※自分でアクセスする場合はセキュリティに気をつけろ!不正利用されて高額請求が来ても知りません。
5.自分の携帯に電話がかかってくるので、友達(録音)と話すだけ!
これで友達がいるアピールがいつでもできる!やったね!
・・・。