void EventEditorDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { const Event& event = m_model->eventForIndex( index ); Q_ASSERT( event.isValid() ); const TaskTreeItem& item = DATAMODEL->taskTreeItem( event.taskId() ); if ( event.isValid() ) { bool locked = DATAMODEL->isEventActive( event.id() ); QString dateAndDuration; QTextStream dateStream( &dateAndDuration ); QDate date = event.startDateTime().date(); QTime time = event.startDateTime().time(); QTime endTime = event.endDateTime().time(); dateStream << date.toString( Qt::SystemLocaleDate ) << " " << time.toString( "h:mm" ) << " - " << endTime.toString( "h:mm" ) << " (" << hoursAndMinutes( event.duration() ) << ") Week " << date.weekNumber(); QString taskName; QTextStream taskStream( &taskName ); // print leading zeroes for the TaskId const int taskIdLength = CONFIGURATION.taskPaddingLength; taskStream << QString( "%1" ).arg( item.task().id(), taskIdLength, 10, QChar( '0' ) ) << " " << DATAMODEL->smartTaskName( item.task() ); paint( painter, option, taskName, dateAndDuration, logDuration( event.duration() ), locked ? EventState_Locked : EventState_Default ); } }
Response BaseDiscordClient::request(const RequestMethod method, Route path, const std::string jsonParameters/*, cpr::Parameters httpParameters*/, const std::initializer_list<Part>& multipartParameters) { //check if rate limited Response response; const time_t currentTime = getEpochTimeMillisecond(); if (isGlobalRateLimited) { if (nextRetry <= currentTime) { isGlobalRateLimited = false; } else { onExceededRateLimit(isGlobalRateLimited, nextRetry - currentTime, { *this, method, path, jsonParameters, multipartParameters }); response.statusCode = TOO_MANY_REQUESTS; setError(response.statusCode); return response; } } const std::string bucket = path.bucket(method); auto bucketResetTimestamp = buckets.find(bucket); if (bucketResetTimestamp != buckets.end()) { if (bucketResetTimestamp->second <= currentTime) { buckets.erase(bucketResetTimestamp); } else { onExceededRateLimit(false, bucketResetTimestamp->second - currentTime, { *this, method, path, jsonParameters, multipartParameters }); response.statusCode = TOO_MANY_REQUESTS; setError(response.statusCode); return response; } } { //the { is used so that onResponse is called after session is removed to make debugging performance issues easier //request starts here Session session; session.setUrl("https://discordapp.com/api/v6/" + path.url()); std::vector<HeaderPair> header = { { "Authorization", bot ? "Bot " + getToken() : getToken() }, { "User-Agent", "DiscordBot (https://github.com/yourWaifu/SleepyDiscord, vtheBestVersion)" }, }; if (jsonParameters != "") { session.setBody(&jsonParameters); header.push_back({ "Content-Type" , "application/json" }); header.push_back({ "Content-Length", std::to_string(jsonParameters.length()) }); //} else if (httpParameters.content != "") { //this is broken for now // session.SetParameters(httpParameters); } else if (0 < multipartParameters.size()) { session.setMultipart(multipartParameters); header.push_back({ "Content-Type", "multipart/form-data" }); } else { header.push_back({ "Content-Length", "0" }); } session.setHeader(header); //Response response; switch (method) { case Post: response = session.Post(); break; case Patch: response = session.Patch(); break; case Delete: response = session.Delete(); break; case Get: response = session.Get(); break; case Put: response = session.Put(); break; default: response.statusCode = BAD_REQUEST; break; //unexpected method } //status checking switch (response.statusCode) { case OK: case CREATED: case NO_CONTENT: case NOT_MODIFIED: break; case TOO_MANY_REQUESTS: { //this should fall down to default int retryAfter = std::stoi(response.header["Retry-After"]); isGlobalRateLimited = response.header["X-RateLimit-Global"] == "true"; nextRetry = getEpochTimeMillisecond() + retryAfter; if (!isGlobalRateLimited) buckets[bucket] = nextRetry; onExceededRateLimit(isGlobalRateLimited, retryAfter, { *this, method, path, jsonParameters, multipartParameters }); } default: { //error const ErrorCode code = static_cast<ErrorCode>(response.statusCode); setError(code); //https error std::vector<std::string> values = json::getValues(response.text.c_str(), { "code", "message" }); //parse json to get code and message if (!values.empty() && values[0] != "") onError(static_cast<ErrorCode>(std::stoi(values[0])), values[1]); //send message to the error event else onError(ERROR_NOTE, response.text); #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) throw code; #endif } break; } //rate limit check if (response.header["X-RateLimit-Remaining"] == "0" && response.statusCode != TOO_MANY_REQUESTS) { std::tm date = {}; //for some reason std::get_time requires gcc 5 std::istringstream dateStream(response.header["Date"]); dateStream >> std::get_time(&date, "%a, %d %b %Y %H:%M:%S GMT"); const time_t reset = std::stoi(response.header["X-RateLimit-Reset"]); #if defined(_WIN32) || defined(_WIN64) std::tm gmTM; std::tm*const resetGM = &gmTM; gmtime_s(resetGM, &reset); #else std::tm* resetGM = std::gmtime(&reset); #endif const time_t resetDelta = (std::mktime(resetGM) - std::mktime(&date)) * 1000; buckets[bucket] = resetDelta + getEpochTimeMillisecond(); onDepletedRequestSupply(resetDelta, { *this, method, path, jsonParameters, multipartParameters }); } }