aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/facebook/requests')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt117
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt124
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt192
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Messages.kt48
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Notifications.kt38
5 files changed, 0 insertions, 519 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt
index b948506f..7900534c 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt
@@ -17,58 +17,11 @@
package com.pitchedapps.frost.facebook.requests
import com.pitchedapps.frost.BuildConfig
-import com.pitchedapps.frost.facebook.FB_DTSG_MATCHER
-import com.pitchedapps.frost.facebook.FB_JSON_URL_MATCHER
-import com.pitchedapps.frost.facebook.FB_REV_MATCHER
-import com.pitchedapps.frost.facebook.FB_URL_BASE
-import com.pitchedapps.frost.facebook.FB_USER_MATCHER
import com.pitchedapps.frost.facebook.USER_AGENT
-import com.pitchedapps.frost.facebook.get
-import com.pitchedapps.frost.kotlin.Flyweight
-import com.pitchedapps.frost.utils.L
-import kotlinx.coroutines.GlobalScope
import okhttp3.Call
-import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.logging.HttpLoggingInterceptor
-import org.apache.commons.text.StringEscapeUtils
-
-/**
- * Created by Allan Wang on 21/12/17.
- */
-val fbAuth = Flyweight<String, RequestAuth>(GlobalScope, 3600000 /* an hour */) {
- it.getAuth()
-}
-
-/**
- * Underlying container for all fb requests
- */
-data class RequestAuth(
- val userId: Long = -1,
- val cookie: String = "",
- val fb_dtsg: String = "",
- val rev: String = ""
-) {
- val isComplete
- get() = userId > 0 && cookie.isNotEmpty() && fb_dtsg.isNotEmpty() && rev.isNotEmpty()
-}
-
-/**
- * Request container with the execution call
- */
-class FrostRequest<out T : Any?>(val call: Call, private val invoke: (Call) -> T) {
- fun invoke() = invoke(call)
-}
-
-internal inline fun <T : Any?> RequestAuth.frostRequest(
- noinline invoke: (Call) -> T,
- builder: Request.Builder.() -> Request.Builder // to ensure we don't do anything extra at the end
-): FrostRequest<T> {
- val request = cookie.requestBuilder()
- request.builder()
- return FrostRequest(request.call(), invoke)
-}
val httpClient: OkHttpClient by lazy {
val builder = OkHttpClient.Builder()
@@ -80,21 +33,6 @@ val httpClient: OkHttpClient by lazy {
builder.build()
}
-internal fun List<Pair<String, Any?>>.toForm(): FormBody {
- val builder = FormBody.Builder()
- forEach { (key, value) ->
- val v = value?.toString() ?: ""
- builder.add(key, v)
- }
- return builder.build()
-}
-
-internal fun List<Pair<String, Any?>>.withEmptyData(vararg key: String): List<Pair<String, Any?>> {
- val newList = toMutableList()
- newList.addAll(key.map { it to null })
- return newList
-}
-
internal fun String?.requestBuilder(): Request.Builder {
val builder = Request.Builder()
.header("User-Agent", USER_AGENT)
@@ -105,58 +43,3 @@ internal fun String?.requestBuilder(): Request.Builder {
}
fun Request.Builder.call(): Call = httpClient.newCall(build())
-
-fun String.getAuth(): RequestAuth {
- L.v { "Getting auth for ${hashCode()}" }
- var auth = RequestAuth(cookie = this)
- val id = FB_USER_MATCHER.find(this)[1]?.toLong() ?: return auth
- auth = auth.copy(userId = id)
- val call = this.requestBuilder()
- .url(FB_URL_BASE)
- .get()
- .call()
- call.execute().body()?.charStream()?.useLines { lines ->
- lines.forEach {
- val text = try {
- StringEscapeUtils.unescapeEcmaScript(it)
- } catch (ignore: Exception) {
- return@forEach
- }
- val fb_dtsg = FB_DTSG_MATCHER.find(text)[1]
- if (fb_dtsg != null) {
- auth = auth.copy(fb_dtsg = fb_dtsg)
- if (auth.isComplete) return auth
- }
-
- val rev = FB_REV_MATCHER.find(text)[1]
- if (rev != null) {
- auth = auth.copy(rev = rev)
- if (auth.isComplete) return auth
- }
- }
- }
-
- return auth
-}
-
-/**
- * Execute the call and attempt to check validity
- * Valid = not blank & no "error" instance
- */
-fun executeForNoError(call: Call): Boolean {
- val body = call.execute().body() ?: return false
- var empty = true
- body.charStream().useLines { lines ->
- lines.forEach {
- if (it.contains("error")) return false
- if (empty && it.isNotEmpty()) empty = false
- }
- }
- return !empty
-}
-
-fun getJsonUrl(call: Call): String? {
- val body = call.execute().body() ?: return null
- val url = FB_JSON_URL_MATCHER.find(body.string())[1] ?: return null
- return StringEscapeUtils.unescapeEcmaScript(url)
-}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt
index 1b5e8b99..0115d6fc 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt
@@ -16,38 +16,17 @@
*/
package com.pitchedapps.frost.facebook.requests
-import com.bumptech.glide.Priority
-import com.bumptech.glide.RequestBuilder
-import com.bumptech.glide.load.DataSource
-import com.bumptech.glide.load.Options
-import com.bumptech.glide.load.data.DataFetcher
-import com.bumptech.glide.load.model.ModelLoader
-import com.bumptech.glide.load.model.ModelLoaderFactory
-import com.bumptech.glide.load.model.MultiModelLoaderFactory
-import com.bumptech.glide.request.RequestOptions
-import com.bumptech.glide.request.target.Target
-import com.bumptech.glide.signature.ObjectKey
-import com.pitchedapps.frost.facebook.FB_IMAGE_ID_MATCHER
import com.pitchedapps.frost.facebook.FB_REDIRECT_URL_MATCHER
-import com.pitchedapps.frost.facebook.FB_URL_BASE
import com.pitchedapps.frost.facebook.formattedFbUrl
import com.pitchedapps.frost.facebook.get
import com.pitchedapps.frost.utils.L
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
-import okhttp3.Call
-import java.io.IOException
-import java.io.InputStream
/**
* Created by Allan Wang on 29/12/17.
*/
-fun RequestAuth.getFullSizedImage(fbid: Long) = frostRequest(::getJsonUrl) {
- url("${FB_URL_BASE}photo/view_full_size/?fbid=$fbid&__ajax__=&__user=$userId")
- get()
-}
/**
* Attempts to get the fbcdn url of the supplied image redirect url
@@ -65,106 +44,3 @@ suspend fun String.getFullSizedImageUrl(url: String, timeout: Long = 3000): Stri
null
}
}
-
-/**
- * Request loader for a potentially hd version of a url
- * In this case, each url may potentially return an id,
- * which may potentially be used to fetch a higher res image url
- * The following aims to allow such loading while adhering to Glide's lifecycle
- */
-data class HdImageMaybe(val url: String, val cookie: String) {
-
- val id: Long by lazy { FB_IMAGE_ID_MATCHER.find(url)[1]?.toLongOrNull() ?: -1 }
-
- val isValid: Boolean by lazy {
- id != -1L && cookie.isNotBlank()
- }
-}
-
-/*
- * The following was a test to see if hd image loading would work
- *
- * It's working and tested, though the improvements aren't really worth the extra data use
- * and reload
- */
-
-class HdImageLoadingFactory : ModelLoaderFactory<HdImageMaybe, InputStream> {
-
- override fun build(multiFactory: MultiModelLoaderFactory) = HdImageLoading()
-
- override fun teardown() = Unit
-}
-
-fun <T> RequestBuilder<T>.loadWithPotentialHd(model: HdImageMaybe) =
- thumbnail(clone().load(model.url))
- .load(model)
- .apply(RequestOptions().override(Target.SIZE_ORIGINAL))
-
-class HdImageLoading : ModelLoader<HdImageMaybe, InputStream> {
-
- override fun buildLoadData(
- model: HdImageMaybe,
- width: Int,
- height: Int,
- options: Options
- ): ModelLoader.LoadData<InputStream>? =
- if (!model.isValid) null
- else ModelLoader.LoadData(ObjectKey(model), HdImageFetcher(model))
-
- override fun handles(model: HdImageMaybe) = model.isValid
-}
-
-class HdImageFetcher(private val model: HdImageMaybe) : DataFetcher<InputStream> {
-
- @Volatile
- private var cancelled: Boolean = false
- private var urlCall: Call? = null
- private var inputStream: InputStream? = null
-
- private fun DataFetcher.DataCallback<in InputStream>.fail(msg: String) {
- onLoadFailed(RuntimeException(msg))
- }
-
- override fun getDataClass(): Class<InputStream> = InputStream::class.java
-
- override fun getDataSource(): DataSource = DataSource.REMOTE
-
- override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
- if (!model.isValid) return callback.fail("Model is invalid")
- val result: Result<InputStream?> = runCatching {
- runBlocking {
- withTimeout(20000L) {
- val auth = fbAuth.fetch(model.cookie).await()
- if (cancelled) throw RuntimeException("Cancelled")
- val url = auth.getFullSizedImage(model.id).invoke()
- ?: throw RuntimeException("Null url")
- if (cancelled) throw RuntimeException("Cancelled")
- if (!url.contains("png") && !url.contains("jpg")) throw RuntimeException("Invalid format")
- urlCall?.execute()?.body()?.byteStream()
- }
- }
- }
- if (result.isSuccess)
- callback.onDataReady(result.getOrNull())
- else
- callback.onLoadFailed(
- result.exceptionOrNull() as? Exception ?: RuntimeException("Failed")
- )
- }
-
- override fun cleanup() {
- try {
- inputStream?.close()
- } catch (e: IOException) {
- } finally {
- inputStream = null
- }
- }
-
- override fun cancel() {
- cancelled = true
- urlCall?.cancel()
- urlCall = null
- cleanup()
- }
-}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt
deleted file mode 100644
index dcb0ce10..00000000
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright 2018 Allan Wang
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.pitchedapps.frost.facebook.requests
-
-import com.fasterxml.jackson.annotation.JsonCreator
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties
-import com.fasterxml.jackson.annotation.JsonProperty
-import com.fasterxml.jackson.databind.MapperFeature
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.pitchedapps.frost.facebook.FB_URL_BASE
-import com.pitchedapps.frost.facebook.formattedFbUrl
-import com.pitchedapps.frost.utils.L
-import okhttp3.Call
-import org.apache.commons.text.StringEscapeUtils
-import org.jsoup.Jsoup
-import java.io.IOException
-
-/**
- * Created by Allan Wang on 29/12/17.
- */
-fun RequestAuth.getMenuData(): FrostRequest<MenuData?> {
-
- val body = listOf(
- "fb_dtsg" to fb_dtsg,
- "__user" to userId
- ).withEmptyData("m_sess", "__dyn", "__req", "__ajax__")
-
- return frostRequest(::parseMenu) {
- url("${FB_URL_BASE}bookmarks/flyout/body/?id=u_0_2")
- post(body.toForm())
- }
-}
-
-fun parseMenu(call: Call): MenuData? {
- val fullString = call.execute().body()?.string() ?: return null
- var jsonString = fullString.substringAfter("bookmarkGroups", "")
- .substringAfter("[", "")
-
- if (jsonString.isBlank()) return null
-
- jsonString = "{ \"data\" : [${StringEscapeUtils.unescapeEcmaScript(jsonString)}"
-
- val mapper = ObjectMapper()
- .disable(MapperFeature.AUTO_DETECT_SETTERS)
-
- return try {
- val data = mapper.readValue(jsonString, MenuData::class.java)
-
- // parse footer content
-
- val footer = fullString.substringAfter("footerMarkup", "")
- .substringAfter("{", "")
- .substringBefore("}", "")
-
- val doc = Jsoup.parseBodyFragment(
- StringEscapeUtils.unescapeEcmaScript(
- StringEscapeUtils.unescapeEcmaScript(footer)
- )
- )
- val footerData = mutableListOf<MenuFooterItem>()
- val footerSmallData = mutableListOf<MenuFooterItem>()
-
- doc.select("a[href]").forEach {
- val text = it.text()
- it.parent()
- if (text.isEmpty()) return@forEach
- val href = it.attr("href").formattedFbUrl
- val item = MenuFooterItem(name = text, url = href)
- if (it.parent().tag().name == "span")
- footerSmallData.add(item)
- else
- footerData.add(item)
- }
-
- return data.copy(footer = MenuFooter(footerData, footerSmallData))
- } catch (e: IOException) {
- L.e(e) { "Menu parse fail" }
- null
- }
-}
-
-@JsonIgnoreProperties(ignoreUnknown = true)
-data class MenuData(
- val data: List<MenuHeader> = emptyList(),
- val footer: MenuFooter = MenuFooter()
-) {
-
- @JsonCreator
- constructor(
- @JsonProperty("data") data: List<MenuHeader>?
- ) : this(data ?: emptyList(), MenuFooter())
-
- fun flatMapValid(): List<MenuItemData> {
- val items = mutableListOf<MenuItemData>()
- data.forEach {
- if (it.isValid) items.add(it)
- items.addAll(it.visible.filter(MenuItem::isValid))
- }
-
- items.addAll(footer.data.filter(MenuFooterItem::isValid))
- items.addAll(footer.smallData.filter(MenuFooterItem::isValid))
-
- return items
- }
-}
-
-interface MenuItemData {
- val isValid: Boolean
-}
-
-@JsonIgnoreProperties(ignoreUnknown = true)
-data class MenuHeader(
- val id: String? = null,
- val header: String? = null,
- val visible: List<MenuItem> = emptyList(),
- val all: List<MenuItem> = emptyList()
-) : MenuItemData {
-
- @JsonCreator
- constructor(
- @JsonProperty("id") id: String?,
- @JsonProperty("header") header: String?,
- @JsonProperty("visible") visible: List<MenuItem>?,
- @JsonProperty("all") all: List<MenuItem>?,
- @JsonProperty("fake") fake: Boolean?
- ) : this(id, header, visible ?: emptyList(), all ?: emptyList())
-
- override val isValid: Boolean
- get() = !header.isNullOrBlank()
-}
-
-@JsonIgnoreProperties(ignoreUnknown = true)
-data class MenuItem(
- val id: String? = null,
- val name: String? = null,
- val pic: String? = null,
- val url: String? = null,
- val badge: String? = null,
- val countDetails: String? = null
-) : MenuItemData {
-
- @JsonCreator
- constructor(
- @JsonProperty("id") id: String?,
- @JsonProperty("name") name: String?,
- @JsonProperty("pic") pic: String?,
- @JsonProperty("url") url: String?,
- @JsonProperty("count") badge: String?,
- @JsonProperty("count_details") countDetails: String?,
- @JsonProperty("fake") fake: Boolean?
- ) : this(
- id, name, pic?.formattedFbUrl,
- url?.formattedFbUrl,
- if (badge == "0") null else badge,
- countDetails
- )
-
- override val isValid: Boolean
- get() = !name.isNullOrBlank() && !url.isNullOrBlank()
-}
-
-data class MenuFooter(
- val data: List<MenuFooterItem> = emptyList(),
- val smallData: List<MenuFooterItem> = emptyList()
-) {
-
- val hasContent
- get() = data.isNotEmpty() || smallData.isNotEmpty()
-}
-
-data class MenuFooterItem(
- val name: String? = null,
- val url: String? = null,
- val isSmall: Boolean = false
-) : MenuItemData {
- override val isValid: Boolean
- get() = name != null && url != null
-}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Messages.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Messages.kt
deleted file mode 100644
index f350c547..00000000
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Messages.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2018 Allan Wang
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.pitchedapps.frost.facebook.requests
-
-import com.pitchedapps.frost.facebook.FB_URL_BASE
-import okhttp3.Call
-
-/**
- * Created by Allan Wang on 07/01/18.
- */
-fun RequestAuth.sendMessage(group: String, content: String): FrostRequest<Boolean> {
-
- // todo test more; only tested against tids=cid...
- val body = listOf(
- "tids" to group,
- "body" to content,
- "fb_dtsg" to fb_dtsg,
- "__user" to userId
- ).withEmptyData("m_sess", "__dyn", "__req", "__ajax__")
-
- return frostRequest(::validateMessage) {
- url("${FB_URL_BASE}messages/send")
- post(body.toForm())
- }
-}
-
-/**
- * Messages are a bit weird with their responses
- */
-private fun validateMessage(call: Call): Boolean {
- val body = call.execute().body() ?: return false
- // todo
- return true
-}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Notifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Notifications.kt
deleted file mode 100644
index bf974034..00000000
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Notifications.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2018 Allan Wang
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.pitchedapps.frost.facebook.requests
-
-import com.pitchedapps.frost.facebook.FB_URL_BASE
-
-/**
- * Created by Allan Wang on 29/12/17.
- */
-fun RequestAuth.markNotificationRead(notifId: Long): FrostRequest<Boolean> {
-
- val body = listOf(
- "click_type" to "notification_click",
- "id" to notifId,
- "target_id" to "null",
- "fb_dtsg" to fb_dtsg,
- "__user" to userId
- ).withEmptyData("m_sess", "__dyn", "__req", "__ajax__")
-
- return frostRequest(::executeForNoError) {
- url("${FB_URL_BASE}a/jewel_notifications_log.php")
- post(body.toForm())
- }
-}