-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfacebook_conroller.go
190 lines (164 loc) · 6.06 KB
/
facebook_conroller.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Not unit tested because there are only two calls to the fb SDK library. The code is primarily
// parsing logic around the custom types that are returned from the dependency. Would
// have to implement fake interfaces for any functionality called on fb; paging and result.
package main
import (
"fmt"
"github.com/JoeSelvik/hdm-service/models"
fb "github.com/huandu/facebook"
"log"
"net/http"
"strconv"
"time"
)
type Facebooker interface {
PullContendersFromFb() ([]*models.Contender, *ApplicationError)
PullPostsFromFb() ([]*models.Post, *ApplicationError)
}
type FacebookHandle struct {
config *Configuration
}
// getFbSession returns the pointer to a fb Session object.
//
// Panics if interacting with FB fails.
func (fh FacebookHandle) getFbSession() *fb.Session {
// "your-app-id", "your-app-secret", from 'development' app I made
var globalApp = fb.New("756979584457445", "023c1d8f5e901c2111d7d136f5165b2a")
session := globalApp.Session(fh.config.FbAccessToken)
err := session.Validate()
if err != nil {
panic(err)
}
return session
}
// PullContendersFromFb returns a slice of pointers to Contenders from a FB group set in the configuration.
func (fh FacebookHandle) PullContendersFromFb() ([]*models.Contender, *ApplicationError) {
// request members via fb graph api, response is a map[string]interface{} fb.Result
response, err := fb.Get(fmt.Sprintf("/%d/members", fh.config.FbGroupId), fb.Params{
"access_token": fh.config.FbAccessToken,
"fields": []string{"name", "id"},
})
if err != nil {
msg := "Failed to get group members from fb"
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
// get the member's paging object
session := fh.getFbSession()
paging, err := response.Paging(session)
if err != nil {
msg := "Failed to page on the group members response"
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
// create each Contender from the facebook response
var contenders []*models.Contender
for {
results := paging.Data()
// ie - map[administrator:false name:Jack White id:6666666666666666]
for i := 0; i < len(results); i++ {
var c models.Contender
facebookContender := fb.Result(results[i]) // cast the var
// convert interface to its real string value, then the string to an int.
id, err := strconv.Atoi(facebookContender.Get("id").(string))
if err != nil {
msg := "Failed to convert fb contenders id to a string"
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
c.FbId = id
c.FbGroupId = fh.config.FbGroupId
c.Name = facebookContender.Get("name").(string)
contenders = append(contenders, &c)
}
noMore, err := paging.Next()
if err != nil {
msg := "Failed to get next paging object for members"
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
if noMore {
break
}
}
return contenders, nil
}
// PullPostsFromFb returns a slice of Post pointers from the configured group feed up to a given date.
func (fh FacebookHandle) PullPostsFromFb() ([]*models.Post, *ApplicationError) {
// request group feed, response is a map[string]interface{} fb.Result
response, err := fb.Get(fmt.Sprintf("/%d/feed", fh.config.FbGroupId), fb.Params{
"access_token": fh.config.FbAccessToken,
"fields": []string{"from", "created_time", "likes"},
})
if err != nil {
msg := "Failed to get group feed"
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
// get the feed's paging object
session := fh.getFbSession()
paging, err := response.Paging(session)
if err != nil {
msg := "Failed to page on the group feed response"
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
// create each Post from the facebook response
var posts []*models.Post
// loop until a fb post's created_time is older than config.StartTime
// todo: clean up messy code
Loop:
for {
results := paging.Data()
// 25 posts per page, load data into a Post struct
for i := 0; i < len(results); i++ {
var p models.Post
facebookPost := fb.Result(results[i]) // cast the var
// parse post's created_time
t, err := time.Parse(GoTimeLayout, facebookPost.Get("created_time").(string))
if err != nil {
msg := "Failed to parse a fb post's postedDate"
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
// continue until post is after EndTime
if t.After(fh.config.EndTime) {
continue
}
// stop when post reaches startDate
if t.Before(fh.config.StartTime) {
break Loop
}
// convert interface to it's real string value, then the string to an int.
id, err := strconv.Atoi(facebookPost.Get("from.id").(string))
if err != nil {
msg := fmt.Sprintf("Failed to convert fb contenders id to a string: %s", err)
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
p.FbId = facebookPost.Get("id").(string) // a post's id has an underscore, 111_222
p.FbGroupId = fh.config.FbGroupId
p.AuthorFbId = id
p.PostedDate = t
// extract fb_ids of contenders who liked post
if facebookPost.Get("likes.data") != nil {
postLikes := facebookPost.Get("likes.data").([]interface{})
for _, l := range postLikes {
// Convert interface to its real string value, then the string to an int.
lid, err := strconv.Atoi(l.(map[string]interface{})["id"].(string))
if err != nil {
msg := "Failed to convert a posts liker id to an int"
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
p.Likes = append(p.Likes, lid)
}
} else {
p.Likes = []int{}
}
// save the new Post
posts = append(posts, &p)
}
noMore, err := paging.Next()
if err != nil {
msg := "Failed to get next paging object for posts"
return nil, &ApplicationError{Msg: msg, Err: err, Code: http.StatusInternalServerError}
}
if noMore {
log.Println("Reached the end of group feed")
break Loop
}
}
return posts, nil
}