Init project
commit
129ee5ac3d
@ -0,0 +1,2 @@
|
|||||||
|
<?php
|
||||||
|
$specialPageAliases = [];
|
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
use Isekai\UserPoints\Service\IsekaiUserPointsFactory;
|
||||||
|
use Isekai\UserPoints\Service\IsekaiUserDailySignFactory;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'IsekaiUserPoints' => static function ( MediaWikiServices $services ) {
|
||||||
|
return new IsekaiUserPointsFactory( $services );
|
||||||
|
},
|
||||||
|
'IsekaiUserDailySign' => static function ( MediaWikiServices $services ) {
|
||||||
|
return new IsekaiUserDailySignFactory( $services );
|
||||||
|
},
|
||||||
|
];
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "hyperzlib/isekai-user-points",
|
||||||
|
"type": "library",
|
||||||
|
"license": "GPL-2.0-or-later",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Lex Lim",
|
||||||
|
"email": "hyperzlib@outlook.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
{
|
||||||
|
"name": "Isekai User Points",
|
||||||
|
"namemsg": "isekai-userpoints",
|
||||||
|
"author": "Hyperzlib",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"url": "https://git.isekai.cn/Isekai-Project/mediawiki-extension-IsekaiUserPoints",
|
||||||
|
"descriptionmsg": "isekai-userpoints-desc",
|
||||||
|
"license-name": "GPL-2.0-or-later",
|
||||||
|
"type": "api",
|
||||||
|
"requires": {
|
||||||
|
"MediaWiki": ">= 1.35.0"
|
||||||
|
},
|
||||||
|
"MessagesDirs": {
|
||||||
|
"IsekaiUserPoints": [
|
||||||
|
"i18n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ExtensionMessagesFiles": {
|
||||||
|
"IsekaiUserPointsAlias": "IsekaiUserPoints.alias.php"
|
||||||
|
},
|
||||||
|
"AutoloadNamespaces": {
|
||||||
|
"Isekai\\UserPoints\\": "includes"
|
||||||
|
},
|
||||||
|
"Hooks": {
|
||||||
|
"LoadExtensionSchemaUpdates": "Isekai\\UserPoints\\Hooks::onLoadExtensionSchemaUpdates",
|
||||||
|
"BeforePageDisplay": "Isekai\\UserPoints\\Hooks::onBeforePageDisplay",
|
||||||
|
"GetPreferences": "Isekai\\UserPoints\\Hooks::onGetPreferences"
|
||||||
|
},
|
||||||
|
"APIModules": {
|
||||||
|
"userdailysign": "Isekai\\UserPoints\\Api\\ApiUserDailySign"
|
||||||
|
},
|
||||||
|
"APIPropModules": {
|
||||||
|
"userpoints": "Isekai\\UserPoints\\Api\\ApiQueryUserPoints",
|
||||||
|
"pointinfo": "Isekai\\UserPoints\\Api\\ApiQueryPointInfo"
|
||||||
|
},
|
||||||
|
"APIListModules": {
|
||||||
|
"userspoints": "Isekai\\UserPoints\\Api\\ApiQueryUsersPoints"
|
||||||
|
},
|
||||||
|
"ServiceWiringFiles": [
|
||||||
|
"IsekaiUserPoints.services.php"
|
||||||
|
],
|
||||||
|
"GroupPermissions": {
|
||||||
|
"bureaucrat": {
|
||||||
|
"queryuserpoints": true,
|
||||||
|
"edituserpoints": true
|
||||||
|
},
|
||||||
|
"suppress": {
|
||||||
|
"queryuserpoints": true,
|
||||||
|
"edituserpoints": true
|
||||||
|
},
|
||||||
|
"sysop": {
|
||||||
|
"queryuserpoints": true,
|
||||||
|
"edituserpoints": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GrantPermissions": {
|
||||||
|
"userpointsmanager": {
|
||||||
|
"queryuserpoints": true,
|
||||||
|
"edituserpoints": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ResourceModules": {
|
||||||
|
"ext.isekai.userpoints.base": {
|
||||||
|
"styles": ["ext.isekai.userpoints.base.less"]
|
||||||
|
},
|
||||||
|
"ext.isekai.userpoints.dailysign": {
|
||||||
|
"scripts": ["ext.isekai.userpoints.dailysign.js"],
|
||||||
|
"messages": [
|
||||||
|
"comma-separator",
|
||||||
|
"isekai-userpoints-point-name-num",
|
||||||
|
"isekai-userpoints-dailysign-notify-title",
|
||||||
|
"isekai-userpoints-dailysign-notify-success"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ResourceFileModulePaths": {
|
||||||
|
"localBasePath": "modules",
|
||||||
|
"remoteExtPath": "IsekaiUserPoints/modules"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"IsekaiUserPointConfig": {
|
||||||
|
"value": {
|
||||||
|
"exp": {
|
||||||
|
"name": "Exp.",
|
||||||
|
"namemsg": "isekai-userpoints-point-name-exp",
|
||||||
|
"icon": {
|
||||||
|
"normal": {
|
||||||
|
"image": "https://static.isekai.dev/isekaiwiki/isekaiwiki-exp.png"
|
||||||
|
},
|
||||||
|
"invert": {
|
||||||
|
"image": "https://static.isekai.dev/isekaiwiki/isekaiwiki-exp-invert.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IsekaiUserPointShowOnUserPerferences": {
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
"IsekaiUserDailySignConfig": {
|
||||||
|
"value": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"manifest_version": 2
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"isekai-userpoints": "异世界百科 用户积分",
|
||||||
|
"isekai-userpoints-desc": "存储用户积分,为其他应用提供积分功能",
|
||||||
|
|
||||||
|
"isekai-userpoints-point-name-num": "$2 $3 $1",
|
||||||
|
|
||||||
|
"isekai-userpoints-service-system": "积分系统",
|
||||||
|
"isekai-userpoints-action-system-point-expired": "积分过期",
|
||||||
|
|
||||||
|
"apihelp-query+userpoints-summary": "获取当前登录用户的积分",
|
||||||
|
|
||||||
|
"apihelp-query+userspoints-summary": "获取用户积分",
|
||||||
|
"apihelp-query+userspoints-param-users": "要获取信息的用户列表。",
|
||||||
|
"apihelp-query+userspoints-param-userids": "要获得信息的用户ID列表。",
|
||||||
|
|
||||||
|
"apierror-isekaiuserpoints-notfound": "未找到积分信息",
|
||||||
|
|
||||||
|
"isekai-userpoints-point-name-exp": "经验值",
|
||||||
|
"isekai-userpoints-point-name-kamakano": "星砂",
|
||||||
|
|
||||||
|
"isekai-userpoints-source-system": "积分系统",
|
||||||
|
|
||||||
|
"apihelp-userdailysign-summary": "用户签到",
|
||||||
|
"apiwarn-isekai-userpoints-already-signed-today": "今天已经签到过了",
|
||||||
|
|
||||||
|
"isekai-userpoints-dailysign-notify-title": "每日签到",
|
||||||
|
"isekai-userpoints-dailysign-notify-success": "每日登录网站,获得了$1"
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
namespace Isekai\UserPoints\Api;
|
||||||
|
|
||||||
|
use ApiQueryBase;
|
||||||
|
use ApiQuery;
|
||||||
|
use Config;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
use WANObjectCache;
|
||||||
|
|
||||||
|
use Isekai\UserPoints\Utils;
|
||||||
|
|
||||||
|
class ApiQueryPointInfo extends ApiQueryBase {
|
||||||
|
private const CACHE_VERSION = 2;
|
||||||
|
|
||||||
|
private const PREFIX = 'pi';
|
||||||
|
|
||||||
|
private $params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Config
|
||||||
|
*/
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var WANObjectCache
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiQuery $query API query module object
|
||||||
|
* @param string $moduleName Name of this query module
|
||||||
|
*/
|
||||||
|
public function __construct($query, $moduleName) {
|
||||||
|
parent::__construct($query, $moduleName, self::PREFIX);
|
||||||
|
$this->config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'textextracts' );
|
||||||
|
$this->cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \ApiUsageException
|
||||||
|
*/
|
||||||
|
public function execute() {
|
||||||
|
$pointConfig = $this->config->get('IsekaiUserPointConfig');
|
||||||
|
$pointTypes = array_keys($pointConfig);
|
||||||
|
|
||||||
|
$pointInfos = [];
|
||||||
|
foreach ($pointTypes as $pointType) {
|
||||||
|
$pointInfos[$pointType] = [
|
||||||
|
'name' => Utils::getPointName($pointType),
|
||||||
|
'icon' => Utils::getPointIcon($pointType),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->getResult();
|
||||||
|
$result->addValue(
|
||||||
|
[ 'query', $this->getModuleName() ],
|
||||||
|
'pointinfo',
|
||||||
|
$pointInfos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $params Ignored parameters
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCacheMode($params) {
|
||||||
|
return 'public';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedParams() {
|
||||||
|
return [
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
namespace Isekai\UserPoints\Api;
|
||||||
|
|
||||||
|
use ApiQueryBase;
|
||||||
|
use ApiQuery;
|
||||||
|
use Config;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
use WANObjectCache;
|
||||||
|
use Isekai\UserPoints\Service\IsekaiUserPointsFactory;
|
||||||
|
use Isekai\UserPoints\Utils;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class ApiQueryUserPoints extends ApiQueryBase {
|
||||||
|
private const CACHE_VERSION = 2;
|
||||||
|
|
||||||
|
private const PREFIX = 'up';
|
||||||
|
|
||||||
|
private $params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Config
|
||||||
|
*/
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var WANObjectCache
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiQuery $query API query module object
|
||||||
|
* @param string $moduleName Name of this query module
|
||||||
|
*/
|
||||||
|
public function __construct($query, $moduleName) {
|
||||||
|
parent::__construct($query, $moduleName, self::PREFIX);
|
||||||
|
$this->config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'textextracts' );
|
||||||
|
$this->cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \ApiUsageException
|
||||||
|
*/
|
||||||
|
public function execute() {
|
||||||
|
$user = $this->getUser();
|
||||||
|
if (!$user->isRegistered()) {
|
||||||
|
$this->dieWithError('apierror-mustbeloggedin-generic', 'login-required');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pointConfig = $this->config->get('IsekaiUserPointConfig');
|
||||||
|
$pointTypes = array_keys($pointConfig);
|
||||||
|
|
||||||
|
/** @var IsekaiUserPointsFactory */
|
||||||
|
$userPointsFactory = MediaWikiServices::getInstance()->getService('IsekaiUserPoints');
|
||||||
|
|
||||||
|
/** @var array<IsekaiUserPoints> */
|
||||||
|
$userPoints = [];
|
||||||
|
foreach ($pointTypes as $pointType) {
|
||||||
|
$userPoint = $userPointsFactory->newFromUser($user, $pointType);
|
||||||
|
if ($userPoint) {
|
||||||
|
$userPoints[$pointType] = $userPointsFactory->newFromUser($user, $pointType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($userPoints) === 0) {
|
||||||
|
$this->dieWithError('apierror-isekaiuserpoints-notfound', 'not-found');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->getResult();
|
||||||
|
foreach ($userPoints as $pointType => $userPoint) {
|
||||||
|
$pointName = Utils::getPointName($pointType);
|
||||||
|
$pointIcon = Utils::getPointIcon($pointType);
|
||||||
|
$result->addValue(['query', $this->getModuleName()], $pointType, [
|
||||||
|
'points' => $userPoint->points,
|
||||||
|
'timed_points_data' => $userPoint->timedPointsData,
|
||||||
|
'locked_points' => $userPoint->lockedPoints,
|
||||||
|
'locked_points_data' => $userPoint->lockedPointsData,
|
||||||
|
'name' => $pointName,
|
||||||
|
'icon' => $pointIcon,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (empty($userPoints)) {
|
||||||
|
$result->addValue(['query'], $this->getModuleName(), new stdClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $params Ignored parameters
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCacheMode($params) {
|
||||||
|
return 'private';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedParams() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
<?php
|
||||||
|
namespace Isekai\UserPoints\Api;
|
||||||
|
|
||||||
|
use ApiQueryBase;
|
||||||
|
use ApiQuery;
|
||||||
|
use Config;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
use WANObjectCache;
|
||||||
|
use Wikimedia\ParamValidator\ParamValidator;
|
||||||
|
use User;
|
||||||
|
|
||||||
|
use Isekai\UserPoints\Service\IsekaiUserPointsFactory;
|
||||||
|
use Isekai\UserPoints\Utils;
|
||||||
|
|
||||||
|
class ApiQueryUsersPoints extends ApiQueryBase {
|
||||||
|
private const CACHE_VERSION = 2;
|
||||||
|
|
||||||
|
private const PREFIX = 'up';
|
||||||
|
|
||||||
|
private $params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Config
|
||||||
|
*/
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var WANObjectCache
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiQuery $query API query module object
|
||||||
|
* @param string $moduleName Name of this query module
|
||||||
|
*/
|
||||||
|
public function __construct($query, $moduleName) {
|
||||||
|
parent::__construct($query, $moduleName, self::PREFIX);
|
||||||
|
$this->config = MediaWikiServices::getInstance()->getMainConfig();
|
||||||
|
$this->cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \ApiUsageException
|
||||||
|
*/
|
||||||
|
public function execute() {
|
||||||
|
$this->checkUserRightsAny('queryuserpoints');
|
||||||
|
|
||||||
|
$params = $this->extractRequestParams();
|
||||||
|
$this->requireMaxOneParameter( $params, 'userids', 'users' );
|
||||||
|
|
||||||
|
$users = (array)$params['users'];
|
||||||
|
$userids = (array)$params['userids'];
|
||||||
|
|
||||||
|
$services = MediaWikiServices::getInstance();
|
||||||
|
|
||||||
|
$pointConfig = $this->config->get('IsekaiUserPointConfig');
|
||||||
|
$pointTypes = array_keys($pointConfig);
|
||||||
|
|
||||||
|
$pointInfos = [];
|
||||||
|
foreach ($pointTypes as $pointType) {
|
||||||
|
$pointInfos[$pointType] = [
|
||||||
|
'name' => Utils::getPointName($pointType),
|
||||||
|
'icon' => Utils::getPointIcon($pointType),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var IsekaiUserPointsFactory */
|
||||||
|
$userPointsFactory = $services->getService('IsekaiUserPoints');
|
||||||
|
|
||||||
|
$userFactory = $services->getUserFactory();
|
||||||
|
$userInstances = [];
|
||||||
|
foreach ( $users as $u ) {
|
||||||
|
$user = $userFactory->newFromName( $u );
|
||||||
|
$userInstances[] = $user;
|
||||||
|
}
|
||||||
|
foreach ( $userids as $u ) {
|
||||||
|
$user = $userFactory->newFromId( $u );
|
||||||
|
$userInstances[] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userPointsInstances = $userPointsFactory->newFromUsers($userInstances, $pointTypes);
|
||||||
|
|
||||||
|
$result = $this->getResult();
|
||||||
|
foreach ($userPointsInstances as $userPointsTuple) {
|
||||||
|
list($user, $type, $userPoint) = $userPointsTuple;
|
||||||
|
$result->addValue(
|
||||||
|
[ 'query', $this->getModuleName(), "pointdata", $user->getId() ],
|
||||||
|
$type,
|
||||||
|
[
|
||||||
|
'point_type' => $type,
|
||||||
|
'points' => $userPoint->points,
|
||||||
|
'timed_points_data' => $userPoint->timedPointsData,
|
||||||
|
'locked_points' => $userPoint->lockedPoints,
|
||||||
|
'locked_points_data' => $userPoint->lockedPointsData,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$result->addValue(
|
||||||
|
[ 'query', $this->getModuleName() ],
|
||||||
|
'pointinfo',
|
||||||
|
$pointInfos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $params Ignored parameters
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCacheMode($params) {
|
||||||
|
return 'public';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedParams() {
|
||||||
|
return [
|
||||||
|
'users' => [
|
||||||
|
ParamValidator::PARAM_ISMULTI => true
|
||||||
|
],
|
||||||
|
'userids' => [
|
||||||
|
ParamValidator::PARAM_ISMULTI => true,
|
||||||
|
ParamValidator::PARAM_TYPE => 'integer'
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
namespace Isekai\UserPoints\Api;
|
||||||
|
|
||||||
|
use ApiBase;
|
||||||
|
use Isekai\UserPoints\Service\IsekaiUserPointsFactory;
|
||||||
|
use Isekai\UserPoints\Service\IsekaiUserDailySignFactory;
|
||||||
|
use Isekai\UserPoints\Utils;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
|
||||||
|
class ApiUserDailySign extends ApiBase {
|
||||||
|
public function __construct( $main, $method ) {
|
||||||
|
parent::__construct( $main->getMain(), $method );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() {
|
||||||
|
$services = MediaWikiServices::getInstance();
|
||||||
|
|
||||||
|
$user = $this->getUser();
|
||||||
|
|
||||||
|
if (!$user->isRegistered()) {
|
||||||
|
$this->dieWithError('apierror-mustbeloggedin-generic', 'login-required');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var IsekaiUserDailySignFactory */
|
||||||
|
$dailySignFactory = $services->getService('IsekaiUserDailySign');
|
||||||
|
$dailySignService = $dailySignFactory->newFromUser($user);
|
||||||
|
|
||||||
|
$result = $this->getResult();
|
||||||
|
if (date('Y-m-d') === date('Y-m-d', strtotime($dailySignService->lastSignDate))) {
|
||||||
|
$result->addValue(['userdailysign'], 'success', 0);
|
||||||
|
$result->addValue(['userdailysign'], 'point_delta', []);
|
||||||
|
$this->addWarning('isekai-userpoints-already-signed-today', 'already-signed-today');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pointDelta = $dailySignService->doSign();
|
||||||
|
|
||||||
|
if (!empty($pointDelta)) {
|
||||||
|
$result->addValue(['userdailysign'], 'success', 1);
|
||||||
|
} else {
|
||||||
|
$result->addValue(['userdailysign'], 'success', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$resultPointDelta = [];
|
||||||
|
foreach ($pointDelta as $pointType => $delta) {
|
||||||
|
$pointName = Utils::getPointName($pointType);
|
||||||
|
$pointIcon = Utils::getPointIcon($pointType);
|
||||||
|
|
||||||
|
$resultPointDelta[] = [
|
||||||
|
'point_type' => $pointType,
|
||||||
|
'name' => $pointName,
|
||||||
|
'icon' => $pointIcon,
|
||||||
|
'points' => $delta,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result->addValue(['userdailysign'], 'point_delta', $resultPointDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function needsToken() {
|
||||||
|
return 'csrf';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Isekai\UserPoints;
|
||||||
|
|
||||||
|
use DatabaseUpdater;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
use OutputPage;
|
||||||
|
use Skin;
|
||||||
|
use User;
|
||||||
|
|
||||||
|
class Hooks {
|
||||||
|
/**
|
||||||
|
* Implements LoadExtensionSchemaUpdates hook.
|
||||||
|
*
|
||||||
|
* @param \DatabaseUpdater $updater
|
||||||
|
*/
|
||||||
|
public static function onLoadExtensionSchemaUpdates($updater) {
|
||||||
|
$dir = dirname(__DIR__) . '/sql/';
|
||||||
|
$type = $updater->getDB()->getType();
|
||||||
|
$updater->addExtensionTable('isekai_user_daily_sign', $dir . $type . '/isekai_user_daily_sign.sql');
|
||||||
|
$updater->addExtensionTable('isekai_user_daily_sign_log', $dir . $type . '/isekai_user_daily_sign_log.sql');
|
||||||
|
$updater->addExtensionTable('isekai_user_points', $dir . $type . '/isekai_user_points.sql');
|
||||||
|
$updater->addExtensionTable('isekai_user_points_log', $dir . $type . '/isekai_user_points_log.sql');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onBeforePageDisplay(OutputPage $out, Skin $skin) {
|
||||||
|
$config = MediaWikiServices::getInstance()->getMainConfig();
|
||||||
|
|
||||||
|
$out->addModuleStyles(['ext.isekai.userpoints.base']);
|
||||||
|
|
||||||
|
$dailySignConfig = $config->get('IsekaiUserDailySignConfig');
|
||||||
|
if ($dailySignConfig) {
|
||||||
|
$out->addModules(['ext.isekai.userpoints.dailysign']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onGetPreferences(User $user, &$preferences) {
|
||||||
|
$config = MediaWikiServices::getInstance()->getMainConfig();
|
||||||
|
if ($config->get('IsekaiUserPointShowOnUserPerferences')) {
|
||||||
|
$userPointConfig = $config->get('IsekaiUserPointConfig');
|
||||||
|
|
||||||
|
/** @var \Isekai\UserPoints\Service\IsekaiUserPointsFactory */
|
||||||
|
$userPointsFactory = MediaWikiServices::getInstance()->getService('IsekaiUserPoints');
|
||||||
|
|
||||||
|
foreach ($userPointConfig as $pointType => $pointConfig) {
|
||||||
|
$userPoints = $userPointsFactory->newFromUser($user, $pointType);
|
||||||
|
|
||||||
|
$icon = Utils::getPointIcon($pointType);
|
||||||
|
$name = Utils::getPointName($pointType);
|
||||||
|
|
||||||
|
$language = MediaWikiServices::getInstance()->getContentLanguage();
|
||||||
|
|
||||||
|
$preferences['isekai-userpoints-' . $pointType] = [
|
||||||
|
'type' => 'text',
|
||||||
|
'label-raw' => $icon . ' ' . $name,
|
||||||
|
'default' => $language->formatNum($userPoints->points),
|
||||||
|
'section' => 'personal/info',
|
||||||
|
'raw' => true,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,256 @@
|
|||||||
|
<?php
|
||||||
|
namespace Isekai\UserPoints\Service;
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
use User;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $user_id
|
||||||
|
* @property string $last_sign_date
|
||||||
|
* @property string|array $sign_days_data
|
||||||
|
* @property int $total_sign_days
|
||||||
|
*/
|
||||||
|
class IsekaiUserDailySignData extends stdClass {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $userId
|
||||||
|
* @property string $lastSignDate
|
||||||
|
* @property array $signDaysData
|
||||||
|
* @property int $totalSignDays
|
||||||
|
*/
|
||||||
|
class IsekaiUserDailySign {
|
||||||
|
/** @var MediaWikiServices */
|
||||||
|
private $services;
|
||||||
|
|
||||||
|
/** @var User */
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $pointConfig;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $dailySignConfig;
|
||||||
|
|
||||||
|
private $dataKeysMap = [
|
||||||
|
'userId' => 'user_id',
|
||||||
|
'lastSignDate' => 'last_sign_date',
|
||||||
|
'signDaysData' => 'sign_days_data',
|
||||||
|
'totalSignDays' => 'total_sign_days'
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var IsekaiUserDailySignData|null */
|
||||||
|
private $mDailySignData = null;
|
||||||
|
|
||||||
|
private $recordExists = false;
|
||||||
|
|
||||||
|
public function __construct(User $user, array $pointConfig, array $dailySignConfig, MediaWikiServices $services) {
|
||||||
|
$this->user = $user;
|
||||||
|
$this->pointConfig = $pointConfig;
|
||||||
|
$this->dailySignConfig = $dailySignConfig;
|
||||||
|
$this->services = $services;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadDailySignData() {
|
||||||
|
if (!$this->mDailySignData) {
|
||||||
|
$dbr = $this->services->getDBLoadBalancer()->getConnection(DB_REPLICA);
|
||||||
|
$this->mDailySignData = $dbr->selectRow(
|
||||||
|
'isekai_user_daily_sign',
|
||||||
|
'*',
|
||||||
|
[
|
||||||
|
'user_id' => $this->user->getId()
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$this->mDailySignData) {
|
||||||
|
$this->initDefaultData();
|
||||||
|
} else {
|
||||||
|
$this->mDailySignData->sign_days_data = json_decode($this->mDailySignData->sign_days_data, true) ?? [];
|
||||||
|
|
||||||
|
$this->recordExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initDefaultData() {
|
||||||
|
$this->mDailySignData = new IsekaiUserDailySignData();
|
||||||
|
$this->mDailySignData->user_id = $this->user->getId();
|
||||||
|
$this->mDailySignData->last_sign_date = '1970-01-01';
|
||||||
|
$this->mDailySignData->sign_days_data = [];
|
||||||
|
$this->mDailySignData->total_sign_days = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name) {
|
||||||
|
$this->loadDailySignData();
|
||||||
|
if (isset($this->dataKeysMap[$name])) {
|
||||||
|
$key = $this->dataKeysMap[$name];
|
||||||
|
return $this->mDailySignData->$key;
|
||||||
|
} else {
|
||||||
|
throw new \Exception("Invalid property $name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doSign() {
|
||||||
|
$this->loadDailySignData();
|
||||||
|
|
||||||
|
$pointDelta = [];
|
||||||
|
|
||||||
|
$lastSignTime = strtotime($this->mDailySignData->last_sign_date);
|
||||||
|
|
||||||
|
if (date('Y-m-d', time()) == date('Y-m-d', $lastSignTime)) { // already signed in today
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var IsekaiUserPointsFactory */
|
||||||
|
$userPointsFactory = $this->services->getService('IsekaiUserPoints');
|
||||||
|
foreach ($this->dailySignConfig as $pointType => $config) {
|
||||||
|
if (!isset($this->pointConfig[$pointType])) { // point type not exist, ignore
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($config['points'])) { // no points config, ignore
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$signDays = $this->mDailySignData->sign_days_data[$pointType] ?? 0;
|
||||||
|
|
||||||
|
$continuous_reset = $config['continuous_reset'] ?? false;
|
||||||
|
$expireTime = $config['expire_time'] ?? -1;
|
||||||
|
$points = $config['points'];
|
||||||
|
|
||||||
|
if (!is_array($points)) {
|
||||||
|
$points = [ $points ];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($continuous_reset) {
|
||||||
|
case 'day':
|
||||||
|
if (date('Y-m-d', strtotime('-1 day')) != date('Y-m-d', $lastSignTime)) {
|
||||||
|
$signDays = 0;
|
||||||
|
}
|
||||||
|
case 'week':
|
||||||
|
if (date('Y-W', time()) != date('Y-W', $lastSignTime)) {
|
||||||
|
$signDays = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'month':
|
||||||
|
if (date('Y-m', time()) != date('Y-m', $lastSignTime)) {
|
||||||
|
$signDays = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'year':
|
||||||
|
if (date('Y', time()) != date('Y', $lastSignTime)) {
|
||||||
|
$signDays = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pointsIndex = min($signDays, count($points) - 1);
|
||||||
|
$pointsAdd = $points[$pointsIndex];
|
||||||
|
|
||||||
|
$pointsService = $userPointsFactory->newFromUser($this->user, $pointType);
|
||||||
|
$pointsService->addPoints($pointsAdd, $expireTime, 'daily_sign', 'sign_in');
|
||||||
|
|
||||||
|
$pointDelta[$pointType] = $pointsAdd;
|
||||||
|
|
||||||
|
$this->mDailySignData->sign_days_data[$pointType] = $signDays + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->mDailySignData->last_sign_date = date('Y-m-d H:i:s', time());
|
||||||
|
$this->mDailySignData->total_sign_days ++;
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
$this->saveSignLog($pointDelta);
|
||||||
|
|
||||||
|
return $pointDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function save() {
|
||||||
|
$dbw = $this->services->getDBLoadBalancer()->getConnection(DB_PRIMARY);
|
||||||
|
if ($this->recordExists) {
|
||||||
|
$dbw->update(
|
||||||
|
'isekai_user_daily_sign',
|
||||||
|
[
|
||||||
|
'last_sign_date' => $this->mDailySignData->last_sign_date,
|
||||||
|
'sign_days_data' => json_encode($this->mDailySignData->sign_days_data),
|
||||||
|
'total_sign_days' => $this->mDailySignData->total_sign_days,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'user_id' => $this->user->getId()
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$dbw->insert(
|
||||||
|
'isekai_user_daily_sign',
|
||||||
|
[
|
||||||
|
'user_id' => $this->mDailySignData->user_id,
|
||||||
|
'last_sign_date' => $this->mDailySignData->last_sign_date,
|
||||||
|
'sign_days_data' => json_encode($this->mDailySignData->sign_days_data),
|
||||||
|
'total_sign_days' => $this->mDailySignData->total_sign_days,
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
$this->recordExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function saveSignLog($pointDelta) {
|
||||||
|
$dbr = $this->services->getDBLoadBalancer()->getConnection(DB_REPLICA);
|
||||||
|
$dbw = $this->services->getDBLoadBalancer()->getConnection(DB_PRIMARY);
|
||||||
|
|
||||||
|
$currentYear = intval(date('Y', time()));
|
||||||
|
$currentMonth = intval(date('m', time()));
|
||||||
|
$currentDate = intval(date('d', time()));
|
||||||
|
|
||||||
|
$logData = $dbr->selectRow(
|
||||||
|
'isekai_user_daily_sign_log',
|
||||||
|
'*',
|
||||||
|
[
|
||||||
|
'user_id' => $this->user->getId(),
|
||||||
|
'year' => $currentYear,
|
||||||
|
'month' => $currentMonth
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($logData) {
|
||||||
|
$signLog = json_decode($logData->sign_log, true);
|
||||||
|
if (!is_array($signLog)) {
|
||||||
|
$signLog = [];
|
||||||
|
}
|
||||||
|
$signLog[] = [
|
||||||
|
'date' => $currentDate,
|
||||||
|
'delta' => $pointDelta
|
||||||
|
];
|
||||||
|
$logData->sign_log = json_encode($signLog);
|
||||||
|
$dbw->update(
|
||||||
|
'isekai_user_daily_sign_log',
|
||||||
|
[
|
||||||
|
'sign_log' => $logData->sign_log
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'user_id' => $this->user->getId(),
|
||||||
|
'year' => $currentYear,
|
||||||
|
'month' => $currentMonth
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$signLog = [[
|
||||||
|
'date' => $currentDate,
|
||||||
|
'delta' => $pointDelta
|
||||||
|
]];
|
||||||
|
$dbw->insert(
|
||||||
|
'isekai_user_daily_sign_log',
|
||||||
|
[
|
||||||
|
'user_id' => $this->user->getId(),
|
||||||
|
'year' => $currentYear,
|
||||||
|
'month' => $currentMonth,
|
||||||
|
'sign_log' => json_encode($signLog)
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
namespace Isekai\UserPoints\Service;
|
||||||
|
|
||||||
|
use Config;
|
||||||
|
use User;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
|
||||||
|
class IsekaiUserDailySignFactory {
|
||||||
|
/** @var MediaWikiServices */
|
||||||
|
private $services;
|
||||||
|
|
||||||
|
/** @var \Config */
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $pointConfig;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $dailySignConfig;
|
||||||
|
|
||||||
|
private static $instances;
|
||||||
|
|
||||||
|
public function __construct(MediaWikiServices $services) {
|
||||||
|
$this->services = $services;
|
||||||
|
$this->config = $services->getMainConfig();
|
||||||
|
$this->pointConfig = $this->config->get('IsekaiUserPointConfig');
|
||||||
|
$this->dailySignConfig = $this->config->get('IsekaiUserDailySignConfig');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @return IsekaiUserDailySign|null
|
||||||
|
*/
|
||||||
|
public function newFromUser(User $user) {
|
||||||
|
if (!$this->dailySignConfig) {
|
||||||
|
throw new \Exception('$wgIsekaiUserDailySignConfig is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user->isRegistered()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $user->getId();
|
||||||
|
$cacheKey = $userId;
|
||||||
|
if (!isset(self::$instances[$cacheKey])) {
|
||||||
|
self::$instances[$cacheKey] = new IsekaiUserDailySign($user, $this->pointConfig, $this->dailySignConfig, $this->services);
|
||||||
|
}
|
||||||
|
return self::$instances[$cacheKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $userId
|
||||||
|
* @return IsekaiUserDailySign|null
|
||||||
|
*/
|
||||||
|
public function newFromUserId($userId) {
|
||||||
|
$user = $this->services->getUserFactory()->newFromId($userId);
|
||||||
|
if (!$user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $this->newFromUser($user);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,441 @@
|
|||||||
|
<?php
|
||||||
|
namespace Isekai\UserPoints\Service;
|
||||||
|
|
||||||
|
use User;
|
||||||
|
use stdClass;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $user_id
|
||||||
|
* @property string $type
|
||||||
|
* @property int $points
|
||||||
|
* @property string|array $timed_points_data
|
||||||
|
* @property int $locked_points
|
||||||
|
* @property string|array $locked_points_data
|
||||||
|
* @property int $next_refresh_time
|
||||||
|
*/
|
||||||
|
class IsekaiUserPointsData extends stdClass {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $points
|
||||||
|
* @property array $timedPointsData
|
||||||
|
* @property int $lockedPoints
|
||||||
|
* @property array $lockedPointsData
|
||||||
|
* @property int $nextRefreshTime
|
||||||
|
*/
|
||||||
|
class IsekaiUserPoints {
|
||||||
|
public const DEFAULT_TRANSACTION_TIMEOUT = 60 * 10;
|
||||||
|
public const MAX_TRANSACTION_TIMEOUT = 60 * 60;
|
||||||
|
|
||||||
|
/** @var MediaWikiServices */
|
||||||
|
private $services;
|
||||||
|
|
||||||
|
/** @var \Config */
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
/** @var User */
|
||||||
|
public $user;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $type;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $pointName = '';
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $rawName = '';
|
||||||
|
|
||||||
|
/** @var IsekaiUserPointsData|null */
|
||||||
|
private $mPointData = null;
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $pointRecordExists = false;
|
||||||
|
|
||||||
|
private $pointDataKeysMap = [
|
||||||
|
'points' => 'points',
|
||||||
|
'timedPointsData' => 'timed_points_data',
|
||||||
|
'lockedPoints' => 'locked_points',
|
||||||
|
'lockedPointsData' => 'locked_points_data',
|
||||||
|
'nextRefreshTime' => 'next_refresh_time'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(User $user, string $type, array $pointConfig, MediaWikiServices $mediaWikiServices = null) {
|
||||||
|
if (!$mediaWikiServices) {
|
||||||
|
$this->services = MediaWikiServices::getInstance();
|
||||||
|
} else {
|
||||||
|
$this->services = $mediaWikiServices;
|
||||||
|
}
|
||||||
|
$this->config = $this->services->getMainConfig();
|
||||||
|
|
||||||
|
$this->user = $user;
|
||||||
|
$this->type = $type;
|
||||||
|
|
||||||
|
if (isset($pointConfig['namemsg'])) {
|
||||||
|
$this->pointName = wfMessage($pointConfig['namemsg'])->text();
|
||||||
|
$this->rawName = $pointConfig['name'] ?? $this->pointName;
|
||||||
|
} elseif (isset($pointConfig['name'])) {
|
||||||
|
$this->pointName = $this->rawName = $pointConfig['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setData($pointData) {
|
||||||
|
$this->mPointData = $pointData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadPointData() {
|
||||||
|
if (!$this->mPointData) {
|
||||||
|
$dbr = $this->services->getDBLoadBalancer()->getConnection(DB_REPLICA);
|
||||||
|
$this->mPointData = $dbr->selectRow(
|
||||||
|
'isekai_user_points',
|
||||||
|
'*',
|
||||||
|
[
|
||||||
|
'user_id' => $this->user->getId(),
|
||||||
|
'type' => $this->type
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$this->mPointData) {
|
||||||
|
$this->initDefaultData();
|
||||||
|
} else {
|
||||||
|
$this->mPointData->timed_points_data = json_decode($this->mPointData->timed_points_data, true) ?? [];
|
||||||
|
$this->mPointData->locked_points_data = json_decode($this->mPointData->locked_points_data, true) ?? [];
|
||||||
|
$this->pointRecordExists = true;
|
||||||
|
$this->removeExpiredPoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initDefaultData() {
|
||||||
|
$this->mPointData = new IsekaiUserPointsData();
|
||||||
|
$this->mPointData->user_id = $this->user->getId();
|
||||||
|
$this->mPointData->type = $this->type;
|
||||||
|
$this->mPointData->points = 0;
|
||||||
|
$this->mPointData->timed_points_data = [];
|
||||||
|
$this->mPointData->locked_points = 0;
|
||||||
|
$this->mPointData->locked_points_data = [];
|
||||||
|
$this->mPointData->next_refresh_time = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove expired points
|
||||||
|
*/
|
||||||
|
private function removeExpiredPoints() {
|
||||||
|
$currentTime = time();
|
||||||
|
if ($this->mPointData->next_refresh_time !== null &&
|
||||||
|
$currentTime > $this->mPointData->next_refresh_time &&
|
||||||
|
is_array($this->mPointData->timed_points_data)) {
|
||||||
|
|
||||||
|
$touched = false;
|
||||||
|
$originalPoints = $this->mPointData->points;
|
||||||
|
foreach ($this->mPointData->timed_points_data as $key => $data) {
|
||||||
|
list($expireTime, $points) = $data;
|
||||||
|
if ($currentTime > $expireTime) {
|
||||||
|
$this->mPointData->points -= $points;
|
||||||
|
unset($this->mPointData->timed_points_data[$key]);
|
||||||
|
$touched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($touched) {
|
||||||
|
// Update next refresh time
|
||||||
|
if (count($this->mPointData->timed_points_data) > 0) {
|
||||||
|
// Sort by expire time
|
||||||
|
usort($this->mPointData->timed_points_data, function($a, $b) {
|
||||||
|
return $a[0] - $b[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->mPointData->next_refresh_time = $this->mPointData->timed_points_data[0][0];
|
||||||
|
} else {
|
||||||
|
$this->mPointData->next_refresh_time = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
$deltaPoints = $originalPoints - $this->mPointData->points;
|
||||||
|
|
||||||
|
$hookContainer = $this->services->getHookContainer();
|
||||||
|
$hookContainer->run('IsekaiUserPoints::PointsExpired', [$this, $deltaPoints]);
|
||||||
|
|
||||||
|
$this->savePointLog(-$deltaPoints, 'system', 'point-expired');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name) {
|
||||||
|
$this->loadPointData();
|
||||||
|
if (isset($this->pointDataKeysMap[$name])) {
|
||||||
|
$key = $this->pointDataKeysMap[$name];
|
||||||
|
return $this->mPointData->$key;
|
||||||
|
} else {
|
||||||
|
throw new \Exception("Invalid property $name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add user points
|
||||||
|
* @param int $points Points to add
|
||||||
|
* @param int $expireTime Expire time in seconds. -1 for never expire
|
||||||
|
* @param string $service Service name that add points. Message key is `isekai-userpoints-service-$service`.
|
||||||
|
* @param ?string $action Action name that add points. Message key is `isekai-userpoints-action-$service-$action`.
|
||||||
|
*/
|
||||||
|
public function addPoints($points, $expireTime = -1, $service = 'system', $action = null) {
|
||||||
|
$this->loadPointData();
|
||||||
|
|
||||||
|
$hookContainer = MediaWikiServices::getInstance()->getHookContainer();
|
||||||
|
|
||||||
|
$hookContainer->run('IsekaiUserPoints::BeforeAddPoints', [$this, &$points, &$expireTime, &$service, &$action]);
|
||||||
|
|
||||||
|
$this->mPointData->points += $points;
|
||||||
|
|
||||||
|
// Handle expire time
|
||||||
|
$expireTime = intval($expireTime);
|
||||||
|
$resolvedExipreTime = null;
|
||||||
|
if ($expireTime > 0) {
|
||||||
|
// Quantify expire time to 1 day
|
||||||
|
$expireTime = ceil($expireTime / 86400) * 86400;
|
||||||
|
|
||||||
|
$resolvedExipreTime = time() + $expireTime;
|
||||||
|
|
||||||
|
$recordExists = false;
|
||||||
|
foreach ($this->mPointData->timed_points_data as $key => $data) {
|
||||||
|
$oneExpireTime = $data[0];
|
||||||
|
if ($resolvedExipreTime === $oneExpireTime) {
|
||||||
|
$this->mPointData->timed_points_data[$key][1] += $points;
|
||||||
|
$recordExists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$recordExists) {
|
||||||
|
$this->mPointData->timed_points_data[] = [$resolvedExipreTime, $points];
|
||||||
|
}
|
||||||
|
if ($this->mPointData->next_refresh_time === null) {
|
||||||
|
$this->mPointData->next_refresh_time = $resolvedExipreTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @todo Add point log */
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
$this->savePointLog($points, $service, $action, $resolvedExipreTime);
|
||||||
|
$hookContainer->run('IsekaiUserPoints::AfterAddPoints', [$this, $points, $expireTime, $service, $action]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the user has enough points to consume
|
||||||
|
* @param int $points Points to consume
|
||||||
|
*/
|
||||||
|
public function hasEnoughPoints($points) {
|
||||||
|
$this->loadPointData();
|
||||||
|
return $this->mPointData->points >= $points;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consume points
|
||||||
|
* @param int $points Points to consume
|
||||||
|
* @param bool $force Force consume points even if the user has not enough points
|
||||||
|
* @param bool $ignoreSave Ignore save points data after consume points
|
||||||
|
* @param string $service Service name that consume points. Message key is `isekai-userpoints-service-$service`.
|
||||||
|
* @param ?string $action Action name that consume points. Message key is `isekai-userpoints-action-$service-$action`.
|
||||||
|
* @return bool True if the user has enough points to consume
|
||||||
|
*/
|
||||||
|
public function consumePoints($points, $force = false, $ignoreSave = false, $service = 'system', $action = null) {
|
||||||
|
$this->loadPointData();
|
||||||
|
|
||||||
|
$hookContainer = MediaWikiServices::getInstance()->getHookContainer();
|
||||||
|
|
||||||
|
$hookContainer->run('IsekaiUserPoints::BeforeConsumePoints', [$this, &$points, &$service, &$action]);
|
||||||
|
|
||||||
|
if (!$this->hasEnoughPoints($points) && !$force) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->mPointData->points = max(0, $this->mPointData->points - $points);
|
||||||
|
|
||||||
|
if ($this->mPointData->next_refresh_time !== null) { // Update timed points data
|
||||||
|
$consumedPoints = $points;
|
||||||
|
$touched = false;
|
||||||
|
foreach ($this->mPointData->timed_points_data as $key => $data) {
|
||||||
|
list($expireTime, $onePoints) = $data;
|
||||||
|
if ($onePoints <= $consumedPoints) {
|
||||||
|
$consumedPoints -= $onePoints;
|
||||||
|
unset($this->mPointData->timed_points_data[$key]);
|
||||||
|
$touched = true;
|
||||||
|
} else { // All points consumed
|
||||||
|
$this->mPointData->timed_points_data[$key][1] -= $consumedPoints;
|
||||||
|
$consumedPoints = 0;
|
||||||
|
$touched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($touched) {
|
||||||
|
// Update next refresh time
|
||||||
|
if (count($this->mPointData->timed_points_data) > 0) {
|
||||||
|
// Sort by expire time
|
||||||
|
usort($this->mPointData->timed_points_data, function($a, $b) {
|
||||||
|
return $a[0] - $b[0];
|
||||||
|
});
|
||||||
|
$this->mPointData->next_refresh_time = $this->mPointData->timed_points_data[0][0];
|
||||||
|
} else {
|
||||||
|
$this->mPointData->next_refresh_time = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$ignoreSave) {
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->savePointLog(-$points, $service, $action);
|
||||||
|
$hookContainer->run('IsekaiUserPoints::AfterConsumePoints', [$this, $points, $service, $action]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a consume points transaction
|
||||||
|
* @param int $points Points to consume
|
||||||
|
* @param int|null $timeout Transaction timeout in seconds
|
||||||
|
* @param string $service Service name that consume points. Message key is `isekai-userpoints-service-$service`.
|
||||||
|
* @param ?string $action Action name that consume points. Message key is `isekai-userpoints-action-$service-$action`.
|
||||||
|
* @return bool True if the transaction started successfully
|
||||||
|
*/
|
||||||
|
public function startConsumePointsTransaction($points, $timeout = null, $service = 'system', $action = null) {
|
||||||
|
$this->loadPointData();
|
||||||
|
|
||||||
|
$hookContainer = MediaWikiServices::getInstance()->getHookContainer();
|
||||||
|
|
||||||
|
$hookContainer->run('IsekaiUserPoints::BeforeConsumePoints', [$this, &$points, &$service, &$action]);
|
||||||
|
|
||||||
|
if (!$this->hasEnoughPoints($points)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timeout = min($timeout ?? self::DEFAULT_TRANSACTION_TIMEOUT, self::MAX_TRANSACTION_TIMEOUT);
|
||||||
|
$expireTime = time() + $timeout;
|
||||||
|
|
||||||
|
// Generate random string for transaction id
|
||||||
|
$transactionId = '';
|
||||||
|
do {
|
||||||
|
$transactionId = bin2hex(random_bytes(8));
|
||||||
|
} while (isset($this->mPointData->locked_points_data[$transactionId]));
|
||||||
|
|
||||||
|
$result = $this->consumePoints($points, false, true);
|
||||||
|
if (!$result) return false;
|
||||||
|
|
||||||
|
$this->mPointData->locked_points += $points;
|
||||||
|
$this->mPointData->locked_points_data[$transactionId] = [$expireTime, $points, $service, $action];
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return $transactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit a consume points transaction
|
||||||
|
* @param string $transactionId Transaction id
|
||||||
|
* @return bool True if the transaction is committed successfully
|
||||||
|
*/
|
||||||
|
public function commitConsumePointsTransaction($transactionId) {
|
||||||
|
if (!isset($this->mPointData->locked_points_data[$transactionId])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($expireTime, $points, $service, $action) = $this->mPointData->locked_points_data[$transactionId];
|
||||||
|
$this->mPointData->locked_points = max(0, $this->mPointData->locked_points - $points);
|
||||||
|
unset($this->mPointData->locked_points_data[$transactionId]);
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
$hookContainer = MediaWikiServices::getInstance()->getHookContainer();
|
||||||
|
|
||||||
|
$this->savePointLog(-$points, $service, $action);
|
||||||
|
$hookContainer->run('IsekaiUserPoints::AfterConsumePoints', [$this, $points, $service, $action]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback a consume points transaction
|
||||||
|
* @param string $transactionId Transaction id
|
||||||
|
* @return bool True if the transaction is rolled back successfully
|
||||||
|
*/
|
||||||
|
public function rollbackConsumePointsTransaction($transactionId) {
|
||||||
|
if (!isset($this->mPointData->locked_points_data[$transactionId])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($expireTime, $points) = $this->mPointData->locked_points_data[$transactionId];
|
||||||
|
$this->mPointData->locked_points = max(0, $this->mPointData->locked_points - $points);
|
||||||
|
$this->mPointData->points += $points;
|
||||||
|
unset($this->mPointData->locked_points_data[$transactionId]);
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save point data
|
||||||
|
*/
|
||||||
|
private function save() {
|
||||||
|
$dbw = $this->services->getDBLoadBalancer()->getConnection(DB_PRIMARY);
|
||||||
|
if ($this->pointRecordExists) {
|
||||||
|
$dbw->update(
|
||||||
|
'isekai_user_points',
|
||||||
|
[
|
||||||
|
'points' => $this->mPointData->points,
|
||||||
|
'timed_points_data' => json_encode($this->mPointData->timed_points_data),
|
||||||
|
'locked_points' => $this->mPointData->locked_points,
|
||||||
|
'locked_points_data' => json_encode($this->mPointData->locked_points_data),
|
||||||
|
'next_refresh_time' => $this->mPointData->next_refresh_time
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'user_id' => $this->user->getId(),
|
||||||
|
'type' => $this->type,
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$dbw->insert(
|
||||||
|
'isekai_user_points',
|
||||||
|
[
|
||||||
|
'user_id' => $this->mPointData->user_id,
|
||||||
|
'type' => $this->mPointData->type,
|
||||||
|
'points' => $this->mPointData->points,
|
||||||
|
'timed_points_data' => json_encode($this->mPointData->timed_points_data),
|
||||||
|
'locked_points' => $this->mPointData->locked_points,
|
||||||
|
'locked_points_data' => json_encode($this->mPointData->locked_points_data),
|
||||||
|
'next_refresh_time' => $this->mPointData->next_refresh_time,
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
$this->pointRecordExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function savePointLog($points, $service, $action, $expireTime = null) {
|
||||||
|
$dbw = $this->services->getDBLoadBalancer()->getConnection(DB_PRIMARY);
|
||||||
|
|
||||||
|
if ($expireTime) {
|
||||||
|
$expireDate = date('Y-m-d', $expireTime);
|
||||||
|
} else {
|
||||||
|
$expireDate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbw->insert(
|
||||||
|
'isekai_user_points_log',
|
||||||
|
[
|
||||||
|
'user_id' => $this->user->getId(),
|
||||||
|
'point_type' => $this->type,
|
||||||
|
'points' => $points,
|
||||||
|
'point_expire' => $expireDate,
|
||||||
|
'service' => $service,
|
||||||
|
'action' => $action,
|
||||||
|
'timestamp' => wfTimestampNow()
|
||||||
|
],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
namespace Isekai\UserPoints\Service;
|
||||||
|
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
use User;
|
||||||
|
|
||||||
|
class IsekaiUserPointsFactory {
|
||||||
|
/** @var MediaWikiServices */
|
||||||
|
private $services;
|
||||||
|
|
||||||
|
/** @var \Config */
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $pointConfig;
|
||||||
|
|
||||||
|
private static $instances;
|
||||||
|
|
||||||
|
public function __construct(MediaWikiServices $services) {
|
||||||
|
$this->services = $services;
|
||||||
|
$this->config = $services->getMainConfig();
|
||||||
|
$this->pointConfig = $this->config->get('IsekaiUserPointConfig');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $userId
|
||||||
|
* @param string $pointType
|
||||||
|
* @return IsekaiUserPoints|null
|
||||||
|
*/
|
||||||
|
public function newFromUserId(int $userId, string $pointType) {
|
||||||
|
$user = $this->services->getUserFactory()->newFromId($userId);
|
||||||
|
return $this->newFromUser($user, $pointType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @return IsekaiUserPoints|null
|
||||||
|
*/
|
||||||
|
public function newFromUser(User $user, string $pointType) {
|
||||||
|
if (!isset($this->pointConfig[$pointType])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user->isRegistered()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $user->getId();
|
||||||
|
$cacheKey = $userId . ':' . $pointType;
|
||||||
|
if (!isset(self::$instances[$cacheKey])) {
|
||||||
|
self::$instances[$cacheKey] = new IsekaiUserPoints($user, $pointType, $this->pointConfig, $this->services);
|
||||||
|
}
|
||||||
|
return self::$instances[$cacheKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @param string $pointType
|
||||||
|
* @param stdClass $data
|
||||||
|
*/
|
||||||
|
public function newFromData(User $user, string $pointType, $data) {
|
||||||
|
$instance = $this->newFromUser($user, $pointType);
|
||||||
|
if ($instance) {
|
||||||
|
$instance->setData($data);
|
||||||
|
}
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{0: User, 1: string, 2: IsekaiUserPoints} (user, instance)
|
||||||
|
*/
|
||||||
|
public function newFromUsers(array $users, array $pointTypes) {
|
||||||
|
$userids = [];
|
||||||
|
$usermap = [];
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$userId = $user->getId();
|
||||||
|
$userids[] = $userId;
|
||||||
|
$usermap[$userId] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbr = $this->services->getDBLoadBalancer()->getConnection(DB_REPLICA);
|
||||||
|
$pointDataRows = $dbr->select(
|
||||||
|
'isekai_user_points',
|
||||||
|
'*',
|
||||||
|
[ 'user_id' => $userids, 'point_type' => $pointTypes ],
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
$initedUsers = [];
|
||||||
|
foreach ($pointDataRows as $pointData) {
|
||||||
|
$user = $usermap[$pointData->user_id];
|
||||||
|
$pointType = $pointData->type;
|
||||||
|
$instance = $this->newFromUser($user, $pointType);
|
||||||
|
if ($instance) {
|
||||||
|
$instance->setData($pointData);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[] = [$user, $instance];
|
||||||
|
$initedUsers[] = $user;
|
||||||
|
}
|
||||||
|
foreach ($userids as $userId) {
|
||||||
|
if (!in_array($userId, $initedUsers)) {
|
||||||
|
$user = $usermap[$userId];
|
||||||
|
$pointType = $pointData->type;
|
||||||
|
$instance = $this->newFromUser($user, $pointType);
|
||||||
|
|
||||||
|
$result[] = [$user, $pointType, $instance];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
namespace Isekai\UserPoints;
|
||||||
|
|
||||||
|
use Html;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
|
||||||
|
class Utils {
|
||||||
|
public static function getPointName($pointType) {
|
||||||
|
$pointConfig = MediaWikiServices::getInstance()->getMainConfig()->get('IsekaiUserPointConfig');
|
||||||
|
|
||||||
|
if (!isset($pointConfig[$pointType])) {
|
||||||
|
return '{' . $pointType . '}';
|
||||||
|
} else {
|
||||||
|
$currentConfig = $pointConfig[$pointType];
|
||||||
|
if (isset($currentConfig['namemsg'])) {
|
||||||
|
return wfMessage($currentConfig['namemsg'])->text();
|
||||||
|
} else {
|
||||||
|
return $currentConfig['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPointIcon($pointType) {
|
||||||
|
$pointConfig = MediaWikiServices::getInstance()->getMainConfig()->get('IsekaiUserPointConfig');
|
||||||
|
|
||||||
|
if (!isset($pointConfig[$pointType])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentConfig = $pointConfig[$pointType];
|
||||||
|
if (!isset($currentConfig['icon']) || !isset($currentConfig['icon']['normal'])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$iconDom = [];
|
||||||
|
foreach(['normal', 'invert'] as $iconType) {
|
||||||
|
if (!isset($currentConfig['icon'][$iconType])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$iconConfig = $currentConfig['icon'][$iconType] ?? $currentConfig['icon']['normal'];
|
||||||
|
|
||||||
|
$className = $iconConfig['class'] ?? [];
|
||||||
|
if (is_string($className)) {
|
||||||
|
$className = explode(' ', $className);
|
||||||
|
}
|
||||||
|
|
||||||
|
$className[] = 'isekai-point-icon-' . $iconType;
|
||||||
|
|
||||||
|
if (isset($iconConfig['image'])) {
|
||||||
|
$iconDom[] = Html::element('img', [
|
||||||
|
'src' => $iconConfig['image'],
|
||||||
|
'class' => implode(' ', $className),
|
||||||
|
]);
|
||||||
|
} elseif (isset($iconConfig['html'])) {
|
||||||
|
$iconDom[] = Html::rawElement('span', [
|
||||||
|
'class' => implode(' ', $className),
|
||||||
|
], $iconConfig['html']);
|
||||||
|
} else {
|
||||||
|
$text = $iconConfig['text'] ?? '';
|
||||||
|
$iconDom[] = Html::element('span', [
|
||||||
|
'class' => implode(' ', $className),
|
||||||
|
], $text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Html::rawElement('span', [
|
||||||
|
'class' => "isekai-point-icon isekai-point-icon-type-$pointType"
|
||||||
|
], implode('', $iconDom));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
.isekai-point-icon {
|
||||||
|
> .isekai-point-icon-invert {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
$(function() {
|
||||||
|
const storeKey = 'isekai-userpoints-dailysign-lastSignDate';
|
||||||
|
const lastSignDate = localStorage.getItem(storeKey);
|
||||||
|
const today = new Date().toLocaleDateString();
|
||||||
|
if (lastSignDate !== today) {
|
||||||
|
let mwApi = new mw.Api();
|
||||||
|
mwApi.postWithToken('csrf', {
|
||||||
|
action: 'userdailysign',
|
||||||
|
}).done(function(data) {
|
||||||
|
if (data.userdailysign && data.userdailysign.success) {
|
||||||
|
if (Array.isArray(data.userdailysign.point_delta)) {
|
||||||
|
const pointDelta = data.userdailysign.point_delta;
|
||||||
|
let pointMsgList = [];
|
||||||
|
pointDelta.forEach(function (pointDeltaInfo) {
|
||||||
|
let msg = mw.msg('isekai-userpoints-point-name-num', pointDeltaInfo.name, pointDeltaInfo.icon, pointDeltaInfo.points);
|
||||||
|
pointMsgList.push(msg);
|
||||||
|
});
|
||||||
|
let separator = mw.msg('comma-separator');
|
||||||
|
let pointMsg = pointMsgList.join(separator);
|
||||||
|
|
||||||
|
let notificationMsg = mw.msg('isekai-userpoints-dailysign-notify-success', pointMsg);
|
||||||
|
mw.notify('', {
|
||||||
|
title: mw.msg('isekai-userpoints-dailysign-notify-title'),
|
||||||
|
tag: 'isekai-userpoints-dailysign',
|
||||||
|
id: 'isekai-userpoints-dailysign-notify',
|
||||||
|
});
|
||||||
|
|
||||||
|
function changeNotifyContent() {
|
||||||
|
let notifyDom = document.querySelector('#isekai-userpoints-dailysign-notify');
|
||||||
|
if (notifyDom) {
|
||||||
|
notifyDom.querySelector('.mw-notification-content').innerHTML = notificationMsg;
|
||||||
|
} else {
|
||||||
|
requestAnimationFrame(changeNotifyContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeNotifyContent();
|
||||||
|
}
|
||||||
|
localStorage.setItem(storeKey, today);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE /*_*/isekai_user_daily_sign (
|
||||||
|
`user_id` INT UNSIGNED NOT NULL,
|
||||||
|
`last_sign_date` DATE NOT NULL,
|
||||||
|
`sign_days_data` LONGTEXT NOT NULL,
|
||||||
|
`total_sign_days` INT UNSIGNED NOT NULL DEFAULT '0'
|
||||||
|
) /*$wgDBTableOptions*/;
|
||||||
|
ALTER TABLE /*_*/isekai_user_daily_sign ADD PRIMARY KEY (`user_id`);
|
@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE /*_*/isekai_user_daily_sign_log (
|
||||||
|
`user_id` INT UNSIGNED NOT NULL,
|
||||||
|
`year` INT UNSIGNED NOT NULL,
|
||||||
|
`month` INT UNSIGNED NOT NULL,
|
||||||
|
`sign_log` LONGTEXT NOT NULL
|
||||||
|
) /*$wgDBTableOptions*/;
|
||||||
|
ALTER TABLE /*_*/isekai_user_daily_sign_log ADD PRIMARY KEY (`user_id`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_daily_sign_log ADD INDEX(`user_id`, `year`, `month`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_daily_sign_log ADD INDEX(`year`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_daily_sign_log ADD INDEX(`month`);
|
@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE /*_*/isekai_user_points (
|
||||||
|
`user_id` INT UNSIGNED NOT NULL,
|
||||||
|
`type` VARCHAR(20) NOT NULL,
|
||||||
|
`points` INT UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`timed_points_data` LONGTEXT NOT NULL,
|
||||||
|
`locked_points` INT UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`locked_points_data` LONGTEXT NOT NULL,
|
||||||
|
`next_refresh_time` BIGINT UNSIGNED NULL
|
||||||
|
) /*$wgDBTableOptions*/;
|
||||||
|
ALTER TABLE /*_*/isekai_user_points ADD PRIMARY KEY (`user_id`, `type`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_points ADD INDEX(`user_id`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_points ADD INDEX(`type`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_points ADD INDEX(`points`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_points ADD INDEX(`next_refresh_time`);
|
@ -0,0 +1,15 @@
|
|||||||
|
CREATE TABLE /*_*/isekai_user_points_log (
|
||||||
|
`id` INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
`user_id` INT UNSIGNED NOT NULL,
|
||||||
|
`point_type` VARCHAR(20) NOT NULL,
|
||||||
|
`points` INT NOT NULL DEFAULT '0',
|
||||||
|
`point_expire` DATE NULL,
|
||||||
|
`service` VARCHAR(255) NOT NULL,
|
||||||
|
`action` VARCHAR(255) NOT NULL,
|
||||||
|
`timestamp` BIGINT UNSIGNED NOT NULL
|
||||||
|
) /*$wgDBTableOptions*/;
|
||||||
|
ALTER TABLE /*_*/isekai_user_points_log ADD INDEX(`user_id`, `point_type`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_points_log ADD INDEX(`user_id`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_points_log ADD INDEX(`service`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_points_log ADD INDEX(`action`);
|
||||||
|
ALTER TABLE /*_*/isekai_user_points_log ADD INDEX(`timestamp`);
|
@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE isekai_user_daily_sign (
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
last_sign_date DATE NOT NULL,
|
||||||
|
sign_days_data JSON NOT NULL,
|
||||||
|
total_sign_days INT NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
ALTER TABLE isekai_user_daily_sign ADD PRIMARY KEY (user_id);
|
@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE isekai_user_daily_sign_log (
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
year INT NOT NULL,
|
||||||
|
month INT NOT NULL,
|
||||||
|
sign_log JSON NOT NULL
|
||||||
|
);
|
||||||
|
ALTER TABLE isekai_user_daily_sign_log ADD PRIMARY KEY (user_id);
|
||||||
|
ALTER TABLE isekai_user_daily_sign_log ADD INDEX(user_id, year, month);
|
||||||
|
ALTER TABLE isekai_user_daily_sign_log ADD INDEX(year);
|
||||||
|
ALTER TABLE isekai_user_daily_sign_log ADD INDEX(month);
|
@ -0,0 +1,17 @@
|
|||||||
|
-- Create table for postgres
|
||||||
|
CREATE TABLE isekai_user_points (
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
type VARCHAR(20) NOT NULL,
|
||||||
|
points INTEGER NOT NULL DEFAULT 0,
|
||||||
|
timed_points_data TEXT NOT NULL,
|
||||||
|
locked_points INTEGER NOT NULL DEFAULT 0,
|
||||||
|
locked_points_data TEXT NOT NULL,
|
||||||
|
next_refresh_time BIGINT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE isekai_user_points ADD PRIMARY KEY (user_id, type);
|
||||||
|
ALTER TABLE isekai_user_points ADD INDEX (user_id);
|
||||||
|
ALTER TABLE isekai_user_points ADD INDEX (type);
|
||||||
|
ALTER TABLE isekai_user_points ADD INDEX (points);
|
||||||
|
ALTER TABLE isekai_user_points ADD INDEX (timed_points_data);
|
||||||
|
ALTER TABLE isekai_user_points ADD INDEX (next_refresh_time);
|
@ -0,0 +1,15 @@
|
|||||||
|
CREATE TABLE isekai_user_points_log (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
point_type VARCHAR(20) NOT NULL,
|
||||||
|
points INT NOT NULL DEFAULT 0,
|
||||||
|
point_expire DATE NULL,
|
||||||
|
service VARCHAR(255) NOT NULL,
|
||||||
|
action VARCHAR(255) NOT NULL,
|
||||||
|
timestamp BIGINT NOT NULL
|
||||||
|
);
|
||||||
|
ALTER TABLE isekai_user_points_log ADD INDEX(user_id, point_type);
|
||||||
|
ALTER TABLE isekai_user_points_log ADD INDEX(user_id);
|
||||||
|
ALTER TABLE isekai_user_points_log ADD INDEX(service);
|
||||||
|
ALTER TABLE isekai_user_points_log ADD INDEX(action);
|
||||||
|
ALTER TABLE isekai_user_points_log ADD INDEX(timestamp);
|
Loading…
Reference in New Issue