diff --git a/event_rewards/php/bootstrap_user_upgrade.php b/event_rewards/php/bootstrap_user_upgrade.php new file mode 100644 index 00000000..474ec545 --- /dev/null +++ b/event_rewards/php/bootstrap_user_upgrade.php @@ -0,0 +1,38 @@ +repository('XF:UserUpgrade'); + diff --git a/event_rewards/php/private_user_upgrade_api.php b/event_rewards/php/private_user_upgrade_api.php new file mode 100644 index 00000000..78609f55 --- /dev/null +++ b/event_rewards/php/private_user_upgrade_api.php @@ -0,0 +1,156 @@ + - Required: A respective ID query string should be set. E.g. the Steam provider requires a "steam_id" query string. + days_amount - Required: The amount of days to give as userUpgrade. this is related to event rewards. +*/ + +// --- CONFIG START --- +// API key for authorization +$api_key = "k9Mk2lA3mF9Sk0FoaD"; + +// Provider name with given query string +$provider_config = array( + "th_cap_steam" => "steam_id", + "minecraft" => "uuid" +); + +// For backwards compatibility +$default_provider = "th_cap_steam"; + +// Responses +$response_error_apikey = 'API KEY INVALID'; +$response_no_account_associated = 'NOACCOUNT'; +$response_group_none = 'NOGROUP'; +$response_error_id = 'MISSING ID'; + + + +// --- CONFIG END --- + +// TODO: Use single query string for ID? + +// Prints the reason and exits. +function quitWithReason($reason, $status_code = 200){ + + http_response_code($status_code); + echo $reason; + die; +} + +function debug_to_console($data) { + $output = $data; + if (is_array($output)) + $output = implode(',', $output); + echo ""; +} + + +// Check the API key +if(!isset($_GET['api_key']) || $_GET['api_key'] !== $api_key){ + quitWithReason($response_error_apikey, 401); +} + + +// Load XenForo stuffs +require ('bootstrap_user_upgrade.php'); + +// Fetch provider from query string. If none is found, use the default provider. +$provider = isset($_GET['provider']) ? $_GET['provider'] : $default_provider; + + +$days_reward = $_GET['days_reward']; + +//debug_to_console("$days_reward"); //will always be set. + +// Check if the given provider has been configured +if(!array_key_exists($provider, $provider_config)){ + quitWithReason($response_error_provider, 404); +} + +// Check if the ID is set. +if(!isset($_GET[$provider_config[$provider]])){ + quitWithReason($response_error_id, 404); +} + + +// Fetch the respective ID query string. +$provider_key = $_GET[$provider_config[$provider]]; + +// Use finder for external accounts +$connectedAccountFinder = \XF::finder('XF:UserConnectedAccount'); + +// Search for external accounts with the given provider and ID +$stAssoc = $connectedAccountFinder->where('provider', $provider)->where('provider_key', $provider_key)->fetchOne(); + +// If no account is associated to the steam account return NOACCOUNT +if(empty($stAssoc)){ + quitWithReason($response_no_account_associated, 500); +} + +// Load XenForo model for users +$userFinder = \XF::finder('XF:User'); + +// Fetch the user from the external account entry +$user = $userFinder->where('user_id', $stAssoc->user_id)->fetchOne(); + +// (This should not happen) If no user is found, assign no group. +if(empty($user)){ + // Push error to XenForo error log. + trigger_error("External account association exists for non-existing user ID (" . $stAssoc->user_id . ")."); + quitWithReason($response_group_none, 500); +} + +//debug_to_console("$stAssoc->user_id"); + +// Get UserUpgrade Information +$upgradeFinder = \XF::finder('XF:UserUpgrade'); +$userUpgrade = $upgradeFinder->where('user_upgrade_id', 1)->fetchOne(); //its 1 because it should not matter which userupgrade we pick. its actually 1 month vip. + +//debug_to_console("$user"); + +//before upgrading the user lets confirm if they already have a running vip duration. if they do we update it instead of creating a new one. +// Get Active Upgrade Information +$userUpgradeActiveFinder = \XF::Finder('XF:UserUpgradeActive'); +$activeUserUpgrades = $userUpgradeActiveFinder->where('user_id', $stAssoc->user_id)->fetchOne(); +//debug_to_console("$activeUserUpgrades"); + +if (isset($activeUserUpgrades)) +{ + debug_to_console("has user upgrade"); + $userUpgrade_new = $activeUserUpgrades->Upgrade; + $expireDate = $activeUserUpgrades->end_date; + //debug_to_console("$userUpgrade_new"); + //debug_to_console("$expireDate"); + + $dateObject = date_create_from_format('U', $expireDate); + $intervalString = "P{$days_reward}D"; + $interval = new DateInterval($intervalString); + $dateObject->add($interval); + $newTimestamp = $dateObject->getTimestamp(); + + //debug_to_console("$newTimestamp"); + $upgradeService = $app->service('XF:User\Upgrade', $userUpgrade_new, $user); + $upgradeService->setEndDate($newTimestamp); + $upgradeService->ignoreUnpurchasable(true); + $upgradeService->upgrade(); +} +else +{ + debug_to_console("no user upgrade so far"); + // add days to current date + $date = strtotime("+$days_reward day"); + + // Upgrade User + $upgradeService = $app->service('XF:User\Upgrade', $userUpgrade, $user); + $upgradeService->setEndDate($date); + $upgradeService->ignoreUnpurchasable(true); + $upgradeService->upgrade(); +} + +quitWithReason(" end ", 200); + diff --git a/event_rewards/python/README.md b/event_rewards/python/README.md new file mode 100644 index 00000000..0aa03146 --- /dev/null +++ b/event_rewards/python/README.md @@ -0,0 +1,7 @@ +this is meant to automatically give event rewards through some xenforo API call + +mysql-connector-python +requests + + + diff --git a/event_rewards/python/main.py b/event_rewards/python/main.py new file mode 100644 index 00000000..8532092e --- /dev/null +++ b/event_rewards/python/main.py @@ -0,0 +1,71 @@ +#!/home/nonroot/handle_event_rewards/venv/bin/python3 +from settings import (get_connection_events, api_key) +import requests + +unloze_user_upgrade_api = f"https://unloze.com/api/private_user_upgrade_api.php?api_key={api_key}&steam_id=" + +def get_all_pending_event_rewards(): + with get_connection_events() as conn: + with conn.cursor(buffered=True) as cur: + sql_statement = """ + select steamid, event_reward_days + from unloze_event.rewards + where is_processed is false + """ + cur.execute(sql_statement) + res = cur.fetchall() + conn.close() + return res + +def update_is_procssed(): + with get_connection_events() as conn: + with conn.cursor(buffered=True) as cur: + sql_statement = """ + update unloze_event.rewards set is_processed = true where is_processed is false + """ + cur.execute(sql_statement) + conn.commit() #pretty gay how contextmanager cant commit and close itself like in psycopg2 + conn.close() + +def steamid_to_commid(steamid): + steamid64ident = 76561197960265728 + sid_split = steamid.split(':') + commid = int(sid_split[2]) * 2 + if sid_split[1] == '1': + commid += 1 + commid += steamid64ident + return commid + +def update_steam_id_has_no_forum_account_with_steam(steamid): + with get_connection_events() as conn: + with conn.cursor(buffered=True) as cur: + sql_statement = """ + update unloze_event.rewards + set has_no_forum_account_with_associated_steam = true + where has_no_forum_account_with_associated_steam is not true + and steamid = %s + """ + cur.execute(sql_statement, [steamid]) + conn.commit() #pretty gay how contextmanager cant commit and close itself like in psycopg2 + conn.close() + +def reward_vip_to_event_winners(res): + for result in res: + steamid = result[0] + days_rewarded = result[1] + steam_community_id = steamid_to_commid(steamid) + + r = requests.get(f"{unloze_user_upgrade_api}{steam_community_id}&days_reward={days_rewarded}") + #print("r.url: ", r.url) + if r.content.decode('utf8').startswith("NOACCOUNT"): #not registed with associated steam account + update_steam_id_has_no_forum_account_with_steam(steamid) + +def main(): + res = get_all_pending_event_rewards() + #send request to xenforo for giving vip time + reward_vip_to_event_winners(res) + update_is_procssed() #we took them all out so we just update all rows where is_processed is false. + +if __name__ == '__main__': + main() + diff --git a/event_rewards/scripting/trackwinners.sp b/event_rewards/scripting/trackwinners.sp new file mode 100644 index 00000000..55c95f9f --- /dev/null +++ b/event_rewards/scripting/trackwinners.sp @@ -0,0 +1,118 @@ +#pragma semicolon 1 +#define PLUGIN_AUTHOR "jenz" +#define PLUGIN_VERSION "1.0" +#include +#include + +Database g_hDatabase; +int g_ireward_days = 0; +char g_cadmin_sid[64]; +char g_cadmin_escaped_name[256]; + +public Plugin myinfo = +{ + name = "unloze track event winners", + author = PLUGIN_AUTHOR, + description = "during events it tracks through the admin command if people won the event", + version = PLUGIN_VERSION, + url = "www.unloze.com" +}; + + +public void OnPluginStart() +{ + if (!g_hDatabase) + { + Database.Connect(SQL_OnDatabaseConnect, "Event_notifier"); + } + RegAdminCmd("sm_trackwinners", Cmd_trackwinners, ADMFLAG_GENERIC, "Use this each round where humans can win VIP. the alive humans on that round will have their steam IDs stored. Also specify a day as the number."); + HookEvent("round_start", Event_RoundStart); + HookEvent("round_end", Event_RoundEnd); +} + +public void Event_RoundStart(Handle event, const char[] name, bool dontBroadcast) +{ + g_ireward_days = 0; + Format(g_cadmin_sid, sizeof(g_cadmin_sid), ""); + Format(g_cadmin_escaped_name, sizeof(g_cadmin_escaped_name), ""); +} + +public void Event_RoundEnd(Handle event, const char[] name, bool dontBroadcast) +{ + int winner_team = GetEventInt(event, "winner"); + if (winner_team == 3 && g_ireward_days > 0) //ct won the round and are supposed to get vip for it. + { + char current_map[128]; + GetCurrentMap(current_map, sizeof(current_map)); + for (int i = 1; i <= MaxClients; i++) + { + if (IsValidClient(i) && !IsFakeClient(i) && IsPlayerAlive(i) && GetClientTeam(i) == CS_TEAM_CT) + { + write_reward_entry(i, current_map); + } + } + } +} + +public void write_reward_entry(int client, char[] event_map) +{ + char sQuery[512]; + char sName[MAX_NAME_LENGTH]; + GetClientName(client, sName, sizeof(sName)); + int size2 = 2 * strlen(sName) + 1; + char[] sEscapedName = new char[size2 + 1]; + g_hDatabase.Escape(sName, sEscapedName, size2 + 1); + + char SID[64]; + GetClientAuthId(client, AuthId_Steam2, SID, sizeof(SID)); + Format(sQuery, sizeof(sQuery), "INSERT INTO `rewards` (`steamid`, `username`, `event_map`, `event_reward_days`, `admin_created_reward_steamid`, `admin_created_reward_name`) VALUES ('%s', '%s', '%s', '%i', '%s', '%s')", SID, sEscapedName, event_map, g_ireward_days, g_cadmin_sid, g_cadmin_escaped_name); + g_hDatabase.Query(DummyCallback, sQuery, DBPrio_Normal); + PrintToChat(client, "Your %i days VIP were stored for winning this round. They will be awarded in the next 2-3 hours if you have a forum account.", g_ireward_days); +} + +public void DummyCallback(Handle hOwner, Handle hChild, const char[] err, any data) +{ + if (hOwner == null || hChild == null) + LogError("Query error. (%s)", err); +} + +public Action Cmd_trackwinners(int client, int args) +{ + if (!IsValidClient(client)) + return Plugin_Handled; + if (args != 1) + { + ReplyToCommand(client, "[SM] sm_trackwinners "); + return Plugin_Handled; + } + char sDays[65]; + GetCmdArg(1, sDays, sizeof(sDays)); + g_ireward_days = StringToInt(sDays); + + GetClientAuthId(client, AuthId_Steam2, g_cadmin_sid, sizeof(g_cadmin_sid)); + + char sName[MAX_NAME_LENGTH]; + GetClientName(client, sName, sizeof(sName)); + int size2 = 2 * strlen(sName) + 1; + g_hDatabase.Escape(sName, g_cadmin_escaped_name, size2 + 1); + + PrintToChatAll("Admin %N set a VIP reward of %i days for winning this round", client, g_ireward_days); + return Plugin_Handled; +} + +public void SQL_OnDatabaseConnect(Database db, const char[] error, any data) +{ + if(!db || strlen(error)) + { + LogError("Database error: %s", error); + return; + } + g_hDatabase = db; +} + +stock bool IsValidClient(int client) +{ + if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client)) + return true; + return false; +} diff --git a/event_rewards/sql/rewards_table.sql b/event_rewards/sql/rewards_table.sql new file mode 100644 index 00000000..2ec5dd67 --- /dev/null +++ b/event_rewards/sql/rewards_table.sql @@ -0,0 +1,13 @@ +CREATE TABLE unloze_event.rewards ( + `rowid` int auto_increment NOT NULL, + `steamid` varchar(64) not NULL, + `username` varchar(256) DEFAULT NULL, + `event_map` varchar(512) DEFAULT NULL, + `event_reward_days` int not NULL, + `created_on` datetime DEFAULT current_timestamp(), + `admin_created_reward_steamid` varchar(64) not NULL, + `admin_created_reward_name` varchar(64) not NULL, + `is_processed` bool default false, + `has_no_forum_account_with_associated_steam` bool default null, + PRIMARY KEY (`rowid`) +) diff --git a/event_rewards/systemctl/handle_event_rewards.service b/event_rewards/systemctl/handle_event_rewards.service new file mode 100644 index 00000000..363033fc --- /dev/null +++ b/event_rewards/systemctl/handle_event_rewards.service @@ -0,0 +1,10 @@ +[Unit] +Description=runs python script to check if there are missing event rewards to hand out. + +[Service] +Type=simple +User=nonroot +Environment=PYTHONUNBUFFERED=1 +Environment=PATH=/home/nonroot/handle_event_rewards/venv/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/snap/bin +WorkingDirectory=/home/nonroot/handle_event_rewards +ExecStart=/home/nonroot/handle_event_rewards/main.py diff --git a/event_rewards/systemctl/handle_event_rewards.timer b/event_rewards/systemctl/handle_event_rewards.timer new file mode 100644 index 00000000..52daded8 --- /dev/null +++ b/event_rewards/systemctl/handle_event_rewards.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Checks every 3 hours if there are event rewards to hand out. + +[Timer] +OnCalendar=0/3:00:00 + +[Install] +WantedBy=multi-user.target +