400 _, err := mc.getSingleAccount(endPoint, accountID) |
398 _, err := mc.getSingleAccount(endPoint, accountID) |
401 return err |
399 return err |
402 } |
400 } |
403 |
401 |
404 // UpdateAccount updates the connected user's account data |
402 // UpdateAccount updates the connected user's account data |
405 // The fields avatar & headerImage can contain base64-encoded images; if |
403 // |
406 // they do not (that is; if they don't contain ";base64,"), they are considered |
404 // The fields avatar & headerImage are considered as file paths |
407 // as file paths and their content will be encoded. |
405 // and their content will be uploaded. |
|
406 // Please note that currently Mastodon leaks the avatar file name: |
|
407 // https://github.com/tootsuite/mastodon/issues/5776 |
|
408 // |
408 // Setting 'locked' to true means all followers should be approved. |
409 // Setting 'locked' to true means all followers should be approved. |
409 // All fields can be nil, in which case they are not updated. |
410 // All fields can be nil, in which case they are not updated. |
410 // displayName and note can be set to "" to delete previous values; |
411 // displayName and note can be set to "" to delete previous values; |
411 // I'm not sure images can be deleted -- only replaced AFAICS. |
412 // I'm not sure images can be deleted -- only replaced AFAICS. |
412 func (mc *Client) UpdateAccount(displayName, note, avatar, headerImage *string, locked *bool) (*Account, error) { |
413 func (mc *Client) UpdateAccount(displayName, note, avatarImagePath, headerImagePath *string, locked *bool) (*Account, error) { |
413 const endPoint = "accounts/update_credentials" |
414 const endPoint = "accounts/update_credentials" |
414 params := make(apiCallParams) |
415 params := make(apiCallParams) |
415 |
416 |
416 if displayName != nil { |
417 if displayName != nil { |
417 params["display_name"] = *displayName |
418 params["display_name"] = *displayName |
426 params["locked"] = "false" |
427 params["locked"] = "false" |
427 } |
428 } |
428 } |
429 } |
429 |
430 |
430 var err error |
431 var err error |
431 avatar, err = fileToBase64(avatar, nil) |
432 var avatar, headerImage []byte |
432 if err != nil { |
433 |
433 return nil, err |
434 avatar, err = readFile(avatarImagePath) |
434 } |
435 if err != nil { |
435 headerImage, err = fileToBase64(headerImage, nil) |
436 return nil, err |
|
437 } |
|
438 |
|
439 headerImage, err = readFile(headerImagePath) |
436 if err != nil { |
440 if err != nil { |
437 return nil, err |
441 return nil, err |
438 } |
442 } |
439 |
443 |
440 var formBuf bytes.Buffer |
444 var formBuf bytes.Buffer |
441 w := multipart.NewWriter(&formBuf) |
445 w := multipart.NewWriter(&formBuf) |
442 |
446 |
443 if avatar != nil { |
447 if avatar != nil { |
444 w.WriteField("avatar", *avatar) |
448 formWriter, err := w.CreateFormFile("avatar", filepath.Base(*avatarImagePath)) |
|
449 if err != nil { |
|
450 return nil, errors.Wrap(err, "avatar upload") |
|
451 } |
|
452 formWriter.Write(avatar) |
445 } |
453 } |
446 if headerImage != nil { |
454 if headerImage != nil { |
447 w.WriteField("header", *headerImage) |
455 formWriter, err := w.CreateFormFile("header", filepath.Base(*headerImagePath)) |
|
456 if err != nil { |
|
457 return nil, errors.Wrap(err, "header upload") |
|
458 } |
|
459 formWriter.Write(headerImage) |
448 } |
460 } |
449 w.Close() |
461 w.Close() |
450 |
462 |
451 // Prepare the request |
463 // Prepare the request |
452 req, err := mc.prepareRequest(endPoint, rest.Patch, params) |
464 req, err := mc.prepareRequest(endPoint, rest.Patch, params) |
477 return nil, errors.Wrap(err, "cannot decode API response") |
489 return nil, errors.Wrap(err, "cannot decode API response") |
478 } |
490 } |
479 return &account, nil |
491 return &account, nil |
480 } |
492 } |
481 |
493 |
482 // fileToBase64 is a helper function to convert a file's contents to |
494 // readFile is a helper function to read a file's contents. |
483 // base64-encoded data. Is the data string already contains base64 data, it |
495 func readFile(filename *string) ([]byte, error) { |
484 // is not modified. |
496 if filename == nil || *filename == "" { |
485 // If contentType is nil, it is detected. |
|
486 func fileToBase64(data, contentType *string) (*string, error) { |
|
487 if data == nil { |
|
488 return nil, nil |
497 return nil, nil |
489 } |
498 } |
490 |
499 |
491 if *data == "" { |
500 file, err := os.Open(*filename) |
492 return data, nil |
|
493 } |
|
494 |
|
495 if strings.Contains(*data, ";base64,") { |
|
496 return data, nil |
|
497 } |
|
498 |
|
499 // We need to convert the file and file name to base64 |
|
500 |
|
501 file, err := os.Open(*data) |
|
502 if err != nil { |
501 if err != nil { |
503 return nil, err |
502 return nil, err |
504 } |
503 } |
505 defer file.Close() |
504 defer file.Close() |
506 |
505 |