aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/services
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-07-13 13:50:00 -0700
committerGitHub <noreply@github.com>2017-07-13 13:50:00 -0700
commit91119de328bf5f4e8c945f8fb470453319b9f0ed (patch)
tree9ba1786f9cd8488a0cc0dfb247e1b387a4161cfb /app/src/main/kotlin/com/pitchedapps/frost/services
parentde34d09f975079d5c044eae6da7ed92605009faf (diff)
downloadfrost-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.kt5
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt103
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 &nbsp;
+ 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 &nbsp;
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) {