diff options
| -rw-r--r-- | funcs.go | 128 | ||||
| -rw-r--r-- | main.go | 218 |
2 files changed, 150 insertions, 196 deletions
diff --git a/funcs.go b/funcs.go new file mode 100644 index 0000000..5e334ff --- /dev/null +++ b/funcs.go | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | package main | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "encoding/json" | ||
| 5 | "fmt" | ||
| 6 | "io" | ||
| 7 | "net/http" | ||
| 8 | "net/url" | ||
| 9 | "strings" | ||
| 10 | ) | ||
| 11 | |||
| 12 | /* | ||
| 13 | func get_courses(token string, link string, client http.Client) []CanvasCourse { | ||
| 14 | resp, err := client.Get(link) | ||
| 15 | if err != nil { | ||
| 16 | fmt.Println("Error performing GET request to get courses.") | ||
| 17 | } | ||
| 18 | defer resp.Body.Close() | ||
| 19 | body, err := io.ReadAll(resp.Body) | ||
| 20 | |||
| 21 | if err != nil { | ||
| 22 | fmt.Println("Error trying to read html body for courses.") | ||
| 23 | } | ||
| 24 | |||
| 25 | var canvas_courses []CanvasCourse | ||
| 26 | err2 := json.Unmarshal(body, &canvas_courses) | ||
| 27 | if err2 != nil { | ||
| 28 | fmt.Println(err2) | ||
| 29 | return nil | ||
| 30 | } | ||
| 31 | |||
| 32 | return canvas_courses | ||
| 33 | } | ||
| 34 | |||
| 35 | // Need to change this to grab both ID and name, return as pairs. | ||
| 36 | func get_course_id_name_pair(canvas_courses []CanvasCourse) []uint64 { | ||
| 37 | var courses []uint64 | ||
| 38 | |||
| 39 | for _, course := range canvas_courses { | ||
| 40 | courses = append(courses, course.ID%10000) | ||
| 41 | } | ||
| 42 | return courses | ||
| 43 | } | ||
| 44 | |||
| 45 | func select_course() int { | ||
| 46 | // course selection by user happens below | ||
| 47 | //favorites := fmt.Sprintf("%s/users/self/favorites/courses?access_token=%s&per_page=100", base_link, token) | ||
| 48 | //all_courses := fmt.Sprintf("%s/courses?access_token=%s&per_page=100", base_link,token) | ||
| 49 | // var canvas_courses []CanvasCourse | ||
| 50 | //canvas_courses = get_courses(token, favorites, client) | ||
| 51 | //var course_ids []uint64 | ||
| 52 | //course_ids = get_course_ids(canvas_courses) | ||
| 53 | //selected := 1 // course_ids[selected] | ||
| 54 | // course selection by user happens above | ||
| 55 | return 0 | ||
| 56 | } | ||
| 57 | */ | ||
| 58 | |||
| 59 | func get_aplus(token string, link string, client http.Client) string { | ||
| 60 | resp, err := client.Get(link) | ||
| 61 | |||
| 62 | if err != nil { | ||
| 63 | fmt.Println("Error performing GET request to initial link.") | ||
| 64 | } | ||
| 65 | defer resp.Body.Close() | ||
| 66 | body, err := io.ReadAll(resp.Body) | ||
| 67 | |||
| 68 | if err != nil { | ||
| 69 | fmt.Println("Error reading body of initial aplus html response " + | ||
| 70 | "which is supposed to contain a one-time link for getting the form.") | ||
| 71 | } | ||
| 72 | |||
| 73 | var aplus Aplus | ||
| 74 | json.Unmarshal(body, &aplus) | ||
| 75 | |||
| 76 | return aplus.URL | ||
| 77 | } | ||
| 78 | |||
| 79 | func get_form_from_request_body(req_body []byte) string { | ||
| 80 | body_str := string(req_body) | ||
| 81 | form_start := strings.Index(body_str, "<form") | ||
| 82 | form_end := strings.Index(body_str, "</form>") + 7 | ||
| 83 | form_html := req_body[form_start:form_end] | ||
| 84 | |||
| 85 | return string(form_html) | ||
| 86 | } | ||
| 87 | |||
| 88 | // parse_form extracts form fields and values from the given HTML form string. | ||
| 89 | func parse_form(form_html string) url.Values { | ||
| 90 | form_values := make(url.Values) | ||
| 91 | inputs := strings.Split(form_html, "<input") | ||
| 92 | |||
| 93 | for _, input := range inputs { | ||
| 94 | // Extract field name and value | ||
| 95 | name := extract_attribute(input, "name") | ||
| 96 | value := extract_attribute(input, "value") | ||
| 97 | |||
| 98 | if name != "" { | ||
| 99 | form_values.Add(name, value) | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | return form_values | ||
| 104 | } | ||
| 105 | |||
| 106 | func extract_attribute(input string, attribute string) string { | ||
| 107 | start := strings.Index(input, attribute+"=\"") | ||
| 108 | if start == -1 { | ||
| 109 | start = strings.Index(input, attribute+"='") | ||
| 110 | } | ||
| 111 | |||
| 112 | if start == -1 { | ||
| 113 | return "" | ||
| 114 | } | ||
| 115 | |||
| 116 | start += len(attribute) + 2 | ||
| 117 | |||
| 118 | end := strings.Index(input[start:], "\"") | ||
| 119 | if end == -1 { | ||
| 120 | end = strings.Index(input[start:], "'") | ||
| 121 | } | ||
| 122 | |||
| 123 | if end == -1 { | ||
| 124 | return "" | ||
| 125 | } | ||
| 126 | |||
| 127 | return input[start : start+end] | ||
| 128 | } | ||
| @@ -1,97 +1,12 @@ | |||
| 1 | package main | 1 | package main |
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "encoding/json" | ||
| 5 | "fmt" | 4 | "fmt" |
| 6 | "io" | 5 | "io" |
| 7 | "net/http" | 6 | "net/http" |
| 8 | "net/url" | 7 | "os" |
| 9 | "strings" | ||
| 10 | ) | 8 | ) |
| 11 | 9 | ||
| 12 | func get_courses(token string, link string, client http.Client) []CanvasCourse { | ||
| 13 | resp, err := client.Get(link) | ||
| 14 | if err != nil { | ||
| 15 | // handle error | ||
| 16 | } | ||
| 17 | defer resp.Body.Close() | ||
| 18 | body, err := io.ReadAll(resp.Body) | ||
| 19 | |||
| 20 | var canvas_courses []CanvasCourse | ||
| 21 | err2 := json.Unmarshal(body, &canvas_courses) | ||
| 22 | if err2 != nil { | ||
| 23 | fmt.Println(err2) | ||
| 24 | return nil | ||
| 25 | } | ||
| 26 | |||
| 27 | return canvas_courses | ||
| 28 | } | ||
| 29 | |||
| 30 | func get_aplus(token string, link string, client http.Client) string { | ||
| 31 | resp, err := client.Get(link) | ||
| 32 | |||
| 33 | if err != nil { | ||
| 34 | // handle error | ||
| 35 | } | ||
| 36 | defer resp.Body.Close() | ||
| 37 | body, err := io.ReadAll(resp.Body) | ||
| 38 | var aplus Aplus | ||
| 39 | json.Unmarshal(body, &aplus) | ||
| 40 | |||
| 41 | return aplus.URL | ||
| 42 | } | ||
| 43 | |||
| 44 | func get_course_ids(canvas_courses []CanvasCourse) []uint64 { | ||
| 45 | var courses []uint64 | ||
| 46 | |||
| 47 | for _, course := range canvas_courses { | ||
| 48 | courses = append(courses, course.ID%10000) | ||
| 49 | } | ||
| 50 | return courses | ||
| 51 | } | ||
| 52 | |||
| 53 | // parse_form extracts form fields and values from the given HTML form string. | ||
| 54 | func parse_form(form_html string) url.Values { | ||
| 55 | form_values := make(url.Values) | ||
| 56 | inputs := strings.Split(form_html, "<input") | ||
| 57 | |||
| 58 | for _, input := range inputs { | ||
| 59 | // Extract field name and value | ||
| 60 | name := extract_attribute(input, "name") | ||
| 61 | value := extract_attribute(input, "value") | ||
| 62 | |||
| 63 | if name != "" { | ||
| 64 | form_values.Add(name, value) | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | return form_values | ||
| 69 | } | ||
| 70 | |||
| 71 | func extract_attribute(input string, attribute string) string { | ||
| 72 | start := strings.Index(input, attribute+"=\"") | ||
| 73 | if start == -1 { | ||
| 74 | start = strings.Index(input, attribute+"='") | ||
| 75 | } | ||
| 76 | |||
| 77 | if start == -1 { | ||
| 78 | return "" | ||
| 79 | } | ||
| 80 | |||
| 81 | start += len(attribute) + 2 | ||
| 82 | |||
| 83 | end := strings.Index(input[start:], "\"") | ||
| 84 | if end == -1 { | ||
| 85 | end = strings.Index(input[start:], "'") | ||
| 86 | } | ||
| 87 | |||
| 88 | if end == -1 { | ||
| 89 | return "" | ||
| 90 | } | ||
| 91 | |||
| 92 | return input[start : start+end] | ||
| 93 | } | ||
| 94 | |||
| 95 | func main() { | 10 | func main() { |
| 96 | client := http.Client{ | 11 | client := http.Client{ |
| 97 | CheckRedirect: func(req *http.Request, via []*http.Request) error { | 12 | CheckRedirect: func(req *http.Request, via []*http.Request) error { |
| @@ -100,145 +15,56 @@ func main() { | |||
| 100 | }, | 15 | }, |
| 101 | } | 16 | } |
| 102 | 17 | ||
| 103 | // https://canvas.instructure.com/api/v1/courses?access_token={}&per_page=100", canvas_token | ||
| 104 | base_link := "https://floridapolytechnic.instructure.com/api/v1" | 18 | base_link := "https://floridapolytechnic.instructure.com/api/v1" |
| 105 | token := "token" | 19 | token := os.Getenv("CANVAS_API_KEY") |
| 106 | 20 | ||
| 107 | // course selection by user happens below | 21 | // select_course() |
| 108 | //favorites := fmt.Sprintf("%s/users/self/favorites/courses?access_token=%s&per_page=100", base_link, token) | ||
| 109 | //all_courses := fmt.Sprintf("%s/courses?access_token=%s&per_page=100", base_link,token) | ||
| 110 | //var canvas_courses []CanvasCourse | ||
| 111 | //canvas_courses = get_courses(token, favorites, client) | ||
| 112 | //var course_ids []uint64 | ||
| 113 | //course_ids = get_course_ids(canvas_courses) | ||
| 114 | //selected := 1 // course_ids[selected] | ||
| 115 | // course selection by user happens above | ||
| 116 | 22 | ||
| 117 | aplus_link := fmt.Sprintf("%s/courses/%d/external_tools/sessionless_launch?id=913&access_token=%s", base_link, 7329, token) // 7329 always works | 23 | aplus_link := fmt.Sprintf("%s/courses/%d/external_tools/sessionless_launch?id=913&access_token=%s", base_link, 7329, token) // 7329 always works |
| 118 | var aplus string | 24 | aplus := get_aplus(token, aplus_link, client) |
| 119 | aplus = get_aplus(token, aplus_link, client) | ||
| 120 | fmt.Println(aplus) | 25 | fmt.Println(aplus) |
| 121 | resp, err := client.Get(aplus) | 26 | resp, err := client.Get(aplus) |
| 122 | 27 | ||
| 123 | if err != nil { | 28 | if err != nil { |
| 124 | // handle error | 29 | fmt.Println("Error while performing GET request to aplus auth link.") |
| 125 | } | 30 | } |
| 126 | 31 | ||
| 127 | //fmt.Println(resp) | ||
| 128 | |||
| 129 | defer resp.Body.Close() | 32 | defer resp.Body.Close() |
| 130 | body, err := io.ReadAll(resp.Body) | 33 | body, err := io.ReadAll(resp.Body) |
| 131 | 34 | ||
| 132 | //fmt.Println(string(body)) | ||
| 133 | //fmt.Println(resp) | ||
| 134 | //fmt.Println("Status code:", resp.StatusCode) | ||
| 135 | |||
| 136 | if err != nil { | 35 | if err != nil { |
| 137 | // handle error | 36 | fmt.Println("Error reading body of initial aplus html response.") |
| 138 | } | 37 | } |
| 139 | 38 | ||
| 140 | body_str := string(body) | 39 | form_str := get_form_from_request_body(body) |
| 141 | 40 | fmt.Println(form_str) | |
| 142 | form_start := strings.Index(body_str, "<form") | ||
| 143 | form_end := strings.Index(body_str, "</form>") + 7 | ||
| 144 | form_html := body[form_start:form_end] | ||
| 145 | 41 | ||
| 146 | form := string(form_html) | 42 | form_values := parse_form(form_str) |
| 147 | fmt.Println(form) | ||
| 148 | |||
| 149 | form_values := parse_form(form) | ||
| 150 | 43 | ||
| 151 | fmt.Println("Form Values:") | 44 | fmt.Println("Form Values:") |
| 152 | for key, values := range form_values { | 45 | for key, values := range form_values { |
| 153 | fmt.Printf("%s: %s\n", key, values) | 46 | fmt.Printf("%s: %s\n", key, values) |
| 154 | } | 47 | } |
| 155 | 48 | ||
| 156 | //req, err := http.PostForm("https://floridapoly.aplusattendance.com/canvas", form_values) | ||
| 157 | req, err := client.PostForm("https://floridapoly.aplusattendance.com/canvas", form_values) | 49 | req, err := client.PostForm("https://floridapoly.aplusattendance.com/canvas", form_values) |
| 158 | body, err = io.ReadAll(req.Body) | ||
| 159 | |||
| 160 | fmt.Println(string(body)) | ||
| 161 | fmt.Println(req) | ||
| 162 | } | ||
| 163 | 50 | ||
| 164 | /* | 51 | if err != nil { |
| 165 | 52 | fmt.Println("Error posting form to Aplus Attendance.") | |
| 166 | The code below takes the form that defines the post request we must perform to the aplusattendance.com site, and actually maps it to a request. | ||
| 167 | what I have to do is programmatically grab that form, translate it on the fly, and submit the request rather than doing this manually. | ||
| 168 | // Define the form data as a map of key-value pairs. | ||
| 169 | formData := map[string]string{ | ||
| 170 | "oauth_consumer_key": "aplus-attendance", | ||
| 171 | "oauth_signature_method": "HMAC-SHA1", | ||
| 172 | "oauth_timestamp": "1698650339", | ||
| 173 | "oauth_nonce": "nV9EswFv8LtXXRVgRmWW8NSQ7mO3Sheeqi1SjJWPJGE", | ||
| 174 | "oauth_version": "1.0", | ||
| 175 | "context_id": "0c1f49ff7b35cba2226f93e9cbb84ae810ed00ab", | ||
| 176 | "context_label": "CIS4367.01I&T", | ||
| 177 | "context_title": "Computer Security(FA 2023_CIS4367.01 I&T", | ||
| 178 | "custom_canvas_api_domain": "floridapolytechnic.instructure.com", | ||
| 179 | "custom_canvas_course_id": "7329", | ||
| 180 | "custom_canvas_enrollment_state": "active", | ||
| 181 | "custom_canvas_user_id": "6858", | ||
| 182 | "custom_canvas_user_login_id": "jroig9590", | ||
| 183 | "custom_canvas_workflow_state": "available", | ||
| 184 | "ext_roles": "urn:lti:instrole:ims/lis/Student,urn:lti:role:ims/lis/Learner,urn:lti:sysrole:ims/lis/User", | ||
| 185 | "launch_presentation_document_target": "iframe", | ||
| 186 | "launch_presentation_locale": "en", | ||
| 187 | "launch_presentation_return_url": "https://floridapolytechnic.instructure.com/courses/7329", | ||
| 188 | "lis_course_offering_sourcedid": "7093", | ||
| 189 | "lis_person_contact_email_primary": "jroig9590@floridapoly.edu", | ||
| 190 | "lis_person_name_family": "Roig", | ||
| 191 | "lis_person_name_full": "Juan Roig", | ||
| 192 | "lis_person_name_given": "Juan", | ||
| 193 | "lis_person_sourcedid": "10308", | ||
| 194 | "lti_message_type": "basic-lti-launch-request", | ||
| 195 | "lti_version": "LTI-1p0", | ||
| 196 | "oauth_callback": "about:blank", | ||
| 197 | "resource_link_id": "0c1f49ff7b35cba2226f93e9cbb84ae810ed00ab", | ||
| 198 | "resource_link_title": "aPlus+ Attendance", | ||
| 199 | "roles": "Learner", | ||
| 200 | "tool_consumer_info_product_family_code": "canvas", | ||
| 201 | "tool_consumer_info_version": "cloud", | ||
| 202 | "tool_consumer_instance_contact_email": "notifications@instructure.com", | ||
| 203 | "tool_consumer_instance_guid": "7db438071375c02373713c12c73869ff2f470b68.floridapolytechnic.instructure.com", | ||
| 204 | "tool_consumer_instance_name": "Florida Polytechnic University", | ||
| 205 | "user_id": "91ba5b390440f646d9d4c47a0dc14f91cf595887", | ||
| 206 | "user_image": "https://canvas.instructure.com/images/messages/avatar-50.png", | ||
| 207 | "oauth_signature": "u3mIfUZ+qgSN4zzk0Ipklc0A7KU=", | ||
| 208 | } | 53 | } |
| 209 | 54 | ||
| 210 | // Create an HTTP client | 55 | body, err = io.ReadAll(req.Body) |
| 211 | client := &http.Client{} | ||
| 212 | 56 | ||
| 213 | // Create a new request | ||
| 214 | req, err := http.NewRequest("POST", "https://floridapoly.aplusattendance.com/canvas", bytes.NewBuffer([]byte{})) | ||
| 215 | if err != nil { | 57 | if err != nil { |
| 216 | fmt.Println("Error creating the request:", err) | 58 | fmt.Println("Error reading body of post form response from aplus attendance.") |
| 217 | return | ||
| 218 | } | ||
| 219 | |||
| 220 | // Add the form data as URL-encoded values to the request | ||
| 221 | q := req.URL.Query() | ||
| 222 | for key, value := range formData { | ||
| 223 | q.Add(key, value) | ||
| 224 | } | 59 | } |
| 225 | req.URL.RawQuery = q.Encode() | ||
| 226 | 60 | ||
| 227 | // Set the appropriate content type for the form | 61 | fmt.Println(string(body)) |
| 228 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") | 62 | fmt.Println(req) |
| 229 | 63 | ||
| 230 | // Perform the POST request | 64 | // we need to submit the above post request correctly in order to submit this request. |
| 231 | resp, err := client.Do(req) | 65 | //resp, err = client.Get("https://floridapoly.aplusattendance.com/canvas/student/?canvasCourse=7329&doStudentAuth=true") |
| 232 | if err != nil { | 66 | //body, err = io.ReadAll(req.Body) |
| 233 | fmt.Println("Error sending the POST request:", err) | ||
| 234 | return | ||
| 235 | } | ||
| 236 | defer resp.Body.Close() | ||
| 237 | 67 | ||
| 238 | // Check the response | 68 | //fmt.Println(string(body)) |
| 239 | if resp.StatusCode == http.StatusOK { | 69 | //fmt.Println(req) |
| 240 | fmt.Println("Request successful. You may need to parse and process the response body if applicable.") | 70 | } |
| 241 | } else { | ||
| 242 | fmt.Println("Request failed with status code:", resp.Status) | ||
| 243 | } | ||
| 244 | */ | ||
