#include "client.h" #include #include #include #include #include #include #include "request.h" namespace twitter { client::client( const auth& _arg) : auth_(_arg), currentUser_( get(auth_, "https://api.twitter.com/1.1/account/verify_credentials.json") .perform()) { } tweet client::updateStatus(std::string msg, std::list media_ids) const { std::stringstream datastrstream; datastrstream << "status=" << OAuth::PercentEncode(msg); if (!media_ids.empty()) { datastrstream << "&media_ids="; datastrstream << hatkirby::implode(std::begin(media_ids), std::end(media_ids), ","); } return tweet( post(auth_, "https://api.twitter.com/1.1/statuses/update.json", datastrstream.str()) .perform()); } tweet client::replyToTweet(std::string msg, tweet_id in_response_to, std::list media_ids) const { std::stringstream datastrstream; datastrstream << "status=" << OAuth::PercentEncode(msg); datastrstream << "&in_reply_to_status_id="; datastrstream << in_response_to; if (!media_ids.empty()) { datastrstream << "&media_ids="; datastrstream << hatkirby::implode(std::begin(media_ids), std::end(media_ids), ","); } return tweet( post(auth_, "https://api.twitter.com/1.1/statuses/update.json", datastrstream.str()) .perform()); } tweet client::replyToTweet(std::string msg, const tweet& in_response_to, std::list media_ids) const { return replyToTweet(msg, in_response_to.getID(), media_ids); } long client::uploadMedia(std::string media_type, const char* data, long data_length) const try { curl::curl_form form; std::string str_data_length = std::to_string(data_length); curl::curl_pair command_name(CURLFORM_COPYNAME, "command"); curl::curl_pair command_cont(CURLFORM_COPYCONTENTS, "INIT"); curl::curl_pair bytes_name(CURLFORM_COPYNAME, "total_bytes"); curl::curl_pair bytes_cont(CURLFORM_COPYCONTENTS, str_data_length); curl::curl_pair type_name(CURLFORM_COPYNAME, "media_type"); curl::curl_pair type_cont(CURLFORM_COPYCONTENTS, media_type); form.add(command_name, command_cont); form.add(bytes_name, bytes_cont); form.add(type_name, type_cont); if (media_type == "image/gif") { curl::curl_pair category_name(CURLFORM_COPYNAME, "media_category"); curl::curl_pair category_cont(CURLFORM_COPYCONTENTS, "tweet_gif"); form.add(category_name, category_cont); } std::string init_response = multipost(auth_, "https://upload.twitter.com/1.1/media/upload.json", form.get()) .perform(); long media_id; try { nlohmann::json response_json = nlohmann::json::parse(init_response); media_id = response_json["media_id"].get(); } catch (const std::invalid_argument& error) { std::throw_with_nested(invalid_response(init_response)); } catch (const std::domain_error& error) { std::throw_with_nested(invalid_response(init_response)); } // TODO: Currently have to use the C libcurl API to create this form because it uses a buffer and // libcurlcpp currently messes that up. curl_httppost* append_form_post = nullptr; curl_httppost* append_form_last = nullptr; if ( curl_formadd(&append_form_post, &append_form_last, CURLFORM_COPYNAME, "command", CURLFORM_COPYCONTENTS, "APPEND", CURLFORM_END) || curl_formadd(&append_form_post, &append_form_last, CURLFORM_COPYNAME, "media_id", CURLFORM_COPYCONTENTS, std::to_string(media_id).c_str(), CURLFORM_END) || curl_formadd(&append_form_post, &append_form_last, CURLFORM_COPYNAME, "media", CURLFORM_BUFFER, "media", CURLFORM_BUFFERPTR, data, CURLFORM_BUFFERLENGTH, data_length, CURLFORM_CONTENTTYPE, "application/octet-stream", CURLFORM_END) || curl_formadd(&append_form_post, &append_form_last, CURLFORM_COPYNAME, "segment_index", CURLFORM_COPYCONTENTS, std::to_string(0).c_str(), CURLFORM_END)) { assert(false); } multipost(auth_, "https://upload.twitter.com/1.1/media/upload.json", append_form_post).perform(); curl_formfree(append_form_post); curl::curl_form finalize_form; std::string str_media_id = std::to_string(media_id); curl::curl_pair command3_name(CURLFORM_COPYNAME, "command"); curl::curl_pair command3_cont(CURLFORM_COPYCONTENTS, "FINALIZE"); curl::curl_pair media_id_name(CURLFORM_COPYNAME, "media_id"); curl::curl_pair media_id_cont(CURLFORM_COPYCONTENTS, str_media_id); finalize_form.add(command3_name, command3_cont); finalize_form.add(media_id_name, media_id_cont); std::string finalize_response = multipost(auth_, "https://upload.twitter.com/1.1/media/upload.json", finalize_form.get()) .perform(); nlohmann::json finalize_json; try { finalize_json = nlohmann::json::parse(finalize_response); } catch (const std::invalid_argument& error) { std::throw_with_nested(invalid_response(finalize_response)); } if (finalize_json.find("processing_info") != finalize_json.end()) { std::stringstream datastr; datastr << "https://upload.twitter.com/1.1/media/upload.json?command=STATUS&media_id=" << media_id; for (;;) { std::string status_response = get(auth_, datastr.str()).perform(); try { nlohmann::json status_json = nlohmann::json::parse(status_response); std::string state = status_json["processing_info"]["state"].get(); if (state == "succeeded") { break; } int ttw = status_json["processing_info"]["check_after_secs"].get(); std::this_thread::sleep_for(std::chrono::seconds(ttw)); } catch (const std::invalid_argument& error) { std::throw_with_nested(invalid_response(status_response)); } catch (const std::domain_error& error) { std::throw_with_nested(invalid_response(status_response)); } } } return media_id; } catch (const curl::curl_exception& error) { std::throw_with_nested(co
name: "West Side"
panel_display_name: "West Side"
panels {
  name: "ERASE"
  path: "Panels/General/entry_9"
  clue: "erase"
  answer: "write"
  symbols: SUN
}
panels {
  name: "TEACH"
  path: "Panels/Magnet Room/magnet_1"
  clue: "teach"
  answer: "learn"
  symbols: SUN
}
panels {
  name: "RIGHT"
  path: "Panels/Impressive/entry_1"
  clue: "right"
  answer: "wrong"
  symbols: SUN
}
panels {
  name: "SAVORY"
  path: "Panels/Favors/favors_1"
  clue: "savory"
  answer: "unsavory"
  symbols: SUN
}
panels {
  name: "FLAVORS"
  path: "Panels/Favors/favors_2"
  clue: "flavors"
  answer: "favors"
  symbols: SPARKLES
}
panels {
  name: "COLOR"
  path: "Panels/General/entry_5"
  clue: "color"
  answer: "red"
  symbols: EXAMPLE
}
panels {
  name: "CLUE"
  path: "Panels/General/entry_3"
  clue: "clue"
  answer: "hint"
  symbols: SUN
}
letters {
  key: "a"
  path: "Components/Collectables/a"
}
paintings {
  name: "EXTRAVAGANT"
  path: "Components/Paintings/unless"
  orientation: "north"
  move: true
  enter_only: true
  required_door { name: "Savory Painting" }
}
ports {
  name: "IMPRESSIVE"
  display_name: "Impressive Entrance"
  path: "Meshes/Blocks/Warps/worldport2"
  destination { x: -34 y: 0 z: 22.5 }
  rotation: 0
}
ports {
  name: "CC"
  display_name: "Control Center"
  path: "Meshes/Blocks/Warps/worldport9"
  destination { x: -59 y: 0 z: -50.5 }
  rotation: 180
}
ports {
  name: "PARTIAL"
  display_name: "Red Hallway"
  path: "Meshes/Blocks/Warps/worldport7"
  destination { x: -62 y: 0 z: 11 }
  rotation: 90
  # ER with this is weird; make sure to place on the surface
  required_door { name: "Partial Entrance" }
}