GithubHelp home page GithubHelp logo

apollo-backend's People

Contributors

andremedeiros avatar christianselig avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

apollo-backend's Issues

Notifications for when a post/comment does well in terms of upvotes

If a user's post or comment does well, we should send a push notification alerting them of this. Users have asked for this and it matches the Reddit app's functionality here well.

Could do at three tiers: 25 upvotes, 500 upvotes, 1500 upvotes, with a different level of excitement in the notification for each tier.

Only look back for 24 hours, or maybe max 48 hours, because we don't need to introduce the complexity of seeing if a 4 month old post exploded randomly, this is for more recent things.

Option/endpoint to remove account from push notification server

For instance if a user signs out of their Reddit account, we don't want to keep delivering them notifications for that account, would be seen as weird. So when the user signs out, we should probably have an API endpoint where you specify an account ID and the APNs token maybe and it removes it from the server.

Could potentially be used to power #6 as well, or that could be done separately

Receipt validation

Gotta spank those scammers.

Should work for both Ultra (notifications/subscriptions) and for normal in-app purchases as well (like Pro).

JSON format changed and a rubric from my crappy Golang code is available.

iOS 16 Live Activity/Dynamic Island Support

Let's add support for iOS 16's Live Activities/Dynamic Island, which is powered by push notifications and we should only need a simple API.

