mirror of
https://github.com/christianselig/apollo-backend
synced 2024-11-22 03:37:43 +00:00
send notifications and worker stuff
This commit is contained in:
parent
9cb4f4693f
commit
6e5198831e
9 changed files with 224 additions and 49 deletions
1
Procfile
1
Procfile
|
@ -1,4 +1,3 @@
|
|||
postgres: postgres -D tmp/postgresql
|
||||
faktory: faktory
|
||||
server: go run github.com/andremedeiros/apollo/cmd/apollo-api
|
||||
worker: go run github.com/andremedeiros/apollo/cmd/apollo-worker
|
||||
|
|
|
@ -8,12 +8,11 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/andremedeiros/apollo/internal/data"
|
||||
"github.com/andremedeiros/apollo/internal/reddit"
|
||||
|
||||
faktory "github.com/contribsys/faktory/client"
|
||||
"github.com/joho/godotenv"
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
"github.com/andremedeiros/apollo/internal/data"
|
||||
"github.com/andremedeiros/apollo/internal/reddit"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
|
@ -21,12 +20,11 @@ type config struct {
|
|||
}
|
||||
|
||||
type application struct {
|
||||
cfg config
|
||||
logger *log.Logger
|
||||
db *sql.DB
|
||||
faktory *faktory.Client
|
||||
models *data.Models
|
||||
client *reddit.Client
|
||||
cfg config
|
||||
logger *log.Logger
|
||||
db *sql.DB
|
||||
models *data.Models
|
||||
client *reddit.Client
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -47,19 +45,12 @@ func main() {
|
|||
}
|
||||
defer db.Close()
|
||||
|
||||
faktory, err := faktory.Open()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer faktory.Close()
|
||||
|
||||
rc := reddit.NewClient(os.Getenv("REDDIT_CLIENT_ID"), os.Getenv("REDDIT_CLIENT_SECRET"))
|
||||
|
||||
app := &application{
|
||||
cfg,
|
||||
logger,
|
||||
db,
|
||||
faktory,
|
||||
data.NewModels(db),
|
||||
rc,
|
||||
}
|
||||
|
@ -72,10 +63,4 @@ func main() {
|
|||
logger.Printf("starting server on %s", srv.Addr)
|
||||
err = srv.ListenAndServe()
|
||||
logger.Fatal(err)
|
||||
|
||||
/*
|
||||
rc := reddit.NewClient("C7MjYkx1czyRDA", "I2AsVWbrf8h4vdQxVa5Pvf84vScF1w")
|
||||
rac := rc.NewAuthenticatedClient("2532458-kGp6OeR-LMoQNrUXRL-7UNfyBbViRA", "2532458-UE7IvJK3-VuTdMJB0bgMv58fxKQhww")
|
||||
rac.MessageInbox()
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -4,9 +4,16 @@ import (
|
|||
"database/sql"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
worker "github.com/contribsys/faktory_worker_go"
|
||||
"github.com/joho/godotenv"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/sideshow/apns2"
|
||||
"github.com/sideshow/apns2/payload"
|
||||
"github.com/sideshow/apns2/token"
|
||||
|
||||
"github.com/andremedeiros/apollo/internal/data"
|
||||
"github.com/andremedeiros/apollo/internal/reddit"
|
||||
|
@ -19,6 +26,95 @@ type application struct {
|
|||
client *reddit.Client
|
||||
}
|
||||
|
||||
var workers int = runtime.NumCPU() * 2
|
||||
|
||||
func accountWorker(id int, rc *reddit.Client, db *sql.DB, logger *log.Logger, quit chan bool) {
|
||||
authKey, err := token.AuthKeyFromFile("./tmp/authkey.p8")
|
||||
token := &token.Token{
|
||||
AuthKey: authKey,
|
||||
KeyID: "T88A7G9LZ8",
|
||||
TeamID: "XG3L8T56DK",
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("token error:", err)
|
||||
}
|
||||
|
||||
client := apns2.NewTokenClient(token)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-quit:
|
||||
return
|
||||
default:
|
||||
now := time.Now().UTC().Unix()
|
||||
|
||||
tx, err := db.Begin()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
continue
|
||||
}
|
||||
|
||||
query := `
|
||||
SELECT id, access_token, refresh_token, expires_at, last_message_id FROM accounts
|
||||
WHERE last_checked_at <= $1 - 5
|
||||
ORDER BY last_checked_at
|
||||
LIMIT 1
|
||||
FOR UPDATE SKIP LOCKED`
|
||||
args := []interface{}{now}
|
||||
|
||||
account := &data.Account{}
|
||||
err = tx.QueryRow(query, args...).Scan(&account.ID, &account.AccessToken, &account.RefreshToken, &account.ExpiresAt, &account.LastMessageID)
|
||||
|
||||
if account.ID == 0 {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
tx.Commit()
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Printf("Worker #%d, account %d", id, account.ID)
|
||||
|
||||
_, err = tx.Exec(`UPDATE accounts SET last_checked_at = $1 WHERE id = $2`, now, account.ID)
|
||||
|
||||
rac := rc.NewAuthenticatedClient(account.RefreshToken, account.AccessToken)
|
||||
if account.ExpiresAt < now {
|
||||
tokens, _ := rac.RefreshTokens()
|
||||
tx.Exec(`UPDATE accounts SET access_token = $1, refresh_token = $2, expires_at = $3 WHERE id = $4`,
|
||||
tokens.AccessToken, tokens.RefreshToken, now+3500, account.ID)
|
||||
}
|
||||
|
||||
msgs, err := rac.MessageInbox(account.LastMessageID)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(msgs.MessageListing.Messages) == 0 {
|
||||
tx.Commit()
|
||||
continue
|
||||
}
|
||||
|
||||
// Set latest message we alerted on
|
||||
latestMsg := msgs.MessageListing.Messages[0]
|
||||
|
||||
_, err = tx.Exec(`UPDATE accounts SET last_message_id = $1 WHERE id = $2`, latestMsg.FullName(), account.ID)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, msg := range msgs.MessageListing.Messages {
|
||||
notification := &apns2.Notification{}
|
||||
notification.DeviceToken = "9e1eb4c68d24a8f43eb92ed0a65f46aadbfbdbfe0a15ef4b5c34a9a4deb9ca49"
|
||||
notification.Topic = "com.christianselig.Apollo"
|
||||
notification.Payload = payload.NewPayload().AlertTitle(msg.Subject).AlertBody(msg.Body)
|
||||
client.Push(notification)
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
|
@ -27,23 +123,31 @@ func main() {
|
|||
|
||||
logger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
|
||||
|
||||
rc := reddit.NewClient(os.Getenv("REDDIT_CLIENT_ID"), os.Getenv("REDDIT_CLIENT_SECRET"))
|
||||
|
||||
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
rc := reddit.NewClient(os.Getenv("REDDIT_CLIENT_ID"), os.Getenv("REDDIT_CLIENT_SECRET"))
|
||||
db.SetMaxOpenConns(workers)
|
||||
|
||||
app := &application{
|
||||
logger,
|
||||
db,
|
||||
data.NewModels(db),
|
||||
rc,
|
||||
// This is a very conservative value -- seen as most of the work that is done in these jobs is
|
||||
//
|
||||
runtime.GOMAXPROCS(workers)
|
||||
quitCh := make(chan bool, workers)
|
||||
for i := 0; i < workers; i++ {
|
||||
go accountWorker(i, rc, db, logger, quitCh)
|
||||
}
|
||||
|
||||
mgr := worker.NewManager()
|
||||
mgr.ProcessStrictPriorityQueues("critical", "default", "bulk")
|
||||
mgr.Concurrency = 20
|
||||
mgr.Run()
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
<-sigs
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
quitCh <- true
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -6,8 +6,10 @@ require (
|
|||
github.com/contribsys/faktory v1.5.1
|
||||
github.com/contribsys/faktory_worker_go v1.4.2
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/lib/pq v1.10.1
|
||||
github.com/sideshow/apns2 v0.20.0 // indirect
|
||||
github.com/stretchr/testify v1.5.1 // indirect
|
||||
)
|
||||
|
|
30
go.sum
30
go.sum
|
@ -1,4 +1,6 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/benbjohnson/ego v0.4.0 h1:dJJoyR+HVi0CAyg6Eo/j3bLozdK91p/5WkAZ7ul99Uc=
|
||||
github.com/benbjohnson/ego v0.4.0/go.mod h1:6Qzj43pKX58WdlHDZKZS4Q/XXYwz6toIqXqNjzt4mNA=
|
||||
github.com/contribsys/faktory v1.3.0-1/go.mod h1:qCp3bcPrIT7mb/aR5KendsK/Nyg7yE3JOg4sETojQN8=
|
||||
github.com/contribsys/faktory v1.5.1 h1:Ac22RjbR8USVEwRV7jvz0+ytp2NquL5j7HC0IRM1K34=
|
||||
|
@ -8,9 +10,14 @@ github.com/contribsys/faktory_worker_go v1.4.2/go.mod h1:v53dJh0lrk18B/3zFJjAWM6
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE=
|
||||
github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||
github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
|
||||
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
|
@ -18,45 +25,60 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
|||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/justinas/nosurf v1.1.0 h1:qqV6FJmnDBJ6F9pOzhZgZitAZWBYonMOXglof7TtdZw=
|
||||
github.com/justinas/nosurf v1.1.0/go.mod h1:ALpWdSbuNGy2lZWtyXdjkYv4edL23oSEgfBT1gPJ5BQ=
|
||||
github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo=
|
||||
github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4=
|
||||
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
|
||||
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sideshow/apns2 v0.20.0 h1:5Lzk4DUq+waVc6/BkKzpDTpQjtk/BZOP0YsayBpY1NE=
|
||||
github.com/sideshow/apns2 v0.20.0/go.mod h1:f7dArLPLbiZ3qPdzzrZXdCSlMp8FD0p6z7tHssDOLvk=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -66,24 +88,32 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
@ -2,7 +2,6 @@ package reddit
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -51,7 +50,7 @@ func (rac *AuthenticatedClient) request(r *Request) ([]byte, error) {
|
|||
return ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (rac *AuthenticatedClient) refreshTokens() (string, string, error) {
|
||||
func (rac *AuthenticatedClient) RefreshTokens() (*RefreshTokenResponse, error) {
|
||||
req := NewRequest(
|
||||
WithMethod("POST"),
|
||||
WithURL(tokenURL),
|
||||
|
@ -61,20 +60,33 @@ func (rac *AuthenticatedClient) refreshTokens() (string, string, error) {
|
|||
)
|
||||
|
||||
body, err := rac.request(req)
|
||||
fmt.Println(string(body))
|
||||
return "", "", err
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rtr := &RefreshTokenResponse{}
|
||||
json.Unmarshal([]byte(body), rtr)
|
||||
return rtr, nil
|
||||
}
|
||||
|
||||
func (rac *AuthenticatedClient) MessageInbox() error {
|
||||
func (rac *AuthenticatedClient) MessageInbox(from string) (*MessageListingResponse, error) {
|
||||
req := NewRequest(
|
||||
WithMethod("GET"),
|
||||
WithToken(rac.accessToken),
|
||||
WithURL("https://oauth.reddit.com/message/inbox.json"),
|
||||
WithQuery("before", from),
|
||||
)
|
||||
|
||||
body, _ := rac.request(req)
|
||||
fmt.Println(string(body))
|
||||
return nil
|
||||
body, err := rac.request(req)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mlr := &MessageListingResponse{}
|
||||
json.Unmarshal([]byte(body), mlr)
|
||||
return mlr, nil
|
||||
}
|
||||
|
||||
type MeResponse struct {
|
||||
|
|
|
@ -8,10 +8,11 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const userAgent = "server:test-api:v0.0.1 (by /u/changelog)"
|
||||
const userAgent = "server:test-api:v0.0.2 (by /u/changelog)"
|
||||
|
||||
type Request struct {
|
||||
body url.Values
|
||||
query url.Values
|
||||
method string
|
||||
token string
|
||||
url string
|
||||
|
@ -21,7 +22,7 @@ type Request struct {
|
|||
type RequestOption func(*Request)
|
||||
|
||||
func NewRequest(opts ...RequestOption) *Request {
|
||||
req := &Request{url.Values{}, "GET", "", "", ""}
|
||||
req := &Request{url.Values{}, url.Values{}, "GET", "", "", ""}
|
||||
for _, opt := range opts {
|
||||
opt(req)
|
||||
}
|
||||
|
@ -31,6 +32,8 @@ func NewRequest(opts ...RequestOption) *Request {
|
|||
|
||||
func (r *Request) HTTPRequest() (*http.Request, error) {
|
||||
req, err := http.NewRequest(r.method, r.url, strings.NewReader(r.body.Encode()))
|
||||
req.URL.RawQuery = r.query.Encode()
|
||||
|
||||
req.Header.Add("User-Agent", userAgent)
|
||||
|
||||
if r.token != "" {
|
||||
|
@ -74,3 +77,9 @@ func WithBody(key, val string) RequestOption {
|
|||
req.body.Set(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
func WithQuery(key, val string) RequestOption {
|
||||
return func(req *Request) {
|
||||
req.query.Set(key, val)
|
||||
}
|
||||
}
|
||||
|
|
34
internal/reddit/types.go
Normal file
34
internal/reddit/types.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package reddit
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Message struct {
|
||||
ID string `json:"id"`
|
||||
Kind string `json:"kind"`
|
||||
Author string `json:"author"`
|
||||
Subject string `json:"subject"`
|
||||
Body string `json:"body"`
|
||||
CreatedAt float64 `json:"created_utc"`
|
||||
}
|
||||
|
||||
type MessageData struct {
|
||||
Message `json:"data"`
|
||||
Kind string `json:"kind"`
|
||||
}
|
||||
|
||||
func (md MessageData) FullName() string {
|
||||
return fmt.Sprintf("%s_%s", md.Kind, md.ID)
|
||||
}
|
||||
|
||||
type MessageListing struct {
|
||||
Messages []MessageData `json:"children"`
|
||||
}
|
||||
|
||||
type MessageListingResponse struct {
|
||||
MessageListing MessageListing `json:"data"`
|
||||
}
|
||||
|
||||
type RefreshTokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username character(20),
|
||||
access_token character(64),
|
||||
refresh_token character(64),
|
||||
username character varying(20),
|
||||
access_token character varying(64),
|
||||
refresh_token character varying(64),
|
||||
expires_at integer,
|
||||
last_message_id character(32),
|
||||
last_message_id character varying(32),
|
||||
device_count integer,
|
||||
last_checked_at integer
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue