mozyのかきおき

mozyの読書感想文や思考置き場

monappy事件についての雑記【後日編】

monappy事件ですが、犯人が見つかって書類送検されたようですね。

www.jiji.com

www.itmedia.co.jp

私たちは、当時リアルタイムで追跡を行っていたのでした

mozy-ok.hatenablog.com

mozy-ok.hatenablog.com

感想

結局わかったことは、犯人は必ずどこかに痕跡を残す。
それをわかりやすく残す人は捕まる。残さない人は捕まえることができない。ということなのだと思います。
また、追い始めるのは早ければ早いほど良い。人は多ければ多いほどいい。

そして、高い技術を持った彼を思うと、私はなんだか釈然としない気持ちになりました。
その時の気持ちを想像すると犯罪を犯してしまうのもわかる気もするからです。目の前に面白いものがあったら解きたくなってしまう気持ちが。
そんな時に、自分の立ち位置を気づきさえしていれば、真っ当に社会を変えられたでしょうし、それができるほどの技術を持っていたと思います。
彼は今、後悔しているかもしれません。犯罪者になってしまった自分を。
今後、反省をして戻ってきたその時に、
あなたの持っている素晴らしい技術を、どうか社会のために、世界のために使ってください。君にはそれができるのだから。
追跡を行っていた、とある無名のエンジニアからのお願いです。

最後に僕が尊敬しているプログラマであり、数学ガールの作者であり、クリスチャンである結城先生のツイートを引用します。

最後に

普段、我々エンジニアが生きている一風変わった業界には、若い年齢の方を含めて高い技術力を持った素晴らしいエンジニア達がいます。
そして、その素晴らしい技術を持ったエンジニアには、その技術を自分の為に使うのか、それとも社会の為に使うのか。そんな難しい判断を常に求められています。
承認欲求や、自己顕示欲に駆られる時もあるでしょう。そんなエンジニアがあなたの隣にいるかもしれません。
そんな業界で、あなたはどの様に振舞って、誰とどのようにこの先生きのこっていくのか。それがあなたにも求められています。

Swift で動画再生機能を最速で作る【約5分】

経緯

最速で実装チャレンジとしてやったことを書いていく。

目次

まず、先に手順を書いておく。

  1. Xcodeでプロジェクトを作成する
  2. mp4の動画をプロジェクトにコピーする
  3. storyboardで画面レイアウトを作る
  4. storyboardとViewControllerをつなぐ
  5. コードを書いていく
  6. 完成

やっていく

1. Xcodeでプロジェクトを作成する

言語はSwiftを選ぶのを忘れずに。

f:id:mozy_ok:20190309231339p:plain
今回はSingle View Appを選択

2. mp4の動画をプロジェクトにコピーする

iPhone でとったMOVなどは、ffmpegなどを使って変換してから追加すると良いかも。こんな感じに
ffmpeg -i 画面収録\ 2019-03-08\ 20.22.04.mov -pix_fmt yuv420p video.mp4

f:id:mozy_ok:20190309231528p:plain
ここのリソースに追加している

3. storyboardで画面レイアウトを作る

constractionsあたりの設定で中央に表示されるようにレイアウトも固定しておく。

f:id:mozy_ok:20190309231915p:plain
シンプルにボタンのみ

4. storyboardとViewControllerをつなぐ

右上にある丸が二つ重なっているアイコンを押してstoryboardとViewControllerを表示する。
んでそののちに、storyboardでボタンを選択して、controlキーを押しながらViewControllerに引っ張っていき適当な名前を付ける。 ここでconnectionAction にするのをお忘れなく。

5. コードを書いていく

ボタンアクションをガリガリかいていく。
コードの意味はシンプルなので読み解けると思う。

まずは import AVKit する。
あとはガリガリ

@IBAction func buttonAction(_ sender: Any)
    {
        if let path = Bundle.main.path(forResource: "video", ofType: "mp4")
        {
            let video = AVPlayer(url: URL(fileURLWithPath: path))
            let videoPlayer = AVPlayerViewController()
            videoPlayer.player = video
            
            present(videoPlayer, animated: true, completion:
            {
                video.play()
            })
        }
    }

6. 完成

ちなみに7分かかった笑
f:id:mozy_ok:20190309233221p:plain

シンプルな画面
f:id:mozy_ok:20190309233202p:plain
再生画面(標準で色々ついてる)

MySQL5.7 以降なら ElasticSearch 使わなくても いい感じに日本語全文検索できるよって話

経緯

とあるシステムでフォームに入力された文字を元に駅名テーブルから駅名を推薦したいという需要があった。そこで日本語の全文一致検索について調べた時のメモ

概要

もともと、MySQLには、LIKE検索というものがあって、文字を比較して検索をかけることができた。しかし、精度が低いしなんといっても遅い。
そこで、色々調べていたところ、MySQL5.7 以降ならinnodbの日本語全文検索がいい感じに使えるらしい。
そのため試しに使ってみた結果、速いし精度高いし、手軽だし幸せになれた。

やっていく

