経緯
2年ぶりにWindows機を引っ張り出してきたのだが、不覚にもログインパスワードを忘れてしまっている。ありうるパターンを30ぐらい試してみたが一向にログインできる気配がない。途方にくれていたところ、大昔に自分のPCのパスワードをLINEを使って友人に送っていたことを思い出した。しめた、と思いLINEを開いてみたところ、件の送信記録は見つからない。これは、iPhoneを5から7に変更するときにとっておいたLine.sqlite
のバックアップに残っているハズだ!
ということでLine.sqlite
からLINEのメッセージログをサルベージする運びとなった。
生でSQLを書くよりかは、コードにした方が柔軟性があるだろうと思い、最近ご無沙汰になっているTypeScriptでサルベージスクリプトを書くことにした。
利用ライブラリ
TypeORMというORマッパーを利用してsqliteを掘る。型があって便利。
(意訳) 他のしょぼいライブラリと違ってActive RecordパターンとData Mapperパターンを両方利用可能。だから、しゅごいクオリティの高い、疎結合で、スケールしまくる、メンテも余裕でできちゃう、アプリケーションが書けてやばたにえん。
データ構造
今回、全てのデータにアクセスする必要はないので、必要なところのみをエンティティに記述する。こんな感じ。ZMESSAGE
とかが実際のテーブル名 / カラム名。よくあるリレーションを張る。
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from "typeorm"
import { User } from "./user"
@Entity("ZMESSAGE")
export class Message {
@PrimaryGeneratedColumn({ name: "Z_PK" })
id!: number
@ManyToOne(type => User)
@JoinColumn({ name: "ZSENDER", referencedColumnName: "id" })
sender?: User
@Column({ name: "ZTEXT" })
text?: string
}
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"
import { Message } from "./message"
@Entity("ZUSER")
export class User {
@PrimaryGeneratedColumn({ name: "Z_PK" })
id!: number
@Column({ name: "ZNAME" })
name?: string
@OneToMany(type => Message, message => message.sender)
messages?: Message[]
}
処理
データ構造をうつしたエンティティを書いたらこんな感じで利用する。
import { ConnectionOptions, createConnection } from "typeorm"
import { root } from "./paths"
import { User } from "./entity/user"
import { Message } from "./entity/message"
const options: ConnectionOptions = {
type: "sqlite",
database: `${root}/data/line.sqlite`,
entities: [ User, Message ],
logging: true
}
async function main () {
const connection = await createConnection(options)
const messageRepository = connection.getRepository(Message)
const allMessages = await messageRepository.find()
// メッセージを検索する
allMessages.forEach((message: Message) => {
const text = message.text
if (!text) {
return
}
if (text.match(/ape/)) {
console.log(text)
}
})
// メッセージと送信者を紐付ける
const messages = await messageRepository
.createQueryBuilder("ZMESSAGE")
.leftJoinAndSelect("ZMESSAGE.sender", "ZUSER")
.where("ZMESSAGE.Z_PK=:id")
.setParameter("id", 3)
.getMany()
console.log(messages[0].sender)
}
main().catch(console.error)
おわり(^ω^ )
無事パスワードをサルベージして、ログインすることができました。ついでに、この記事を書いてある間に、RAIDのリビルドも終わって最高マジ卍