diff --git a/src/AKSRT/User/BadgeAutoAward.php b/src/AKSRT/User/BadgeAutoAward.php new file mode 100644 index 00000000..e04af9d7 --- /dev/null +++ b/src/AKSRT/User/BadgeAutoAward.php @@ -0,0 +1,168 @@ +evaluate((int) $user->ID); + } + } + + public function maybe_for_current_user(): void { + if (!is_user_logged_in() || is_admin()) { + return; + } + // Throttle: once per 10 minutes per user. + $uid = get_current_user_id(); + $key = 'aksrt_badge_auto_' . $uid; + if (get_transient($key)) { + return; + } + set_transient($key, 1, 10 * MINUTE_IN_SECONDS); + $this->evaluate($uid); + } + + public function on_content_change($arg = null): void { + $uid = get_current_user_id(); + if ($uid > 0) { + $this->evaluate($uid); + } + } + + public function on_follow(int $follower_id, int $following_id): void { + if ($following_id > 0) { + $this->evaluate($following_id); + } + if ($follower_id > 0) { + $this->evaluate($follower_id); + } + } + + public function on_checkin(int $user_id): void { + if ($user_id > 0) { + $this->evaluate($user_id); + } + } + + public function evaluate(int $user_id): void { + if ($user_id <= 0 || !class_exists('\AKSRT\User\Badges') || !Badges::enabled()) { + return; + } + if (!class_exists('\AKSRT\Settings\Settings')) { + return; + } + $identity = \AKSRT\Settings\Settings::get_user_identity_settings(); + $medal = is_array($identity['medal'] ?? null) ? $identity['medal'] : []; + $items = is_array($medal['items_by_slug'] ?? null) ? $medal['items_by_slug'] : []; + if (!$items) { + // Fall back to badge dictionary without thresholds. + return; + } + + foreach ($items as $slug => $item) { + $get_type = sanitize_key((string) ($item['get_type'] ?? 'manually_add')); + if ($get_type === 'manually_add' || $get_type === '') { + continue; + } + $need = max(0, (int) ($item['get_val'] ?? 0)); + if ($need <= 0) { + continue; + } + $val = $this->metric($user_id, $get_type); + if ($val >= $need) { + Badges::grant($user_id, (string) $slug, 0); + } + } + } + + private function metric(int $user_id, string $type): int { + switch ($type) { + case 'registration_time': + $user = get_userdata($user_id); + if (!$user) { + return 0; + } + $reg = strtotime((string) $user->user_registered); + if ($reg <= 0) { + return 0; + } + return max(0, (int) floor((time() - $reg) / DAY_IN_SECONDS)); + + case 'checkin_all_day': + $v = (int) get_user_meta($user_id, 'aksrt_checkin_all_day', true); + if ($v <= 0) { + $v = (int) get_user_meta($user_id, 'checkin_all_day', true); + } + return $v; + + case 'new_post': + return (int) count_user_posts($user_id, 'post', true); + + case 'bbs_post': + return post_type_exists('aksrt_forum_topic') + ? (int) count_user_posts($user_id, 'aksrt_forum_topic', true) + : 0; + + case 'comment': + return (int) get_comments(['user_id' => $user_id, 'count' => true, 'status' => 'approve']); + + case 'followed': + if (class_exists('\AKSRT\User\Follow')) { + return (int) Follow::get_instance()->get_follower_count($user_id); + } + return 0; + + case 'like': + return (int) get_user_meta($user_id, 'aksrt_like_count', true) + ?: (int) get_user_meta($user_id, 'like-posts', true); + + case 'views': + return (int) get_user_meta($user_id, 'aksrt_user_views', true); + + case 'pay_income': + return (int) get_user_meta($user_id, 'aksrt_pay_income', true); + + case 'pay_rebate': + return (int) get_user_meta($user_id, 'aksrt_pay_rebate', true); + + default: + /** + * Allow extensions to provide metric values. + * + * @param int $value + * @param int $user_id + * @param string $type + */ + return (int) apply_filters('aksrt_badge_metric_value', 0, $user_id, $type); + } + } +}