具体的には FULLTEXT KEY(title) WITH PARSER NGRAM みたいな感じにして検索するだけ。

以下の参考サイトの解説が最強なので読むべし。

原理の説明

MySQLのフルテキストインデックスを利用した全文検索は、以下のような原理で動きます。

INSERT/UPDATE時は対象カラムの内容をトークナイズしフルテキストインデックスに格納 全文検索時はフルテキストインデックス内から検索文字列をトークナイズし完全一致で検索 だが、MySQL5.6以前では日本語のトークナイズが空白文字やカンマなどでの区分けのみの状態でした。 つまり、日本語の場合は文字列ほぼそのまんまでインデックスに格納されてしまっていたのです。 そのインデックスに対して完全一致でしか検索しないので、文章の一部を含む文字列で全文検索をかけても、完全一致しないからヒットしないという状況でした。

MySQL5.7ではここが変わり、ngram(n文字で区分け)やmecab(辞書を利用する)によるトークナイズが利用可能になり、使い物になるレベルでの日本語全文検索が利用可能になったのです!

参考サイトより引用

参考サイト

LIKE検索より50倍速い!?MySQLでラクラク高速な日本語全文検索 - bitA Tech Blog

React Native + expo + FirebaseでFBログインを実装してみる

経緯

最近React Native + expo を触っていて、手習いとしてFirebase + FBログインを実装したのでメモ
本当はAuth0を使おうと思っていたけど時間がなかったのでFirebaseで。

やっていく

基本的には、以下の通りに進めていく。今回はiOSを試してみる。
Facebook - Expo Documentation

まずはFacebook Developer ページからアプリケーションを作成して、Facebookログイン プロダクトを追加する。 その後、プラットフォームを追加 > iOS を選択して、「バンドルID」にhost.exp.Exponent を指定する。
こんな感じ

f:id:mozy_ok:20190223184713p:plain
設定画面

そして、そのうち使うので、アプリIDと、シークレットキーをメモっておこう。

f:id:mozy_ok:20190223185251p:plain
FBの設定

次にFirebase Authentication を設定する。
Firebaseに登録をして、FBログインを有効にする。

f:id:mozy_ok:20190223185119p:plain
スクショとったときにはもう有効になってしまっていた 笑

そして、ここに先ほどメモったIDとシークレットキーを入れる。

f:id:mozy_ok:20190223185200p:plain
設定内容

最後に、ガンガンReact Nativeのコードをかいていく。
まずは expo init して雛形を作成して、App.jsを以下のように変えていく。
npm install firebase なども忘れずに。
apiKey などの config部分は、Firebaseのプロジェクト概要部分から、Webを選んで中身を置き換えてくださいな。

f:id:mozy_ok:20190223185555p:plain
プロジェクト設定部分

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

import * as firebase from 'firebase';

var config = {
  apiKey: "APIKEY",
  authDomain: "HOGE.firebaseapp.com",
  databaseURL: "HUGA.firebaseio.com",
  projectId: "IDID",
  storageBucket: "PIYO.appspot.com",
  messagingSenderId: "NUMBER"
};
firebase.initializeApp(config);

import { Container, Content, Header, Form, Input, Item, Button, Label } from 'native-base'; 

export default class App extends React.Component {
  constructor(props){
    super(props)

    this.state = ({
      email: '',
      password: ''
    })
  }

  signUpUser = ( email, password ) => {

    try {
      if(this.state.password.length<6)
      {
        alert('パスワードが短いよー 6文字以上にしてくれー')
        return;
      }

      firebase.auth().createUserAndRetrieveDataWithEmailAndPassword(email, password)

    } catch (error) {
      console.log(error.toString());
    }

  }

  loginUser = ( email, password ) => {

    try {
      firebase.auth().signInWithEmailAndPassword(email, password).then(function (user) {
        console.log(user); 
      })
    } catch (error) {
       console.log(error.toString());
    }

  }

  async loginWithFacebook() {

    const { type, token } = await Expo.Facebook.logInWithReadPermissionsAsync('632193713899286', { permissions: ['public_profile'] })

    if (type == 'success') {
      const credential = firebase.auth.FacebookAuthProvider.credential(token)

      firebase.auth().signInWithCredential(credential).catch((error) => {
        console.log(error)
      })
    }
  }

  render() {
    return (
      <Container style={styles.container}>
        <Form>
          <Item>
            <Label>Email</Label>
            <Input
              autoCorrect={false}
              autoCapitalize="none"
              onChangeText={(email) => this.setState({ email })} 
            />
          </Item>

          <Item>
            <Label>Password</Label>
            <Input
              secureTextEntry={true}
              autoCorrect={false}
              autoCapitalize="none"
              onChangeText={(password) => this.setState({password})}
            />
          </Item>

          <Button style={{ marginTop: 10 }}
            full
            rounded
            success
            onPress={() => this.loginUser(this.state.email, this.state.password)}
          >
            <Text style={{ color: 'white' }}>Login</Text>
          </Button>

          <Button style={{ marginTop: 10 }}
            full
            rounded
            primary
            onPress={() => this.signUpUser(this.state.email, this.state.password)}
          >
            <Text style={{ color: 'white' }}>Sign Up</Text>
          </Button>

          <Button style={{ marginTop: 10 }}
            full
            rounded
            primary
            onPress={() => this.loginWithFacebook()}
          >
            <Text style={{ color: 'white' }}>Login with Facebook</Text>
          </Button>

        </Form>
      </Container>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    justifyContent: 'center',
    padding: 10
  },
});

