feat(badges): auto-award medals from catalog thresholds

Evaluate medal get_type metrics on login/checkin/content and grant
matching badges; optional search-history slot on search widget.
This commit is contained in:
2026-07-19 01:47:35 +08:00
parent 8f17ac410b
commit dbe1275173
+168
View File
@@ -0,0 +1,168 @@
<?php
/**
* Auto-grant medals from catalog thresholds (zibll zib_auto_add_user_medal lite).
*
* @package AKSRT
*/
namespace AKSRT\User;
defined('ABSPATH') || exit;
class BadgeAutoAward {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
// Run after login / current user bootstrap.
add_action('wp_login', [$this, 'on_login'], 20, 2);
add_action('wp', [$this, 'maybe_for_current_user'], 30);
// Cheap hooks for common counters.
add_action('publish_post', [$this, 'on_content_change'], 20, 1);
add_action('comment_post', [$this, 'on_content_change'], 20, 1);
add_action('aksrt_user_followed', [$this, 'on_follow'], 20, 2);
add_action('aksrt_checkin_done', [$this, 'on_checkin'], 20, 1);
}
public function on_login($user_login, $user): void {
if ($user instanceof \WP_User) {
$this->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);
}
}
}