aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt158
1 files changed, 158 insertions, 0 deletions
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
new file mode 100644
index 00000000..59f87fbd
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt
@@ -0,0 +1,158 @@
+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 != null
+}
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+data class MenuItem(val id: String? = null,
+ val name: String? = null,
+ val pic: String? = null,
+ val url: String? = null,
+ val count: Int = 0,
+ 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") count: Int?,
+ @JsonProperty("count_details") countDetails: String?,
+ @JsonProperty("fake") fake: Boolean?
+ ) : this(id, name, pic?.formattedFbUrl, url?.formattedFbUrl, count ?: 0, countDetails)
+
+ override val isValid: Boolean
+ get() = name != null && url != null
+}
+
+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
+} \ No newline at end of file