diff options
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/facebook')
3 files changed, 120 insertions, 1 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt index 43bc5724..d98241f1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt @@ -6,8 +6,9 @@ package com.pitchedapps.frost.facebook const val FACEBOOK_COM = "facebook.com" const val HTTPS_FACEBOOK_COM = "https://$FACEBOOK_COM" -const val FB_URL_BASE = "https://m.$FACEBOOK_COM/" +const val FB_URL_BASE = "https://touch.$FACEBOOK_COM/" fun PROFILE_PICTURE_URL(id: Long) = "https://graph.facebook.com/$id/picture?type=large" +const val FB_LOGIN_URL = "${FB_URL_BASE}login" const val USER_AGENT_FULL = "Mozilla/5.0 (Linux; Android 4.4.2; en-us; SAMSUNG SM-G900T Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.6 Chrome/28.0.1500.94 Mobile Safari/537.36" const val USER_AGENT_BASIC = "Mozilla/5.0 (BB10; Kbd) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.1.0.4633 Mobile Safari/537.10+" diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRegex.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRegex.kt new file mode 100644 index 00000000..39e8c467 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRegex.kt @@ -0,0 +1,20 @@ +package com.pitchedapps.frost.facebook + +/** + * Created by Allan Wang on 21/12/17. + * + * Collection of regex matchers + * Input text must be properly unescaped + * + * See [StringEscapeUtils] + */ + +/** + * Matches the fb_dtsg component of a page containing it as a hidden value + */ +val FB_DTSG_MATCHER: Regex by lazy { Regex("name=\"fb_dtsg\" value=\"(.*?)\"") } + +/** + * Matches user id from cookie + */ +val FB_USER_MATCHER: Regex by lazy { Regex("c_user=([0-9]*);") }
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRequest.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRequest.kt new file mode 100644 index 00000000..428043a0 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRequest.kt @@ -0,0 +1,98 @@ +package com.pitchedapps.frost.facebook + +import com.pitchedapps.frost.BuildConfig +import com.pitchedapps.frost.utils.L +import io.reactivex.Single +import io.reactivex.schedulers.Schedulers +import okhttp3.* +import okhttp3.logging.HttpLoggingInterceptor +import org.apache.commons.text.StringEscapeUtils + +/** + * Created by Allan Wang on 21/12/17. + */ +data class RequestAuth(val userId: Long = -1, val cookie: String = "", val fb_dtsg: String = "") + +private val client: OkHttpClient by lazy { + val builder = OkHttpClient.Builder() + if (BuildConfig.DEBUG) + builder.addInterceptor(HttpLoggingInterceptor() + .setLevel(HttpLoggingInterceptor.Level.BASIC)) + builder.build() +} + +private fun List<Pair<String, Any?>>.toForm(): RequestBody { + val builder = FormBody.Builder() + forEach { (key, value) -> + val v = value?.toString() ?: "" + builder.add(key, v) + } + return builder.build() +} + +private fun String.requestBuilder() = Request.Builder() + .header("Cookie", this) + .header("User-Agent", USER_AGENT_BASIC) + .cacheControl(CacheControl.FORCE_NETWORK) + +private fun Request.Builder.call() = client.newCall(build()) + + +fun Pair<Long, String>.getAuth(): RequestAuth? { + val (userId, cookie) = this + val call = cookie.requestBuilder() + .url(FB_URL_BASE) + .get() + .call() + call.execute().body()?.charStream()?.useLines { + it.forEach { + val text = StringEscapeUtils.unescapeEcmaScript(it) + val result = FB_DTSG_MATCHER.find(text) + val fb_dtsg = result?.groupValues?.get(1) + if (fb_dtsg != null) { + L.d(null, "fb_dtsg for $userId: $fb_dtsg") + return RequestAuth(userId, cookie, fb_dtsg) + } + } + } + + return null +} + +fun RequestAuth.markNotificationRead(notifId: Long): Call { + + val body = listOf( + "click_type" to "notification_click", + "id" to notifId, + "target_id" to "null", + "m_sess" to null, + "fb_dtsg" to fb_dtsg, + "__dyn" to null, + "__req" to null, + "__ajax__" to null, + "__user" to userId + ) + + return cookie.requestBuilder() + .url("${FB_URL_BASE}a/jewel_notifications_log.php") + .post(body.toForm()) + .call() +} + +private inline fun <T, reified R : Any, O> zip(data: Array<T>, + crossinline mapper: (List<R>) -> O, + crossinline caller: (T) -> R): Single<O> { + val singles = data.map { Single.fromCallable { caller(it) }.subscribeOn(Schedulers.io()) } + return Single.zip(singles) { + val results = it.mapNotNull { it as? R } + mapper(results) + } +} + +fun RequestAuth.markNotificationsRead(vararg notifId: Long) = zip<Long, Boolean, Int>(notifId.toTypedArray(), + { it.count { it } }) { + val response = markNotificationRead(it).execute() + val buffer = CharArray(20) + response.body()?.charStream()?.read(buffer) ?: return@zip false + !buffer.toString().contains("error") +} |