結果

動いたぜ。いえーい

f:id:mozy_ok:20190223190026p:plain
動いた

GitHub でWIPな意味でのPRが作れるようになったよ

マジで最高です。

詳細はこちら

Introducing draft pull requests - The GitHub Blog

重要な部分を抜き出すと

Draft pull requests cannot be merged, and code owners are not automatically requested to review draft pull requests

要約すると

Draft PRはマージされないし、コードのオーナーに自動でレビューお願いしまっすーって通知も飛ばないよー ってことかな。

数百とか開いているSafariのタブ情報をエクスポートする方法【実際にやってみた】

経緯

つまりはこういうことだ。
開きすぎる癖があって、これをどうにかしてCSVなりに書き出したかった。
あなたもこういう癖あったりしませんか?

f:id:mozy_ok:20190126131319j:plain
つまりはこういうことだ

iPhoneのストレージも逼迫するし。

探していたところ

かみさーまー みたいな記事を発見した。BOOOM!!!

Ok!!! This has been SOLVED. For those who were wondering...

The post request to https://p58-ckdatabase.icloud.com/api/client/record/sync contained gzip-encoded information which made reference to SafariBookmarksSyncAgent.

SafariBookmarksSyncAgent is located at /System/Library/CoreServices/SafariSupport.bundle/Contents/MacOS/SafariBookmarksSyncAgent.

Ran strings on the binary and saw it made reference to: (allow file* (home-literal "/Library/Safari/CloudTabs.db"))

/Library/Safari/CloudTabs.db does not exist, but ~/Library/Safari/CloudTabs.db does!

CloudTabs.db is a SQLite3 database with table cloud_tabs and cloud_tab_devices.

select * from cloud_tab_devices gives the device_uuid in column 1.

select url from cloud_tabs where device_uuid='';

BOOM!

Reddit - mac - iPhone Safari Tabs into Text List via iCloud?

実際にやってみた

  1. まずは、iPhone 設定 > iCloud から Safariの設定をOnにする

  2. ターミナルでSafariのブックマークの同期をするエージェントを動かす。
    /System/Library/CoreServices/SafariSupport.bundle/Contents/MacOS/SafariBookmarksSyncAgent

    f:id:mozy_ok:20190202162136p:plain
    手動で同期する

  3. SQLite なデータベース ~/Library/Safari/CloudTabs.dbを見つけて開く 

    f:id:mozy_ok:20190202162234p:plain
    このDBの中にタブ情報が入っている

  4. データベースの中身はこんな感じ

    f:id:mozy_ok:20190202162348p:plain
    テーブル構成

    f:id:mozy_ok:20190202162448p:plain
    iPhoneもきちんと認識されているぞ!!

  5. cloud_tabs テーブルの中身に目的のiPhoneで開いている数百のタブのリストがある!

    f:id:mozy_ok:20190202162732p:plain
    きたああああああーーーーー

  6. クエリを叩く select title, url from cloud_tabs

    f:id:mozy_ok:20190202162933p:plain
    実行した結果

  7. CSVに書き出して完了!!

    f:id:mozy_ok:20190202163136p:plain
    CSVが手に入った!

まとめ

意外と簡単にiPhoneで開いている数百のタブの情報がCSVでエクスポートできた!!
これでiPhoneのストレージの容量が減らせるぜ

数百とか開いているSafariのタブ情報をエクスポートする

経緯

つまりはこういうことだ
開きすぎる癖があって、これをどうにかしてCSVなりに書き出したかった

iPhoneのストレージも逼迫するし。

探していたところ

かみさーまー みたいな記事を発見した。BOOOM!!!

Ok!!! This has been SOLVED. For those who were wondering...

The post request to https://p58-ckdatabase.icloud.com/api/client/record/sync contained gzip-encoded information which made reference to SafariBookmarksSyncAgent.

SafariBookmarksSyncAgent is located at /System/Library/CoreServices/SafariSupport.bundle/Contents/MacOS/SafariBookmarksSyncAgent.

Ran strings on the binary and saw it made reference to: (allow file* (home-literal "/Library/Safari/CloudTabs.db"))

/Library/Safari/CloudTabs.db does not exist, but ~/Library/Safari/CloudTabs.db does!

CloudTabs.db is a SQLite3 database with table cloud_tabs and cloud_tab_devices.

select * from cloud_tab_devices gives the device_uuid in column 1.

select url from cloud_tabs where device_uuid='';

BOOM!

Reddit - mac - iPhone Safari Tabs into Text List via iCloud?

やってみた

今出先でPCがないため、家に戻ったら速攻やるぞ。