diff options
author | Allan Wang <me@allanwang.ca> | 2017-07-13 13:50:00 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-13 13:50:00 -0700 |
commit | 91119de328bf5f4e8c945f8fb470453319b9f0ed (patch) | |
tree | 9ba1786f9cd8488a0cc0dfb247e1b387a4161cfb /app/src/main/kotlin/com/pitchedapps/frost/services | |
parent | de34d09f975079d5c044eae6da7ed92605009faf (diff) | |
download | frost-91119de328bf5f4e8c945f8fb470453319b9f0ed.tar.gz frost-91119de328bf5f4e8c945f8fb470453319b9f0ed.tar.bz2 frost-91119de328bf5f4e8c945f8fb470453319b9f0ed.zip |
Dev 1.2.2 - Add framework for messenger notifications (#47)
* Update KAU to v2.0
* Only inject theme for facebook and inject js after
* Clean up menu loading logic
* Add path null check
* Remove .idea files
* Add url formatter testers
* Update tests and check url nullability - Fixes
* Create instant messaging parser
* Shorted notification log and remove unnecessary null checks
* Make migration buildable
* Test message parser
* finalize messenger notifs for now
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/services')
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt | 5 | ||||
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt | 103 |
2 files changed, 86 insertions, 22 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt index 6af6b1db..2cea55b4 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt @@ -64,6 +64,7 @@ class FrostNotificationTarget(val context: Context, data class NotificationContent(val data: CookieModel, val notifId: Int, val href: String, + val title: String? = null, val text: String, val timestamp: Long, val profileUrl: String) { @@ -81,7 +82,7 @@ data class NotificationContent(val data: CookieModel, val group = "frost_${data.id}" val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) val notifBuilder = context.frostNotification - .setContentTitle(context.string(R.string.app_name)) + .setContentTitle(title ?: context.string(R.string.app_name)) .setContentText(text) .setContentIntent(pendingIntent) .setCategory(Notification.CATEGORY_SOCIAL) @@ -133,7 +134,7 @@ const val NOTIFICATION_JOB_NOW = 6 /** * Run notification job right now */ -fun Context.fetchNotifications():Boolean { +fun Context.fetchNotifications(): Boolean { val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val serviceComponent = ComponentName(this, NotificationService::class.java) val builder = JobInfo.Builder(NOTIFICATION_JOB_NOW, serviceComponent) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt index 4c03f056..f9d0c63c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt @@ -7,9 +7,13 @@ import android.support.v4.app.NotificationManagerCompat import ca.allanwang.kau.utils.string import com.pitchedapps.frost.BuildConfig import com.pitchedapps.frost.R -import com.pitchedapps.frost.dbflow.* +import com.pitchedapps.frost.dbflow.CookieModel +import com.pitchedapps.frost.dbflow.lastNotificationTime +import com.pitchedapps.frost.dbflow.loadFbCookie +import com.pitchedapps.frost.dbflow.loadFbCookiesSync import com.pitchedapps.frost.facebook.FACEBOOK_COM import com.pitchedapps.frost.facebook.FbTab +import com.pitchedapps.frost.facebook.USER_AGENT_BASIC import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.frostAnswersCustom @@ -31,6 +35,7 @@ class NotificationService : JobService() { companion object { val epochMatcher: Regex by lazy { Regex(":([0-9]*?),") } val notifIdMatcher: Regex by lazy { Regex("notif_id\":([0-9]*?),") } + val messageNotifIdMatcher: Regex by lazy { Regex("thread_fbid_([0-9]+)") } val profMatcher: Regex by lazy { Regex("url\\(\"(.*?)\"\\)") } } @@ -59,14 +64,26 @@ class NotificationService : JobService() { return true } + fun logNotif(text: String): NotificationContent? { + L.eThrow("NotificationService: $text") + return null + } + fun fetchNotifications(data: CookieModel) { + fetchGeneralNotifications(data) +// fetchMessageNotifications(data) + debugNotification("Hello") + } + + fun fetchGeneralNotifications(data: CookieModel) { L.i("Notif fetch for $data") - val doc = Jsoup.connect(FbTab.NOTIFICATIONS.url).cookie(FACEBOOK_COM, data.cookie).get() + val doc = Jsoup.connect(FbTab.NOTIFICATIONS.url).cookie(FACEBOOK_COM, data.cookie).userAgent(USER_AGENT_BASIC).get() //aclb for unread, acw for read - val unreadNotifications = doc.getElementById("notifications_list").getElementsByClass("aclb") + val unreadNotifications = (doc.getElementById("notifications_list") ?: return L.eThrow("Notification list not found")).getElementsByClass("aclb") var notifCount = 0 // val prevLatestEpoch = 1498931565L // for testing - val prevLatestEpoch = lastNotificationTime(data.id) + val prevNotifTime = lastNotificationTime(data.id) + val prevLatestEpoch = prevNotifTime.epoch L.v("Notif Prev Latest Epoch $prevLatestEpoch") var newLatestEpoch = prevLatestEpoch unreadNotifications.forEach unread@ { @@ -79,31 +96,77 @@ class NotificationService : JobService() { newLatestEpoch = notif.timestamp notifCount++ } - if (newLatestEpoch != prevLatestEpoch) saveNotificationTime(NotificationModel(data.id, newLatestEpoch)) - frostAnswersCustom("Notifications") { putCustomAttribute("Count", notifCount) } + if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epoch = newLatestEpoch).update() + frostAnswersCustom("Notifications") { + putCustomAttribute("Type", "General") + putCustomAttribute("Count", notifCount) + } summaryNotification(data.id, notifCount) } fun parseNotification(data: CookieModel, element: Element): NotificationContent? { - val a = element.getElementsByTag("a").first() ?: return null - //fetch id - val dataStore = a.attr("data-store") - val notifId = if (dataStore == null) System.currentTimeMillis() - else notifIdMatcher.find(dataStore)?.groups?.get(1)?.value?.toLong() ?: System.currentTimeMillis() + val a = element.getElementsByTag("a").first() ?: return logNotif("IM No a tag") val abbr = element.getElementsByTag("abbr") - val timeString = abbr?.text() - var text = a.text().replace("\u00a0", " ") //remove + val epoch = epochMatcher.find(abbr.attr("data-store"))?.groups?.get(1)?.value?.toLong() ?: return logNotif("IM No epoch") + //fetch id + val notifId = notifIdMatcher.find(a.attr("data-store"))?.groups?.get(1)?.value?.toLong() ?: System.currentTimeMillis() + val timeString = abbr.text() + val text = a.text().replace("\u00a0", " ").removeSuffix(timeString).trim() //remove if (Prefs.notificationKeywords.any { text.contains(it, ignoreCase = true) }) return null //notification filtered out - if (timeString != null) text = text.removeSuffix(timeString) - text = text.trim() - //fetch epoch - val abbrData = abbr?.attr("data-store") - val epoch = if (abbrData == null) -1L else epochMatcher.find(abbrData)?.groups?.get(1)?.value?.toLong() ?: -1L //fetch profpic val p = element.select("i.img[style*=url]") - val pUrl = profMatcher.find(p.getOrNull(0)?.attr("style") ?: "")?.groups?.get(1)?.value ?: "" - return NotificationContent(data, notifId.toInt(), a.attr("href"), text, epoch, pUrl) + val pUrl = profMatcher.find(p.attr("style"))?.groups?.get(1)?.value ?: "" + return NotificationContent(data, notifId.toInt(), a.attr("href"), null, text, epoch, pUrl) + } + + fun fetchMessageNotifications(data: CookieModel) { + if (!Prefs.notificationsInstantMessages) return + L.i("Notif IM fetch for $data") + val doc = Jsoup.connect(FbTab.MESSAGES.url).cookie(FACEBOOK_COM, data.cookie).userAgent(USER_AGENT_BASIC).get() + val unreadNotifications = (doc.getElementById("threadlist_rows") ?: return L.eThrow("Notification messages not found")).getElementsByClass("aclb") + var notifCount = 0 + L.d("IM notif count ${unreadNotifications.size}") + unreadNotifications.forEach { + with(it) { + L.d("notif ${id()} ${className()}") + } + } + val prevNotifTime = lastNotificationTime(data.id) + val prevLatestEpoch = prevNotifTime.epochIm + L.v("Notif Prev Latest Im Epoch $prevLatestEpoch") + var newLatestEpoch = prevLatestEpoch + unreadNotifications.forEach unread@ { + elem -> + val notif = parseMessageNotification(data, elem) ?: return@unread + L.v("Notif im timestamp ${notif.timestamp}") + if (notif.timestamp <= prevLatestEpoch) return@unread + notif.createNotification(this@NotificationService) + if (notif.timestamp > newLatestEpoch) + newLatestEpoch = notif.timestamp + notifCount++ + } +// if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epochIm = newLatestEpoch).update() + frostAnswersCustom("Notifications") { + putCustomAttribute("Type", "Message") + putCustomAttribute("Count", notifCount) + } + summaryNotification(data.id, notifCount) + } + + fun parseMessageNotification(data: CookieModel, element: Element): NotificationContent? { + val a = element.getElementsByTag("a").first() ?: return null + val abbr = element.getElementsByTag("abbr") + val epoch = epochMatcher.find(abbr.attr("data-store"))?.groups?.get(1)?.value?.toLong() ?: return logNotif("No epoch") + val thread = element.getElementsByAttributeValueContaining("id", "thread_fbid_").first() ?: return null + //fetch id + val notifId = messageNotifIdMatcher.find(thread.id())?.groups?.get(1)?.value?.toLong() ?: System.currentTimeMillis() + val text = element.select("span.snippet").firstOrNull()?.text()?.trim() ?: getString(R.string.new_message) + if (Prefs.notificationKeywords.any { text.contains(it, ignoreCase = true) }) return null //notification filtered out + //fetch convo pic + val p = element.select("i.img[style*=url]") + val pUrl = profMatcher.find(p.attr("style"))?.groups?.get(1)?.value ?: "" + return NotificationContent(data, notifId.toInt(), a.attr("href"), a.text(), text, epoch, pUrl) } private fun Context.debugNotification(text: String) { |