aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/FrostParser.kt
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/FrostParser.kt')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/FrostParser.kt128
1 files changed, 128 insertions, 0 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/FrostParser.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/FrostParser.kt
new file mode 100644
index 00000000..5d023023
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/FrostParser.kt
@@ -0,0 +1,128 @@
+package com.pitchedapps.frost.facebook.parsers
+
+import com.pitchedapps.frost.dbflow.CookieModel
+import com.pitchedapps.frost.facebook.FB_CSS_URL_MATCHER
+import com.pitchedapps.frost.facebook.formattedFbUrl
+import com.pitchedapps.frost.facebook.get
+import com.pitchedapps.frost.services.NotificationContent
+import com.pitchedapps.frost.utils.frostJsoup
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import org.jsoup.select.Elements
+
+/**
+ * Created by Allan Wang on 2017-10-06.
+ *
+ * Interface for a given parser
+ * Use cases should be attached as delegates to objects that implement this interface
+ *
+ * In all cases, parsing will be done from a JSoup document
+ * Variants accepting strings are also permitted, and they will be converted to documents accordingly
+ * The return type must be nonnull if no parsing errors occurred, as null signifies a parse error
+ * If null really must be allowed, use Optionals
+ */
+interface FrostParser<out T : Any> {
+
+ /**
+ * Name associated to parser
+ * Purely for display
+ */
+ var nameRes: Int
+
+ /**
+ * Url to request from
+ */
+ val url: String
+
+ /**
+ * Call parsing with default implementation using cookie
+ */
+ fun parse(cookie: String?): ParseResponse<T>?
+
+ /**
+ * Call parsing with given document
+ */
+ fun parse(cookie: String?, document: Document): ParseResponse<T>?
+
+ /**
+ * Call parsing using jsoup to fetch from given url
+ */
+ fun parseFromUrl(cookie: String?, url: String): ParseResponse<T>?
+
+ /**
+ * Call parsing with given data
+ */
+ fun parseFromData(cookie: String?, text: String): ParseResponse<T>?
+
+}
+
+const val FALLBACK_TIME_MOD = 1000000
+
+data class FrostLink(val text: String, val href: String)
+
+data class ParseResponse<out T>(val cookie: String, val data: T) {
+ override fun toString() = "ParseResponse\ncookie: $cookie\ndata:\n$data"
+}
+
+interface ParseNotification {
+ fun getUnreadNotifications(data: CookieModel): List<NotificationContent>
+}
+
+internal fun <T> List<T>.toJsonString(tag: String, indent: Int) = StringBuilder().apply {
+ val tabs = "\t".repeat(indent)
+ append("$tabs$tag: [\n\t$tabs")
+ append(this@toJsonString.joinToString("\n\t$tabs"))
+ append("\n$tabs]\n")
+}.toString()
+
+/**
+ * T should have a readable toString() function
+ * [redirectToText] dictates whether all data should be converted to text then back to document before parsing
+ */
+internal abstract class FrostParserBase<out T : Any>(private val redirectToText: Boolean) : FrostParser<T> {
+
+ final override fun parse(cookie: String?) = parseFromUrl(cookie, url)
+
+ final override fun parseFromData(cookie: String?, text: String): ParseResponse<T>? {
+ cookie ?: return null
+ val doc = textToDoc(text) ?: return null
+ val data = parseImpl(doc) ?: return null
+ return ParseResponse(cookie, data)
+ }
+
+ final override fun parseFromUrl(cookie: String?, url: String): ParseResponse<T>? =
+ parse(cookie, frostJsoup(cookie, url))
+
+ override fun parse(cookie: String?, document: Document): ParseResponse<T>? {
+ cookie ?: return null
+ if (redirectToText)
+ return parseFromData(cookie, document.toString())
+ val data = parseImpl(document) ?: return null
+ return ParseResponse(cookie, data)
+ }
+
+ protected abstract fun parseImpl(doc: Document): T?
+
+ // protected abstract fun parse(doc: Document): T?
+
+ /**
+ * Attempts to find inner <i> element with some style containing a url
+ * Returns the formatted url, or an empty string if nothing was found
+ */
+ protected fun Element.getInnerImgStyle() =
+ select("i.img[style*=url]").getStyleUrl()
+
+ protected fun Elements.getStyleUrl() =
+ FB_CSS_URL_MATCHER.find(attr("style"))[1]?.formattedFbUrl
+
+ protected open fun textToDoc(text: String) = if (!redirectToText)
+ Jsoup.parse(text)
+ else
+ throw RuntimeException("${this::class.java.simpleName} requires text redirect but did not implement textToDoc")
+
+ protected fun parseLink(element: Element?): FrostLink? {
+ val a = element?.getElementsByTag("a")?.first() ?: return null
+ return FrostLink(a.text(), a.attr("href"))
+ }
+} \ No newline at end of file