aplus

A+Attendance CLI utility via Instructure Canvas
Log | Files | Refs | README | LICENSE

commit 4af67b036396ca952b85b93b4d4bc371cd7bfcd9
parent 410a8c7f1866f30ff958eb102c8c0fcad2cf64ab
Author: Juan Roig <jaroig@outlook.com>
Date:   Sat,  4 Nov 2023 01:55:30 -0400

Moved functional code to funcs.go

Diffstat:
Afuncs.go | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmain.go | 218++++++++-----------------------------------------------------------------------
2 files changed, 150 insertions(+), 196 deletions(-)

diff --git a/funcs.go b/funcs.go @@ -0,0 +1,128 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +/* +func get_courses(token string, link string, client http.Client) []CanvasCourse { + resp, err := client.Get(link) + if err != nil { + fmt.Println("Error performing GET request to get courses.") + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + + if err != nil { + fmt.Println("Error trying to read html body for courses.") + } + + var canvas_courses []CanvasCourse + err2 := json.Unmarshal(body, &canvas_courses) + if err2 != nil { + fmt.Println(err2) + return nil + } + + return canvas_courses +} + +// Need to change this to grab both ID and name, return as pairs. +func get_course_id_name_pair(canvas_courses []CanvasCourse) []uint64 { + var courses []uint64 + + for _, course := range canvas_courses { + courses = append(courses, course.ID%10000) + } + return courses +} + +func select_course() int { + // course selection by user happens below + //favorites := fmt.Sprintf("%s/users/self/favorites/courses?access_token=%s&per_page=100", base_link, token) + //all_courses := fmt.Sprintf("%s/courses?access_token=%s&per_page=100", base_link,token) + // var canvas_courses []CanvasCourse + //canvas_courses = get_courses(token, favorites, client) + //var course_ids []uint64 + //course_ids = get_course_ids(canvas_courses) + //selected := 1 // course_ids[selected] + // course selection by user happens above + return 0 +} +*/ + +func get_aplus(token string, link string, client http.Client) string { + resp, err := client.Get(link) + + if err != nil { + fmt.Println("Error performing GET request to initial link.") + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + + if err != nil { + fmt.Println("Error reading body of initial aplus html response " + + "which is supposed to contain a one-time link for getting the form.") + } + + var aplus Aplus + json.Unmarshal(body, &aplus) + + return aplus.URL +} + +func get_form_from_request_body(req_body []byte) string { + body_str := string(req_body) + form_start := strings.Index(body_str, "<form") + form_end := strings.Index(body_str, "</form>") + 7 + form_html := req_body[form_start:form_end] + + return string(form_html) +} + +// parse_form extracts form fields and values from the given HTML form string. +func parse_form(form_html string) url.Values { + form_values := make(url.Values) + inputs := strings.Split(form_html, "<input") + + for _, input := range inputs { + // Extract field name and value + name := extract_attribute(input, "name") + value := extract_attribute(input, "value") + + if name != "" { + form_values.Add(name, value) + } + } + + return form_values +} + +func extract_attribute(input string, attribute string) string { + start := strings.Index(input, attribute+"=\"") + if start == -1 { + start = strings.Index(input, attribute+"='") + } + + if start == -1 { + return "" + } + + start += len(attribute) + 2 + + end := strings.Index(input[start:], "\"") + if end == -1 { + end = strings.Index(input[start:], "'") + } + + if end == -1 { + return "" + } + + return input[start : start+end] +} diff --git a/main.go b/main.go @@ -1,97 +1,12 @@ package main import ( - "encoding/json" "fmt" "io" "net/http" - "net/url" - "strings" + "os" ) -func get_courses(token string, link string, client http.Client) []CanvasCourse { - resp, err := client.Get(link) - if err != nil { - // handle error - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - - var canvas_courses []CanvasCourse - err2 := json.Unmarshal(body, &canvas_courses) - if err2 != nil { - fmt.Println(err2) - return nil - } - - return canvas_courses -} - -func get_aplus(token string, link string, client http.Client) string { - resp, err := client.Get(link) - - if err != nil { - // handle error - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - var aplus Aplus - json.Unmarshal(body, &aplus) - - return aplus.URL -} - -func get_course_ids(canvas_courses []CanvasCourse) []uint64 { - var courses []uint64 - - for _, course := range canvas_courses { - courses = append(courses, course.ID%10000) - } - return courses -} - -// parse_form extracts form fields and values from the given HTML form string. -func parse_form(form_html string) url.Values { - form_values := make(url.Values) - inputs := strings.Split(form_html, "<input") - - for _, input := range inputs { - // Extract field name and value - name := extract_attribute(input, "name") - value := extract_attribute(input, "value") - - if name != "" { - form_values.Add(name, value) - } - } - - return form_values -} - -func extract_attribute(input string, attribute string) string { - start := strings.Index(input, attribute+"=\"") - if start == -1 { - start = strings.Index(input, attribute+"='") - } - - if start == -1 { - return "" - } - - start += len(attribute) + 2 - - end := strings.Index(input[start:], "\"") - if end == -1 { - end = strings.Index(input[start:], "'") - } - - if end == -1 { - return "" - } - - return input[start : start+end] -} - func main() { client := http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { @@ -100,145 +15,56 @@ func main() { }, } - // https://canvas.instructure.com/api/v1/courses?access_token={}&per_page=100", canvas_token base_link := "https://floridapolytechnic.instructure.com/api/v1" - token := "token" - - // course selection by user happens below - //favorites := fmt.Sprintf("%s/users/self/favorites/courses?access_token=%s&per_page=100", base_link, token) - //all_courses := fmt.Sprintf("%s/courses?access_token=%s&per_page=100", base_link,token) - //var canvas_courses []CanvasCourse - //canvas_courses = get_courses(token, favorites, client) - //var course_ids []uint64 - //course_ids = get_course_ids(canvas_courses) - //selected := 1 // course_ids[selected] - // course selection by user happens above + token := os.Getenv("CANVAS_API_KEY") + + // select_course() aplus_link := fmt.Sprintf("%s/courses/%d/external_tools/sessionless_launch?id=913&access_token=%s", base_link, 7329, token) // 7329 always works - var aplus string - aplus = get_aplus(token, aplus_link, client) + aplus := get_aplus(token, aplus_link, client) fmt.Println(aplus) resp, err := client.Get(aplus) if err != nil { - // handle error + fmt.Println("Error while performing GET request to aplus auth link.") } - //fmt.Println(resp) - defer resp.Body.Close() body, err := io.ReadAll(resp.Body) - //fmt.Println(string(body)) - //fmt.Println(resp) - //fmt.Println("Status code:", resp.StatusCode) - if err != nil { - // handle error + fmt.Println("Error reading body of initial aplus html response.") } - body_str := string(body) - - form_start := strings.Index(body_str, "<form") - form_end := strings.Index(body_str, "</form>") + 7 - form_html := body[form_start:form_end] + form_str := get_form_from_request_body(body) + fmt.Println(form_str) - form := string(form_html) - fmt.Println(form) - - form_values := parse_form(form) + form_values := parse_form(form_str) fmt.Println("Form Values:") for key, values := range form_values { fmt.Printf("%s: %s\n", key, values) } - //req, err := http.PostForm("https://floridapoly.aplusattendance.com/canvas", form_values) req, err := client.PostForm("https://floridapoly.aplusattendance.com/canvas", form_values) - body, err = io.ReadAll(req.Body) - - fmt.Println(string(body)) - fmt.Println(req) -} -/* - -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. -what I have to do is programmatically grab that form, translate it on the fly, and submit the request rather than doing this manually. -// Define the form data as a map of key-value pairs. - formData := map[string]string{ - "oauth_consumer_key": "aplus-attendance", - "oauth_signature_method": "HMAC-SHA1", - "oauth_timestamp": "1698650339", - "oauth_nonce": "nV9EswFv8LtXXRVgRmWW8NSQ7mO3Sheeqi1SjJWPJGE", - "oauth_version": "1.0", - "context_id": "0c1f49ff7b35cba2226f93e9cbb84ae810ed00ab", - "context_label": "CIS4367.01I&T", - "context_title": "Computer Security(FA 2023_CIS4367.01 I&T", - "custom_canvas_api_domain": "floridapolytechnic.instructure.com", - "custom_canvas_course_id": "7329", - "custom_canvas_enrollment_state": "active", - "custom_canvas_user_id": "6858", - "custom_canvas_user_login_id": "jroig9590", - "custom_canvas_workflow_state": "available", - "ext_roles": "urn:lti:instrole:ims/lis/Student,urn:lti:role:ims/lis/Learner,urn:lti:sysrole:ims/lis/User", - "launch_presentation_document_target": "iframe", - "launch_presentation_locale": "en", - "launch_presentation_return_url": "https://floridapolytechnic.instructure.com/courses/7329", - "lis_course_offering_sourcedid": "7093", - "lis_person_contact_email_primary": "jroig9590@floridapoly.edu", - "lis_person_name_family": "Roig", - "lis_person_name_full": "Juan Roig", - "lis_person_name_given": "Juan", - "lis_person_sourcedid": "10308", - "lti_message_type": "basic-lti-launch-request", - "lti_version": "LTI-1p0", - "oauth_callback": "about:blank", - "resource_link_id": "0c1f49ff7b35cba2226f93e9cbb84ae810ed00ab", - "resource_link_title": "aPlus+ Attendance", - "roles": "Learner", - "tool_consumer_info_product_family_code": "canvas", - "tool_consumer_info_version": "cloud", - "tool_consumer_instance_contact_email": "notifications@instructure.com", - "tool_consumer_instance_guid": "7db438071375c02373713c12c73869ff2f470b68.floridapolytechnic.instructure.com", - "tool_consumer_instance_name": "Florida Polytechnic University", - "user_id": "91ba5b390440f646d9d4c47a0dc14f91cf595887", - "user_image": "https://canvas.instructure.com/images/messages/avatar-50.png", - "oauth_signature": "u3mIfUZ+qgSN4zzk0Ipklc0A7KU=", + if err != nil { + fmt.Println("Error posting form to Aplus Attendance.") } - // Create an HTTP client - client := &http.Client{} + body, err = io.ReadAll(req.Body) - // Create a new request - req, err := http.NewRequest("POST", "https://floridapoly.aplusattendance.com/canvas", bytes.NewBuffer([]byte{})) if err != nil { - fmt.Println("Error creating the request:", err) - return - } - - // Add the form data as URL-encoded values to the request - q := req.URL.Query() - for key, value := range formData { - q.Add(key, value) + fmt.Println("Error reading body of post form response from aplus attendance.") } - req.URL.RawQuery = q.Encode() - // Set the appropriate content type for the form - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + fmt.Println(string(body)) + fmt.Println(req) - // Perform the POST request - resp, err := client.Do(req) - if err != nil { - fmt.Println("Error sending the POST request:", err) - return - } - defer resp.Body.Close() + // we need to submit the above post request correctly in order to submit this request. + //resp, err = client.Get("https://floridapoly.aplusattendance.com/canvas/student/?canvasCourse=7329&doStudentAuth=true") + //body, err = io.ReadAll(req.Body) - // Check the response - if resp.StatusCode == http.StatusOK { - fmt.Println("Request successful. You may need to parse and process the response body if applicable.") - } else { - fmt.Println("Request failed with status code:", resp.Status) - } -*/ + //fmt.Println(string(body)) + //fmt.Println(req) +}