aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/facebook
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/facebook')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRegex.kt20
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRequest.kt98
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")
+}