Implementation Notes

  • Client sends server endpoint a post ID, app version (for potential versioning needs though unlikely), and an APNs token (note that a 'Live Activity push token' is different from the 'normal' push token we've been using)
  • Server sends a notification once a minute, analyzing the last 100 comments (sorted by new) in the thread and selecting the comment with the highest score that was posted within the last 60 seconds (if multiple scores are tied, choose random, if no new comment in last 60 seconds return most recent comment)
  • Does not require receipt validation (available for everyone for ease/speed of implementation on both server and client)
  • Send end request after 70 minutes
  • content-state payload in push should look like:
"content-state": {
    "postTitle": "",
    "postAuthor": "",
    "commentAuthor": "",
    "postTotalComments": 88,
    "postScore": 493,
    "commentBody": "",
    "commentAge": 1357802308,
    "commentScore": 5
}

Basic Code Structure

As a little test app I build a small Golang program to ping my phone and update the Live Activity using hardcoded values, perhaps this can help in implementing the actual program. A thing of note is that the apns2 Golang library we use doesn't appear to have support for dictionaries in their .Custom(key, value) Payload builder, or at least I couldn't figure out how to do it, so I used the raw byte construction method for the payload.

package main

import (
	"fmt"
	"time"

	apns "github.com/christianselig/apns2"
	token "github.com/sideshow/apns2/token"
)

func main() {
	fmt.Println("testing testing 123")

	authKey, err := token.AuthKeyFromFile("./AuthKey_T88A7G9LZ8.p8")

	if err != nil {
		fmt.Println(fmt.Sprintf("Dangit! AuthKey err: %s", err))
		fmt.Println("token error:", err)
	}

	token := &token.Token{
		AuthKey: authKey,
		KeyID:   "T88A7G9LZ8",
		TeamID:  "XG3L8T56DK",
	}

	pushClient := apns.NewTokenClient(token)

	byteString := fmt.Sprintf(`{"aps":{"timestamp":%d,"event":"update","content-state":{"postTotalComments":69,"postScore":102,"commentAuthor":"one_who_waits","commentScore":4,"commentAge":1665854499,"commentBody":"Wow that was a great field goal!"}}}`, time.Now().Unix())

	notification := &apns.Notification{
		DeviceToken: "808c89e511963466ee79642d66eaed0f77f035829f39c51326f9b78c69a2384076e28ab182b160a7d96e3573056205e109a508966d6ea75f277a0208f1081ae24009dabb286fe9aa515d24382f371250",
		Topic:       "com.christianselig.Apollo.push-type.liveactivity",
		PushType:    "liveactivity",
		Payload:     []byte(byteString),
	}

	pushResponse, pushErr := pushClient.Push(notification)

	fmt.Println(pushResponse)
	fmt.Println(pushErr)
}

"Mute all notifications" endpoint

Would be great to have an "all muted" endpoint for a specific account in addition to the current "separate" toggles for inbox and watchers.

The reason is (copied from our discussion):

Also on a scale of 1-10, how hard would it be to have a separate “all muted” flag for an account on the server? Basically right now we have the flag for inbox and a flag for watchers, but if a user has inbox OFF and watchers ON, and they hit “mute everything”, I basically would have to flip both to off. The issue is when they “unmute all” I want to leave inbox still off, and there’s no way for me to know that without storing a potentially error prone flag client side (kinda violating my ‘server is one source of truth’ edict)

Implement a plan/strategy for migrating/tackling the dual server problem (legacy server plus new one)

Currently some users receive double notifications because they jump between the current production version of Apollo with the "legacy" server, and the beta version using the new and improved server, causing them to be registered on both.

A client side strategy is currently implemented to moderate success, but where the servers are the real sources of truth it would likely be best to have a solution there as well.

(Currently the "addition" flow works well in that users will automatically be registered with the new server, so this issue is discussing the "deletion" flow wherein we want to remove them from the old server after being added to the new one)

One obvious strategy would be that when the update to Apollo comes out that introduces the new server into production, delete all accounts on the old server and request users upgrade to the newest version of Apollo. But it's possible that some will remain on the old version and not update due to iOS version/device limitations and we wouldn't want to remove their accounts.

Open to suggestions here obviously! Perhaps have the new server sent an HTTP request to the legacy server directly, notifying of the account ID plus APNs token being added, and then the legacy server will use that to delete it? Wouldn't want users discovering the HTTP call necessarily, so going directly from server to server without the client having anything to do with it might solve that?

Return original_transaction_id in server response for receipt validation

In addition to returning the name of the product (eg: ultra), the unlock status and the subscription type, ALSO return the original transaction ID in that same JSON so it can be determined client-side easily if the purchase was truly a purchase and not just a restore. Should be pretty easy to glean it from the pending_renewal_info we already look at:

"pending_renewal_info": [
    {
        "auto_renew_product_id": "com.christianselig.apollo.sub.monthly",
        "original_transaction_id": "420000571430640",
        "product_id": "com.christianselig.apollo.sub.monthly",
        "auto_renew_status": "1"
    }
]

Issue is I wrote some of the old receipt validation code not the greatest and some of the functions only return whether or not the subscription exists, not the accompanying ID, so will have to fix what bad past Christian did with fancy new André insights.

There's a good enough bandaid solution in place that's worked for well over a decade and likely will forever but this'd be a good one to fix one day, it's a handy one for client-side analytics.

Further reading on issue: https://stackoverflow.com/questions/5623652/differentiating-between-initial-buy-and-free-re-buy-in-storekit-in-app-purchas

A user-facing "send test notification" endpoint/functionality

Sometimes a user emails me saying "I'm not getting notifications, what's up with that" and I normally ask them for their account ID, jump through some hoops to find out what's going on, etc. and then just get them to reauthenticate after manually deleting it from the server.

It would be pretty awesome if there was just a "Send Test Notification" button in the app that I could wire up to an endpoint or something, and it would send back a notification with the list of accounts being used on the server, or something. Maybe the account health to? (eg: tokens aren't empty). This would show them that APNs is working and that the server has their accounts enrolled, which would save a lot of troubleshooting potentially.

ExampleException in home#example

Test error in Example.com

ExampleException in home#example
Something really bad happened

View on Bugsnag

Stacktrace

app/controllers/home_controller.rb:123 - example
app/controllers/other_controller.rb:12 - broken
lib/important/magic.rb:4 - load_something

View full stacktrace

Created by André Medeiros via Bugsnag

Option to mute notifications in some capacity

If a user doesn't want notifications for a little bit for whatever reason, we should give them the option to mute notifications (without actually having to nuke the notifications permission entirely for Apollo). Could just be a flag, could remove their device from the server temporarily.

My other thought was time-based/temporary mutes, but it looks like iOS 15 supports that at the OS level finally so not needed. https://ios.gadgethacks.com/how-to/mute-any-iphone-apps-notifications-for-one-hour-all-day-with-ios-15-0384784/

Don't delete saved user watchers upon an error/failure event, implement a grace period instead

As discussed currently if the user encounters a failure event (notification fails to deliver, or Apollo Ultra status expires) server deletes the account and its corresponding watchers. This is likely a little harsh as the user could simply have muted notifications at the iOS level, or had a credit card issue, and deleting a swath of watchers they worked hard to create would likely (and understandably) annoy the user so implementing a grace period would likely be optimal.

Suggestion: require failure events to be a month apart in order for the deletion to occur, so if first occurs on March 1st, another would have to occur on April 1st at the earliest to trigger an actual deletion.

Notifications for hot/trending posts in a subreddit

Similar to the official Reddit app, users have requested notifications for when a post is hot or trending in a subreddit hey like. Like it does exceptionally well and is seemingly worth seeing, otherwise every decent post from 100 subreddits would be blowing up their phone every minute of the day.

I think for obvious reasons we need to constrain this to say, the front page of the subreddit. If a user posts a stupid thing that never hits the front page but barely meets the qualification, I really don't think we have to add all the computational exhaustion for looking for those kinda posts when if the user logged onto the Reddit website and scanned for it they likely wouldn't manually see it either.

So basically the "time" factor (how far back do we look?) can be just decided by checking the front page of the subreddit periodically and matching stuff to that and if it exceeds.

For what defines hotness, we could look at the top posts in the subreddit for that month, see what say, the top 10% fall in, and if a post falls into that top 10% of points it's deemed hot maybe, and send out the notification?

For restricted, private, quarantined, all those weird subreddits, ehhhh, I say we just stay away from that for now. 5 users probably care and if there's enough that care we could tackle that down the road

It also might be worth making this code more generic. Check out the Pager app (I don't feel bad about pointing at another app because I've had these ideas for awhile, it's just an easy illustration) for kinda what I mean. Not only do they support "hot" style post notifications by basically arbitrary, user-defined input (ie: you define the upvotes. 50? 100?). That might allow us (down the road) to do more cool stuff and really become a "one stop shop" for granular Reddit notifications in a way that Pager only scratched the surface with (I think it's a dead project now), with stuff like notifications if title contains a keyword, for instance.

Support for following a thread that the signed in user didn't create

I think at some point it'd be cool to have a feature where if a user finds an interesting thread (perhaps asking a question relevant to them) they could subscribe to/follow that thread and get notifications about new (likely only top-level) comments on the thread.

For instance, UserA posts a thread in their city's subreddit wondering when the new Batman movie will be showing. UserB (signed in and using Apollo and also from this city) sees this thread and there's 0 comments, but is curious to see what responses will be, so requests Apollo to notify them.

Examples of user requesting this:

500 error while trying to toggle inbox notification enabled

When attempting to hit the https://beta.apollonotifications.com/v1/device/{apns_token}/account/{account_id}/notifications endpoint with the following payload:

{"watcher_notifications":true,"inbox_notifications":false}

The server currently reports an HTTP 500 error in response.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.