diff options
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers')
4 files changed, 166 insertions, 92 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 index 3d5c5bce..5709bb9f 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/FrostParser.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/FrostParser.kt @@ -1,3 +1,19 @@ +/* + * 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.parsers import com.pitchedapps.frost.dbflow.CookieModel @@ -54,7 +70,6 @@ interface FrostParser<out T : Any> { * Call parsing with given data */ fun parseFromData(cookie: String?, text: String): ParseResponse<T>? - } const val FALLBACK_TIME_MOD = 1000000 @@ -92,7 +107,7 @@ internal abstract class FrostParserBase<out T : Any>(private val redirectToText: } final override fun parseFromUrl(cookie: String?, url: String): ParseResponse<T>? = - parse(cookie, frostJsoup(cookie, url)) + parse(cookie, frostJsoup(cookie, url)) override fun parse(cookie: String?, document: Document): ParseResponse<T>? { cookie ?: return null @@ -109,17 +124,17 @@ internal abstract class FrostParserBase<out T : Any>(private val redirectToText: * Returns the formatted url, or an empty string if nothing was found */ protected fun Element.getInnerImgStyle(): String? = - select("i.img[style*=url]").getStyleUrl() + select("i.img[style*=url]").getStyleUrl() protected fun Elements.getStyleUrl(): String? = - FB_CSS_URL_MATCHER.find(attr("style"))[1]?.formattedFbUrl + FB_CSS_URL_MATCHER.find(attr("style"))[1]?.formattedFbUrl protected open fun textToDoc(text: String): Document? = - if (!redirectToText) Jsoup.parse(text) - else throw RuntimeException("${this::class.java.simpleName} requires text redirect but did not implement textToDoc") + 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 +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/MessageParser.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/MessageParser.kt index 27b731bc..f05c42e9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/MessageParser.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/MessageParser.kt @@ -1,7 +1,27 @@ +/* + * 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.parsers import com.pitchedapps.frost.dbflow.CookieModel -import com.pitchedapps.frost.facebook.* +import com.pitchedapps.frost.facebook.FB_EPOCH_MATCHER +import com.pitchedapps.frost.facebook.FB_MESSAGE_NOTIF_ID_MATCHER +import com.pitchedapps.frost.facebook.FbItem +import com.pitchedapps.frost.facebook.formattedFbUrl +import com.pitchedapps.frost.facebook.get import com.pitchedapps.frost.services.NotificationContent import com.pitchedapps.frost.utils.L import org.apache.commons.text.StringEscapeUtils @@ -19,12 +39,12 @@ import org.jsoup.nodes.Element object MessageParser : FrostParser<FrostMessages> by MessageParserImpl() { fun queryUser(cookie: String?, name: String) = parseFromUrl(cookie, "${FbItem.MESSAGES.url}/?q=$name") - } -data class FrostMessages(val threads: List<FrostThread>, - val seeMore: FrostLink?, - val extraLinks: List<FrostLink> +data class FrostMessages( + val threads: List<FrostThread>, + val seeMore: FrostLink?, + val extraLinks: List<FrostLink> ) : ParseNotification { override fun toString() = StringBuilder().apply { append("FrostMessages {\n") @@ -35,19 +55,19 @@ data class FrostMessages(val threads: List<FrostThread>, }.toString() override fun getUnreadNotifications(data: CookieModel) = - threads.asSequence().filter(FrostThread::unread).map { - with(it) { - NotificationContent( - data = data, - id = id, - href = url, - title = title, - text = content ?: "", - timestamp = time, - profileUrl = img - ) - } - }.toList() + threads.asSequence().filter(FrostThread::unread).map { + with(it) { + NotificationContent( + data = data, + id = id, + href = url, + title = title, + text = content ?: "", + timestamp = time, + profileUrl = img + ) + } + }.toList() } /** @@ -58,14 +78,16 @@ data class FrostMessages(val threads: List<FrostThread>, * [unread] true if image is unread, false otherwise * [content] optional string for thread */ -data class FrostThread(val id: Long, - val img: String?, - val title: String, - val time: Long, - val url: String, - val unread: Boolean, - val content: String?, - val contentImgUrl: String?) +data class FrostThread( + val id: Long, + val img: String?, + val title: String, + val time: Long, + val url: String, + val unread: Boolean, + val content: String?, + val contentImgUrl: String? +) private class MessageParserImpl : FrostParserBase<FrostMessages>(true) { @@ -92,11 +114,12 @@ private class MessageParserImpl : FrostParserBase<FrostMessages>(true) { override fun parseImpl(doc: Document): FrostMessages? { val threadList = doc.getElementById("threadlist_rows") ?: return null - val threads: List<FrostThread> = threadList.getElementsByAttributeValueMatching("id", ".*${FB_MESSAGE_NOTIF_ID_MATCHER.pattern}.*") + val threads: List<FrostThread> = + threadList.getElementsByAttributeValueMatching("id", ".*${FB_MESSAGE_NOTIF_ID_MATCHER.pattern}.*") .mapNotNull(this::parseMessage) val seeMore = parseLink(doc.getElementById("see_older_threads")) val extraLinks = threadList.nextElementSibling().select("a") - .mapNotNull(this::parseLink) + .mapNotNull(this::parseLink) return FrostMessages(threads, seeMore, extraLinks) } @@ -106,21 +129,20 @@ private class MessageParserImpl : FrostParserBase<FrostMessages>(true) { val epoch = FB_EPOCH_MATCHER.find(abbr.attr("data-store"))[1]?.toLongOrNull() ?: -1L //fetch id val id = FB_MESSAGE_NOTIF_ID_MATCHER.find(element.id())[1]?.toLongOrNull() - ?: System.currentTimeMillis() % FALLBACK_TIME_MOD + ?: System.currentTimeMillis() % FALLBACK_TIME_MOD val snippet = element.select("span.snippet").firstOrNull() val content = snippet?.text()?.trim() val contentImg = snippet?.select("i[style*=url]")?.getStyleUrl() val img = element.getInnerImgStyle() return FrostThread( - id = id, - img = img, - title = a.text(), - time = epoch, - url = a.attr("href").formattedFbUrl, - unread = !element.hasClass("acw"), - content = content, - contentImgUrl = contentImg + id = id, + img = img, + title = a.text(), + time = epoch, + url = a.attr("href").formattedFbUrl, + unread = !element.hasClass("acw"), + content = content, + contentImgUrl = contentImg ) } - } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/NotifParser.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/NotifParser.kt index 8aa8e706..b8aa899b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/NotifParser.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/NotifParser.kt @@ -1,7 +1,27 @@ +/* + * 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.parsers import com.pitchedapps.frost.dbflow.CookieModel -import com.pitchedapps.frost.facebook.* +import com.pitchedapps.frost.facebook.FB_EPOCH_MATCHER +import com.pitchedapps.frost.facebook.FB_NOTIF_ID_MATCHER +import com.pitchedapps.frost.facebook.FbItem +import com.pitchedapps.frost.facebook.formattedFbUrl +import com.pitchedapps.frost.facebook.get import com.pitchedapps.frost.services.NotificationContent import org.jsoup.nodes.Document import org.jsoup.nodes.Element @@ -13,8 +33,8 @@ import org.jsoup.nodes.Element object NotifParser : FrostParser<FrostNotifs> by NotifParserImpl() data class FrostNotifs( - val notifs: List<FrostNotif>, - val seeMore: FrostLink? + val notifs: List<FrostNotif>, + val seeMore: FrostLink? ) : ParseNotification { override fun toString() = StringBuilder().apply { append("FrostNotifs {\n") @@ -24,19 +44,19 @@ data class FrostNotifs( }.toString() override fun getUnreadNotifications(data: CookieModel) = - notifs.asSequence().filter(FrostNotif::unread).map { - with(it) { - NotificationContent( - data = data, - id = id, - href = url, - title = null, - text = content, - timestamp = time, - profileUrl = img - ) - } - }.toList() + notifs.asSequence().filter(FrostNotif::unread).map { + with(it) { + NotificationContent( + data = data, + id = id, + href = url, + title = null, + text = content, + timestamp = time, + profileUrl = img + ) + } + }.toList() } /** @@ -49,14 +69,16 @@ data class FrostNotifs( * [timeString] text version of time from Facebook * [thumbnailUrl] optional thumbnail url if existent */ -data class FrostNotif(val id: Long, - val img: String?, - val time: Long, - val url: String, - val unread: Boolean, - val content: String, - val timeString: String, - val thumbnailUrl: String?) +data class FrostNotif( + val id: Long, + val img: String?, + val time: Long, + val url: String, + val unread: Boolean, + val content: String, + val timeString: String, + val thumbnailUrl: String? +) private class NotifParserImpl : FrostParserBase<FrostNotifs>(false) { @@ -67,8 +89,8 @@ private class NotifParserImpl : FrostParserBase<FrostNotifs>(false) { override fun parseImpl(doc: Document): FrostNotifs? { val notificationList = doc.getElementById("notifications_list") ?: return null val notifications = notificationList - .getElementsByAttributeValueMatching("id", ".*${FB_NOTIF_ID_MATCHER.pattern}.*") - .mapNotNull(this::parseNotif) + .getElementsByAttributeValueMatching("id", ".*${FB_NOTIF_ID_MATCHER.pattern}.*") + .mapNotNull(this::parseNotif) val seeMore = parseLink(doc.getElementsByAttributeValue("href", "/notifications.php?more").first()) return FrostNotifs(notifications, seeMore) } @@ -79,22 +101,20 @@ private class NotifParserImpl : FrostParserBase<FrostNotifs>(false) { val epoch = FB_EPOCH_MATCHER.find(abbr.attr("data-store"))[1]?.toLongOrNull() ?: -1L //fetch id val id = FB_NOTIF_ID_MATCHER.find(element.id())[1]?.toLongOrNull() - ?: System.currentTimeMillis() % FALLBACK_TIME_MOD + ?: System.currentTimeMillis() % FALLBACK_TIME_MOD val img = element.getInnerImgStyle() val timeString = abbr.text() val content = a.text().replace("\u00a0", " ").removeSuffix(timeString).trim() //remove val thumbnail = element.selectFirst("img.thumbnail")?.attr("src") return FrostNotif( - id = id, - img = img, - time = epoch, - url = a.attr("href").formattedFbUrl, - unread = !element.hasClass("acw"), - content = content, - timeString = timeString, - thumbnailUrl = if (thumbnail?.isNotEmpty() == true) thumbnail else null + id = id, + img = img, + time = epoch, + url = a.attr("href").formattedFbUrl, + unread = !element.hasClass("acw"), + content = content, + timeString = timeString, + thumbnailUrl = if (thumbnail?.isNotEmpty() == true) thumbnail else null ) } - - } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/SearchParser.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/SearchParser.kt index d3367514..7869d881 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/SearchParser.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/parsers/SearchParser.kt @@ -1,3 +1,19 @@ +/* + * 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.parsers import ca.allanwang.kau.searchview.SearchItem @@ -46,9 +62,9 @@ data class FrostSearch(val href: String, val title: String, val description: Str companion object { fun create(href: String, title: String, description: String?) = FrostSearch( - with(href.indexOf("?")) { if (this == -1) href else href.substring(0, this) }, - title.format(), - description?.format() + with(href.indexOf("?")) { if (this == -1) href else href.substring(0, this) }, + title.format(), + description?.format() ) } } @@ -61,17 +77,18 @@ private class SearchParserImpl : FrostParserBase<FrostSearches>(false) { override fun parseImpl(doc: Document): FrostSearches? { val container: Element = doc.getElementById("BrowseResultsContainer") - ?: doc.getElementById("root") - ?: return null + ?: doc.getElementById("root") + ?: return null /** * * Removed [data-store*=result_id] */ return FrostSearches(container.select("a.touchable[href]").filter(Element::hasText).map { - FrostSearch.create(it.attr("href").formattedFbUrl, - it.select("._uoi").first()?.text() ?: "", - it.select("._1tcc").first()?.text()) + FrostSearch.create( + it.attr("href").formattedFbUrl, + it.select("._uoi").first()?.text() ?: "", + it.select("._1tcc").first()?.text() + ) }.filter { it.title.isNotBlank() }) } - -}
\ No newline at end of file +} |