最近、数年間の QQ グループが規制されました。大学からこのグループに参加しており、このグループには少し感情があります。普段は暇なときにグループで少し話すことがあり、少し残念に感じています。
QQ アプリ自体から始めることができるかどうか考えました。OCR を使用して、グループの友達リストから友達の QQ 番号を取得し、[email protected]の形式でメールを送信して友達を新しいグループに案内することができるかどうかです。
しかし、私はこの方法を操作することができません。
当時、封鎖された QQ グループの封鎖ダイアログの退出グループチャットボタンをクリックしましたが、そのグループは QQ グループリストに存在しなくなり、UI を操作することができませんでした。したがって、その QQ グループにログインしたことがあるデバイスを見つけ、封鎖ダイアログの退出グループチャットボタンをクリックしていないデバイスを見つける必要があります。残念ながら、私は持っていません...
OCR は機能しませんので、ローカルデータベースから始めるしかありませんでした。インターネットで検索したところ、想像以上に難しいことがわかりました。
まず、QQ アプリのローカル db ファイルは暗号化されていますが、吾爱破解で投稿を見つけることができました。しかし、操作が非常に難しかったため、あきらめました。
幸いなことに、友達の提案で、新しいバージョンの Electron QQ はデータを同期した後、グループが封鎖されたときの初期状態に戻り、そのグループを右クリックしてチャットウィンドウを開くことができます。
そうですね、Electron を使用しているので、チャットウィンドウの devtools をデバッガーとして開けば、友達リストの DOM を取得できるのではないでしょうか?
アイデアができたので、操作を始めます:
-
最新の Electron 版 QQ をダウンロードします。
-
debugtronというツールを使用して QQ を起動します。
-
QQ にログインし、グループリストでそのグループを見つけ、右クリックして個別のチャットウィンドウを開きます:
-
debugtron ツールのセッション画面で、先ほど開いたページのアドレスを見つけ、respect ボタンをクリックすると、おなじみの devtools パネルが表示されます。
- devtools があれば、JavaScript を使用して友達リストを操作できます。以下はコードです:
void (async () => {
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
/**
* フレームタイマー
* @param {Funct} func [コールバック関数]
* @param {Number} timeout [タイムアウト時間]
* @return {Promise}
*/
const asyncLoopTimer = (func, timeout = Infinity) => {
const startTime = performance.now()
return new Promise(resolve => {
const timer = async nowTime => {
cancelAnimationFrame(requestID)
const data = await func()
if (data || nowTime - startTime > timeout) {
resolve(data)
} else {
requestID = requestAnimationFrame(timer)
}
}
let requestID = requestAnimationFrame(timer)
})
}
/**
* 非同期CSSセレクタ
* @param {String} selector [CSSセレクタ]
* @param {Number} timeout [タイムアウト時間]
* @return {Promise} [Target]
*/
const asyncQuerySelector = (selector, timeout) => {
return asyncLoopTimer(() => {
return document.querySelector(selector)
}, timeout)
}
/**
* 文字列テンプレートで要素を作成
* @param {String} template [要素テンプレート]
* @return {Element} 要素オブジェクト
*/
const createElement = template => {
return new Range().createContextualFragment(template).firstElementChild
}
/** ダウンロード */
const download = (data, name, options) => {
const href = URL.createObjectURL(new Blob(data), options)
const a = createElement(`<a href="${href}" download="${name}"></a>`)
a.click()
}
const LIST_REF_CLASS = '.viewport-list__inner' // 友達リストのDOM
const USER_CARD_REF_CLASS = '.buddy-profile' // 友達の情報カード
const USER_NAME_REF_CLASS = '.buddy-profile__header-name' // 友達の名前
const USER_QQ_REF_CLASS = '.buddy-profile__header-uid' // 友達のQQ
const autopilot = (delay = 300) => {
let userRef = document.querySelector(LIST_REF_CLASS).firstElementChild
const userList = []
return async () => {
userRef.scrollIntoView()
userRef.firstElementChild.click()
const cardRef = await asyncQuerySelector(USER_CARD_REF_CLASS, 1000)
await sleep(delay)
userList.push({
name: cardRef.querySelector(USER_NAME_REF_CLASS)?.textContent,
qq: cardRef.querySelector(USER_QQ_REF_CLASS)?.textContent?.split(' ')[1]
})
document.body.click()
userRef = userRef.nextElementSibling
console.log('----userList----', userList)
return userRef ? false : userList
}
}
const userList = await asyncLoopTimer(autopilot(100))
download([JSON.stringify(userList)], 'users.json', { type: 'application/json' })
})().catch(error => {
console.error(error)
})
上記のコードの大まかな流れは、友達リストをスクロールして、順番に情報カードを開いて友達の情報を記録し、最後に JSON ファイルとしてダウンロードすることです。
この記事とタイトルは少し違いますが、本当の「回復」ではありません。もしもある日、あなたのグループが突然規制された場合、これは実行可能な解決策の一つであり、あなたの損失を少しでも取り戻すのに役立つことを願っています。